# 機能設計書 20-共通支払先取得

## 概要

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

### 本機能の処理概要

共通支払先取得機能は、よく使う支払先を取得する機能である。過去12週間の取引データを分析し、最も頻繁に使用された支払先をリストアップする。取引入力時の支払先候補として使用される。

**業務上の目的・背景**：取引入力時に毎回支払先を検索するのは手間がかかる。よく使う支払先を優先的に表示することで、入力効率が向上する。本機能により、ユーザーは頻繁に取引する相手を素早く選択できる。

**機能の利用シーン**：
- 新規取引入力時の支払先選択
- 支払先オートコンプリートの候補表示

**主要な処理内容**：
1. 過去12週間の取引データを集計
2. 支払先ごとの取引回数をカウント
3. 取引回数の多い順にソート
4. 上位10件を返却

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

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | 取引入力フォーム | 主機能 | 支払先入力時の候補表示 |

## 機能種別

データ検索

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| （なし） | - | - | パラメータなし | - |

### 入力データソース

- データベース（payeesテーブル、v_transactions_internal_aliveビュー）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | string | 支払先ID |
| name | string | 支払先名 |
| favorite | boolean | お気に入りフラグ |
| category | string | 関連カテゴリID |
| common | boolean | 共通支払先フラグ（常にtrue） |
| transfer_acct | null | 振替口座（常にnull、振替支払先は除外） |
| c | number | 取引回数 |
| latest | number | 最新取引日（数値形式） |

### 出力先

- API応答（クライアントサイドで使用）

## 処理フロー

### 処理シーケンス

```
1. API呼び出し
   └─ getCommonPayees()

2. 集計期間計算
   └─ 現在日時から12週間前の日付を計算

3. クエリ実行
   └─ 支払先と取引をJOINし、期間内の取引を集計

4. ソート・絞り込み
   └─ 取引回数の多い順にソートし、上位10件を取得

5. 結果返却
   └─ PayeeEntity[]形式で返却
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[12週間前の日付計算]
    B --> C[SQLクエリ実行]
    C --> D[payees LEFT JOIN v_transactions_internal_alive]
    D --> E[WHERE date > 12週間前]
    E --> F[GROUP BY payee.id]
    F --> G[COUNT取引数]
    G --> H[ORDER BY count DESC]
    H --> I[LIMIT 10]
    I --> J[結果返却]
    J --> K[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 集計期間 | 過去12週間の取引データを対象 | クエリ実行時 |
| BR-02 | 上限件数 | 最大10件を返却 | クエリ実行時 |
| BR-03 | 振替支払先除外 | transfer_acctが設定されている支払先は除外 | クエリ実行時 |
| BR-04 | 名前必須 | 名前が空の支払先は除外 | クエリ実行時 |
| BR-05 | 削除済み除外 | tombstone=1の支払先は除外 | クエリ実行時 |

### 計算ロジック

**集計期間計算**:
```typescript
// 12週間前の日付を数値形式で取得
const twelveWeeksAgo = toDateRepr(
  monthUtils.subWeeks(monthUtils.currentDate(), 12)
);
```

**取引回数集計クエリ**:
```sql
SELECT
  p.id as id,
  p.name as name,
  p.favorite as favorite,
  p.category as category,
  TRUE as common,
  NULL as transfer_acct,
  count(*) as c,
  max(t.date) as latest
FROM payees p
LEFT JOIN v_transactions_internal_alive t ON t.payee = p.id
WHERE LENGTH(p.name) > 0
  AND p.tombstone = 0
  AND t.date > ${twelveWeeksAgo}
GROUP BY p.id
ORDER BY c DESC, p.transfer_acct IS NULL DESC, p.name COLLATE NOCASE
LIMIT 10
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 共通支払先取得 | payees, v_transactions_internal_alive | SELECT | 支払先と取引を結合して集計 |

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

#### payees, v_transactions_internal_alive

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | p.*, count(*), max(t.date) | tombstone=0, LENGTH(name)>0, date>12週間前 | 集計クエリ |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | なし | - | - |

### リトライ仕様

特になし

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

読み取り専用操作のため、トランザクション管理は不要。

## パフォーマンス要件

- 即座に結果を返却すること
- 取引数が多くてもパフォーマンスを維持すること

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

- 特別なセキュリティ要件なし
- 取得結果は予算ファイル内のデータのみ

## 備考

- API関数: getCommonPayees()
- 12週間という期間と10件という上限は定数としてハードコードされている

---

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

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

### 推奨読解順序

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

共通支払先取得APIの仕様を確認する。

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

**読解のコツ**:
- **254-256行目**: getCommonPayees関数の定義（パラメータなし）

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

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

**主要処理フロー**:
1. **25行目**: ハンドラ登録
2. **46-49行目**: getCommonPayees関数（db.getCommonPayeesを呼び出し、payeeModel.fromDbで変換）

#### Step 3: データベースクエリを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | db/index.ts | `packages/loot-core/src/server/db/index.ts` | getCommonPayees関数のSQLクエリ |

**主要処理フロー**:
- **652-679行目**: getCommonPayees関数
- **653-656行目**: 12週間前の日付計算、limit定数
- **657-678行目**: 集計SQLクエリ

#### Step 4: 日付ユーティリティを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | months.ts | `packages/loot-core/src/shared/months.ts` | subWeeks、currentDate関数 |

**主要処理フロー**:
- subWeeks: 指定週数前の日付を計算
- currentDate: 現在日付を取得

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

```
Client (支払先選択UI)
    │
    └─ send('common-payees-get')
           └─ app.ts: getCommonPayees
                  └─ db.getCommonPayees
                         │
                         ├─ 12週間前日付計算
                         │      └─ monthUtils.subWeeks
                         │
                         └─ 集計SQLクエリ実行
                                └─ payees LEFT JOIN v_transactions_internal_alive
                                       └─ GROUP BY, ORDER BY, LIMIT
```

### データフロー図

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

（なし） ──────▶ getCommonPayees
                      │
                      ├──▶ 12週間前日付計算
                      │
                      ├──▶ SQL実行（payees + transactions JOIN）
                      │
                      ├──▶ GROUP BY payee.id
                      │
                      ├──▶ ORDER BY count DESC
                      │
                      └──▶ LIMIT 10 ──▶ PayeeEntity[]
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| 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` | ソース | データベースクエリ |
| models.ts | `packages/loot-core/src/server/models.ts` | ソース | payeeModel（データ変換） |
| months.ts | `packages/loot-core/src/shared/months.ts` | ソース | 日付ユーティリティ |
