# 画面設計書 38-クロスオーバーレポート画面

## 概要

本ドキュメントは、Actual Budget アプリケーションのクロスオーバーレポート画面の設計仕様を記述したものである。

### 本画面の処理概要

クロスオーバーレポート画面は、投資収入と生活費が交差する「リタイアメントポイント」を予測・可視化する画面である。安全引出率（SWR: Safe Withdrawal Rate）に基づいて、選択した口座の投資収入が選択した支出カテゴリの費用を上回る時期を計算し、グラフで表示する。

**業務上の目的・背景**：FIRE（Financial Independence, Retire Early）やリタイアメント計画において、「いつ経済的自立を達成できるか」を可視化することは重要である。この画面では、過去の支出パターンと投資口座の成長率から将来を予測し、投資収入が生活費を超える「クロスオーバーポイント」を算出する。ユーザーは安全引出率、期待リターン、月次積立額などのパラメータを調整して、様々なシナリオをシミュレーションできる。

**画面へのアクセス方法**：サイドバーの「レポート」メニューをクリック後、レポートダッシュボードから「クロスオーバー」ウィジェットを選択、または直接 `/reports/crossover/:id` URLにアクセスする。

**主要な操作・処理内容**：
1. 期間選択：分析対象の開始月と終了月を選択
2. 支出カテゴリ選択：リタイア後の生活費に含めるカテゴリを選択
3. 収入口座選択：投資収入の計算に使用する口座を選択
4. 安全引出率設定：年間引出率（デフォルト4%）を設定
5. 支出予測タイプ選択：Hampel/Median/Meanから選択
6. 目標収入率設定：支出に対する目標収入の割合を設定
7. カスタム成長予測設定：期待リターンと月次積立額をカスタム設定
8. ウィジェット保存：設定をダッシュボードウィジェットとして保存

**画面遷移**：レポートダッシュボード画面（`/reports`）から遷移する。モバイル画面幅では戻るボタンでレポートダッシュボードへ戻る。

**権限による表示制御**：特に権限による制御はない。予算ファイルがロードされていることが前提条件となる。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 33 | 収支レポート | 主機能 | クロスオーバーポイントの計算・表示 |

## 画面種別

詳細・分析画面

## URL/ルーティング

- `/reports/crossover/:id` - ダッシュボードウィジェットID指定

## 入出力項目

| 項目名 | 入力/出力 | データ型 | 必須 | 説明 |
|--------|----------|---------|------|------|
| 開始月 | 入力 | string | Yes | 分析期間の開始月 |
| 終了月 | 入力 | string | Yes | 分析期間の終了月 |
| 支出カテゴリ | 入力 | CategoryEntity[] | Yes | リタイア後の生活費カテゴリ |
| 収入口座 | 入力 | string[] | Yes | 投資収入計算に使用する口座ID |
| 安全引出率 | 入力 | number | Yes | 年間引出率（デフォルト0.04=4%） |
| 支出予測タイプ | 入力 | string | Yes | 'hampel', 'median', 'mean' |
| 目標収入率 | 入力 | number | Yes | 支出に対する目標収入割合（デフォルト1.0=100%） |
| カスタム成長使用 | 入力 | boolean | No | カスタム成長予測を使用するか |
| 期待リターン | 入力 | number | No | 年間期待リターン率 |
| 月次積立額 | 入力 | number | No | 月次積立金額 |
| 非表示カテゴリ表示 | 入力 | boolean | No | 非表示カテゴリを表示するか |

## 表示項目

| 項目名 | データ型 | 説明 |
|--------|---------|------|
| リタイアまでの年数 | number | クロスオーバーポイントまでの年数 |
| 目標月次収入 | number | 月次目標収入額 |
| 目標生涯貯蓄 | number | 必要な総貯蓄額（ネストエッグ） |
| クロスオーバーグラフ | component | 投資収入と支出の推移グラフ |
| サイドバー設定パネル | component | 各種設定項目の入力UI |

## イベント仕様

### 1-期間変更

ヘッダーの期間選択を変更すると、`onChangeDates`関数が呼び出される。現在月は含まれないよう、sliding-windowおよびfullモードでは1ヶ月前に調整される。

### 2-支出カテゴリ変更

CategorySelectorで支出カテゴリを選択・解除すると、`setSelectedExpenseCategories`により状態が更新され、スプレッドシートが再計算される。

### 3-収入口座変更

AccountSelectorで口座を選択・解除すると、`setSelectedIncomeAccountIds`により状態が更新され、スプレッドシートが再計算される。

### 4-安全引出率変更

SWR入力フィールドの値を変更すると、`setSwr`により状態が更新される。値は0-100の範囲で入力され、内部では0.00-1.00に変換される。

### 5-支出予測タイプ変更

「Expense Projection Type」ドロップダウンで'hampel', 'median', 'mean'のいずれかを選択すると、`setProjectionType`により状態が更新される。

### 6-目標収入率変更

「Target Income」入力フィールドの値を変更すると、`setExpenseAdjustmentFactor`により状態が更新される。100%を基準とし、100%未満でリタイア後の支出減少、100%超で支出増加を想定。

### 7-カスタム成長切り替え

「Use custom growth projections」チェックボックスを切り替えると、`setUseCustomGrowth`により状態が更新される。有効化時、期待リターンがnullの場合はデフォルト6%が設定される。

### 8-期待リターン変更

カスタム成長有効時、「Expected return」入力フィールドの値を変更すると、`setEstimatedReturn`により状態が更新される。

### 9-月次積立額変更

カスタム成長有効時、「Expected monthly contribution」入力フィールドの値を変更すると、`setExpectedContribution`により状態が更新される。

### 10-非表示カテゴリ表示切り替え

「Show hidden」/「Hide hidden」ボタンを押すと、`setShowHiddenCategories`により状態が切り替わる。

### 11-ウィジェット名変更

ヘッダーのタイトルをクリックして編集すると、`onSaveWidgetName`関数が実行され、ウィジェット名が更新される。

### 12-ウィジェット保存

「Save widget」ボタン押下時、`onSaveWidget`関数が実行され、全設定がウィジェットのメタデータとして保存される。

## データベース更新仕様

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| ウィジェット保存 | dashboard_widgets | UPDATE | ウィジェットのmeta情報を更新 |
| データ取得 | transactions | SELECT | 取引データを取得して分析 |
| 最古取引取得 | transactions | SELECT | 最古の取引日を取得 |

### テーブル別更新項目詳細

#### dashboard_widgets

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | meta | JSON（expenseCategoryIds, incomeAccountIds, safeWithdrawalRate, estimatedReturn, expectedContribution, projectionType, expenseAdjustmentFactor, showHiddenCategories, timeFrame, name） | ウィジェット保存時 |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|--------------|---------|
| MSG_WIDGET_SAVED | 成功 | Dashboard widget successfully saved. | ウィジェット保存成功時 |
| MSG_NO_WIDGET | エラー | Save failed: No widget found to save. | ウィジェットなしで保存試行時 |

## 例外処理

| 例外条件 | 処理内容 |
|---------|---------|
| ウィジェットが存在しない | 保存時にエラー通知を表示、処理中断 |
| 口座が存在しない | ローディング表示を継続 |
| カテゴリが存在しない | ローディング表示を継続 |
| 期間が未初期化 | ローディング表示を継続 |
| クロスオーバーポイント算出不可 | 「N/A」と表示 |

## 備考

- デフォルトの期間は過去120ヶ月（10年）から前月まで
- 現在月は完全なデータがないため、分析対象から除外される
- Hampel Filtered Median：外れ値を除去してから中央値を計算する手法
- 安全引出率（SWR）4%は一般的なFIRE計算で使用される値（4%ルール）
- 投資収入 = 総貯蓄 x (安全引出率 / 12)
- 必要貯蓄額（ネストエッグ） = 年間支出 / 安全引出率
- カスタム成長無効時は、過去の口座残高推移から成長率を自動計算
- プライバシーフィルタにより金額表示をマスク可能
- ツールチップで各設定項目の詳細説明を表示

---

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

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

### 推奨読解順序

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

まず、クロスオーバーレポートで使用されるデータ構造を理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | models.ts | `loot-core/types/models` | `CrossoverWidget`, `TimeFrame`型の定義を確認 |
| 1-2 | Crossover.tsx | `packages/desktop-client/src/components/reports/reports/Crossover.tsx` | `CrossoverData`型（行55-76）の構造を理解 |

**読解のコツ**: CrossoverDataのgraphData配列に含まれるinvestmentIncome, expenses, nestEgg, adjustedExpenses, isProjectionの各フィールドの意味を把握する。

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

処理の起点となるメインコンポーネントを特定する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Crossover.tsx | `packages/desktop-client/src/components/reports/reports/Crossover.tsx` | `Crossover`関数コンポーネントがエントリーポイント |

**主要処理フロー**:
1. **84-96行**: `Crossover`関数 - useWidgetでウィジェットデータ取得
2. **100-1163行**: `CrossoverInner`関数 - メインのレンダリングロジック
3. **103-110行**: useAccounts, useCategoriesでマスタデータ取得
4. **112-141行**: 状態の初期化
5. **143-186行**: ウィジェットメタデータからの初期化処理
6. **188-216行**: 最古/最新取引日の取得
7. **339-376行**: params関数 - スプレッドシートパラメータ生成
8. **378行**: useReportフック呼び出し

#### Step 3: 計算ロジックを理解する

クロスオーバーポイントの計算処理を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | crossover-spreadsheet.ts | `packages/desktop-client/src/components/reports/spreadsheets/crossover-spreadsheet.ts` | 計算ロジック本体 |

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

グラフとサイドバーの実装を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | CrossoverGraph.tsx | `packages/desktop-client/src/components/reports/graphs/CrossoverGraph.tsx` | グラフ描画 |
| 4-2 | CategorySelector.tsx | `packages/desktop-client/src/components/reports/CategorySelector.tsx` | カテゴリ選択UI |
| 4-3 | AccountSelector.tsx | `packages/desktop-client/src/components/reports/AccountSelector.tsx` | 口座選択UI |

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

```
Crossover (エントリーポイント)
    │
    ├─ useWidget (ウィジェットデータ取得)
    │
    └─ CrossoverInner (メインコンポーネント)
           │
           ├─ useAccounts (口座一覧取得)
           │
           ├─ useCategories (カテゴリ一覧取得)
           │
           ├─ useFormat (書式設定)
           │
           ├─ send('get-earliest-transaction')
           │
           ├─ createCrossoverSpreadsheet (計算パラメータ)
           │      └─ useReport (データ取得フック)
           │
           ├─ Header (期間選択UI)
           │
           ├─ サイドバー（設定パネル）
           │      ├─ CategorySelector (支出カテゴリ選択)
           │      ├─ AccountSelector (収入口座選択)
           │      ├─ Input (SWR入力)
           │      ├─ Select (予測タイプ選択)
           │      ├─ Input (目標収入率)
           │      ├─ Checkbox (カスタム成長切り替え)
           │      ├─ Input (期待リターン)
           │      └─ Input (月次積立額)
           │
           ├─ ヘッダー統計
           │      ├─ Years to Retire
           │      ├─ Target Monthly Income
           │      └─ Target Life Savings
           │
           ├─ CrossoverGraph (グラフ表示)
           │
           └─ 説明パネル
```

### データフロー図

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

URLパラメータ ─────────▶ useWidget ─────────────────▶ ウィジェットメタデータ
                              │
                              ├──▶ meta.expenseCategoryIds
                              ├──▶ meta.incomeAccountIds
                              ├──▶ meta.safeWithdrawalRate
                              ├──▶ meta.estimatedReturn
                              ├──▶ meta.expectedContribution
                              ├──▶ meta.projectionType
                              ├──▶ meta.expenseAdjustmentFactor
                              └──▶ meta.timeFrame

useAccounts ──────────────▶ accounts[]
                              │
useCategories ────────────▶ categories.list[]
                              │
                              ▼
                    状態初期化
                              │
send('get-earliest-transaction')
                              │
                              ▼
                    期間計算（calculateTimeRange）
                              │
                              ▼
設定変更 ─────────────────▶ 各種状態
                              │
                              ▼
                    createCrossoverSpreadsheet
                              │
                              ├──▶ start, end
                              ├──▶ expenseCategoryIds
                              ├──▶ incomeAccountIds
                              ├──▶ safeWithdrawalRate
                              ├──▶ estimatedReturn
                              ├──▶ expectedContribution
                              ├──▶ projectionType
                              └──▶ expenseAdjustmentFactor
                              │
                              ▼
                        useReport
                              │
                              ▼
取引データ(DB) ──────▶ クロスオーバー計算 ──────────▶ CrossoverData
                                                        │ graphData
                                                        │ lastKnownBalance
                                                        │ lastKnownMonthlyIncome
                                                        │ lastKnownMonthlyExpenses
                                                        │ historicalReturn
                                                        │ yearsToRetire
                                                        │ targetMonthlyIncome
                                                        │ targetNestEgg
                              │
                              ▼
                    CrossoverGraph
                              │
                              ▼
                        画面表示
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Crossover.tsx | `packages/desktop-client/src/components/reports/reports/Crossover.tsx` | ソース | メインコンポーネント |
| crossover-spreadsheet.ts | `packages/desktop-client/src/components/reports/spreadsheets/crossover-spreadsheet.ts` | ソース | 計算ロジック |
| CrossoverGraph.tsx | `packages/desktop-client/src/components/reports/graphs/CrossoverGraph.tsx` | ソース | グラフ描画 |
| Header.tsx | `packages/desktop-client/src/components/reports/Header.tsx` | ソース | 期間選択ヘッダー |
| CategorySelector.tsx | `packages/desktop-client/src/components/reports/CategorySelector.tsx` | ソース | カテゴリ選択UI |
| AccountSelector.tsx | `packages/desktop-client/src/components/reports/AccountSelector.tsx` | ソース | 口座選択UI |
| PrivacyFilter.tsx | `packages/desktop-client/src/components/PrivacyFilter.tsx` | ソース | プライバシーフィルタ |
| FinancialText.tsx | `packages/desktop-client/src/components/FinancialText.tsx` | ソース | 金額表示 |
