概要
| 項目 | 内容 |
|---|
| 案件ID | MAS-001 |
| カテゴリ | FP&A |
| Phase | P1 |
| 優先度 | ★★★ |
| 所要時間 | 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.js(DDL登録) |
目的
実績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.js | PL_VAR タブ (65_pl_variance) を DDL 登録 |
608_datamart_render.js | dmApplyVarianceFormat_() 新規関数追加(2段ヘッダー結合 + 閾値色分け) |
Step 2: 差異計算・出力関数
| ファイル | 変更内容 |
|---|
603_datamart_pl.js | dmBuildPlVarianceOutput_(actCtx, planCtx) 新規関数追加 |
- 実績
actCtx.sectionTotalsPl / actCtx.martPl と計画 planCtx.sectionTotalsPl / planCtx.martPl を比較
- 各科目・セクションについて53列の出力配列を構築
- 実績モードの
filterWithRecalcTotal を適用済みの値を使用(境界月以降は空白)
Step 3: メイン統合
| ファイル | 変更内容 |
|---|
602_datamart_main.js | シート取得 + dmBuildPlVarianceOutput_ 呼び出し + dmApplyVarianceFormat_ で出力 |
注意事項
- 実績の境界月以降: 実績側は
boundaryMonthStr 以降が空白。差異計算では空白月の差異額・差異率も空白とする(予算のみ表示しない)
- auto_tax セクション: 自動税計算の予実差異は参考値。予算側も同じ税率計算で算出されるため、差異は税引前利益の差異に連動する
- 計画タブ未生成時:
planCtx が存在しない場合(計画パイプラインの条件分岐外)はスキップ
- YTDタブ: 本案件のスコープではYTD差異タブは作成しない。必要に応じて後続案件で追加
関連ドキュメント
人間が検討すべき事項
- アラート閾値(差異率何%で赤/黄表示するか)。デフォルト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.6 | 53列レイアウト設計、P/Lセクション横断の差異計算ロジック設計に高い推論力が必要 |
| Step 1 実装 | Claude Sonnet 4.6 | レンダラーは既存パターンの拡張。2段ヘッダー結合が少し複雑 |
| Step 2 実装 | Claude Opus 4.6 | 実績/計画の両ctx横断、片側のみ存在する科目の処理、境界月フィルタの組み合わせに高い推論力が必要 |
| Step 3 実装 | Claude Haiku 4.5 | 数行の追加。判断要素なし |
変更履歴