概要
| 項目 | 内容 |
|---|
| 案件ID | MAS-094 |
| カテゴリ | データマート |
| Phase | P2 |
| 優先度 | ★★ |
| 所要時間 | 2時間 |
| 対象ファイル | 600_report/602_datamart_main.js, 100_config/101_sys_config.js(メニュー追加) |
| 前提案件 | なし(独立着手可能) |
目的
マート更新時に実績集計の基準年月(境界月)をユーザーが選択できるようにし、月次締め前の仮集計や過去月の実績確認を可能にする。現在は boundaryMonthStr が自動計算(max実績月の翌月)で固定されており、任意月での実績確認ができない。
現在のコード
boundaryMonthStr の自動計算(601_datamart_ingest.js L251-256)
const today = new Date();
const currentYmStr = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}`;
let boundaryMonthStr = hasInvData && maxInvYm !== '1900-01'
? Utils.addMonths(maxInvYm, 1) : currentYmStr;
if (boundaryMonthStr > currentYmStr) boundaryMonthStr = currentYmStr;
buildBudgetTrendDataMart の関数シグネチャ(602_datamart_main.js L157)
function buildBudgetTrendDataMart() { // ← 引数なし
boundaryMonthStr の上書き挿入ポイント(602_datamart_main.js L211)
dmIngestData_(ctx, sheetInv, sheetBank, sheetAcct);
// ★ この直後に overrideBoundary の反映を挿入する
// 1b. 32タブの諸表区分・表示区分を科目マスタから同期
dmSyncInvClassification_(ctx.acctMap, sheetInv);
boundaryMonthStr の使用箇所
| ファイル | 行 | 用途 |
|---|
602_datamart_main.js | L229-234 | uiHeader の「実績反映: YYYY-MM まで」表示、targetMonthsWithActBgt 生成 |
602_datamart_main.js | L252 | 計画パイプラインの planCtx への引き継ぎ |
603_datamart_pl.js | 各所 | isActualOnly 時の filterValues / filterWithRecalcTotal |
604_datamart_bs.js | 各所 | B/S の filterBsValues |
605_datamart_cf.js | 各所 | C/F の filterValues |
既存のメニュー構造(101_sys_config.js L328-331)
ui.createMenu('📊 マート更新')
.addItem('財務3表(P/L・B/S・C/F)の更新', 'buildBudgetTrendDataMart')
.addItem('プロジェクト別 採算(限界利益)の生成', 'buildProjectProfitability')
.addToUi();
修正方針
実装方式: ダイアログ方式
マート更新メニューに「基準年月を指定して更新」を追加。実行時にダイアログで基準月を入力し、buildBudgetTrendDataMart にオプション引数として渡す。
この方式を選んだ理由:
| 方式 | 評価 | 理由 |
|---|
| 03_sys_params パラメータ | △ | 毎回シートを開いて値を変更する手間。戻し忘れリスク |
| ダイアログ (prompt) | ◎ | 1回のメニュー操作で完結。一時的な上書きで戻し忘れなし |
| 実績タブにプルダウン | △ | プルダウン値変更後に再計算が必要。トリガーの仕組みが複雑 |
Step 1: buildBudgetTrendDataMart() への引数追加
602_datamart_main.js L157 の関数シグネチャにオプション引数を追加し、L211(dmIngestData_ 直後)に上書きロジックを挿入する。
// L157: 関数シグネチャ変更
function buildBudgetTrendDataMart(overrideBoundary) {
// L211-214: dmIngestData_ 直後に挿入
dmIngestData_(ctx, sheetInv, sheetBank, sheetAcct);
// S-22: 基準年月の手動上書き
if (overrideBoundary) {
ctx.boundaryMonthStr = overrideBoundary;
Utils.logInfo(FUNC, '基準年月を手動指定: ' + overrideBoundary);
}
// 1b. 32タブの諸表区分・表示区分を科目マスタから同期
dmSyncInvClassification_(ctx.acctMap, sheetInv);
Step 2: buildDataMartWithCustomBoundary() ラッパー関数の追加
602_datamart_main.js の末尾(buildBudgetTrendDataMart 関数の外側)に追加。
/**
* S-22: 基準年月を指定して財務3表を更新する
* ユーザーに基準年月をpromptで入力させ、buildBudgetTrendDataMart に渡す
*/
function buildDataMartWithCustomBoundary() {
var ui = SpreadsheetApp.getUi();
// 選択肢の構築: 今期の12ヶ月
var today = new Date();
var startYear = (today.getMonth() + 1 >= 8) ? today.getFullYear() : today.getFullYear() - 1;
var choices = [];
for (var i = 0; i < 12; i++) {
var d = new Date(startYear, 7 + i, 1);
choices.push(d.getFullYear() + '-' + String(d.getMonth() + 1).padStart(2, '0'));
}
var result = ui.prompt(
'📅 基準年月の選択',
'実績集計の末月を指定してください。\n'
+ '指定月までが実績、翌月以降が予算として表示されます。\n\n'
+ '選択肢:\n' + choices.join('\n') + '\n\n'
+ '入力例: 2025-12\n(空欄で自動計算)',
ui.ButtonSet.OK_CANCEL
);
if (result.getSelectedButton() !== ui.Button.OK) return;
var input = result.getResponseText().trim();
if (!input) {
buildBudgetTrendDataMart(); // 通常実行
} else {
var ym = Utils.parseDateToYm(input);
if (!ym) {
ui.alert('⚠️ 入力形式エラー', '「YYYY-MM」形式で入力してください。', ui.ButtonSet.OK);
return;
}
// 入力月 = 実績最終月 → 翌月を境界月として設定
buildBudgetTrendDataMart(Utils.addMonths(ym, 1));
}
}
Step 3: メニューへの追加
101_sys_config.js L328-331 のメニュー構築に1行追加。
ui.createMenu('📊 マート更新')
.addItem('財務3表(P/L・B/S・C/F)の更新', 'buildBudgetTrendDataMart')
.addItem('📅 基準年月を指定して更新', 'buildDataMartWithCustomBoundary') // S-22
.addItem('プロジェクト別 採算(限界利益)の生成', 'buildProjectProfitability')
.addToUi();
影響範囲
| 変更対象 | 変更内容 | 変更量 |
|---|
600_report/602_datamart_main.js | 引数追加(1行) + 上書きロジック(4行) + ラッパー関数(~35行) | ~40行 |
100_config/101_sys_config.js | メニュー項目1行追加 | 1行 |
- 既存動作への影響:
buildBudgetTrendDataMart() を引数なしで呼んだ場合は完全に従来通り。引数はオプション
- 下流への波及:
ctx.boundaryMonthStr の上書きにより、filterValues / filterWithRecalcTotal / uiHeader / targetMonthsWithActBgt が全て連動して変わる。603/604/605 の変更は不要
注意事項
- 上書きは一時的: ダイアログで指定した基準月は今回の実行にのみ適用。次回の通常実行(引数なし)では自動計算に戻る
- 未来月の制限:
dmIngestData_ 内の if (boundaryMonthStr > currentYmStr) クランプは維持。上書きはその後なので、未来月を指定しても実績データが存在しない月が含まれうる。ただしデータがない月は金額0になるだけで壊れない
- 過去月の指定: 問題なし。指定月以降は予算扱いになるだけ
- 計画タブ(63/64):
planCtx.boundaryMonthStr = ctx.boundaryMonthStr(L252)で計画にも波及する。意図通りの動作
- 上書き挿入位置:
dmIngestData_ の直後かつ dmSyncInvClassification_ の前に挿入。dmIngestData_ で自動計算された boundaryMonthStr を上書きする
エッジケース
| 条件 | 動作 | 理由 |
|---|
| 空欄で OK | 引数なしで通常実行(自動計算) | ユーザーが「やっぱりデフォルトで」と思った場合の安全パス |
| キャンセル | 何も実行しない | ユーザー中断 |
| 不正な入力(例: "abc") | Utils.parseDateToYm が空文字を返す → エラーダイアログ | パースエラーの安全な処理 |
| 今期外の月(例: "2024-01") | 正常に実行される。ただし当期の12ヶ月すべてが予算扱いになる | boundaryMonthStr が期首より前になるだけ。壊れはしないが意味のある集計にならない |
| 未来月の指定(例: "2026-08") | 翌月(2026-09)が境界月になる。601の自動計算より先の月を指定可能 | 実績データがない月は金額0。P/L・B/S・CFの数値は0になるだけで構造的に壊れない |
| 期首月を指定(例: "2025-08") | 8月のみ実績、9月以降予算 | 正常動作。最小の実績範囲 |
実データ検証(MCP でのデータ確認が必要な場合)
| 確認項目 | 確認方法 | 理由 |
|---|
現在の boundaryMonthStr 自動計算値 | マート更新を実行し、61タブのヘッダー「実績反映: YYYY-MM まで」を確認 | 上書き前のデフォルト値を把握し、テスト時の期待値を決定するため |
| 実績データの存在月範囲 | 32_wrk_invoice の 発生日(P/L計上日) のmin/max を確認 | 指定可能な実績月の範囲を把握 |
関連ドキュメント
人間が検討すべき事項
- プルダウンの配置場所: 本仕様ではダイアログ方式を採用(TODO_future.md から転記)
- 未来月の指定を許可するか: 技術的には可能だが、実績データがない月は金額0になる。初版では制限せず、ユーザー判断に委ねる
実装プロンプト(Claude Code 用)
あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者です。
案件 MAS-094「実績集計の基準年月プルダウン選択」を実装してください。
## 実行前タスク
以下のファイルを読み込んでください:
1. `600_report/602_datamart_main.js` — `buildBudgetTrendDataMart()` の全体構造(L157)。`dmIngestData_()` 直後(L211)での ctx.boundaryMonthStr 設定箇所を確認
2. `600_report/601_datamart_ingest.js` — L251-256 の boundaryMonthStr 自動計算ロジック(変更しない。参照のみ)
3. `100_config/101_sys_config.js` — L328-331 の「📊 マート更新」メニュー構築箇所
4. `000_infra/004_utils.js` — `Utils.parseDateToYm()`(L92)と `Utils.addMonths()`(L127)のシグネチャ
5. `CLAUDE.md`
6. `docs/dev/dev_mas-094_boundary_month_selector.md`
## 修正対象ファイル
- `600_report/602_datamart_main.js` — 引数追加 + ラッパー関数追加
- `100_config/101_sys_config.js` — メニュー項目追加
## 実装内容
### A: buildBudgetTrendDataMart() への引数追加(602_datamart_main.js L157)
関数シグネチャを `function buildBudgetTrendDataMart(overrideBoundary)` に変更。
L211(`dmIngestData_(ctx, ...)` の直後、`dmSyncInvClassification_` の前)に以下を挿入:
```js
// S-22: 基準年月の手動上書き
if (overrideBoundary) {
ctx.boundaryMonthStr = overrideBoundary;
Utils.logInfo(FUNC, '基準年月を手動指定: ' + overrideBoundary);
}
```
### B: buildDataMartWithCustomBoundary() ラッパー関数の追加(602_datamart_main.js 末尾)
- `SpreadsheetApp.getUi().prompt()` でユーザーに基準年月を入力させる
- 今期の12ヶ月を選択肢として表示
- 入力値を `Utils.parseDateToYm()` でパースし、`Utils.addMonths(ym, 1)` で翌月を境界月に変換
- 空欄なら引数なしで通常実行、キャンセルなら何もしない
### C: メニュー追加(101_sys_config.js L328-331)
「📊 マート更新」メニューの「財務3表の更新」と「プロジェクト別」の間に追加:
`.addItem('📅 基準年月を指定して更新', 'buildDataMartWithCustomBoundary')`
## 制約
- 既存の `buildBudgetTrendDataMart()` を引数なしで呼んだ場合の動作は一切変えない
- `dmIngestData_()` 内の boundaryMonthStr 自動計算ロジック(601_datamart_ingest.js)は変更しない
- 603/604/605 のファイルは変更しない
## エッジケース
| 条件 | 動作 |
|------|------|
| 空欄で OK | 引数なしで通常実行 |
| キャンセル | 何も実行しない |
| 不正な入力 | エラーダイアログ表示 |
| 今期外の月 | 正常実行(全月が予算扱い) |
## 動作確認
`npm run push:dev` 後:
1. スプレッドシートをリロードし、「📊 マート更新」メニューに「📅 基準年月を指定して更新」が表示されること
2. メニュークリック → ダイアログが表示され、今期12ヶ月の選択肢が表示されること
3. 「2025-12」と入力 → OK → マート更新実行。61タブで2025-12月までが「(実績)」、2026-01以降が「(予算)」であること
4. 空欄で OK → 通常の自動計算と同じ結果になること
5. キャンセル → 何も実行されないこと
6. 「abc」と入力 → エラーダイアログが表示されること
### 拡張思考の使用状況
| フェーズ | 拡張思考 | 備考 |
|---------|:--------:|------|
| buildBudgetTrendDataMart 引数追加 | なし | 1行のシグネチャ変更 + 4行の条件分岐追加 |
| ラッパー関数実装 | なし | prompt → parseDateToYm → addMonths の単純なフロー |
| メニュー追加 | なし | addItem 1行追加 |
推奨実行モデル
| 工程 | 推奨モデル | 理由 |
|---|
| 仕様書作成(本ドキュメント) | Claude Opus 4.6 | boundaryMonthStr の全使用箇所の影響分析、実装方式の比較判断に高い推論力が必要 |
| 実装 | Claude Haiku 4.5 | 仕様書でコードが完全に定義済み。引数追加 + ラッパー関数 + メニュー1行の機械的作業 |
| 動作確認 | ユーザー手動 | GASエディタでのメニュー操作とダイアログ入力が必要 |
変更履歴
| 日付 | 変更内容 |
|---|
| 2026-04-14 | 初版作成 |
| 2026-04-16 | テンプレート準拠で全面改訂。エッジケース・実データ検証セクション追加、行番号を最新コードに更新、実装プロンプトを4字インデントに変更 |