# 機能設計書 7-口座クローズ・再開

## 概要

本ドキュメントは、Actual Budget における口座を一時的にクローズしたり、クローズした口座を再開する機能の設計を記述する。

### 本機能の処理概要

**業務上の目的・背景**：口座を完全に削除するのではなく、一時的に非アクティブにしたい場合がある。例えば、使用しなくなった銀行口座やクレジットカードを履歴は残しつつ、日常の操作から隠したい場合である。本機能は、口座のクローズと再開を管理し、過去のデータを保持しながら柔軟な口座管理を可能にする。

**機能の利用シーン**：
- 解約したクレジットカードをクローズする
- 使用頻度の低い口座を一時的に非表示にする
- 誤ってクローズした口座を再開する
- クローズ時に残高を別口座に移動する

**主要な処理内容**：
1. 口座のクローズ（closed フラグを true に設定）
2. クローズ時の残高処理（別口座への振替）
3. 口座の再開（closed フラグを false に設定）
4. クローズ済み口座の表示/非表示制御

**関連システム・外部連携**：口座管理機能、取引管理機能

**権限による制御**：特別な権限制御なし

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 12 | 口座一覧画面（デスクトップ） | 主画面 | 口座クローズ・再開処理 |
| 13 | 口座一覧画面（モバイル） | 主画面 | モバイルでの口座クローズ・再開 |

## 機能種別

CRUD操作

## 入力仕様

### 入力パラメータ

#### 口座クローズ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| id | string | Yes | 口座ID | 存在する口座ID |
| transferAccountId | string | No | 残高振替先口座ID | 存在する口座ID |
| transferCategoryId | string | No | 残高振替先カテゴリID | 存在するカテゴリID |

#### 口座再開

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| id | string | Yes | 口座ID | 存在する口座ID |

### 入力データソース

- 画面入力：口座メニューから操作

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| closed | boolean | 口座のクローズ状態 |

### 出力先

- データベース：accounts テーブルの closed フラグ
- 画面表示：口座一覧から非表示/表示

## 処理フロー

### 処理シーケンス

```
【口座クローズ】
1. 口座メニューから「Close」を選択
   └─ openAccountCloseModal を発火

2. クローズ確認ダイアログを表示
   └─ 残高がある場合は振替先を選択

3. クローズを実行
   └─ api/account-close を呼び出し
   └─ transferAccountId/transferCategoryId を指定

4. サーバーで処理
   └─ 残高振替取引を作成（必要に応じて）
   └─ accounts.closed = true に更新

5. UIを更新
   └─ 口座一覧からクローズ済み口座を非表示

【口座再開】
1. クローズ済み口座を表示
   └─ 「Show closed accounts」オプション

2. 口座メニューから「Reopen」を選択
   └─ dispatch(reopenAccount({ id }))

3. api/account-reopen を呼び出し
   └─ accounts.closed = false に更新

4. UIを更新
   └─ 口座一覧に再表示
```

### フローチャート

```mermaid
flowchart TD
    A[口座メニュー] --> B{操作種別?}
    B -->|Close| C[クローズダイアログ]
    B -->|Reopen| D[api/account-reopen]
    C --> E{残高あり?}
    E -->|Yes| F[振替先を選択]
    E -->|No| G[直接クローズ]
    F --> H[振替取引作成]
    H --> I[api/account-close]
    G --> I
    I --> J[closed = true]
    D --> K[closed = false]
    J --> L[UI更新]
    K --> L
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | 残高処理 | 残高がある場合は振替先を指定 | クローズ時に残高 > 0 |
| BR-002 | クローズ済み非表示 | クローズ済み口座はデフォルトで非表示 | 常時 |
| BR-003 | データ保持 | クローズしても取引履歴は保持 | 常時 |
| BR-004 | 再開制限なし | いつでも再開可能 | 常時 |

### 計算ロジック

```
// 残高振替取引
transferTransaction = {
  account: closingAccountId,
  amount: -currentBalance,
  transfer_id: transferTransactionId,
  payee: transferPayeeId
}

counterTransaction = {
  account: transferAccountId,
  amount: currentBalance,
  transfer_id: transferTransactionId,
  payee: counterPayeeId
}
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| クローズ | accounts | UPDATE | closed = 1 |
| 残高振替 | transactions | INSERT | 振替取引ペア |
| 再開 | accounts | UPDATE | closed = 0 |

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

#### accounts

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | closed | 1 (クローズ) / 0 (再開) | |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| ACCOUNT_NOT_FOUND | データ不整合 | 存在しない口座ID | エラーを返す |
| TRANSFER_FAILED | 処理エラー | 振替処理失敗 | エラー通知を表示 |

### リトライ仕様

- 同期エラー時は自動リトライ

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

- 残高振替とクローズはトランザクション内で実行

## パフォーマンス要件

- クローズ/再開は即座に反映

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

- 特別なセキュリティ考慮なし

## 備考

- クローズ済み口座は「Show closed accounts」で表示可能

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | methods.ts | `packages/api/methods.ts` | closeAccount, reopenAccount 関数 |

**主要処理フロー**:
- **186-196行目**: `closeAccount` 関数（id, transferAccountId, transferCategoryId）
- **198-200行目**: `reopenAccount` 関数

#### Step 2: 画面操作を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Account.tsx | `packages/desktop-client/src/components/accounts/Account.tsx` | メニュー操作 |

**主要処理フロー**:
- **822-823行目**: 'close' → `openAccountCloseModal({ accountId })`
- **825-826行目**: 'reopen' → `dispatch(reopenAccount({ id: accountId }))`

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

```
Account.tsx
    │
    └─ onMenuSelect
           │
           ├─ 'close' → dispatch(openAccountCloseModal({ accountId }))
           │               │
           │               └─ AccountCloseModal
           │                      │
           │                      └─ api/account-close
           │                             │
           │                             ├─ 振替取引作成（残高あり時）
           │                             └─ accounts.closed = 1
           │
           └─ 'reopen' → dispatch(reopenAccount({ id }))
                              │
                              └─ api/account-reopen
                                     │
                                     └─ accounts.closed = 0
```

### データフロー図

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

クローズ要求       ───▶  api/account-close
(+ 振替先)                    │
                              ▼
                         振替取引作成（必要時）   ───▶  transactions INSERT
                              │
                              ▼
                         closed = 1             ───▶  accounts UPDATE
                              │
                              ▼
                         UI更新                 ───▶  口座非表示
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| methods.ts | `packages/api/methods.ts` | ソース | closeAccount, reopenAccount API |
| Account.tsx | `packages/desktop-client/src/components/accounts/Account.tsx` | ソース | 口座画面（メニュー操作） |
| accountsSlice.ts | `packages/desktop-client/src/accounts/accountsSlice.ts` | ソース | Redux口座スライス |
| modalsSlice.ts | `packages/desktop-client/src/modals/modalsSlice.ts` | ソース | openAccountCloseModal |
