# 機能設計書 33-収支レポート

## 概要

本ドキュメントは、収入と支出の推移をグラフで表示するレポート機能について記述する。

### 本機能の処理概要

**業務上の目的・背景**：家計管理において、収入と支出のバランスを把握することは最も基本的かつ重要なニーズである。本機能は、時系列での収支推移を視覚化することで、ユーザーが財務状況を直感的に理解し、将来の予算計画や支出削減の意思決定を支援する。特にキャッシュフローレポートは、残高推移と合わせて「手元資金がどのように変動しているか」を把握するのに役立つ。

**機能の利用シーン**：
- 月次/日次の収支バランス確認
- 特定期間の収入・支出傾向分析
- 予算と実績の乖離確認
- 純資産推移の長期分析
- ダッシュボードでの収支サマリー表示

**主要な処理内容**：
1. 期間（開始日・終了日）の指定とフィルタ条件の適用
2. 取引データの集計（収入・支出・振替を分離）
3. 日別または月別のデータ集計とグラフ用データ変換
4. 残高推移の計算（キャッシュフローの場合）
5. グラフコンポーネントへのデータ渡しとレンダリング
6. ウィジェットとしてダッシュボードへの保存

**関連システム・外部連携**：AQL（Actual Query Language）によるデータベースクエリ

**権限による制御**：予算ファイルへの読み取り権限が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 30 | レポートダッシュボード画面 | 補助機能 | 収支レポートカードの表示 |
| 31 | 純資産レポート画面 | 主機能 | 純資産推移グラフの表示・分析 |
| 32 | キャッシュフローレポート画面 | 主機能 | 収入・支出のキャッシュフロー表示 |
| 34 | カスタムレポート画面 | 補助機能 | 収支データの取得・表示 |
| 35 | サマリーレポート画面 | 主機能 | 収支サマリーの表示 |
| 36 | カレンダーレポート画面 | 主機能 | カレンダー形式での取引・収支表示 |
| 38 | クロスオーバーレポート画面 | 主機能 | 月間クロスオーバー分析の表示 |

## 機能種別

計算処理 / データ集計 / 可視化

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| start | string | Yes | 開始日（YYYY-MM-DD形式） | 有効な日付形式 |
| end | string | Yes | 終了日（YYYY-MM-DD形式） | start以降の日付 |
| conditions | RuleConditionEntity[] | No | フィルタ条件 | - |
| conditionsOp | 'and' \| 'or' | No | フィルタ条件の結合演算子 | デフォルト: 'and' |
| isConcise | boolean | No | 月別集計モード | デフォルト: false（日別） |

### 入力データソース

- transactionsテーブル（取引データ）
- accountsテーブル（口座データ）
- payeesテーブル（支払先データ、振替判定用）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| graphData | object | グラフ描画用データ |
| graphData.income | Array<{x: Date, y: number}> | 収入データ配列 |
| graphData.expenses | Array<{x: Date, y: number}> | 支出データ配列 |
| graphData.transfers | Array<{x: Date, y: number}> | 振替データ配列 |
| graphData.balances | Array<{x: Date, y: number, premadeLabel: JSX.Element}> | 残高推移データ |
| totalIncome | number | 合計収入 |
| totalExpenses | number | 合計支出 |
| totalTransfers | number | 合計振替額 |
| balance | number | 最終残高 |
| totalChange | number | 期間中の残高変化 |

### 出力先

レポート画面（グラフ表示）

## 処理フロー

### 処理シーケンス

```
1. パラメータ受け取りと日付範囲の正規化
   └─ 終了日が現在日より後の場合は現在日に補正
2. フィルタ条件の変換
   └─ make-filters-from-conditionsでAQLフィルタに変換
3. 初期残高の取得
   └─ 開始日より前の取引合計を計算
4. 期間内取引の集計
   └─ 収入（amount > 0）と支出（amount < 0）を分離して集計
5. 日別/月別データの生成
   └─ 各日/月の収入・支出・振替・残高を計算
6. グラフデータの整形
   └─ ツールチップラベル付きのデータ配列を生成
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[日付範囲の正規化]
    B --> C[フィルタ条件の変換]
    C --> D[初期残高クエリ実行]
    D --> E[収入クエリ実行]
    D --> F[支出クエリ実行]
    E --> G[runAll: 並列実行]
    F --> G
    G --> H{isConcise?}
    H -->|Yes| I[月別集計]
    H -->|No| J[日別集計]
    I --> K[グラフデータ生成]
    J --> K
    K --> L[recalculate: データ整形]
    L --> M[setData: 結果出力]
    M --> N[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | オフバジェット除外 | オフバジェット口座の取引は集計対象外 | 常時 |
| BR-02 | 振替分離 | 振替取引（payee.transfer_acctがnullでない）は収支から分離 | 常時 |
| BR-03 | 日付補正 | 終了日が現在日より後の場合は現在日に補正 | 常時 |
| BR-04 | 自動集計切替 | 期間が93日（3ヶ月）を超える場合は月別集計に自動切替 | isConcise自動判定時 |

### 計算ロジック

**残高計算**:
```
balance[n] = balance[n-1] + income[n] + expense[n] + creditTransfers[n] + debitTransfers[n]
```

**合計変化量**:
```
totalChange = balances[最終] - balances[最初]
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 初期残高取得 | transactions | SELECT | 開始日前の取引合計を計算 |
| 収入集計 | transactions | SELECT | amount > 0の取引を日/月でグループ化 |
| 支出集計 | transactions | SELECT | amount < 0の取引を日/月でグループ化 |

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

#### transactions

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | date | グループ化キー | 日別または月別 |
| SELECT | amount | 合計（$sum） | - |
| SELECT | payee.transfer_acct | グループ化キー | 振替判定用 |
| WHERE | account.offbudget | = false | オフバジェット除外 |
| WHERE | date | >= start, <= end | 期間フィルタ |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | データなし | 取引データが存在しない | 空のグラフを表示 |

### リトライ仕様

特になし。データ取得は非同期で行われ、ローディング表示を行う。

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

読み取り専用。データベースの更新は行わない。

## パフォーマンス要件

- 大量の取引データでも効率的に集計できるよう、AQLのグループ化機能を活用
- 日別と月別で適切にクエリを切り替えることでパフォーマンスを最適化

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

- 金額表示にはPrivacyFilterを適用し、プライバシーモード時は非表示にできる

## 備考

- Rechartsライブラリを使用してグラフを描画
- ツールチップには日付、収入、支出、変化量、残高を表示

---

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

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

### 推奨読解順序

#### Step 1: データ構造を理解する

レポートで使用されるデータ構造を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | models.ts | `packages/loot-core/types/models/index.ts` | RuleConditionEntity, TimeFrame等の型定義 |

#### Step 2: スプレッドシート（データ取得層）を理解する

データ取得と集計ロジックを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | cash-flow-spreadsheet.tsx | `packages/desktop-client/src/components/reports/spreadsheets/cash-flow-spreadsheet.tsx` | キャッシュフローのデータ取得ロジック |

**主要処理フロー**:
- **18-72行目**: `simpleCashFlow` - 簡易収支取得（ダッシュボードカード用）
- **74-146行目**: `cashFlowByDate` - 日別/月別の詳細収支取得
- **148-290行目**: `recalculate` - グラフデータへの変換処理

**読解のコツ**:
- `q('transactions')` はAQLクエリビルダー
- `runAll` は複数クエリの並列実行ヘルパー
- `indexCashFlow` は日付をキーにしたインデックス作成

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

レポート画面のUIを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | CashFlow.tsx | `packages/desktop-client/src/components/reports/reports/CashFlow.tsx` | キャッシュフローレポート画面 |

**主要処理フロー**:
- **48-52行目**: `defaultTimeFrame` - デフォルトの期間設定
- **54-66行目**: `CashFlow` - メインコンポーネント（ウィジェット読み込み）
- **72-403行目**: `CashFlowInner` - 実際のレポート表示ロジック
- **111-123行目**: `params` - スプレッドシートパラメータ生成
- **124行目**: `useReport` - レポートデータ取得フック

#### Step 4: グラフコンポーネントを理解する

グラフ描画の詳細を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | CashFlowGraph.tsx | `packages/desktop-client/src/components/reports/graphs/CashFlowGraph.tsx` | キャッシュフローグラフ描画 |

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

```
CashFlow (CashFlow.tsx:54)
    │
    ├─ useWidget() ───▶ ウィジェット設定取得
    │
    └─ CashFlowInner (CashFlow.tsx:72)
           │
           ├─ useRuleConditionFilters() ───▶ フィルタ状態管理
           │
           ├─ cashFlowByDate() (cash-flow-spreadsheet.tsx:74)
           │      │
           │      ├─ send('make-filters-from-conditions', ...)
           │      │
           │      ├─ q('transactions').filter(...).calculate(...)  [初期残高]
           │      │
           │      ├─ q('transactions').filter(...).groupBy(...).select(...)  [収入]
           │      │
           │      ├─ q('transactions').filter(...).groupBy(...).select(...)  [支出]
           │      │
           │      └─ recalculate() ───▶ グラフデータ変換
           │
           ├─ useReport('cash_flow', params) ───▶ データ取得
           │
           └─ CashFlowGraph (CashFlowGraph.tsx)
                  └─ Recharts描画
```

### データフロー図

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

期間設定 ────────┐
                 │
フィルタ条件 ───┼──▶ cashFlowByDate() ──▶ AQLクエリ実行
                 │          │
                 │          ├── 初期残高取得
                 │          │
                 │          ├── 収入集計
                 │          │
                 │          ├── 支出集計
                 │          │
                 │          └── recalculate() ──▶ graphData
                 │                                    │
                 │                                    ▼
                 └────────────────────────────▶ CashFlowGraph
                                                     │
                                                     ▼
                                              グラフ表示
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| CashFlow.tsx | `packages/desktop-client/src/components/reports/reports/CashFlow.tsx` | ソース | キャッシュフローレポート画面 |
| cash-flow-spreadsheet.tsx | `packages/desktop-client/src/components/reports/spreadsheets/cash-flow-spreadsheet.tsx` | ソース | データ取得・集計ロジック |
| CashFlowGraph.tsx | `packages/desktop-client/src/components/reports/graphs/CashFlowGraph.tsx` | ソース | グラフ描画コンポーネント |
| NetWorth.tsx | `packages/desktop-client/src/components/reports/reports/NetWorth.tsx` | ソース | 純資産レポート画面 |
| net-worth-spreadsheet.ts | `packages/desktop-client/src/components/reports/spreadsheets/net-worth-spreadsheet.ts` | ソース | 純資産データ取得 |
| useReport.ts | `packages/desktop-client/src/components/reports/useReport.ts` | ソース | レポートデータ取得フック |
| util.ts | `packages/desktop-client/src/components/reports/util.ts` | ソース | レポート用ユーティリティ |
| Header.tsx | `packages/desktop-client/src/components/reports/Header.tsx` | ソース | レポートヘッダー（期間選択等） |
