# 帳票設計書 3-Spending（月間支出）レポート

## 概要

本ドキュメントは、Actual Budgetアプリケーションにおける「Spending（月間支出）レポート」の設計仕様を記述したものである。月間支出を分析し、特定月の支出を過去の月、予算、または過去3ヶ月の平均と比較可能なレポート機能について詳細に解説する。

### 本帳票の処理概要

Spending（月間支出）レポートは、特定月の支出状況を分析し、様々な基準と比較するためのレポートである。比較対象として、他の特定月、予算額、または過去3ヶ月の平均を選択できる。日ごとの累積支出をグラフで可視化し、月の進捗に応じた支出ペースを確認できる。

**業務上の目的・背景**：月間の支出管理において、現在の支出ペースが過去と比較してどうなのか、予算内に収まっているかを把握することは重要である。本レポートは、支出の比較分析を通じて、月末までの支出見込みを予測し、必要に応じて支出抑制の判断を支援する。特にMTD（Month-To-Date）形式での表示により、月途中での現状把握が容易になる。

**帳票の利用シーン**：月中の支出進捗確認時、月間予算との比較時、支出パターンの季節変動分析時、過去月との支出傾向比較時など、月単位での支出管理場面で利用される。

**主要な出力内容**：
1. 日ごとの累積支出グラフ（比較対象とのオーバーレイ表示）
2. 当月の支出累計額
3. 比較対象月/予算/平均の支出額
4. 予算額との差異（予算モード時）

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

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

## 帳票種別

比較分析グラフレポート

## 利用画面

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

## 出力形式

### 基本仕様

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

### 表示固有設定

| 項目 | 内容 |
|-----|------|
| グラフライブラリ | Recharts |
| グラフタイプ | AreaChart (累積支出) |
| カラーテーマ | 当月:緑系、比較対象:グレー系 |

## 帳票レイアウト

### レイアウト概要

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

```
+------------------------------------------+
|              ページヘッダー                |
|  [タイトル]                    [保存ボタン] |
+------------------------------------------+
|              コントロール部                |
| [Live/Static] [Compare ▼] to [▼]         |
| [Single month] [Budgeted] [Average]      |
| [Filter]                      [Save]     |
+------------------------------------------+
|              凡例                        |
| ● 当月 (緑)    ● 比較対象 (グレー)        |
+------------------------------------------+
|              サマリー（右寄せ）            |
|              Spent MMM YYYY MTD: ¥XXXX   |
|              Spent MMM YYYY: ¥XXXX       |
|              Budgeted MTD: ¥XXXX         |
+------------------------------------------+
|              グラフ表示部                  |
|  [累積支出グラフ]                          |
+------------------------------------------+
|              説明文（平均モード時）         |
+------------------------------------------+
```

### ヘッダー部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | タイトル | レポート名（編集可能） | widget.meta.name | テキスト |
| 2 | Live/Static切替 | ライブ更新/固定表示 | isLive | ボタン |

### コントロール部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | Compare月 | 分析対象月 | compare | ドロップダウン |
| 2 | To月/ラベル | 比較対象月またはモードラベル | compareTo | ドロップダウン |
| 3 | モード選択 | Single month/Budgeted/Average | reportMode | ボタングループ |
| 4 | フィルター | 条件フィルター | conditions | フィルターボタン |

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

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | 当月支出MTD | 当月の累計支出（月途中の場合） | intervalData[todayDay].compare | 通貨形式 |
| 2 | 比較月支出 | 比較対象月の累計支出 | intervalData[todayDay].compareTo | 通貨形式 |
| 3 | 予算MTD | 予算額（予算モード時） | intervalData[todayDay].budget | 通貨形式 |
| 4 | 平均支出 | 過去3ヶ月平均（平均モード時） | intervalData[todayDay].average | 通貨形式 |

### グラフ部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | X軸 | 日（1-28/29/30/31） | intervalData[n] | 日付ラベル |
| 2 | Y軸 | 累積支出額 | cumulative | 通貨形式 |
| 3 | 当月ライン | 分析対象月の累積支出 | compare | 緑色エリア |
| 4 | 比較ライン | 比較対象の累積支出 | compareTo/budget/average | グレーエリア |

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| 分析対象月 | 分析する月（compare） | Yes |
| 比較対象月 | 比較する月（compareTo） | モードによる |
| レポートモード | single-month/budget/average | Yes |
| カスタムフィルター | ルール条件によるフィルタリング（日付除外） | No |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| 1 | 日 | 昇順（1日〜月末） |

### 改ページ条件

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

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

### 参照テーブル一覧

| テーブル名 | 用途 | 結合条件 |
|-----------|------|---------|
| transactions | トランザクションデータ | - |
| categories | カテゴリマスタ（予算参照） | category.id |

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

#### transactions

| 参照項目（カラム名） | 帳票項目との対応 | 取得条件 | 備考 |
|-------------------|----------------|---------|------|
| date | 日別集計 | 対象月内 | 日単位で集計 |
| amount | 支出額 | amount < 0 | 支出（負の値）のみ |
| category | カテゴリ別集計 | フィルター条件 | オプション |

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| 日別支出 | SUM(amount) WHERE date = 対象日 AND amount < 0 | なし | 各日の支出合計 |
| 累積支出 | 1日〜N日の日別支出の累積合計 | なし | グラフ表示用 |
| 月間平均 | (過去3ヶ月の累積支出) / 3 | なし | 平均モード用 |
| 予算MTD | 予算額 * (経過日数/月日数) | 四捨五入 | 予算モード用 |
| todayDay | 当月:現在日-1、過去月:27 | なし | 最大27（28日目以降は27） |

### レポートモード判定

| モード | 判定条件 | 比較対象 |
|-------|---------|---------|
| single-month | reportMode === 'single-month' | 選択した比較月 |
| budget | reportMode === 'budget' | 予算額 |
| average | reportMode === 'average' | 過去3ヶ月平均 |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[レポート画面表示] --> B[パラメータ初期化]
    B --> C[最古/最新トランザクション取得]
    C --> D[月リスト生成]
    D --> E[createSpendingSpreadsheet呼び出し]
    E --> F[対象月の日別支出集計]
    E --> G[比較月の日別支出集計]
    E --> H[予算データ取得]
    E --> I[過去3ヶ月平均計算]
    F --> J[累積計算]
    G --> J
    H --> J
    I --> J
    J --> K[intervalData生成]
    K --> L[グラフ描画]
    L --> M{パラメータ変更?}
    M -->|Yes| E
    M -->|No| N[表示完了]
```

## エラー処理

### エラーケース一覧

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

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | 数百〜数千件/月のトランザクション |
| 目標出力時間 | 2秒以内 |
| キャッシュ | useMemoによるメモ化 |

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

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

## 備考

- 日付フィルターは除外される（exclude: ['date']）
- 28日以降のデータは27日目にマッピングされる（月の日数差異を吸収）
- 平均計算は過去3ヶ月のデータが必要（showAverageで条件判定）
- Live/Staticモードでウィジェットの更新挙動を制御

---

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

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

### 推奨読解順序

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

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

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

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

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

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

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

**主要処理フロー**:
1. **行52-64**: Spending関数コンポーネント - ウィジェットデータの読み込み
2. **行70-86**: SpendingInternal - フィルター条件の初期化
3. **行91-96**: 比較対象の初期化 - calculateSpendingReportTimeRange
4. **行99**: reportMode - single-month/budget/averageの選択
5. **行140-149**: getGraphData - スプレッドシートパラメータのメモ化
6. **行151**: useReport - データ取得フック呼び出し
7. **行186-204**: 表示条件判定 - showAverage, showCompareTo, showCompare
8. **行627-634**: SpendingGraph - グラフコンポーネントへのデータ渡し

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

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

**主要処理フロー**:
- **createSpendingSpreadsheet関数**: 支出データの集計
- **compare/compareTo**: 2つの月の比較データ生成
- **budget**: 予算データの取得と日割り計算
- **average**: 過去3ヶ月の平均計算
- **intervalData**: 28日分のデータ配列生成

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

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

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

```
Spending.tsx
    |
    +-- useWidget<SpendingWidget>() --> ウィジェットデータ取得
    |
    +-- SpendingInternal
            |
            +-- useRuleConditionFilters() --> フィルター条件管理
            |
            +-- calculateSpendingReportTimeRange() --> 期間計算
            |
            +-- createSpendingSpreadsheet() --> spending-spreadsheet.ts
            |       |
            |       +-- 対象月の日別支出クエリ
            |       |
            |       +-- 比較月の日別支出クエリ
            |       |
            |       +-- 予算データクエリ
            |       |
            |       +-- 過去3ヶ月平均計算
            |
            +-- useReport('default', getGraphData) --> データ取得・キャッシュ
            |
            +-- SpendingGraph --> グラフ描画
```

### データフロー図

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

トランザクション ────+
                    |
compare月 ──────────+──> createSpendingSpreadsheet ──> intervalData
                    |         |                           |
compareTo月 ────────+         |                           |
                    |         v                           v
フィルター条件 ──────+    日別支出集計                [SpendingGraph]
                    |         |                           |
予算データ ─────────+         v                           v
                    |    累積計算                   累積支出エリアグラフ
reportMode ─────────+         |                    (compare vs compareTo/
                              v                     budget/average)
                         intervalData[0-27]
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Spending.tsx | `packages/desktop-client/src/components/reports/reports/Spending.tsx` | ソース | メインコンポーネント |
| spending-spreadsheet.ts | `packages/desktop-client/src/components/reports/spreadsheets/spending-spreadsheet.ts` | ソース | データ集計ロジック |
| SpendingGraph.tsx | `packages/desktop-client/src/components/reports/graphs/SpendingGraph.tsx` | ソース | グラフ描画 |
| LegendItem.tsx | `packages/desktop-client/src/components/reports/LegendItem.tsx` | ソース | 凡例表示 |
| ModeButton.tsx | `packages/desktop-client/src/components/reports/ModeButton.tsx` | ソース | モード切替ボタン |
| reportRanges.ts | `packages/desktop-client/src/components/reports/reportRanges.ts` | ソース | calculateSpendingReportTimeRange |
| useReport.ts | `packages/desktop-client/src/components/reports/useReport.ts` | ソース | レポートデータ取得フック |
