# 機能設計書 1-予算作成・編集

## 概要

本ドキュメントは、Actual Budget における月次予算の作成・編集・削除機能の設計を記述する。カテゴリごとに予算額を設定し、支出を管理するための中核機能である。

### 本機能の処理概要

**業務上の目的・背景**：家計管理において、各支出カテゴリに対して月ごとの予算を設定することは、計画的な支出管理の基盤となる。本機能は、ユーザーが収入に基づいて各カテゴリへの予算配分を行い、実際の支出と比較することで、財務目標の達成を支援する。Envelope Budgeting（封筒予算方式）を採用し、「使う前に予算を割り当てる」という原則に基づいた直感的な予算管理を実現する。

**機能の利用シーン**：
- 毎月の給料日後に、翌月の予算を各カテゴリに配分する
- 予期せぬ支出が発生した際に、他のカテゴリから予算を移動する
- 過去の支出実績を参考に、予算額を調整する
- 年間予算計画の一環として、複数月にわたる予算を設定する

**主要な処理内容**：
1. 月次予算テーブルの表示と編集インターフェースの提供
2. カテゴリごとの予算額（Budgeted）の設定・更新
3. 支出額（Spent）と残高（Balance）のリアルタイム計算・表示
4. 予算テンプレートの適用（過去の平均値、前月コピーなど）
5. カテゴリグループ単位での予算合計の計算
6. 「To Be Budgeted」（未割当予算）の自動計算

**関連システム・外部連携**：内部スプレッドシートエンジンによるリアルタイム計算、CRDT同期機能による複数デバイス間の同期

**権限による制御**：特別な権限制御なし。予算ファイルにアクセスできるユーザーは全機能を利用可能

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 9 | 予算画面（デスクトップ） | 主画面 | 月次予算の作成・編集・削除、カテゴリ予算額設定 |
| 10 | 予算画面（モバイル） | 主画面 | モバイルでの月次予算表示・編集処理 |

## 機能種別

CRUD操作 / 計算処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| month | string | Yes | 予算を設定する月（YYYY-MM形式） | 有効な年月形式であること |
| categoryId | string | Yes | 予算を設定するカテゴリのID | 存在するカテゴリIDであること |
| amount | number | Yes | 設定する予算額（整数、セント単位） | 整数であること |

### 入力データソース

- 画面入力：ユーザーが予算テーブル上で直接入力
- API経由：`api/budget-set-amount` ハンドラー

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| budgeted | number | カテゴリの予算額 |
| spent | number | カテゴリの支出額（負の値） |
| balance | number | 残高（budgeted + spent） |
| toBeBudgeted | number | 未割当の予算総額 |

### 出力先

- 画面表示：予算テーブルのセルにリアルタイム反映
- データベース：SQLiteの budget テーブル

## 処理フロー

### 処理シーケンス

```
1. ユーザーが予算セルをクリック
   └─ SheetCell コンポーネントが編集モードに遷移

2. 金額を入力しEnterまたはTab
   └─ onSave コールバックが発火

3. onBudgetAction 呼び出し
   └─ 'budget-amount' タイプでディスパッチ

4. applyBudgetAction (Redux) 実行
   └─ サーバーサイドの予算処理を呼び出し

5. スプレッドシートエンジンが再計算
   └─ balance, groupBalance, totalBalance 等を更新

6. UIが自動更新
   └─ CellValue コンポーネントがバインディング経由で新値を取得
```

### フローチャート

```mermaid
flowchart TD
    A[ユーザーが予算セルをクリック] --> B[編集モードに遷移]
    B --> C[金額を入力]
    C --> D{入力完了?}
    D -->|Enter/Tab| E[onSave発火]
    D -->|Escape| F[編集キャンセル]
    E --> G[onBudgetAction呼び出し]
    G --> H[applyBudgetAction Redux]
    H --> I[api/budget-set-amount送信]
    I --> J[DBに予算額保存]
    J --> K[スプレッドシート再計算]
    K --> L[UI自動更新]
    F --> M[元の値を表示]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | 予算額の符号 | 支出カテゴリの予算は正の値として入力 | 常時 |
| BR-002 | 残高計算 | 残高 = 予算額 + 支出額（支出は負の値） | 常時 |
| BR-003 | グループ集計 | カテゴリグループの予算・支出・残高は配下カテゴリの合計 | 常時 |
| BR-004 | 未割当予算 | To Be Budgeted = 収入 - 全カテゴリ予算合計 | Envelope Budgeting時 |

### 計算ロジック

```
// カテゴリ残高
categoryBalance = categoryBudgeted + categorySumAmount

// グループ残高
groupBalance = SUM(categoryBalance) for each category in group

// 未割当予算（To Be Budgeted）
toBeBudgeted = totalIncome - totalBudgeted - totalOverspent
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 予算設定 | zero_budgets または reflect_budgets | UPSERT | カテゴリ・月ごとの予算額を保存 |

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

#### zero_budgets（Envelope Budgeting用）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPSERT | id | カテゴリID + 月のハッシュ | 主キー |
| UPSERT | month | 対象月（YYYY-MM） | |
| UPSERT | category | カテゴリID | |
| UPSERT | amount | 設定された予算額 | 整数（セント単位） |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| INVALID_AMOUNT | バリデーション | 無効な金額形式 | 入力フォーマットを修正 |
| CATEGORY_NOT_FOUND | データ不整合 | 存在しないカテゴリID | カテゴリを再作成 |

### リトライ仕様

- 同期エラー時は自動リトライ（CRDT機構による）

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

- 個別の予算更新はアトミック操作
- バッチ更新時は `batchBudgetUpdates` でまとめて処理

## パフォーマンス要件

- 予算セル編集から表示更新まで 100ms 以内
- 月切替時の予算データ読み込み 500ms 以内

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

- 予算データはローカルファースト設計でデバイス内に保存
- クラウド同期時はE2E暗号化オプションあり

## 備考

- Envelope Budgeting と Tracking Budgeting で内部処理が異なる
- モバイル版は同一ロジックをレスポンシブUIで提供

---

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

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

### 推奨読解順序

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

予算データのバインディング定義を理解することで、スプレッドシートエンジンとの連携方法を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | bindings.ts | `packages/desktop-client/src/spreadsheet/bindings.ts` | envelopeBudget, trackingBudget のバインディング定義 |

**読解のコツ**: バインディングは `Binding<SheetName, FieldName>` 型で定義され、スプレッドシートセルへの参照を抽象化している。

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

予算画面のメインコンポーネントから処理の流れを追う。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | index.tsx | `packages/desktop-client/src/components/budget/index.tsx` | Budget コンポーネントの初期化と状態管理 |

**主要処理フロー**:
1. **40-78行目**: Budget コンポーネントの初期化、カテゴリ取得、予算境界の取得
2. **133-135行目**: `onBudgetAction` でReduxディスパッチ
3. **156-208行目**: budgetType による EnvelopeBudgetProvider / TrackingBudgetProvider の切り替え

#### Step 3: 予算テーブルコンポーネントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | BudgetTable.tsx | `packages/desktop-client/src/components/budget/BudgetTable.tsx` | テーブル構造とキーボードナビゲーション |
| 3-2 | EnvelopeBudgetComponents.tsx | `packages/desktop-client/src/components/budget/envelope/EnvelopeBudgetComponents.tsx` | 予算セルの編集ロジック |

**主要処理フロー**:
- **86-88行目** (BudgetTable.tsx): `onEditMonth` で編集状態を管理
- **369-408行目** (EnvelopeBudgetComponents.tsx): EnvelopeSheetCell による予算入力と `onSave` コールバック

#### Step 4: API層を理解する

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

**主要処理フロー**:
- **96-102行目**: `setBudgetAmount` 関数が `api/budget-set-amount` メッセージを送信

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

```
Budget (index.tsx)
    │
    ├─ AutoSizingBudgetTable (DynamicBudgetTable.tsx)
    │      │
    │      └─ BudgetTable (BudgetTable.tsx)
    │             │
    │             ├─ BudgetCategories
    │             │      │
    │             │      └─ ExpenseCategoryMonth (EnvelopeBudgetComponents.tsx)
    │             │             │
    │             │             └─ EnvelopeSheetCell
    │             │                    │
    │             │                    └─ onSave → onBudgetAction
    │             │
    │             └─ BudgetSummaries
    │
    └─ onBudgetAction
           │
           └─ applyBudgetAction (Redux budgetSlice)
                  │
                  └─ send('api/budget-set-amount')
```

### データフロー図

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

ユーザー入力              EnvelopeSheetCell
(金額)       ───────▶    formatExpr/unformatExpr  ───▶  整数変換
                              │
                              ▼
                         onBudgetAction
                              │
                              ▼
                         Redux dispatch
                              │
                              ▼
                         api/budget-set-amount    ───▶  DB更新
                              │
                              ▼
                         スプレッドシート再計算   ───▶  UI更新
                              │
                              ▼
                         CellValue binding        ───▶  画面表示
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.tsx | `packages/desktop-client/src/components/budget/index.tsx` | ソース | 予算画面メインコンポーネント |
| BudgetTable.tsx | `packages/desktop-client/src/components/budget/BudgetTable.tsx` | ソース | 予算テーブルコンポーネント |
| EnvelopeBudgetComponents.tsx | `packages/desktop-client/src/components/budget/envelope/EnvelopeBudgetComponents.tsx` | ソース | Envelope予算セルコンポーネント |
| TrackingBudgetComponents.tsx | `packages/desktop-client/src/components/budget/tracking/TrackingBudgetComponents.tsx` | ソース | Tracking予算セルコンポーネント |
| methods.ts | `packages/api/methods.ts` | ソース | API関数定義 |
| budgetSlice.ts | `packages/desktop-client/src/budget/budgetSlice.ts` | ソース | Redux予算スライス |
| BudgetCategories.tsx | `packages/desktop-client/src/components/budget/BudgetCategories.tsx` | ソース | カテゴリ一覧コンポーネント |
| BudgetSummaries.tsx | `packages/desktop-client/src/components/budget/BudgetSummaries.tsx` | ソース | 予算サマリーコンポーネント |
| util.ts | `packages/desktop-client/src/components/budget/util.ts` | ソース | ユーティリティ関数 |
