# 機能設計書 31-YNAB5（nYNAB）インポート

## 概要

本ドキュメントは、YNAB5（nYNAB）形式のバックアップファイルからActualへデータをインポートする機能について記述する。

### 本機能の処理概要

**業務上の目的・背景**：YNAB（You Need A Budget）は世界的に利用されている人気の予算管理アプリケーションである。YNABユーザーがActualに移行する際、過去の予算データ、口座情報、取引履歴を手作業で再入力する必要があると、移行コストが大幅に増加する。本機能は、YNAB5/nYNAB形式のJSON/ZIPバックアップファイルを解析し、Actualの内部データ構造に変換してインポートすることで、スムーズな移行を実現する。

**機能の利用シーン**：
- 新規ユーザーがYNAB5からActualへ移行する際のデータ移行
- 既存のYNAB5バックアップファイルを活用した予算データの復元
- 複数の予算管理アプリケーション間でのデータ連携テスト

**主要な処理内容**：
1. YNAB5形式のJSONバックアップファイルの解析とパース
2. 口座情報（accounts）のActual形式への変換とインポート
3. カテゴリ・カテゴリグループの変換とインポート（特殊カテゴリの処理含む）
4. 支払先（payees）情報の変換とインポート
5. 取引データ（transactions）の変換とインポート（分割取引、振替取引の処理含む）
6. 月次予算データの変換とインポート
7. エンティティID間のマッピング管理

**関連システム・外部連携**：Actual APIを使用して口座、カテゴリ、支払先、取引、予算データを作成する。

**権限による制御**：予算ファイルへの書き込み権限が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 7 | ウェルカム画面 | 遷移先機能 | YNAB5形式のデータインポート画面への遷移 |

## 機能種別

データ連携 / データ変換 / CRUD操作（一括登録）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| buffer | Buffer | Yes | YNAB5形式のJSONデータを含むバッファ | JSON形式であること |
| filepath | string | No | インポートファイルのパス（予算名取得用） | - |

### 入力データソース

YNAB5形式のバックアップファイル（JSON形式）。以下のデータ構造を含む：
- `data.budget` または `budget` 直下に予算データ
- `accounts`: 口座一覧
- `payees`: 支払先一覧
- `category_groups`: カテゴリグループ一覧
- `categories`: カテゴリ一覧
- `transactions`: 取引一覧
- `subtransactions`: 分割取引一覧
- `months`: 月次予算データ

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 口座 | Account[] | Actual形式に変換された口座データ |
| カテゴリグループ | CategoryGroup[] | Actual形式に変換されたカテゴリグループ |
| カテゴリ | Category[] | Actual形式に変換されたカテゴリ |
| 支払先 | Payee[] | Actual形式に変換された支払先 |
| 取引 | Transaction[] | Actual形式に変換された取引データ |
| 月次予算 | BudgetAmount[] | Actual形式に変換された予算データ |

### 出力先

Actualの内部データベース（SQLite）

## 処理フロー

### 処理シーケンス

```
1. ファイル解析（parseFile）
   └─ JSONデータのパースとデータ構造の正規化
2. 口座インポート（importAccounts）
   └─ 削除済み以外の口座をActual APIで作成、IDマッピング登録
3. カテゴリインポート（importCategories）
   └─ カテゴリグループとカテゴリの作成、特殊カテゴリの処理
4. 支払先インポート（importPayees）
   └─ 削除済み以外の支払先をActual APIで作成
5. 取引インポート（importTransactions）
   └─ 取引IDの事前生成、振替取引のマッチング、分割取引処理
6. 予算インポート（importBudgets）
   └─ 月次予算データの一括更新
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[parseFile: JSONパース]
    B --> C[importAccounts: 口座作成]
    C --> D[importCategories: カテゴリ作成]
    D --> E[importPayees: 支払先作成]
    E --> F[importTransactions: 取引作成]
    F --> G{振替取引?}
    G -->|Yes| H[振替先マッチング]
    G -->|No| I[通常取引処理]
    H --> J[分割取引処理]
    I --> J
    J --> K[importBudgets: 予算作成]
    K --> L[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 金額変換 | YNABの金額（1000倍）をActualの金額（100倍）に変換（10で割る） | 全ての金額フィールド |
| BR-02 | 削除済みスキップ | deleted=trueのエンティティはインポートしない | 口座、カテゴリ、支払先、取引 |
| BR-03 | 特殊カテゴリマッピング | "To be Budgeted"/"Inflow: Ready to Assign"をIncomeカテゴリにマッピング | カテゴリインポート時 |
| BR-04 | クレジットカードカテゴリ除外 | "Credit Card Payments"グループ配下のカテゴリはスキップ | カテゴリインポート時 |
| BR-05 | Starting Balance処理 | 開始残高取引はStarting Balancesカテゴリに割り当て | 取引インポート時 |
| BR-06 | 振替取引リンク | 孤立した振替取引と分割振替取引をマッチングしてリンク | 取引インポート時 |
| BR-07 | 重複名回避 | カテゴリ/グループ名が重複する場合はサフィックス（-1, -2等）を付与 | 最大10回リトライ |

### 計算ロジック

**金額変換**: `actualAmount = Math.round(ynabAmount / 10)`

YNABは金額を1000倍（ミリユニット）で保持し、Actualは100倍（センチユニット）で保持するため、10で割って変換する。

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 口座作成 | accounts | INSERT | YNAB口座をActual口座として作成 |
| カテゴリグループ作成 | category_groups | INSERT | YNABカテゴリグループを作成 |
| カテゴリ作成 | categories | INSERT | YNABカテゴリを作成 |
| 支払先作成 | payees | INSERT | YNAB支払先を作成 |
| 取引作成 | transactions | INSERT | YNAB取引をActual取引として一括作成 |
| 予算設定 | zero_budgets/reflect_budgets | INSERT/UPDATE | 月次予算額を設定 |
| ノート保存 | notes | INSERT | カテゴリグループ/カテゴリのノートを保存 |

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

#### accounts

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | name | YNAB account.name | 口座名 |
| INSERT | offbudget | !account.on_budget | オフバジェットフラグ |
| INSERT | closed | account.closed | クローズ状態 |

#### transactions

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | id | 事前生成UUID | エンティティIDマップで管理 |
| INSERT | account | マッピング後の口座ID | - |
| INSERT | date | YNAB transaction.date | YYYY-MM-DD形式 |
| INSERT | amount | amountFromYnab(transaction.amount) | 金額変換適用 |
| INSERT | category | マッピング後のカテゴリID | nullable |
| INSERT | payee | マッピング後の支払先ID | - |
| INSERT | cleared | cleared/reconciled判定 | boolean |
| INSERT | reconciled | reconciled判定 | boolean |
| INSERT | notes | transaction.memo | メモ |
| INSERT | imported_id | transaction.import_id | インポートID |
| INSERT | transfer_id | マッチング後の振替先取引ID | 振替取引のみ |
| INSERT | subtransactions | 分割取引配列 | 分割取引のみ |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | JSONパースエラー | 不正なJSON形式のファイル | エラーをスローして中断 |
| - | 重複名エラー | カテゴリ/グループ名の重複が10回を超えた | エラーをスローして中断 |
| - | 孤立振替 | 対応する振替先が見つからない | transfer_idをnullのまま処理続行 |

### リトライ仕様

カテゴリ/カテゴリグループ名の重複時は最大10回までサフィックス付きでリトライする。

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

各インポート処理（口座、カテゴリ、支払先、取引、予算）は独立したトランザクションとして実行される。予算インポートは `batchBudgetUpdates` でバッチトランザクションとして処理される。

## パフォーマンス要件

- 大量の取引データ（数万件）を効率的にインポートするため、`addTransactions` APIで一括登録を行う
- 予算データは `batchBudgetUpdates` でバッチ処理を行い、個別更新によるオーバーヘッドを回避

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

- インポートファイルはユーザーのローカルファイルシステムから読み込む
- データベースへの書き込みは予算ファイルへのアクセス権限が必要

## 備考

- YNABの「Internal Master Category」と「Credit Card Payments」カテゴリグループは特殊処理される
- 振替取引のマッチングは日付、金額、メモを基準にソートして対応付けを行う
- 分割取引内の振替取引は孤立振替取引とのマッチングが必要

---

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

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

### 推奨読解順序

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

YNAB5形式のデータ構造を定義する型定義ファイルを最初に確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | ynab5-types.ts | `packages/loot-core/src/server/importers/ynab5-types.ts` | YNAB5形式の全データ型（Budget, Account, Payee, Category, Transaction等）を理解する |

**読解のコツ**: 各型の構造を把握し、YNABとActualのデータモデルの違いを意識する。特に`deleted`フラグ、`on_budget`フラグ、金額の単位（1000倍）に注目。

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

メインのインポート処理を行うファイルを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ynab5.ts | `packages/loot-core/src/server/importers/ynab5.ts` | インポート処理の全体フローを理解する |

**主要処理フロー**:
- **16-20行目**: `amountFromYnab` - 金額変換ロジック（YNABの1000倍からActualの100倍へ）
- **22-35行目**: `importAccounts` - 口座インポート処理
- **37-168行目**: `importCategories` - カテゴリインポート（特殊カテゴリ処理含む）
- **170-181行目**: `importPayees` - 支払先インポート
- **183-451行目**: `importTransactions` - 取引インポート（振替取引マッチング含む）
- **453-498行目**: `importBudgets` - 予算インポート
- **502-521行目**: `doImport` - メインエントリーポイント
- **523-533行目**: `parseFile` - ファイルパース処理

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

Actual APIの使用方法を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | methods.ts | `packages/api/methods.ts` | 使用されるAPI関数（createAccount, createCategory, addTransactions, batchBudgetUpdates等）の仕様を理解する |

**主要処理フロー**:
- **67-74行目**: `batchBudgetUpdates` - 予算一括更新のバッチ処理
- **112-126行目**: `addTransactions` - 取引一括追加
- **172-177行目**: `createAccount` - 口座作成
- **214-216行目**: `createCategoryGroup` - カテゴリグループ作成
- **236-238行目**: `createCategory` - カテゴリ作成
- **262-264行目**: `createPayee` - 支払先作成
- **96-102行目**: `setBudgetAmount` - 予算額設定

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

```
doImport (ynab5.ts:502)
    │
    ├─ importAccounts (ynab5.ts:22)
    │      └─ actual.createAccount (methods.ts:172)
    │
    ├─ importCategories (ynab5.ts:37)
    │      ├─ actual.getCategories (methods.ts:232)
    │      ├─ actual.createCategoryGroup (methods.ts:214)
    │      ├─ actual.createCategory (methods.ts:236)
    │      └─ send('notes-save', ...)
    │
    ├─ importPayees (ynab5.ts:170)
    │      └─ actual.createPayee (methods.ts:262)
    │
    ├─ importTransactions (ynab5.ts:183)
    │      ├─ actual.getPayees (methods.ts:258)
    │      ├─ actual.getCategories (methods.ts:232)
    │      └─ actual.addTransactions (methods.ts:112)
    │
    └─ importBudgets (ynab5.ts:453)
           ├─ actual.batchBudgetUpdates (methods.ts:67)
           └─ actual.setBudgetAmount (methods.ts:96)
```

### データフロー図

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

YNAB5 JSON ───▶ parseFile() ───▶ Budget型データ
     │
     ├─▶ importAccounts() ───▶ accounts テーブル
     │
     ├─▶ importCategories() ───▶ category_groups, categories テーブル
     │
     ├─▶ importPayees() ───▶ payees テーブル
     │
     ├─▶ importTransactions() ───▶ transactions テーブル
     │
     └─▶ importBudgets() ───▶ zero_budgets/reflect_budgets テーブル
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ynab5.ts | `packages/loot-core/src/server/importers/ynab5.ts` | ソース | YNAB5インポートのメイン処理 |
| ynab5-types.ts | `packages/loot-core/src/server/importers/ynab5-types.ts` | ソース | YNAB5データ型定義 |
| methods.ts | `packages/api/methods.ts` | ソース | Actual API関数定義 |
| months.ts | `packages/loot-core/src/shared/months.ts` | ソース | 日付/月ユーティリティ |
| util.ts | `packages/loot-core/src/shared/util.ts` | ソース | groupBy, sortByKey等のユーティリティ |
