# 帳票設計書 9-Formula（数式）レポート

## 概要

本ドキュメントは、Actual Budgetアプリケーションにおける「Formula（数式）レポート」の設計仕様を記述したものである。カスタム数式を使用して柔軟な計算結果を表示するウィジェット機能について詳細に解説する。

### 本帳票の処理概要

Formula（数式）レポートは、Excel互換の数式エンジン（HyperFormula）を使用して、ユーザー定義の計算式を評価し結果を表示するダッシュボードウィジェットである。QUERY関数やQUERY_COUNT関数を使用してトランザクションデータを集計し、SUM、AVG、IF等の標準関数と組み合わせた複雑な計算が可能である。

**業務上の目的・背景**：標準レポートでは対応できない独自の計算やKPIを表示したい場合に、ユーザーが自由に数式を定義できる機能が求められる。本ウィジェットは、貯蓄率、支出比率、カスタム財務指標など、ユーザー固有の計算ニーズに対応する。

**帳票の利用シーン**：ダッシュボードでのカスタムKPI表示、複数クエリを組み合わせた財務分析、条件付き色分けによる視覚的アラート表示など、高度なカスタマイズが必要な場面で利用される。

**主要な出力内容**：
1. 数式の計算結果（数値または文字列）
2. 条件付きカラー表示
3. 動的/静的フォントサイズ

**帳票の出力タイミング**：ダッシュボードウィジェットとして設定された際に、数式変更時またはクエリバージョン変更時にリアルタイムで計算・表示される。

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

## 帳票種別

カスタム計算ウィジェット

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| R-009 | Formulaレポート | `/reports/formula` | 直接アクセス不可（ウィジェット専用） |
| R-009-W | Formulaウィジェット | `/reports/formula/:id` | ダッシュボードウィジェットクリック |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | HTML（画面表示）|
| 表示形式 | 単一値表示 |
| レスポンシブ対応 | 有（モバイル/デスクトップ） |
| データ更新 | リアルタイム（数式/クエリ変更時に再計算） |
| 数式エンジン | HyperFormula（GPLv3） |

### 表示固有設定

| 項目 | 内容 |
|-----|------|
| エディタライブラリ | CodeMirror |
| 数式構文 | Excel互換 |
| フォントサイズ | 動的（dynamic）または静的（static） |

## 帳票レイアウト

### レイアウト概要

レポートは上部のヘッダー部、結果表示部、数式エディタ部、設定部、クエリ管理部の5つのセクションで構成される。

```
+------------------------------------------+
|              ページヘッダー                |
|  [タイトル]                    [保存ボタン] |
+------------------------------------------+
|              結果表示部                    |
|  Result:                                  |
|  +------------------------------------+   |
|  |          計算結果表示               |   |
|  +------------------------------------+   |
+------------------------------------------+
|              数式エディタ部               |
|  Formula:                                |
|  +------------------------------------+   |
|  |  =SUM(QUERY('expenses'), ...)      |   |
|  +------------------------------------+   |
+------------------------------------------+
|              設定部                       |
|  [Font size: Dynamic/Static] [サイズ入力]  |
|  [Conditional color (optional)]          |
+------------------------------------------+
|              クエリ管理部                  |
|  Query Definitions                        |
|  [クエリ一覧/追加/編集]                    |
+------------------------------------------+
```

### ヘッダー部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | タイトル | ウィジェット名（編集可能） | widget.meta.name | テキスト |
| 2 | 保存ボタン | ウィジェット設定保存 | onSaveWidget | ボタン |

### 結果表示部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | Result | 数式の計算結果 | useFormulaExecution.result | 数値/文字列 |
| 2 | エラー表示 | 数式エラー時のメッセージ | useFormulaExecution.error | エラーテキスト |

### 数式エディタ部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | FormulaEditor | 数式入力エディタ | formula | CodeMirrorエディタ |

### 設定部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | Font size | 動的/静的フォントサイズ | fontSizeMode | ドロップダウン |
| 2 | Font size (px) | 静的時のピクセルサイズ | staticFontSize | 数値入力 |
| 3 | Conditional color | 結果に応じた色指定数式 | colorFormula | 数式入力 |

### クエリ管理部（QueryManager）

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | Query一覧 | 定義済みクエリ | queries | リスト |
| 2 | 条件設定 | フィルター条件 | conditions | フィルターUI |
| 3 | 期間設定 | 時間範囲 | timeFrame | 期間選択 |

## 出力条件

### 数式構文

| 機能 | 構文例 | 説明 |
|-----|-------|------|
| クエリ合計 | QUERY('queryName') | クエリ結果の合計金額 |
| クエリ件数 | QUERY_COUNT('queryName') | クエリ結果のトランザクション件数 |
| 基本演算 | =A+B, =A-B, =A*B, =A/B | 四則演算 |
| 関数 | =SUM(1,2,3), =AVG(...) | HyperFormula標準関数 |
| 条件分岐 | =IF(A>B, "Yes", "No") | 条件付き結果 |

### クエリ設定

| 項目 | 説明 | 必須 |
|-----|------|-----|
| 名前 | クエリ識別名 | Yes |
| 条件 | フィルター条件 | No |
| 条件演算 | AND/OR | No |
| 時間範囲 | 集計期間 | No |

### 時間範囲モード

| モード | 説明 |
|-------|------|
| sliding-window | 現在からの相対期間（ライブ更新） |
| static | 固定期間 |
| full | 全期間 |
| lastMonth | 先月 |
| lastYear | 昨年 |
| yearToDate | 年初来 |
| priorYearToDate | 前年同期 |

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

### 参照テーブル一覧

| テーブル名 | 用途 | 結合条件 |
|-----------|------|---------|
| transactions | トランザクションデータ | - |
| dashboard | ウィジェット設定 | widget.id |

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

#### transactions（QUERY関数経由）

| 参照項目（カラム名） | 帳票項目との対応 | 取得条件 | 備考 |
|-------------------|----------------|---------|------|
| amount | QUERY関数結果 | $sum計算 | integerToAmount変換 |
| * | QUERY_COUNT関数結果 | $count計算 | 件数カウント |

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| QUERY結果 | SUM(amount) / 100 | 小数点2桁 | 金額変換 |
| QUERY_COUNT結果 | COUNT(*) | なし | 整数 |
| 数式結果 | HyperFormula評価 | 数式依存 | - |
| 色判定 | colorFormula評価 | なし | 文字列返却 |

### 数式実行フロー

1. 数式からQUERY/QUERY_COUNT関数呼び出しを抽出
2. 各クエリに対してトランザクション集計を実行
3. 関数呼び出しを実際の値で置換
4. HyperFormulaで数式を評価
5. 結果を表示

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[ウィジェット表示] --> B[useWidget<FormulaWidget>]
    B --> C{widget存在?}
    C -->|No| D[LoadingIndicator]
    C -->|Yes| E[FormulaInner]
    E --> F[数式/クエリ初期化]
    F --> G[useFormulaExecution]
    G --> H[QUERY/QUERY_COUNT抽出]
    H --> I[各クエリ実行]
    I --> J[buildFilteredTransactionsQuery]
    J --> K[fetchQuerySum/fetchQueryCount]
    K --> L[数式内の関数を値で置換]
    L --> M[HyperFormula評価]
    M --> N[FormulaResult描画]
    N --> O{パラメータ変更?}
    O -->|Yes| G
    O -->|No| P[表示完了]
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| 数式構文エラー | 不正な数式 | Formula must start with = | =で開始 |
| クエリ未定義 | QUERY参照先なし | Query "xxx" not found | クエリ定義追加 |
| HyperFormulaエラー | 計算エラー | Formula error: [type] | 数式修正 |
| ウィジェット未保存 | widget未定義 | Cannot save: No widget available. | ウィジェット経由でアクセス |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定クエリ数 | 1〜10個程度 |
| 目標計算時間 | 2秒以内 |
| キャッシュ | useMemoによるメモ化 |

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

- HyperFormulaはサンドボックス内で実行
- ローカルデータストレージ（サーバーへのデータ送信なし）
- 数式はJSON形式でウィジェットメタデータとして保存

## 備考

- ウィジェット専用機能（直接アクセス不可）
- HyperFormulaはGPLv3ライセンス
- CodeMirrorエディタによるシンタックスハイライト
- RESULT変数で計算結果を色条件式で参照可能
- theme_xxx変数でテーマ色を参照可能

---

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

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

### 推奨読解順序

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

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

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

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

**主要処理フロー**:
1. **行47-59**: Formula関数 - ウィジェットデータ読み込み
2. **行65-89**: FormulaInner - 状態初期化（formula, fontSizeMode, colorFormula）
3. **行91-95**: useFormulaExecution - 数式実行フック
4. **行97-115**: colorVariables/colorResult - 条件付き色計算
5. **行153-186**: onSaveWidget - ウィジェット保存
6. **行284-291**: FormulaResult - 結果表示
7. **行313-321**: FormulaEditor - 数式エディタ
8. **行433-436**: QueryManager - クエリ管理

#### Step 3: 数式実行エンジンを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | useFormulaExecution.ts | `packages/desktop-client/src/hooks/useFormulaExecution.ts` | 数式実行ロジック（352行） |

**主要処理フロー**:
1. **行30-35**: useFormulaExecution - フック定義
2. **行44-52**: 数式バリデーション（=で開始確認）
3. **行56-116**: QUERY/QUERY_COUNT抽出と置換
4. **行118-146**: HyperFormula初期化と数式設定
5. **行149-163**: 結果取得とエラーハンドリング
6. **行231-327**: buildFilteredTransactionsQuery - クエリ構築
7. **行329-339**: fetchQuerySum - 合計取得
8. **行341-351**: fetchQueryCount - 件数取得

#### Step 4: クエリ管理UIを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | QueryManager.tsx | `packages/desktop-client/src/components/formula/QueryManager.tsx` | クエリ管理コンポーネント（882行） |

**主要処理フロー**:
1. **行55-195**: QueryManager - メインコンポーネント
2. **行61-91**: handleAddQuery - クエリ追加
3. **行93-104**: handleRemoveQuery/handleUpdateQuery
4. **行204-881**: QueryItem - 個別クエリ設定UI
5. **行305-308**: useRuleConditionFilters - フィルター管理
6. **行396-429**: handleExport - JSON形式でエクスポート
7. **行431-480**: handleImport - JSONからインポート

#### Step 5: 数式エディタを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | FormulaEditor.tsx | `packages/desktop-client/src/components/formula/FormulaEditor.tsx` | CodeMirrorエディタ（106行） |

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

```
Formula.tsx
    |
    +-- useWidget<FormulaWidget>() --> ウィジェットデータ取得
    |
    +-- FormulaInner
            |
            +-- useFormulaExecution(formula, queries) --> useFormulaExecution.ts
            |       |
            |       +-- QUERY/QUERY_COUNT抽出
            |       +-- buildFilteredTransactionsQuery()
            |       |       |
            |       |       +-- send('make-filters-from-conditions')
            |       |       +-- getLiveRange()
            |       |
            |       +-- fetchQuerySum() --> send('query', summedQuery)
            |       +-- fetchQueryCount() --> send('query', countQuery)
            |       +-- HyperFormula.buildEmpty() --> 数式評価
            |
            +-- useFormulaExecution(colorFormula) --> 色計算
            |
            +-- FormulaResult --> 結果表示
            |
            +-- FormulaEditor --> 数式編集UI
            |       |
            |       +-- CodeMirror
            |       +-- excelFormulaExtension --> シンタックスハイライト
            |
            +-- QueryManager --> クエリ管理UI
                    |
                    +-- QueryItem
                            |
                            +-- useRuleConditionFilters()
                            +-- FilterButton
                            +-- AppliedFilters
```

### データフロー図

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

widget.meta.formula ──+
                      |
widget.meta.queries ──+──> useFormulaExecution
                      |         |
queriesVersion ───────+         v
                           QUERY/QUERY_COUNT抽出
                                |
                                v
                      buildFilteredTransactionsQuery
                                |
                           +----+----+
                           |         |
                           v         v
                      fetchQuerySum  fetchQueryCount
                           |         |
                           +----+----+
                                |
                                v
                        数式内の値置換
                                |
                                v
                      HyperFormula.setCellContents
                                |
                                v
                      HyperFormula.getCellValue ──────> result
                                                           |
                                                           v
                                                    [FormulaResult]
                                                           |
colorFormula ──────────> 色計算 ──────────────────────> customColor
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Formula.tsx | `packages/desktop-client/src/components/reports/reports/Formula.tsx` | ソース | メインコンポーネント |
| useFormulaExecution.ts | `packages/desktop-client/src/hooks/useFormulaExecution.ts` | ソース | 数式実行エンジン |
| QueryManager.tsx | `packages/desktop-client/src/components/formula/QueryManager.tsx` | ソース | クエリ管理UI |
| FormulaEditor.tsx | `packages/desktop-client/src/components/formula/FormulaEditor.tsx` | ソース | 数式エディタ |
| FormulaResult.tsx | `packages/desktop-client/src/components/reports/FormulaResult.tsx` | ソース | 結果表示 |
| codeMirror-excelLanguage.ts | `packages/desktop-client/src/components/formula/codeMirror-excelLanguage.ts` | ソース | Excel構文ハイライト |
