概要

項目内容
案件IDMAS-010
カテゴリFP&A・シミュレーション
PhaseP2
優先度★★
所要時間8-12h(MVP: P/L のみ先行)
対象ファイル600_report/611_financial_modeling.js(新規) / 000_infra/002_constants.jsMENU_DEFINITION / 03_sys_params キー追加) / 01_sys_configFS_LONGTERM キー追加、DDL 書込不要で Constants.CONFIG_SHEET 経由) / CLAUDE.md(DDL 管理外タブに 94_fs_longterm_forecast 追記)
前提案件MAS-024(固変区分活用)、MAS-020(YoY 実績比較)
関連案件MAS-018(財務3表完全連動モデリング)B/S・CF 推計連携の前提 / MAS-013(投資回収シミュレーション)— 投資案件 CF を本機能 5 年予測に重ね合わせ / MAS-005(What-if シナリオ UI) / MAS-011(ボトムアップ What-if) / MAS-017(資金調達シミュレーション)

目的

過去 12〜36 ヶ月の実績(42_trn_journal仕訳データ + 11_mst_account の固変区分)から成長率・費用構造を自動抽出し、向こう 60 ヶ月(5 年)分の 月次 P/L ベースライン予測 を一括生成する。MVP は P/L のみに集中し、B/S・CF 予測は MAS-018 完了後に段階拡張する。

「生成された予測値は、経営判断のインプットとして使用する前に、必ず人間による妥当性のレビューと必要な調整を行うことを前提とする。」

  • 経営計画の起点: 中期事業計画(銀行融資・投資家説明・社内 KGI 設定)のベースラインを手入力ゼロで生成
  • MAS-013(投資ROI)との接続: 投資案件の年次 CF を本機能のベースラインに重ね合わせ、投資実行後の損益インパクトを即時可視化
  • MAS-018(3 表連動)の後続拡張: B/S・CF 予測は MAS-018 の連動エンジン完成後に Step 3 B/S・CF 推計 として追加
  • Human-in-the-Loop: 予測結果は自動的に下流マスタ/予算タブへ反映せず、94_fs_longterm_forecast でレビュー承認後に使用

既存関連コード

200_data/202_repository.js

関数戻り値用途
JournalRepository.findAll()L270{ headers: string[], dtos: JournalEntryDTO[] }実績仕訳データ取得(売上・費用集計の主ソース)
AccountRepository.findAll()L315{ headers: string[], dtos: Object[] }固変区分の取得にはこちらを使用findAsMap は不可)
AccountRepository.findAsMap()L323Object.<string, {stmt, cat}>stmt / cat のみ。固変区分を含まない

000_infra/003_contracts.js

JournalEntryDTO @typedef L97-129 に以下の主要フィールド:

  • 発生日(P/L計上日) L99
  • 科目名 L104
  • 税抜金額_実績 L108
  • 取引ID / 決済日_実績 / 収支区分 / 取引先名

AccountDTO の明示的 typedef は無く、findAll() は汎用 Object[] を返す。固変区分列は dto['固変区分'] で直接参照(ヘッダー名ベース)。

000_infra/004_utils.js

関数シグネチャ
Utils.addMonths(ymStr, months)L389(string, number) → string"YYYY-MM" 加減算)
Utils.parseDateToYm(val)L354(Date|string|number) → string
Utils.parseAmt(val)L453(*) → number

000_infra/002_constants.js

  • Constants.getParam(key, defaultVal) L147-167: 03_sys_params シート全体を _paramsCache に初回キャッシュ、以降は再読み込み無しで返却。型は defaultVal に強制変換。
  • MENU_DEFINITION L231-240: 既存カテゴリ 📋 サイドバー: 📊 マート更新source: 'sidebar')。項目フォーマット { label, funcName, description }、区切りは { separator: true }

600_report/

既存ファイル最大番号は 609 (609_datamart_kpi.js)。次の空き番号は 611(610 は予約済み案件向けに保留し、本案件は 611 を使用する)。

docs/dev/dev_mas-001_variance_analysis.md

FP&A 系仕様書の参考フォーマット。H2 見出し構成の雛形。

修正方針

Step 1: 成長率・費用構造抽出エンジン

611_financial_modeling.jsFinancialModelingService.extractHistoricalMetrics_(lookbackMonths) を実装。

  • 売上予測の月次構成比(季節性指数): 過去 3 年分の月別売上高を集計し、年間合計に対する各月構成比を算出 seasonalIndex[1..12] = sum(m) / sum(12ヶ月)。データ 12 ヶ月未満のケースは後述のエッジケース E01 でアラート
  • 売上 CAGR: Constants.getParam('F10_REVENUE_CAGR', 0.1) が明示値を持てばそれを採用。無ければ直近 12 ヶ月 vs その前 12 ヶ月の売上比から算出(複合年成長率 = (最終年 / 初年)^(1/年数) - 1)
  • 変動費対売上比率: AccountRepository.findAll().dtos を走査し、dto['固変区分'] === '変動費' の科目を抽出 → 過去 12 ヶ月の 税抜金額_実績 合計 / 売上合計 で各科目別比率を算出
  • 固定費月平均: dto['固変区分'] === '固定費' の科目について、直近 12 ヶ月の月平均値をベースに Constants.getParam('F10_FIXED_COST_INCREASE_RATE', 0.02) でインフレさせる

Step 2: 月次 60 ヶ月 P/L 推計エンジン

FinancialModelingService.projectMonthlyPL_(startYm, months, metrics) を実装。

  1. 開始年月を Constants.getParam('F10_ANALYSIS_START_YM', 直近月+1) で決定。本日が 2026-04 なら予測開始は 2026-05 が標準
  2. Utils.addMonths(startYm, i)i = 0..59 を生成(new Date() への直接加算禁止
  3. 各月の売上 = 年次売上 × seasonalIndex[month](年次売上は前年 × (1 + CAGR))
  4. 各変動費科目 = 当月売上 × 対売上比率
  5. 各固定費科目 = 前月 × (1 + monthlyRate) ここで monthlyRate = (1 + 年率増加率)^(1/12) - 1
  6. 営業利益 / 税引前利益 / 法人税等(Constants.TAX_RATES 累進)/ 当期純利益を算出
  7. 戻り値: Array<{年月, 科目名, 値, セクション}>

Step 3: 出力シート 94_fs_longterm_forecast 生成

動的上書き型(DDL 管理外、CLAUDE.md「DDL で管理されないタブ」リストに追記)。

  • 未存在時は ss.insertSheet('94_fs_longterm_forecast') で自動作成
  • 存在時は sheet.clearContents() で洗い替え
  • レイアウト: 1-3 行目にメタ情報(実行日時・対象期間・パラメータ)、5 行目ヘッダー(科目名 + 60 ヶ月)、6 行目以降に科目別の月次値
  • setValues() で一括書込み(セル単位ループ禁止)
  • フォーマッティング: 数値セルに #,##0 表示形式を setNumberFormat で適用、年間小計行は背景色 #f3f3f3

Step 4: メニュー登録

000_infra/002_constants.jsMENU_DEFINITION 既存カテゴリ 📋 サイドバー: 📊 マート更新 に以下を追加:

{ label: "📈 5 ヶ年財務モデル更新", funcName: "buildFiveYearForecast", description: "過去実績から 60 ヶ月分の月次 P/L 予測を 94_fs_longterm_forecast に出力" }

グローバル関数 buildFiveYearForecast()611_financial_modeling.js に定義し FinancialModelingService.run() に委譲する。

Step 5: 前提パラメータ整備

03_sys_params に以下のキーを追加(マイグレーションスクリプト不要・初回実行時に Constants.getParam(key, defaultVal) のフォールバックで動作、運用者が必要に応じて手動入力):

キーデフォルト意味
F10_REVENUE_CAGR数値0.10売上の年次複合成長率(0.10 = 10%)
F10_FIXED_COST_INCREASE_RATE数値0.02固定費の年次増加率(インフレ率)
F10_LOOKBACK_MONTHS数値36過去実績の参照月数
F10_ANALYSIS_START_YM文字列空文字予測開始年月(空なら直近月+1 を自動採用)

将来課題(Phase 2)として明記するのみで本スコープでは非実装:

  • B/S・CF 予測(MAS-018 完了後の後続案件)
  • 投資案件の CF 重ね合わせ(MAS-013 の 29_mst_investment_plan 参照)
  • シナリオ切替 UI(MAS-005 で対応)

影響範囲

ファイル変更種別
600_report/611_financial_modeling.js(新規)新規~400 行
000_infra/002_constants.jsMENU_DEFINITION 追加 1 項目~3 行
CLAUDE.mdDDL 管理外タブに 94_fs_longterm_forecast 追記~1 行
docs/_config.jsonnav §E.5 追加~1 行
docs/_internal/changelog.md初版記録~1 行

既存動作への影響

  • 実績マート(61_pl_monthly / 71_bs / 42_trn_journal)は 読取のみ。書込は一切行わない
  • 既存の財務 3 表タブ(91_fs_bs / 92_fs_pl / 82_cf_plan)・科目マスタへの影響なし
  • AccountRepository.findAll() / findAsMap() の API 変更なし(読取利用のみ)

注意事項

  1. AccountRepository.findAsMap(){stmt, cat} のみ返す。固変区分取得には AccountRepository.findAll().dtos の各 dto['固変区分'] を直接参照すること。型の不一致を埋めるため、実装時に一時キャッシュ Map<科目名, 固変区分> を構築するのが良い
  2. 列参照はヘッダー名ベースheaders.indexOf('列名'))。列番号ハードコード禁止(CLAUDE.md コーディング規約)
  3. Constants.getParam() は同一実行内でキャッシュされるため、複数回呼び出しても追加の I/O は発生しない。反面、同一実行中にパラメータを書き換えても反映されないことに注意
  4. 94_fs_longterm_forecast 未存在時は自動作成ss.insertSheet('94_fs_longterm_forecast') を先行実行
  5. メニュー名は既存形式に完全に倣う(failure_patterns #18-#20 対策)。MENU_DEFINITION L231-240 を Read で裏取りしてから追加
  6. appsscript.jsonoauthScopes には触らない(failure_patterns #26)
  7. failure_patterns #21(getLastColumn() 脆弱性)対策: 月次列数は常に 60 固定、データ書込時は明示的な範囲指定を使用
  8. 数式を埋め込まない(failure_patterns #22-#24)。全ての計算値は GAS 側で計算して setValues で書込む。Volatile 関数(NOW() / TODAY())使用禁止

Human-in-the-Loop(HitL)ポリシー

  • 予測結果は 94_fs_longterm_forecast への出力のみ。他タブへの自動反映禁止
  • 成長率の自動抽出値が明らかに異常(年率 >100% or <-50%)の場合は赤背景+警告メタ行を表示
  • ユーザーによる手動調整を想定し、出力セルには保護を設定しない(自由編集可能)。ただし再実行時は clearContents() で上書きされる旨をダイアログで事前警告

影響範囲

注意事項

エッジケース

#条件表示値・動作理由
E01実績データが 12 ヶ月未満SpreadsheetApp.getUi().alert() でエラー表示して処理中断季節性指数・対売上比率の計算に最低 12 ヶ月が必要
E02実績データが 12〜35 ヶ月(CAGR 算出最短の 24 ヶ月にも満たないケース含む)Utils.logInfo() でログ出力して処理継続。CAGR 算出不可の場合はデフォルト値 F10_REVENUE_CAGR を採用データ不足を記録した上で可能な期間で計算
E03売上高がゼロの月対売上高比率計算の対象月から除外(分母ゼロ回避)ゼロ除算防止(failure_patterns #2)
E04全分析期間で売上高がゼロ変動費 = 0 として予測、Utils.logInfo() でログ出力比率算出不能のためフォールバック
E05dto['固変区分'] が空欄・未設定の科目固定費として扱い Utils.logInfo() でログ出力保守的推計(コストを過大に見積もる方向)
E0603_sys_params に F10_ プレフィックスキー未設定Constants.getParam(key, defaultVal) のデフォルト値で動作getParam のフォールバック機能活用
E0794_fs_longterm_forecast シート未存在ss.insertSheet() で自動作成後に書込み初回実行時のエラー防止
E08CAGR 算出で初年売上が 0(分母ゼロ)デフォルト F10_REVENUE_CAGR を採用、警告ログゼロ除算防止
E09季節性指数の合計が 1.0 でない(端数誤差)最終月で調整して合計 = 1.0 を担保年間合計がズレないよう正規化
E10固定費月平均計算で対象月が 0 件(全月とも 税抜金額_実績 = 0)固定費 = 0 として予測除算対象月が無いケースの安全装置
E11成長率の自動抽出値が年率 >100% or <-50%(異常値)赤背景 #f4cccc + 警告メタ行「⚠️ 異常値検出」を出力シート先頭に表示Human-in-the-Loop での異常検知喚起
E12開始年月が過去(例: F10_ANALYSIS_START_YM に 2020-01 が設定)そのまま予測開始年月として採用(過去予測 = バックテスト用途)ユーザー意図の尊重(強制補正しない)
E13開始年月のフォーマット不正(例: 2026/05Utils.parseDateToYm で正規化試行、失敗時はデフォルト(直近月+1)採用入力揺れ吸収
E1411_mst_account の固変区分列が未追加(DDL 未実行)処理開始時に headers.indexOf('固変区分') が -1 の場合はエラーダイアログDDL 未整備時の明示エラー(MAS-024 未実行環境への配慮)
E15法人税等の計算で赤字年(税引前利益 ≤ 0)Constants.TAX_RATES.localMinimumAnnual(地方税均等割)のみ加算MAS-013 と同じ累進税率ロジック
E16税引前利益が 800 万円超800 万以下は 21.4%、超過分を 33.6% で段階加算Constants.TAX_RATES の累進構造に従う
E17月末書込時に 94_fs_longterm_forecast の既存データが 60 ヶ月分超(前回実行時の残骸)sheet.clearContents() で全消去してから書込冪等性担保、前回残りデータの混入防止

実データ検証(MCP での事前確認項目)

実装着手前に以下を MCP 等で必ず確認する:

  1. 11_mst_account のヘッダー行: 固変区分列の正確なヘッダー名(固変区分 / 固変 / variable_fixed 等の表記)と格納値(変動費 / 固定費 / 半固定 / VAR / FIXED 等)を確定。DDL 定義と実データの乖離チェック(failure_patterns #3 対策)
  2. 42_trn_journal のヘッダー行: 科目名 / 税抜金額_実績 / 発生日(P/L計上日) の列インデックスを確定。同名の列が複数ある場合は indexOf 返り値の位置を確認
  3. 03_sys_params の F10_ プレフィックス: 既存登録されていないことを確認。実装後、運用者が明示値を入れる前に Constants.getParam のデフォルト値で動作することを検証
  4. 01_sys_config のシートキー: FS_LONGTERM キーが未使用であることを確認(重複登録回避)
  5. Constants.TAX_RATES の累進ブラケット定義: localMinimumAnnual / 800 万円境界 / 実効税率が MAS-013 と同じ値を使用できることを確認

関連ドキュメント

仕様書関連箇所
dev_mas-018_financial_statement_linkage.mdB/S・CF 推計連携のインターフェース(本案件 Step 2 の MVP では未使用、Phase 2 で追加)
dev_mas-013_investment_simulation.md累進税率の算出ロジック共通化、投資案件 CF 重ね合わせの接続口
dev_mas-024_bep_analysis.md固変区分の活用方法(本案件で踏襲)
dev_mas-020_yoy_comparison.md年次成長率の抽出パターン参考
dev_mas-001_variance_analysis.mdFP&A 系仕様書の参考フォーマット
CLAUDE.mdファイル番号体系・コーディング規約(列参照・有効フラグスキップ等)

人間が検討すべき事項

  1. 成長率前提の決め方(TODO_future.md 転記): 売上 CAGR / 固定費増加率のデフォルト値の妥当性。業種別・規模別でベンチマークを参照すべきか
  2. 成長率抽出方式の優先度: CAGR(長期トレンド重視)vs 直近 12 ヶ月平均(足元重視)のどちらをデフォルトにするか
  3. 調整額列の追加: 生成された予測値を手動で上書き調整するための +調整額 列を 94_fs_longterm_forecast に追加すべきか(MVP では非実装)
  4. B/S・CF 5 カ年予測の拡張: MAS-018 完了後に追加する際のシート設計(同一タブに追加 vs タブ分割)
  5. 03_sys_params パラメータキー名の命名規則: F10_ プレフィックスの命名規則を DDL または README に明記する必要があるか
  6. 投資案件 CF の重ね合わせ運用: MAS-013 の 29_mst_investment_plan と本機能の接続仕様(Phase 2 で追加)
  7. シナリオ対応: MAS-005 with What-if UI 完成後、シナリオ切替(楽観 / 標準 / 悲観)を本機能に統合する手順
  8. 予測精度の事後検証: 予測実行から 6 ヶ月後に実績と照合する「バックテスト機能」の必要性
  9. データ 36 ヶ月超の長期実績: 古い実績を予測にどれだけ反映すべきか(減衰重み付き平均の導入)
  10. アラート閾値の運用: 異常値検知(E11 の年率 >100%/<-50%)の閾値をシステム固定 vs 03_sys_params で可変にするか

実装プロンプト(Claude Code 用)

あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者です。
案件 MAS-010「中長期(5カ年)財務モデリング」を実装してください。

## 実行前タスク
1. `docs/dev/dev_mas-010_financial_modeling.md` 全文を Read して本仕様書の方針を把握
2. `200_data/202_repository.js` L270-341 を Read し AccountRepository.findAll() の dtos 型と JournalRepository.findAll() の戻り値を確認
3. `000_infra/002_constants.js` L147-167 を Read し Constants.getParam(key, defaultVal) の引数・デフォルト値の渡し方を確認
4. `000_infra/002_constants.js` L231-240 を Read し MENU_DEFINITION の既存フォーマット(label/funcName/description)を確認
5. `000_infra/004_utils.js` L354/L389/L453 を Read し Utils.parseDateToYm / addMonths / parseAmt の引数・戻り値を確認
6. MCP またはシート直読で `11_mst_account` のヘッダー行を確認し、固変区分列の正確なヘッダー名と格納値("変動費"/"固定費" 等の実表記)を特定
7. MCP で `42_trn_journal` のヘッダー行を確認し `科目名` / `税抜金額_実績` / `発生日(P/L計上日)` の列位置を確定

## 修正対象ファイル
- `600_report/611_financial_modeling.js`(新規作成のみ、既存 601〜609 は変更しない)
- `000_infra/002_constants.js`(MENU_DEFINITION に 1 項目追加のみ)
- `CLAUDE.md`(DDL 管理外タブリストに `94_fs_longterm_forecast` を 1 行追記)
- `docs/_config.json`(nav §E.5 に 1 エントリ追加)

## 実装内容

### Step 1: サービス定義の骨格
`600_report/611_financial_modeling.js` に `var FinancialModelingService = {};` を定義し、以下のメンバーを順次実装:
- `run()` — メニューから呼ぶ公開関数
- `extractHistoricalMetrics_(lookbackMonths)` — 過去実績から成長率・費用構造を抽出
- `projectMonthlyPL_(startYm, months, metrics)` — 60 ヶ月の P/L 予測を生成
- `writeToSheet_(projection, params)` — 94_fs_longterm_forecast に書込み

### Step 2: extractHistoricalMetrics_ の実装
- JournalRepository.findAll().dtos から `発生日(P/L計上日)` を Utils.parseDateToYm で年月化
- AccountRepository.findAll().dtos から `Map<科目名, 固変区分>` を構築(findAsMap は使わない)
- 過去 12 ヶ月の月別売上を集計 → 年間合計に対する構成比 = 季節性指数(12 個の配列)
- 直近 12 ヶ月 vs その前 12 ヶ月の売上比から CAGR を算出(初年売上 = 0 の場合は Constants.getParam デフォルト)
- 固変区分 === '変動費' の科目について対売上比率(過去 12 ヶ月平均)を科目別に算出
- 固変区分 === '固定費' の科目について過去 12 ヶ月平均を算出

### Step 3: projectMonthlyPL_ の実装
- 開始年月 = Constants.getParam('F10_ANALYSIS_START_YM', 直近月+1)
- for i = 0..59 で Utils.addMonths(startYm, i) を呼び年月ループ
- 年次売上 = 前年 × (1 + CAGR)、当月売上 = 年次売上 × seasonalIndex[月-1]
- 変動費 = 当月売上 × 対売上比率(科目別)
- 固定費 = 前月 × (1 + monthlyRate) ここで monthlyRate = (1 + 年次増加率)^(1/12) - 1
- 営業利益 / 税引前利益 / 法人税等(Constants.TAX_RATES 累進)/ 当期純利益を算出
- 戻り値: Array<{年月, 科目名, 値, セクション}>

### Step 4: writeToSheet_ の実装
- シート未存在なら ss.insertSheet('94_fs_longterm_forecast') で自動作成
- sheet.clearContents() で全消去
- 1-3 行目にメタ情報(実行日時・対象期間・CAGR・固定費増加率・異常値検出フラグ)
- 5 行目ヘッダー: 科目名 + 60 ヶ月(Utils.addMonths で生成)
- 6 行目以降に科目別の月次値
- setValues() で一括書込み(セル単位ループ禁止)
- 数値セルに `#,##0` 表示形式を setNumberFormat で適用
- 年間小計行(12 ヶ月ごと)の背景色を `#f3f3f3` に

### Step 5: run() 公開関数
- 設定読込(F10_REVENUE_CAGR / F10_FIXED_COST_INCREASE_RATE / F10_LOOKBACK_MONTHS / F10_ANALYSIS_START_YM)
- extractHistoricalMetrics_ → projectMonthlyPL_ → writeToSheet_ を順に実行
- 処理完了時に SpreadsheetApp.getUi().alert() で結果サマリーを表示
- 異常値検出時(年率 >100% or <-50%)は警告テキストを alert に含める

### Step 6: メニュー追加
002_constants.js の MENU_DEFINITION `📋 サイドバー: 📊 マート更新` カテゴリに:
`{ label: "📈 5 ヶ年財務モデル更新", funcName: "buildFiveYearForecast", description: "過去実績から 60 ヶ月分の月次 P/L 予測を 94_fs_longterm_forecast に出力" }`
を追加。611_financial_modeling.js の末尾に `function buildFiveYearForecast() { return FinancialModelingService.run(); }` を定義。

### Step 7: CLAUDE.md 追記
CLAUDE.md「DDL (setupAllSchemas) で管理されないタブ」リストに `94_fs_longterm_forecast` を 1 行追加。

### Step 8: _config.json nav 追加
§E.5 FP&A・レポーティング セクションに `{ "file": "dev/dev_mas-010_financial_modeling.md", "title": "E.5.X MAS-010 中長期(5カ年)財務モデリング" }` を追加(X は既存連番の次)。

## 制約
- AccountRepository.findAsMap() は {stmt, cat} のみ返す。固変区分は AccountRepository.findAll().dtos から取得すること
- 月次ループは Utils.addMonths() を使用。new Date() への直接加算禁止
- 列参照はヘッダー名ベース(headers.indexOf())。列番号ハードコード禁止
- 既存の 601_datamart_ingest.js 〜 609_datamart_kpi.js は変更しない
- メニュー文字列は MENU_DEFINITION 既存形式に完全に倣う(failure_patterns #18-#20)
- appsscript.json の oauthScopes には触らない(failure_patterns #26)
- 数式を埋め込まない(failure_patterns #22-#24)。Volatile 関数(NOW/TODAY)使用禁止
- getLastColumn() は使わない(failure_patterns #21)。月数 60 は明示定数として扱う

## エッジケース
仕様書 §エッジケース E01〜E17 を全件カバー。特に:
- E01: 実績 12 ヶ月未満 → alert エラー中断
- E03: 売上ゼロ月 → 対売上比率計算から除外
- E05: 固変区分空欄 → 固定費として扱う
- E11: 異常値(年率 >100%/<-50%)→ 赤背景 + 警告メタ行
- E14: 固変区分列未追加(DDL 未実行)→ エラーダイアログ

## 実データ検証
実装前に MCP で以下を確認:
1. `11_mst_account` の固変区分列の正確なヘッダー名と格納値
2. `42_trn_journal` の `科目名` / `税抜金額_実績` / `発生日(P/L計上日)` の列インデックス
3. `03_sys_params` に F10_ プレフィックスのキーが未登録であること
4. `01_sys_config` の `FS_LONGTERM` キーが未使用であること

## 動作確認
1. `npm run push:dev` で dev 環境にデプロイ
2. GAS エディタで onOpen() を手動実行し、サイドバーに「📈 5 ヶ年財務モデル更新」が表示されることを確認
3. メニューから実行し、`94_fs_longterm_forecast` に 60 ヶ月分のデータが出力されることを確認
4. メタ行に実行日時・CAGR・固定費増加率が正しく表示されることを確認
5. `03_sys_params` の `F10_REVENUE_CAGR` を 0.10 → 0.20 に変更して再実行し、売上予測が上振れすることを確認
6. テスト環境で実績データを 12 ヶ月未満にフィルタし、エラーダイアログが表示されることを確認(E01)
7. `11_mst_account` の固変区分列を一時削除して実行し、エラーダイアログが表示されることを確認(E14)

### 拡張思考の使用状況
| フェーズ | 拡張思考 | 備考 |
|---|:---:|---|
| Phase 1 調査 | あり | 固変区分列の実表記・既存メニュー構造の裏取り |
| Phase 2 実装 | なし | 仕様書で決定済みの内容を書き下すのみ |

推奨実行モデル

工程推奨モデル理由
Step 1 骨格定義Claude Haiku 4.5機械的な関数骨格
Step 2-3 計算エンジンClaude Opus 4.7 (1M context)累進税率・季節性・CAGR 等の会計ロジック横断判断
Step 4-5 出力 + run()Claude Sonnet 4.6SpreadsheetApp / setValues / setNumberFormat の既存パターン適用
Step 6-8 メニュー + 登録Claude Haiku 4.5既存形式の踏襲のみ
統合テスト設計Claude Opus 4.7 (1M context)実データ検証 + エッジケース E01-E17 の end-to-end 確認

公開 API

FinancialModelingService.simulateWithOverlay(overlay, opts)

MAS-011(What-if シミュレーション)との連携用に追加された公開 API。baseline 60 ヶ月予測に overlay 仮想イベントを重ね、baseline / scenario 双方の FY 集計(P/L + CF + 現金残高)を返す。既存関数(projectMonthlyPL_ / computeBsCfLinkage_ / aggregateByFiscalYear_)は無変更で再利用し、本 API は追加のみ(_applyOverlay_ / _fyToSummary_ の 2 ヘルパと simulateWithOverlay 本体)。スプレッドシートへの書き込みは一切行わない。

シグネチャ

/**
 * @param {Array<{pYm:string, amt:number, kind:string}>} overlay
 * @param {Object} [opts]
 * @param {number} [opts.openingCash=0]     期首現金残高 (円)
 * @param {number} [opts.lookbackMonths]    実績参照月数 (未指定で F10_LOOKBACK_MONTHS)
 * @param {number} [opts.fiscalStartMonth]  会計年度開始月 (未指定で CFG_FISCAL_START_MONTH、不正値は 4)
 * @param {number} [opts.cagr]              CAGR 上書き (未指定で自動抽出)
 * @returns {{ baseline:Object, scenario:Object, meta:Object }}
 */
FinancialModelingService.simulateWithOverlay(overlay, opts)

overlay 形式

[{ pYm: 'YYYY-MM', amt: number, kind: 'revenue'|'variable'|'fixed' }]

  • pYm: 発生年月。baseline の months[] に含まれない月のイベントは無視される(idxOfMonth 照合)
  • amt: 円単位の金額。正値は totalRevenue / totalVariable / totalFixed に加算
  • kind: 3 種のみ。revenue → 売上増、variable → 変動費増、fixed → 固定費増
    • 呼び出し側(MAS-011)は AccountRepository の科目マスタから cat='収益' / 固変区分='変動費' 判定で overlay に変換する

opts

キー既定値用途
openingCash0期首現金残高(computeBsCfLinkage_ の第 2 引数に渡す)
lookbackMonthsF10_LOOKBACK_MONTHS(既定 36)実績参照月数。短縮すると実績不足エラー(insufficientData)を回避可能
fiscalStartMonthCFG_FISCAL_START_MONTH(既定 4)会計年度開始月。1-12 の範囲外は 4 にフォールバック
cagr自動抽出metrics.cagr を上書き(シナリオ分析で成長率前提を振る用途)

戻り値

{
  baseline: {  // baseline projection の FY サマリ (_fyToSummary_)
    labels, fyRanges,
    totalRevenue, totalVariable, totalFixed,
    grossProfit, operatingProfit, preTaxProfit, taxArr, netProfit,
    operCf, investCf, financeCf, netCashChange, cashBalance
  },
  scenario: { /* 上と同形式。overlay 適用後 */ },
  meta: {
    startYm,              // 予測開始年月 (次 FY 開始月にアラインメント済)
    fiscalStartMonth,     // 適用された会計年度開始月
    cagr,                 // 使用した CAGR
    forecastMonths,       // 予測月数 (FORECAST_MONTHS = 60)
    lookbackUsed,         // 実際に参照した実績月数
    overlayEventCount     // 適用された overlay イベント数
  }
}

法人税再計算

_applyOverlay_ では overlay 適用後に scn.preTaxProfit から法人税をカレンダー年度単位で再計算する(computeCorporateTax_ を経由し、projectMonthlyPL_ と同じロジックを踏襲)。年税額は月数で等分し、最終月で端数調整。

呼び出し元

  • 400_domain/430_what_if_simulator.js_runFiveYear_() から、mode='FIVE_YEAR' モードで使用(MAS-011 MVP)
  • 将来 MAS-005(動的シナリオ UI)/ MAS-013 感度分析の 5 カ年連結モード等からの再利用を想定

使用例

var overlay = [
  { pYm: '2026-05', amt: -600000, kind: 'fixed' },   // 給与 60 万円/月 追加
  { pYm: '2026-08', amt:  500000, kind: 'revenue' }  // 売上 50 万円/月 立ち上がり
];
var result = FinancialModelingService.simulateWithOverlay(overlay, {
  openingCash: 5000000,
  fiscalStartMonth: 4
});
// result.scenario.netProfit[0] - result.baseline.netProfit[0] = FY1 純利益 Delta

変更履歴

日付変更内容
2026-04-22初版作成。Gemini Pro メタプロンプト + Claude Sonnet 添削(scripts/1_generate_prompts_gemini.js パイプライン)で生成された tasks/prompts/task_F-10.md.done をベースに執筆。5 カ年月次 P/L ベースライン予測エンジン(FinancialModelingService611_financial_modeling.js 新規)の仕様。MAS-018(3 表連動)完了後に B/S・CF 予測を段階拡張する方針。AccountRepository.findAsMap() が固変区分を含まないため findAll() の dto を直接参照する注意点を明記。エッジケース 17 件・人間検討事項 10 件・推奨実行モデル 5 工程を定義
2026-04-22MAS-011 MVP (PR #315) 連携用に simulateWithOverlay(overlay, opts) 公開 API を追加。既存関数(projectMonthlyPL_ / computeBsCfLinkage_ / aggregateByFiscalYear_)は無変更で再利用し、_applyOverlay_ / _fyToSummary_ の 2 ヘルパと本 API を追加。overlay 形式・opts・戻り値・法人税再計算ロジックを「公開 API」セクションに明記

仕様書作成プロンプト

展開して表示(Gemini Pro + Claude Sonnet レビュー済み・`tasks/prompts/task_F-10.md.done`)
<instruction>
【タイムアウト回避・実行原則(v1.7・必ず遵守すること)】
1. **拡張思考の使い分け**: Phase 1(設計)ではフル活用し、ファイル名形式・エッジケース一覧・Step 分割粒度・固有名詞(関数名/シート名/列名/行番号)を完全確定させる。Phase 2(清書)の各 Step 内では最小限に抑え、Phase 1 確定内容の書き下しに徹する。出力途中で再考しない。
2. **テキスト報告の禁止**: 「〜を作成します」等の text のみで tool_use なしに turn を終了しない。説明は 1 文以内。直ちに tool を呼ぶ。
3. **4-5 分割の Write/Edit 実行**: 2-1(骨格 ~20行)/2-2(概要〜注意事項 ~300行)/2-3a(エッジケース〜人間検討事項 ~200行)/2-3b(実装プロンプト〜変更履歴 ~250行)/2-4(`<details>` プロンプト記録)に必ず分割して実行する。
4. **各 Step で何を書くかを具体指示**: 設計判断を Phase 2 実行時に持ち込まない。

======================================================================
あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者兼仕様書ライターです。
案件 MAS-010「中長期(5カ年)財務モデリング」の開発仕様書を作成してください。

(Phase 1 調査項目 1-A〜1-C-4、Phase 2 Step 2-1〜2-4、Phase 3 登録・記録の全内容が記載される。
全文は `tasks/prompts/task_F-10.md.done` を参照)
</instruction>

※ Gemini Pro (gemini-2.5-pro) が docs/_internal/failure_patterns.md / docs/_internal/dev_spec_prompt_template.md / コアコード 4 ファイル(002_constants.js / 003_contracts.js / 004_utils.js / 202_repository.js)を読み込んで設計した後、Claude Sonnet 4.6 が実行者目線で添削した成果物。プロンプトパイプライン: scripts/1_generate_prompts_gemini.jstasks/prompts/task_F-10.md(→ 処理完了後 .done にリネーム)