概要

項目内容
案件IDMAS-007
カテゴリFP&A・レポーティング
PhaseP3 (シミュレーション系)
優先度
所要時間約 6〜8 時間
対象ファイル600_report/610_datamart_driver_plan.js(新規)、100_config/101_sys_config.js(追記)、000_infra/002_constants.js(任意追記)
前提案件F-01(予実差異分析・実績値算出ロジック流用元)、F-25(66_pl_budget_variance のパターン参考)

目的

P/L 実績データ(42_trn_journal)と、ユーザーが手入力する計画ドライバー値(ARPU・チャーンレート・新規顧客獲得数等の業種固有 KPI)を組み合わせ、将来月次の P/L をシミュレーションする意思決定支援機能を新設する。完全自動化ではなく、人間がパラメータを調整しながら複数シナリオを繰り返し試行するツールと位置づける(Human-in-the-Loop)。これにより、単純な前年度ベース予算では捉えられないビジネスモデル特有の感応度を可視化し、経営判断の根拠データを提供する。

現在のコード

本機能は新規追加のため、修正対象となる既存コードは存在しない。実装時の参照先とする既存パターンは以下のとおり。

役割既存ファイル参照する実装
データマートの公開エントリポイント+シート全置換書き込み600_report/609_datamart_kpi.js (buildKpiDashboard() L21-L53)「ss 取得→sheet 取得 or insert→clear→ctx 構築→render→logInfo」の標準フロー
集計ロジック分離パターン600_report/603_datamart_pl.js (dmBuildPlSections_ L59-L79、dmCalcPl_ L115-L189)セクション定義配列+集計関数の分離スタイル
Repository 経由の実績取得200_data/202_repository.js (JournalRepository.findAll() L270-L272)readSheetAsDtos_{ headers, dtos } を取得する読み出し
全置換書き込み200_data/202_repository.js (writeDtosToSheet_ L38-L78)clearContent → setValues の全置換方式(冪等性
ヘッダー名ベースのアクセス000_infra/003_contracts.js (Contracts.toDtoList L197-L204、Contracts.toRow L212-L221)DTO ⇔ 行配列の変換(列番号ハードコード回避)

修正方針

以下の 3 コンポーネントを新設する。

① 入力シート 29_bud_drivers(新規)

ユーザーが計画ドライバー値を手入力するシート。Phase 1 で 25 番が 25_bud_finance (BUD_FIN) に占有済みであることを確認したため、2x 系の空き番号 29 を採用する。

  • DDL スキーマを setupAllSchemas に追加: 100_config/101_sys_config.js L826 以降の schemas オブジェクトに以下のエントリを追記する。挿入位置は BUD_ALLOC (L842) の直下、または 2x 系 BUD 群の末尾が適切。

    'BUD_DRVR': { headers: ["有効フラグ","ドライバーID","ドライバー名","ドライバー種別","対象年月","計画値","単位","摘要"], color: "#674ea7",
      validations: {
        "計画値": { type: 'range', min: -999999999, max: 999999999, helpText: '数値(小数可)。チャーンレートは 0〜1 の小数で入力' }
      }
    },
    
  • システムキー BUD_DRVR として 01_sys_config に登録: 100_config/101_sys_config.js L782 (BUD_ALLOC の直下) に以下を追記。

    if (!existKeys.includes('BUD_DRVR')) confSheet.appendRow(['BUD_DRVR', '', '29_bud_drivers', '予算_ドライバーベース計画(ARPU・チャーン等)']);
    
  • SHEET_DEFAULTS への登録(任意): 000_infra/002_constants.js L73 の SHEET_DEFAULTS 配列に追記。smartAddRow でユーザーが行追加した際のデフォルト値を補う。必要なデフォルトは限定的(ドライバー種別の初期値のみ)のため、ユーザー要望次第で追加する。追記する場合は以下の形式。

    { pattern: '29_bud_drivers', prefix: 'DRV_', defaults: { 'ドライバー種別': 'ARPU', '単位': '円', _dynamic: { '対象年月': 'nextYm' } } },
    

    さらに ID_PREFIX_MAP (L93) に以下を追記すると DRV_NNNN 形式の連番 ID が自動発番される。

    { pattern: '29_bud_drivers', prefix: 'DRV_', digit: 4, isDate: false },
    
  • カラム構成(提案):

    ヘッダー名説明
    A有効フラグbooleanFALSE 行はスキップ(コーディング規約準拠)
    BドライバーIDstringDRV_NNNN。ID 列で最終行判定(A 列のチェックボックス回避)
    Cドライバー名stringユーザー任意(例: "メインプロダクトARPU")
    Dドライバー種別stringARPU / チャーンレート / 新規顧客数 / カスタム の 4 種を想定
    E対象年月stringYYYY-MM 形式(Utils.parseDateToYm で正規化)
    F計画値numberARPU=円、チャーンレート=0〜1 の小数、新規顧客数=人数
    G単位string / % / / 等(表示用、計算には使わない)
    H摘要stringユーザーメモ

    ユーザーが行を自由追加できる柔軟な構造とし、対象年月×ドライバー種別の組合せで一意となる。

  • アクセス方法: コード内では必ず Utils.getSheetByKey('BUD_DRVR', '29_bud_drivers') 経由で取得する(PropertiesService 直接呼び出し禁止)。

② 計算エンジン 600_report/610_datamart_driver_plan.js(新規ファイル)

Phase 1 で 609_datamart_kpi.js まで使用済みを確認したため、次の連番 610 を使用する。

  • 公開エントリポイント: buildDriverBasedPlan() 関数。

  • 処理フロー:

    1. 入力読み込み: var sheet = Utils.getSheetByKey('BUD_DRVR', '29_bud_drivers'); でシートを取得し、var data = sheet.getDataRange().getValues();Contracts.toDtoList(data) に渡してドライバー計画値を { headers, rows } 形式で読み込む。有効フラグ === false の行はスキップ(コーディング規約準拠)。

    2. ベースライン算出: JournalRepository.findAll() で実績仕訳を取得し、直近 N 月(N は Constants.getParam('DRV_BASELINE_MONTHS', 3) で 03_sys_params から取得、デフォルト 3)の売上系科目(売上高売上原価 等)の合計から実績 ARPU・実績顧客数を算出する。顧客数は Constants.getParam('DRV_BASELINE_CUSTOMERS', 0) でユーザー指定値を採用するモードも用意(実績仕訳から顧客数は取れないため、現実的にはパラメータ指定)。

    3. 月次再帰計算: Utils.parseDateToYm で対象年月を YM 文字列に正規化、Utils.addMonths(baselineYm, n) でループ。漸化式は以下:

      顧客数(t)  = 顧客数(t-1) × (1 - チャーンレート(t)) + 新規顧客数(t)
      売上(t)    = 顧客数(t) × ARPU(t)
      

      ドライバー値は対象年月で 25_bud_drivers から検索(種別×年月キーのマップを事前構築)。該当月にデータがなければ「前月値繰越」(エッジケース表参照)。

    4. DTO 構築 → 全置換書き込み: 出力 DTO 配列を構築し、var outSheet = Utils.getSheetByKey('RPT_DRVPL', '67_pl_driver_based_plan'); でシート取得(無ければ ss.insertSheet('67_pl_driver_based_plan'))。var headers = outSheet.getRange(1, 1, 1, outSheet.getMaxColumns()).getValues()[0].map(function(h){ return String(h).trim(); }); でヘッダー取得後、writeDtosToSheet_(outSheet, headers, dtos) で全置換書き込み。writeDtosToSheet_GAS グローバルスコープ上で 200_data/202_repository.js から参照可能。

    5. 完了通知: Utils.toastResult('buildDriverBasedPlan', '完了: ' + dtos.length + '行出力'); および Utils.logInfo('buildDriverBasedPlan', '完了: ' + dtos.length + '行');

  • コーディング規約遵守事項:

    • 列参照はヘッダー名ベース(Contracts.toDtoList() で取得した headers 配列、または DTO のキー直接アクセス)。列番号ハードコード禁止。
    • 全社共通パラメータは Constants.getParam('key', defaultVal) 経由で 03_sys_params から取得。PropertiesService.getScriptProperties() を直接呼ばない。
    • 環境依存値(スプレッドシート ID 等)は Env モジュール経由で取得。

③ 出力シート 67_pl_driver_based_plan(新規)

Phase 1 で setupAllSchemas 内に 67 番が登録されていないことを確認したため、67 番を採用する。なお、F-20(66_pl_prev_year)/F-25(66_pl_budget_variance)/S-19(67_tb_monthly)/S-29(67_ar_aging_report)等の他案件で同番台が「予約候補」となっている dev 仕様書が存在するが、いずれも未実装かつ番号は重複候補が複数あるため、本仕様書では「実装が先んじた者勝ち」のポリシーで 67_pl_driver_based_plan を採用する。後続案件で衝突が発生した場合は当該案件側で再採番すること。

  • DDL スキーマを setupAllSchemas に追加: 100_config/101_sys_config.js L826 以降の schemas オブジェクトに以下を追記。

    'RPT_DRVPL': { headers: ["対象年月","顧客数","ARPU","売上(計画)","チャーンレート","新規顧客数","算出根拠","摘要"], color: "#1155cc" },
    
  • システムキー RPT_DRVPL として 01_sys_config に登録: L795 (PL_VAR の直下) に以下を追記。

    if (!existKeys.includes('RPT_DRVPL')) confSheet.appendRow(['RPT_DRVPL', '', '67_pl_driver_based_plan', 'P/Lドライバーベース計画']);
    
  • アクセス方法: コード内では Utils.getSheetByKey('RPT_DRVPL', '67_pl_driver_based_plan') 経由で取得。

  • 冪等性: writeDtosToSheet_ が内部で clearContent()setValues() を行う全置換方式のため、何度実行しても同一入力に対し同一結果になる。appendDtosToSheet_(追記方式)は使用しない。

メニュー登録

100_config/101_sys_config.js L233-L239 の MENU_DEFINITION「📋 サイドバー: 📊 マート更新」セクションに以下を追記する(buildKpiDashboard の直下が自然な配置)。

{ label: '📈 ドライバー計画 (ARPU/チャーン)', funcName: 'buildDriverBasedPlan', description: '29_bud_drivers のドライバー値から 67_pl_driver_based_plan を再計算' },

メニュー名は既存のサイドバー項目と統一感のある絵文字付き日本語表記とし、Phase 1 で確認した既存パターン(例: 📊 KPIダッシュボード再描画 / 📸 前年度P/Lスナップショット)に準拠する。造語のメニュー名は使わない。

影響範囲

  • 新規ファイル:
    • 600_report/610_datamart_driver_plan.js — 計算エンジン本体
  • 変更ファイル:
    • 100_config/101_sys_config.jssetupAllSchemasconfSheet.appendRow 追記(2 箇所: BUD_DRVR・RPT_DRVPL)、schemas オブジェクト追記(2 エントリ: BUD_DRVR・RPT_DRVPL)、MENU_DEFINITION への 1 項目追加
    • 000_infra/002_constants.jsSHEET_DEFAULTS への 1 エントリ追加(任意)、ID_PREFIX_MAP への 1 エントリ追加(任意)
  • 既存動作への影響: なし(純粋な新規追加)。既存 setupAllSchemas の DDL ハッシュ(S-62 増分モード)は schemas オブジェクト変更により自動的に Full モードへ切り替わる。

注意事項

  1. シート番号の確定: Phase 1 確認結果として、25_bud_drivers25_bud_finance (BUD_FIN) と衝突するため 29_bud_drivers に変更済み。67_pl_driver_based_plansetupAllSchemas 内で衝突なしのため当初案どおり 67 を採用。dev 仕様書段階で 66/67/68/69 を予約している他案件(F-20/F-25/S-19/S-29/S-51/F-38/F-39)が複数存在するため、本案件と並行して着手する案件がある場合は番号再調整の合意を取ること。
  2. writeDtosToSheet_ のスコープ: GAS は単一プロジェクト内で全関数がグローバルスコープに展開されるため、200_data/202_repository.js 内の writeDtosToSheet_ (関数名末尾 _ は「内部用」を示す慣習) は新規ファイル 600_report/610_datamart_driver_plan.js から呼び出し可能。ただしこの依存関係は明記しておくこと(将来 Repository を ES Module 化した際の移行ポイント)。
  3. buildHeaderIndex_ は使用禁止: コード規約には記載があるが、実装上 buildHeaderIndex_ 関数は提供されていない。列参照は Contracts.toDtoList() が返す headers 配列の indexOf メソッド、または dto[ヘッダー名] の直接アクセスで代替する。
  4. JournalRepository.findAll() の負荷: 全仕訳を読み込むため、ベースライン計算では直近 N 月分のみに dtos.filter() で絞ってから集計すること。N の既定値は 3 (Constants.getParam('DRV_BASELINE_MONTHS', 3))。データ量が増えた場合は読み込み列を絞る最適化が必要となる。
  5. Constants.getParam() のキャッシュ: _paramsCache はセッション内(GAS の同一実行)でキャッシュされる。テスト時、複数回 setupAllSchemas を実行する場合や 03_sys_params を編集した場合、キャッシュが残っているために旧値が読まれる可能性がある。動作確認時は新しい実行セッションで再検証すること。
  6. setupAllSchemas は特権ユーザー限定: isPrivilegedUser_() チェック (L750-L755) があるため、開発環境で実行する際は 03_sys_paramsPRIVILEGED_USERS に自身のメールアドレスが含まれていることを確認する。
  7. S-62 増分モード: schemas オブジェクトを変更した際、次回の setupAllSchemasIncremental 実行で自動的に Full モードに切り替わる(schemas のハッシュ差分検知)。手動で CFG_DDL_VERSION を bump する必要はない。

エッジケース

条件表示値 / 動作理由
顧客数(t-1) = 0 で ARPU・売上を計算(ゼロ除算)0 を表示(数式は if (prevCustomers === 0) return 0;分母ゼロのため計算スキップ。保守的にゼロ表示。'-' 文字列だと後段集計が NaN になるため数値 0 を採用
29_bud_drivers に対象月のドライバーデータが存在しない直前月のドライバー値を繰越(prevDriverValue を保持)空白月を 0 として計算するとチャーン 0 / 新規 0 で顧客数が固定化、ARPU 0 で売上急落となり実態と乖離するため、繰越(明示的定義)を採用
チャーンレートが 0〜1 の範囲外(例: 1.5、-0.1)バリデーションエラー:当該月をスキップ+算出根拠 列にエラーメッセージ「チャーンレート異常値: {value}」を記録計算結果が発散・負値化するため、異常値は計算対象外とする(DDL の range バリデーションでは小数値のため緩い制約しかかけられないので、計算側で再チェック)
実績ベースライン(JournalRepository.findAll())の対象月に売上仕訳が存在しないベースライン売上 = 0、ベースライン顧客数 = Constants.getParam('DRV_BASELINE_CUSTOMERS', 0) を起点として計算初期導入時・新規事業の場合に該当。保守的推計方向(ゼロ起点)を採用
計画対象月が実績取込済み月(直近実績月)より過去計算対象外・スキップし 算出根拠 列に「実績月のためスキップ」と記録実績との二重計上を防止(84_cf_daily_plan の設計思想と同じ:実績月は実績優先、未来月のみ計画)
Constants.getParam('DRV_BASELINE_MONTHS') が未設定defaultVal = 3 を使用(Constants.getParam 第2引数)getParam の第 2 引数 defaultVal を必ず指定する(規約)。3 は「直近1四半期相当」の保守的初期値
Constants.getParam('DRV_BASELINE_CUSTOMERS') が未設定defaultVal = 0 を使用既定で 0 起点。ユーザーが 03_sys_paramsDRV_BASELINE_CUSTOMERS=100 等を設定すれば反映される
29_bud_drivers有効フラグ が FALSE の行スキップ(コーディング規約: 「有効フラグ=FALSE の行は全処理でスキップ」)コード規約準拠。論理削除パターン
29_bud_drivers シートが未作成(DDL 未実行)Utils.getSheetByKey('BUD_DRVR', '29_bud_drivers')null を返す → エラーメッセージで「先に setupAllSchemas を実行してください」を Utils.toastResult 表示し処理終了ユーザー導線を明示。例外を握りつぶさず Utils.logError で記録
67_pl_driver_based_plan シートが未作成Utils.getSheetByKey()null を返した場合 getWebSpreadsheet_().insertSheet('67_pl_driver_based_plan') で自動生成し、ヘッダー行を 1 行目に書き込んでから処理続行初回実行時の自動セットアップ(setupAllSchemas 未実行でも単体実行可能にする)
同一年月×同一ドライバー種別の重複行が 29_bud_drivers に存在後勝ち(Map.set で上書き)+ Utils.logInfo で警告ログ集計上、最新値を優先するのが直感的。ただしユーザーには警告を出して気づかせる
計画期間が 0 ヶ月(ベースライン月のみ)ベースライン値のみ 1 行出力退避動作。最低 1 行の出力で「計算は走った」ことを保証

実データ検証

実装後、以下のチェックを開発スプレッドシートで実施する。

  1. 類似入力シートとの整合: 21_bud_pipeline22_bud_headcount23_bud_subscription の実際のカラム構成(100_config/101_sys_config.js L877-L894 の BUD_HC / BUD_PIPE / BUD_SUBS schemas)と 29_bud_drivers の設計を比較し、ヘッダー名(特に 有効フラグ管理ID 等の共通項)の命名規則を揃えること。本仕様書では ID 列名を ドライバーID としているが、必要に応じて 管理ID に統一しても良い。
  2. 実績仕訳に売上系科目が存在するか確認: JournalRepository.findAll() で取得した dto 配列を dto.科目名 === '売上高' でフィルタし、最低 1 件以上ヒットすることを確認。ヒットしない場合は 11_mst_account の科目マスタに 売上高 または 営業収益 系の科目が登録されているか照合する。
  3. 3 ヶ月先までの計算結果を手計算と照合: ベースライン顧客数 100、ARPU 10000、チャーン 0.05、新規 10 の単純条件を 29_bud_drivers に投入し、t+1=105×10000=1,050,000、t+2=110.25×10000=1,102,500 等の漸化計算結果がシートと一致することを確認。

関連ドキュメント

人間が検討すべき事項

  • TODO_future.md からの転記: 「モデル化するドライバー変数の選定(業種固有のKPI)」— 業種・ビジネスモデルにより最適なドライバー変数は異なる。本仕様書の初期実装では SaaS 系を想定した ARPU・チャーンレート・新規顧客数の 3 種を主軸とするが、対象事業のビジネスモデル(受託開発系・物販系・コンサル系等)に応じて事業担当者と合意の上で取捨選択する必要がある。
  • 追記①: ドライバー変数の選定責任: ARPU・チャーンレート・新規顧客獲得コスト・LTV・CAC 回収期間等、どの KPI をモデル化対象とするかは事業担当者(経営企画・事業責任者)との合意事項。システム初期実装では主要 4 種(ARPU / チャーンレート / 新規顧客数 / カスタム)に対応し、カスタム 種別を介して将来的にユーザー定義ドライバーを計算式に組み込める拡張性を残す。本格運用時には別途「ドライバー定義マスタ」を 30_mst_driver_def 等で正規化することを検討。
  • 追記②: 実行運用ポリシー: 本機能はバッチ自動実行(時刻トリガー等)ではなく、ユーザーが 29_bud_drivers を編集後に手動でメニュー(「📈 ドライバー計画 (ARPU/チャーン)」)から実行する Human-in-the-Loop 方式の意思決定支援ツールとして位置づける。前提として 67_pl_driver_based_plan の出力は「シナリオ試算結果」であり、これをそのまま 41_trn_budget 等に転記することはせず、人間が承認したシナリオのみを別途予算バージョンに反映する運用フローとする。
  • 追記③: ベースライン顧客数の取得方法: 仕訳データから顧客数を機械的に算出する方法は本仕様書では未定義(取引先名のユニーク数を使うか、12_mst_partner の取引先区分=顧客 の件数を使うか、CRM 連携が必要かは別途設計)。初期実装では Constants.getParam('DRV_BASELINE_CUSTOMERS', 0) でユーザー指定値を採用する簡易方式を採るが、本格運用時には顧客数取得ロジックを別案件で設計する必要がある。
  • 追記④: シナリオ管理機能の有無: F-05(動的シナリオプランニング UI)と密接に関連する機能のため、シナリオ間の比較や「楽観・悲観・現実」3 シナリオの並列出力は本案件のスコープ外とし、F-05 で実装することを推奨する。本案件は単一シナリオ計算に集中する。

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

あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者です。
案件 F-07「ドライバーベース計画の拡張」を実装してください。

## 実行前タスク

1. `600_report/601_datamart_ingest.js` を Read し、既存データマートビルダーのシート読み書きパターンを把握する(特に `getDataRange().getValues()` → `Contracts.toDtoList()` の流れ)
2. `600_report/609_datamart_kpi.js` を Read し、`buildKpiDashboard()` (L21-L53) の標準フロー(ss 取得→sheet 取得 or insert→clear→ctx 構築→render→logInfo→toastResult)を把握する
3. `100_config/101_sys_config.js` を Read し、`setupAllSchemas` (L749-) の既存スキーマ登録形式(L826 以降の `schemas` オブジェクト)と、`01_sys_config` への `confSheet.appendRow` パターン(L771-L823)、および `MENU_DEFINITION`「📋 サイドバー: 📊 マート更新」セクション (L233-L239) の実在するメニュー名・構造を確認する(メニュー名は造語しない)
4. `000_infra/002_constants.js` を Read し、`SHEET_DEFAULTS` (L73) の `{ pattern, prefix, defaults }` 形式と `ID_PREFIX_MAP` (L93) の形式を確認する
5. `200_data/202_repository.js` を Read し、`writeDtosToSheet_` (L38-L78、全置換)・`readSheetAsDtos_` (L19-) の実装と、`JournalRepository.findAll()` (L270-L272) の戻り値型を把握する
6. `000_infra/003_contracts.js` を Read し、`Contracts.toDtoList(data)` (L197-L204) と `Contracts.toRow(headers, dto)` (L212-L221) の使い方を確認する
7. `000_infra/004_utils.js` の `Utils.getSheetByKey` (L40)・`Utils.parseDateToYm` (L92)・`Utils.addMonths` (L127)・`Utils.logInfo` (L232)・`Utils.toastResult` (L258) のシグネチャを確認する

## 修正対象ファイル

- `600_report/610_datamart_driver_plan.js`(新規作成)
- `100_config/101_sys_config.js`(`setupAllSchemas` の `schemas` 追記 2 件、`confSheet.appendRow` 追記 2 件、および `MENU_DEFINITION` への 1 項目追加)
- `000_infra/002_constants.js`(`SHEET_DEFAULTS` への 1 エントリ追加と `ID_PREFIX_MAP` への 1 エントリ追加 ※任意)

## 実装内容

仕様書「修正方針」に記載した 3 コンポーネント(`29_bud_drivers` / `610_datamart_driver_plan.js` / `67_pl_driver_based_plan`)を実装する。

公開エントリポイントは `buildDriverBasedPlan()` 関数。処理フローは仕様書の「② 計算エンジン」節で明記したとおり:

1. `Utils.getSheetByKey('BUD_DRVR', '29_bud_drivers')` でシート取得 → `Contracts.toDtoList()` でドライバー計画値を読み込む(有効フラグ FALSE 行はスキップ)
2. `JournalRepository.findAll()` でベースライン値を算出(直近 N 月分のみ filter、N は `Constants.getParam('DRV_BASELINE_MONTHS', 3)`)
3. 月次再帰計算(`Utils.parseDateToYm` / `Utils.addMonths` 使用)。漸化式: 顧客数(t) = 顧客数(t-1) × (1 - チャーンレート(t)) + 新規顧客数(t)、売上(t) = 顧客数(t) × ARPU(t)
4. `Utils.getSheetByKey('RPT_DRVPL', '67_pl_driver_based_plan')` で出力シート取得(無ければ `ss.insertSheet()`)→ `writeDtosToSheet_(outSheet, headers, dtos)` で全置換書き込み
5. `Utils.toastResult('buildDriverBasedPlan', '完了: ' + dtos.length + '行出力')` で完了通知

## 制約

- 既存の `600_report/601_datamart_ingest.js` 〜 `609_datamart_kpi.js` は変更しない
- 列参照はヘッダー名ベース(`Contracts.toDtoList()` 経由、または DTO のキー直接アクセス)。列番号ハードコード禁止
- 書き込みは `writeDtosToSheet_()` の全置換パターンのみ。`sheet.appendRow()` は使わない(冪等性確保のため)
- `PropertiesService.getScriptProperties()` を直接呼ばない。`Env` モジュールまたは `Constants.getParam()` を使用
- シート番号は `29_bud_drivers`(25 が `25_bud_finance` と衝突するため変更済み)と `67_pl_driver_based_plan` を使用
- `buildHeaderIndex_` は実装上存在しないため使用禁止。代替は `headers.indexOf(name)` または `dto[name]` の直接アクセス

## エッジケース

仕様書「エッジケース」テーブルの全条件を実装すること:
- 顧客数(t-1)=0 のゼロ除算 → 0 表示
- ドライバー値の月欠損 → 前月値繰越
- チャーンレート 0〜1 範囲外 → スキップ+エラー記録
- ベースライン仕訳ゼロ → ゼロ起点で計算
- 計画対象月が実績月より過去 → スキップ
- `Constants.getParam` のデフォルト値必須
- 有効フラグ FALSE 行スキップ
- 入力シート未作成 → エラーメッセージ表示
- 出力シート未作成 → 自動 insertSheet
- 重複ドライバー行 → 後勝ち+警告ログ

## 動作確認

1. `npm run push:dev` で開発用 GAS にデプロイ
2. 開発スプレッドシートで `setupAllSchemas` を実行(特権ユーザー必須)し、`29_bud_drivers` と `67_pl_driver_based_plan` シートが作成されること、および `01_sys_config` に `BUD_DRVR` / `RPT_DRVPL` の 2 行が追加されることを確認
3. `29_bud_drivers` にテストデータを入力(例: 2026-05 の ARPU=10000・チャーンレート=0.05・新規顧客数=10、対象月を 6 ヶ月分繰り返す)
4. メニューバーから「📋 サイドバー: 📊 マート更新」→「📈 ドライバー計画 (ARPU/チャーン)」を実行(メニュー名は実装した文字列通りに確認)
5. `67_pl_driver_based_plan` に計算結果が正しく出力されること、特に漸化式の結果(顧客数の自己更新と売上の連動)が手計算と一致することを確認
6. 同じデータで 2 回目の実行を行い、前回データが全件クリアされ新データで上書きされることを確認(冪等性テスト)
7. エッジケースの一部を実機検証:
   - チャーンレート 1.5 を 1 行投入 → 該当月の `算出根拠` 列に「チャーンレート異常値」が記録されることを確認
   - `有効フラグ` を FALSE にした行が無視されることを確認
   - `29_bud_drivers` を空にして実行 → ベースラインのみ 1 行出力されることを確認

### 拡張思考の使用状況

| フェーズ | 拡張思考 | 備考 |
|---------|---------|------|
| 実行前タスク(Read) | あり | ファイル読み込みと挿入位置の確定 |
| 実装(Write/Edit) | なし | 仕様書の内容を書き下すのみ |

推奨実行モデル

工程推奨モデル理由
全工程Claude Sonnet複数ファイル横断の新規実装。仕様書で設計は完全定義済みだが、setupAllSchemas 内の挿入位置の特定・既存パターン(KPI ダッシュボードや F-25 budget_variance)への忠実な追従が必要

変更履歴

日付変更内容
2026-04-21初版作成。29_bud_drivers(入力)・610_datamart_driver_plan.js(計算エンジン)・67_pl_driver_based_plan(出力)の 3 コンポーネント構成を設計。シート番号は 25→29(25_bud_finance との衝突回避)、67 はそのまま採用

仕様書作成プロンプト

展開して表示
ファイル tasks/prompts/task_F-07.md を読み込み、<instruction> タグの指示に従って Phase 1 から Phase 3 まで完全に自律実行して終了してください。途中のテキスト報告や確認は不要です。

tasks/prompts/task_F-07.md<instruction> 全文(v1.7 ルール準拠):

【タイムアウト回避・実行原則(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>記録・最重量・必ず独立Step)。1回のWrite/Editは300行以内。
4. 各Stepで何を書くかを具体指示: 設計判断をPhase 2実行時に持ち込まない。各Stepの記述内容はPhase 1で確定した固有名詞・行番号に基づき書き下すのみ。

あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者兼仕様書ライターです。
案件 F-07「ドライバーベース計画の拡張」の開発仕様書を作成してください。
作成後は docs/_config.json の nav 配列の §E.5(FP&A・レポーティング)にも必ず追記してください。

## Phase 1: 実行前タスク(テキスト報告禁止。即座にツール実行)
- TODO_future.md / CLAUDE.md / 既存データマートビルダー / 101_sys_config.js / 002_constants.js / 202_repository.js / 003_contracts.js / 004_utils.js を Read
- シート番号競合を確認 (25, 67)

## Phase 2: 仕様書の分割作成
出力先: docs/dev/dev_F-07_driver_based_planning.md
- Step 2-1: 骨格 (~20行)
- Step 2-2: 概要〜注意事項 (~300行)
- Step 2-3a: エッジケース〜人間検討事項 (~200行)
- Step 2-3b: 実装プロンプト〜変更履歴 (~250行)
- Step 2-4: 仕様書作成プロンプト記録

## Phase 3: 保存と記録
- 3-A: ファイル確認
- 3-B: docs/_config.json §E.5 に追記
- 3-C: docs/_internal/changelog.md 先頭に追記
- 3-D: コミット & プッシュ

実行モデル: Claude Opus 4.7 (1M context) 作成日時: 2026-04-21


📌 取り込み時の注記 (2026-06-02 sub 復元)

本仕様書は旧 F-番号体系で作成され PR 未マージのまま孤立していたドラフトを、origin/docs/dev-* ブランチから内容無改変で復元し、案件ID のみ MAS 体系へ正規化したもの。status: Open(未実装)

⚠️ ファイル番号ドリフト: 本文「対象ファイル」が指す 600_report/610〜612_*.js は現行 main で 別機能に使用済み(610=投資分析/MAS-013・611=財務モデリング/MAS-010・612=採用sim/MAS-012)。 実装時にファイル番号の再割当が必要。