# 帳票設計書 2-Cash Flow（キャッシュフロー）レポート

## 概要

本ドキュメントは、Actual Budgetアプリケーションにおける「Cash Flow（キャッシュフロー）レポート」の設計仕様を記述したものである。予算口座のキャッシュフローを分析し、収入・支出・振替の内訳を時系列で可視化するレポート機能について詳細に解説する。

### 本帳票の処理概要

Cash Flow（キャッシュフロー）レポートは、予算口座（オンバジェット口座）のキャッシュフローを分析するレポートである。収入、支出、口座間振替の内訳を時系列で表示し、残高推移もグラフ化する。日次または月次での表示切り替えに対応している。

**業務上の目的・背景**：家計のキャッシュフローを把握することは、日々の支出管理と将来の資金計画において極めて重要である。本レポートは、ユーザーが「手持ちの現金」がどのように増減しているかを可視化することで、支出パターンの分析や予算超過の早期発見を支援する。オフバジェット口座（投資口座等）を除外することで、日常的に使用可能な資金の流れに焦点を当てている。

**帳票の利用シーン**：月次の家計レビュー時、支出パターンの分析時、収入と支出のバランス確認時、振替による資金移動の確認時など、日常的なキャッシュフロー管理の場面で利用される。

**主要な出力内容**：
1. 収入・支出・振替の時系列推移グラフ
2. 残高推移ライン（オプション表示）
3. 合計収入額・合計支出額・合計振替額
4. 期間内の純変化額

**帳票の出力タイミング**：ユーザーがレポート画面から「Cash Flow」を選択した際、またはダッシュボードウィジェットとして設定された際にリアルタイムで生成される。

**帳票の利用者**：アプリケーションの全ユーザー（個人の家計管理者）

## 帳票種別

集計分析グラフレポート

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| R-002 | Cash Flowレポート | `/reports/cash-flow` | レポートメニューから選択 |
| R-002-W | Cash Flowウィジェット | `/reports/cash-flow/:id` | ダッシュボードウィジェットクリック |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | HTML（画面表示）/ SVGグラフ |
| 表示形式 | インタラクティブなグラフ表示 |
| レスポンシブ対応 | 有（モバイル/デスクトップ） |
| データ更新 | リアルタイム（パラメータ変更時に再計算） |

### 表示固有設定

| 項目 | 内容 |
|-----|------|
| グラフライブラリ | Recharts (VictoryChart) |
| グラフタイプ | BarChart + LineChart (残高) |
| カラーテーマ | 収入:緑系、支出:赤系、振替:グレー系 |

## 帳票レイアウト

### レイアウト概要

レポートは上部のヘッダー部、中央のサマリー部、下部のグラフ表示部の3つのセクションで構成される。

```
+------------------------------------------+
|              ページヘッダー                |
|  [タイトル]                    [保存ボタン] |
+------------------------------------------+
|              フィルターヘッダー              |
| [期間選択]           [残高表示] [フィルター] |
+------------------------------------------+
|              サマリー表示（右寄せ）          |
|                         Income: ¥XXXX    |
|                        Expenses: ¥XXXX   |
|                       Transfers: ¥XXXX   |
|                          Change: +¥XXXX  |
+------------------------------------------+
|              グラフ表示部                  |
|  [棒グラフ + 残高ライン]                    |
+------------------------------------------+
|              説明文                       |
+------------------------------------------+
```

### ヘッダー部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | タイトル | レポート名（編集可能） | widget.meta.name | テキスト |
| 2 | 期間選択 | 開始日・終了日の選択 | allMonths | ドロップダウン |
| 3 | 残高表示切替 | 残高ラインの表示/非表示 | showBalance | ボタン |
| 4 | フィルター | 条件フィルター | conditions | フィルターボタン |

### 明細部（サマリー）

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | Income | 合計収入額 | totalIncome | 通貨形式 |
| 2 | Expenses | 合計支出額 | totalExpenses | 通貨形式 |
| 3 | Transfers | 合計振替額 | totalTransfers | 通貨形式 |
| 4 | Change | 純変化額 | 収入+支出+振替 | 通貨形式（色分け） |

### グラフ部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | X軸 | 時間軸（日次/月次） | graphData.x | 日付ラベル |
| 2 | 収入バー | 収入額の棒グラフ | graphData.income | 緑色バー |
| 3 | 支出バー | 支出額の棒グラフ | graphData.expenses | 赤色バー |
| 4 | 振替バー | 振替額の棒グラフ | graphData.transfers | グレーバー |
| 5 | 残高ライン | 累積残高の推移 | graphData.balances | ライン |

### フッター部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | 説明文 | キャッシュフローの計算方法説明 | 固定テキスト | パラグラフ |

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| 期間（開始） | 集計開始月 | Yes |
| 期間（終了） | 集計終了月 | Yes |
| オフバジェット除外 | offbudget=falseで固定 | Yes（自動） |
| 振替除外 | transfer_acct=nullで非振替のみ | No（計算で区別） |
| カスタムフィルター | ルール条件によるフィルタリング | No |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| 1 | 日付 | 昇順 |

### 改ページ条件

該当なし（単一画面表示）

## データベース参照仕様

### 参照テーブル一覧

| テーブル名 | 用途 | 結合条件 |
|-----------|------|---------|
| transactions | トランザクションデータ | - |
| accounts | 口座マスタ（offbudgetフラグ参照） | account.id |
| payees | 支払先マスタ（振替判定） | payee.transfer_acct |

### テーブル別参照項目詳細

#### transactions

| 参照項目（カラム名） | 帳票項目との対応 | 取得条件 | 備考 |
|-------------------|----------------|---------|------|
| date | グラフX軸 | date >= start AND date <= end | 期間内のみ |
| amount | 金額 | - | 正:収入、負:支出 |
| account | 口座参照 | account.offbudget = false | 予算口座のみ |
| payee | 振替判定 | - | transfer_acct有無で判定 |

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| 開始時残高 | SUM(amount) WHERE date < startDate AND offbudget = false | なし | 期間前の残高 |
| 収入 | SUM(amount) WHERE amount > 0 AND isTransfer = false | なし | 正の金額で振替以外 |
| 支出 | SUM(amount) WHERE amount < 0 AND isTransfer = false | なし | 負の金額で振替以外 |
| 振替（入金） | SUM(amount) WHERE amount > 0 AND isTransfer = true | なし | 正の金額で振替 |
| 振替（出金） | SUM(amount) WHERE amount < 0 AND isTransfer = true | なし | 負の金額で振替 |
| 累積残高 | 開始時残高 + 累積(収入 + 支出 + 振替) | なし | 各日/月の残高 |
| 純変化額 | 合計収入 + 合計支出 + 合計振替 | なし | 期間内の増減 |

### 表示モード判定

| 項目名 | 判定式 | 説明 |
|-------|-------|-----|
| isConcise | differenceInCalendarDays(end, start) > 31 * 3 | 93日超で月次表示、以下で日次表示 |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[レポート画面表示] --> B[パラメータ初期化]
    B --> C[最古/最新トランザクション取得]
    C --> D[期間範囲設定]
    D --> E[表示モード判定]
    E --> F[cashFlowByDate呼び出し]
    F --> G[開始時残高クエリ]
    F --> H[収入クエリ]
    F --> I[支出クエリ]
    G --> J[runAll並列実行]
    H --> J
    I --> J
    J --> K[recalculate]
    K --> L[グラフデータ生成]
    L --> M[グラフ描画]
    M --> N{パラメータ変更?}
    N -->|Yes| E
    N -->|No| O[表示完了]
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| データなし | トランザクションが存在しない | 該当するデータがありません | 期間やフィルターを変更 |
| ウィジェット未設定 | widget未定義時の保存 | No widget that could be saved. | ウィジェット経由でアクセス |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | 数千〜数万件のトランザクション |
| 目標出力時間 | 3秒以内 |
| 並列処理 | runAllで3クエリを並列実行 |

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

- プライバシーフィルター（PrivacyFilter）による金額のマスキング機能
- ローカルデータストレージ（サーバーへのデータ送信なし）

## 備考

- 予算口座（offbudget=false）のみを対象とし、投資口座等は含まない
- 振替は収入・支出とは別に集計され、口座間の資金移動を追跡可能
- 93日を超える期間では自動的に月次表示に切り替わる

---

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

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

### 推奨読解順序

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

まず、レポートで使用されるデータ型とウィジェット定義を理解する。

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

**読解のコツ**: TypeScriptの型定義から、レポートが扱うデータの形状を把握する。

#### Step 2: エントリーポイントを理解する

処理の起点となるReactコンポーネントを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | CashFlow.tsx | `packages/desktop-client/src/components/reports/reports/CashFlow.tsx` | メインコンポーネントの構造 |

**主要処理フロー**:
1. **行54-66**: CashFlow関数コンポーネント - ウィジェットデータの読み込み
2. **行72-88**: CashFlowInner - フィルター条件の初期化
3. **行103-109**: isConcise状態 - 日次/月次表示の判定
4. **行111-123**: params - スプレッドシートパラメータのメモ化
5. **行124**: useReport - データ取得フック呼び出し
6. **行376-380**: CashFlowGraph - グラフコンポーネントへのデータ渡し

#### Step 3: スプレッドシート（データ集計ロジック）を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | cash-flow-spreadsheet.tsx | `packages/desktop-client/src/components/reports/spreadsheets/cash-flow-spreadsheet.tsx` | データ集計ロジック |

**主要処理フロー**:
- **行18-72**: simpleCashFlow関数 - シンプルな収入/支出合計計算
- **行74-146**: cashFlowByDate関数 - 日次/月次のキャッシュフロー計算
- **行97-127**: makeQuery関数 - 集計クエリ生成（isConciseで分岐）
- **行129-145**: runAll - 3つのクエリを並列実行（開始残高、収入、支出）
- **行148-290**: recalculate関数 - グラフデータへの変換

#### Step 4: グラフ描画を理解する

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

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

```
CashFlow.tsx
    |
    +-- useWidget<CashFlowWidget>() --> ウィジェットデータ取得
    |
    +-- CashFlowInner
            |
            +-- useRuleConditionFilters() --> フィルター条件管理
            |
            +-- cashFlowByDate() --> cash-flow-spreadsheet.tsx
            |       |
            |       +-- send('make-filters-from-conditions') --> フィルター生成
            |       |
            |       +-- runAll([開始残高, 収入, 支出]) --> 並列クエリ実行
            |       |
            |       +-- recalculate() --> グラフデータ変換
            |
            +-- useReport('cash_flow', params) --> データ取得・キャッシュ
            |
            +-- CashFlowGraph --> グラフ描画
```

### データフロー図

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

トランザクション ────+
                    |
accounts            +──> cashFlowByDate ──────────────────+
(offbudget判定)     |         |                           |
                    |         v                           v
payees              +    makeQuery (isConcise分岐)    graphData
(振替判定)                    |                           |
                              v                           v
フィルター条件 ──────+     runAll                    [CashFlowGraph]
                    |    (並列実行)                       |
期間設定 ───────────+         |                           v
                              v                    収入/支出/振替バー
                         recalculate              + 残高ライン
                              |
                              v
                    indexCashFlow (振替区分)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| 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` | ソース | グラフ描画 |
| Header.tsx | `packages/desktop-client/src/components/reports/Header.tsx` | ソース | ヘッダーUI |
| util.ts | `packages/desktop-client/src/components/reports/util.ts` | ソース | ユーティリティ（indexCashFlow, runAll） |
| useReport.ts | `packages/desktop-client/src/components/reports/useReport.ts` | ソース | レポートデータ取得フック |
| reportRanges.ts | `packages/desktop-client/src/components/reports/reportRanges.ts` | ソース | 期間計算ユーティリティ |
| months.ts | `loot-core/shared/months.ts` | ソース | 日付操作ユーティリティ |
