# 機能設計書 19-支払先マージ

## 概要

本ドキュメントは、Actualアプリケーションにおける支払先マージ機能について、その処理内容、入出力仕様、および実装詳細を記述した機能設計書である。

### 本機能の処理概要

支払先マージ機能は、重複する支払先を1つに統合する機能である。マージ元の支払先に関連付けられた取引は、自動的にマージ先の支払先に関連付けが変更される。

**業務上の目的・背景**：同一の取引相手が異なる名前で登録されてしまうことがある。例えば、「Amazon」「AMAZON.CO.JP」「Amazon.co.jp」が別々の支払先として存在する場合がある。本機能により、これらを1つの支払先に統合し、支払先データの整合性を保つことができる。

**機能の利用シーン**：
- 銀行連携でインポートされた支払先名が既存と微妙に異なる場合
- 手動入力で同じ店舗を異なる名前で登録してしまった場合
- 支払先データのクリーンアップを行いたい場合

**主要な処理内容**：
1. マージ先（target）支払先の選択
2. マージ元（merge）支払先の選択（複数可）
3. payee_mappingテーブルの更新（マージ元→マージ先への参照変更）
4. マージ元支払先の論理削除

**関連システム・外部連携**：本機能は内部完結型であり、外部システムとの直接連携はない。

**権限による制御**：特別な権限制御はなく、予算ファイルを開いているユーザーであれば利用可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 19 | 支払先管理画面（デスクトップ） | 補助機能 | 重複する支払先の統合処理 |

## 機能種別

データ変換 / CRUD操作

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| targetId | string | Yes | マージ先支払先ID | 存在する支払先ID、振替支払先は不可 |
| mergeIds | string[] | Yes | マージ元支払先ID配列 | 存在する支払先ID、振替支払先は除外 |

### 入力データソース

- 画面入力（支払先管理画面）
- API（外部システムからの呼び出し）

## 出力仕様

### 出力データ

マージ処理は戻り値を持たない（void）。
以下のデータベース変更が行われる：

| 項目 | 変更内容 |
|------|---------|
| payee_mapping | マージ元のtargetIdをマージ先IDに更新 |
| payees | マージ元をtombstone=1で論理削除 |

### 出力先

- データベース（payeesテーブル、payee_mappingテーブル）

## 処理フロー

### 処理シーケンス

```
1. マージ対象選択
   └─ マージ先（target）とマージ元（merge）を選択

2. 振替支払先フィルタ
   └─ マージ先・マージ元から振替支払先を除外

3. マッピング更新
   └─ マージ元を参照する全てのpayee_mappingのtargetIdをマージ先に更新

4. マージ元削除
   └─ payee_mappingを更新後、マージ元支払先を論理削除
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[マージ先選択]
    B --> C[マージ元選択]
    C --> D{マージ先は振替支払先?}
    D -->|Yes| E[処理終了]
    D -->|No| F[マージ元から振替支払先除外]
    F --> G[batchMessages開始]
    G --> H[各マージ元について]
    H --> I[payee_mapping取得]
    I --> J[targetIdをマージ先に更新]
    J --> K[payee_mapping更新]
    K --> L[マージ元を論理削除]
    L --> M{次のマージ元?}
    M -->|Yes| H
    M -->|No| N[batchMessages終了]
    N --> E
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 振替支払先除外 | マージ先・マージ元ともに振替支払先は対象外 | マージ実行時 |
| BR-02 | 複数マージ | 複数のマージ元を同時にマージ可能 | マージ実行時 |
| BR-03 | 取引参照維持 | マージ後も過去の取引は正しい支払先（マージ先）を参照 | マージ後 |
| BR-04 | マッピング連鎖 | マージ元を参照していた他のマージ済み支払先も自動的にマージ先を参照 | マージ実行時 |

### 計算ロジック

**マッピング更新ロジック**:
```typescript
// マージ元を参照する全てのマッピングを取得
const mappings = await all('SELECT id FROM payee_mapping WHERE targetId = ?', [mergeId]);

// 各マッピングのtargetIdをマージ先に更新
for (const mapping of mappings) {
  await update('payee_mapping', { id: mapping.id, targetId: targetId });
}

// マージ元自身のマッピングも更新
await update('payee_mapping', { id: mergeId, targetId: targetId });
```

## データベース操作仕様

### 操作別データベース影響一覧

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| マッピング取得 | payee_mapping | SELECT | マージ元を参照するマッピングを取得 |
| マッピング更新 | payee_mapping | UPDATE | targetIdをマージ先に変更 |
| 支払先削除 | payees | UPDATE(tombstone) | マージ元を論理削除 |

### テーブル別操作詳細

#### payee_mapping

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | id | WHERE targetId = マージ元ID | 参照マッピング取得 |
| UPDATE | targetId | マージ先ID | 参照先変更 |

#### payees

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | tombstone | 1 | マージ元の論理削除 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 処理スキップ | マージ先が振替支払先の場合 | 処理をスキップ（エラーは発生しない） |

### リトライ仕様

特になし

## トランザクション仕様

- マージ処理はundoable（元に戻す対応）
- undoable関数に引数情報（mergeIds, targetId）を渡して履歴を保持
- batchMessagesで全てのUPDATEをバッチ処理

## パフォーマンス要件

- 複数の支払先を同時にマージしても適切な時間で完了すること

## セキュリティ考慮事項

- 特別なセキュリティ要件なし
- マージ操作は元に戻すことが可能

## 備考

- API経由でのマージ: mergePayees(targetId, mergeIds)
- 未使用支払先のマージモーダル: MergeUnusedPayeesModal

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: API定義を理解する

マージAPIの仕様を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | methods.ts | `packages/api/methods.ts` | mergePayees関数の定義 |

**読解のコツ**:
- **277-282行目**: mergePayees関数の定義とパラメータ

#### Step 2: APIハンドラを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | app.ts | `packages/loot-core/src/server/payees/app.ts` | mergePayees関数の実装 |

**主要処理フロー**:
1. **29-37行目**: ハンドラ登録（undoableラッパー付き）
2. **73-81行目**: mergePayees関数（db.mergePayeesを呼び出し）

#### Step 3: データベース操作を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | db/index.ts | `packages/loot-core/src/server/db/index.ts` | mergePayees関数の実装 |

**主要処理フロー**:
- **603-641行目**: mergePayees関数
- **607-609行目**: 支払先データの読み込み
- **611-615行目**: 振替支払先のフィルタリング
- **617-640行目**: batchMessagesでマッピング更新と削除

#### Step 4: UIコンポーネントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | ManagePayees.tsx | `packages/desktop-client/src/components/payees/ManagePayees.tsx` | onMerge関数 |
| 4-2 | PayeeMenu.tsx | `packages/desktop-client/src/components/payees/PayeeMenu.tsx` | マージメニュー項目 |

**主要処理フロー**:
- **196-201行目**: onMerge関数の実装

### プログラム呼び出し階層図

```
UI (ManagePayees.tsx / PayeeMenu.tsx)
    │
    └─ send('payees-merge')
           └─ app.ts: mergePayees (undoable)
                  └─ db.mergePayees
                         │
                         ├─ 支払先データ読み込み (all('SELECT * FROM payees'))
                         │
                         ├─ 振替支払先フィルタ
                         │
                         └─ batchMessages
                                │
                                ├─ payee_mapping取得 (各マージ元)
                                │
                                ├─ payee_mapping更新 (targetId変更)
                                │
                                └─ 支払先削除 (tombstone)
```

### データフロー図

```
[入力]                    [処理]                           [出力]

targetId ─────┐
              │
              ├──▶ mergePayees
              │         │
mergeIds[] ───┘         │
                        ├──▶ 振替支払先チェック
                        │
                        ├──▶ payee_mapping更新 (各マージ元のtargetId→targetに変更)
                        │
                        └──▶ payees削除 (マージ元をtombstone=1)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| methods.ts | `packages/api/methods.ts` | ソース | 外部API定義 |
| app.ts | `packages/loot-core/src/server/payees/app.ts` | ソース | APIハンドラ |
| db/index.ts | `packages/loot-core/src/server/db/index.ts` | ソース | データベース操作 |
| ManagePayees.tsx | `packages/desktop-client/src/components/payees/ManagePayees.tsx` | ソース | 支払先管理UI |
| PayeeMenu.tsx | `packages/desktop-client/src/components/payees/PayeeMenu.tsx` | ソース | コンテキストメニュー |
| MergeUnusedPayeesModal.tsx | `packages/desktop-client/src/components/modals/MergeUnusedPayeesModal.tsx` | ソース | マージモーダル |
