# 機能設計書 16-カテゴリグループ管理

## 概要

本ドキュメントは、Actualアプリケーションにおけるカテゴリグループ管理機能について、その処理内容、入出力仕様、および実装詳細を記述した機能設計書である。

### 本機能の処理概要

カテゴリグループ管理機能は、カテゴリをグループ化して整理する機能である。カテゴリグループは「固定費」「変動費」「収入」などの大分類を表し、その下に個別のカテゴリが配置される階層構造を形成する。

**業務上の目的・背景**：多数のカテゴリを効率的に管理するためには、論理的なグループ化が必要である。例えば、「住居費」グループの下に「家賃」「水道光熱費」「通信費」などのカテゴリを配置することで、予算画面での視認性が向上し、グループ単位での予算管理も可能になる。

**機能の利用シーン**：
- 新しいカテゴリグループ（例：「サブスクリプション」）を作成したい場合
- グループ名を変更したい場合
- 使用しなくなったグループを削除したい場合
- グループの表示順序を変更したい場合
- 収入カテゴリ用のグループを作成したい場合

**主要な処理内容**：
1. カテゴリグループの新規作成（名前、収入/支出フラグを設定）
2. カテゴリグループ名の編集
3. カテゴリグループの削除（配下カテゴリの移行）
4. カテゴリグループの順序変更

**関連システム・外部連携**：本機能は内部完結型であり、外部システムとの直接連携はない。

**権限による制御**：特別な権限制御はなく、予算ファイルを開いているユーザーであれば利用可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 9 | 予算画面（デスクトップ） | 補助機能 | カテゴリグループの作成・編集・削除処理 |

## 機能種別

CRUD操作

## 入力仕様

### 入力パラメータ

#### カテゴリグループ作成

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| name | string | Yes | グループ名 | 空文字不可、システム内で一意 |
| isIncome | boolean | No | 収入グループフラグ | デフォルト: false |
| hidden | boolean | No | 非表示フラグ | デフォルト: false |

#### カテゴリグループ更新

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| id | string | Yes | グループID | 存在するグループID |
| name | string | Yes | グループ名 | 空文字不可 |
| is_income | boolean | Yes | 収入グループフラグ | - |

#### カテゴリグループ削除

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| id | string | Yes | 削除対象グループID | 存在するグループID |
| transferId | string | No | 移行先カテゴリID | 存在するカテゴリID |

### 入力データソース

- 画面入力（予算画面）
- API（外部システムからの呼び出し）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | string | グループID（UUID） |
| name | string | グループ名 |
| is_income | boolean | 収入グループフラグ |
| sort_order | number | 表示順序 |
| hidden | boolean | 非表示フラグ |
| categories | CategoryEntity[] | 配下のカテゴリ一覧 |

### 出力先

- データベース（category_groupsテーブル）

## 処理フロー

### 処理シーケンス

```
【カテゴリグループ作成】
1. グループ名入力
2. 収入/支出フラグ選択
3. バリデーション（名前の一意性チェック）
4. sort_order計算（末尾に追加）
5. category_groupsテーブルにINSERT

【カテゴリグループ削除】
1. 削除対象グループ選択
2. 移行先カテゴリ選択（配下にカテゴリがある場合）
3. 予算値の移行
4. 配下カテゴリの削除
5. グループの論理削除
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B{操作種別}
    B -->|作成| C[グループ名入力]
    B -->|更新| D[グループ選択]
    B -->|削除| E[削除対象選択]

    C --> F[収入/支出選択]
    F --> G{名前重複チェック}
    G -->|重複| H[エラー表示]
    G -->|OK| I[sort_order計算]
    I --> J[INSERT実行]

    D --> K[名前変更]
    K --> L{名前重複チェック}
    L -->|重複| H
    L -->|OK| M[UPDATE実行]

    E --> N{配下カテゴリあり?}
    N -->|Yes| O[移行先選択]
    N -->|No| Q[グループ削除]
    O --> P[予算移行・カテゴリ削除]
    P --> Q

    H --> Z[終了]
    J --> Z
    M --> Z
    Q --> Z
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 名前一意性 | グループ名はシステム内で一意でなければならない | 作成・更新時 |
| BR-02 | 必須フィールド | nameは必須 | 作成時 |
| BR-03 | 削除時カテゴリ処理 | 配下のカテゴリは個別に削除または移行される | 削除時 |
| BR-04 | ソート順序 | is_incomeごとにグループ化され、sort_orderでソート | 表示時 |
| BR-05 | 収入グループ | is_income=trueのグループは収入カテゴリ専用 | 全操作 |

### 計算ロジック

**sort_order計算**（新規作成時）:
```typescript
const lastGroup = await first(`
  SELECT sort_order FROM category_groups
  WHERE tombstone = 0 ORDER BY sort_order DESC, id DESC LIMIT 1
`);
const sort_order = (lastGroup ? lastGroup.sort_order : 0) + SORT_INCREMENT;
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| グループ作成 | category_groups | INSERT | 新規グループを追加 |
| グループ更新 | category_groups | UPDATE | グループ情報を更新 |
| グループ削除 | category_groups | UPDATE(tombstone) | グループを論理削除 |
| グループ削除 | categories | UPDATE(tombstone) | 配下カテゴリを論理削除 |

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

#### category_groups

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | name, is_income, hidden, sort_order | 入力値、計算値 | - |
| UPDATE | name, is_income | 更新値 | - |
| UPDATE | tombstone | 1 | 削除時 |
| SELECT | * | ORDER BY is_income, sort_order, id | 表示順 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 重複エラー | 同名のグループが既に存在する場合 | 別の名前を使用 |

### リトライ仕様

エラー発生時、ユーザーが入力を修正して再実行する。

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

- グループ操作はundoable（元に戻す対応）
- 削除時はbatchMessagesでカテゴリ削除とグループ削除をバッチ処理

## パフォーマンス要件

- グループ一覧の取得は高速であること
- グループ削除時のカテゴリ移行は適切な時間で完了すること

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

- 特別なセキュリティ要件なし
- グループデータは予算ファイル内でのみ有効

## 備考

- API経由でのグループ操作: createCategoryGroup, updateCategoryGroup, deleteCategoryGroup
- グループ取得: getCategoryGroups（配下カテゴリを含む）

---

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

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

### 推奨読解順序

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

カテゴリグループの型定義を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | category-group.ts | `packages/loot-core/src/types/models/category-group.ts` | CategoryGroupEntityの型定義 |

**読解のコツ**:
- グループには`categories`配列が含まれる
- `is_income`で収入/支出グループを区別
- `hidden`で非表示制御

#### Step 2: モデルとバリデーションを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | models.ts | `packages/loot-core/src/server/models.ts` | categoryGroupModelの実装 |

**主要処理フロー**:
1. **112-167行目**: categoryGroupModelの定義
2. **113-126行目**: validate関数
3. **127-146行目**: toDb関数
4. **147-166行目**: fromDb関数（categoriesの変換含む）

#### Step 3: 予算APIハンドラを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | app.ts | `packages/loot-core/src/server/budget/app.ts` | カテゴリグループCRUD処理 |

**主要処理フロー**:
1. **390-394行目**: getCategoryGroups関数
2. **396-410行目**: createCategoryGroup関数
3. **412-414行目**: updateCategoryGroup関数
4. **416-426行目**: moveCategoryGroup関数
5. **428-449行目**: deleteCategoryGroup関数

#### Step 4: データベース操作を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | db/index.ts | `packages/loot-core/src/server/db/index.ts` | グループDB操作 |

**主要処理フロー**:
- **356-386行目**: insertCategoryGroup関数
- **388-404行目**: updateCategoryGroup関数
- **406-419行目**: moveCategoryGroup関数
- **421-433行目**: deleteCategoryGroup関数

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

```
UI (予算画面)
    │
    ├─ send('category-group-create')
    │      └─ app.ts: createCategoryGroup
    │             └─ db.insertCategoryGroup
    │                    ├─ 重複チェック
    │                    ├─ sort_order計算
    │                    └─ INSERT category_groups
    │
    ├─ send('category-group-update')
    │      └─ app.ts: updateCategoryGroup
    │             └─ db.updateCategoryGroup
    │
    └─ send('category-group-delete')
           └─ app.ts: deleteCategoryGroup
                  ├─ 配下カテゴリ取得
                  ├─ budget.doTransfer (予算移行)
                  └─ db.deleteCategoryGroup
                         ├─ 配下カテゴリ削除
                         └─ グループ削除 (tombstone)
```

### データフロー図

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

name ─────────┐
              │
isIncome ─────┼──▶ createCategoryGroup ──▶ category_groups テーブル
              │
hidden ───────┘


id ───────────┐
              │
transferId ───┼──▶ deleteCategoryGroup
              │         │
              │         ├──▶ doTransfer ──▶ 予算値移行
              │         │
              │         ├──▶ deleteCategory (各カテゴリ)
              │         │
              │         └──▶ delete_ (グループ)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| category-group.ts | `packages/loot-core/src/types/models/category-group.ts` | 型定義 | グループデータ型 |
| models.ts | `packages/loot-core/src/server/models.ts` | ソース | グループモデル・バリデーション |
| app.ts | `packages/loot-core/src/server/budget/app.ts` | ソース | グループAPIハンドラ |
| db/index.ts | `packages/loot-core/src/server/db/index.ts` | ソース | データベース操作 |
| methods.ts | `packages/api/methods.ts` | ソース | 外部API |
