概要

項目内容
案件IDMAS-088
カテゴリ会計指針(月次自動計上)
PhaseP2
優先度
所要時間90分(実装40分 + 動作確認30分 + マスタ/パラメータ投入20分)
実装ステータス📝 仕様書段階・実装未着手 (2026-04-28 監査時点)
対象ファイル400_domain/410_subledger_engine.js(新関数 createBonusProvisionJournal_ を追加)、600_report/602_datamart_main.jsbuildBudgetTrendDataMart 末尾から呼び出しを追加)
前提案件なし(科目マスタは 11_mst_account209 賞与引当金 / 542 賞与引当金繰入額 を予約済み。ただし現状 有効フラグ=FALSE

TODO_future.md L185 より:

MAS-088 | 役員賞与引当金の自動計上 | 会計指針 | P2 | ★ | 未着手 | 科目マスタ予約済み。来期有効化し月次按分で自動計上。 | 中小企業会計指針準拠 | 引当額の算定方法と金額の決定

目的

中小企業会計指針 第52項「翌期に従業員に対して支給する賞与の見積額のうち、当期の負担に属する部分の金額は賞与引当金として計上しなければならない」に準拠し、 年間支給予定総額を対象期間の月数で按分し、毎月の月次マート更新buildBudgetTrendDataMart)実行時に 42_trn_journal へ引当繰入の自動仕訳を起票する。

  • 会計インパクト: 対象月の P/L 「賞与引当金繰入額」を押し上げ、B/S に「賞与引当金」を積み上げる(本案件では P/L 借方のみを機械生成し、B/S 貸方は会計エンジンの既存スタックで自動反映させる設計を採用する。「人間が検討すべき事項」参照)
  • 運用インパクト: 月次クローズ時にユーザーが 03_sys_params に 3 パラメータ(総額・開始YM・終了YM)を設定するだけで、以降は毎月のマート更新操作で自動計上される

現在のコード

現状(未実装)

  • 11_mst_account209 賞与引当金(B/S: その他流動負債)と 542 賞与引当金繰入額(P/L: 一般管理費, 固定費, 税区分=対象外)は登録済みだが、いずれも 有効フラグ=FALSEmst_account.md L162, L274)
  • 42_trn_journal への引当繰入仕訳を生成する処理は存在しない
  • 月次マート更新のユーザー起点は templates/operations_sidebar.html L63 の「財務3表の更新」ボタン → buildBudgetTrendDataMart()600_report/602_datamart_main.js L159〜)

関連既存資産

資産ファイル用途
SubledgerService 名前空間400_domain/410_subledger_engine.js L12-17自動仕訳エンジンの公開API。Action A/B 以外の自動計上処理も同ファイルに集約するのが自然
generateTrnId_(trnSheet, dateStr, offset)400_domain/410_subledger_engine.js L26-43同ファイル内 file-scope 関数。TRN_YYYYMMDD_NNNN を生成
JournalRepository.findAll()200_data/202_repository.js L270-272戻り値 { headers, dtos: JournalEntryDTO[] }
JournalRepository.append(dtos)200_data/202_repository.js L291-297DTO 配列を 42_trn_journal 末尾に追記。lastRowCol=0(A列=取引ID で最終行判定)
Constants.getParam(key, defaultVal)000_infra/002_constants.js L147-16703_sys_params の A列=key / B列=value を読み込みキャッシュ。戻り値は defaultVal が数値なら Number キャスト、文字列なら String キャスト
JournalEntryDTO 型定義000_infra/003_contracts.js L97-129全フィールドを確認済み(「修正方針」参照)
Utils.parseDateToYm(val)000_infra/004_utils.js L92-99Date または文字列を "YYYY-MM" に正規化
Utils.addMonths(ymStr, months)000_infra/004_utils.js L127-132"YYYY-MM" に月数加算
Utils.logInfo / toastResult / auditLog000_infra/004_utils.js L232, L254, L273ログ/トースト/監査ログ

修正方針

アーキテクチャ決定事項

1. 新関数の配置先

400_domain/410_subledger_engine.js に追加する。

  • 理由1: 既存の自動仕訳エンジン(Action A/B)と同じ責務カテゴリ(= 42_trn_journal への自動追記)であり、SubledgerService 名前空間にまとめて公開するのが自然。
  • 理由2: 同ファイルに generateTrnId_() が既に存在し、TRN ID 採番ロジックを再利用できる。新ファイルに切り出すと採番キャッシュが分散する。
  • 理由3: 新規ファイルを追加するとファイル番号体系(CLAUDE.md「GAS ファイル番号体系」)の採番判断が必要になり、本案件のスコープを超える。
// 400_domain/410_subledger_engine.js に以下の公開APIを追加
var SubledgerService = {
  processApprovals: function() { return processInvoiceApprovals(); },
  processClearings: function() { return processSettlementClearings(); },
  // ↓ S-16 追加
  createBonusProvision: function(targetYm) { return createBonusProvisionJournal_(targetYm); },
};

関数シグネチャ: function createBonusProvisionJournal_(targetYm)

  • 引数 targetYm: "YYYY-MM" 形式の文字列(例 "2026-04"
  • 戻り値: なし(副作用として 42_trn_journal に追記 + トースト/監査ログ出力)

2. 呼び出し元

600_report/602_datamart_main.jsbuildBudgetTrendDataMart() 末尾で、計算済みの boundaryMonthStr を基に呼び出す。

  • buildBudgetTrendDataMart は「財務3表の更新」サイドバーボタン(templates/operations_sidebar.html L63)から呼ばれる月次マート更新のメイン関数。
  • 呼び出しタイミングはマート書込みの とする(引当仕訳が 42_trn_journal に追記された状態で P/L マートを構築させるため)。
  • targetYmbuildBudgetTrendDataMart が内部で算出する「実績として集計する最終月」を流用する。詳細は実装プロンプト参照。

補足: buildDataMartWithCustomBoundary()(同ファイル L386)は buildBudgetTrendDataMart(boundary) をそのまま呼ぶラッパーなので、上記一箇所に組み込めば両経路から発火する。

3. パラメータ管理(03_sys_params

既存の CFG_* 命名規則に合わせて以下の 3 キーで管理する:

キー内容
CFG_BONUS_PROVISION_AMOUNTnumber6000000年間支給予定総額(円・総支給額ベース)
CFG_BONUS_PROVISION_START_YMstring ("YYYY-MM")"2026-04"対象開始年月(この月から計上開始)
CFG_BONUS_PROVISION_END_YMstring ("YYYY-MM")"2027-03"対象終了年月(この月で計上終了 = 最終月扱い)

読み込みは Constants.getParam('CFG_BONUS_PROVISION_AMOUNT', 0) 等。第2引数(デフォルト値)の型で戻り値の cast が決まることに注意。

4. 計算ロジック

対象月数 = Utils.addMonths を用いて START_YM から END_YM まで +1 ずつループした月数
月次引当額(通常月) = Math.floor(CFG_BONUS_PROVISION_AMOUNT / 対象月数)
端数           = CFG_BONUS_PROVISION_AMOUNT - (月次引当額 × 対象月数)
月次引当額(最終月) = 月次引当額(通常月) + 端数
  • targetYm === CFG_BONUS_PROVISION_END_YM の月のみ端数を加算する(切捨てで発生する年間合計との差額を最終月で回収)。
  • 月数計算は Utils.addMonths を START に加えながら END に到達するまでのステップ数。Constants.MONTH_ITERATION_LIMIT=120(約10年)を上限ガードとして使用。

5. JournalEntryDTO の構築

42_trn_journal ヘッダーに存在する DTO フィールドのみ設定する(余計なフィールドは設定しない)。

フィールド備考
取引IDgenerateTrnId_(trnSheet, "YYYYMMDD", 0) で採番TRN_YYYYMMDD_NNNNYYYYMMDDtargetYm + "01" をベース
発生日(P/L計上日)targetYm の1日(例: new Date(2026, 3, 1)2026-04-01期ずれ判定のため月初固定
決済日_計画仕訳振替のため決済なし
決済日_実績同上
収支区分"支出"P/L 借方計上
取引先名""(空)社内引当のため取引先なし(既存 Action A の仕訳振替INVも空運用)
科目名"賞与引当金繰入額"11_mst_account 542 の正式科目名と完全一致(下記「人間が検討すべき事項」参照: 案件名の「役員」は実装では付けない
税区分"対象外"引当繰入は課税取引対象外
外貨金額0
通貨"JPY"
税抜金額_実績月次引当額端数調整後の値
消費税額_実績0対象外 のため
税込金額_実績月次引当額税抜と同値
組織名"指定なし_共通費など"既存 InvoiceRepository.append と同じデフォルト値(全社共通費として扱う)
PJ名"指定なし_共通費など"同上
決済手段"仕訳振替"B/S 側(賞与引当金)への振替のため。CLAUDE.md規約に従い完全一致文字列
仕訳ステータス"自動計上"二重計上判定キー
証憑URL""
摘要"賞与引当金 月次計上 (YYYY-MM)" (YYYY-MM は targetYm)人間可読な識別子
管理ID""
CF支払ID""仕訳振替のため CF 除外(CLAUDE.md 決済手段ルール)
CF支払ID枝番""同上
コード系列(収支区分コード 他)""マート構築側で付番される想定(Action A の既存仕訳も空出力)

6. 二重計上防止(冪等性)

関数冒頭で JournalRepository.findAll() を実行し、dtos 配列を走査する。判定条件:

Utils.parseDateToYm(dto['発生日(P/L計上日)']) === targetYm
  && dto['科目名'] === '賞与引当金繰入額'
  && dto['仕訳ステータス'] === '自動計上'

3 条件全一致が 1 件でも存在すればスキップし、Utils.logInfo('createBonusProvisionJournal_', 'skip: already posted for ' + targetYm) をログ出力して return する。

7. データ永続化

JournalRepository.append([dto]) を呼び出し、戻り値(追記行数)が 1 でない場合は Utils.logError でエラーログを残す。

8. 通知・監査

Utils.toastResult('createBonusProvisionJournal_',
  '賞与引当金 ' + amount.toLocaleString() + '円を計上しました (' + targetYm + ')', 5);
Utils.auditLog('RUN', '42_trn_journal', trnId, '', 'createBonusProvisionJournal_',
  null, amount, '賞与引当金月次自動計上 ' + targetYm);

影響範囲

領域影響内容既存挙動への影響
400_domain/410_subledger_engine.js新関数 createBonusProvisionJournal_ 追加、SubledgerService.createBonusProvision 追加なし(既存関数は無変更)
600_report/602_datamart_main.jsbuildBudgetTrendDataMart() 末尾付近に呼び出し 1 行追加パラメータ未設定時はスキップ(ノーオペ)。設定されれば新規仕訳が 42_trn_journal に 1 件/月追加
42_trn_journal月次マート更新ごとに 仕訳ステータス=自動計上 / 科目名=賞与引当金繰入額 の行が最大 1 件追加されるbuildBudgetTrendDataMart42_trn_journal を消さない前提のため蓄積される
11_mst_account事前に 209 賞与引当金542 賞与引当金繰入額有効フラグ=TRUE に手動更新が必要未更新の場合 InvoiceRepository.append パスの諸表区分自動付与は動作しないが、JournalRepository.append は動作する(マスタ照合ロジックを通らない)
03_sys_params新規3キー手動投入が必要未投入時は関数がスキップ(エッジケース表参照)
P/L マート(61_pl_monthly, 92_fs_pl 等)賞与引当金繰入額 の金額が月次で反映される科目マスタ有効化が前提
B/S マート(62_bs_monthly, 91_fs_bs 等)賞与引当金 残高が月次で増加科目マスタ有効化と 決済手段=仕訳振替 の B/S 側展開ロジックが前提(既存エンジン依存)
CF タブ(84_cf_daily_plan 等)影響なし決済手段=仕訳振替 は CF 除外(CLAUDE.md規約)

注意事項

  1. 03_sys_params のキーはタイポ厳禁: CFG_BONUS_PROVISION_AMOUNT / CFG_BONUS_PROVISION_START_YM / CFG_BONUS_PROVISION_END_YM の完全一致で読み込む。全角半角・_/- 混在に注意。
  2. 科目マスタの「役員」有無: 案件名は「役員賞与引当金の自動計上」だが、11_mst_account に登録済みの正式科目名は 賞与引当金 / 賞与引当金繰入額(役員プレフィックスなし)。実装ではマスタ登録名と完全一致させる必要があるため、コード側は "賞与引当金繰入額" を使用する。役員用に別科目を切り出すかどうかは「人間が検討すべき事項」で判断する。
  3. 科目マスタの 有効フラグ: 209/542 は現状 FALSE。実装動作確認前に必ず TRUE に切り替える(CLAUDE.md「有効フラグ=FALSE の行は全処理でスキップ」のため、マート側で集計されない)。
  4. 仕訳振替 の判定: === "仕訳振替" の完全一致(CLAUDE.md「会計ロジック」規約)。"仕訳・振替" 等の別名を使わない。
  5. 列参照はヘッダー名ベース: JournalEntryDTO のフィールド名はシートヘッダーと完全一致させる。列番号ハードコード禁止(CLAUDE.md「データアクセス」規約)。
  6. JournalRepository.append の最終行判定: 内部で lastRowCol=0(A列=取引ID)を用いる。取引ID列が空の行があると上書きされる可能性があるため、直前に 42_trn_journal の手動編集で取引ID 列を空にしないこと。
  7. TRN_ID 採番キャッシュ: generateTrnId_._cache はバッチ冒頭でのみリセットされる(Action A/B 側で)。本関数を単発で呼ぶ場合、直前に generateTrnId_._cache = {} でリセットする。複数月を連続実行しても日付が異なれば衝突しないため、buildBudgetTrendDataMart 経由では通常問題にならない。
  8. 実行頻度: 「財務3表の更新」ボタンは 1 日に複数回押されうる。冪等性チェック(#6)で同一月の二重計上は防止されるが、チェック前にログが頻繁に出ないよう info ログに留める(warn/error 不可)。

エッジケース

#条件動作理由
1CFG_BONUS_PROVISION_AMOUNT が未設定・空・0スキップ。Utils.logInfo('createBonusProvisionJournal_', 'skip: amount not set')ゼロ除算・ゼロ計上の防止
2CFG_BONUS_PROVISION_START_YM または CFG_BONUS_PROVISION_END_YM が未設定・空・"YYYY-MM" 形式として解釈不能スキップ。Utils.logInfo('createBonusProvisionJournal_', 'skip: period not set')対象期間が確定できない
3END_YM < START_YM(期間不正)スキップ。Utils.logError でエラー記録対象月数が 0 以下になりゼロ除算が発生する
4targetYm < START_YM または targetYm > END_YM(期間外)静かにスキップ(ログ出力なし)期間外の月は計上不要。マート更新操作は毎月実行されるため、期間外スキップをログ化するとノイズになる
5同一 targetYm の引当仕訳が既に存在する(冪等性チェック 3 条件一致)スキップ。Utils.logInfo('createBonusProvisionJournal_', 'skip: already posted for YYYY-MM')二重計上防止。buildBudgetTrendDataMart は日に複数回実行されうる
6CFG_BONUS_PROVISION_AMOUNT % 対象月数 !== 0(端数あり)最終月(targetYm === END_YM)の計上額に端数を加算年間合計が CFG_BONUS_PROVISION_AMOUNT と完全一致することを保証
742_trn_journal シートが存在しないUtils.logError でエラー記録、SpreadsheetApp.getUi().alert で警告致命エラー。マート更新処理は継続させる
8JournalRepository.append が 0 を返した(書き込み失敗)Utils.logError でエラー記録後続のトースト通知・監査ログを出さない
9対象月数が Constants.MONTH_ITERATION_LIMIT(120ヶ月)超スキップ。Utils.logError でエラー記録パラメータ誤入力(例: END_YM=2100-12)の防止
1011_mst_account542 賞与引当金繰入額有効フラグ=FALSE関数自体は正常書き込み成功するが、マート側で集計対象外となる本関数はマスタ照合を行わないため検知できない。実データ検証で事前チェック

スコープ外事項

以下は本案件のスコープ外とし、別案件または手動運用で対応する:

  • 遡及計上: 実行対象月(targetYm)の引当のみを計上対象とする。過去月への遡及(例: START_YM=2025-04 設定時に 2025-04〜現在分を一括計上)は本機能のスコープ外。別途ワンショット関数として実装するか、手動で3_sys_params を月ごとに変更して再実行する運用で対応。
  • 引当金取崩: 実際の賞与支給(例: 2027年6月賞与支給)時の取崩仕訳(賞与引当金 / 現金及び預金)生成は本案件のスコープ外。通常の RPA-HC 経由の賞与支給仕訳と突合せて手動で振替する運用(または別案件で対応)。
  • 役員賞与と従業員賞与の区別: マスタに 役員賞与引当金 / 役員賞与引当金繰入額 が追加された場合、本関数を分岐させるか関数を複製するかの判断は別案件。現状は単一科目(賞与引当金繰入額)で運用する。

実データ検証

実装完了前に以下を MCP または GAS エディタから確認する:

  1. 科目マスタ検証 (11_mst_account):

    • 賞与引当金(科目コード 209)の行が存在し、有効フラグ=TRUE に設定されていること
    • 賞与引当金繰入額(科目コード 542)の行が存在し、有効フラグ=TRUE に設定されていること
    • 未登録・無効の場合は本実装前にマスタ更新が必要(運用ハンドブック: docs/_internal/data_maintenance.md にも追記を検討)
  2. パラメータシート検証 (03_sys_params):

    • A列に以下 3 キーの行が存在すること(行順不問):
      • CFG_BONUS_PROVISION_AMOUNT
      • CFG_BONUS_PROVISION_START_YM
      • CFG_BONUS_PROVISION_END_YM
    • B列に妥当な値が入っていること(金額は正の整数、年月は YYYY-MM 文字列)
    • 03_sys_paramsDDL (setupAllSchemas) で管理されない動的シートのため、手動で行追加する
  3. 既存自動仕訳パターンの目視検証 (42_trn_journal):

    • 仕訳ステータス="自動計上" の既存レコードを数件確認し、DTO フィールド充足状況(取引先名・組織名・PJ名 等の空運用)が本案件の DTO 設計と整合していることを確認
    • 取引ID 形式(TRN_YYYYMMDD_NNNN)が generateTrnId_ の想定と一致していることを確認
  4. マート集計影響の事前シミュレーション:

    • dev 環境で 1 件計上後、buildBudgetTrendDataMart() を再実行し 61_pl_monthly賞与引当金繰入額 行が表示されること
    • 62_bs_monthly賞与引当金 残高が増加していること(B/S 反映は既存の仕訳振替展開ロジックに依存)

Human-in-the-Loop 運用

  • 事前確認ダイアログは表示しない(自動実行優先): 月次マート更新の一部として無人実行されるため、UI ブロックは避ける
  • 正常完了時の通知: Utils.toastResult('createBonusProvisionJournal_', '賞与引当金 X,XXX円を計上しました (YYYY-MM)', 5) でトースト表示(5秒)
  • 監査ログ: Utils.auditLog('RUN', '42_trn_journal', <生成した取引ID>, '', 'createBonusProvisionJournal_', null, <計上金額>, '賞与引当金月次自動計上 ' + targetYm) を計上成功後に必ず呼ぶ
  • 人間レビューポイント: 月次の「財務3表の更新」完了後、ユーザーは 42_trn_journal仕訳ステータス=自動計上 かつ 科目名=賞与引当金繰入額 の行が意図通りの金額・年月で計上されているか目視確認する。異常があれば 有効フラグ=FALSE に切替 or 行削除で取消可能

関連ドキュメント

仕様書関連箇所
CLAUDE.mdコーディング規約(有効フラグ、ヘッダー名参照、仕訳振替判定、ID体系)
mst_account.md科目 209 賞与引当金 / 542 賞与引当金繰入額 の登録状況
ref_sme_accounting_guidelines.md中小企業会計指針 第52項「賞与引当金の計上額」
spec_engine.mdAction A / Action B の自動仕訳エンジン(SubledgerService)の既存パターン
TODO_future.mdMAS-088 案件の原ソース

人間が検討すべき事項

  1. 「役員賞与」vs「全社賞与」の切り分け:

    • TODO_future.md および案件タイトルは「役員賞与引当金」と明記しているが、11_mst_account に登録済みの科目は 賞与引当金 / 賞与引当金繰入額(役員プレフィックスなし、= 通常は従業員賞与用の科目)。
    • 選択肢:
      • (A) 現状マスタ名のまま流用し、「会社の賞与(従業員 + 役員)全体」を本機能で計上 → マスタ追加不要、実装即可
      • (B) マスタに 役員賞与引当金 / 役員賞与引当金繰入額 を新設し、従業員用と分離 → マスタ追加案件が前提、実装前提条件の複雑化
    • 推奨: 現状ビジネス規模・役員数を踏まえれば (A) で十分。役員賞与のみ分離管理が必要になった時点で (B) に拡張。税務上の損金不算入の取扱い(役員賞与は原則損金不算入)を P/L 表示分離で可視化したい場合は (B) が必要になるため、税理士と擦り合わせ。
  2. 引当額(CFG_BONUS_PROVISION_AMOUNT)の算定方法:

    • TODO_future.md の「人間が検討すべき事項」原文そのまま: 「引当額の算定方法と金額の決定」
    • 一般的な算定方法:
      • 前年実績ベース(過去1年の賞与支給総額)
      • 翌期支給予定額ベース(就業規則・役員会決議で確定した見積額)
      • 年俸ベースの理論値(基本給 × 月数按分)
    • 必須アクション: 運用開始前に代表取締役 + 税理士で算定根拠を合意し、根拠資料を Drive に保管
  3. 対象期間(START_YM / END_YM)の設定ポリシー:

    • 通常は事業年度(当社: 11月設立だが事業年度の起点要確認)と一致させる
    • 年度途中の導入時は「導入月〜年度末」に短縮し、翌年度から満年度適用
    • 年度またぎのリセット運用(毎年度末にパラメータを更新する手順)をどこに文書化するか(→ 運用ハンドブック or daily_log)
  4. B/S 貸方(賞与引当金)の仕訳計上方式:

    • 本案件では P/L 借方(賞与引当金繰入額)のみ 42_trn_journal に 1 行計上し、B/S 側は 決済手段=仕訳振替 のエンジン既存ロジックで自動展開される想定。
    • これで十分かどうか、B/S マート(62_bs_monthly)で意図通りに 賞与引当金 残高が積み上がるかを実データ検証で確認必須。
    • 意図通りに展開されない場合、明示的に 2 行(借方・貸方)の DTO を生成する実装に切替える必要がある。その場合は SubledgerService 内の既存 Action A の処理を参考にする。
  5. 税務調整(別表四)との整合:

    • 賞与引当金繰入額は税務上、期末時点で未払確定債務でない場合は損金不算入となるケースが多い(中小企業会計指針と税務会計の乖離)
    • 本案件はあくまで「会計帳簿への計上」であり、法人税申告時の別表四調整は別運用(手計算 or 税理士対応)
    • 本仕様書の読み手(開発者)にとってスコープ外だが、税理士との情報共有が必要

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

あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者です。
案件 MAS-088「役員賞与引当金の自動計上」を実装してください。

## 実行前タスク(Read で裏取り。推測禁止)

1. `000_infra/002_constants.js` — `Constants.getParam(key, defaultVal)` L147-167。第2引数の型で戻り値 cast が決まる挙動を確認。
2. `000_infra/003_contracts.js` — `JournalEntryDTO` の全フィールド(L97-129)と許容値を確認。
3. `200_data/202_repository.js` — `JournalRepository.findAll` (L270-272) と `append` (L291-297)。`appendDtosToSheet_` の `lastRowCol=0`(A列=取引ID)挙動を確認。
4. `000_infra/004_utils.js` — `Utils.parseDateToYm` (L92) / `Utils.addMonths` (L127) / `Utils.logInfo` (L232) / `Utils.toastResult` (L254) / `Utils.auditLog` (L273) の引数シグネチャを確認。
5. `400_domain/410_subledger_engine.js` — `generateTrnId_(trnSheet, dateStr, offset)` L26-43 を流用。`SubledgerService` 名前空間 L12-17 に新 API を追加する位置を確認。
6. `600_report/602_datamart_main.js` — `buildBudgetTrendDataMart()` L159 の「基準月 (boundaryMonthStr) を算出している箇所」を特定し、その直後(マート書込み前)に呼び出しを追加する。
7. `docs/master/mst_account.md` — `209 賞与引当金` / `542 賞与引当金繰入額` の登録状況と `有効フラグ` を確認。
8. `CLAUDE.md` — 有効フラグ、ヘッダー名参照、仕訳振替判定、TRN ID 体系。
9. `docs/dev/dev_mas-088_bonus_provision.md`(本仕様書) — 全セクション。

## 修正対象ファイル(2 ファイルのみ)

- `400_domain/410_subledger_engine.js`: 新関数 `createBonusProvisionJournal_(targetYm)` を追加、`SubledgerService` に `createBonusProvision` エントリ追加
- `600_report/602_datamart_main.js`: `buildBudgetTrendDataMart` 内で `SubledgerService.createBonusProvision(targetYm)` を呼び出す 1 行を追加

**上記 2 ファイル以外は変更しない。**

## 実装内容

### (1) `400_domain/410_subledger_engine.js` への追加

`SubledgerService` の定義に 1 行追加:

```js
var SubledgerService = {
  processApprovals: function() { return processInvoiceApprovals(); },
  processClearings: function() { return processSettlementClearings(); },
  // S-16: 賞与引当金 月次自動計上
  createBonusProvision: function(targetYm) { return createBonusProvisionJournal_(targetYm); },
};
```

ファイル末尾(または `processSettlementClearings()` の後)に新関数を追加:

```js
/**
 * S-16: 賞与引当金 月次自動計上
 * 03_sys_params の CFG_BONUS_PROVISION_* に基づき、対象年月 targetYm の引当繰入仕訳を
 * 42_trn_journal へ 1 行追記する。冪等性あり(同月再実行しても二重計上しない)。
 * @param {string} targetYm - "YYYY-MM" 形式
 */
function createBonusProvisionJournal_(targetYm) {
  const FUNC = 'createBonusProvisionJournal_';
  try {
    // --- 1. パラメータ取得 ---
    const amountTotal = Constants.getParam('CFG_BONUS_PROVISION_AMOUNT', 0);
    const startYm = Constants.getParam('CFG_BONUS_PROVISION_START_YM', '');
    const endYm = Constants.getParam('CFG_BONUS_PROVISION_END_YM', '');

    if (!amountTotal || amountTotal <= 0) {
      Utils.logInfo(FUNC, 'skip: amount not set');
      return;
    }
    if (!startYm || !endYm || !/^\d{4}-\d{2}$/.test(startYm) || !/^\d{4}-\d{2}$/.test(endYm)) {
      Utils.logInfo(FUNC, 'skip: period not set');
      return;
    }
    if (endYm < startYm) {
      Utils.logError(FUNC, new Error('invalid period: end < start ' + startYm + '〜' + endYm));
      return;
    }

    // --- 2. 対象期間外はスキップ(ログなし)---
    if (targetYm < startYm || targetYm > endYm) return;

    // --- 3. 対象月数算出(MONTH_ITERATION_LIMIT でガード) ---
    let monthCount = 0;
    let cursor = startYm;
    while (cursor <= endYm) {
      monthCount++;
      if (monthCount > Constants.MONTH_ITERATION_LIMIT) {
        Utils.logError(FUNC, new Error('period too long: > MONTH_ITERATION_LIMIT'));
        return;
      }
      cursor = Utils.addMonths(cursor, 1);
    }

    // --- 4. 月次金額計算(端数は最終月に寄せる) ---
    const base = Math.floor(amountTotal / monthCount);
    const remainder = amountTotal - base * monthCount;
    const amount = (targetYm === endYm) ? base + remainder : base;

    // --- 5. 冪等性チェック ---
    const existing = JournalRepository.findAll();
    for (let i = 0; i < existing.dtos.length; i++) {
      const d = existing.dtos[i];
      if (Utils.parseDateToYm(d['発生日(P/L計上日)']) === targetYm
          && d['科目名'] === '賞与引当金繰入額'
          && d['仕訳ステータス'] === '自動計上') {
        Utils.logInfo(FUNC, 'skip: already posted for ' + targetYm);
        return;
      }
    }

    // --- 6. 取引ID 発番 ---
    const trnSheet = Utils.getSheetByKey('TRN_JOUR', '42_trn_journal');
    if (!trnSheet) {
      Utils.logError(FUNC, new Error('42_trn_journal not found'));
      SpreadsheetApp.getUi().alert('🚨 42_trn_journal シートが見つかりません。');
      return;
    }
    generateTrnId_._cache = {};  // 単発実行のためキャッシュリセット
    const accrualDate = new Date(Number(targetYm.split('-')[0]), Number(targetYm.split('-')[1]) - 1, 1);
    const dateStr = Utilities.formatDate(accrualDate, Session.getScriptTimeZone(), 'yyyyMMdd');
    const trnId = generateTrnId_(trnSheet, dateStr, 0);

    // --- 7. DTO 構築 ---
    const dto = {
      '取引ID': trnId,
      '発生日(P/L計上日)': accrualDate,
      '決済日_計画': '',
      '決済日_実績': '',
      '収支区分': '支出',
      '取引先名': '',
      '科目名': '賞与引当金繰入額',
      '税区分': '対象外',
      '外貨金額': 0,
      '通貨': 'JPY',
      '税抜金額_実績': amount,
      '消費税額_実績': 0,
      '税込金額_実績': amount,
      '組織名': '指定なし_共通費など',
      'PJ名': '指定なし_共通費など',
      '決済手段': '仕訳振替',
      '仕訳ステータス': '自動計上',
      '証憑URL': '',
      '摘要': '賞与引当金 月次計上 (' + targetYm + ')',
      '管理ID': '',
      'CF支払ID': '',
      'CF支払ID枝番': '',
    };

    // --- 8. 追記 ---
    const written = JournalRepository.append([dto]);
    if (written !== 1) {
      Utils.logError(FUNC, new Error('append failed: written=' + written));
      return;
    }

    // --- 9. 通知 + 監査 ---
    Utils.toastResult(FUNC, '賞与引当金 ' + amount.toLocaleString() + '円を計上しました (' + targetYm + ')', 5);
    Utils.auditLog('RUN', '42_trn_journal', trnId, '', FUNC, null, amount, '賞与引当金月次自動計上 ' + targetYm);
  } catch (e) {
    Utils.logError(FUNC, e, 'targetYm=' + targetYm);
  }
}
```

### (2) `600_report/602_datamart_main.js` への呼び出し追加

`buildBudgetTrendDataMart()` 内で「実績最終月 (`boundaryMonthStr` 計算後 / マート書込み前)」の位置に以下を挿入:

```js
// S-16: 賞与引当金 月次自動計上(パラメータ未設定時は内部でスキップ)
try {
  var bonusTargetYm = Utils.addMonths(boundaryMonthStr, -1);  // 実績最終月(boundary は翌月)
  SubledgerService.createBonusProvision(bonusTargetYm);
} catch (e) {
  Utils.logError('buildBudgetTrendDataMart', e, 'createBonusProvision failed');
}
```

**補足**: `boundaryMonthStr` の定義位置は Read で特定する。`buildBudgetTrendDataMart(overrideBoundary)` シグネチャから `boundaryMonthStr` が内部変数として算出されている想定。実際の変数名が異なる場合は Read で確認して置換する。

## 制約

- `JournalEntryDTO` のフィールド名はシートヘッダーと完全一致(列番号ハードコード禁止)
- 科目マスタ未登録の科目名を使用しない(`賞与引当金繰入額` はマスタ登録済み。ただし `有効フラグ` は実装動作確認前に TRUE にする必要あり)
- `buildBudgetTrendDataMart` 以外からの呼び出しを追加しない(将来スコープ拡大時のみ別途)
- `03_sys_params` の手動投入が前提のため、パラメータ未設定時は必ず静かにスキップ(例外を投げない)

## エッジケース

| # | 条件 | 動作 | 理由 |
|---|------|------|------|
| 1 | `CFG_BONUS_PROVISION_AMOUNT` 未設定・0 | スキップ (logInfo) | ゼロ除算防止 |
| 2 | `CFG_BONUS_PROVISION_START_YM` / `END_YM` 未設定・形式不正 | スキップ (logInfo) | 対象期間確定不能 |
| 3 | `END_YM < START_YM` | スキップ (logError) | 月数算出不能 |
| 4 | `targetYm` が期間外 | 静かにスキップ(ログなし) | 毎月実行の騒音防止 |
| 5 | 同月計上済(3 条件一致) | スキップ (logInfo) | 冪等性 |
| 6 | 端数あり | 最終月に加算 | 年間合計一致 |
| 7 | `42_trn_journal` 未発見 | logError + alert | 致命エラー |
| 8 | append 失敗 | logError、通知なし | 不完全状態の通知抑止 |
| 9 | 対象月数 > `MONTH_ITERATION_LIMIT` | スキップ (logError) | パラメータ誤入力ガード |
| 10 | 542 の `有効フラグ=FALSE` | 検知不可(マート側で集計対象外になる) | 実データ検証で事前確認必須 |

## 実データ検証

1. `11_mst_account`: `209 賞与引当金` / `542 賞与引当金繰入額` の `有効フラグ=TRUE` を確認
2. `03_sys_params`: `CFG_BONUS_PROVISION_AMOUNT` / `START_YM` / `END_YM` の 3 行投入
3. `42_trn_journal`: 既存の `仕訳ステータス=自動計上` 行の DTO 充足状況と本案件 DTO の整合性
4. dev で 1 件計上後、`61_pl_monthly` / `62_bs_monthly` への反映を目視

## 動作確認

1. `npm run push:dev` でデプロイ
2. `03_sys_params` に 3 キーを投入(例: `AMOUNT=6000000`, `START_YM=2026-04`, `END_YM=2027-03`)
3. `11_mst_account` の 209 / 542 の `有効フラグ` を `TRUE` に更新
4. サイドバー「財務3表の更新」ボタンをクリック → `buildBudgetTrendDataMart()` 実行
5. `42_trn_journal` を開き、以下の行が 1 件追加されていることを確認:
   - `仕訳ステータス = "自動計上"`
   - `科目名 = "賞与引当金繰入額"`
   - `発生日(P/L計上日) = 2026-04-01`(対象月の1日)
   - `税込金額_実績 = 500,000`(6,000,000 / 12)
   - `摘要 = "賞与引当金 月次計上 (2026-04)"`
6. トースト通知「賞与引当金 500,000円を計上しました (2026-04)」が表示されること
7. `98_audit_log` に対応する RUN ログが記録されていること
8. **再実行テスト(冪等性)**: 同じ月で「財務3表の更新」を再クリック → `42_trn_journal` に重複行が作られないことを確認
9. **パラメータ未設定テスト**: `03_sys_params` の `CFG_BONUS_PROVISION_AMOUNT` を空にして再実行 → スキップされエラーなく完了することを確認
10. **期間外テスト**: `targetYm` が `END_YM` より後になる時期(例: 2027-04 を基準月に設定)で実行 → 計上行が追加されないことを確認
11. **端数テスト**: `AMOUNT=100`, `START=2026-04`, `END=2026-06` (3ヶ月) で実行 → 4月=33円, 5月=33円, 6月=34円(100/3=33 余り1、6月に加算)になることを確認
12. **本番適用**: dev で全テストパス後、`npm run push:prod` → prod の `03_sys_params` / `11_mst_account` を dev と同じ状態にして本番実行

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

| フェーズ | 拡張思考 | 備考 |
|---------|:--------:|------|
| Phase 1(調査・設計) | あり | 関数配置先・呼び出し元・DTO フィールド・パラメータキー命名を Read で確定 |
| Phase 2(実装) | なし | 本仕様書の「実装内容」セクションをそのままコード化 |

推奨実行モデル

工程推奨モデル理由
仕様書作成(本ドキュメント)Claude Opus 4.7複数ファイル横断の設計判断、科目マスタと案件名の差分識別、会計ロジック(端数調整・冪等性)の設計
実装(Phase 2)Claude Haiku 4.5本仕様書でコード完全定義済み。判断要素は boundaryMonthStr 変数名の現物確認のみ。残り判断があれば Sonnet にフォールバック

変更履歴

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

仕様書作成プロンプト

展開して表示
【タイムアウト回避・実行原則(v1.7・必ず遵守すること)】
1. **拡張思考の使い分け**: Phase 1(設計)では拡張思考をフル活用し、ファイル名・関数名・挿入行番号・エッジケース一覧を完全確定させる。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>` 記録)に分割。1 回の Write/Edit は約 300 行以内。
4. **各 Step で何を書くかを具体指示**: 設計判断を Phase 2 実行時に持ち込まないよう、各 Step の内容を Phase 1 で確定してから清書に入る。

======================================================================
あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者兼仕様書ライターです。
案件 S-16「役員賞与引当金の自動計上」の開発仕様書を作成してください。
仕様書を新規作成した後は、`docs/_config.json` の `nav` 配列の適切なセクションにも必ず追記してください。

---

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

以下のファイルを順に Read し、仕様書を書く前に **固有名詞・型・行番号・呼び出し経路をすべて確定** させること。Grep は発見まで。「どう書くか」の判断は必ず Read で裏取りする。

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

### 1-B: プロジェクト規約の読み込み
- `CLAUDE.md` — コーディング規約・ファイル番号体系・会計ロジックルール(仕訳振替判定、IDプレフィックス体系等)を把握する。

### 1-C: 既存テンプレートの確認
- `docs/dev/` 配下で自動仕訳・自動計上に最も近い仕様書を 1 件読み込み、セクション構成・実装プロンプト形式を把握する(候補: `dev_mas-077_settlement_date_sync.md` または `dev_mas-001_variance_analysis.md`)。

### 1-D: 関連コードの調査(以下の順で Read)

1. **`000_infra/002_constants.js`**
   - `Constants.getParam(key, defaultVal)` の実装(引数の型・`03_sys_params` シートの読み方)を確認する。
   - `ID_PREFIX_MAP` で `42_trn_journal` エントリのプレフィックス (`TRN_`) と `isDate` フラグを確認する。
   - `SHEET_DEFAULTS` の構造(`{ pattern, prefix, defaults }` オブジェクト配列)を確認し、仕訳シートのデフォルト設定有無を確認する。

2. **`000_infra/003_contracts.js`**
   - `JournalEntryDTO` の `@typedef` を全フィールド確認する(`収支区分` / `税区分` / `税抜金額_実績` / `消費税額_実績` / `税込金額_実績` / `決済手段` / `仕訳ステータス` / `取引ID` の型と許容値を特定する)。

3. **`200_data/202_repository.js`**
   - `JournalRepository.findAll()` の戻り値型(`{ headers, dtos }`)を確認する。
   - `JournalRepository.append(dtos)` の引数型と `lastRowCol` のデフォルト値を確認する。
   - `appendDtosToSheet_` の内部実装(最終行判定列)を確認する。

4. **`000_infra/004_utils.js`**
   - `Utils.logInfo(funcName, message)` / `Utils.toastResult(funcName, message, duration)` / `Utils.auditLog(operation, targetSheet, targetId, targetCol, funcName, beforeValue, afterValue, note)` の引数シグネチャを確認する。
   - `Utils.parseDateToYm(val)` の戻り値形式(`"YYYY-MM"`)を確認する。
   - `Utils.addMonths(ymStr, months)` の仕様を確認する(対象期間の月数ループに使用)。

5. **`400_domain/400_rpa_common.js`**
   - `RpaCommon` 内の ID 生成関数(`generateNextId` 等)の実際の関数名・引数・戻り値を Read で確認する。`JournalRepository.append` と組み合わせて `取引ID(TRN_YYYYMMDD_NNNN)` をどう生成するかのパターンを特定する。関数名は推測せず、必ず Read して実在する文字列のみ引用すること。

6. **`400_domain/410_subledger_engine.js`**
   - `SubledgerService` の公開 API・既存の自動仕訳計上パターンを確認する。
   - 新関数 `createBonusProvisionJournal_(targetYm)` の配置先として適切か(同ファイルへの追加か、新ファイルかを判断する)。

7. **`100_config/101_sys_config.js`**
   - メニュー定義(`onOpen()` 内の `ui.createMenu`)を Read し、呼び出し元として追加するメニュー項目の実在する文字列を確認する(**推測で造語しない**)。
   - `buildAllDatamarts_` または月次自動処理の起点となる関数名を Read で特定する(`600_report/` ファイルも必要に応じて確認する)。

8. **`11_mst_account` シートの科目名確認(MCP 使用)**
   - `役員賞与引当金繰入` が科目マスタに登録済みかを確認する。未登録の場合は「人間が検討すべき事項」に記載し、実装前にマスタ追加を必須条件として明記する。
   - 合わせて `役員賞与引当金`(B/S 貸方科目)の登録有無も確認する。

---

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

出力先: `docs/dev/dev_mas-088_bonus_provision.md`(ID は大文字 `MAS-088`)

**【絶対厳守】1 回のツール呼び出しで全内容を出力しない。以下の 5 Step に分割して実行する。**

---

### Step 2-1: 骨格の作成(File Write・約 20 行)

セクション見出しのみ。本文は空で可。以下の構成で出力する:

(見出し一覧: 概要/目的/現在のコード/修正方針/影響範囲/注意事項/エッジケース/実データ検証/関連ドキュメント/人間が検討すべき事項/実装プロンプト/推奨実行モデル/変更履歴/仕様書作成プロンプト)

---

### Step 2-2: 概要〜注意事項の追記(File Edit または Bash heredoc・約 300 行)

Phase 1 で確定した内容を清書する。以下の事項を含めること。

**概要テーブル**: 案件ID / カテゴリ / Phase / 優先度 / 所要時間 / 対象ファイル(Phase 1 で特定したファイル名を記載)/ 前提案件

**アーキテクチャ決定事項**(修正方針セクション内):
- **新関数の配置先**: Phase 1 の調査結果に基づき決定する。`410_subledger_engine.js` への追加、または新規ドメインファイルのいずれかを根拠付きで選択する。関数名: `createBonusProvisionJournal_(targetYm)`(引数 `targetYm` は `"YYYY-MM"` 形式の文字列)。
- **呼び出し元**: Phase 1 で特定した実在する関数名・ファイル名を記載する(`buildAllDatamarts_` は仮称。実在を確認してから記載すること)。
- **設定値の管理**: `03_sys_params` シートを `Constants.getParam(key, defaultVal)` で読み込む。パラメータキーは以下の 3 つ:
  - `BONUS_PROVISION_AMOUNT`(年間支給予定総額・数値)
  - `BONUS_PROVISION_START_YM`(対象開始年月・`"YYYY-MM"` 文字列)
  - `BONUS_PROVISION_END_YM`(対象終了年月・`"YYYY-MM"` 文字列)
- **計算ロジック**: 月次引当額 = `年間総額 / 対象月数`(整数除算)。端数(`年間総額 % 対象月数`)は最終月の計上額に加算する。
- **JournalEntryDTO の必須フィールド**(Phase 1 の Read 結果と照合して記載する):
  - `収支区分`: `"支出"`
  - `科目名`: `"役員賞与引当金繰入"`(要マスタ登録確認)
  - `税区分`: `"対象外"`
  - `通貨`: `"JPY"`
  - `税抜金額_実績`: 月次引当額
  - `消費税額_実績`: `0`
  - `税込金額_実績`: 月次引当額(税抜と同値)
  - `決済手段`: `"仕訳振替"`
  - `仕訳ステータス`: `"自動計上"`
  - `発生日(P/L計上日)`: 対象年月の 1 日(例: `2026-04-01`)
  - `摘要`: `"役員賞与引当金 月次計上 (YYYY-MM)"` 形式
  - `取引先名` / `組織名` / `PJ名`: Phase 1 で既存自動仕訳パターンを確認して決定する(未定の場合は「要確認」と明記)
  - `取引ID`: Phase 1 で特定した ID 生成関数を使用して `"TRN_YYYYMMDD_NNNN"` 形式で採番する
- **二重計上防止**: 関数冒頭で `JournalRepository.findAll()` を実行し、`dtos` 配列を走査する。判定条件: `Utils.parseDateToYm(dto['発生日(P/L計上日)']) === targetYm` かつ `dto['科目名'] === '役員賞与引当金繰入'` かつ `dto['仕訳ステータス'] === '自動計上'` の 3 条件が全て一致する行が存在する場合はスキップし `Utils.logInfo()` でログ出力する。
- **データ永続化**: `JournalRepository.append([dto])` で `42_trn_journal` に追記する。

**注意事項**(番号付きリスト):
- `03_sys_params` のパラメータキーは文字列の完全一致で読み込む。タイポ・全角半角混在に注意。
- `役員賞与引当金繰入` が `11_mst_account` に未登録の場合、`InvoiceRepository.append` 内の科目マスタ照合が通らない可能性がある(仕訳シートへの直接 append は通るが、諸表区分・大分類が空になる)。マスタ登録を実装前提条件とする。
- 列参照はヘッダー名ベースのみ。`JournalEntryDTO` のフィールド名とシートヘッダーは完全一致させること。
- `仕訳振替` の判定は `=== "仕訳振替"` の完全一致(CLAUDE.md規約)。

---

### Step 2-3a: エッジケース〜人間が検討すべき事項の追記

(エッジケーステーブル、スコープ外事項、実データ検証セクション、Human-in-the-Loop、人間が検討すべき事項)

### Step 2-3b: 実装プロンプト〜変更履歴の追記

実装プロンプトは **バッククォートで囲まず、行頭 4 スペースインデント** で出力すること。
(推奨実行モデルテーブル、変更履歴テーブルを含める)

### Step 2-4: 仕様書作成プロンプト全文の記録

仕様書末尾の「## 仕様書作成プロンプト」セクションに、この `<instruction>` タグで囲まれたプロンプト全文を `<details><summary>展開して表示</summary>...</details>` 形式で記録する。

---

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

### 3-A: `_config.json` にナビゲーション登録(必須)

`docs/_config.json` の `nav` 配列の適切なセクションに追加する。S-16 の案件カテゴリ(Phase 1 の TODO_future.md 確認結果)に応じて以下から選択する:
- 自動仕訳・RPA 系 → `§E.6` パイプライン・RPA・外部連携
- 財務諸表・データマート系 → `§E.4` データマート・財務諸表

### 3-B: changelog.md に追記

### 3-C: コミット&プッシュ

(branch: docs/dev-S-16)