概要

項目内容
案件IDMAS-024
カテゴリFP&A
PhaseP2
優先度★★★
所要時間1時間
対象ファイル600_report/601_datamart_ingest.js(acctMap拡張)
600_report/603_datamart_pl.jsBEP計算+出力行追加)
後続案件MAS-003(KPIダッシュボード)の基盤

目的

科目マスタの固変区分(固定費/変動費)を活用し、損益分岐点(BEP)売上高を月次・通期で自動算出してP/Lタブに表示する。

BEP計算式

BEP売上高 = 固定費 / (1 - 変動費率)
変動費率 = 変動費 / 売上高

現在のコード

科目マスタの固変区分(101_sys_config.js L451, L794)

DDL定義済み。MST_ACCT ヘッダーの第8列。

// ヘッダー定義
headers: [..., "固変区分", "デフォルト税率", "科目名", "説明"]

// プルダウン値(15_mst_dict)
['固変区分', [['FV_NA','対象外'], ['FV_VAR','変動費'], ['FV_FIX','固定費']]]

acctMap の現在の構造(601_datamart_ingest.js L21付近)

acctMap[inputName] = {
  stmt: String(acctData[i][2]).trim(),  // 諸表区分 (P/L or B/S)
  cat:  String(acctData[i][3]).trim(),  // 大分類
  disp: String(acctData[i][4]).trim()   // 表示区分
  // ★ 固変区分が未含有
};

martPl の構造

martPl[sectionId][科目名] = [Total, M1, M2, ..., M12]  // 13要素配列
sectionTotalsPl[sectionId] = [Total, M1, M2, ..., M12]

修正方針

Step 1: acctMap に固変区分を追加

601_datamart_ingest.js の acctMap 構築箇所で、第8列(固変区分)を追加。

acctMap[inputName] = {
  stmt: String(acctData[i][2]).trim(),
  cat:  String(acctData[i][3]).trim(),
  disp: String(acctData[i][4]).trim(),
  fvCat: String(acctData[i][7]).trim()  // S-24追加: FV_NA / FV_VAR / FV_FIX
};

Step 2: BEP計算と出力行追加

603_datamart_pl.jsdmBuildPlOutput_() の末尾(return直前)に、BEPサマリー行を追加。

// F-24: 損益分岐点(BEP)サマリー
var acctMap = ctx.acctMap;
var fixedCost = Array(13).fill(0);   // 固定費合計
var variableCost = Array(13).fill(0); // 変動費合計
var salesTotal = sectionTotalsPl['sales'] || Array(13).fill(0);

// 費用セクション(cogs, sga, buffer)から固変分類集計
['cogs', 'sga', 'buffer'].forEach(function(secId) {
  var secAccts = martPl[secId] || {};
  Object.keys(secAccts).forEach(function(accName) {
    var fv = acctMap[accName] ? acctMap[accName].fvCat : '';
    var arr = secAccts[accName];
    for (var i = 0; i < 13; i++) {
      var amt = Math.abs(Number(arr[i]) || 0);
      if (fv === 'FV_FIX') fixedCost[i] += amt;
      else if (fv === 'FV_VAR') variableCost[i] += amt;
      // FV_NA / 未設定 は固定費として扱う(保守的推計)
      else fixedCost[i] += amt;
    }
  });
});

// BEP算出
var bepSales = Array(13).fill(0);
var bepRatio = Array(13).fill(0);  // BEP / 実績売上(安全余裕率の逆数)
for (var i = 0; i < 13; i++) {
  var sales = Math.abs(Number(salesTotal[i]) || 0);
  var varRate = sales > 0 ? variableCost[i] / sales : 0;
  var contribution = 1 - varRate;
  bepSales[i] = contribution > 0 ? Math.round(fixedCost[i] / contribution) : 0;
  bepRatio[i] = sales > 0 ? bepSales[i] / sales : 0;
}

// 出力行に追加
outM.push(Array(hPl.length).fill(''));  // 空行
fmtM.push('empty');
outM.push(['📊 損益分岐点分析 (BEP)', ...Array(hPl.length - 1).fill('')]);
fmtM.push('mega_header');
outM.push(['  固定費合計', ...filterWithRecalcTotal(fixedCost)]);
fmtM.push('account');
outM.push(['  変動費合計', ...filterWithRecalcTotal(variableCost)]);
fmtM.push('account');
outM.push(['  BEP売上高', ...filterWithRecalcTotal(bepSales)]);
fmtM.push('profit');
outM.push(['  BEP到達率 (BEP/売上)', ...filterValues(bepRatio)]);
fmtM.push('auto_tax');

YTD(outY)にも同様に追加(filterValues + dmToYtdArray_ の組み合わせ)。

未設定科目の扱い

固変区分扱い理由
FV_FIX固定費そのまま
FV_VAR変動費そのまま
FV_NA / 空固定費保守的推計(BEPが高く出る方が安全)

影響範囲

  • 601_datamart_ingest.js: acctMap に fvCat プロパティ追加(1行)。既存の参照箇所(stmt, cat, disp)に影響なし
  • 603_datamart_pl.js: dmBuildPlOutput_() の return 直前にBEPセクション追加(約40行)
  • 既存タブへの影響: 61/62/63/64タブの末尾にBEPセクションが追加される。既存の行は一切変更なし

注意事項

  1. 固変区分の精度: BEP計算の精度は科目マスタの固変区分設定に依存する。「人間が検討すべき事項」参照
  2. isActualOnly の適用: BEP行にも既存の filterValues / filterWithRecalcTotal を適用し、実績タブ(61/62)では境界月以降を空白にする
  3. BEP到達率の数値フォーマット: パーセント表示。100%超=売上がBEP未達(赤字ライン)
  4. 売上=0の月: 変動費率が計算不可のためBEP=0とする
  5. 営業外・特別損益: BEP計算には含めない(営業利益ベース)

関連ドキュメント

仕様書関連箇所
CLAUDE.md変更時の動作確認テスト: 600_report/6*_datamart_*.jsマート更新テスト
TODO_future.mdMAS-003 KPIダッシュボード(MAS-024が基盤)

人間が検討すべき事項

  • 固変区分の正確性の確認(科目マスタのレビュー)。特に「通信費」「水道光熱費」等の準変動費をどちらに分類するか
  • 未設定科目を固定費として扱うか変動費として扱うか(本仕様では固定費=保守的推計を採用)

実装プロンプト(Claude Code 用)

Step 1: acctMap 拡張

あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者です。
案件 F-24 の Step 1(acctMap への固変区分追加)を実装してください。

## 実行前タスク

以下のファイルを読み込んでください:
1. `600_report/601_datamart_ingest.js` — acctMap 構築箇所。科目マスタ(11_mst_account)のどの列を読んでいるか確認
2. `100_config/101_sys_config.js` — L451の MST_ACCT headers。固変区分の列位置(index 7)を確認
3. `docs/dev/dev_mas-024_bep_analysis.md`

## 実装内容

`601_datamart_ingest.js` の acctMap 構築ループで、`acctData[i][7]`(固変区分)を `fvCat` プロパティとして追加。

## 制約
- 既存の stmt, cat, disp プロパティは変更しない
- acctData の列インデックスがヘッダーベースでない場合は、ヘッダー検索方式に変更してもよい

Step 2: BEP計算と出力行追加

あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者です。
案件 F-24 の Step 2(BEP計算と出力行追加)を実装してください。

## 実行前タスク

以下のファイルを読み込んでください:
1. `600_report/603_datamart_pl.js` — `dmBuildPlOutput_()` の全体構造。`martPl`, `sectionTotalsPl`, `filterValues`, `filterWithRecalcTotal` の使い方。return文の位置
2. Step 1 で追加した `acctMap.fvCat` の構造
3. `docs/dev/dev_mas-024_bep_analysis.md` — BEP計算式と出力レイアウト

## 実装内容

`dmBuildPlOutput_()` の return 文の直前に、BEPサマリーセクションを追加:

1. 費用セクション(cogs, sga, buffer)の科目別データを `acctMap[科目名].fvCat` で固変分類
2. 固定費合計・変動費合計を月次で集計
3. BEP売上高 = 固定費 / (1 - 変動費/売上) を月次で算出
4. BEP到達率 = BEP売上高 / 実績売上高 を月次で算出
5. outM / outY の両方に行を追加。isActualOnly のフィルタも適用

**未設定科目(fvCat が FV_NA/空/undefined)は固定費として集計する。**

## 動作確認

`npm run push:dev` 後:
1. `buildBudgetTrendDataMart()` を実行
2. 61タブの末尾に「📊 損益分岐点分析」セクションが表示されること
3. 固定費合計 + 変動費合計 ≒ 売上原価 + 販管費 であること
4. BEP到達率が100%以下なら黒字ライン(売上 > BEP)

### 拡張思考の使用状況

| フェーズ | 拡張思考 | 備考 |
|---------|:--------:|------|
| Step 1: acctMap拡張 | なし | 1プロパティ追加のみ |
| Step 2: BEP計算 | あり | 固変分類→変動費率→BEP算出のロジック構築。売上=0のエッジケース |
| Step 2: 出力行追加 | なし | 既存パターン(outM.push + fmtM.push)の踏襲 |

推奨実行モデル

工程推奨モデル理由
仕様書作成(本ドキュメント)Claude Opus 4.6会計上のBEP算出ロジック、固変区分の未設定科目の扱いに高い推論力が必要
Step 1 実装Claude Haiku 4.51プロパティ追加のみ
Step 2 実装Claude Sonnet 4.6BEP計算ロジック構築、filterValues/filterWithRecalcTotal の適切な適用に中程度の判断力が必要

変更履歴

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