概要

項目内容
案件IDMAS-001
カテゴリFP&A
PhaseP1
優先度★★★
所要時間2-3時間
対象ファイル600_report/603_datamart_pl.js(新規関数追加)
600_report/602_datamart_main.js(統合)
600_report/608_datamart_render.js(フォーマット拡張)
100_config/101_sys_config.jsDDL登録)

目的

実績P/Lと計画P/Lの差異を自動計算し、新規タブ 65_pl_variance に出力する。差異額・差異率の閾値超過時にセル色分けアラートを表示し、月次の予実管理を支援する。

出力レイアウト

列構成(53列)

[科目名] [通期:実績,予算,差異額,差異率] [M1:実績,予算,差異額,差異率] ... [M12:実績,予算,差異額,差異率]
= 1 + 4 + (12 × 4) = 53列

ヘッダー2段構成

行1(空)通期サマリー(結合4列)2025-08(結合4列)2025-09(結合4列)...2026-07(結合4列)
行2科目名実績予算差異額差異率実績

データ行(既存P/Lと同じ行構成)

■ 売上高                (group_header)
  売上高              12,000  10,000   2,000   20%  |  1,500  1,000  500  50%  | ...
 【売上高 計】         12,000  10,000   2,000   20%  |  1,500  1,000  500  50%  | ...
✨ 売上総利益            8,000   7,000   1,000   14%  |    800    600  200  33%  | ...
■ 販売費及び一般管理費   (group_header)
  ...
✨ 営業利益              ...
✨ 経常利益              ...
✨ 当期純利益            ...

差異の計算ルール

項目差異額差異率符号の意味
収益(売上等)実績 - 予算(実績 - 予算) / |予算| × 100正=好調、負=未達
費用(原価・販管費等)実績 - 予算同上正=超過、負=節約
利益行実績 - 予算同上正=好調、負=未達
予算=0 の場合実績 - 0 = 実績-(非表示)ゼロ除算回避

閾値超過の色分け

条件背景色対象セル
|差異率| ≥ 閾値(デフォルト20%)赤系 #F4CCCC差異額・差異率セル
|差異率| ≥ 閾値/2(デフォルト10%)黄系 #FCE5CD差異額・差異率セル
上記以外通常(白)

閾値は 03_sys_params で設定可能:

  • CFG_VARIANCE_ALERT_PCT: 赤アラート閾値(デフォルト 20
  • CFG_VARIANCE_WARN_PCT: 黄ワーニング閾値(デフォルト 10

現在のコード構造

実績・計画の計算フロー(602_datamart_main.js)

// 実績 (L234)
ctx.isActualOnly = true;
const plOut = dmBuildPlOutput_(ctx);
// → ctx.sectionTotalsPl, ctx.martPl に実績データ

// 計画 (L243-271)
const planCtx = { ...ctx, isActualOnly: false };
planCtx.finalUnionData = dmIngestPlanData_(ctx, sheetInv);
dmProcessAllEvents_(planCtx);
dmCalcPl_(planCtx);
planPlOut = dmBuildPlOutput_(planCtx);
// → planCtx.sectionTotalsPl, planCtx.martPl に計画データ

データ構造

  • sectionTotalsPl[sectionId]: 13要素配列 [Total, M1, M2, ..., M12]
  • martPl[sectionId][科目名]: 13要素配列(同上)
  • PL_SECTIONS: セクション定義配列(group/profit/auto_tax)

PL_SECTIONS 完全リスト(603_datamart_pl.js L59-79)

ID名前type
sales売上高group
cogs売上原価group
gross_profit✨ 売上総利益profit
sga販売費及び一般管理費group
buffer未定・バッファ予算group
op_profit✨ 営業利益profit
non_op_inc営業外収益group
non_op_exp営業外費用group
ord_profit✨ 経常利益profit
ext_inc特別利益group
ext_exp特別損失group
pre_tax_profit✨ 税引前当期純利益profit
tax法人税等group
auto_tax_national法人税等・国税auto_tax
auto_tax_local法人税等・地方税auto_tax
net_profit✨ 当期純利益profit

実装ステップ

Step 1: DDL・パラメータ・レンダラー拡張

ファイル変更内容
101_sys_config.jsPL_VAR タブ (65_pl_variance) を DDL 登録
608_datamart_render.jsdmApplyVarianceFormat_() 新規関数追加(2段ヘッダー結合 + 閾値色分け)

Step 2: 差異計算・出力関数

ファイル変更内容
603_datamart_pl.jsdmBuildPlVarianceOutput_(actCtx, planCtx) 新規関数追加
  • 実績 actCtx.sectionTotalsPl / actCtx.martPl と計画 planCtx.sectionTotalsPl / planCtx.martPl を比較
  • 各科目・セクションについて53列の出力配列を構築
  • 実績モードの filterWithRecalcTotal を適用済みの値を使用(境界月以降は空白)

Step 3: メイン統合

ファイル変更内容
602_datamart_main.jsシート取得 + dmBuildPlVarianceOutput_ 呼び出し + dmApplyVarianceFormat_ で出力

注意事項

  1. 実績の境界月以降: 実績側は boundaryMonthStr 以降が空白。差異計算では空白月の差異額・差異率も空白とする(予算のみ表示しない)
  2. auto_tax セクション: 自動税計算の予実差異は参考値。予算側も同じ税率計算で算出されるため、差異は税引前利益の差異に連動する
  3. 計画タブ未生成時: planCtx が存在しない場合(計画パイプラインの条件分岐外)はスキップ
  4. YTDタブ: 本案件のスコープではYTD差異タブは作成しない。必要に応じて後続案件で追加

関連ドキュメント

仕様書関連箇所
CLAUDE.md変更時の動作確認テスト: 600_report/6*_datamart_*.jsマート更新テスト
B.3 統合テスト手順マート更新テスト(P/L・B/S・CF)

人間が検討すべき事項

  • アラート閾値(差異率何%で赤/黄表示するか)。デフォルト20%/10%を提案。03_sys_params で変更可能
  • 費用科目の差異の符号表現: 「費用超過=正(赤)」か「費用超過=負(赤)」か。本仕様では「実績-予算」の素直な計算とし、費用超過は正値=赤とする

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

Step 1: DDL・レンダラー拡張

あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者です。
案件 MAS-001「予実差異分析」の Step 1(DDL・レンダラー拡張)を実装してください。

## 実行前タスク

以下のファイルを読み込んでください:
1. `100_config/101_sys_config.js` — 既存のタブ登録パターン(PL_M_ACT 等)
2. `600_report/608_datamart_render.js` — `dmApplyDwhFormat_()` の全体構造
3. `000_infra/002_constants.js` — `Constants.getParam()` の使用パターン
4. `CLAUDE.md`
5. `docs/dev/dev_mas-001_variance_analysis.md` — 本仕様書の出力レイアウト仕様

## 実装内容

### 1-A: DDL登録(101_sys_config.js)

P/Lセクション(PL_M_ACT 等の登録箇所)の近くに以下を追加:
```js
if (!existKeys.includes('PL_VAR'))
  confSheet.appendRow(['PL_VAR', '', '65_pl_variance', 'P/L予実差異分析']);
```

### 1-B: レンダラー新規関数(608_datamart_render.js)

`dmApplyVarianceFormat_(sheet, dataArray, formatArray, targetMonths, alertPct, warnPct)` を新規追加。

**要件:**
- 2段ヘッダーの結合処理: 行1で「通期サマリー」(4列結合) + 各月(4列結合)をmerge
- 行2にサブヘッダー「実績/予算/差異額/差異率」を繰り返し
- fmtタグによる背景色: 既存の `dmApplyDwhFormat_` と同じ行タイプ(group_header, account, subtotal, profit, auto_tax 等)
- **差異セルの閾値色分け**: 差異率列の絶対値が `alertPct` 以上なら赤系(#F4CCCC)、`warnPct` 以上なら黄系(#FCE5CD)。差異額列にも同じ色を適用
- 数値フォーマット: 実績・予算・差異額は `#,##0;[Red]△ #,##0`、差異率は `0.0%;[Red]△ 0.0%;"-"`
- 列幅: 科目名=300, 数値列=90, 差異率列=70
- フォント: BIZ UDGothic(既存と統一)
- フィルター: 行2に設定

**閾値色分けのロジック:**
各データ行の差異率セル(通期 + 月別 × 12 = 13箇所)を走査し:
- 値が空白 or `-` → スキップ
- |値| >= alertPct/100 → 赤系
- |値| >= warnPct/100 → 黄系
- 隣接する差異額セルにも同じ色を適用

## 制約
- `dmApplyDwhFormat_()` は変更しない(新規関数として追加)
- 既存のフォーマットタグ名はそのまま使用

Step 2: 差異計算・出力関数

あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者です。
案件 MAS-001「予実差異分析」の Step 2(差異計算・出力関数)を実装してください。

## 実行前タスク

以下のファイルを読み込んでください:
1. `600_report/603_datamart_pl.js` — `dmBuildPlOutput_()` の構造、`PL_SECTIONS` 定義、`sectionTotalsPl` / `martPl` の構造
2. `600_report/602_datamart_main.js` — 実績ctx と planCtx の構築方法(L228-271)
3. `docs/dev/dev_mas-001_variance_analysis.md` — 出力レイアウト仕様(53列構成)

## 実装内容

`603_datamart_pl.js` に `dmBuildPlVarianceOutput_(actCtx, planCtx)` を新規追加する。

### 関数シグネチャ
```js
/**
 * P/L予実差異分析の出力配列を構築する
 * @param {Object} actCtx - 実績コンテキスト (sectionTotalsPl, martPl, PL_SECTIONS, targetMonths, boundaryMonthStr)
 * @param {Object} planCtx - 計画コンテキスト (sectionTotalsPl, martPl)
 * @returns {{ outV: Array[], fmtV: string[] }}
 */
```

### 出力配列の構造(53列/行)

各データ行:
```js
[科目名,
 実績Total, 予算Total, 差異額Total, 差異率Total,
 実績M1, 予算M1, 差異額M1, 差異率M1,
 実績M2, 予算M2, 差異額M2, 差異率M2,
 ...
 実績M12, 予算M12, 差異額M12, 差異率M12]
```

### 構築ロジック

1. **ヘッダー構築(行1-2)**:
   - 行1: タイトル行(1列目にuiHeader、残り空白)→ fmt='title'
   - 行2: 2段ヘッダー上段(科目名, 通期サマリー(結合用), 月名(結合用)...)→ fmt='header_var_top'
   - 行3: 2段ヘッダー下段(科目名, 実績,予算,差異額,差異率, ×13グループ)→ fmt='header_var'

2. **データ行構築**:
   PL_SECTIONS をループし:
   - `type === 'group'`: グループヘッダー行 + 科目明細行 + 小計行
   - `type === 'profit'`: 利益行
   - `type === 'auto_tax'`: 税行

3. **各科目の53列データ生成ヘルパー**:
```js
function buildVarianceRow_(actArr, planArr, targetMonths, boundaryMonthStr) {
  // actArr, planArr: [Total, M1, ..., M12] の13要素配列
  var row = [];
  for (var i = 0; i < 13; i++) {
    // 境界月以降は全て空白
    if (i > 0 && targetMonths[i-1] >= boundaryMonthStr) {
      row.push('', '', '', '');
      continue;
    }
    var act = Number(actArr[i]) || 0;
    var plan = Number(planArr[i]) || 0;
    var diff = act - plan;
    var rate = (plan !== 0) ? diff / Math.abs(plan) : '-';
    row.push(act, plan, diff, rate);
  }
  return row;
}
```

4. **fmtタグ**: 既存P/Lと同じ(group_header, account, subtotal, profit, auto_tax)

### 境界月の扱い

- `actCtx.boundaryMonthStr` 以降の月は実績が未確定
- 差異分析は実績確定月のみ意味があるため、境界月以降は4列とも空白にする

## 制約
- 実績・計画の `sectionTotalsPl` / `martPl` は読み取りのみ。変更しない
- 既存の `dmBuildPlOutput_()` は変更しない
- 計画側に存在するが実績側に存在しない科目(またはその逆)も正しく処理する(存在しない側は0として計算)

Step 3: メイン統合

あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者です。
案件 MAS-001「予実差異分析」の Step 3(メイン統合)を実装してください。

## 実行前タスク

以下のファイルを読み込んでください:
1. `600_report/602_datamart_main.js` — シート取得セクション(L163-186)と計画パイプライン(L243-288)
2. Step 1 で追加した `608_datamart_render.js` の `dmApplyVarianceFormat_()` のシグネチャ
3. Step 2 で追加した `603_datamart_pl.js` の `dmBuildPlVarianceOutput_()` のシグネチャ
4. `000_infra/002_constants.js` — `Constants.getParam()` の使用パターン
5. `docs/dev/dev_mas-001_variance_analysis.md`

## 実装内容

### 3-A: シート取得の追加(602_datamart_main.js L173付近)

P/Lシート取得セクションに追加:
```js
let sheetPlVar = Utils.getSheetByKey('PL_VAR', '65_pl_variance');
```

### 3-B: 差異タブ出力の追加(L290付近、P/L計画出力の後)

計画パイプラインの if ブロック内(`planPlOut` が生成された後)に追加:

```js
// MAS-001: 予実差異分析
if (sheetPlVar && planPlOut) {
  var alertPct = Constants.getParam('CFG_VARIANCE_ALERT_PCT', 20);
  var warnPct = Constants.getParam('CFG_VARIANCE_WARN_PCT', 10);
  var varOut = dmBuildPlVarianceOutput_(ctx, planCtx);
  dmApplyVarianceFormat_(sheetPlVar, varOut.outV, varOut.fmtV,
    targetMonths, Number(alertPct), Number(warnPct));
}
```

### 3-C: 条件分岐の位置

差異タブは計画パイプラインが実行される場合のみ生成される(`planCtx` が存在する場合)。計画パイプラインの if ブロック内、`planPlOut = dmBuildPlOutput_(planCtx)` の後に配置する。

## 動作確認

実装後、`npm run push:dev` で開発環境にデプロイし:
1. GASエディタで `setupAllSchemas()` を実行(65タブのDDL登録)
2. `buildBudgetTrendDataMart()` を実行
3. 65_pl_variance タブが生成されること
4. 2段ヘッダーが正しく結合されていること
5. 実績確定月に差異額・差異率が表示されること
6. 境界月以降が空白であること
7. 閾値超過のセルが赤/黄で色分けされていること

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

| フェーズ | 拡張思考 | 備考 |
|---------|:--------:|------|
| Step 1: DDL登録 | なし | 既存パターンの踏襲 |
| Step 1: レンダラー | あり | 2段ヘッダー結合+閾値色分けのセル走査ロジック |
| Step 2: 差異計算関数 | あり | 53列配列の構築、境界月フィルタ、片側のみ存在する科目の処理 |
| Step 3: メイン統合 | なし | 数行の追加のみ |

推奨実行モデル

工程推奨モデル理由
仕様書作成(本ドキュメント)Claude Opus 4.653列レイアウト設計、P/Lセクション横断の差異計算ロジック設計に高い推論力が必要
Step 1 実装Claude Sonnet 4.6レンダラーは既存パターンの拡張。2段ヘッダー結合が少し複雑
Step 2 実装Claude Opus 4.6実績/計画の両ctx横断、片側のみ存在する科目の処理、境界月フィルタの組み合わせに高い推論力が必要
Step 3 実装Claude Haiku 4.5数行の追加。判断要素なし

変更履歴

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