# 帳票設計書 10-CSV Export（CSVエクスポート）

## 概要

本ドキュメントは、Actual Budgetアプリケーションにおける「CSV Export（CSVエクスポート）」機能の設計仕様を記述したものである。トランザクションデータをCSV形式でエクスポートするバックエンド機能について詳細に解説する。

### 本帳票の処理概要

CSV Export（CSVエクスポート）は、トランザクションデータをCSV形式のテキストとして出力する機能である。2つのエクスポート方式を提供し、通常のトランザクションエクスポートと、クエリベースのエクスポート（分割トランザクション対応）が利用可能である。

**業務上の目的・背景**：家計データを外部ツール（Excel、Google Sheets等）で分析したい場合や、データのバックアップ・移行を行いたい場合に、標準的なCSV形式でのエクスポートが求められる。本機能は、トランザクションデータを一般的なCSV形式で出力し、他のアプリケーションとの連携を可能にする。

**帳票の利用シーン**：データの外部分析、会計ソフトへのインポート、年度末のデータ保存、税務申告用データ作成など、トランザクションデータを外部形式で取得する必要がある場面で利用される。

**主要な出力内容**：
1. 口座名、日付、支払先、メモ、カテゴリ、金額
2. 消込・照合状態
3. 分割トランザクション情報（クエリ版）

**帳票の出力タイミング**：ユーザーが口座画面やレポート画面からCSVエクスポート操作を実行した際に生成される。

**帳票の利用者**：アプリケーションの全ユーザー（個人の家計管理者）

## 帳票種別

データエクスポート（CSV形式）

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| A-XXX | 口座詳細画面 | `/accounts/:id` | エクスポートボタン |
| R-XXX | 各種レポート画面 | `/reports/*` | エクスポートボタン |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | CSV（Comma Separated Values）|
| 文字エンコーディング | UTF-8 |
| 区切り文字 | カンマ（,） |
| ヘッダー行 | 有（1行目） |
| 改行コード | 標準（csv-stringifyによる自動処理） |

### エクスポート方式

| 方式 | 関数名 | 用途 |
|-----|-------|------|
| 通常エクスポート | exportToCSV | 基本的なトランザクションエクスポート |
| クエリエクスポート | exportQueryToCSV | フィルター条件付き、分割トランザクション対応 |

## 帳票レイアウト

### exportToCSV 出力カラム

| No | カラム名 | データ元 | 説明 | 形式 |
|----|---------|----------|------|------|
| 1 | Account | accountNamesById[account] | 口座名 | 文字列 |
| 2 | Date | date | 日付 | YYYY-MM-DD |
| 3 | Payee | payeeNamesById[payee] | 支払先名 | 文字列 |
| 4 | Notes | notes | メモ | 文字列 |
| 5 | Category | categoryNamesById[category] | カテゴリ名（グループ: サブカテゴリ） | 文字列 |
| 6 | Amount | integerToAmount(amount) | 金額 | 小数 |
| 7 | Cleared | cleared | 消込済み | boolean |
| 8 | Reconciled | reconciled | 照合済み | boolean |

### exportQueryToCSV 出力カラム

| No | カラム名 | データ元 | 説明 | 形式 |
|----|---------|----------|------|------|
| 1 | Account | account.name | 口座名 | 文字列 |
| 2 | Date | date | 日付 | YYYY-MM-DD |
| 3 | Payee | payee.name | 支払先名 | 文字列 |
| 4 | Notes | notes（分割情報付加） | メモ | 文字列 |
| 5 | Category | category.name | カテゴリ名 | 文字列 |
| 6 | Amount | integerToAmount(amount) | 金額（親は0） | 小数 |
| 7 | Split_Amount | integerToAmount(amount) | 分割元金額（親のみ） | 小数 |
| 8 | Cleared | 状態文字列 | 消込状態 | Reconciled/Cleared/Not cleared |

### 分割トランザクションのNotes形式

| 種別 | 形式 | 例 |
|-----|------|-----|
| 親トランザクション | (SPLIT INTO n) [元のメモ] | (SPLIT INTO 3) 食費まとめ |
| 子トランザクション | (SPLIT x OF n) [元のメモ] | (SPLIT 1 OF 3) 朝食 |
| 通常トランザクション | [メモのまま] | 日用品購入 |

## 出力条件

### exportToCSV 入力パラメータ

| パラメータ | 型 | 説明 | 必須 |
|-----------|-----|------|-----|
| transactions | Transaction[] | エクスポート対象トランザクション | Yes |
| accounts | Account[] | 口座マスタ | Yes |
| categoryGroups | CategoryGroup[] | カテゴリグループマスタ | Yes |
| payees | Payee[] | 支払先マスタ | Yes |

### exportQueryToCSV 入力パラメータ

| パラメータ | 型 | 説明 | 必須 |
|-----------|-----|------|-----|
| query | Query | AQLクエリオブジェクト | Yes |

### クエリ選択項目（exportQueryToCSV）

| 項目 | AQLパス | 説明 |
|-----|---------|------|
| Id | id | トランザクションID |
| Account | account.name | 口座名（結合） |
| Date | date | 日付 |
| Payee | payee.name | 支払先名（結合） |
| ParentId | parent_id | 親トランザクションID |
| IsParent | is_parent | 親フラグ |
| IsChild | is_child | 子フラグ |
| SortOrder | sort_order | ソート順 |
| Notes | notes | メモ |
| Category | category.name | カテゴリ名（結合） |
| Amount | amount | 金額 |
| Cleared | cleared | 消込フラグ |
| Reconciled | reconciled | 照合フラグ |

## データベース参照仕様

### 参照テーブル一覧

| テーブル名 | 用途 | 結合条件 |
|-----------|------|---------|
| transactions | トランザクションデータ | - |
| accounts | 口座マスタ | id |
| category_groups | カテゴリグループ | - |
| categories | カテゴリ | group_id |
| payees | 支払先マスタ | id |

### テーブル別参照項目詳細

#### transactions

| 参照項目（カラム名） | 帳票項目との対応 | 取得条件 | 備考 |
|-------------------|----------------|---------|------|
| account | Account列 | - | ID参照 |
| date | Date列 | - | - |
| payee | Payee列 | - | ID参照 |
| notes | Notes列 | - | - |
| category | Category列 | - | ID参照 |
| amount | Amount列 | - | 整数形式 |
| cleared | Cleared列 | - | boolean |
| reconciled | Reconciled列 | - | boolean |
| parent_id | 分割判定 | クエリ版のみ | - |
| is_parent | 親判定 | クエリ版のみ | - |
| is_child | 子判定 | クエリ版のみ | - |

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| Amount | amount / 100 | なし | integerToAmount使用 |
| Split_Amount | parent.amount / 100 | なし | 親トランザクションのみ |
| 子番号 | 累積カウント | なし | Map使用 |
| 兄弟総数 | 親ごとにカウント | なし | Map使用 |

### 分割トランザクション処理フロー

1. 全トランザクションを走査
2. is_childフラグのあるトランザクションを検出
3. parent_idごとに子の総数をカウント（parentsChildCount Map）
4. 各子トランザクションに順序番号を付与（childSplitOrder Map）
5. エクスポート時に適切なNotes形式を適用

## 処理フロー

### exportToCSV フロー

```mermaid
flowchart TD
    A[exportToCSV呼び出し] --> B[口座名マップ生成]
    B --> C[カテゴリ名マップ生成]
    C --> D[支払先名マップ生成]
    D --> E[トランザクション変換]
    E --> F[ID参照を名前に解決]
    F --> G[金額変換 integerToAmount]
    G --> H[csvStringify実行]
    H --> I[CSV文字列返却]
```

### exportQueryToCSV フロー

```mermaid
flowchart TD
    A[exportQueryToCSV呼び出し] --> B[AQLクエリ実行]
    B --> C[splits: 'all'オプション]
    C --> D[トランザクション取得]
    D --> E[子トランザクション走査]
    E --> F[parentsChildCountマップ構築]
    F --> G[childSplitOrderマップ構築]
    G --> H[トランザクション変換]
    H --> I{IsParent?}
    I -->|Yes| J[SPLIT INTO n 形式]
    I -->|No| K{IsChild?}
    K -->|Yes| L[SPLIT x OF n 形式]
    K -->|No| M[通常形式]
    J --> N[金額処理]
    L --> N
    M --> N
    N --> O{IsParent?}
    O -->|Yes| P[Amount=0, Split_Amount=実額]
    O -->|No| Q[Amount=実額, Split_Amount=0]
    P --> R[Cleared状態変換]
    Q --> R
    R --> S[csvStringify実行]
    S --> T[CSV文字列返却]
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| 空データ | transactions配列が空 | - | 空のCSV（ヘッダーのみ） |
| ID参照エラー | マスタに存在しないID | undefined表示 | データ整合性確認 |
| クエリエラー | AQLクエリ失敗 | エラーthrow | クエリ条件確認 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | 数万件のトランザクション |
| 目標出力時間 | 5秒以内 |
| メモリ使用 | トランザクション件数に比例 |

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

- ローカルデータストレージ（サーバーへのデータ送信なし）
- ファイル保存はユーザーのローカルファイルシステムへ
- 個人情報を含むため、ファイル管理はユーザー責任

## 備考

- csv-stringify/sync ライブラリを使用
- 金額は整数形式（cents）から小数形式に変換
- カテゴリ名は「グループ名: サブカテゴリ名」形式（通常版）
- クエリ版はAQLのsplits: 'all'オプションで分割取得

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | models.ts | `loot-core/types/models` | Transaction, Account, Category型定義 |

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | export-to-csv.ts | `packages/loot-core/src/server/transactions/export/export-to-csv.ts` | エクスポート関数（131行） |

**主要処理フロー**:
1. **行7-57**: exportToCSV関数 - 基本エクスポート
2. **行13-16**: accountNamesById - 口座名マップ生成
3. **行18-27**: categoryNamesById - カテゴリ名マップ生成（グループ: サブカテゴリ形式）
4. **行29-32**: payeeNamesById - 支払先名マップ生成
5. **行34-54**: transactionsForExport - トランザクション変換
6. **行56**: csvStringify - CSV文字列生成

7. **行59-130**: exportQueryToCSV関数 - クエリベースエクスポート
8. **行60-78**: aqlQuery - 分割トランザクション含む全データ取得
9. **行80-92**: 分割トランザクション情報収集ループ
10. **行95-127**: transactionsForExport - 分割情報付きトランザクション変換
11. **行129**: csvStringify - CSV文字列生成

#### Step 3: 関連ユーティリティを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | util.ts | `loot-core/src/shared/util.ts` | integerToAmount関数 |
| 3-2 | aql.ts | `loot-core/src/server/aql.ts` | aqlQuery関数 |

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

```
export-to-csv.ts
    |
    +-- exportToCSV(transactions, accounts, categoryGroups, payees)
    |       |
    |       +-- accounts.reduce() --> accountNamesById マップ
    |       +-- categoryGroups.reduce() --> categoryNamesById マップ
    |       +-- payees.reduce() --> payeeNamesById マップ
    |       +-- transactions.map() --> transactionsForExport
    |       |       |
    |       |       +-- integerToAmount(amount) --> 金額変換
    |       |
    |       +-- csvStringify(transactionsForExport, {header: true})
    |
    +-- exportQueryToCSV(query)
            |
            +-- aqlQuery(query.select([...]).options({splits: 'all'}))
            |       |
            |       +-- transactions取得（分割含む）
            |
            +-- parentsChildCount Map構築
            +-- childSplitOrder Map構築
            |
            +-- transactions.map() --> transactionsForExport
            |       |
            |       +-- Notes形式決定（SPLIT INTO/SPLIT x OF/通常）
            |       +-- Amount/Split_Amount決定
            |       +-- Cleared状態文字列変換
            |       +-- integerToAmount(amount)
            |
            +-- csvStringify(transactionsForExport, {header: true})
```

### データフロー図

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

=== exportToCSV ===

transactions ────────+
                     |
accounts ────────────+──> マスタ名前解決
                     |         |
categoryGroups ──────+         v
                     |    transactionsForExport
payees ──────────────+         |
                               v
                         integerToAmount
                               |
                               v
                         csvStringify ──────────> CSV文字列


=== exportQueryToCSV ===

query ──────────────────> aqlQuery
                              |
                              v
                    transactions (with splits)
                              |
                     +--------+--------+
                     |                 |
                     v                 v
             parentsChildCount    childSplitOrder
                     |                 |
                     +--------+--------+
                              |
                              v
                    transactionsForExport
                              |
                    +---------+---------+
                    |         |         |
                    v         v         v
               IsParent   IsChild   通常
                    |         |         |
                    v         v         v
            SPLIT INTO   SPLIT x OF   Notes
                    |         |         |
                    +----+----+---------+
                         |
                         v
                   csvStringify ──────────> CSV文字列
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| export-to-csv.ts | `packages/loot-core/src/server/transactions/export/export-to-csv.ts` | ソース | エクスポート関数 |
| util.ts | `packages/loot-core/src/shared/util.ts` | ソース | integerToAmount等ユーティリティ |
| aql.ts | `packages/loot-core/src/server/aql.ts` | ソース | AQLクエリ実行 |
| csv-stringify | node_modules | 依存 | CSV文字列生成ライブラリ |
