# 画面設計書 15-取引編集画面（モバイル）

## 概要

本ドキュメントは、Actual Budgetアプリケーションにおけるモバイル向け取引編集画面の設計仕様を定義する。

### 本画面の処理概要

取引編集画面（モバイル）は、取引の新規作成および既存取引の編集を行うモバイル専用画面である。金額、支払先、カテゴリ、口座、日付、メモ、クリア状態などの取引情報を入力・変更できる。分割取引（Split Transaction）にも対応している。

**業務上の目的・背景**：モバイルデバイスから手軽に取引を記録できるようにすることで、日々の支出管理の継続性を高める。レシートを受け取った直後にその場で取引を記録したり、銀行同期で取り込まれた取引を確認・修正したりする用途に対応する。ルール機能と連携して、入力した支払先に基づいてカテゴリを自動設定することも可能。

**画面へのアクセス方法**：
- 新規取引：各画面の取引追加ボタン（+）をタップ
- 既存取引編集：取引一覧から取引項目をタップ
- URLパターンは `/transactions/:transactionId` または `/transactions/new`

**主要な操作・処理内容**：
1. 金額の入力（大きな数字表示の入力フィールド）
2. 支払先の選択（オートコンプリート）
3. カテゴリの選択（オートコンプリート）
4. 口座の選択（オートコンプリート）
5. 日付の入力（日付ピッカー）
6. メモの入力
7. クリア状態の切り替え
8. 取引の分割（Split）
9. 分割取引の追加・削除
10. 取引の保存
11. 取引の削除
12. 未来日取引のスケジュール変換

**画面遷移**：
- 遷移元：口座詳細画面（モバイル）、カテゴリ画面（モバイル）、予算画面（モバイル）
- 遷移先：なし（保存/削除後は前画面に戻る）

**権限による表示制御**：特になし。認証済みユーザーであれば全機能を利用可能。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 9 | 取引追加・編集・削除 | 主機能 | モバイルでの取引詳細編集 |
| 12 | 分割取引（Split Transactions） | 補助機能 | モバイルでの分割取引編集 |

## 画面種別

編集

## URL/ルーティング

- `/transactions/new` - 新規取引作成
- `/transactions/:transactionId` - 既存取引編集

## 入出力項目

| 項目名 | 項目ID | 入力/出力 | 型 | 必須 | 説明 |
|--------|--------|----------|-----|------|------|
| 取引ID | transactionId | 入力（URL） | string | Yes | 編集対象取引のID（新規時は「new」） |
| 金額 | amount | 入力 | number | Yes | 取引金額（整数、cents単位） |
| 支払先 | payee | 入力 | string | No | 支払先ID |
| カテゴリ | category | 入力 | string | No | カテゴリID |
| 口座 | account | 入力 | string | Yes | 口座ID |
| 日付 | date | 入力 | string | Yes | 取引日（YYYY-MM-DD形式） |
| メモ | notes | 入力 | string | No | メモテキスト |
| クリア | cleared | 入力 | boolean | No | クリア状態 |

### URLクエリパラメータ（新規作成時）

| パラメータ | 説明 |
|-----------|------|
| date | 取引日のプリセット |
| amount | 金額のプリセット |
| account | 口座のプリセット |
| category | カテゴリのプリセット |
| payee | 支払先のプリセット |
| cleared | クリア状態のプリセット |
| notes | メモのプリセット |

## 表示項目

| 項目名 | 説明 | データソース |
|--------|------|-------------|
| ページタイトル | 「New Transaction」または支払先名 | payee.name |
| 金額フィールド | 大きなフォントで金額を表示 | transaction.amount |
| 支払先フィールド | 支払先名（分割取引時はイタリック） | payee.name / 「Split」 |
| カテゴリフィールド | カテゴリ名（オフバジェット口座時は「Off budget」、振替時は「Transfer」） | category.name |
| 口座フィールド | 口座名 | account.name |
| 日付フィールド | 取引日（ネイティブ日付ピッカー） | transaction.date |
| クリア切替 | クリア/消込済みの状態 | transaction.cleared / transaction.reconciled |
| メモフィールド | メモテキスト | transaction.notes |

## イベント仕様

### 1-戻るボタン押下

ヘッダー左側の戻るボタン（MobileBackButton）を押下すると、変更を保存せずに前の画面に戻る。

### 2-金額フィールド操作

金額フィールドをタップするとフォーカスが当たり、テンキーで金額を入力できる。ゼロの場合のデフォルト符号は「-」（支出）。

### 3-支払先フィールドタップ

支払先フィールドをタップすると、payee-autocompleteモーダルが表示され、支払先を選択できる。

### 4-カテゴリフィールドタップ

カテゴリフィールドをタップすると、category-autocompleteモーダルが表示され、カテゴリを選択できる。オフバジェット口座または振替の場合は無効。

### 5-口座フィールドタップ

口座フィールドをタップすると、account-autocompleteモーダルが表示され、口座を選択できる。

### 6-日付フィールド変更

日付フィールドを変更すると、取引日が更新される。ネイティブの日付ピッカーを使用。

### 7-クリア切替

クリアトグルをオン/オフすることで、取引のクリア状態を切り替える。消込済み取引の場合は「Reconciled」として無効表示。

### 8-メモフィールド編集

メモフィールドに自由テキストを入力できる。

### 9-分割ボタン押下

「Split」ボタンを押下すると、取引が分割され、2つの子取引が作成される。親取引の金額が維持される。

### 10-分割取引追加

分割取引のエラー表示（残金がある場合）部分のボタンを押下すると、新しい分割取引が追加される。

### 11-分割取引削除

各分割取引の「Delete split」ボタンを押下すると、その分割取引が削除される。

### 12-保存ボタン押下（Add transaction / Save changes）

フッターの保存ボタンを押下すると、取引が保存される。未来日の取引の場合は、スケジュールへの変換を提案するモーダルが表示される。消込済み取引の場合は確認モーダルが表示される。

### 13-削除ボタン押下

「Delete transaction」ボタンを押下すると、確認モーダルが表示され、確認後に取引が削除される。消込済み取引の場合は追加の確認モーダルが表示される。

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 取引保存（新規） | transactions | INSERT | 新規取引レコードを作成 |
| 取引保存（編集） | transactions | UPDATE | 既存取引レコードを更新 |
| 取引削除 | transactions | DELETE | 取引レコードを削除 |
| 分割取引作成 | transactions | INSERT | 親取引を更新し、子取引を追加 |
| ルール実行 | transactions | - | 新規取引時にrules-runで自動補完 |

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

#### transactions

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT/UPDATE | amount | ユーザー入力金額 | 整数（cents） |
| INSERT/UPDATE | payee | ユーザー選択 | PayeeのID |
| INSERT/UPDATE | category | ユーザー選択 | CategoryのID |
| INSERT/UPDATE | account | ユーザー選択 | AccountのID |
| INSERT/UPDATE | date | ユーザー入力 | YYYY-MM-DD形式 |
| INSERT/UPDATE | notes | ユーザー入力 | テキスト |
| INSERT/UPDATE | cleared | ユーザー設定 | boolean |
| INSERT/UPDATE | is_parent | true | 分割取引の親 |
| INSERT/UPDATE | parent_id | 親取引のID | 分割取引の子 |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|---------------|---------|
| M001 | 情報 | New Transaction | 新規取引時のタイトル |
| M002 | 情報 | Transaction | 編集時で支払先未設定の場合 |
| M003 | 情報 | Off budget | オフバジェット口座のカテゴリ表示 |
| M004 | 情報 | Transfer | 振替取引のカテゴリ表示 |
| M005 | 情報 | Add new split - {amount} left | 分割残金がある場合 |
| M006 | 情報 | Amount left: {amount} | 0円分割がある場合の残金表示 |
| M007 | 情報 | Select account | 口座未選択時のフッターボタン |
| M008 | 情報 | Add transaction | 新規取引時のフッターボタン |
| M009 | 情報 | Save changes | 編集時のフッターボタン |
| M010 | 確認 | Are you sure you want to delete the transaction? | 削除確認 |
| M011 | 情報 | Schedule created successfully | スケジュール変換成功時 |

## 例外処理

| 例外条件 | 処理内容 |
|---------|---------|
| 口座が0件 | 口座追加を促すメッセージと「Add account」ボタンを表示 |
| カテゴリが0件 | カテゴリ追加を促すメッセージと「Go to budget」ボタンを表示 |
| 取引データ取得失敗 | ローディング状態を維持（nullを返す） |
| 消込済み取引の編集 | confirm-transaction-editモーダルで確認 |
| 消込済み取引の削除 | confirm-transaction-editモーダルで確認後、confirm-deleteモーダル |
| 未来日取引の保存 | convert-to-scheduleモーダルでスケジュール変換を提案 |

## 備考

- 新規取引時、iOSではキーボードが自動で開かないため金額フィールドに自動フォーカスしない
- 新規取引時、ルール（rules-run）が自動実行され、支払先に基づいてカテゴリなどが自動補完される
- 分割取引の子取引の金額のデフォルト符号は、親取引の符号に従う
- 前回の取引（lastTransaction）の日付と口座が新規取引のデフォルト値として使用される
- SingleActiveEditFormProviderで同時に1つのフィールドのみ編集可能な状態を管理
- 履歴が1件のみの場合（直接URLアクセス）、ブラウザ履歴を操作してホームに戻れるようにする

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | models/index.d.ts | `packages/loot-core/types/models/` | TransactionEntityの型定義 |
| 1-2 | transactions.ts | `packages/loot-core/shared/transactions.ts` | 分割取引のロジック（splitTransaction, addSplitTransaction, makeChild） |

**読解のコツ**: is_parent, parent_id, subtransactionsフィールドが分割取引の構造を決定する。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | TransactionEdit.tsx | `packages/desktop-client/src/components/mobile/transactions/TransactionEdit.tsx` | メインコンポーネント（行1681-1702） |

**主要処理フロー**:
1. **行1681-1688**: useCategoriesやusePayeesで選択肢データ取得
2. **行1690-1701**: SingleActiveEditFormProviderでラップしてレンダリング

#### Step 3: 内部コンポーネントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | TransactionEditUnconnected | 同上（行1302-1674） | メインロジック |

**主要処理フロー**:
- **行1335-1370**: useEffectで既存取引取得（aqlQuery）
- **行1372-1407**: useEffectで新規取引の初期値設定
- **行1409-1467**: onUpdateコールバック - ルール実行含む
- **行1469-1502**: onSaveコールバック - transactions-batch-update
- **行1504-1524**: onDeleteコールバック
- **行1526-1544**: onSplit, onAddSplitコールバック

#### Step 4: 編集フォームを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | TransactionEditInner | 同上（行559-1288） | フォームUI |

**主要処理フロー**:
- **行673-780**: onSaveInnerコールバック - 未来日/消込済みの確認処理
- **行808-927**: onEditFieldInner - 各フィールドのモーダル呼び出し
- **行1019-1286**: レンダリング - 金額、支払先、カテゴリ、分割取引、口座、日付、メモ、削除ボタン

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

```
TransactionEdit (エントリーポイント)
    |
    +-- SingleActiveEditFormProvider (同時編集制御)
    |
    +-- TransactionEditUnconnected
            |
            +-- aqlQuery (既存取引取得)
            +-- send('rules-run') (ルール実行)
            +-- send('transactions-batch-update') (保存)
            |
            +-- TransactionEditInner
                    |
                    +-- MobilePageHeader
                    |       +-- MobileBackButton
                    |
                    +-- FocusableAmountInput (金額)
                    +-- TapField (支払先)
                    +-- TapField (カテゴリ)
                    +-- ChildTransactionEdit[] (分割取引)
                    +-- TapField (口座)
                    +-- InputField (日付)
                    +-- ToggleField (クリア)
                    +-- InputField (メモ)
                    +-- Button (削除)
                    |
                    +-- Footer
                            +-- Button (保存/追加/残金分割)
```

### データフロー図

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

URL params        --->  useParams                  --->  transactionId
(/:transactionId)

DB (aqlQuery)     --->  ungroupTransactions        --->  transactions array
                        (グループ解除)

User input        --->  deserializeTransaction     --->  更新済transaction
(form fields)           (日付/金額変換)

New transaction   --->  send('rules-run')          --->  auto-filled fields
(temporary)             (ルール実行)

transactions      --->  diffItems                  --->  changes
(original vs new)       (差分計算)

changes           --->  send('transactions-batch-update') --> DB更新
                        (サーバー送信)

lastTransaction   --->  Redux store                --->  次回新規取引のデフォルト
(transactionsSlice)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| TransactionEdit.tsx | `packages/desktop-client/src/components/mobile/transactions/TransactionEdit.tsx` | ソース | メインコンポーネント |
| FocusableAmountInput.tsx | `packages/desktop-client/src/components/mobile/transactions/FocusableAmountInput.tsx` | ソース | 金額入力 |
| MobileForms.tsx | `packages/desktop-client/src/components/mobile/MobileForms.tsx` | ソース | TapField, InputField, ToggleField |
| MobileBackButton.tsx | `packages/desktop-client/src/components/mobile/MobileBackButton.tsx` | ソース | 戻るボタン |
| transactions.ts | `packages/loot-core/shared/transactions.ts` | ソース | 分割取引ロジック |
| useSingleActiveEditForm.ts | `packages/desktop-client/src/hooks/useSingleActiveEditForm.ts` | ソース | 編集状態管理 |
| transactionsSlice.ts | `packages/desktop-client/src/transactions/transactionsSlice.ts` | ソース | lastTransaction管理 |
| modalsSlice.ts | `packages/desktop-client/src/modals/modalsSlice.ts` | ソース | モーダル管理 |
