MAS-010: 中長期(5カ年)財務モデリング
概要
| 項目 | 内容 |
|---|---|
| 案件ID | MAS-010 |
| カテゴリ | FP&A・シミュレーション |
| Phase | P2 |
| 優先度 | ★★ |
| 所要時間 | 8-12h(MVP: P/L のみ先行) |
| 対象ファイル | 600_report/611_financial_modeling.js(新規) / 000_infra/002_constants.js(MENU_DEFINITION / 03_sys_params キー追加) / 01_sys_config(FS_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() | L323 | Object.<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_DEFINITIONL231-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.js に FinancialModelingService.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) を実装。
- 開始年月を
Constants.getParam('F10_ANALYSIS_START_YM', 直近月+1)で決定。本日が2026-04なら予測開始は2026-05が標準 Utils.addMonths(startYm, i)でi = 0..59を生成(new Date()への直接加算禁止)- 各月の売上 =
年次売上 × seasonalIndex[month](年次売上は前年 × (1 + CAGR)) - 各変動費科目 =
当月売上 × 対売上比率 - 各固定費科目 =
前月 × (1 + monthlyRate)ここでmonthlyRate = (1 + 年率増加率)^(1/12) - 1 - 営業利益 / 税引前利益 / 法人税等(
Constants.TAX_RATES累進)/ 当期純利益を算出 - 戻り値:
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.js の MENU_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.js | MENU_DEFINITION 追加 1 項目 | ~3 行 |
CLAUDE.md | DDL 管理外タブに 94_fs_longterm_forecast 追記 | ~1 行 |
docs/_config.json | nav §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 変更なし(読取利用のみ)
注意事項
AccountRepository.findAsMap()は{stmt, cat}のみ返す。固変区分取得にはAccountRepository.findAll().dtosの各dto['固変区分']を直接参照すること。型の不一致を埋めるため、実装時に一時キャッシュMap<科目名, 固変区分>を構築するのが良い- 列参照はヘッダー名ベース(
headers.indexOf('列名'))。列番号ハードコード禁止(CLAUDE.md コーディング規約) Constants.getParam()は同一実行内でキャッシュされるため、複数回呼び出しても追加の I/O は発生しない。反面、同一実行中にパラメータを書き換えても反映されないことに注意94_fs_longterm_forecast未存在時は自動作成。ss.insertSheet('94_fs_longterm_forecast')を先行実行- メニュー名は既存形式に完全に倣う(failure_patterns #18-#20 対策)。
MENU_DEFINITIONL231-240 を Read で裏取りしてから追加 appsscript.jsonのoauthScopesには触らない(failure_patterns #26)- failure_patterns #21(
getLastColumn()脆弱性)対策: 月次列数は常に 60 固定、データ書込時は明示的な範囲指定を使用 - 数式を埋め込まない(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() でログ出力 | 比率算出不能のためフォールバック |
| E05 | dto['固変区分'] が空欄・未設定の科目 | 固定費として扱い Utils.logInfo() でログ出力 | 保守的推計(コストを過大に見積もる方向) |
| E06 | 03_sys_params に F10_ プレフィックスキー未設定 | Constants.getParam(key, defaultVal) のデフォルト値で動作 | getParam のフォールバック機能活用 |
| E07 | 94_fs_longterm_forecast シート未存在 | ss.insertSheet() で自動作成後に書込み | 初回実行時のエラー防止 |
| E08 | CAGR 算出で初年売上が 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/05) | Utils.parseDateToYm で正規化試行、失敗時はデフォルト(直近月+1)採用 | 入力揺れ吸収 |
| E14 | 11_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 等で必ず確認する:
11_mst_accountのヘッダー行: 固変区分列の正確なヘッダー名(固変区分/固変/variable_fixed等の表記)と格納値(変動費/固定費/半固定/VAR/FIXED等)を確定。DDL 定義と実データの乖離チェック(failure_patterns #3 対策)42_trn_journalのヘッダー行:科目名/税抜金額_実績/発生日(P/L計上日)の列インデックスを確定。同名の列が複数ある場合はindexOf返り値の位置を確認03_sys_paramsの F10_ プレフィックス: 既存登録されていないことを確認。実装後、運用者が明示値を入れる前にConstants.getParamのデフォルト値で動作することを検証01_sys_configのシートキー:FS_LONGTERMキーが未使用であることを確認(重複登録回避)Constants.TAX_RATESの累進ブラケット定義:localMinimumAnnual/ 800 万円境界 / 実効税率が MAS-013 と同じ値を使用できることを確認
関連ドキュメント
| 仕様書 | 関連箇所 |
|---|---|
| dev_mas-018_financial_statement_linkage.md | B/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.md | FP&A 系仕様書の参考フォーマット |
| CLAUDE.md | ファイル番号体系・コーディング規約(列参照・有効フラグスキップ等) |
人間が検討すべき事項
- 成長率前提の決め方(TODO_future.md 転記): 売上 CAGR / 固定費増加率のデフォルト値の妥当性。業種別・規模別でベンチマークを参照すべきか
- 成長率抽出方式の優先度: CAGR(長期トレンド重視)vs 直近 12 ヶ月平均(足元重視)のどちらをデフォルトにするか
- 調整額列の追加: 生成された予測値を手動で上書き調整するための
+調整額列を94_fs_longterm_forecastに追加すべきか(MVP では非実装) - B/S・CF 5 カ年予測の拡張: MAS-018 完了後に追加する際のシート設計(同一タブに追加 vs タブ分割)
03_sys_paramsパラメータキー名の命名規則:F10_プレフィックスの命名規則を DDL または README に明記する必要があるか- 投資案件 CF の重ね合わせ運用: MAS-013 の
29_mst_investment_planと本機能の接続仕様(Phase 2 で追加) - シナリオ対応: MAS-005 with What-if UI 完成後、シナリオ切替(楽観 / 標準 / 悲観)を本機能に統合する手順
- 予測精度の事後検証: 予測実行から 6 ヶ月後に実績と照合する「バックテスト機能」の必要性
- データ 36 ヶ月超の長期実績: 古い実績を予測にどれだけ反映すべきか(減衰重み付き平均の導入)
- アラート閾値の運用: 異常値検知(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.6 | SpreadsheetApp / 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 に変換する
- 呼び出し側(MAS-011)は
opts
| キー | 既定値 | 用途 |
|---|---|---|
openingCash | 0 | 期首現金残高(computeBsCfLinkage_ の第 2 引数に渡す) |
lookbackMonths | F10_LOOKBACK_MONTHS(既定 36) | 実績参照月数。短縮すると実績不足エラー(insufficientData)を回避可能 |
fiscalStartMonth | CFG_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 ベースライン予測エンジン(FinancialModelingService、611_financial_modeling.js 新規)の仕様。MAS-018(3 表連動)完了後に B/S・CF 予測を段階拡張する方針。AccountRepository.findAsMap() が固変区分を含まないため findAll() の dto を直接参照する注意点を明記。エッジケース 17 件・人間検討事項 10 件・推奨実行モデル 5 工程を定義 |
| 2026-04-22 | MAS-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.js → tasks/prompts/task_F-10.md(→ 処理完了後 .done にリネーム)