概要

項目内容
案件IDMAS-034
カテゴリR&D
PhaseP2
優先度★★
所要時間3-4時間
対象ファイル100_config/101_sys_config.jsDDL14_mst_project スキーマ拡張)
000_infra/002_constants.jsMENU_DEFINITION にR&D集計メニュー2項目追加)
600_report/611_rd_cost_aggregator.js(新規:下書き集計+仕訳確定)
docs/_internal/memory/(変更なし)
前提案件特になし(14_mst_project / 32_wrk_invoice / 42_trn_journal は既存。InvoiceRepository / JournalRepository200_data/202_repository.js 実装済み)

目的

R&D活動に紐づく費用(外注開発費・クラウド利用料・消耗品費等)を 14_mst_project のR&Dフラグと紐付けて自動抽出し、研究フェーズ(費用処理)と開発フェーズ(資産計上候補)に区分した下書きを 69_rd_summary に出力、人間のレビュー後に 42_trn_journal へ区分情報を持った仕訳としてマーキング(管理IDプレフィックス RD_F34_)する。これにより研究開発税額控除の適用漏れ防止と、R&D投資額の正確な集計を実現する。

現在のコード

項目現状参照箇所
14_mst_project のスキーマ14列(有効フラグ / PJコード / プロジェクト名 / PJ小区分 / PJ大区分 / 社内外 / 資産化 / 顧客・取引先名 / PJ区分 / 契約形態 / ステータス / 資産化対象 / PM・責任者名 / 配賦区分100_config/101_sys_config.js:836'MST_PROJ' エントリ
InvoiceRepository.findAll()INV{ headers, dtos } 形式で取得。ヘッダー→DTOは Contracts.toDto() 経由200_data/202_repository.js:163
JournalRepository.append()DTO配列をシート末尾に追記。InvoiceRepository.append() と異なり科目マスタ照合・PJデフォルト処理は行わない200_data/202_repository.js:291
readSheetAsDtos_()Repository 内部共通ヘルパー。同パターンを readProjectMaster_() として流用する200_data/202_repository.js:19
PJマスタ読み込みの既存パターンloadPjMaster_(ss)code / allocType / pm の3フィールド返却)が 420_project_profitability.js:803 に存在。本仕様では新フィールドを加えた専用関数を 611_rd_cost_aggregator.js 内に実装する
メニュー登録000_infra/002_constants.jsMENU_DEFINITION で宣言的に管理。onOpen() が自動展開(100_config/101_sys_config.js 側の直接編集は不要)

修正方針

Step 1: DDL変更(14_mst_project スキーマ拡張)

100_config/101_sys_config.js:836'MST_PROJ' スキーマ定義 headers 配列末尾に以下2列を追記する。既存14列の後ろに追加するため、Nカラム(14)→Pカラム(16) となる。

'MST_PROJ': { headers: [
  "有効フラグ","PJコード","プロジェクト名","PJ小区分","PJ大区分","社内外","資産化",
  "顧客・取引先名","PJ区分","契約形態","ステータス","資産化対象","PM・責任者名","配賦区分",
  "R&D対象フラグ","R&Dフェーズ"
], color: "#666666" },
  • R&D対象フラグ: TRUE / FALSE のブーリアン(既存の 有効フラグ と同じフォーマット)
  • R&Dフェーズ: 研究 / 開発 / (空欄) のいずれか。空欄は非R&D案件、または研究開発税制の判定保留として扱う
  • DDL実行後、既存行は新列が空欄(=R&D対象外扱い)となる。既存データへの破壊的影響なし

Step 2: R&D費用集計(下書き生成・Human-in-the-Loop)

新規ファイル 600_report/611_rd_cost_aggregator.js に公開関数 buildRdSummary() を実装する。

処理フロー:

  1. 14_mst_projectreadProjectMaster_()(新規・readSheetAsDtos_ と同パターン)で読み込み、{ [プロジェクト名]: { phase: '研究'|'開発', rdFlag: true } } マップを構築。有効フラグ=FALSE の行はスキップ。R&D対象フラグ=TRUE の行のみマップに登録
  2. InvoiceRepository.findAll()32_wrk_invoice 全件取得
  3. 次の条件を全て満たすINVのみ抽出:
    • 有効フラグ !== falseFALSE 文字列も弾く)
    • 請求ステータス === '承認済'(Phase 1で 002_constants.js:85003_contracts.js:49 にて確認済)
    • PJ名 がR&Dマップのキーに一致
  4. 抽出INVを { [PJ名]: { [phase]: { [科目名]: { amount, count, invIds[] } } } } にバケット集計(amount税抜金額_計画Utils.parseAmt() で数値化した総和)
  5. 新設シート 69_rd_summary に以下のヘッダーで書き出し(動的生成・毎回 clearContents() → 全行再書き込みで冪等性確保):
    • R&Dフェーズ / PJ名 / 科目名 / 件数 / 税抜金額合計 / 対象INV_ID一覧(カンマ区切り) / 確認FLG / 備考
    • 確認FLG 列は空欄で出力。ユーザーが手動で OK を入力した行だけが Step 3 の対象
  6. フィルター自動設定(行1にフィルター)+ラベル列以外の数値・日付列のみ列幅自動調整
  7. ゼロ件時はヘッダーのみ書き出し、SpreadsheetApp.getUi().alert で「R&D対象INVが見つかりませんでした」を表示

Step 3: 仕訳確定(管理IDマーキング)

同ファイル内の公開関数 confirmRdJournal() を実装する。

処理フロー:

  1. 69_rd_summary を読み込み、確認FLG === 'OK' の行のみ対象とする
  2. JournalRepository.findAll()42_trn_journal を読み込み、管理ID 列に RD_F34_ プレフィックスを持つ既存レコードの 摘要 からINV_IDを抽出し Set に登録(二重計上防止ロック)
  3. 各対象行の 対象INV_ID一覧 をカンマ分割し、各INV_IDについて:
    • Set に既に含まれていればスキップ(ロック済み)
    • 新規であれば、対応するINVから新規 JournalEntryDTO を生成。管理ID には RD_F34_<INV_ID>_<YYYYMMDD> を設定、摘要 には [R&D:<phase>] <元INVの摘要> を付与、他フィールドは元INVから転記(発生日(P/L計上日) / 取引先名 / 科目名 / 税区分 / 通貨 / 税抜金額_実績 / 消費税額_実績 / 税込金額_実績 / 組織名 / PJ名 / 決済手段 / 証憑URL)。仕訳ステータス手動 を設定(仕訳振替 ではない。Phase 1で 003_contracts.js:114 確認済)
    • 実績金額は元INVの計画金額(税抜金額_計画 / 消費税額_計画 / 税込金額_計画)をそのまま転記(R&D集計は実績寄せで運用する。元INVは既にAction Aで通常仕訳済みのため、本仕訳はあくまで「R&D区分情報を持つマーキング仕訳」として機能する)
  4. JournalRepository.append(dtos) で一括追記
  5. 結果を Utils.toastResult で通知+Utils.auditLog('CREATE', '42_trn_journal', '', '', 'confirmRdJournal', null, dtos.length, 'R&D仕訳確定: ' + 件数) に記録

アーキテクチャ決定事項

項目決定理由
69_rd_summary のDDL管理DDL非管理タブとして扱う(CLAUDE.md の「DDL (setupAllSchemas) で管理されないタブ」リストに追記)77_pj_raw / 78_pj_pl 同様、毎回 clearContents() → 全行再書き込みで冪等性を確保する動的生成シートのため
PJマスタ読み込み関数611_rd_cost_aggregator.js 内に readProjectMaster_()readSheetAsDtos_ 同パターン)を新規実装202_repository.jsProjectRepository は未実装。420_project_profitability.js:803loadPjMaster_()code / allocType / pm の3フィールド専用で本仕様の R&D対象フラグ / R&Dフェーズ を扱えないため、専用関数として分離
メニュー登録000_infra/002_constants.jsMENU_DEFINITION📋 サイドバー: 📊 マート更新 カテゴリ末尾に「🔬 R&D費用を下書き集計」「🔬 R&D仕訳を確定」の2項目を追加📋 サイドバー: 📊 マート更新 にF-28 SaaS KPI・F-23 月次コメント・F-26 TDABC等の類似アグリゲーションが既に集まっており、宣言的メニュー(onOpen() が自動展開)の既存規約に合致するため
分類キーのSSoT14_mst_projectR&Dフェーズインライン判定ロジック(科目名や摘要からのキーワード推論)を持たない。分類変更時はマスタ1箇所の修正で全ての下流処理に反映される
仕訳の性質RD_F34_ プレフィックスを持つ「R&D区分マーキング仕訳」。通常仕訳(Action A/B由来)と重複する可能性があるため、P/LB/Sマート集計時は本仕訳を除外する運用を想定。除外ロジック実装の要否は本仕様のスコープ外(F-38 研究開発税制シミュレーションで連携)既存マート生成(601_datamart_ingest 以降)は全仕訳を対象に集計するため、本仕訳をそのまま流すと二重計上が発生する
前払費用・期ずれの扱い本仕様ではINVの 発生日(P/L計上日) ベースで集計するのみ。期ずれ処理は行わない税額控除判定は原則として発生年度ベースであり、F-34はあくまで下書き集計。厳密な期間帰属は会計担当者判断事項として「人間が検討すべき事項」に記載

影響範囲

ファイル変更種別変更量
100_config/101_sys_config.js既存スキーマ拡張('MST_PROJ'headers に2列追加)1行変更(+2要素)
000_infra/002_constants.jsMENU_DEFINITION📋 サイドバー: 📊 マート更新 カテゴリに2メニュー項目追加+2行
600_report/611_rd_cost_aggregator.js新規ファイル作成(buildRdSummary() / confirmRdJournal() / readProjectMaster_() / renderRdSummarySheet_()+200〜250行
CLAUDE.md「DDL (setupAllSchemas) で管理されないタブ」リストに 69_rd_summary を追記+1要素
14_mst_project シートDDL再実行で新列2列が追加される(既存データ保持)データ移行不要(新列は空欄で初期化)
docs/_config.json§E.5 FP&A・レポーティング に本仕様書エントリを追加+1行
docs/_internal/changelog.md先頭行に初版作成の変更履歴を追加+1行

注意事項

  1. PJマスタ読み込みに readSheetAsDtos_() を直接呼ばない: 200_data/202_repository.js:19 に定義されているが、GASの慣習でアンダースコア末尾関数はプライベート扱い(ファイル跨ぎ呼び出し不可ではないが設計意図として非公開)。611_rd_cost_aggregator.js 内に同ロジックの readProjectMaster_() を実装する
  2. JournalRepository.append() は科目マスタ照合・PJデフォルト設定を行わない: InvoiceRepository.append()AccountRepository.findAsMap()諸表区分 / 大分類 を自動付与するのと異なり、JournalRepository.append() はDTOをそのまま書き込む(200_data/202_repository.js:291 確認済)。仕訳DTOは呼び出し元で全フィールドを完全に設定してから渡すこと
  3. 列名・関数名・メニュー名は全てPhase 1でReadした実在する文字列のみ使用: 失敗パターン #18-#20(コード未読による固有名詞誤記)を防ぐため、造語禁止。シート名 32_wrk_invoice / 14_mst_project / 42_trn_journal / 69_rd_summary、列名 請求ステータス / PJ名 / 税抜金額_計画 / 管理ID / 仕訳ステータス は全て実装確認済みの文字列
  4. 請求ステータス === '承認済' の完全一致: 420_project_profitability.js:111'承認済' / '部分決済' / '決済完了' の3種を許容している例があるが、F-34では下書き集計の性質上 '承認済' のみを対象とする(承認前のINVは金額変動の可能性が高いため)
  5. 二重計上防止ロック: Step 3 の冪等性は JournalRepository.findAll() から 管理ID プレフィックス RD_F34_ を持つレコードを Set でメモリ管理する方式(失敗パターン #16 の「合算マッチ成功時のロック処理・matched=true による二重消費防止」と同思想)
  6. 14_mst_project の追加列はDDL実行で初期値空欄: 既存PJについてR&D対象フラグ・フェーズ設定は手動入力が必要。Step 1 実行後、ユーザーに「既存PJのR&D属性を手動設定してください」とアナウンスする
  7. 新シート 69_rd_summary の番号衝突リスク: F-28 69_kpi_saas(2026-04-20 仕様書作成済・未実装)と同じ 6X 番台帯を使用する。どちらも動的生成タブで物理シート名は異なるため技術的衝突はないが、将来の番号採番ガイドラインで見直す可能性あり(人間検討事項に記載)

エッジケース

条件表示値/動作理由
R&D対象INVが存在しない(14_mst_projectR&D対象フラグ=TRUE のPJが1件もない、または該当PJにINVが1件もない)69_rd_summary はヘッダー行のみ書き出し、仕訳生成なし。SpreadsheetApp.getUi().alert('R&D対象INVが見つかりませんでした') を表示して早期リターン空データでも落ちず、利用者に状況を明確に伝える。ゼロ件チェックはステップ早期で実施し無駄な処理を避ける
マイナス金額のINV(返金・修正仕訳相当)Utils.parseAmt() が符号を保持するため集計額に減算として反映される。件数 は絶対値換算せず1件としてカウント返金処理の会計実態を正しく反映する。R&D費用の返金があった場合の年間純額把握に必須
INVの PJ名 が空(PJマスタ未設定)'指定なし_共通費など' として扱い、R&Dマップに含まれないためスキップされる(=集計対象外)InvoiceRepository.append() の既存デフォルト値(202_repository.js:202)と統一。空PJは意図的にR&D対象外とする保守的判断
14_mst_projectR&D対象フラグ=TRUE だが R&Dフェーズ が空欄のPJデフォルト 研究 として扱い、下書きに「フェーズ未設定」列を立てる。備考列に ⚠️ R&Dフェーズ未設定(研究として仮集計) を表示開発フェーズは資産計上候補で影響大のため保守的に費用処理(研究)側に倒す。人間のレビューで補正可能
Step 3 を重複実行した場合JournalRepository.findAll()管理ID プレフィックス RD_F34_ を持つ既存レコードをメモリにSet化し、既処理のINV_IDをスキップ。スキップ件数を Utils.toastResult で通知失敗パターン #16 の「matched=true による二重消費防止」と同思想。ロールバックではなく二重実行ガードによる冪等性確保
承認前(請求ステータス !== '承認済')のINV集計対象外としてスキップ金額確定前のINVを集計に含めると人間レビュー後の確定仕訳との乖離が発生する。承認済のみが会計確定データのSSoT
請求ステータス === '却下' のINV集計対象外としてスキップ(上記と同じ分岐で処理)却下INVは有効データではない。明示的に除外
確認FLG === 'OK' 以外の値(ok / / 空欄 / 全角OK 等)完全一致'OK' のみを対象。それ以外はスキップ表記ゆれによる意図しない仕訳計上を防ぐ。CLAUDE.md「コーディング規約 > 仕訳振替の判定は === "仕訳振替" の完全一致」と同じ設計思想
対象INV_ID一覧 がカンマ区切りで複数INV_IDを含む場合、一部のINV_IDだけがロック済みロック済みINV_IDはスキップ、未処理INV_IDのみ仕訳化。行単位ではなくINV_ID単位でロック判定部分的な再実行に対応。バッチ途中で落ちた場合の再開を容易にする
42_trn_journal管理ID 列が存在しない(DDL未実行環境)JournalRepository.findAll()dtos[i]['管理ID']undefined になるため、Set登録時は `String(value
69_rd_summary シートが未作成ss.getSheetByName('69_rd_summary')null の場合 ss.insertSheet('69_rd_summary') で新規作成初回実行時の自動セットアップ。DDL非管理タブのため明示的に生成する
14_mst_projectR&D対象フラグ 列が存在しない(Step 1 未実行環境)readProjectMaster_() 内で列が見つからない場合は空マップを返し、buildRdSummary() は「R&D対象INVが見つかりませんでした」分岐に入るStep 1 のDDL未実行時でも落ちず、ユーザーにDDL実行を促すUIに誘導する

実データ検証

MCP 等で実装前に以下を確認する:

  1. 14_mst_project の実際の列名一覧: SHEET_DEFAULTS / MST_PROJ スキーマ定義(101_sys_config.js:836)と実シートのヘッダーが一致していること。追加する R&D対象フラグ / R&Dフェーズ が既存列と重複していないことを確認
  2. 32_wrk_invoice請求ステータス 列の実際の格納値: '承認済' の文字列表記を確認(全角空白・末尾改行等がないこと)。Utils.getSheetByKey('WRK_INVC', '32_wrk_invoice') 経由で取得した実データで検証
  3. 42_trn_journal管理ID 列に既存の RD_F34_ プレフィックスを持つレコードがないこと: 初回実行前にクリーンな状態であることを確認。既存レコードがある場合は過去の実験実装を削除するか、プレフィックス衝突を回避する変更が必要
  4. 42_trn_journal仕訳ステータス 列のバリデーションドロップダウン: '手動' が選択可能な値に含まれていることを確認(003_contracts.js:114 では '自動計上' | '手動' | '仕訳振替' が型コメント。実シートのデータバリデーション設定と一致しているか setupAllSchemas で確認)
  5. 14_mst_project のR&D対象候補PJ: 実データに「研究開発」「R&D」「開発」等を含むPJ名がどれだけ存在するか事前にカウント(R&Dフラグの手動入力対象件数の見積もり)

関連ドキュメント

仕様書関連箇所
CLAUDE.md「DDL (setupAllSchemas) で管理されないタブ」に 69_rd_summary 追記。「変更時の動作確認テスト」対象に追加(100_config/101_sys_config.js 変更時 → setupAllSchemas → 全テスト)
F-06 PJ別管理会計の共通費配賦PJマスタ読み込みの既存パターン (loadPjMaster_()) の参考。本仕様は R&D対象フラグ / R&Dフェーズ 専用読み込み関数を別途実装
F-37 ソフトウェア資産の減価償却管理開発フェーズで資産計上対象となった支出の償却処理。本仕様の下書きが資産計上候補の入口となる
F-38 研究開発税制の適用シミュレーション本仕様で集計した 69_rd_summary を入力とし、税額控除額を自動計算する後続案件
C.5.5 研究開発税制の節税効果試算 (2026年度〜)社内の節税効果試算メモ。本仕様で実運用値に置き換える
F.4 実装失敗パターン一覧#16 ロック処理による二重消費防止 / #18-#20 コード未読による固有名詞誤記(本仕様は Phase 1 で全固有名詞をRead確認済)
A.9 活動領域の段階的分離戦略R&D / 自社運営 / プロダクト / 商用運用の分離方針。本仕様はR&D領域の会計的分離の第一歩

人間が検討すべき事項

  • 14_mst_projectR&D対象フラグ / R&Dフェーズ 列を追加する方針でよいか(列名・型の最終確認): 既存14列との命名整合性、将来的なマスタ拡張余地(サブフェーズ等)の要否を確認。列追加後は既存PJへの手動属性設定作業が発生するため、一括CSV投入フローの要否も検討

  • R&D/開発フェーズの判定基準(社内定義): 「技術的実現可能性の確立」を社内でどう定義するか。会計基準(研究開発費等に係る会計基準)ではフェーズ境界は企業判断に委ねられている。顧問税理士との擦り合わせが必要

  • 資産計上の開始時点: 開発フェーズ判定時点で即資産計上するのか、一定の検収・マイルストーン達成時点とするのか。F-37 減価償却管理との連携設計に影響

  • 人件費(給与・法定福利費等)をR&D費用集計に含めるか: 含める場合の集計元は2案:

    • Option A: 42_trn_journal の実績仕訳(科目名給料手当 / 役員報酬 / 法定福利費 等のレコード)を PJ名 でR&D対象PJに紐付ける
    • Option B: 22_bud_headcount の予算データを 70_bud_resource の稼働率で按分してR&D対象PJに配賦する(420_project_profitability.js の既存ロジックを流用)

    Option B は既存の工数按分ロジックと整合するが、実績ベースではなく予算×稼働率の擬似実績となる。プロジェクトオーナーの判断に依存

  • R&D費用の税務上の取り扱い(試験研究費税額控除の対象範囲判定): 租税特別措置法の「試験研究費」定義(製造業・ソフトウェア業で範囲が異なる)に基づき、どの科目を税額控除対象として計上するかは会計担当者・顧問税理士の確認事項。本仕様ではレベル判定は行わず、全R&D区分INVを下書きに含め、税務判定はF-38で行う

  • RD_F34_ マーキング仕訳をP/L・B/Sマート集計から除外するロジックの実装要否: 本仕様のスコープ外としているが、実運用上は必須機能。F-38 または別途マイグレーションで実装する場合の仕様整理が必要

  • 69_rd_summary の番号帯: F-28 69_kpi_saas(仕様書作成済・未実装)と同じ 6X 番台を使用する。将来的に 69_rd_summary68_rd_summary67_rd_summary に移す余地があるか、タブナンバリング規約(spec_tab_renumber.md)の見直しと合わせて検討

  • 確認FLG の表記: 本仕様では 'OK' 完全一致。クレカ明細タブ等の既存 確認FLG 運用と統一するか、R&D専用の表記(例: 研究承認 / 開発承認 でフェーズ上書きも兼ねる)とするかの設計判断

エッジケース

実データ検証

関連ドキュメント

人間が検討すべき事項

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

あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者です。
案件 F-34「R&D費用の自動分類・集計」を実装してください。

## 実行前タスク(Read で実物確認・造語禁止)

以下のファイルを必ず Read で実物確認し、関数名・シート名・列名・メニュー名を正しく引用すること:

1. `000_infra/002_constants.js`
   - `SHEET_DEFAULTS` 内の `32_wrk_invoice` のデフォルト値(`'請求ステータス': '未処理'`)
   - `MENU_DEFINITION` の `📋 サイドバー: 📊 マート更新` カテゴリの既存項目(財務3表の更新 / プロジェクト別 採算 等)
   - `ID_PREFIX_MAP` の `14_mst_project` (`prefix: 'PRJ_'`) / `42_trn_journal` (`prefix: 'TRN_'`)
2. `000_infra/003_contracts.js`
   - `InvoiceDTO` のフィールド名(`請求ステータス` / `PJ名` / `科目名` / `税抜金額_計画` / `請求ID(INV)` / `発生日(P/L計上日)` / `摘要` / `証憑URL` 等)
   - `JournalEntryDTO` のフィールド名(`取引ID` / `管理ID` / `仕訳ステータス` / `摘要` 等)。`仕訳ステータス` のとりうる値は `'自動計上' | '手動' | '仕訳振替'`
3. `200_data/202_repository.js`
   - `readSheetAsDtos_()` の実装パターン(L19-29)
   - `InvoiceRepository.findAll()` の戻り値型 `{ headers: string[], dtos: InvoiceDTO[] }`
   - `JournalRepository.append()` の実装(L291-298): 科目マスタ照合・PJデフォルトを**行わない**ことを確認
4. `100_config/101_sys_config.js`
   - `'MST_PROJ'` スキーマ定義(L836付近): 現在14列
   - `MENU_DEFINITION` は `000_infra/002_constants.js` 側に移管済。`onOpen()` は自動展開する
5. `docs/dev/dev_F-34_rd_cost_aggregation.md` — 本仕様書

## 修正対象ファイル

| ファイル | 変更種別 |
|---------|---------|
| `100_config/101_sys_config.js` | `'MST_PROJ'` スキーマに `R&D対象フラグ` / `R&Dフェーズ` の2列追加 |
| `000_infra/002_constants.js` | `MENU_DEFINITION` の `📋 サイドバー: 📊 マート更新` に2メニュー項目追加 |
| `600_report/611_rd_cost_aggregator.js` | 新規ファイル作成 |
| `CLAUDE.md` | 「DDLで管理されないタブ」リストに `69_rd_summary` を追記 |

## 実装内容

### Step 1: DDL変更(`101_sys_config.js`)

`'MST_PROJ'` の `headers` 配列末尾に `"R&D対象フラグ", "R&Dフェーズ"` を追加する。既存14列の順序は変更しない。
`setupAllSchemas` 実行時、既存データは保持されたまま新列が空欄で追加される。

### Step 2: メニュー追加(`002_constants.js`)

`MENU_DEFINITION` 配列から `category: '📋 サイドバー: 📊 マート更新'` のブロックを特定し、`items` 配列の末尾(`📸 前年度P/Lスナップショット` の直後)に以下2項目を追加:

```js
{ label: '🔬 R&D費用を下書き集計', funcName: 'buildRdSummary', description: '14_mst_project の R&D対象フラグ=TRUE の PJ に紐付く承認済INVを集計し 69_rd_summary に下書き出力' },
{ label: '🔬 R&D仕訳を確定', funcName: 'confirmRdJournal', description: '69_rd_summary の 確認FLG=OK 行から 42_trn_journal へ R&D区分マーキング仕訳を追記(二重計上防止ロック付き)' },
```

### Step 3: 新規ファイル `600_report/611_rd_cost_aggregator.js` を作成

以下の関数構成で実装する(詳細は本仕様書の「修正方針」セクション参照):

- `function readProjectMaster_()` — 非公開ヘルパー。`14_mst_project` を読み込み `{ [プロジェクト名]: { phase: string, rdFlag: boolean } }` マップを返す。`readSheetAsDtos_` を流用せず、本ファイル内に同パターンで再実装
- `function buildRdSummary()` — 公開関数。`InvoiceRepository.findAll()` で全INV取得→ `請求ステータス === '承認済'` かつ R&D対象PJに紐付くものを抽出→ `{PJ, phase, 科目}` 単位で集計→ `69_rd_summary` に書き出し
- `function confirmRdJournal()` — 公開関数。`69_rd_summary` の `確認FLG === 'OK'` 行→ `42_trn_journal` から `管理ID` プレフィックス `RD_F34_` を持つ既存仕訳をSet化→ 重複しないINV_IDのみ `JournalRepository.append()` で追記
- `function renderRdSummarySheet_(ss, summaryMap)` — 非公開ヘルパー。シート取得/生成→ `clearContents()` → ヘッダー書き込み → データ行書き込み → フィルター設定

**二重計上防止ロック(Step 3 冪等性)**:
```js
var existingJnl = JournalRepository.findAll();
var lockedInvIds = new Set();
existingJnl.dtos.forEach(function(dto) {
  var mid = String(dto['管理ID'] || '');
  if (mid.indexOf('RD_F34_') === 0) {
    // 管理ID形式: RD_F34_<INV_ID>_<YYYYMMDD>
    var parts = mid.split('_');
    // RD_F34_INV_20260101_0001_20260420 → INV_20260101_0001 を復元
    if (parts.length >= 6) {
      lockedInvIds.add(parts.slice(2, 5).join('_'));
    }
  }
});
// 以降 Set.has(invId) で判定
```

### Step 4: `CLAUDE.md` の更新

「DDL (setupAllSchemas) で管理されないタブ」セクションの動的生成タブリスト末尾に `69_rd_summary` を追記:

```
動的に生成・上書きされるタブ:
03_sys_params, 75_ss_equity_changes, 76_notes,
77_pj_raw, 78_pj_pl, 91_fs_bs, 92_fs_pl, 93_kpi_dashboard, 90_test_results, 69_rd_summary
```

## 制約

- 既存の `InvoiceRepository` / `JournalRepository` のpublicメソッドのシグネチャを変更しない
- メニュー名・シート名・列名はPhase 1でReadした実在する文字列のみ使用する。造語禁止(`R&D対象フラグ` / `R&Dフェーズ` は本仕様で新規追加する固定名称)
- 仕訳の管理IDプレフィックスは `RD_F34_` で固定
- `101_sys_config.js` への変更はMST_PROJスキーマの2列追加のみ。既存スキーマ・検証ロジックは変更しない
- `readProjectMaster_()` は `202_repository.js` 外のファイル(`611_rd_cost_aggregator.js`)に実装。`readSheetAsDtos_()` を直接import/referenceしない
- `JournalRepository.append()` に渡すDTOは呼び出し元で全フィールドを完全に設定する(科目マスタ自動照合なし)
- `請求ステータス === '承認済'` の完全一致(`'部分決済'` / `'決済完了'` は対象外)

## エッジケース

本仕様書の「エッジケース」セクションを参照(R&D対象0件の早期リターン / 重複実行時のロック / `確認FLG` 完全一致等、全12項目)。

## 動作確認

1. `npm run push:dev` でdev環境にデプロイ
2. GASエディタで `setupAllSchemas()` を実行(`14_mst_project` に `R&D対象フラグ` / `R&Dフェーズ` 列が追加されることを確認)
3. `14_mst_project` の任意の1〜2行に `R&D対象フラグ=TRUE`, `R&Dフェーズ=研究` or `開発` を手動入力
4. `32_wrk_invoice` に上記R&D対象PJに紐付く承認済INVが存在することを確認
5. サイドバー「📊 マート更新 > 🔬 R&D費用を下書き集計」を実行
6. `69_rd_summary` に期待通りのデータが集計されることを確認(ヘッダー・フィルター・集計値)
7. `69_rd_summary` の対象行の `確認FLG` に `OK` を手動入力
8. サイドバー「📊 マート更新 > 🔬 R&D仕訳を確定」を実行
9. `42_trn_journal` に `管理ID` プレフィックス `RD_F34_` の仕訳が追記されることを確認
10. 同じ操作(Step 3)を再実行し、二重計上が発生しないこと(スキップ通知が出ること)を確認
11. R&D対象PJを全て `FALSE` にした状態で Step 2 を実行し、「R&D対象INVが見つかりませんでした」のalertが出て早期リターンすることを確認
12. `900_test/901_test_runner.js` の既存テスト全件実行で既存機能にリグレッションがないことを確認(特にP/L・B/Sマート再生成)

推奨実行モデル

工程推奨モデル理由
Step 1: DDL変更(スキーマ2列追加)Claude SonnetsetupAllSchemas の既存スキーマ定義パターンと整合確認が必要。既存14列との列順・命名整合性の判断を伴う
Step 2: メニュー追加Claude HaikuMENU_DEFINITION 配列への2行追加のみ。既存パターンの踏襲で判断要素なし
Step 3: 集計ロジック実装(buildRdSummaryClaude Sonnet複数シート横断(14_mst_project / 32_wrk_invoice / 69_rd_summary)の設計判断と、既存 readSheetAsDtos_ パターンの適用が必要
Step 3: 仕訳確定ロジック実装(confirmRdJournalClaude SonnetJournalRepository.append() の特性(科目マスタ照合なし)を踏まえたDTO完全設定と、二重計上防止ロックの実装が必要
Step 4: CLAUDE.md 追記Claude Haiku1要素の追加のみ

変更履歴

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

仕様書作成プロンプト

展開して表示
【タイムアウト回避・実行原則(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(骨格〜20行)/2-2(概要〜注意事項〜300行)/2-3a(エッジケース〜人間検討事項〜200行)/2-3b(実装プロンプト〜変更履歴〜250行)/2-4(`<details>`プロンプト全文記録)に分割。1回のWrite/Editは300行以内。
4. 各Stepで何を書くかを具体指示: Phase 2実行時に設計判断を持ち込まない。Phase 1で固有名詞・行番号・エッジケースを確定してから書き出す。

======================================================================
あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者兼仕様書ライターです。
案件 F-34「R&D費用の自動分類・集計」の開発仕様書を作成してください。
作成後、`docs/_config.json` の `nav` 配列の適切なセクションにも必ず追記してください。

---

## Phase 1: 実行前タスク(テキスト報告禁止。全て即座にツール実行)

### 1-A: 案件定義の読み込み
`docs/_internal/TODO_future.md` でF-34の行を検索し、案件名・カテゴリ・Phase・優先度・概要・期待効果・人間が検討すべき事項を取得する。

### 1-B: プロジェクト規約の読み込み
`CLAUDE.md` を読み込み、以下を把握する:
- ファイル番号体系(`600_report/` レイヤーの採番ルール、新規ドメインファイルの配置場所)
- 「DDL (setupAllSchemas) で管理されないタブ」の一覧(`69_rd_summary` を動的生成タブとして追記すべきか判断するため)
- マイグレーションスクリプト運用ガイドライン(DDL変更の手順確認)

### 1-C: 既存仕様書テンプレートの読み込み
`docs/dev/` 配下のFP&A系仕様書を1件読み込み(ファイル名に `F-` を含むもの)、セクション構成・実装プロンプトのフォーマットを把握する。

### 1-D: 関連GASコードの調査(Grep→必ずReadで裏取り)

【最重要】Grepは「どこにあるか」の発見まで。データ構造の型・固有名詞(関数名/シート名/列名/メニュー名)は必ずReadで実物を確認してから仕様書に記述する。「〜だろう」と推測した瞬間に手を止めてReadする。過去にF-03仕様書でコード未読から3件の固有名詞誤記が同時発生した(失敗パターン #18-#20)。

以下を順番にReadして確認すること:

1. `000_infra/002_constants.js`
   - `SHEET_DEFAULTS` 内の `32_wrk_invoice`・`14_mst_project` のエントリが存在するか、存在する場合のフィールド名を確認
   - `ID_PREFIX_MAP` で `14_mst_project` の prefix・digit を確認

2. `000_infra/003_contracts.js`
   - `InvoiceDTO`(`32_wrk_invoice` のフィールド構造): `請求ステータス`・`PJ名`・`科目名`・`税抜金額_計画`・`請求ID(INV)` 等の実際の列名を確認
   - `JournalEntryDTO`(`42_trn_journal` のフィールド構造): `管理ID` 列が実在するか、既存の管理IDの値形式(例: 空欄、または既存プレフィックス)を確認

3. `000_infra/004_utils.js`
   - `parseAmt()`・`parseDateToYm()` 等の利用可能なユーティリティの引数・戻り値を確認

4. `200_data/202_repository.js`
   - `InvoiceRepository.findAll()` の戻り値型(`{ headers: string[], dtos: InvoiceDTO[] }` であることを確認)
   - `JournalRepository.append()` の引数型と内部ロジック(`InvoiceRepository.append()` と異なりアカウントマッピング処理がないことを確認)
   - `readSheetAsDtos_()` の実装パターンを確認(PJマスタ読み込み関数の雛形として利用するため)。なお、GASではアンダースコア末尾関数はプライベート慣習のため、同関数を直接呼び出す代わりに同パターンの `readProjectMaster_()` を実装することを仕様に明記する

5. `100_config/101_sys_config.js`
   - `onOpen()` 内のメニュー定義をReadし、実在するメニュー名・グループ名の正確な文字列を確認。新規メニュー追加箇所(グループ名・前後の項目)を特定する。メニュー名の造語禁止。実在する文字列のみ引用する。

6. `400_domain/420_project_profitability.js`(存在する場合)
   - プロジェクト別費用集計の既存実装パターン(PJマスタ読み込み方法・集計ロジック)を確認。類似ロジックがあれば横展開する

7. `600_report/601_datamart_ingest.js` または番号が最も近い `6xx_datamart_*.js`
   - レポートシート生成の既存パターン(ヘッダー書き込み・データ書き込み・フィルター設定)を確認。`69_rd_summary` 実装の雛形として利用する

8. `docs/_internal/failure_patterns.md`
   - #16(合算マッチ成功時のロック処理・matched=trueによる二重消費防止の思想)
   - #18-#20(コード未読による固有名詞誤記)
   - #21-#24(GAS + Sheetsの数式設計の落とし穴)を確認

人件費データソースの調査(R&D費用に人件費を含める場合のみ):
`42_trn_journal` で `科目名` が給料手当・法定福利費等に該当する仕訳レコードがどのように記録されているか確認する。仕様が不明な場合は確定させず「人間が検討すべき事項」に記載する。

---

## Phase 2: 仕様書の分割作成

出力先: `docs/dev/dev_F-34_rd_cost_aggregation.md`(Fは大文字)

### Step 2-1 〜 2-4

(本プロンプト本体の指示に従って骨格→前半→エッジケース→実装プロンプトの順に作成する)

---

## Phase 3: 保存・記録・コミット

### 3-A: `docs/_config.json` への登録(必須)
### 3-B: `docs/_internal/changelog.md` への追記
### 3-C: コミット&プッシュ

📌 取り込み時の注記 (2026-06-02 sub 復元)

本仕様書は旧 F-番号体系で作成され PR 未マージのまま孤立していたドラフトを、origin/docs/dev-* ブランチから内容無改変で復元し、案件ID のみ MAS 体系へ正規化したもの。status: Open(未実装)

⚠️ ファイル番号ドリフト: 本文「対象ファイル」が指す 600_report/610〜612_*.js は現行 main で 別機能に使用済み(610=投資分析/MAS-013・611=財務モデリング/MAS-010・612=採用sim/MAS-012)。 実装時にファイル番号の再割当が必要。