概要

項目内容
案件IDMAS-093
カテゴリデータマート
PhaseP1
優先度★★★
所要時間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項目性質フィルタ方式理由
税引前当期純利益フローfilterWithRecalcTotalP/Lと同じ。Totalは実績月のみで再計算
減価償却費フローfilterWithRecalcTotal同上
流動資産増減フローfilterWithRecalcTotal同上
流動負債増減フローfilterWithRecalcTotal同上
法人税等支払額フローfilterWithRecalcTotal同上
営業CF小計フローfilterWithRecalcTotal同上
投資CFフローfilterWithRecalcTotal同上
財務CFフローfilterWithRecalcTotal同上
現金増減額フローfilterWithRecalcTotal同上
期首残高ストックfilterValues残高は合算不可。Totalはそのまま維持
期末現金残高ストックfilterValues同上
YTD全行累積filterValuesP/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;

cfHeaderscfHeadersYtargetMonthsWithActBgtheaderMonths に置換。

変更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.jsdmBuildCfOutput_() のみ
  • 呼び出し元: 602_datamart_main.js の行239・271 — 変更不要(既に ctx.isActualOnly を設定済み)
  • 出力先タブ: 81(実績→フィルタ有効)、81b(実績YTD→フィルタ有効)、82/82b(計画→フィルタ無効=変化なし)
  • 他タブへの影響: なし

注意事項

  1. buildCfRow() はスプレッドシートの INDEX/MATCH 数式を含む「選択月スナップ」列を動的に生成している。フィルタで空白になった月が選択された場合、数式は 0 を返す(既存の P/L と同じ挙動で問題なし)
  2. buildCfYtdRow() は内部で dmToYtdArray_() を呼ぶ。フィルタは YTD 変換に適用すること(P/L と同じ順序)
  3. 計画タブ(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.6P/L・B/S・CFの3パターン横断比較と、フロー/ストックの区別判断に高い推論力が必要
実装Claude Sonnet 4.6仕様書でフィルタ適用先が明確に定義済み。P/Lからの移植が中心のため速度優先
動作確認レビューユーザー手動GASエディタでの実行とスプレッドシート目視確認が必要。CLIでは検証不可

変更履歴

日付変更内容
2026-04-14初版作成