MAS-003: KPIダッシュボード/経営指標
概要
| 項目 | 内容 |
|---|---|
| 案件ID | MAS-003 |
| カテゴリ | FP&A |
| Phase | P1 |
| 優先度 | ★★ |
| 所要時間 | 4-6時間(MVP 5 KPI + 条件付き書式) |
| 対象ファイル | 600_report/609_datamart_kpi.js(新規)100_config/101_sys_config.js(01_sys_config への KPI_DASH 登録+メニュー追加)CLAUDE.md(「DDLで管理されないタブ」に追記) |
| 出力先タブ | 93_kpi_dashboard(新設) |
| 前提案件 | MAS-024(BEP・完了)、MAS-020(前年度スナップショット・完了)、MAS-001(予実差異・完了) |
| 一次資料 | docs/spec/spec_dashboard.md §6(KPI 一覧・レイアウト・GAS制約) |
目的
売上総利益率・営業利益率・BEP・ランウェイ・労働分配率など経営指標を 1 枚に集約し、CFO が当月/前月/YTD/6ヶ月トレンドを一目で把握できる情報提供型ダッシュボードを新設する。既存の 61 (P/L実績単月)・62 (YTD)・71 (B/S) に実装済みの値を Spreadsheet 数式で参照するのみとし、GAS は DDL 初期化・HELPER 行生成・条件付き書式設定に限定する。
現在のコード(参照先)
spec_dashboard.md §6 の設計ドラフト
docs/spec/spec_dashboard.md §6 に 8 KPI のドラフトが既に存在する。本仕様書は当該 §6 を一次資料として踏襲しつつ、以下の差分を反映する。
| 差分項目 | ドラフト (§6) | 本仕様書 |
|---|---|---|
| BEP 行の参照名 | 「 BEP到達率」 | 実装は「 安全余裕率 (%)」(603_datamart_pl.js:383)。こちらを採用 |
| 表示 KPI 数 | 8個 | MVP=5個 (K1/K2/K4/K5/K6) + Phase 2=3個 (K3/K7/K8) |
| 閾値管理 | 言及なし | 03_sys_params の CFG_KPI_* パラメータで管理(後述) |
| 実装ファイル | 言及なし | 600_report/609_datamart_kpi.js 新規 |
既存実装の活用(車輪の再発明を禁ずる)
| 参照対象 | 既存実装 | 参照方法 |
|---|---|---|
| K5 BEP・安全余裕率 | 603_datamart_pl.js:271-399 の BEP セクション。61/62 タブに「📊 損益分岐点分析 (BEP)」行として出力 | INDEX+MATCH で「 BEP売上高」「 安全余裕率 (%)」行を拾う |
| 前年比較の基盤 | 66_pl_prev_year(MAS-020 savePlSnapshot()) | 前月比ではなく YoY 比較を出す場合はこの行にアクセス(MVPでは前月比のみで YoY は任意) |
| P/L 合計行 | 61_pl_monthly 上の「✨ 売上総利益」「✨ 営業利益」「✨ 経常利益」(603_datamart_pl.js:264 付近の fmtM.push('profit') で出力) | 同上 INDEX+MATCH(改行対策必須) |
| B/S 残高 | 71_bs 上の「現金預金」「売掛金」等の科目行 | 同上 |
| マート更新起動点 | 602_datamart_main.js:buildBudgetTrendDataMart() | 末尾で buildKpiDashboard() を呼び出す(HELPER 行と条件付き書式のみ更新、本体は数式) |
| 描画パターン | dmApplyDwhFormat_(), dmApplyYoyFormat_() | 本機能は独自レイアウトのため dmApplyDwhFormat_ は使わず、buildKpiDashboard() 内で setValues + newConditionalFormatRule を直接呼ぶ |
| 動的生成タブの先例 | 76_notes, 72_bs_snap | CLAUDE.md「DDLで管理されないタブ」に 93_kpi_dashboard も追加 |
修正方針
アーキテクチャ決定
- 新規タブ方式:
93_kpi_dashboardを新設(既存 91/92 決算書は法定様式で目的が異なるため分離) - 行・列リテラル埋め込み方式(Step 2 実装時に仕様変更): 当初は
INDEX+MATCH+ARRAYFORMULA+SUBSTITUTEベースで設計したが、絵文字・全角スペース・改行混在で MATCH が壊れる事例が多発。GAS 側で行番号と列番号を事前計算してリテラルとして数式に埋め込む方式に変更。INDEX(sheet!$C:$N, <row>, <col>)の形で生成し MATCH を廃止 - 月列は 12 列固定(C-N): 61_pl_monthly は MAS-020 で YoY 差異列(O-Z の
△YYYY-MM)が追加されており、getLastColumn()がこれを含めて返す。列解決は必ずC:$Nの 12 列に限定する。YoY 差異列の"-"値を末尾非空セルと誤判定して列番号が崩れた事例あり - 境界月の動的検出:
03_sys_paramsのCFG_BOUNDARY_YMが基本だが未設定時は61_pl_monthlyの売上高行(売上高or【売上高 計】)の末尾非空月から自動検出。ヘッダーが Date オブジェクトの場合に備えUtilities.formatDate()で"yyyy-MM"に正規化 - ラベル比較の注意:
String.trim()は全角スペース (U+3000) も除去する。売上高を trim すると売上高になるため、比較する側も同等に trim するか、trim せず完全一致でマッチする - B2 の文字列強制: 境界月
"2026-03"をsetValue(boundaryYm)するとスプレッドシートが日付 or 負数として解釈する問題あり。setNumberFormat('@').setValue("'" + boundaryYm)(apostrophe 前置)で強制文字列化 - SPARKLINE のデータ範囲: 同一シート内の HELPER 行(最下部の行 28-34)に過去 6 ヶ月値を展開して参照する(他シート参照は一部ケースで機能しない既知制約)
- 更新タイミング: 数式参照のため
buildBudgetTrendDataMart()実行で自動追随。末尾でbuildKpiDashboard()を try/catch で呼び、KPI 描画失敗でもマート更新本体は成功扱い - シートキー:
KPI_DASHをシステム設定シート01_sys_config(=Constants.CONFIG_SHEET)に登録する。setupAllSchemas()内でconfSheet.appendRow(['KPI_DASH', '', '93_kpi_dashboard', 'KPIダッシュボード'])。タブ名はUtils.getSheetNameByKey('KPI_DASH') \|\| '93_kpi_dashboard'で取得。000_infra/002_constants.jsのSHEET_DEFAULTSは触らない({ pattern, prefix, defaults }形式でsmartAddRow行デフォルト値用) - 動的生成タブ扱い:
setupAllSchemas()では登録のみ、本体描画はbuildKpiDashboard()に委譲(76_notesと同じ扱い)
表示 KPI の範囲
| # | KPI | 計算式 | Phase | データソース |
|---|---|---|---|---|
| K1 | 売上総利益率 | 売上総利益 ÷ 売上高 | MVP | 61_pl_monthly |
| K2 | 営業利益率 | 営業利益 ÷ 売上高 | MVP | 61_pl_monthly |
| K3 | 経常利益率 | 経常利益 ÷ 売上高 | 拡張 | 61_pl_monthly |
| K4 | 労働分配率 | 人件費合計 ÷ 売上総利益 | MVP | 61_pl_monthly |
| K5 | BEP売上高 / 安全余裕率 | MAS-024 算出済みを参照 | MVP | 61_pl_monthly(BEPセクション) |
| K6 | ランウェイ (月数) | 現預金残高 ÷ 月間固定支出 | MVP | 71_bs + 61_pl_monthly(固定費合計) |
| K7 | 売掛金回転日数 | 売掛金残高 ÷ (売上高 ÷ 365) | 拡張 | 71_bs + 62_pl_ytd |
| K8 | 自動起票率 | 自動起票INV数 ÷ 全INV数 | 拡張 | 32_wrk_invoice(起票種別列の存在に依存) |
MVP = TODO_future.md に明記された 5 KPI(K1/K2/K4/K5/K6)。K3/K7/K8 は Phase 2 として仕様書末尾「人間が検討すべき事項」で実装判断を仰ぐ。
レイアウト(Step 2 実装版)
KPI 本体行の直下に、分子・分母・内訳科目を埋め込み表示する構成。単独の「計算の内訳」セクションは設けない(上表・下表の列ズレを回避するため)。
行 1: [📊 KPIダッシュボード] (A1:F1 merge, 黒帯ヘッダー)
行 2: [対象期間:] [<YYYY-MM>] [(前月比=前月との差分)]
行 3: [KPI] [当月] [前月] [前月比] [YTD] [トレンド (6ヶ月)]
行 4: K1 売上総利益率 [main: 黄色背景+太字+SPARKLINE]
行 5-6: - 売上総利益 / 売上高 計 [sub: グレー文字]
行 7: K2 営業利益率
行 8-9: - 営業利益 / 売上高 計
行 10: K4 労働分配率
行 11: - 人件費合計 [sub: グレー文字]
行 12-17: · 役員報酬〜雑給 6科目 [subsub: 更に薄いグレー]
行 18: - 売上総利益 (分母)
行 19: K5 BEP売上高
行 20: - 固定費合計
行 21-22: · うち人件費合計 / うち非人件費 [subsub]
行 23-24: - 変動費合計 / 変動費率
行 23: K5 安全余裕率 (派生値なので sub なし)
行 24: K6 ランウェイ
行 25-26: - 現預金残高 / 固定費合計 (分母)
行 27: (空白)
行 28-34: HELPER (非表示): 月ラベル + K1〜K6 の過去6ヶ月値
行タイプ別の書式:
| タイプ | プレフィックス | 書式 | 対象列 |
|---|---|---|---|
| main | (なし) | 黄色背景 #FFF2CC + 太字 + SPARKLINE 付き | B-F 全列使用 |
| sub | - (半角スペース2+ハイフン) | グレー文字 #555555 | B-D のみ (E/F 空白) |
| subsub | · (半角スペース4+中黒) | 薄グレー文字 #888888 | B-D のみ |
列定義(上表・埋め込み内訳 共通):
| 列 | 内容 | 書式 |
|---|---|---|
| A | 項目名 (KPI名 or sub/subsub ラベル) | テキスト |
| B | 当月 | 率 0.0%;[Red]△ 0.0%;"-" / 金額 #,##0;[Red]△ #,##0;"-" / 月数 0.0"ヶ月" |
| C | 前月 | 同上 |
| D | 前月比 | =B-C の IFERROR ラップ、表示は同フォーマット |
| E | YTD | main 行のみ(sub/subsub は空白) |
| F | トレンド | main 行のみ。SPARKLINE は HELPER 行(B28-G34)を参照 |
閾値・条件付き書式
| KPI | 条件 | 背景色 | 文字色 | パラメータキー |
|---|---|---|---|---|
| K1 売上総利益率 | < 閾値(デフォ 40%) | #F4CCCC | #CC0000 | CFG_KPI_GROSS_MARGIN_MIN |
| K2 営業利益率 | < 0% | #F4CCCC | #CC0000 | (閾値固定。赤字月は必ず警告) |
| K4 労働分配率 | > 閾値(デフォ 60%) | #FCE5CD | #B45F06 | CFG_KPI_LABOR_RATIO_MAX |
| K5 安全余裕率 | < 0%(BEP未達) | #F4CCCC | #CC0000 | (閾値固定) |
| K6 ランウェイ | < 閾値(デフォ 6ヶ月) | #F4CCCC | #CC0000 | CFG_KPI_RUNWAY_MIN_MONTHS |
03_sys_params 未設定時はデフォ値を使用。Constants.getParam('CFG_KPI_*', デフォルト値) で取得する。
数式の主要パターン(Step 2 実装版:行・列リテラル方式)
行番号と列番号は GAS 側で事前計算し、MATCH を使わずに INDEX(sheet!$C:$N, <行>, <列>) を生成する。
K1 売上総利益率(当月列、例: 売上総利益が行9、2026-03 が C:N 範囲の 8列目の場合):
=IFERROR(INDEX('61_pl_monthly'!$C:$N,9,8) / INDEX('61_pl_monthly'!$C:$N,5,8), "-")
- 分子行(売上総利益 = 行 9)、分母行(売上高 計 = 行 5)、列(2026-03 = 8)は全て GAS の
buildKpiContext_()が返すctx.pl[label]とctx.colIdxから取得 - GAS ヘルパー:
kpiCellRefByIdx_(sheetName, rowNum, colIdx)がINDEX(...)部分を組み立てる
K4 労働分配率:
=IFERROR(
IF(INDEX('61_pl_monthly'!$C:$N,9,8) <= 0, "N/A",
(IFERROR(INDEX('61_pl_monthly'!$C:$N,15,8),0) -- 役員報酬
+IFERROR(INDEX('61_pl_monthly'!$C:$N,16,8),0) -- 給料手当
+ ... 他4科目
) / INDEX('61_pl_monthly'!$C:$N,9,8)
),
"-")
- 人件費6科目(役員報酬・給料手当・賞与・法定福利費・福利厚生費・雑給)を各
IFERROR(..., 0)で合計 - 不在科目(科目マスタに未登録または該当月に計上なし)は自動的に 0 扱い
K6 ランウェイ:
=IFERROR(
IF(INDEX('61_pl_monthly'!$C:$N,<固定費行>,<当月列>) <= 0, "∞",
INDEX('71_bs'!$C:$N,<現預金行>,<当月列>) / INDEX('61_pl_monthly'!$C:$N,<固定費行>,<当月列>)
),
"∞")
固定費 = MAS-024 で算出された BEP セクションの「 固定費合計」行。減価償却費も含まれるため厳密な現金流出ではないが、MVP は単純固定費を使用(「人間が検討すべき事項」参照)。
SPARKLINE (K1 の例、HELPER 行 29 を参照):
=IFERROR(SPARKLINE($B$29:$G$29, {"charttype","line";"color","#1C4587"}), "")
HELPER 行 28 は月ヘッダー、行 29-34 が K1-K6 の過去 6 ヶ月値。
buildKpiDashboard() の責務
GAS 側でやることを限定する:
- タブが存在しなければ作成(
93_kpi_dashboard) - タイトル・ヘッダー行を
setValuesで固定書き込み - 各 KPI 行の数式 (
setFormulas) を記述 - HELPER 行(過去 6 ヶ月値用)を生成。これも数式ベース
- 条件付き書式 (
newConditionalFormatRule) をCFG_KPI_*パラメータを読んで設定 - 列幅・フォント (
BIZ UDGothic) を固定 - HELPER 行を
hideRowsで非表示
本体の値計算は全て数式。buildBudgetTrendDataMart() が 61/71 を更新すれば 93 は自動追随する。
影響範囲
| 変更ファイル | 変更量 | 内容 |
|---|---|---|
600_report/609_datamart_kpi.js | 新規 ~470行 | buildKpiDashboard() + buildKpiContext_() + 行・列リテラル方式の数式ビルダー (kpiRatioByIdx_, kpiDirectByIdx_, kpiLaborRatioByIdx_, kpiRunwayByIdx_, kpiLaborSumRawFormula_, kpiFixedNonLaborFormula_) + renderKpiRows_ (main/sub/subsub 埋め込み) + HELPER 行生成 + applyKpiConditionalFormat_ (Step 3 で本実装予定) |
100_config/101_sys_config.js | +3行 | setupAllSchemas() 内の confSheet.appendRow 群(L440-470付近)に KPI_DASH を追加(FS_PL の直後・SS_NOTES の前)、onOpen() 内「📊 マート更新」メニューに buildKpiDashboard の項目追加 |
600_report/602_datamart_main.js | +1行 | buildBudgetTrendDataMart() 末尾から buildKpiDashboard() 呼び出し |
CLAUDE.md | +1行 | 「DDLで管理されないタブ」一覧に 93_kpi_dashboard を追加 |
docs/_config.json | +1行 | §E.5 FP&A・レポーティング配下に dev_mas-003_kpi_dashboard.md を追加 |
000_infra/002_constants.js は変更しない。SHEET_DEFAULTS はシートキーマッピングではなく smartAddRow 用の行デフォルト値管理({ pattern, prefix, defaults } オブジェクト配列)。シートキーは 01_sys_config に登録する。
既存動作への影響: なし。新規タブ追加のみ。61/62/71 の出力ロジックは変更しない。
注意事項
Step 2 実装で顕在化した落とし穴(失敗パターン #21-#24 候補、要 failure_patterns.md 登録)
#21 YoY 差異列による getLastColumn() の膨張
61_pl_monthly は MAS-020 実装で O-Z 列に △YYYY-MM(YoY 差異)が追加されており、getLastColumn() は 22 を返す。lastCol - 2 = 20 列分の範囲を取ると差異列の "-" 値を末尾非空セルと誤判定し、境界月列が壊れる事例が発生。月列は必ず 12 列固定(C-N)に限定。
#22 String.trim() が全角スペースを除去
' 売上高'.trim() は '売上高' になる(U+3000 が空白扱い)。セルラベル比較で trim を使う場合は比較文字列側も trim して前提を揃えるか、trim せず完全一致で行う。どちらか一方だけ trim すると永続的に不一致になる。
#23 setValue("2026-03") の自動パース
スプレッドシートが文字列 "2026-03" を「2026 マイナス 3 = 2023」の負数として解釈し、△2026-03 のような表示になる事例あり。setNumberFormat('@').setValue("'" + value)(apostrophe 前置)で強制文字列化する。
#24 ARRAYFORMULA+SUBSTITUTE+MATCH の組み合わせの脆弱性
絵文字(例: ✨ 売上総利益 の U+2728)・全角スペース・改行が混在するラベルで MATCH が返値なしになる事例が多発。数式内 MATCH を完全廃止し、GAS 側で行番号を事前計算して埋め込む方式に変更した(設計段階で想定していた動的 MATCH 方式から実装変更)。
一般注意事項
- SPARKLINE の参照範囲: 同一シート内に HELPER 行を置くこと(行 28-34)。他シート直接参照だと一部ケースで描画されない
- 条件付き書式ルール上限(50/シート): MVP 5 KPI × 2 条件(赤・黄)= 10 ルールで余裕あり。拡張時も K7/K8 各 2 ルール追加なので上限問題なし
- DDL 動的生成タブ扱い:
setupAllSchemas()の実行順でbuildKpiDashboard()を呼ぶが、61/62/71 の更新前に呼ぶと数式のエラーが発生する。buildBudgetTrendDataMart()の末尾で try/catch で呼ぶ運用 (KPI 描画失敗でもマート更新本体は成功扱い) - 固変区分の実データ乖離(MAS-024 失敗パターン踏襲): K6 の月間固定支出は MAS-024 の固定費を参照。固変区分未設定科目は MAS-024 と同様「固定費扱い(保守的推計)」
- filterWithRecalcTotal で非加算指標の Total が壊れる失敗パターン: KPI は全て非加算指標。Total (YTD) は 62_pl_ytd の YTD 行から独立参照
- 売上ゼロ月のゼロ除算(MAS-024 失敗パターン): 全ての率系 KPI は
IFERROR(..., "-")でラップ - 新規ファイル
609_datamart_kpi.js(MAS-192 失敗パターン踏襲): 600_report/ 配下に追加。既存関数 (dmApplyDwhFormat_,buildBudgetTrendDataMart) への参照は残し、呼び出し関係を grep で確認してから動作確認 - テスト対応:
90_test_resultsでテストするbuildBudgetTrendDataMart()の下流に KPI 描画が追加されるため、テストの SKIP_PATTERNS に独立タブとして除外設定するか、専用テストケースを追加する判断
エッジケース
計算式があるため必須セクション。MVP で実装する 5 KPI のゼロ除算・異常値対応:
| KPI | 条件 | 表示値 | 理由 |
|---|---|---|---|
| K1 売上総利益率 | 売上=0 | "-" | IFERROR で吸収 |
| K1 売上総利益率 | 売上総利益 < 0(原価割れ) | マイナス率を赤字表示 | 実態を隠さない |
| K2 営業利益率 | 売上=0 | "-" | 同上 |
| K2 営業利益率 | 営業利益 < 0(赤字) | [Red]△ 0.0% | 条件付き書式で赤背景も発火 |
| K4 労働分配率 | 売上総利益 ≦ 0 | "N/A" | 分母が非正のため意味を持たない |
| K4 労働分配率 | 100% 超 | [Red]100.0% 超 + 警告背景 | 人件費 > 売上総利益 |
| K5 BEP売上高 | MAS-024 側で "-" 出力(売上=0 や 変動費率≧100%) | そのまま "-" | MAS-024 の挙動を踏襲 |
| K5 安全余裕率 | MAS-024 側で "-" 出力 | そのまま "-" | 同上 |
| K5 安全余裕率 | < 0%(売上 < BEP) | マイナス% + 赤背景 | 赤字ライン |
| K6 ランウェイ | 固定費=0 または黒字経営(月間CF流入) | "∞" | 分母≦0、定義上無限大 |
| K6 ランウェイ | < 6ヶ月 | 月数 + 赤背景 | 資金ショートリスク警告 |
| 前月比 (D列) | 前月 = "-" or "N/A" | "-" | 差分計算不可 |
| YTD (E列) | isActualOnly で空白月含む | filterValues 相当の挙動(数式側で空白月除外) | 加算可能指標(分子分母)は SUM、非加算指標(率)は YTD Total 行から独立参照 |
| トレンド (F列) | 過去6ヶ月のうちゼロ除算月あり | その月は空値(SPARKLINE が自動スキップ) | IFERROR(計算, "") |
フィルタ整合性(isActualOnly)
- 61 P/L 実績タブは
isActualOnly=true時に境界月以降が空白化される - 当月列は直近実績月 = 境界月の1ヶ月前を参照する。境界月そのものは未確定データ
- 境界月の動的取得は
03_sys_paramsのCFG_BOUNDARY_YMをConstants.getParam()で取得し、セルB2に書き込んで数式から参照 - YTD 列は 62_pl_ytd(実績 YTD タブ)を直接参照。非加算指標(率)は月別の合算ではなく 62 タブの YTD 行から計算
実データ検証(MCP で事前確認)
実装着手前に Google Sheets MCP で dev 環境のスプレッドシートに対して以下を検証する。結果次第で数式の MATCH 文字列や Phase 2 判定を調整:
| # | 確認項目 | シート | 具体的にチェックする内容 |
|---|---|---|---|
| V1 | P/L 合計行ラベルの完全一致 | 61_pl_monthly | 「✨ 売上総利益」「✨ 営業利益」「✨ 経常利益」「 BEP売上高」「 安全余裕率 (%)」「 固定費合計」の厳密な文字列(絵文字・全角スペース含む)。改行 (\n) の有無 |
| V2 | B/S 現預金科目名 | 71_bs | 「現金預金」「現金及び預金」「当座預金」等の表記揺れ。K6 ランウェイの分子取得ラベル確定 |
| V3 | B/S 売掛金科目名 | 71_bs | 「売掛金」単独で MATCH するか、「売上債権」のような集約表記か |
| V4 | 人件費科目の網羅性 | 11_mst_account | 役員報酬・給料手当・賞与・法定福利費・福利厚生費・雑給 の 6 科目が全て登録されているか。大分類 or 表示区分 で一括絞り込み可能か |
| V5 | 固変区分の実データ | 11_mst_account | 固変区分 列(第8列)で FV_FIX/FV_VAR/FV_NA の設定状況。未設定科目の数 |
| V6 | 自動起票列の存在確認 | 32_wrk_invoice | 「起票種別」列があるか。無ければ K8 は Phase 2 先送り |
| V7 | 既存タブ番号の衝突 | (list_sheets) | 93 番台タブが未使用であることを確認(現状 91_fs_bs, 92_fs_pl までの想定) |
| V8 | 境界月の値 | 03_sys_params | CFG_BOUNDARY_YM の現値。当月/前月列で参照する相対計算のベース |
関連ドキュメント
| 仕様書 | 関連箇所 |
|---|---|
| spec_dashboard.md §6 | 一次資料。KPI 一覧・レイアウト・GAS制約 |
| dev_mas-024_bep_analysis.md | K5 の参照元。BEP 算出ロジック・固変区分・「安全余裕率」の出力行ラベル |
| dev_mas-020_yoy_comparison.md | 66_pl_prev_year スナップショット方式(YoY 表示で利用可)、dmApplyYoyFormat_ の独自描画パターン |
| dev_mas-001_variance_analysis.md | 65_pl_variance 予実差異。KPI との棲み分け(KPI は率、variance は差額) |
| CLAUDE.md | コーディング規約・ヘッダー名ベースの列参照・動的生成タブ一覧 |
| TODO_future.md | MAS-003 案件定義 |
人間が検討すべき事項
| # | 項目 | 詳細 |
|---|---|---|
| 1 | MVP vs 拡張の切り分け | TODO_future.md 準拠の 5 KPI (K1/K2/K4/K5/K6) で MVP 着手。K3 経常利益率は数式追加のみで安価なため MVP 同梱してもよい |
| 2 | K6 ランウェイの月間固定支出の定義 | (A) BEP セクションの「固定費合計」そのまま / (B) 減価償却費を控除した現金流出固定費 / (C) 過去 3~12 ヶ月の月間 CF 流出の平均。MVP は (A)、精度向上時は MAS-008(資金繰り予測高度化)の成果を利用 |
| 3 | 閾値のシステムパラメータ化 | CFG_KPI_* として 03_sys_params に追加するか、コード内定数とするか。将来の SaaS 化(T-08 参照)を見据えパラメータ化を推奨 |
| 4 | 前月比 vs YoY | MVP は「前月比」のみ(D列)。YoY は Phase 2 で 66_pl_prev_year を参照する列を追加 |
| 5 | 自動起票率(K8)の判定条件 | 32_wrk_invoice に「起票種別」列が存在するか要 MCP 確認。無い場合は別列(例: 起票者メールアドレスが system@ 等)で判定するか、Phase 2 先送り |
| 6 | トレンド SPARKLINE の期間 | MVP 6 ヶ月 vs 12 ヶ月。SPARKLINE は同一シート内 HELPER 行参照なので期間変更はデータ行の拡張で対応 |
| 7 | ダッシュボードの更新タイミング | 数式ベースなので buildBudgetTrendDataMart() 実行時に自動追随。独立したメニュー項目「📊 KPIダッシュボード再描画」は DDL 変更・閾値変更後のリセット用 |
実装プロンプト(Claude Code 用)
以下 4 Step 構成。Step 1 → 2 → 3 → 4 の順で実行する。Step 間でユーザー確認を挟む。
Step 1: DDL・シートキー追加(Haiku)
あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者です。
案件 MAS-003「KPIダッシュボード/経営指標」の Step 1(DDL・シートキー登録)を実装してください。
## 実行前タスク
以下のファイルを **Read で実読**して既存パターンを把握する(Grep の部分マッチで推測しない):
- `CLAUDE.md` L158-164 — 「DDL (setupAllSchemas) で管理されないタブ」セクションの現行リスト
- `000_infra/004_utils.js` L23-32 — `Utils.getSheetNameByKey` が参照する `Constants.CONFIG_SHEET` の値
- `000_infra/002_constants.js` L7 — `Constants.CONFIG_SHEET = '01_sys_config'`
- `100_config/101_sys_config.js` L420-470 — `setupAllSchemas()` 冒頭で `confSheet = ss.getSheetByName(Constants.CONFIG_SHEET)` を取得した後に `confSheet.appendRow([key, '', tabName, displayName])` を繰り返す登録パターン(`PL_VAR`, `FS_PL`, `SS_NOTES` を参考)
- `100_config/101_sys_config.js` L331-337 — `onOpen()` 内「📊 マート更新」メニューの既存構造
- `docs/dev/dev_mas-003_kpi_dashboard.md` — 本仕様書
## 修正対象ファイル
1. `100_config/101_sys_config.js` への追記(2 箇所: DDL 登録とメニュー項目)
2. `CLAUDE.md` への追記(1 箇所: 動的生成タブ一覧)
**重要**: `000_infra/002_constants.js` の `SHEET_DEFAULTS` は**触らない**。構造が `{ pattern, prefix, defaults }` のオブジェクト配列で `smartAddRow` の行デフォルト値管理用のため、シートキーマッピングとは無関係。シートキーは `01_sys_config` シートに行として登録する。
## 実装内容
### 1-1. `101_sys_config.js` — `01_sys_config` に KPI_DASH 行を登録
`setupAllSchemas()` の L440-470 付近、`FS_PL` の直後・`SS_NOTES` の前に 1 行追加:
if (!existKeys.includes('KPI_DASH')) confSheet.appendRow(['KPI_DASH', '', '93_kpi_dashboard', 'KPIダッシュボード']);
`confSheet` は `Constants.CONFIG_SHEET = '01_sys_config'` から取得済みのシート。ここへの appendRow がシートキーマッピングの実体。
### 1-2. `101_sys_config.js` — メニュー項目追加
`onOpen()` 内の「📊 マート更新」メニュー(L331 付近、既存の `プロジェクト別 採算(限界利益)の生成` の直後・`addSeparator()` の前)に 1 行追加:
.addItem('📊 KPIダッシュボード再描画', 'buildKpiDashboard')
`buildKpiDashboard` 本体は Step 2 で実装するため、Step 1 時点ではクリック時にエラーで OK。
### 1-3. `CLAUDE.md` — 動的生成タブ一覧を更新
L158 付近「## DDL (setupAllSchemas) で管理されないタブ」セクションの現行リスト:
03_sys_params, 75_ss_equity_changes, 76_notes,
77_pj_raw, 78_pj_pl, 91_fs_bs, 92_fs_pl, 90_test_results
の `92_fs_pl,` の直後に `93_kpi_dashboard,` を挿入(カンマ区切り)。
## 制約
- `000_infra/002_constants.js` の `SHEET_DEFAULTS` には**絶対に追加しない**(仕様書内のアーキテクチャ決定 #6 参照)
- DDL のヘッダー定義(`schemas` オブジェクト、L474 付近)には Step 1 では追加しない。Step 2 で本体描画関数内でのみ書き込むため
- メニュー項目追加時、既存の他の `addItem` の順序を変更しない
## 動作確認
1. `npm run push:dev` でデプロイ
2. dev GAS のメニュー「🔧 開発・設定」→「全シートのスキーマとUIを最新化(DDL)」を実行(関数名 `setupAllSchemas`。「🔧 システム初期化」や「MST_CONF_SHEETS 初期化」というメニューは**存在しない**)
3. `01_sys_config` シート(`Constants.CONFIG_SHEET`)に `KPI_DASH / (空) / 93_kpi_dashboard / KPIダッシュボード` 行が追加されていること。**`03_sys_params` ではない**ので注意。この時点では B列(シートID_GID)は空
4. **「🔧 開発・設定 → シートのGID取得・リンク」を実行**(関数名 `initConfigs`)。`01_sys_config` の各行に対し、C列のシート名で実タブを探し、無ければ `insertSheet()` で新規作成し、B列に GID を書き込む。実行後:
- `93_kpi_dashboard` タブが空タブとして新規作成されていること
- `01_sys_config` の KPI_DASH 行の B列(シートID_GID)に数値が入っていること
5. メニュー「📊 マート更新」に「📊 KPIダッシュボード再描画」が表示されること(クリック時は `buildKpiDashboard is not defined` エラーで OK)
**補足**: 手順 4 の `initConfigs` を省略しても Step 2 の `buildKpiDashboard()` 内で `ss.getSheetByName(sheetName) || ss.insertSheet(sheetName)` によりタブは作成される。ただし Step 1 の範囲で「config 行追加 + 実タブ生成 + GID 紐付け」までを完結させるのが既存運用パターンのため、手順 4 を含めて検証する。
### 拡張思考の使用状況
| フェーズ | 拡張思考 | 備考 |
|---------|:---:|------|
| 1-1~1-3 | なし | 既存パターンの模倣のみ |
Step 2: 数式生成ロジック(Sonnet)
あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者です。
案件 MAS-003 の Step 2(数式生成ロジック)を実装してください。
## 実行前タスク
以下を MCP (`mcp__google-sheets__get_sheet_data`) で dev スプレッドシートに対して**必ず先に実行**し、取得した文字列を数式にハードコードする:
1. `61_pl_monthly!A:A` 全行取得 → 「✨ 売上総利益」「✨ 営業利益」「✨ 経常利益」「 BEP売上高」「 安全余裕率 (%)」「 固定費合計」の**完全一致文字列**を確認(改行文字 `\n` や余計な全角スペースの有無)
2. `71_bs!A:A` 全行取得 → 現預金科目「現金預金」or「現金及び預金」等の実名、売掛金科目の実名を確認
3. `11_mst_account` 全行取得 → 人件費科目(役員報酬・給料手当・賞与・法定福利費・福利厚生費・雑給)の存在確認
4. `03_sys_params` → `CFG_BOUNDARY_YM` の現値
以下のファイルを読み込む:
- `docs/dev/dev_mas-003_kpi_dashboard.md` — 本仕様書(レイアウト・数式パターン・エッジケース)
- `docs/spec/spec_dashboard.md` §6 — 一次資料
- `600_report/603_datamart_pl.js:271-399` — BEP セクションの出力行ラベル。**実装が「安全余裕率 (%)」で出力しているので、仕様書のドラフトと異なる点に注意**
- `600_report/602_datamart_main.js:savePlSnapshot()` — GAS 関数の命名・構造の参考
- `000_infra/004_utils.js` — `Utils.getSheetNameByKey()` / `Utils.logInfo()` の使い方
- `000_infra/002_constants.js` — `Constants.getParam()` の使い方
## 修正対象ファイル
- `600_report/609_datamart_kpi.js` を新規作成
- `600_report/602_datamart_main.js` の `buildBudgetTrendDataMart()` 末尾で `buildKpiDashboard()` を呼ぶ 1 行を追加
## 実装内容
### 2-1. `609_datamart_kpi.js` の関数構成
/**
* =========================================================
* 609_datamart_kpi.js — MAS-003: KPIダッシュボード
* =========================================================
* 93_kpi_dashboard タブに 5 KPI + 条件付き書式を配置する。
* データは数式で 61/62/71 タブを参照する(GASは数式配置と書式のみ)。
*/
function buildKpiDashboard() {
var FUNC = 'buildKpiDashboard';
try {
var ss = getWebSpreadsheet_();
var sheetName = Utils.getSheetNameByKey('KPI_DASH') || '93_kpi_dashboard';
var sheet = ss.getSheetByName(sheetName) || ss.insertSheet(sheetName);
sheet.clear();
sheet.clearConditionalFormatRules();
// タイトル・ヘッダー
// 各 KPI 行の setFormulas
// HELPER 行の setFormulas(過去6ヶ月 × 5 KPI = 30セル)
// 列幅・書式
// 条件付き書式(applyKpiConditionalFormat_)
// HELPER 行を hideRows
Utils.logInfo(FUNC, sheetName + ' 再描画完了');
} catch (e) {
Utils.logError(FUNC, e);
throw e;
}
}
### 2-2. KPI 行の数式(抜粋)
**K1 売上総利益率** の当月列(B列):
=IFERROR(
INDEX('61_pl_monthly'!$B:$N,
MATCH("✨ 売上総利益",
ARRAYFORMULA(SUBSTITUTE('61_pl_monthly'!$A:$A, CHAR(10), "")), 0),
<当月列番号>)
/
INDEX('61_pl_monthly'!$B:$N,
MATCH("売上高",
ARRAYFORMULA(SUBSTITUTE('61_pl_monthly'!$A:$A, CHAR(10), "")), 0),
<当月列番号>),
"-")
**K5 安全余裕率(MAS-024 既存を参照)**:
=IFERROR(
INDEX('61_pl_monthly'!$B:$N,
MATCH(" 安全余裕率 (%)",
ARRAYFORMULA(SUBSTITUTE('61_pl_monthly'!$A:$A, CHAR(10), "")), 0),
<当月列番号>) / 100,
"-")
MAS-024 は安全余裕率を整数%(例: `32`)で出力しているため `/100` で率に戻す。
**K6 ランウェイ**:
=IFERROR(
INDEX('71_bs'!$B:$N,
MATCH("現金預金", // ← V2 の MCP 結果に従って置換
ARRAYFORMULA(SUBSTITUTE('71_bs'!$A:$A, CHAR(10), "")), 0),
<直近実績月列番号>)
/
INDEX('61_pl_monthly'!$B:$N,
MATCH(" 固定費合計",
ARRAYFORMULA(SUBSTITUTE('61_pl_monthly'!$A:$A, CHAR(10), "")), 0),
<直近実績月列番号>),
"∞")
### 2-3. HELPER 行(SPARKLINE 用)
最下部に 6 列(過去 6 ヶ月分)の HELPER 行を KPI ごとに配置。`SPARKLINE` はその範囲を参照:
F4: =SPARKLINE($B$11:$G$11, {"charttype","line";"color","#1C4587"})
### 2-4. `buildBudgetTrendDataMart()` への組み込み
`602_datamart_main.js` の `buildBudgetTrendDataMart()` 末尾(return の直前)に以下を追加:
// MAS-003: KPIダッシュボード再描画
try { buildKpiDashboard(); } catch (e) { Utils.logError('buildBudgetTrendDataMart.kpi', e); }
## 制約
- 61/62/71 タブの出力ロジックは変更禁止
- MATCH 文字列は MCP で確認した実名を使う(絵文字・全角スペース含む完全一致)
- HELPER 行は `hideRows` で非表示にする。delete はしない(数式が壊れる)
- 条件付き書式は Step 3 で別関数 `applyKpiConditionalFormat_()` として実装。Step 2 では空実装で構わない
- K3 経常利益率・K7 売掛金回転日数・K8 自動起票率は Step 2 では実装しない(Phase 2)
## エッジケース
| 条件 | 期待動作 |
|------|---------|
| 売上=0 | K1/K2 の数式が `"-"` を返す(IFERROR) |
| 売上総利益≦0 | K4 は `"N/A"`(IF で分岐) |
| 固定費=0 | K6 は `"∞"` |
| MAS-024 BEP 行が未出力(科目マスタ未設定等) | K5 は `"-"`(IFERROR) |
| 当月列 = 境界月 | `isActualOnly` で空白 → IFERROR で `"-"` |
| 過去 6 ヶ月のうちゼロ除算月あり | SPARKLINE は空値をスキップ(IFERROR, "") |
## 実データ検証
実装後に再度 MCP で以下を確認:
- `93_kpi_dashboard` の B4(K1 当月値)が期待値になっているか
- MATCH が失敗して `#N/A` が出ていないか
- SPARKLINE が HELPER 行を参照して描画できているか
## 動作確認
1. `npm run push:dev` でデプロイ
2. メニュー「📊 マート更新 → 財務3表の更新」を実行 → 61/62/71 が更新された後、93 も連動して更新される
3. メニュー「📊 マート更新 → 📊 KPIダッシュボード再描画」を単独実行して再描画のみも動くか確認
4. K1 売上総利益率 の値が手計算と一致するか(1 ヶ月分)
5. K6 ランウェイ の月数が妥当な範囲か(1~24 ヶ月程度)
6. HELPER 行が非表示になっているか
### 拡張思考の使用状況
| フェーズ | 拡張思考 | 備考 |
|---------|:---:|------|
| 数式設計(MATCH 組み立て) | あり | 改行対策・IFERROR ラッピング・列番号動的化 |
| HELPER 行のレイアウト | あり | SPARKLINE の参照範囲設計 |
| setFormulas 書き出し | なし | パターン反復 |
| 動作確認 | なし | チェックリスト |
Step 3: 条件付き書式適用(Sonnet)
あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者です。
案件 MAS-003 の Step 3(条件付き書式)を実装してください。
## 実行前タスク
- `docs/dev/dev_mas-003_kpi_dashboard.md` の「閾値・条件付き書式」表
- `docs/spec/spec_dashboard.md` §4.1 セマンティックカラー、§6.5 条件付き書式上限(50ルール/シート)
- `600_report/608_datamart_render.js:dmApplyYoyFormat_()` — `setConditionalFormatRules` ではなく直接色を塗るパターンも参考
## 修正対象ファイル
- `600_report/609_datamart_kpi.js`(Step 2 で作成済み)のみ
## 実装内容
`applyKpiConditionalFormat_(sheet)` を実装:
function applyKpiConditionalFormat_(sheet) {
var rules = [];
// K1 売上総利益率 < CFG_KPI_GROSS_MARGIN_MIN
var gmMin = Number(Constants.getParam('CFG_KPI_GROSS_MARGIN_MIN', 0.4));
rules.push(
SpreadsheetApp.newConditionalFormatRule()
.whenNumberLessThan(gmMin)
.setBackground('#F4CCCC').setFontColor('#CC0000')
.setRanges([sheet.getRange('B4:E4')])
.build()
);
// K2 営業利益率 < 0
rules.push(
SpreadsheetApp.newConditionalFormatRule()
.whenNumberLessThan(0)
.setBackground('#F4CCCC').setFontColor('#CC0000')
.setRanges([sheet.getRange('B5:E5')])
.build()
);
// K4 労働分配率 > CFG_KPI_LABOR_RATIO_MAX
var lrMax = Number(Constants.getParam('CFG_KPI_LABOR_RATIO_MAX', 0.6));
rules.push(
SpreadsheetApp.newConditionalFormatRule()
.whenNumberGreaterThan(lrMax)
.setBackground('#FCE5CD').setFontColor('#B45F06')
.setRanges([sheet.getRange('B6:E6')])
.build()
);
// K5 安全余裕率 < 0
rules.push(
SpreadsheetApp.newConditionalFormatRule()
.whenNumberLessThan(0)
.setBackground('#F4CCCC').setFontColor('#CC0000')
.setRanges([sheet.getRange('B8:E8')]) // 安全余裕率の行
.build()
);
// K6 ランウェイ < CFG_KPI_RUNWAY_MIN_MONTHS
var rwMin = Number(Constants.getParam('CFG_KPI_RUNWAY_MIN_MONTHS', 6));
rules.push(
SpreadsheetApp.newConditionalFormatRule()
.whenNumberLessThan(rwMin)
.setBackground('#F4CCCC').setFontColor('#CC0000')
.setRanges([sheet.getRange('B9:E9')])
.build()
);
sheet.setConditionalFormatRules(rules);
}
`buildKpiDashboard()` 末尾で `applyKpiConditionalFormat_(sheet)` を呼ぶ。
## 制約
- `sheet.setConditionalFormatRules(rules)` で既存ルールを全置換する(`clearConditionalFormatRules()` を先に呼ぶ必要なし、`setConditionalFormatRules` が上書きする)
- `Constants.getParam()` のデフォルト値は数値型で渡す(文字列 `'0.4'` だと型エラー)
- ルール数は 5 個(MVP)。50 ルール上限まで余裕あり
## エッジケース
| 条件 | 期待動作 |
|------|---------|
| セル値が `"-"` / `"N/A"` / `"∞"`(テキスト) | `whenNumberLessThan` は発火しない(文字列は無視) |
| `CFG_KPI_*` が未設定 | デフォルト値を使用(`Constants.getParam` の第2引数) |
| 前月比列 (D列) に負値 | 書式は**数値の絶対値ではなく KPI 値そのもの**で判定(D列は前月比の差分なのでルール対象外) |
## 動作確認
1. `npm run push:dev` でデプロイ
2. 再描画後、K1 の当月値が 40% 未満の月があれば赤背景になっていること
3. `03_sys_params` に `CFG_KPI_GROSS_MARGIN_MIN = 0.5` を設定して再描画 → 閾値が切り替わっていること
4. K2 が 0% 未満(赤字月)で赤背景が発火すること
### 拡張思考の使用状況
| フェーズ | 拡張思考 | 備考 |
|---------|:---:|------|
| 条件設計 | あり | 範囲指定・デフォルト値設計 |
| `newConditionalFormatRule` 反復 | なし | パターン反復 |
Step 4: 動作確認・_config.json 登録(Haiku)
あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者です。
案件 MAS-003 の Step 4(動作確認と仕様書登録)を実施してください。
## 実行前タスク
- `docs/_config.json` — §E.5 FP&A・レポーティングの既存ページ数を確認
- `docs/_internal/changelog.md` — 先頭エントリーのフォーマット確認
## 実装内容
### 4-1. `docs/_config.json` に登録
§E.5 の末尾に以下を追加(既存の E.5.X の次の番号で):
{ "file": "dev/dev_mas-003_kpi_dashboard.md", "title": "E.5.X MAS-003 KPIダッシュボード" }
### 4-2. `docs/_internal/changelog.md` 先頭に追記
| 2026-04-17 | [dev_mas-003_kpi_dashboard.md](dev_mas-003_kpi_dashboard.md) | 初版作成。93_kpi_dashboard 新設、5 KPI の数式ベース実装と条件付き書式 |
### 4-3. 統合動作確認
1. `npm run push:dev` でデプロイ済み前提
2. 「📊 マート更新 → 財務3表の更新」を実行(`buildBudgetTrendDataMart`)
3. 93_kpi_dashboard タブが自動的に再描画されること
4. 以下のチェックリストを全て ✅:
- [ ] 5 KPI (K1/K2/K4/K5/K6) が全て表示される
- [ ] 当月/前月/前月比/YTD/トレンド の 5 列が揃っている
- [ ] SPARKLINE が 6 ヶ月トレンドを描画している
- [ ] HELPER 行が非表示
- [ ] K1 売上総利益率が手計算(売上総利益 ÷ 売上高)と一致
- [ ] K5 BEP売上高 と 安全余裕率 が 61 タブの BEP セクションと一致
- [ ] K6 ランウェイの値が妥当(正の数値 or `"∞"`)
- [ ] 条件付き書式: K1 < 40% で赤、K4 > 60% で黄、K6 < 6ヶ月で赤
- [ ] `CFG_KPI_*` を `03_sys_params` で変更 → 再描画で閾値切替
- [ ] 61/62/71 タブに変更なし(既存動作リグレッションなし)
5. テストランナー実行: `T4-03`, `T4-21` が KPI セクションを未登録科目と誤検知しないか確認(独立タブなので除外対象)
### 4-4. prod デプロイ
dev で検証完了後:
npm run push:prod
prod の `03_sys_params` で `CFG_KPI_GROSS_MARGIN_MIN`, `CFG_KPI_LABOR_RATIO_MAX`, `CFG_KPI_RUNWAY_MIN_MONTHS` を必要に応じて設定。
### 4-5. git 操作
git add 600_report/609_datamart_kpi.js 600_report/602_datamart_main.js \
100_config/101_sys_config.js 000_infra/002_constants.js \
CLAUDE.md docs/dev/dev_mas-003_kpi_dashboard.md \
docs/_config.json docs/_internal/changelog.md
git commit -m "feat(MAS-003): KPIダッシュボード新設(93_kpi_dashboard)"
git push -u origin feat/MAS-003-kpi-dashboard
# PR 作成後、ユーザー指示を待ってマージ
### 拡張思考の使用状況
| フェーズ | 拡張思考 | 備考 |
|---------|:---:|------|
| 動作確認チェックリスト | なし | 機械的実行 |
| git 操作 | なし | 標準フロー |
推奨実行モデル
| 工程 | 推奨モデル | 理由 |
|---|---|---|
| 仕様書作成(本作業) | Opus | spec_dashboard §6 の昇格、MAS-024/MAS-020/MAS-001 の既存実装との整合判断、KPI選定と閾値設計の会計判断 |
| Step 1 DDL・シートキー追加 | Haiku | 既存パターン(PL_VAR, PL_PREV_YEAR)のコピペで済む |
| Step 2 数式生成ロジック | Sonnet | INDEX/MATCH/SUBSTITUTE(CHAR(10)) の組み立て、IFERROR ラッピング、HELPER 行設計の中程度の判断 |
| Step 3 条件付き書式 | Sonnet | newConditionalFormatRule の範囲指定・閾値デフォルト設計、50ルール上限に収める設計判断 |
| Step 4 動作確認・登録 | Haiku | チェックリスト実行と _config.json / changelog 追記のみ |
変更履歴
| 日付 | 変更内容 |
|---|---|
| 2026-04-17 | 初版作成。spec_dashboard.md §6 を正式な開発仕様書に昇格。MVP 5 KPI (K1/K2/K4/K5/K6) と Phase 2 拡張 (K3/K7/K8) に段階分割。MAS-024 の「安全余裕率」出力行ラベルをそのまま参照する方針を明示 |
| 2026-04-17 | Step 1 実装プロンプトと関連セクションを訂正。Grep だけで類推して書いた 3 件の誤りを修正: (1) 000_infra/002_constants.js の SHEET_DEFAULTS はシートキーマッピング用ではないため 002_constants.js の変更指示を削除 (2) シートキー登録先を 03_sys_params から正しい 01_sys_config(Constants.CONFIG_SHEET)に修正 (3) 実在しないメニュー「🔧 システム初期化 → MST_CONF_SHEETS 初期化」を実在する「🔧 開発・設定 → 全シートのスキーマとUIを最新化(DDL)」に訂正 |
| 2026-04-17 | Step 1 動作確認手順に initConfigs(シートのGID取得・リンク)の実行ステップを追加。setupAllSchemas で 01_sys_config 行追加 → initConfigs で 93_kpi_dashboard タブ生成 + GID 紐付け、の既存運用パターンに合わせた。補足として Step 2 の buildKpiDashboard() が独立してタブ生成する動作も明記 |
| 2026-04-17 | Step 2 実装内容を反映。(1) MATCH/ARRAYFORMULA/SUBSTITUTE 方式を廃止し GAS 側で行・列番号を事前計算してリテラル埋め込み方式に変更 (絵文字・全角スペース・改行での壊れ回避) (2) 月列を C-N の 12 列固定に限定 (MAS-020 YoY 差異列との混同を回避) (3) 境界月は 03_sys_params の CFG_BOUNDARY_YM 未設定時に売上高行末尾非空から動的検出 (4) 内訳を別テーブルから KPI 行直下の埋め込み (main/sub/subsub 3段) に再構成 (5) K5 BEP 配下に「うち人件費合計 / うち非人件費」の内訳行を追加。Step 2 で顕在化した #21-#24 の落とし穴を「注意事項」にも追記 |
| 2026-04-17 | prompt dispatch / receive workflow (dispatch_prompt.yml + receive_prompt.yml) の end-to-end 接続テスト用トリガー。内容の実質変更はなし |