MAS-115: 財務諸表タブの統合(プルダウン切替方式)
概要
| 項目 | 内容 |
|---|---|
| 案件ID | MAS-115 |
| カテゴリ | 財務諸表 UI 改善(データマート) |
| Phase | P2 |
| 優先度 | ★★ |
| 所要時間 | 8〜12時間(Step1〜3の実装+動作確認) |
| 対象ファイル(既存への追記) | 600_report/602_datamart_main.js / 100_config/101_sys_config.js / templates/operations_sidebar.html |
| 対象ファイル(新規) | 600_report/611_datamart_unified.js(仮名。レンダラの分離を推奨) |
| 前提案件 | MAS-094(基準年月プルダウン。buildDataMartWithCustomBoundary を再利用) |
| 関連案件 | MAS-093(isActualOnly 実績専用モード)/ MAS-095(B/S スナップ実績専用化)/ MAS-025(予実差異)/ MAS-001(P/L予実差異) |
目的
現状、P/L・B/S・CF の財務諸表は 61_pl_monthly / 62_pl_ytd / 63_pl_monthly_plan / 64_pl_ytd_plan / 65_pl_variance / 71_bs / 72_bs_snap / 73_bs_plan / 81_cf_indirect / 81b_cf_indirect_ytd / 82_cf_indirect_plan / 82b_cf_indirect_plan_ytd の 12 タブに分散しており、ユーザーが「今月の実績 P/L を単月で見たい」「計画比で累積 B/S を見たい」といった目的ごとに複数タブを跨ぐ必要がある。スプレッドシート左下のタブ一覧が肥大化し、目的のデータへ到達するまでの操作コストが高い。
MAS-115 では、P/L・B/S・間接法 CF をそれぞれ 1 タブに統合し、タブ内 A1 セルに配置する「表示モード」プルダウンで切替できる UX に変更する。切替は onEdit トリガーで即時に再描画し、12 タブ → 4〜5 タブ(P/L 1 + B/S 1 + CF間接 1 + CF日次 1〜2)へ 60% 以上の削減を目指す。なお日次 CF(83/84/85)は性質が異なるため統合対象外とする。
現在のコード
既存シートと KEY マッピング(600_report/602_datamart_main.js L172-188 および 100_config/101_sys_config.js L609-620 で確認済み)
| KEY | デフォルト名 | 種別 | 備考 |
|---|---|---|---|
PL_M_ACT | 61_pl_monthly | P/L 実績 単月 | isActualOnly=true で描画 |
PL_Y_ACT | 62_pl_ytd | P/L 実績 累積 | isActualOnly=true で描画 |
PL_M_PLAN | 63_pl_monthly_plan | P/L 計画 単月 | isActualOnly=false で描画 |
PL_Y_PLAN | 64_pl_ytd_plan | P/L 計画 累積 | isActualOnly=false で描画 |
PL_VAR | 65_pl_variance | P/L 予実差異 | MAS-001 で MAS-025 により分離済 |
BS_ACT | 71_bs | B/S 実績 | isActualOnly=true で描画 |
BS_SNAP | 72_bs_snap | B/S スナップショット | MAS-095 で実績専用化済み |
BS_PLAN | 73_bs_plan | B/S 計画 | isActualOnly=false で描画 |
CF_IND_ACT | 81_cf_indirect | 間接法 CF 実績 単月 | MAS-093 で実績専用対応済 |
CF_IND_ACT_YTD | 81b_cf_indirect_ytd | 間接法 CF 実績 累積 | 同上 |
CF_IND_PLAN | 82_cf_indirect_plan | 間接法 CF 計画 単月 | — |
CF_IND_PLAN_YTD | 82b_cf_indirect_plan_ytd | 間接法 CF 計画 累積 | — |
CF_D_ACT | 83_cf_daily | 日次 CF 実績 | 統合対象外(本案件では変更しない) |
CF_D_PLAN | 84_cf_daily_plan | 日次 CF 計画 | 統合対象外 |
CF_D_REAL | 85_cf_daily_actual | 日次 CF 実績(銀行残高ベース) | 統合対象外 |
既存レンダラ関数(600_report/602_datamart_main.js)
| 関数 | 所在 | シグネチャ | 役割 |
|---|---|---|---|
buildBudgetTrendDataMart | 602 L159 | function buildBudgetTrendDataMart(overrideBoundary) | マート更新の公開エントリ。全シートに一括出力 |
dmIngestData_ | 601 | (ctx, sheetInv, sheetBank, sheetAcct) | 32/33/11 タブを読み込み ctx を構築 |
dmCalcPl_ / dmCalcBs_ / dmCalcCf_ | 603/604/605 | (ctx) | martPl / martBs から P/L・B/S・CF の集計を計算 |
dmBuildPlOutput_ | 603 L199 | (ctx) → {outM, outY, fmtM, fmtY, yoyColCount} | P/L の出力配列(単月=outM / 累積=outY)を同時生成 |
dmBuildBsOutput_ | 604 L145 | (ctx) → {outB, fmtB} | B/S の出力配列を生成 |
dmBuildCfOutput_ | 605 L52 | (ctx) → {outC, outCY, fmtC, fmtCY} | CF の出力配列(単月=outC / 累積=outCY)を同時生成 |
dmApplyDwhFormat_ | 608(描画) | (sheet, out, fmt, 'PL'|'BS'|'CF', targetMonthsWithActBgt) | シートに書き出し・書式適用 |
buildDataMartWithCustomBoundary | 602 L386 | () | MAS-094 の基準年月プルダウン(ダイアログ方式) |
isActualOnly フラグの実際の型と挙動
- 型: JavaScript 素の
boolean(ctx.isActualOnly = true;L243、計画側はisActualOnly: falseL267) - 参照箇所: 603 L204 / 604 L148 / 605 L57
- 挙動:
trueのときfilterValues()/filterWithRecalcTotal()で境界月以降を空白に除去し、ヘッダーから「(実績)/(予算)」ラベルを落とす。falseのときは全月表示+ラベル付き
既存の onEdit 定義(100_config/101_sys_config.js L363-413)
- GAS の
onEditは100_config/101_sys_config.jsL363 で単一定義されている(300_ui/301_ui_assist.jsには存在しない。同ファイルのhandleUxAssist(e)が L412 で呼び出されるだけ) - 既存分岐は
sheetName === e.range.getSheet().getName()とrow/colによる判定(getA1Notation()は使っていない) - 既存分岐リスト: (a)
Constants.CONFIG_SHEETの key/logic 生成、(b)36_wrk_bank_importのマッチ方法自動更新、(c)33_wrk_bank/32_wrk_invoiceの仕訳発行後編集監査、(d)handleUxAssist(e)への委譲
メニュー構造の現状(100_config/101_sys_config.js L299-307)
onOpen() は 🚀 BizLP メニュー 1 本のみを生成し、個別のマート更新系ボタンは全て templates/operations_sidebar.html のサイドバーに移設されている。MAS-094 仕様書内に記載された 📊 マート更新 トップメニューは現行コードでは廃止されている点に注意(新規メニューを増やすのではなく、サイドバー HTML に追記する運用)。
BudgetRepository の不在(Phase 1 で Read 確認済)
200_data/202_repository.jsに定義されている Repository はOrderRepository/InvoiceRepository/BankTxRepository/JournalRepository/AccountRepositoryの 5 つのみBudgetRepositoryは未実装。本案件は既存dmIngestData_が返すctx.finalUnionDataを再利用するため、41_trn_budget への直接アクセスは原則不要。仮に新規に読み込む必要が生じた場合は41_trn_budget.getDataRange().getValues()+ ヘッダー名ベースindexOf参照で暫定対応する(Repository 新設は別案件として分離)
動的生成タブの扱い(CLAUDE.md 記載)
CLAUDE.md の「DDL (setupAllSchemas) で管理されないタブ」に列挙される動的生成タブ: 03_sys_params / 75_ss_equity_changes / 76_notes / 77_pj_raw / 78_pj_pl / 91_fs_bs / 92_fs_pl / 93_kpi_dashboard / 90_test_results。
一方で 61〜65 / 71〜73 / 81〜82b は DDL 管理対象(setupAllSchemas の対象)であり、setupAllSchemas 再実行でプルダウンが消失する可能性がある。統合タブを DDL 管理対象にするか動的生成扱いにするかは Step 1 の実装方針で決定する(本仕様書では DDL 管理対象として登録する方針を推奨)。
修正方針
Step 1: 統合シートの作成と初期化(Claude Sonnet 4.6 推奨)
1-a: 統合シートの番号帯・名称
本案件では以下の 3 シートを新設する。既存番号帯と重複しない 6X / 7X / 8X の空き番号を選定する:
| 新規シート名(候補) | KEY(新設) | 役割 | 既存対応 |
|---|---|---|---|
60_fs_pl_unified | FS_PL_U | P/L 統合 | 61/62/63/64 を集約 |
70_fs_bs_unified | FS_BS_U | B/S 統合 | 71/72/73 を集約 |
80_fs_cf_unified | FS_CF_U | 間接法 CF 統合 | 81/81b/82/82b を集約 |
注記: シート番号 60 / 70 / 80 は現行未使用(Phase 1 調査で確認)。ただし将来的に DDL で予約される可能性があるため、最終的な番号採番は実装着手時に 101_sys_config.js の setupAllSchemas スキーマ一覧と照合して確定する(「要確認」扱い)。
1-b: プルダウン(データバリデーション)の設置
各統合シートの A1 セルに DataValidation を設置する。選択肢は以下:
- P/L 統合(
60_fs_pl_unified):実績・単月(既存 61 相当、isActualOnly=true、viewMode='monthly')実績・累積(既存 62 相当、isActualOnly=true、viewMode='ytd')計画含む・単月(既存 63 相当、isActualOnly=false、viewMode='monthly')計画含む・累積(既存 64 相当、isActualOnly=false、viewMode='ytd')
- B/S 統合(
70_fs_bs_unified):実績(既存 71 相当)スナップショット(月末時点)(既存 72 相当、viewMode='snap')計画含む(既存 73 相当、isActualOnly=false)
- CF 統合(
80_fs_cf_unified):実績・単月(既存 81 相当)実績・累積(既存 81b 相当)計画含む・単月(既存 82 相当)計画含む・累積(既存 82b 相当)
注記: CLAUDE.md コーディング規約「シートへの書き込み位置は列 B (ID列) で最終行を判定(列 A のチェックボックス回避)」は 32_wrk_invoice 等のデータ入力シートに適用される規約。財務諸表レポートシート(本案件のアウトプット)は 1 行目=ヘッダー、2 行目以降=集計結果で、チェックボックス列は存在しないため A1 セルにプルダウンを設置して問題ない(既存 targetMonthsWithActBgt ヘッダーが列 A〜列 P に並ぶレイアウトと整合させ、プルダウンは列 A 1 行目のヘッダー文字列の代わりに配置するか、または 1 行目をプルダウン専用行・2 行目以降に既存のヘッダーを下げる構造に変更する。後者のほうが既存 dmApplyDwhFormat_ との衝突が少ない)。
1-c: 初期化フロー
プルダウンの初期設置は setupAllSchemas に統合するのが望ましい。代替として buildBudgetTrendDataMart 内の「出力書き込み」直前に ensureUnifiedSheetDropdown_(sheet) を挟み、毎回のマート更新で冪等的に設置する方式でもよい(既存 dmApplyDwhFormat_ との同居が容易)。両方式の長所短所は Step 1 実装時に人間判断で決める(「人間が検討すべき事項」に記載)。
1-d: 初期表示モード
プルダウン未選択状態(空セル)では 実績・単月 をデフォルトとしてフォールバック描画する。handleUnifiedFsEdit_ の冒頭で e.value が空のときは 実績・単月 をそのまま採用する。
Step 2: onEdit トリガーへの分岐追加(Claude Sonnet 4.6 推奨)
2-a: 挿入位置
100_config/101_sys_config.js L363-413 の既存 onEdit(e) 関数内、if (sheetName === Constants.CONFIG_SHEET ...) の分岐(L366)の後、if (sheetName === '36_wrk_bank_import') の分岐(L372)の前に、MAS-115 専用分岐を挿入する:
// S-43: 財務諸表統合タブのプルダウン変更を検知して再描画
if (sheetName === '60_fs_pl_unified' || sheetName === '70_fs_bs_unified' || sheetName === '80_fs_cf_unified') {
if (e.range.getA1Notation() === 'A1') {
try { handleUnifiedFsEdit_(e); } catch (err) { Utils.logError('handleUnifiedFsEdit_', err); }
return;
}
}
return することで後続の handleUxAssist(e) を呼ばない(統合タブは手入力対象ではないため)。
2-b: LockService 排他制御
handleUnifiedFsEdit_(e) の冒頭で以下の排他制御を行う:
var lock = LockService.getScriptLock();
if (!lock.tryLock(3000)) {
Utils.toastResult('unifiedFs', '他の処理が実行中のため再描画をスキップしました。数秒後に再操作してください。');
return;
}
try {
// 再描画本体
} finally {
lock.releaseLock();
}
tryLock(3000)で 3 秒待機。取得失敗時はトーストで通知して終了(waitLockではなくtryLockを使う理由は、UI 操作の応答性を優先するため。マート更新など長時間処理と競合すると 3 秒でタイムアウトするが、再操作で済む)finallyでreleaseLock()を呼ぶ。エラー発生時もロックが残らない
2-c: MAS-094 との共存
MAS-094 は現在ダイアログ方式(buildDataMartWithCustomBoundary)のため同一 onEdit には干渉しない。ただし将来 MAS-094 が A1 セルプルダウン化される可能性があるため、e.range.getA1Notation() で判定する構造にしておく(同じ A1 セルを両機能が使うことは避ける。例えば MAS-094 は A2 セル、MAS-115 は A1 セル、と明示的に分担する)。
Step 3: 再描画ロジックの実装(Claude Opus 4.6 推奨)
3-a: 新規ファイル 600_report/611_datamart_unified.js を作成
以下の関数を定義する:
handleUnifiedFsEdit_(e)—onEditから呼ばれるエントリ。シート名で P/L / B/S / CF を分岐renderUnifiedPl_(mode)— P/L 統合シートに描画。modeは{ isActualOnly, viewMode }のオブジェクトrenderUnifiedBs_(mode)— B/S 統合シートに描画renderUnifiedCf_(mode)— CF 統合シートに描画ensureUnifiedSheetDropdown_(sheet, choices)— A1 プルダウンを冪等に設置
3-b: 既存マートビルダの再利用方針
既存 buildBudgetTrendDataMart は 12 シート全てを一括再描画する重い処理(dmIngestData_ で 32/33 タブをフルスキャン、実績と計画の 2 パス dmProcessAllEvents_ / dmCalcPl_ 等)。onEdit からの再描画では 1 モードのみ描画すれば十分なため、以下の方針で軽量化する:
方式 A(推奨): buildBudgetTrendDataMart のロジックを部分抽出
dmIngestData_+dmProcessAllEvents_+dmCalc*_までは共通で実行(実績/計画どちらか一方のみ)dmBuild*Output_の結果をdmApplyDwhFormat_で統合シートに書き出す- 既存の 12 タブへの書き出しはスキップ
方式 B: buildBudgetTrendDataMart をそのまま呼び、最後に統合シートへのコピー処理を追加
- 実装は簡単だが重い(P/L 1 枚見るだけで 12 枚全再描画)
Step 3 では方式 A を採用する。buildBudgetTrendDataMart をリファクタして「実績 ctx 構築部」「計画 ctx 構築部」「シート書き出し部」の 3 関数に分割する(buildActualCtx_() / buildPlanCtx_() / writeToSheets_())。統合シート再描画はこの 3 関数のうち必要なものだけを呼ぶ。
リファクタリングの影響範囲: buildBudgetTrendDataMart 本体(~220 行)を分割。既存の呼び出し側(buildBudgetTrendDataMart / buildDataMartWithCustomBoundary)は引数なし呼び出しを維持し、内部的に分割後関数を順に呼ぶラッパー構造とする。
3-c: 表示モードと isActualOnly の対応
既存の isActualOnly(boolean)は「実績のみフィルタ」の意味であり、統合タブの viewMode(monthly/ytd/snap)とは直交する概念。以下のマトリクスで実際のレンダラ出力配列を切り替える:
| プルダウン選択 | isActualOnly | 出力配列 | 説明 |
|---|---|---|---|
| 実績・単月(P/L) | true | plOut.outM | 境界月以降空白、実績のみ |
| 実績・累積(P/L) | true | plOut.outY | 累積ビュー |
| 計画含む・単月(P/L) | false | planPlOut.outM | 計画パイプライン込み |
| 計画含む・累積(P/L) | false | planPlOut.outY | 同上 |
| 実績(B/S) | true | bsOut.outB | — |
| スナップショット(B/S) | true | dmRenderBsSnapshot_ 相当 | 月末確定残高 |
| 計画含む(B/S) | false | planBsOut.outB | — |
| 実績・単月(CF) | true | cfOut.outC | — |
| 実績・累積(CF) | true | cfOut.outCY | — |
| 計画含む・単月(CF) | false | planCfOut.outC | — |
| 計画含む・累積(CF) | false | planCfOut.outCY | — |
3-d: 処理中表示
handleUnifiedFsEdit_ の冒頭で Utils.toastResult('unifiedFs', '更新中...') を呼び、再描画完了時に再度 Utils.toastResult('unifiedFs', '完了') を呼ぶ。数秒以上かかる場合にユーザーが操作フィードバックを得られる。
3-e: SpreadsheetApp.flush() の挿入
既存コードに SpreadsheetApp.flush() の使用箇所はないが、再描画完了後に 1 回だけ flush() を呼ぶことで、プルダウン変更 → 画面反映のラグを軽減する。dmApplyDwhFormat_ の直後、lock.releaseLock() の直前に挿入。
影響範囲
| 区分 | ファイル | 変更内容 | 変更量 |
|---|---|---|---|
| 追記 | 600_report/602_datamart_main.js | buildBudgetTrendDataMart を 3 関数に分割(buildActualCtx_ / buildPlanCtx_ / writeToSheets_)し、既存ラッパーとして維持 | 大(リファクタ中心で ~250 行の再配置) |
| 新規 | 600_report/611_datamart_unified.js | 統合タブ用のレンダラ(handleUnifiedFsEdit_ / renderUnifiedPl_ / renderUnifiedBs_ / renderUnifiedCf_ / ensureUnifiedSheetDropdown_) | 中(~300 行) |
| 追記 | 100_config/101_sys_config.js | onEdit に MAS-115 分岐 1 ブロック追加、setupAllSchemas の SYS_CONFIG へ 3 KEY(FS_PL_U / FS_BS_U / FS_CF_U)追加、必要なら DDL スキーマ登録 | 小〜中 |
| 追記 | templates/operations_sidebar.html | §📊 マート更新 セクションに「統合タブを初期化」ボタン 1 つ追加(初回セットアップ用) | 小(1 ブロック) |
| 既存維持 | 61〜65 / 71〜73 / 81〜82b | 旧タブは並行稼働期間中は出力を継続。削除は段階移行(別案件) | 変更なし |
注意事項
BudgetRepository不在への対応:200_data/202_repository.jsにBudgetRepositoryは存在しない(Phase 1 で Read 確認済)。本案件では既存dmIngestData_/dmIngestPlanData_/dmIngestPipelinePlanData_が返すctx.finalUnionDataを再利用し、41_trn_budgetへの直接アクセスは行わない。将来 Repository 化する場合は MAS-115 のスコープ外とする。- 既存
onEdit分岐の破壊防止: L364 のif (row === 1) return;で「1 行目の編集はスキップ」されている点に注意。MAS-115 では A1 セルを編集対象とするため、この分岐を経由しない書き換えが必要。具体的には、A1 編集検出をrow === 1チェックより前に行うか、row === 1分岐の中で A1 判定を行う。推奨は後者(既存の仕訳振り分けロジックなど他のrow > 1前提の分岐を壊さない)。 - DDL 管理対象シートの扱い: 統合シート(
60_fs_pl_unified等)は DDL 管理対象としてsetupAllSchemasに登録する方針。DDL 再実行時にプルダウンが消失しないようsetValiで明示的に再設定するか、スキーマ定義にdataValidationプロパティを組み込む。 - CLAUDE.md コーディング規約遵守: 列参照は全てヘッダー名ベース(
indexOf)で行い、列番号ハードコード禁止(既存dmApplyDwhFormat_がtargetMonthsWithActBgt配列を引数で受けるためこの規約は既に守られている)。 - 旧タブの並行稼働: 移行期間中(推奨 1 ヶ月)は
61〜65 / 71〜73 / 81〜82bの旧タブへの出力を継続する。既存buildBudgetTrendDataMartの書き出しロジックを削除しないこと。旧タブの廃止は別案件(MAS-115-follow)として分離し、安定確認後に実施する。 - MAS-094 との A1 セル衝突回避: MAS-094 は現時点でダイアログ方式だが、将来プルダウン化される場合は MAS-094 用セルを A2、MAS-115 用セルを A1 と明示的に分担する。
LockServiceのセッション範囲:LockService.getScriptLock()はスクリプト全体で 1 つのロックを共有するため、マート更新実行中にプルダウン操作すると 3 秒でtryLockが失敗する。これは正しい挙動(二重再描画を防ぐため)だが、ユーザーへの通知文言(Utils.toastResultの第 2 引数)で理由を明示する。isActualOnlyの既存参照箇所(603/604/605)の不変性:isActualOnlyを参照する既存ロジックは変更しない。統合タブでも全く同じctx.isActualOnly+ 出力配列(outM/outY/outB/outC/outCY)を使い回す。dmApplyDwhFormat_の第 1 引数sheetは統合シートに切替可能: 既存の書き出し関数dmApplyDwhFormat_(sheet, out, fmt, 'PL'\|'BS'\|'CF', targetMonthsWithActBgt)はシートを引数で受けるため、統合シートを渡すだけで既存フォーマット処理を再利用できる。
エッジケース
| # | 条件 | 表示値/挙動 | 理由 |
|---|---|---|---|
| E01 | プルダウン未選択(A1 セルが空) | デフォルト 実績・単月 で描画 | 初回アクセス時の UX。ユーザー操作なしでも壊れない |
| E02 | 42_trn_journal / 41_trn_budget が空 | エラーなく空の表を描画(列ヘッダー・科目行は維持) | データ未投入時の安全な初期表示 |
| E03 | P/L の売上高 = 0(比率列でのゼロ除算) | "-" 表示(failure_patterns.md #2 に準拠) | 既存 dmBuild*Output_ が既にこの挙動を実装済。そのまま継承 |
| E04 | onEdit の多重実行(ユーザーが連続でプルダウンを切替) | LockService.tryLock(3000) 失敗 → Utils.toastResult('unifiedFs', '他の処理が実行中...') 通知してスキップ | パフォーマンス保護 |
| E05 | MAS-094 のプルダウンと MAS-115 のプルダウンが同一 onEdit で発火 | e.range.getA1Notation() と sheetName で分岐し、それぞれの処理を独立実行 | MAS-094 との共存(現状は別シート/別メニュー方式で衝突しない) |
| E06 | 既存マートビルダ関数のリファクタ中に旧関数呼び出しが残存 | デプロイ前に grep -rn "dmBuildPlOutput_|dmBuildBsOutput_|dmBuildCfOutput_" 600_report/ で全参照を確認(failure_patterns.md #7 に準拠) | ファイル分割時の未定義関数エラー防止 |
| E07 | 統合シート未作成の状態でプルダウン操作 | getSheetByKey('FS_PL_U', '60_fs_pl_unified') が null を返すため handleUnifiedFsEdit_ が早期 return。Utils.toastResult で「統合タブ未初期化」を通知 | 未セットアップ環境での安全なフェイル |
| E08 | setupAllSchemas 実行でプルダウンが消える | DDL 側で dataValidation を再設定するか、ensureUnifiedSheetDropdown_ を毎マート更新で冪等に呼ぶ | DDL との整合性 |
| E09 | 境界月(boundaryMonthStr)が未確定(1900-01) | 既存 buildBudgetTrendDataMart L237 の処理をそのまま流用し、実績反映: なし 表示 | 既存仕様を継承 |
| E10 | プルダウン選択肢以外の文字列が A1 に書き込まれる | DataValidation.requireValueInList() で入力を拒否。万が一通った場合は handleUnifiedFsEdit_ 内の switch デフォルトで 実績・単月 にフォールバック | 異常入力の安全な処理 |
| E11 | スナップショット(B/S)モードで実績月がない | dmRenderBsSnapshot_ は sheetBs 依存のため、統合タブではセクショナル描画のみ実装。月末残高が 0 の月は空白表示 | 既存 72_bs_snap の挙動を踏襲 |
| E12 | SpreadsheetApp.flush() の重複呼び出し | 冪等なので安全。ただしマート更新本体との競合を避けるため、LockService ブロック内で 1 回だけ呼ぶ | パフォーマンス |
| E13 | プルダウン操作と buildBudgetTrendDataMart 実行が同時 | LockService 競合で tryLock 失敗 → ユーザーに再操作依頼 | 二重書き込み防止 |
| E14 | 統合シート番号(60/70/80)が将来 DDL で予約される | 実装着手時に 101_sys_config.js 内のスキーマ一覧と getSheetByKey の登録済み KEY を grep で確認し、未使用を再確認 | 番号衝突防止 |
| E15 | handleUnifiedFsEdit_ 内で例外発生 | try/catch で Utils.logError + SpreadsheetApp.getUi().alert 通知。ロックは finally で確実に解放 | 安全なエラーハンドリング |
実データ検証(実装前に MCP / GAS 実機で確認すべき項目)
| # | 確認項目 | 確認方法 | 理由 |
|---|---|---|---|
| V01 | 現行 61_pl_monthly / 62_pl_ytd / 71_bs / 81_cf_indirect に実データがあること | dev 環境でマート更新実行後、各シートの 2〜5 行目を目視確認 | 統合タブ描画後の比較対象を確保 |
| V02 | プルダウン選択肢文字列(候補値)の最終決定 | TODO_future.md の記載と「人間が検討すべき事項」を突き合わせ、UX ワーディングを確定する | 実装後の変更はプルダウン再設定+ onEdit switch の書き換えが必要で手戻りコスト大 |
| V03 | isActualOnly の実際の格納型 | 602 L243 を Read 確認済(true)。スプレッドシートセルからの読み取り値ではなく ctx オブジェクトの JS boolean | 統合タブのプルダウン値(文字列)→ boolean 変換ロジックを正しく実装するため |
| V04 | 41_trn_budget に対象月のデータが存在するか | 41 タブを開き、対象期の 12 ヶ月分の予算データが 1 件以上存在することを確認 | 予算ゼロ月の描画確認(E02 ケース) |
| V05 | 統合タブ番号 60/70/80 が既存・計画中スキーマと衝突しないか | grep -rn "60_|70_|80_" 100_config/ 600_report/ で既存参照を全件チェック | 番号衝突防止(E14 ケース) |
| V06 | LockService.tryLock(3000) の挙動 | マート更新中にプルダウン操作して、トースト通知が出ることを手動検証 | E04 / E13 の挙動確認 |
| V07 | setupAllSchemas 再実行でプルダウンが消えないこと | dev 環境で統合タブ初期化 → setupAllSchemas → プルダウンが残存していることを目視 | E08 の挙動確認 |
関連ドキュメント
| 仕様書 / ファイル | 関連箇所 |
|---|---|
| dev_mas-094_boundary_month_selector.md | 基準年月プルダウンの実装方式。MAS-115 の onEdit 分岐と共存設計 |
| dev_mas-093_cf_actual_only.md | isActualOnly パターンの CF への適用。MAS-115 でも同フラグを流用 |
| dev_mas-095_bs_snap_actual_only.md | B/S スナップショットの実績専用化パターン。統合タブの「スナップショット」モードで参照 |
| dev_mas-001_variance_analysis.md | 財務諸表系仕様書のフォーマット・エッジケース記述の深度(参考) |
| dev_mas-025_budget_variance.md | 予算 vs 実績差異分析。MAS-001 分離後の 65 タブと統合タブの共存方針 |
docs/_internal/failure_patterns.md #2 | ゼロ除算フォールバック("-" 表示) |
docs/_internal/failure_patterns.md #7 | ファイル分割/リファクタリング時の旧関数残存 |
| CLAUDE.md | 動作確認テスト(600_report/6*_datamart_*.js → マート更新テスト)/列参照のヘッダー名ベース規約 |
人間が検討すべき事項
以下は TODO_future.md に記載された事項+本仕様策定で追加抽出した事項:
- プルダウンの配置場所: A1 セル固定 vs フローティング(データバリデーションのみ)。本仕様では A1 セル固定案を推奨したが、最終判断は UX 要件次第(TODO_future.md より転記)。
- 切替時の再描画速度: データ量が多いと遅延する可能性(TODO_future.md より転記)。方式 A(ctx 構築部 + 出力部の分割)で軽量化する設計だが、実測で 3 秒を超える場合は
tryLockタイムアウトの調整が必要。 - 既存タブの廃止タイミング: 並行稼働期間の設定(TODO_future.md より転記)。推奨 1 ヶ月。廃止ステップ: (a) 旧タブ上部に「➡️ 統合タブへ移動」バナー設置 → (b) 旧タブ非表示化(シートを非表示、データは保持) → (c) 安定確認(最低 2 週間)後に完全削除。
- GCP 移行(MAS-238)後の投資対効果: WebUI で実現する前提の場合、GAS フェーズでの実装工数の投資対効果判断(TODO_future.md より転記)。移行見込み時期と GAS フェーズの残存期間から ROI を評価。
- 統合シートの番号帯・シート名の最終確定:
60_fs_pl_unified/70_fs_bs_unified/80_fs_cf_unifiedは候補。番号衝突確認(V05 / E14)後に決定。 - プルダウン選択肢文字列の確定: 本仕様では
実績・単月/実績・累積/計画含む・単月/計画含む・累積を提案。ユーザー向け UX として「実績」「見込み」「計画」等の呼称バリエーションの検討余地あり(V02)。 BudgetRepositoryの設計判断: 本案件では直接アクセス不要だが、将来の別案件で新規 Repository クラス作成 vs41_trn_budget直接アクセスのどちらを採用するか。buildBudgetTrendDataMartの 3 関数分割: 方式 A を採用する場合、リファクタの影響範囲が広い。既存テスト(901_test_runner.js)でマート更新系テストが存在する場合はリグレッション確認が必要。- プルダウン初期化の責務: DDL (
setupAllSchemas) で担当 vs 毎マート更新で冪等設置 vs 初回専用のセットアップ関数 — 3 案の選択(1-c)。 - 旧タブ関連の既存テストケース更新: 旧タブ廃止時に
901_test_runner.jsのマート更新テストを更新する必要あり。廃止フェーズで別 PR として分離。 - A1 / A2 の配置方針: MAS-094 が将来 A1 プルダウン化される場合の役割分担(MAS-094 は A2、MAS-115 は A1 等の明示化)。
実装プロンプト(Claude Code 用)
あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者です。
案件 MAS-115「財務諸表タブの統合(プルダウン切替方式)」を実装してください。
## 実行前タスク
以下のファイルを Read し、実装前のコンテキストを把握してください:
1. `docs/dev/dev_mas-115_unified_financial_statements.md` — 本仕様書全体
2. `600_report/602_datamart_main.js` — `buildBudgetTrendDataMart(overrideBoundary)` の構造(L159-L380)。`dmIngestData_` / `dmProcessAllEvents_` / `dmBuildPlOutput_` / `dmBuildBsOutput_` / `dmBuildCfOutput_` / `dmApplyDwhFormat_` の呼び出し順序を確認
3. `600_report/603_datamart_pl.js` — `dmBuildPlOutput_` の引数と戻り値(`{outM, outY, fmtM, fmtY, yoyColCount}`)、`isActualOnly`(L204)および `filterValues` / `filterWithRecalcTotal` の実装
4. `600_report/604_datamart_bs.js` — `dmBuildBsOutput_` の実装、`isActualOnly`(L148)
5. `600_report/605_datamart_cf.js` — `dmBuildCfOutput_` の実装、`isActualOnly`(L57)
6. `100_config/101_sys_config.js` — `onEdit`(L363-413)の既存分岐構造、`setupAllSchemas` と `SYS_CONFIG` への KEY 登録ロジック(L609-620 付近)、`onOpen`(L299-307)のメニュー構造
7. `300_ui/301_ui_assist.js` — `handleUxAssist(e)` が `onEdit` 内 L412 から呼ばれていることを確認
8. `200_data/202_repository.js` — `OrderRepository` / `InvoiceRepository` / `BankTxRepository` / `JournalRepository` / `AccountRepository` のみ存在し、**`BudgetRepository` は未実装**であることを確認
9. `templates/operations_sidebar.html` — サイドバーの `§📊 マート更新` セクションへのボタン追加箇所を確認
10. `CLAUDE.md` — DDL 管理外タブ一覧、コーディング規約
11. `docs/dev/dev_mas-094_boundary_month_selector.md` — MAS-094 のダイアログ方式の実装、将来プルダウン化時の共存方針
## 修正対象ファイル
- **新規作成**: `600_report/611_datamart_unified.js`
- **既存への追記**:
- `600_report/602_datamart_main.js` — `buildBudgetTrendDataMart` を 3 関数に分割(`buildActualCtx_` / `buildPlanCtx_` / `writeToSheets_`)し、既存ラッパーとして維持
- `100_config/101_sys_config.js` — `onEdit` に MAS-115 分岐ブロック追加、`setupAllSchemas` の KEY 登録に `FS_PL_U` / `FS_BS_U` / `FS_CF_U` の 3 行追加、DDL スキーマ定義(該当がある場合のみ)
- `templates/operations_sidebar.html` — 統合タブ初期化ボタン 1 つ追加
## 実装内容
### Step 1: 統合シートの作成と初期化
1. 新規シート `60_fs_pl_unified` / `70_fs_bs_unified` / `80_fs_cf_unified` を導入。シート番号が既存 DDL スキーマと衝突しないことを `101_sys_config.js` の `setupAllSchemas` 内スキーマ一覧で確認してから確定する
2. `SYS_CONFIG` (`03_sys_params` テーブル)に KEY `FS_PL_U` / `FS_BS_U` / `FS_CF_U` を追加(101 L609-620 のパターンに準拠)
3. 各シートの A1 セルにデータバリデーション(プルダウン)を設置:
- P/L: `実績・単月` / `実績・累積` / `計画含む・単月` / `計画含む・累積`
- B/S: `実績` / `スナップショット(月末時点)` / `計画含む`
- CF: `実績・単月` / `実績・累積` / `計画含む・単月` / `計画含む・累積`
4. プルダウン未選択時(A1 が空)のデフォルトは P/L・CF は `実績・単月`、B/S は `実績` とする
### Step 2: `onEdit` トリガーへの分岐追加
1. `100_config/101_sys_config.js` L363 の `onEdit(e)` 関数内、`Constants.CONFIG_SHEET` 分岐(L366-370)の**後**、`36_wrk_bank_import` 分岐(L372)の**前**に以下を挿入:
if (sheetName === '60_fs_pl_unified' || sheetName === '70_fs_bs_unified' || sheetName === '80_fs_cf_unified') {
if (e.range.getA1Notation() === 'A1') {
try { handleUnifiedFsEdit_(e); } catch (err) { Utils.logError('handleUnifiedFsEdit_', err); }
return;
}
}
2. L365 の `if (row === 1) return;` より**前**に上記ブロックを配置することで、1 行目の編集でも処理される分岐を確保する
### Step 3: 再描画ロジックの実装(`600_report/611_datamart_unified.js`)
1. `handleUnifiedFsEdit_(e)` — `LockService.getScriptLock()` + `tryLock(3000)` + `try/finally releaseLock()` パターンで排他制御。`sheetName` で P/L / B/S / CF を分岐し、プルダウン値(`e.value`)を `{ isActualOnly: boolean, viewMode: string }` のオブジェクトに変換して `renderUnified*_` を呼ぶ
2. `renderUnifiedPl_(mode)` — `buildActualCtx_()` または `buildPlanCtx_()` を呼んで ctx を構築し、`dmBuildPlOutput_(ctx)` で出力配列を取得、`mode.viewMode === 'ytd'` なら `outY`、それ以外は `outM` を選んで `dmApplyDwhFormat_(sheet, out, fmt, 'PL', targetMonthsWithActBgt)` で統合シートに書き出す
3. `renderUnifiedBs_(mode)` — 同上の流れで `dmBuildBsOutput_` → `outB` を書き出し。`mode.viewMode === 'snap'` のときは `dmRenderBsSnapshot_` 相当の処理を統合シート向けに呼ぶ
4. `renderUnifiedCf_(mode)` — 同上の流れで `dmBuildCfOutput_` → `outC` / `outCY` を書き出し
5. `ensureUnifiedSheetDropdown_(sheet, choices)` — A1 セルにプルダウンを冪等に設置するヘルパー
6. `buildBudgetTrendDataMart` を 3 関数(`buildActualCtx_` / `buildPlanCtx_` / `writeToSheets_`)に分割。既存の呼び出し側(`buildBudgetTrendDataMart()` / `buildDataMartWithCustomBoundary()`)は引数なし呼び出しを維持し、内部的に分割後関数を順に呼ぶラッパー構造とする
7. `Utils.toastResult('unifiedFs', '更新中...')` → 再描画 → `SpreadsheetApp.flush()` → `Utils.toastResult('unifiedFs', '完了')` の順で UX フィードバック
## 制約
- 既存の `onEdit` 分岐ロジックを破壊しないこと(特に L365 の `row === 1` 早期リターンの挙動は他分岐に影響しない形で回避する)
- 列番号のハードコード禁止。ヘッダー名 `indexOf` で参照(CLAUDE.md 規約)
- `BudgetRepository` が未存在のため、`41_trn_budget` への直接アクセスは本案件では行わない。既存 `dmIngestData_` / `dmIngestPlanData_` / `dmIngestPipelinePlanData_` 経由で得た `ctx` を再利用する
- 動作未確認のコードを GitHub に push しない(CLAUDE.md 規約)
- 実在しないメニュー名・関数名を動作確認手順に記載しない
- 既存 `61〜65 / 71〜73 / 81〜82b` の旧タブへの出力は並行稼働期間中は継続する(本案件では削除しない)
## エッジケース
| # | 条件 | 表示値/挙動 | 理由 |
|---|------|------------|------|
| E01 | プルダウン未選択(A1 セルが空) | デフォルト `実績・単月` で描画 | 初回アクセス時の UX |
| E02 | `42_trn_journal` / `41_trn_budget` が空 | エラーなく空の表を描画 | データ未投入時の安全な初期表示 |
| E03 | P/L 売上高=0(ゼロ除算) | `"-"` 表示 | failure_patterns.md #2 |
| E04 | `onEdit` 多重実行 | `tryLock(3000)` 失敗で `Utils.toastResult` 通知してスキップ | パフォーマンス保護 |
| E05 | MAS-094 / MAS-115 の同一 `onEdit` 発火 | `e.range.getA1Notation()` と `sheetName` で分岐 | MAS-094 との共存 |
| E06 | 旧関数呼び出し残存 | デプロイ前に `grep` で全参照確認 | failure_patterns.md #7 |
| E07 | 統合シート未作成でプルダウン操作 | `getSheetByKey` null → 早期 return | 未セットアップ環境での安全フェイル |
| E08 | `setupAllSchemas` でプルダウン消失 | DDL 側で `dataValidation` 再設定または毎マート更新で冪等設置 | DDL との整合性 |
| E09 | `boundaryMonthStr` 未確定(`1900-01`) | 既存 L237 の挙動継承 | 既存仕様継承 |
| E10 | 選択肢外の文字列が A1 に書き込まれる | バリデーションで拒否。通過時はデフォルトにフォールバック | 異常入力の安全処理 |
| E11 | B/S スナップで実績月なし | 空白表示 | 既存 72 挙動継承 |
| E12 | `flush()` 重複 | 冪等で安全 | パフォーマンス |
| E13 | プルダウン操作とマート更新同時 | `LockService` 競合で失敗 → 再操作依頼 | 二重書き込み防止 |
| E14 | 統合シート番号が将来予約 | 実装着手時にスキーマ一覧で再確認 | 番号衝突防止 |
| E15 | `handleUnifiedFsEdit_` 内例外 | `try/catch` で `Utils.logError` + alert、`finally` で `releaseLock` | 安全なエラーハンドリング |
## 実データ検証
- V01: 現行の `61_pl_monthly` / `62_pl_ytd` / `71_bs` / `81_cf_indirect` に実データがあることを dev 環境で確認
- V02: プルダウン選択肢文字列の UX ワーディングを確定
- V03: `isActualOnly` の型が boolean であることを 602 L243 で確認済
- V04: `41_trn_budget` に対象月のデータが存在することを確認
- V05: 統合タブ番号 60/70/80 が既存・計画中スキーマと衝突しないことを `grep` で確認
- V06: `LockService.tryLock(3000)` の挙動を手動検証
- V07: `setupAllSchemas` 再実行でプルダウンが消えないことを目視検証
## 動作確認
`npm run push:dev` 後、以下を確認:
1. スプレッドシートをリロードし、`🚀 BizLP` → 操作パネル(サイドバー)→ `📊 マート更新` セクションに「統合タブを初期化」ボタンが表示されること
2. 「統合タブを初期化」クリック → `60_fs_pl_unified` / `70_fs_bs_unified` / `80_fs_cf_unified` の 3 シートが生成され、A1 にプルダウンが設置されていること
3. P/L 統合タブの A1 を `実績・単月` に変更 → 61 タブと同じ内容が表示されること(境界月以降空白、実績のみ)
4. P/L 統合タブの A1 を `計画含む・累積` に変更 → 64 タブと同じ内容が表示されること
5. B/S 統合タブの A1 を `スナップショット(月末時点)` に変更 → 72 タブと同じ内容が表示されること
6. CF 統合タブの A1 を `実績・累積` に変更 → 81b タブと同じ内容が表示されること
7. プルダウン操作中に `buildBudgetTrendDataMart` を実行 → `LockService` 競合で片方がトースト通知してスキップされること
8. `setupAllSchemas` 実行 → 統合タブのプルダウンが消失しないこと
推奨実行モデル
| 工程 | 推奨モデル | 理由 |
|---|---|---|
| 統合シート初期化・プルダウン設置(Step 1) | Claude Sonnet 4.6 | setupAllSchemas 既存パターンへの追加、シート番号衝突確認の判断が必要(Haiku では判断不足) |
| onEdit 分岐追加・LockService 実装(Step 2) | Claude Sonnet 4.6 | 既存コードへの挿入位置特定(L365 の row === 1 回避方針の判断)、LockService パターンの適用 |
| 再描画ロジック全体・マートビルダ統合(Step 3) | Claude Opus 4.6 | buildBudgetTrendDataMart の 3 関数分割リファクタ、実績 ctx / 計画 ctx の isActualOnly + viewMode マトリクス、複数ファイル横断の整合性確保 |
| 動作確認 | ユーザー手動 | GAS エディタでのメニュー操作・プルダウン操作・スプレッドシート目視確認が必要 |
変更履歴
| 日付 | 変更内容 |
|---|---|
| 2026-04-19 | 初版作成 |
仕様書作成プロンプト(再現性・監査性のため必ず記録)
展開して表示
【タイムアウト回避・実行原則(v1.7・必ず遵守すること)】
- 拡張思考の使い分け: Phase 1(設計)では拡張思考をフル活用し、ファイル名・関数名・シート名・行番号・エッジケース一覧・Step 分割粒度を完全に確定させる。Phase 2(清書)の各 Step 内では拡張思考を最小限に抑え、Phase 1 で確定済みの内容の書き下しに徹する。出力途中で再考しない。
- テキスト報告の禁止: 「〜を作成します」等の text のみで tool_use なしに turn を終了しない。説明は 1 文以内。直ちに tool を呼ぶ。
- 4-5 分割の Write/Edit 実行: 仕様書作成は以下の Step に分けて実行する:
- 2-1 骨格 Write(~20 行)
- 2-2 概要〜注意事項 Edit/Bash(~300 行)
- 2-3a エッジケース〜人間検討事項 Edit/Bash(~200 行)
- 2-3b 実装プロンプト〜変更履歴 Edit/Bash(~250 行)
- 2-4
<details>にプロンプト全文記録 Edit/Bash(最重量・必ず独立 Step)
- 各 Step で何を書くかを具体指示: 設計判断を Phase 2 実行時に持ち込まないよう、各 Step の内容を Phase 1 で箇条書きとして完全に確定させてから執筆に入る。
======================================================================
あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者兼仕様書ライターです。
案件 MAS-115「財務諸表タブの統合(プルダウン切替方式)」の開発仕様書を作成してください。
作成後は docs/_config.json の nav 配列の適切なセクションに必ず追記してください。
Phase 1: 実行前タスク(テキスト報告禁止。即座にツール実行)
以下をすべて Read/Grep で調査し、Phase 2 着手前に設計内容を完全に確定させる。
1-A: 案件要件の把握
docs/_internal/TODO_future.md— MAS-115 の案件名・概要・期待効果・人間が検討すべき事項を正確に取得する。統合対象シート(P/L・B/S・CF)と対象外シートの範囲、プルダウン選択肢の候補を把握する。
1-B: プロジェクト規約の確認
CLAUDE.md— ブランチ運用・ファイル番号体系・DDL 管理外タブ一覧(91_fs_bs・92_fs_pl・93_kpi_dashboard等)・コーディング規約(列参照はヘッダー名ベース、列 A のチェックボックス回避など)を確認する。
1-C: 既存財務諸表レポートの実装調査
600_report/ 配下の以下のファイルを Read し、実際の関数名・引数・シート書き込みロジックを確認する(Gemini 指示中の dmBuildPlMonthly_ 等は仮称のため、必ず Read で実在する関数名を裏取りすること):
600_report/601_datamart_ingest.js600_report/602_datamart_pl.js600_report/603_datamart_bs.js600_report/604_datamart_cf.js600_report/605_datamart_kpi.js〜608_datamart_render.js(存在するファイルのみ)- 調査で確認すべき点:
- P/L・B/S・CF のレポートシート名(
61_pl_monthly等)の実際の名称 isActualOnlyフラグが定義・参照されているファイルと実際の型(boolean か文字列か)- 再描画関数の実シグネチャ(引数に表示モードを渡せる構造か否か)
SpreadsheetApp.flush()の既存使用箇所
- P/L・B/S・CF のレポートシート名(
1-D: onEdit トリガーの実装場所確認
300_ui/301_ui_assist.js— 既存onEdit関数の構造・分岐パターン(e.range.getA1Notation()による分岐等)を Read する。MAS-115 用の分岐を追加する場合の挿入位置を確定する。
1-E: システム設定・初期化フローの確認
100_config/101_sys_config.js—onOpen()のメニュー定義(実在するメニュー名の文字列を確認)・setupAllSchemasのシートキー登録ロジック・既存シートの番号帯を確認する。統合シートの番号(6X_帯)が既存と重複しないかを確認する。
1-F: MAS-094 との連携確認
docs/dev/dev_mas-094_boundary_month_selector.md— 基準年月プルダウンの実装方式を把握する。MAS-115 の表示モードプルダウンと同一onEditで共存できる設計を検討する。
1-G: データアクセス層の確認(ハルシネーション防止)
200_data/202_repository.js—BudgetRepositoryが存在するかを Read で確認する。現時点では存在が未確認のため、存在しない場合は41_trn_budgetへの直接アクセスパターン(getDataRange().getValues()+ ヘッダー名ベース参照)を採用する方針を仕様書に明記すること。存在する Repository クラスはOrderRepository・InvoiceRepository・BankTxRepository・JournalRepository・AccountRepositoryであることは確認済み。000_infra/003_contracts.js—BudgetDTOの型定義(フィールド名)を確認する。
1-H: 参考仕様書の読み込み
docs/dev/dev_mas-093_cf_actual_only.mdまたはdocs/dev/dev_mas-001_variance_analysis.md— 財務諸表系仕様書のフォーマット・エッジケース記述の深度を把握する。
【Grep vs Read 原則】 シート名・関数名・定数名・メニュー名はすべて Read で実在を確認してから仕様書に記載する。名前や記憶から「〜だろう」と推測した瞬間に手を止めて Read する。
Phase 2: 仕様書の分割作成
出力先: docs/dev/dev_mas-115_unified_financial_statements.md
(ファイル名は大文字 MAS-115 で統一すること)
Step 2-1: 骨格の作成(Write, ~20 行)
以下の見出しのみ(本文空で可)を Write で出力する:
# MAS-115: 財務諸表タブの統合(プルダウン切替方式)
## 概要
## 目的
## 現在のコード
## 修正方針
## 影響範囲
## 注意事項
## エッジケース
## 実データ検証
## 関連ドキュメント
## 人間が検討すべき事項
## 実装プロンプト(Claude Code 用)
## 推奨実行モデル
## 変更履歴
## 仕様書作成プロンプト(再現性・監査性のため必ず記録)
Step 2-2: 前半セクションの追記(Edit または Bash, ~300 行)
概要〜注意事項 に以下の内容を反映する。Phase 1 で確認した実際の関数名・シート名・行番号のみを記載し、未確認の固有名詞は「要調査」と明示する。
概要テーブル: 案件ID=MAS-115 / カテゴリ=財務諸表 UI 改善 / Phase・優先度・所要時間・対象ファイル(Phase 1 で特定したもの)/ 前提案件=MAS-094
目的: 現状 P/L・B/S・CF が別タブに分散している課題と、プルダウン切替方式による一本化の狙い(1〜3 文)
現在のコード: Phase 1 で特定した既存財務諸表シート名・レポートビルダ関数名・行番号を明記。対象シートが DDL 管理外(CLAUDE.md 記載の動的生成タブ)か否かも記載する。
修正方針(Step 分割形式):
- Step 1: 統合シートの作成と初期化
- 新規シート名と番号は Phase 1 で確認した番号帯の空きに基づいて決定する(番号帯が未確定の場合は「要確認」と仮記載)
A1セルへのデータバリデーション(プルダウン)設置。選択肢文字列は Phase 1 の TODO_future.md 調査結果から決定。なお列 A のチェックボックス規約は データ入力シートに適用される規約であり、財務諸表レポートシートでは適用外であることを注記するsetupAllSchemasまたはonOpen時の初期化処理への組み込み方針(100_config/101_sys_config.jsの既存パターンに準拠)
- Step 2:
onEditトリガーへの分岐追加- 配置先:
300_ui/301_ui_assist.js内の既存onEdit関数(Phase 1 で確認した実際の構造に基づき挿入位置を特定) e.range.getA1Notation()で変更セルを判定し、MAS-094 の基準年月プルダウンと共存できる分岐を設計LockService.getScriptLock()による排他制御:tryLock(3000)でロック取得 → 取得失敗時はUtils.toastResult()通知して中断 →finallyブロックでlock.releaseLock()を確実に実行
- 配置先:
- Step 3: 再描画ロジックの実装
- Phase 1 で確認した既存マートビルダ関数(実関数名を明記)を再利用。表示モードを引数オブジェクトで渡すリファクタリングが必要か否かを Phase 1 調査結果に基づいて判断する
isActualOnlyフラグとの整合性: Phase 1 で確認した実際の型・参照箇所に基づいて記述- 処理中表示:
Utils.toastResult(funcName, '更新中...')→ 完了時に再度Utils.toastResult()で通知 - 大量書き込みがある場合は
SpreadsheetApp.flush()を適切に挿入
影響範囲: 変更ファイルリスト(Phase 1 で確定)・既存財務諸表シートへの参照影響・setupAllSchemas への追加の有無
注意事項(番号付きリスト):
BudgetRepositoryが202_repository.jsに存在しない場合は予算データへの直接アクセス方式(41_trn_budgetをgetDataRange().getValues()+ ヘッダー名ベース参照)を採用すること。Phase 1 調査で存在が確認された場合のみ Repository 経由に変更する- 既存
onEdit分岐ロジックへの追記時は、既存のgetA1Notation()による条件分岐を破壊しないこと - 統合シートが DDL 管理外タブ(動的生成)の場合、
setupAllSchemas実行で上書きされないよう注意 - CLAUDE.md コーディング規約: 列参照はヘッダー名ベース(
indexOf)、列番号ハードコード禁止、有効フラグ=FALSE の行スキップを遵守 - 移行期間中は旧タブと統合タブを並行稼働させるため、旧タブの既存レポートビルダ関数への影響が出ないようにすること
Step 2-3a: エッジケース〜人間検討事項の追記(Edit または Bash, ~200 行)
エッジケーステーブル(テーブル形式: 条件 | 表示値/挙動 | 理由):
| 条件 | 表示値/挙動 | 理由 |
|---|---|---|
| プルダウン未選択時 | デフォルトモード(実績単月等)で描画 | 初回アクセス時の UX |
| 仕訳・予算データが空 | エラーなく空の表を描画(列ヘッダー維持) | データなし時の安全な描画 |
| P/L 比率計算で売上=0(ゼロ除算) | "-" 表示(failure_patterns.md #2 に準拠) | ゼロ除算のフォールバック |
onEdit の多重実行 | LockService でスキップ、Utils.toastResult() 通知 | パフォーマンス保護 |
MAS-094 プルダウンと MAS-115 プルダウンが同一 onEdit で発火 | e.range.getA1Notation() で分岐し、両方の値を参照して再描画 | MAS-094 との共存 |
| 既存マートビルダ関数のリファクタリング中に旧関数呼び出しが残存 | デプロイ前に grep で旧関数名の全参照を確認(failure_patterns.md #7 に準拠) | 分割時の未定義関数防止 |
実データ検証(実装前に MCP 等で確認すべき項目):
- 現行の
61_pl_monthly等に実データがある状態で既存表示が統合後も壊れないこと - プルダウン選択肢の文字列(DDL 定義のコード値 vs シートに実際に格納されている値の乖離)
isActualOnlyの実際の格納型(boolean か文字列 "TRUE"/"FALSE" か)41_trn_budgetに対象月のデータが存在するか(予算ゼロ月の動作確認)
関連ドキュメントテーブル(仕様書リンク | 関連箇所):
- MAS-094 仕様書 / 基準年月プルダウンの
onEdit実装 dev_mas-093_cf_actual_only.md/isActualOnly分岐ロジックdocs/_internal/failure_patterns.md#2 / ゼロ除算フォールバックdocs/_internal/failure_patterns.md#7 / ファイル分割時の旧関数残存
人間が検討すべき事項(TODO_future.md から転記 + 以下の追加事項):
- プルダウン選択肢の確定(表示モード名・選択肢数は要件定義が必要)
- 統合シートの番号帯・シート名の最終確定(既存番号帯との重複確認)
BudgetRepositoryの設計判断(新規 Repository クラス作成 vs41_trn_budget直接アクセス)- 移行計画: 新旧タブの並行稼働期間(推奨 1 ヶ月)・旧タブの段階的廃止ステップ(旧タブ上部に移行案内 → 非表示化 → 安定確認後削除)
- 旧タブの廃止タイミングで関連する既存テストケース(
901_test_runner.js)の更新要否
Step 2-3b: 実装プロンプト〜変更履歴の追記(Edit または Bash, ~250 行)
【必須フォーマット規則】実装プロンプトはバッククォートのコードブロックで囲まず、全行を行頭 4 スペースインデントで出力すること。
実装プロンプトには以下を自己完結形式で含める:
あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者です。
案件 MAS-115「財務諸表タブの統合(プルダウン切替方式)」を実装してください。
## 実行前タスク
(Phase 1 で列挙したファイルリストと、各ファイルで確認すべきポイントを明記)
## 修正対象ファイル
(Phase 1 で確定したファイル名のみ。「のみ」または「への追記」を明示)
## 実装内容
(Step 1〜3 の具体的な変更手順。関数名・行番号は Phase 1 で裏取り済みのもののみ記載)
## 制約
- 既存 onEdit 分岐ロジックを破壊しないこと
- 列番号のハードコード禁止(ヘッダー名 indexOf で参照)
- BudgetRepository が未存在の場合は 41_trn_budget を直接参照する方式を採用
- 動作未確認のコードを GitHub に push しない(CLAUDE.md 規約)
- 実在しないメニュー名・関数名を動作確認手順に記載しないこと
## エッジケース
(Step 2-3a のテーブルを転記)
## 実データ検証
(Step 2-3a の実データ検証項目を転記)
## 動作確認
(npm run push:dev 後の検証手順。メニュー名は Phase 1 で onOpen を Read して確認した実在の文字列のみ使用)
推奨実行モデルテーブル(工程 | 推奨モデル | 理由):
| 工程 | 推奨モデル | 理由 |
|---|---|---|
| 統合シート初期化・プルダウン設置(Step 1) | Claude Haiku 4.5 | コードが完全定義済み・判断要素なし |
| onEdit 分岐追加・LockService 実装(Step 2) | Claude Sonnet 4.6 | 既存コードへの挿入位置の特定が必要 |
| 再描画ロジック全体・マートビルダ統合(Step 3) | Claude Opus 4.6 | 複数ファイル横断・会計ロジック理解が必要 |
変更履歴テーブル: | 2026-04-19 | 初版作成 |
Step 2-4: 仕様書作成プロンプトの記録(Edit または Bash, 最重量・必ず独立 Step)
仕様書末尾の ## 仕様書作成プロンプト(再現性・監査性のため必ず記録) セクションに、この <instruction> 全文を以下の形式で追記する:
<details><summary>展開して表示</summary>
(この instruction 全文をそのまま貼り付ける)
</details>
Phase 3: 後処理(テキスト報告禁止。即座にツール実行)
3-A: _config.json への登録(必須)
docs/_config.json の §E.4 データマート・財務諸表 セクションに以下を追加し、追加後に JSON 構文が正しいことを確認する:
{ "file": "dev/dev_mas-115_unified_financial_statements.md", "title": "E.4.X S-43 財務諸表タブの統合(プルダウン切替方式)" }
(E.4.X の連番は既存エントリを確認して採番すること)
3-B: changelog への追記
docs/_internal/changelog.md のヘッダー直後に以下を追記する:
| 2026-04-19 | [dev_mas-115_unified_financial_statements.md](dev_mas-115_unified_financial_statements.md) | 初版作成。財務諸表タブ統合(プルダウン切替方式)の開発仕様書 |
3-C: コミット&プッシュ
git add docs/dev/dev_mas-115_unified_financial_statements.md docs/_internal/changelog.md docs/_config.json
git commit -m "docs: S-43 財務諸表タブの統合(プルダウン切替方式)の開発仕様書を作成
P/L・B/S・CFタブをプルダウン切替方式で統合する仕様書の初版。
onEditトリガー・LockService排他制御・S-22連携・BudgetRepository要調査事項を設計。
https://claude.ai/code/session_XXXXX"
git push -u origin {現在のブランチ}