MAS-026: TDABC(時間主導型ABC:活動基準原価計算)
概要
| 項目 | 内容 |
|---|---|
| 案件ID | MAS-026 |
| カテゴリ | FP&A |
| Phase | P3 |
| 優先度 | ★★ |
| 所要時間 | 6-8時間 |
| 実装ステータス | 📝 仕様書段階・実装未着手 (2026-04-28 監査時点) |
| 対象ファイル | 400_domain/421_tdabc_allocator.js(新規作成。TDABC計算ロジック+一時 Repository)100_config/101_sys_config.js(MENU_DEFINITION に 2項目追加、DDL非管理タブコメントに 79_pj_tdabc_calc 追記) |
| 前提案件 | MAS-006(PJ別管理会計の共通費配賦・実装済み 400_domain/420_project_profitability.js)MAS-105(工数入力・PJ原価連携・仕様書完了/未実装) |
| 後続案件 | MAS-027(M365監査ログ連携→作業時間の自動推定)、MAS-039(ABC分析) |
目的
MAS-006(420_project_profitability.js)が採用している 稼働率(%) × 月額人件費 による PJ 別労務費配賦は、「8月は 80% 稼働・9月は 50% 稼働」といった比率ベースであるため、1時間あたりの原価(キャパシティレート)が経営会議で示せない。結果として「売上 A ランクだが時間を食い潰して赤字」という隠れた不採算 PJ を検出できない。
MAS-026(TDABC = Time-Driven Activity-Based Costing)は、実工数ベースで間接費を配賦する仕組みを追加する。
- 時間単価(キャパシティレート)の可視化 —
人件費合計 ÷ 総供給時間を月次で算出し、1時間あたりのコストを明示する。 - PJ別消費時間の集計 — MAS-105(
43_trn_timesheet)の工数データから PJ × 月の消費時間を集計する。 - PJ別配賦額の算出 —
PJ別消費時間 × 時間単価で間接費を配賦し、78_pj_pl(PJ別 P/L)に反映する。
これにより、MAS-006 の稼働率(%)ベース配賦より精度の高い原価把握が可能となり、MAS-039(ABC分析)・MAS-027(M365監査ログ連携)の土台となる。
現在のコード
1. 400_domain/420_project_profitability.js — MAS-006 の配賦ロジック(現行)
(B) 人員ごとの月額人件費マップ作成 — L40-60
const idxIn = hcData[0].indexOf('開始年月'), idxOut = hcData[0].indexOf('終了年月'),
idxName = hcData[0].indexOf('氏名・ポジション'), idxSal = hcData[0].indexOf('月額給与・報酬'),
idxSoc = hcData[0].indexOf('社保会社負担率'), idxType = hcData[0].indexOf('雇用形態');
// ...
const totalLaborCost = sal + (empType !== '業務委託' ? Math.round(sal * soc) : 0);
hcMap[name][curYm] = totalLaborCost;
(C) 労務費の集計: Resource × Headcount — L73-86
const idxName = rsData[0].indexOf('要員名'), idxPj = rsData[0].indexOf('PJ・案件名'),
idxYm = rsData[0].indexOf('対象年月'), idxRate = rsData[0].indexOf('稼働率(%)');
// ...
const baseSal = (hcMap[name] && hcMap[name][ym]) ? hcMap[name][ym] : 0;
pjObj[ym].laborCost += Math.round(baseSal * rate);
(I) 共通費配賦の「工数比」 — L464-534
配賦元 PJ の費用を配賦先 PJ に按分する際の比率選択肢として 売上高比 / 工数比 / 労務費比 / 均等割 / 手動 が存在するが、「工数比」は 27_bud_resource の稼働率(%)の合計を工数相当として扱うため、実工数ベースではない。
2. 78_pj_pl の出力構造(PJ横並び P/L)— L536-747
P/L セクション定義は以下(L540-552):
var plSections = [
{ id: 'sales', name: '売上高', type: 'group' },
{ id: 'cogs', name: '売上原価', type: 'group' },
{ id: 'gross', name: '✨ 売上総利益', type: 'profit', calc: [...] },
{ id: 'sga_direct', name: '販管費 (直接)', type: 'group' },
{ id: 'labor', name: '労務費 (工数配賦)', type: 'group' },
{ id: 'marginal', name: '✨ 限界利益', type: 'profit', calc: [...] },
{ id: 'alloc', name: '共通費配賦', type: 'group' },
{ id: 'op_profit', name: '✨ PJ営業利益', type: 'profit', calc: [...] },
// ... 営業外・経常利益
];
列構成(L621-633): A列=表示区分 / B列=配賦元合計 / C列以降=PJコード(1行目)+PJ名(2行目) / 最終列=全社合計。配賦先 PJ が売上順に C 列から横並びに並ぶ。
3. 200_data/202_repository.js — Repository パターン
JournalRepository.findAll() は { headers: string[], dtos: JournalEntryDTO[] } を返す(L270-272)。読取は readSheetAsDtos_(sheet) ヘルパー(L19-29)に統一されている。AccountRepository.findAsMap() はキャッシュ付きで { 科目名: { stmt, cat } } を返す(L323-341)。TimesheetRepository(43_trn_timesheet 用)および HeadcountRepository(22_bud_headcount 用)は未実装。
4. 22_bud_headcount の列構造
100_config/101_sys_config.js L877 の DDL で定義されているヘッダー:
有効フラグ / 管理ID / 氏名・ポジション / 雇用形態 / 科目名 / 取引先名 / 適用年度 / 入社年月 / 退職年月 / 開始年月 / 終了年月 / 月額給与・報酬 / 決済手段 / 決済ラグ(月) / ... / 組織名 / 起票ターゲット月 / 最終起票年月日 / 備考
月次稼働時間に相当する列は存在しない。 したがって総供給時間の算出は、別途「1人あたり月次標準稼働時間」の入力源を導入する必要がある(「人間が検討すべき事項」に詳述)。
5. 43_trn_timesheet の有無
MAS-105(工数入力・PJ原価連携)の仕様書 docs/dev/dev_mas-105_workforce_costing.md は完了しているが 未実装。ただし MAS-105 の設計は 70_bud_resource(稼働率(%) → 工数(h))への列変更であり、43_trn_timesheet という独立シートは想定されていない。MAS-026 では本タスクのスコープで 43_trn_timesheet シートの存在を前提とするが、MAS-105 側の設計差異は「人間が検討すべき事項」で調整する。
修正方針
2 Step 構成で実装する。各 Step の前に対象ファイルを Read して既存パターンを把握する(推測禁止)。
Step 1 — 400_domain/421_tdabc_allocator.js の新規作成
CLAUDE.md のファイル番号体系に従い、400_domain/ 直下に 421_tdabc_allocator.js(次番 421。420 までが既使用)を新規作成する。modules/ 等のサブディレクトリは使用しない。
(1-a) 一時 Repository の定義
MAS-105 実装確定後に 200_data/202_repository.js へ移管する前提で、本ファイル内に 同居 させる(OrderRepository / JournalRepository と同じ _getSheet / findAll パターン):
TimesheetRepository—43_trn_timesheetをUtils.getSheetByKey('TRN_TSHT', '43_trn_timesheet')で取得。findAll()はreadSheetAsDtos_(sheet)ヘルパーを呼び出して{ headers, dtos }を返す(※readSheetAsDtos_は202_repository.js内で定義された module-scope 関数なので GAS のグローバルスコープ経由で呼び出せる。呼び出せない場合は本ファイル内に同ロジックを再実装する)。HeadcountRepository—22_bud_headcountをUtils.getSheetByKey('BUD_HC', '22_bud_headcount')で取得。findAll()は同上。
いずれも 400_domain/421_tdabc_allocator.js 内にのみ定義 する。既存の 200_data/202_repository.js は一切変更しない。
(1-b) メイン関数 runTdabcCalculation() の実装
運用フローの ステップ 1(計算 → 中間シート出力)を担う。処理順序:
JournalRepository.findAll()で実績仕訳を取得し、AccountRepository.findAsMap()で特定した人件費科目の当月分のみを抽出して月次人件費合計を算出する。HeadcountRepository.findAll()と 03_sys_params を用いて月次の総供給時間を算出する(算出方法は Phase 1-F で確認し、未定義なら「人間が検討すべき事項」参照)。時間単価 = 人件費合計 ÷ 総供給時間。総供給時間=0 の場合は時間単価=0 としてゼロ除算を回避。TimesheetRepository.findAll()を呼ぶ。シートが存在しない(= MAS-105 未実装)場合はダイアログ工数データシート(43_trn_timesheet)が見つかりません。案件S-33の実装が必要です。を表示し、returnする。- 工数データから
{ PJ名: { 年月: 消費時間h } }マップを構築。 PJ別配賦額 = PJ別消費時間 × 時間単価を算出。79_pj_tdabc_calcシートに中間結果を出力する(列構成は後述)。
(1-c) 中間確認シート 79_pj_tdabc_calc の列構造
ユーザーの目視確認用のタブ。DDL 非管理タブとして CLAUDE.md / 101_sys_config.js に注記を追加する。列構成:
| 列 | ヘッダー名 | 説明 |
|---|---|---|
| A | 対象年月 | YYYY-MM |
| B | PJコード | 14_mst_project の PJ コード(解決できなければ空) |
| C | PJ名 | 14_mst_project のプロジェクト名(消費時間が 0 でも総供給時間算出結果には含める) |
| D | 消費時間(h) | 当該 PJ × 当該月の合計工数 |
| E | 時間単価(円/h) | 人件費合計 ÷ 総供給時間(月別) |
| F | 配賦額(円) | 消費時間 × 時間単価 の整数丸め(Math.round) |
| G | 人件費合計(円) | 分子(月別) |
| H | 総供給時間(h) | 分母(月別) |
| I | 未配賦時間(h) | 総供給時間 − 当該月のPJ消費時間合計(月ごとの合計行にのみ出力) |
| J | 未配賦コスト(円) | 未配賦時間 × 時間単価(月ごとの合計行にのみ出力) |
| K | 備考 | 警告(総供給時間=0 / 人件費=0 / 科目未登録 等) |
ソート: 対象年月 昇順 → PJ名 昇順。月別に合計行(I/J 列に値)を挿入する。
(1-d) 反映関数 applyTdabcToPl() の実装
運用フローの ステップ 2(中間シート → 78_pj_pl 反映)を担う。処理:
79_pj_tdabc_calcを読み取り、当期 12 ヶ月の PJ × 月の配賦額を集計して PJ 別の通期合計を得る。78_pj_plを開き、配賦元合計列(B列)と各PJ列(C 列以降)における共通費配賦セクション(alloc) の行範囲を検出する。検出ロジック: A列の値が■ 共通費配賦から始まる行〜【共通費配賦 計】を含む行まで。- 冪等性保証のため、
【共通費配賦 計】行のすぐ上に 既存の🕐TDABC(工数配賦)行が存在すれば削除してから書き込む。キーは A列ラベル完全一致。 allocセクション内に🕐TDABC(工数配賦)という科目名で新規行を挿入し、各 PJ 列に−(PJ別配賦額)、B 列=配賦元合計に+(全PJ配賦額の合計)を書き込む。これは「共通費から TDABC 分を差し引き、PJ 側に付け替える」差引設計。- 同じ列の
【共通費配賦 計】の小計・✨ PJ営業利益以降の利益行は、既存の式ではなく数値で再計算して上書きする(MAS-006 の出力も数値setValuesのため式ではない)。
Step 2 — 100_config/101_sys_config.js のメニュー追加・コメント追記
(2-a) Constants.MENU_DEFINITION の「📋 サイドバー: 📊 マート更新」カテゴリに 2項目を追加
buildProjectProfitability(プロジェクト別 採算)の直後に以下を挿入する:
{ label: '🕐 TDABC計算実行', funcName: 'runTdabcCalculation', description: '時間単価×PJ別消費時間で間接費を試算(79_pj_tdabc_calc に出力)' },
{ label: '🕐 TDABC→78タブ反映', funcName: 'applyTdabcToPl', description: '79_pj_tdabc_calc の結果を 78_pj_pl の共通費配賦セクションに転記(冪等)' },
(2-b) DDL 非管理タブのコメント追記
CLAUDE.md の「DDL (setupAllSchemas) で管理されないタブ」節に 79_pj_tdabc_calc を追加する。101_sys_config.js の setupAllSchemas 関数直前のコメントブロックにも同旨を追記する(該当コメントブロックが無い場合はこのタイミングで新設する)。DDL の schemas オブジェクトには 追加しない(setupAllSchemas の対象外)。
影響範囲
| 対象 | 影響内容 |
|---|---|
400_domain/421_tdabc_allocator.js | 新規作成(既存影響なし) |
100_config/101_sys_config.js | MENU_DEFINITION に 2項目追加、DDL 非管理タブのコメント追記。setupAllSchemas の処理フローには手を入れない |
400_domain/420_project_profitability.js | 変更しない(MAS-006 の既存ロジック保持)。TDABC 結果は 78_pj_pl の 共通費配賦 セクション内に差込む |
200_data/202_repository.js | 変更しない。TimesheetRepository / HeadcountRepository は 421_tdabc_allocator.js 内に閉じる |
78_pj_pl | 共通費配賦 セクションに 🕐TDABC(工数配賦) 行が 1 行追加される。【共通費配賦 計】・✨ PJ営業利益 以降は再計算される |
79_pj_tdabc_calc | 新規タブ(runTdabcCalculation 実行時に ss.insertSheet)。DDL 管理対象外 |
注意事項
- MAS-006 の出力を壊さない —
buildProjectProfitability実行後にrunTdabcCalculation→applyTdabcToPlを流す運用。MAS-006 が先に 78 タブを再構築した際は、既存の TDABC 行が消えるので再実行が必要。整合性を担保するためapplyTdabcToPl実行時にダイアログで警告する。 - Human-in-the-Loop の徹底 — 計算結果を自動で 78 タブに反映するのではなく、必ず
79_pj_tdabc_calcでユーザーが確認した後にapplyTdabcToPlを手動実行する 2 ステップ運用とする(CLAUDE.md プロダクトポリシー準拠)。 - 冪等性 —
applyTdabcToPlは、実行前に既存の🕐TDABC(工数配賦)行を削除してから書き込む。何度実行しても結果は同じ。 - MAS-105 依存 —
43_trn_timesheetシートは MAS-105 実装後に現れる前提。シート不在時は明示的にダイアログで処理中断し、部分書き込みや黙殺を行わない。 - 80 番台ではない理由 — 中間確認シート
79_pj_tdabc_calcは既存 PJ 別タブ群(77_pj_raw/78_pj_pl/79_pj_monthly)と隣接させるため 79 番台末尾に配置する。79_pj_monthlyは MAS-006 が再生成する PJ 別月次採算表で用途が異なるため別タブとして共存させる。 - 計算対象期間 —
420_project_profitability.jsL22-24 と整合させ、当期 12 ヶ月(4月開始/8月開始は実装時に420_project_profitability.jsのstartYear・targetMonthsと同じロジックで決定)。
エッジケース
| 条件 | 表示値・挙動 | 理由 |
|---|---|---|
| 総供給時間 = 0(従業員なし or 稼働時間未入力) | 時間単価 = 0、全PJ配賦額 = 0。79_pj_tdabc_calc に「備考」列で警告行を出力 | ゼロ除算防止 |
| 人件費合計 = 0(当月実績なし) | 時間単価 = 0、全PJ配賦額 = 0 | 正常ケースとして処理 |
| 特定月の工数データが全件未入力 | 全PJ消費時間 = 0、全PJ配賦額 = 0。月別合計行のみ出力(未配賦時間=総供給時間) | 正常ケースとして処理 |
科目マスタに人件費科目が未登録(AccountRepository.findAsMap() で特定できない) | 計算実行前ガードで検出、ダイアログ警告して処理中断 | 仕様上の前提条件として明記 |
43_trn_timesheet シートが存在しない(MAS-105未実装) | 処理中断。ダイアログ 工数データシート(43_trn_timesheet)が見つかりません。案件S-33の実装が必要です。 | MAS-105 依存を明示 |
| 全PJ消費時間合計 < 総供給時間(未配賦時間あり) | 未配賦コスト = (総供給時間 − 全PJ消費時間合計) × 時間単価 を 79_pj_tdabc_calc の月別合計行(I/J列)に明示。78_pj_pl では配賦せず「共通費」として残す | 未配賦コスト(管理業務・移動・社内会議など)を埋没させない |
| 全PJ消費時間合計 > 総供給時間(超過稼働) | 時間単価は総供給時間ベースのまま(超過は労働生産性の超過として解釈)。79_pj_tdabc_calc の「備考」列に警告 | 時間単価が乱高下しないよう分母を固定 |
| 「TDABC計算実行」を複数回実行 | 79_pj_tdabc_calc を毎回 clear() してから再出力 | 計算のみの冪等性 |
| 「78タブへ反映」を複数回実行 | 実行前に既存の 🕐TDABC(工数配賦) 行を削除してから書き込み(冪等性保証) | 二重計上防止 |
buildProjectProfitability(MAS-006)後に applyTdabcToPl を未実行 | 78タブに TDABC 差込行がない状態。当月の TDABC 反映は手動で applyTdabcToPl を呼ぶ必要がある旨をダイアログで告知 | MAS-006 の再生成で TDABC 行が消える想定 |
| 14_mst_project に登録されていない PJ 名が工数データに存在 | PJコード列を空文字として 79 タブに出力。配賦計算は実施(消費時間 × 時間単価) | マスタ未登録もデータを欠損させない |
実データ検証
実装前に MCP で以下を事前確認する。仕様書中の前提条件が崩れていないかのファクトチェック用。
11_mst_accountのcat(大分類)実値の確認 —AccountRepository.findAsMap()が返す{ stmt, cat }のうち人件費カテゴリをフィルタするための値を特定する。大分類は収益 / 費用 / 資産 / 負債 / 純資産に寄っており人件費が入っていない可能性が高い。その場合は表示区分(disp)側に人件費/販管費等が入っているかを MCP で直接確認する。代替として科目名 に '給料' / '役員報酬' / '法定福利' / '雑給' のいずれかを含む(420_project_profitability.jsL136 で採用されている判定と同一ロジック)をrunTdabcCalculation内でも採用する案を検討する。22_bud_headcount列の再確認 — DDL 上は月額給与・報酬 / 雇用形態 / 開始年月 / 終了年月は存在するが、月次稼働時間に相当する列は存在しない(101_sys_config.jsL877)。1人あたり標準稼働時間の入力源として03_sys_paramsの新キーMONTHLY_STD_HOURS(デフォルト 160h)を想定するが、MCP で03_sys_paramsの既存キー一覧と重複がないことを確認する。78_pj_pl行位置の確認 — 先頭から■ 共通費配賦行の開始行 /【共通費配賦 計】行 /✨ PJ営業利益行の位置を MCP で実データ読み取りして確認する。plSectionsの並び(420_project_profitability.jsL540-552)は固定だが、セクション内の科目行数は月次で変動するため ラベル完全一致でスキャンして行位置を特定する。列についてはC列以降の PJ順序を78_pj_plの行2(PJ名)でスキャンする。
関連ドキュメント
- CLAUDE.md — プロジェクトルール(ファイル番号体系・DDL 非管理タブ・Human-in-the-Loop ポリシー)
- dev_mas-006_project_overhead_allocation.md(前提案件:PJ別管理会計の共通費配賦)
- dev_mas-105_workforce_costing.md(前提案件:工数入力・PJ原価連携)
- TODO_future.md — MAS-026 行(案件定義)
人間が検討すべき事項
以下は 実装前にユーザー判断が必要な設計項目。仮決めで進めず、仕様書時点で明示的に問い合わせる。
総供給時間の算出ロジックの最終確定
22_bud_headcountに月次稼働時間列は存在しない(DDL 確認済み)。MAS-026 では 3 案のうちどれを採用するかを確定する必要がある:- 案A:
03_sys_paramsにMONTHLY_STD_HOURS(デフォルト 160h = 8h × 20営業日)を新設し、「有効フラグ=TRUE×開始年月 ≦ 対象月 ≦ 終了年月」の在籍要員数 ×MONTHLY_STD_HOURSを総供給時間とする(実装コスト最小)。 - 案B:
22_bud_headcountに月次稼働時間列を DDL 追加し、雇用形態ごと(正社員 160h / 業務委託 実契約時間)に個別値を保持する。本案を採用する場合は MAS-105 の70_bud_resource拡張とも整合を取る必要がある。 - 案C: 既存
70_bud_resourceの「稼働率(%)」×MONTHLY_STD_HOURSを個人別に積算する。MAS-105 実装待ちだが、稼働率ベースの現状データを流用できる。
- 案A:
MAS-105 との設計整合 MAS-105 の仕様書(
dev_mas-105_workforce_costing.md)は70_bud_resourceを「稼働率(%) → 工数(h)」に列変更する設計になっており、43_trn_timesheetという独立シートは登場しない。MAS-026 の本仕様書で前提としている43_trn_timesheetシート名は、MAS-105 側と整合させる必要がある。次の 2 択:- 案α: MAS-026 側を MAS-105 に合わせ、
70_bud_resource(工数列)を TDABC 入力源とする。→TimesheetRepositoryは事実上ResourceRepositoryと一体化し、MAS-026 のために新設する意味が薄れる。 - 案β: MAS-105 側を拡張し、工数明細を別タブ
43_trn_timesheetに分離する(70_bud_resourceは月次集計済みサマリのまま)。→ 日次工数入力と PJ別明細を両立できるが、MAS-105 の再設計が必要。
- 案α: MAS-026 側を MAS-105 に合わせ、
人件費科目の識別ロジック 「実データ検証」の通り
大分類だけでは識別不可の可能性が高い。科目名のキーワード一致(420_project_profitability.jsL136 と同じ判定)か、表示区分の人件費完全一致か、いずれを採用するかを MCP 事前確認の上で決定する。未配賦時間の扱い 未配賦時間(管理業務・移動・社内会議・有休等)のコストは、
78_pj_plの共通費配賦セクションに残す方針(案 A:現行の MAS-006 共通費配賦ルールで按分 / 案 B:TDABC の未配賦コスト行として配賦せず残す)のどちらを採用するか。本仕様書は案 B を前提としているが、経営会議で「共通費=未配賦コスト」の可視化方針を確認する。工数の入力粒度 30 分単位 / 1 時間単位のどちらで
43_trn_timesheetを設計するか。MAS-105 の設計選択に依存する。計算対象期間 当期 12 ヶ月 or 直近 3 ヶ月 or 月次選択 UI を用意するか。MAS-006 は当期 12 ヶ月固定。MAS-026 も同じ方針で問題ないか確認。
時間単価の月次変動の扱い 月ごとに時間単価を算出する(本仕様書案)vs 通期平均で単価を固定する、どちらが経営会議で説明しやすいか。
実装プロンプト(Claude Code 用)
別セッションでも自己完結して着手できるよう、本案件の前提情報と手順を全て記載する。コードブロック記法(バッククォート)は使わず、行頭スペース 4 つのインデントで提示する。
あなたは GAS 会計システム (bizlp-gas-accounting) のシニア開発者です。
案件 MAS-026「TDABC(時間主導型ABC:活動基準原価計算)」を実装してください。
【実行前タスク — 1-A〜1-G すべてを調査してから実装に進むこと】
1-A. docs/_internal/TODO_future.md を Grep し、MAS-026 / MAS-006 / MAS-105 / MAS-027 の行を読む。特に MAS-105 の実装状況を確認する(43_trn_timesheet シートが存在するかどうかは TDABC の入力源に直結)。
1-B. 400_domain/420_project_profitability.js を Read し、以下を把握する:
- buildProjectProfitability() の全体構成と呼び出し順序
- loadAcctMaster_ / loadPjMaster_ / loadAllocRules_ の役割
- pjMap / pjAcctMap / allocatedByPj の構造
- plSections(L540-552)の並び
- 78_pj_pl 出力(L700-747)の列構成: A列=表示区分, B列=配賦元合計, C列以降=PJ列, 最終列=全社合計
- 77_pj_raw 出力の列構成(L749-759)
1-C. 100_config/101_sys_config.js を Read し、以下を確認する:
- onOpen() 内で Constants.MENU_DEFINITION をループしてメニューを動的生成している(L323-349)
- サイドバー側メニュー定義は 002_constants.js 側の MENU_DEFINITION に存在(L230-238 の「📋 サイドバー: 📊 マート更新」カテゴリに buildProjectProfitability を含む)
- setupAllSchemas の schemas オブジェクト(L826-901)に TDABC 用シートは追加しない(DDL 対象外)
- CLAUDE.md の「DDL (setupAllSchemas) で管理されないタブ」セクションに 79_pj_tdabc_calc を追記する必要あり
1-D. 200_data/202_repository.js を Read し、以下を把握する:
- readSheetAsDtos_(sheet) → { headers, dtos } を返すヘルパー(L19-29)
- OrderRepository / JournalRepository の _getSheet / findAll パターン(L107-146)
- AccountRepository.findAsMap() はキャッシュ付き。戻り値は { 科目名: { stmt, cat } }(L323-341)
- TimesheetRepository / HeadcountRepository は未実装。本案件では 421_tdabc_allocator.js 内に定義して完結させる(202_repository.js は変更しない)
1-E. 000_infra/004_utils.js を Read し、以下のシグネチャを把握する:
- Utils.parseAmt(val) → number
- Utils.parseDateToYm(val) → "YYYY-MM"
- Utils.logInfo(funcName, message)
- Utils.logError(funcName, error, context)
- Utils.toastResult(funcName, message, duration)
- Utils.getSheetByKey(key, fallbackName)
1-F. 000_infra/002_constants.js を Read し、SHEET_DEFAULTS の 22_bud_headcount エントリ(L76)と MENU_DEFINITION(L206-324)を確認する。
また MCP(または実機)で以下を必ず確認する:
- 11_mst_account の「大分類(cat)」実値の一覧。「人件費」という値が実在するかどうかを検証する。実在しなければ、420_project_profitability.js L136 と同じロジック(科目名に '給料' / '役員報酬' / '法定福利' / '雑給' のいずれかを含む)で識別する
- 22_bud_headcount に月次稼働時間列が存在しないことを再確認(DDL 上は無い)。03_sys_params の既存キー一覧を確認し、MONTHLY_STD_HOURS キーが未使用であることを確認
1-G. 78_pj_pl の列構造を MCP で実データ読み取りし、「■ 共通費配賦」行・「【共通費配賦 計】」行・「✨ PJ営業利益」行の現状の行番号を記録する(ラベル完全一致でスキャンして取得する動的検出の実装に反映)。C列以降の PJ 順序は 78_pj_pl の行2(PJ名)で取得する。
【修正対象ファイル】
- 400_domain/421_tdabc_allocator.js(新規作成。modules/ サブディレクトリは使用しない。CLAUDE.md のファイル番号体系に従い 400_domain/ 直下)
- 100_config/101_sys_config.js(MENU_DEFINITION への 2項目追加と DDL 非管理タブのコメント追記)
- CLAUDE.md(「DDL (setupAllSchemas) で管理されないタブ」節に 79_pj_tdabc_calc を追記)
【制約】
- 400_domain/420_project_profitability.js の既存ロジックを一切変更しない(MAS-006 機能を保持)
- 200_data/202_repository.js の既存 Repository を変更しない(TimesheetRepository / HeadcountRepository は 421_tdabc_allocator.js 内に閉じる)
- DDL の schemas オブジェクトには 79_pj_tdabc_calc を登録しない(setupAllSchemas 対象外)
- 科目マスタに未登録の科目名はエラー(CLAUDE.md: キーワード推測による自動分類禁止)
【運用フロー(2ステップ)】
1. 「🕐 TDABC計算実行」メニュー → runTdabcCalculation() が 79_pj_tdabc_calc に出力(総コスト/総供給時間/時間単価/PJ別消費時間/PJ別配賦額/未配賦コスト)
2. ユーザーが 79_pj_tdabc_calc を目視確認
3. 「🕐 TDABC→78タブ反映」メニュー → applyTdabcToPl() が 78_pj_pl の共通費配賦セクションに「🕐TDABC(工数配賦)」行を差し込む。冪等性保証:実行前に前回行を削除
【計算ロジック(3ステップ)】
1. 時間単価(キャパシティレート):
- JournalRepository.findAll() で実績仕訳を取得
- AccountRepository.findAsMap() で人件費科目を識別(1-F の結果に基づく)
- 科目マスタに人件費科目が 0件ならダイアログ警告して処理中断
- 月別に「人件費合計」を集計
- HeadcountRepository.findAll() と 03_sys_params から月次の総供給時間を算出(案 A: 在籍要員数 × MONTHLY_STD_HOURS を採用。ユーザーが別案を指示した場合はその案で実装)
- 時間単価 = 人件費合計 ÷ 総供給時間(総供給時間=0 の場合は時間単価=0 でゼロ除算回避)
2. PJ別消費時間の集計:
- TimesheetRepository.findAll() を呼ぶ
- シート未存在(MAS-105 未実装)はダイアログ「工数データシート(43_trn_timesheet)が見つかりません。案件MAS-105の実装が必要です。」で処理中断
- { PJ名: { 年月: 消費時間h } } マップを構築
3. PJ別配賦額:
- PJ別配賦額 = PJ別消費時間 × 時間単価(Math.round で整数丸め)
- 79_pj_tdabc_calc に書き出し(列構成は仕様書の Step 1-c を参照)
【78_pj_pl への反映(差引設計)】
- 共通費配賦セクション(「■ 共通費配賦」〜「【共通費配賦 計】」)の行範囲を検出
- 既存の「🕐TDABC(工数配賦)」行があれば削除(冪等性)
- 「【共通費配賦 計】」行の直前に新規行を挿入し:
- A列(表示区分)= " 🕐TDABC(工数配賦)"
- B列(配賦元合計)= +(全PJ配賦額の合計)
- C列以降の各PJ列 = -(該当PJの配賦額)
- 「【共通費配賦 計】」「✨ PJ営業利益」以降の利益行は数値再計算して上書き
- 目的:共通費から TDABC 分を差し引き、PJ 側に付け替える(二重計上防止)
【エッジケース】
- 総供給時間=0 → 時間単価=0、全PJ配賦額=0、備考列に警告
- 人件費合計=0 → 時間単価=0、全PJ配賦額=0(正常ケース)
- 特定月の工数データ全件未入力 → 全PJ消費時間=0、未配賦時間=総供給時間
- 科目マスタに人件費科目未登録 → ガードで検出しダイアログ警告して処理中断
- 43_trn_timesheet 未存在 → ダイアログ「MAS-105の実装が必要」で処理中断
- 全PJ消費時間 < 総供給時間 → 未配賦コスト = (総供給時間 − PJ消費時間合計) × 時間単価 を 79 タブ月別合計行に明示。78 タブでは「共通費」として残す
- 全PJ消費時間 > 総供給時間 → 分母は総供給時間で固定。79 タブ備考列に警告
- 計算実行を複数回 → 79 タブを毎回 clear() して再出力(冪等)
- 78タブ反映を複数回 → 既存 TDABC 行を削除してから書き込み(冪等)
- MAS-006 の後に applyTdabcToPl 未実行 → MAS-006 再生成で TDABC 行が消えることをダイアログで告知
- 14_mst_project 未登録の PJ 名が工数にあり → PJコード列を空で出力するが配賦計算は実施
【動作確認手順】
1. npm run push:dev で dev 環境にデプロイ
2. GAS エディタで対象スプレッドシートを開く
3. サイドバー「📊 マート更新」→「プロジェクト別 採算」を実行(MAS-006 の 78 タブを再生成)
4. サイドバー「📊 マート更新」→「🕐 TDABC計算実行」を実行
→ 79_pj_tdabc_calc タブが生成されることを確認(列構成・月次合計行・時間単価の妥当性)
5. 79_pj_tdabc_calc の内容を目視確認
6. サイドバー「📊 マート更新」→「🕐 TDABC→78タブ反映」を実行
→ 78_pj_pl の共通費配賦セクションに「🕐TDABC(工数配賦)」行が追加されていることを確認
→ B列(配賦元合計)= 全PJ配賦額合計(正の値)
→ 各PJ列 = −PJ別配賦額(負の値)
→ 【共通費配賦 計】・✨ PJ営業利益以降の値が再計算されていることを確認
7. 同じ「🕐 TDABC→78タブ反映」をもう一度実行し、78 タブが変わらない(冪等)ことを確認
8. 43_trn_timesheet が未作成の環境で runTdabcCalculation を実行し、ダイアログが出て処理中断することを確認
9. npm run push:prod は実施しない(本番反映はユーザー判断)
【完了後のコミット】
git add 400_domain/421_tdabc_allocator.js 100_config/101_sys_config.js CLAUDE.md
git commit -m "feat(MAS-026): add TDABC cost allocator (421_tdabc_allocator.js)"
推奨実行モデル
| 工程 | 推奨モデル | 理由 |
|---|---|---|
421_tdabc_allocator.js の新規作成(Repository 新設・TDABC 計算ロジック・2 関数分割・78タブ差込) | Claude Sonnet | 複数ファイル参照(420_project_profitability.js の出力構造・202_repository.js の Repository パターン)が必要。会計ロジック理解も一定必要だが、仕様書で構造が明確に定義済み |
100_config/101_sys_config.js メニュー追加 | Claude Haiku | 挿入位置(MENU_DEFINITION の「📊 マート更新」カテゴリ、buildProjectProfitability 直後)が仕様書で明示済み。判断要素少 |
CLAUDE.md の DDL 非管理タブ節への追記 | Claude Haiku | 追記位置・文字列が仕様書で明示済み |
変更履歴
| 日時 | 変更内容 |
|---|---|
| 2026-04-21 | 初版作成。TDABC 計算ロジック(時間単価 × PJ別消費時間)・2ステップ運用フロー(79_pj_tdabc_calc → 78_pj_pl)・MAS-105 依存設計・冪等性保証を定義 |
仕様書作成プロンプト(展開して表示)
本仕様書は以下のプロンプト(tasks/prompts/task_F-26.md の <instruction> タグ内)に従って作成された。
【タイムアウト回避・実行原則(v1.7・必ず遵守すること)】
1. 拡張思考の使い分け: Phase 1(設計)では拡張思考をフル活用し、ファイル名形式・エッジケース一覧・Step 分割粒度・固有名詞(関数名/シート名/列名/行番号)を完全に確定させる。Phase 2(清書)の各 Step 内では拡張思考を最小限に抑え、Phase 1 で確定済みの内容の書き下しに徹する。出力途中で再考しない。
2. テキスト報告の禁止: 「〜を作成します」等の text のみで tool_use なしに turn を終了しない。説明は 1 文以内。直ちに tool を呼ぶ。
3. 4-5 分割の Write/Edit 実行: 仕様書は 2-1(骨格)/2-2(概要〜注意事項)/2-3a(エッジケース〜人間検討事項)/2-3b(実装プロンプト〜変更履歴)/2-4(details 記録) の 5 Step に分割して実行。1 回の Write/Edit は約 300 行以内。
4. 各 Step で何を書くかを事前確定: Phase 1 で確定した内容だけを Phase 2 で書き出す。Phase 2 実行中に設計判断を持ち込まない。
あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者兼仕様書ライターです。
案件 MAS-026「TDABC(時間主導型ABC:活動基準原価計算)」の開発仕様書を作成してください。
仕様書を新規作成した後は、docs/_config.json の nav 配列の §E.5(FP&A・レポーティング)セクションに必ず追記してください。
【Phase 1: 実行前タスク】
以下を全て調査してから Phase 2 に進むこと。Grep は「どこにあるか」の発見まで。「どう書くか」の判断は必ず Read で行う。変数名・関数名・シート名・メニュー名は実在する文字列のみ引用すること。
1-A. 案件定義の読み込み
docs/_internal/TODO_future.md を検索し、MAS-026・MAS-006・MAS-105・MAS-027 の案件名/概要/人間が検討すべき事項を把握する。特に MAS-105 の実装状況(43_trn_timesheet シートが既に存在するか)は MAS-026 の設計方針(工数データ取得方法)に直結するため必ず確認する。
1-B. 既存配賦ロジックの把握
400_domain/420_project_profitability.js を Read する。確認事項:
- 全体の関数構成と呼び出し順序
- 77_pj_raw / 78_pj_pl への書き込み方法(列ヘッダー名・行位置の実態)
- MAS-006 で実装した共通費配賦ロジックの具体的な計算処理
- 既存の変数名・定数名(Phase 2 で引用するため正確に記録する)
1-C. メニュー構造とDDL登録箇所の確認
100_config/101_sys_config.js を Read する。確認事項:
- onOpen() 内の ui.createMenu で TDABC メニューを追加すべき位置(実在するメニュー名を引用すること)
- setupAllSchemas / DDL 定義で新規シートを登録する箇所のパターン
- CLAUDE.md「DDL で管理されないタブ」コメントの記載場所
1-D. Repository パターンの把握
200_data/202_repository.js を Read する。確認事項:
- JournalRepository.findAll() / AccountRepository.findAsMap() の引数・戻り値・実装パターン
- readSheetAsDtos_() / appendDtosToSheet_() 等の内部ヘルパー関数のシグネチャ
- 新設する TimesheetRepository(43_trn_timesheet 用)と HeadcountRepository(22_bud_headcount 用)の実装テンプレートを決定する
1-E. Utils ヘルパーの確認
000_infra/004_utils.js を Read する。確認事項:
- Utils.parseAmt / Utils.parseDateToYm / Utils.logInfo / Utils.logError / Utils.toastResult の引数と戻り値
1-F. 人件費科目の実態確認
000_infra/002_constants.js を Read し、SHEET_DEFAULTS の 22_bud_headcount エントリのフィールド名(「月額給与・報酬」「雇用形態」「開始年月」等)を確認する。
次に、MCP または Grep で 11_mst_account シートを参照し、AccountRepository.findAsMap() が返す { stmt, cat } のうち人件費関連科目を識別するための 実際の cat 値を特定する(「人件費」という文字列が実際に使われているかを確認し、仮定で記載しない)。
また、22_bud_headcount の月次稼働時間に相当するフィールドが存在するか確認する。存在しない場合は「総供給時間の算出方法」を「人間が検討すべき事項」に昇格させる。
1-G. 出力先シートの列構造確認
MCP または 420_project_profitability.js の Read 結果から 78_pj_pl の列ヘッダーと、共通費・各 PJ コストが格納される行番号の実態を確認する。TDABC 配賦額を書き込む列・行の位置を特定する。
【Phase 2: 仕様書の分割作成】
出力先: docs/dev/dev_mas-026_tdabc_cost_allocation.md
絶対に 1 回の tool_use で全内容を出力しない。以下の Step に必ず分割すること。
Step 2-1: 骨格の作成(Write, ~20行)
全セクションの見出しのみ。本文は空欄で可。
Step 2-2: 前半セクションの追記(Edit または Bash heredoc, ~300行)
記載するセクション: 概要 / 目的 / 現在のコード / 修正方針 / 影響範囲 / 注意事項
アーキテクト指示:
- 新規ファイル: 400_domain/421_tdabc_allocator.js(modules/ プレフィックスは付けない。CLAUDE.md のファイル番号体系に従い 400_domain/ 直下に配置。現在 420_project_profitability.js まで使用済みのため次番 421 を使用)
- 新規シート: 79_pj_tdabc_calc(計算中間結果の確認用。CLAUDE.md の「DDL で管理されないタブ」に該当するため 101_sys_config.js のコメントに追記。DDL の setupAllSchemas 対象外)
- 運用フロー(2ステップ構成):
1. 「TDABC計算実行」メニュー → 79_pj_tdabc_calc に以下を出力: 総コスト/総供給時間/時間単価/PJ別消費時間/PJ別配賦額/未配賦コスト(全列を明記)
2. ユーザーが 79_pj_tdabc_calc を目視確認
3. 「78タブへ反映」メニュー → 78_pj_pl に転記(冪等性保証: 実行前に前回 TDABC 反映分をクリアしてから書き込む)
- 計算ロジック(3ステップ):
1. 時間単価(キャパシティレート): JournalRepository.findAll() で取得した実績仕訳のうち AccountRepository.findAsMap() で特定した人件費科目(Phase 1-F で確認した実際の cat 値)を絞り込み月次合計を算出。22_bud_headcount から月次の総供給時間を算出(算出方法は Phase 1-F の確認結果に基づく。フィールドが存在しない場合は「人間が検討すべき事項」に記載)。時間単価 = 人件費合計 ÷ 総供給時間(総供給時間=0の場合は時間単価=0としてゼロ除算回避)
2. PJ別消費時間の集計: MAS-105 で導入される 43_trn_timesheet シートから TimesheetRepository.findAll()(421_tdabc_allocator.js 内に新設、MAS-105 実装後に 202_repository.js へ移管予定)でPJ別作業時間を集計。シートが存在しない場合(MAS-105 未実装)はガード処理を設け、ダイアログを表示して処理を中断する
3. PJ別配賦額 = PJ別消費時間 × 時間単価
- 二重計上防止: 78_pj_pl への反映時は、Phase 1-G で確認した共通費行から TDABC 配賦額合計をマイナスし、各 PJ 行にプラスする差引設計
- 新設 Repository の配置方針: TimesheetRepository(43_trn_timesheet 用)・HeadcountRepository(22_bud_headcount 用)はともに 400_domain/421_tdabc_allocator.js 内に定義し、202_repository.js の OrderRepository のパターン(_getSheet / findAll)に倣う。MAS-105 実装確定後に 200_data/202_repository.js へ移管することを注記する。既存の 202_repository.js は変更しない
Step 2-3a: エッジケース〜人間検討事項の追記(Edit または Bash, ~200行)
記載するセクション: エッジケース / 実データ検証 / 関連ドキュメント / 人間が検討すべき事項
エッジケーステーブル(必須、以下全行を含める):
- 総供給時間 = 0
- 人件費合計 = 0
- 特定月の工数データが全件未入力
- 科目マスタに人件費科目が未登録
- 43_trn_timesheet シートが存在しない(MAS-105 未実装)
- 全PJ消費時間合計 < 総供給時間(未配賦時間あり)
- 「78タブへ反映」を複数回実行
実データ検証セクション項目:
- 11_mst_account で人件費カテゴリの cat 実値を確認
- 22_bud_headcount の列ヘッダー一覧と、月次稼働時間に相当するフィールドの有無
- 78_pj_pl の列ヘッダーと、共通費・各PJコストが格納される行番号の実態
Step 2-3b: 実装プロンプト〜変更履歴の追記(Edit または Bash, ~250行)
記載するセクション: 実装プロンプト(Claude Code 用)/ 推奨実行モデル / 変更履歴
実装プロンプトのフォーマット規則: コードブロック(バッククォート)禁止。行頭スペース4つのインデントで出力。
実装プロンプトに含める必須項目:
- 役割定義・案件ID・ステップ名
- 実行前タスク: Phase 1 の 1-A〜1-G と同じ調査内容を再掲(別セッションでも自己完結するよう全内容を記載)
- 修正対象ファイル: 400_domain/421_tdabc_allocator.js(新規作成)、100_config/101_sys_config.js(メニュー追加・DDL コメント追記)
- 制約: 400_domain/420_project_profitability.js の既存ロジックを変更しない。200_data/202_repository.js の既存 Repository を変更しない
- エッジケース: Step 2-3a のテーブルをそのまま転記
- 動作確認手順: npm run push:dev → GAS エディタで「TDABC計算実行」メニュー実行(Phase 1-C で確認した実在するメニュー名を使用)→ 79_pj_tdabc_calc 生成確認 → 「78タブへ反映」実行 → 78_pj_pl 差分確認 → 同操作を 2 回実行して冪等性確認
推奨実行モデルテーブル:
- 421_tdabc_allocator.js 新規作成(Repository・計算ロジック)→ Claude Sonnet
- 101_sys_config.js メニュー追加 → Claude Haiku
Step 2-4: 仕様書作成プロンプトの記録(Edit または Bash)
末尾に details ブロックを設け、この instruction 全文を記録する。
【Phase 3: 後処理】
1. docs/_config.json への追記: nav 配列の §E.5(FP&A・レポーティング)セクションに追加:
{ "file": "dev/dev_mas-026_tdabc_cost_allocation.md", "title": "E.5.X MAS-026 TDABC(時間主導型ABC:活動基準原価計算)" }
追記後、JSON 構文エラーがないことを確認する(末尾カンマ・括弧の対応を目視チェック)。
2. docs/_internal/changelog.md への追記: ヘッダー直後の先頭行に追記:
| 2026-04-20 | [dev_mas-026_tdabc_cost_allocation.md](dev_mas-026_tdabc_cost_allocation.md) | 初版作成。TDABC計算ロジック・2ステップ運用フロー・MAS-105依存設計を定義 |
3. git コミット:
git add docs/dev/dev_mas-026_tdabc_cost_allocation.md docs/_config.json docs/_internal/changelog.md
git commit -m "docs: MAS-026 TDABC活動基準原価計算の開発仕様書を作成"
git push -u origin (現在のブランチ)