最終更新: 2026/06/22 18:56
MAS-093: CFタブの実績専用モード対応
概要
| 項目 | 内容 |
|---|---|
| 案件ID | MAS-093 |
| カテゴリ | データマート |
| Phase | P1 |
| 優先度 | ★★★ |
| 所要時間 | 30分 |
| 対象ファイル | 600_report/605_datamart_cf.js |
| 前提案件 | なし(パターン確立済み) |
目的
P/L(61/62)・B/S(71)で確立済みの isActualOnly パターンを CF タブ(81/81b)に適用し、実績専用タブの出力を一貫させる。
現状、dmBuildCfOutput_() は ctx.isActualOnly を一切参照しておらず、実績タブ(81/81b)でも計画月のデータが表示され、ヘッダーに「(実績)/(予算)」ラベルが付いた状態で出力されている。
現在のコード(605_datamart_cf.js)
問題箇所1: ヘッダーが常に計画用ラベル付き(行54-57)
const targetMonthsWithActBgt = ctx.targetMonthsWithActBgt;
const cfHeaders = ['C/F項目 (間接法キャッシュフロー)', '選択月スナップ', '',
'通期(Total)', ...targetMonthsWithActBgt]; // ← 常にラベル付き
問題箇所2: 値のフィルタリングなし(行59-78)
buildCfRow() がデータ配列をそのまま出力しており、境界月以降の計画値が実績タブにも表示される。
問題箇所3: YTDも同様(行81-101)
buildCfYtdRow() も targetMonthsWithActBgt を使用し、フィルタリングなし。
既存パターンの分析
P/L パターン(603_datamart_pl.js L204-233)
const isActualOnly = ctx.isActualOnly || false;
// 値フィルタ: 境界月以降を空白に(Total維持)
const filterValues = function(arr) {
if (!isActualOnly) return arr;
var res = [arr[0]];
for (var i = 0; i < 12; i++) {
res.push(targetMonths[i] >= boundaryMonthStr ? '' : arr[i + 1]);
}
return res;
};
// Total再計算フィルタ: 実績月のみでTotalを再算出
const filterWithRecalcTotal = function(arr) {
if (!isActualOnly) return arr;
var res = filterValues(arr);
var total = 0;
for (var i = 1; i <= 12; i++) {
if (res[i] !== '') total += Number(res[i]) || 0;
}
res[0] = total;
return res;
};
// ヘッダー切り替え
const headerMonths = isActualOnly ? targetMonths : targetMonthsWithActBgt;
CFへの適用方針
CF項目にはフロー(取引額)とストック(残高)の2種類があり、フィルタ方式が異なる。
| CF項目 | 性質 | フィルタ方式 | 理由 |
|---|---|---|---|
| 税引前当期純利益 | フロー | filterWithRecalcTotal | P/Lと同じ。Totalは実績月のみで再計算 |
| 減価償却費 | フロー | filterWithRecalcTotal | 同上 |
| 流動資産増減 | フロー | filterWithRecalcTotal | 同上 |
| 流動負債増減 | フロー | filterWithRecalcTotal | 同上 |
| 法人税等支払額 | フロー | filterWithRecalcTotal | 同上 |
| 営業CF小計 | フロー | filterWithRecalcTotal | 同上 |
| 投資CF | フロー | filterWithRecalcTotal | 同上 |
| 財務CF | フロー | filterWithRecalcTotal | 同上 |
| 現金増減額 | フロー | filterWithRecalcTotal | 同上 |
| 期首残高 | ストック | filterValues | 残高は合算不可。Totalはそのまま維持 |
| 期末現金残高 | ストック | filterValues | 同上 |
| YTD全行 | 累積 | filterValues | P/Lと同じ。YTD変換後にフィルタ |
修正方針
dmBuildCfOutput_() に以下の3つの変更を加える。P/L の実装パターンをそのまま踏襲する。
変更1: isActualOnly の読み取りとフィルタ関数の定義
関数冒頭(行42-54付近)に追加:
const targetMonths = ctx.targetMonths;
const boundaryMonthStr = ctx.boundaryMonthStr;
const isActualOnly = ctx.isActualOnly || false;
const filterValues = function(arr) {
if (!isActualOnly) return arr;
var res = [arr[0]];
for (var i = 0; i < 12; i++) {
res.push(targetMonths[i] >= boundaryMonthStr ? '' : arr[i + 1]);
}
return res;
};
const filterWithRecalcTotal = function(arr) {
if (!isActualOnly) return arr;
var res = filterValues(arr);
var total = 0;
for (var i = 1; i <= 12; i++) {
if (res[i] !== '') total += Number(res[i]) || 0;
}
res[0] = total;
return res;
};
変更2: ヘッダーの切り替え
const headerMonths = isActualOnly ? targetMonths : targetMonthsWithActBgt;
cfHeaders と cfHeadersY で targetMonthsWithActBgt → headerMonths に置換。
変更3: 各行へのフィルタ適用
単月(outC): buildCfRow() のデータ引数にフィルタを適用
// フロー項目: filterWithRecalcTotal
buildCfRow(' 税引前当期純利益', filterWithRecalcTotal(sectionTotalsPl['pre_tax_profit']), 'account');
// ... 他のフロー項目も同様
// ストック項目: filterValues
buildCfRow(' + 期首残高 (前月末の現預金)', filterValues(cashStart), 'subtotal');
buildCfRow('🏦 期末現金残高 (B/S現預金と完全一致)', filterValues(cashEnd), 'cash_plug');
YTD(outCY): buildCfYtdRow() のデータ引数にフィルタを適用
// YTD行: filterValues(dmToYtdArray_変換後)
buildCfYtdRow(' 税引前当期純利益', filterValues(dmToYtdArray_(sectionTotalsPl['pre_tax_profit'])), 'account');
// ... 他の行も同様
// cashEnd行(YTD): filterValues
pushCfY(['🏦 期末現金残高 (B/S現預金と完全一致)', ...filterValues(cashEnd)], 'cash_plug');
影響範囲
- 変更ファイル:
605_datamart_cf.jsのdmBuildCfOutput_()のみ - 呼び出し元:
602_datamart_main.jsの行239・271 — 変更不要(既にctx.isActualOnlyを設定済み) - 出力先タブ: 81(実績→フィルタ有効)、81b(実績YTD→フィルタ有効)、82/82b(計画→フィルタ無効=変化なし)
- 他タブへの影響: なし
注意事項
buildCfRow()はスプレッドシートのINDEX/MATCH数式を含む「選択月スナップ」列を動的に生成している。フィルタで空白になった月が選択された場合、数式は0を返す(既存の P/L と同じ挙動で問題なし)buildCfYtdRow()は内部でdmToYtdArray_()を呼ぶ。フィルタは YTD 変換後に適用すること(P/L と同じ順序)- 計画タブ(82/82b)は
isActualOnly=falseで呼ばれるため、フィルタは素通り。既存の動作に影響なし
関連ドキュメント
| 仕様書 | 関連箇所 |
|---|---|
| B.3 統合テスト手順 | マート更新テスト(P/L・B/S・CF) |
| CLAUDE.md | 変更時の動作確認テスト: 600_report/6*_datamart_*.js → マート更新テスト |
人間が検討すべき事項
なし(P/L・B/Sで確立済みのパターンの適用。即実装可)
実装プロンプト(Claude Code 用)
あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者です。
CLIエージェントである「Claude Code」として、以下の指示に従い、案件 S-21「CFタブの実績専用モード対応」を実装してください。
## 実行前タスク(コンテキストの読み込み)
実装前に**必ず**以下のファイルを読み込んで、既存パターンと現在の実装を正確に把握してください。
1. P/Lの実装パターン(参考実装): `600_report/603_datamart_pl.js` — `dmBuildPlOutput_()` 内の `filterValues`, `filterWithRecalcTotal`, `headerMonths` の実装を確認
2. 修正対象: `600_report/605_datamart_cf.js` — `dmBuildCfOutput_()` の全体構造を把握
3. 呼び出し元(変更不要、参照のみ): `600_report/602_datamart_main.js` — 行234の `ctx.isActualOnly = true` と行254の `isActualOnly: false` で実績/計画を切り替えていることを確認
4. プロジェクト規約: `CLAUDE.md`
5. 開発仕様書: `docs/dev/dev_mas-093_cf_actual_only.md`
## 修正対象ファイル
`600_report/605_datamart_cf.js` の `dmBuildCfOutput_()` のみ。他ファイルの変更は不要。
## 実装内容
### 1. フィルタ関数の追加
`dmBuildCfOutput_()` の冒頭(`const uiHeader = ctx.uiHeader;` の後)に以下を追加:
- `ctx.targetMonths`, `ctx.boundaryMonthStr`, `ctx.isActualOnly` の読み取り
- `filterValues()` — 境界月以降の値を空白にする(Total維持)
- `filterWithRecalcTotal()` — filterValues + Total再計算
- P/L (`603_datamart_pl.js` L207-230) と**同一のロジック**にすること
### 2. ヘッダーの切り替え
`const headerMonths = isActualOnly ? targetMonths : targetMonthsWithActBgt;` を追加し、`cfHeaders` と `cfHeadersY` の月名展開を `headerMonths` に変更。
### 3. 単月(outC)へのフィルタ適用
`buildCfRow()` で出力するデータ配列にフィルタを適用:
- **フロー項目**(税引前利益, 減価償却, 流動資産増減, 流動負債増減, 法人税等, 営業CF小計, 投資CF, 財務CF, 現金増減額): `filterWithRecalcTotal()` でTotal再計算付きフィルタ
- **ストック項目**(期首残高, 期末現金残高): `filterValues()` でTotal維持フィルタ
### 4. YTD(outCY)へのフィルタ適用
`buildCfYtdRow()` で出力するデータ配列にフィルタを適用:
- 全行: `filterValues()` を適用(P/Lの YTD と同じパターン)
- **重要**: `dmToYtdArray_()` で YTD変換した**後**にフィルタを適用する順序を厳守
- cashEnd行(pushCfY直接)にも filterValues を適用
## 制約
- `602_datamart_main.js` は変更しない(既に `ctx.isActualOnly` を正しく設定済み)
- 計画タブ(82/82b)は `isActualOnly=false` で呼ばれるため、フィルタは素通りする。計画タブの出力が変化しないことを確認
- `buildCfRow()` 内の INDEX/MATCH 数式は変更しない
- 既存のコードスタイル(アロー関数、テンプレートリテラル等)を維持
## 動作確認
実装後、`npm run push:dev` で開発環境にデプロイし、以下を確認:
1. GASエディタで `updateAllDatamarts()` を実行
2. 81タブ: 境界月以降が空白になっていること。ヘッダーに「(実績)/(予算)」ラベルがないこと
3. 81bタブ: 同上(YTD)
4. 82タブ: 従来通り全月にデータが表示され、ラベル付きヘッダーであること(変化なし)
5. 82bタブ: 同上
### 拡張思考の使用状況
| フェーズ | 拡張思考 | 備考 |
|---------|:--------:|------|
| ファイル読み込み・パターン理解 | なし | P/Lのパターンを読み取るだけ |
| filterValues/filterWithRecalcTotal 実装 | なし | P/Lからの移植。ロジック確立済み |
| フロー/ストック判定とフィルタ適用 | あり | cashStart/cashEnd はストックのため filterValues。他はフロー。この判定に注意 |
| YTD フィルタ適用順序の確認 | なし | dmToYtdArray_ → filterValues の順序はP/Lと同一 |
推奨実行モデル
| 工程 | 推奨モデル | 理由 |
|---|---|---|
| 仕様書作成(本ドキュメント) | Claude Opus 4.6 | P/L・B/S・CFの3パターン横断比較と、フロー/ストックの区別判断に高い推論力が必要 |
| 実装 | Claude Sonnet 4.6 | 仕様書でフィルタ適用先が明確に定義済み。P/Lからの移植が中心のため速度優先 |
| 動作確認レビュー | ユーザー手動 | GASエディタでの実行とスプレッドシート目視確認が必要。CLIでは検証不可 |
変更履歴
| 日付 | 変更内容 |
|---|---|
| 2026-04-14 | 初版作成 |