概要

項目内容
案件IDMAS-038
カテゴリFP&A / R&D
PhaseP3
優先度
所要時間4-6 時間
対象ファイル000_infra/002_constants.jsRD_TAX_CREDIT_RULES 追加・MENU_DEFINITION にメニュー追加)
100_config/101_sys_config.jsMST_ACCT ヘッダー拡張・69_fp_tax_credit_sim DDL 追加)
600_report/610_datamart_tax_credit.js(新規: buildTaxCreditSimulation()
前提案件F-34(R&D費用の自動分類・集計)※未実装。暫定は「研究開発費対象」列を手動設定で運用
関連案件F-36(R&D ROI ダッシュボード)— 試験研究費集計の概念が共通

目的

試験研究費の税額控除(中小企業技術基盤強化税制)の適用可否判定と控除額を自動計算し、 新規シート 69_fp_tax_credit_sim に参考値として出力する。R&D 投資意思決定を行う前に 「研究開発費として扱う科目の合計」と「適用可能な税額控除額」を可視化することで、 税制優遇の最大活用を支援する。本機能は税務申告を代替するものではなく、 最終判断は顧問税理士に委ねる前提の「参考シミュレーション」である。

現在のコード

① 定数定義 — 000_infra/002_constants.js

TAX_RATES(累進課税ブラケット)は定義済み。RD_TAX_CREDIT_RULES は未定義。

// 000_infra/002_constants.js L21-30
TAX_RATES: {
  brackets: [
    { upTo: 8000000, national: 0.165, local: 0.049 },  // 〜800万: 法人税15%ベース
    { upTo: Infinity, national: 0.256, local: 0.080 }, // 800万超: 法人税23.2%ベース
  ],
  localMinimumAnnual: 70000,
  foundingYear: 2025,
  foundingMonth: 11,
},

メニュー定義は Constants.MENU_DEFINITION(L206-324)に宣言的に記述され、onOpen() が ループして動的生成する。FP&A 系メニューは '📋 サイドバー: 📊 マート更新' カテゴリに集約されている (L230-239)。

// 000_infra/002_constants.js L230-239(抜粋・既存)
{
  category: '📋 サイドバー: 📊 マート更新',
  source: 'sidebar',
  items: [
    { label: '財務3表の更新', funcName: 'buildBudgetTrendDataMart', ... },
    { label: '📅 基準年月指定で更新', funcName: 'buildDataMartWithCustomBoundary', ... },
    { label: 'プロジェクト別 採算', funcName: 'buildProjectProfitability', ... },
    { label: '📊 KPIダッシュボード再描画', funcName: 'buildKpiDashboard', ... },
    { label: '📸 前年度P/Lスナップショット', funcName: 'savePlSnapshot', ... },
  ]
},

② 仕訳 DTO — 000_infra/003_contracts.js

JournalEntryDTO(L96-)に以下のフィールドが定義されている(仕様書で直接参照する)。

取引ID / 発生日(P/L計上日) / 決済日_計画 / 決済日_実績
収支区分 / 取引先名 / 科目名 / 税区分
外貨金額 / 通貨 / 税抜金額_実績 / 消費税額_実績 / 税込金額_実績
組織名 / PJ名 / 決済手段 / 仕訳ステータス / 証憑URL / 摘要 ...

③ Repository — 200_data/202_repository.js

仕訳取得は JournalRepository.findAll()、科目マップは AccountRepository.findAsMap() (キャッシュ付き)を利用する。両者の戻り値は既存コードで確認済み。

// 200_data/202_repository.js L259-298
var JournalRepository = {
  findAll: function() {
    return readSheetAsDtos_(JournalRepository._getSheet());
    // 戻り値: { headers: string[], dtos: JournalEntryDTO[] }
  },
  ...
};

// 200_data/202_repository.js L304-350
var AccountRepository = {
  findAll: function() { return readSheetAsDtos_(AccountRepository._getSheet()); },
  findAsMap: function() {
    if (AccountRepository._cache) return AccountRepository._cache;
    // 有効フラグ=FALSE をスキップして { 科目名: {stmt, cat} } を構築
    // ...
    AccountRepository._cache = map;
    return map;
  },
  _cache: null,
  resetCache: function() { AccountRepository._cache = null; },
};

⚠ 注意: findAsMap()stmt(諸表区分)と cat(大分類)のみを返す。 「研究開発費対象」列を DTO 経由で参照するには、findAsMap() の拡張、 もしくは findAll() で DTO 配列を取得して直接 dto['研究開発費対象'] を参照する。 本仕様では後者(findAll() ベース)で実装する。

④ DDL — 100_config/101_sys_config.js

setupAllSchemas() 内で MST_ACCT(= 11_mst_account)の DDL ヘッダーは以下 11 列。

// 100_config/101_sys_config.js L827
'MST_ACCT': {
  headers: ["有効フラグ","主科目コード","諸表区分","大分類","表示区分","表示科目",
            "正式科目名","固変区分","デフォルト税率","科目名","説明"],
  color: "#666666"
},

FP&A 系マート(61-65 / 71-73 / 81-85)は confSheet.appendRow() による 01_sys_config 登録 + schemas オブジェクトへのキー追加の 2 段で定義される。新規の 69_fp_tax_credit_sim も同じパターンで追加する(システムキー例: FP_TAX_SIM)。

⑤ 既存データマートの構造 — 600_report/601-603

集計基準年月(会計年度 Aug-Jul 起点)は 602_datamart_main.js で毎回動的に計算される。 「61_pl_monthly の C1 セル」のような固定参照は存在しない(Gemini の示唆した位置は 実在せず、Read で確認済み)。会計年度の起点計算ロジックは以下のとおり。

// 600_report/602_datamart_main.js L205-207
const today = new Date();
const startYear = (today.getMonth() + 1 >= 8) ? today.getFullYear() : today.getFullYear() - 1;
const targetMonths = [];
for (let i = 0; i < 12; i++) {
  const d = new Date(startYear, 7 + i, 1);
  targetMonths.push(`${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}`);
}
// targetMonths[0] = 'YYYY-08', targetMonths[11] = 'YYYY-07'

法人税額は 61_pl_monthly に「法人税等」「🤖 法人税等・国税(〜800万:16.5% / 超:25.6%)」 「🤖 法人税等・地方税(〜800万:4.9% / 超:8.0%)」の 3 行として出力されるが(603_datamart_pl.js L50-51, L74-76)、ラベル文字列が動的(brackets に依存)でパースが脆くなる。 本仕様では「61_pl_monthly を読まず、Constants.TAX_RATES と仕訳から計算した税引前利益を 使って法人税額を再計算する」方式を採る(既存 dmCalcPl_ と同じ計算ロジックを再利用)。 これにより 61_pl_monthly が未更新でも F-38 単独で動作可能。

修正方針

Step 1: MST_ACCT DDL 拡張

100_config/101_sys_config.js L827 の MST_ACCT ヘッダー配列末尾に「研究開発費対象」列を 追加する(TRUE/FALSE チェックボックス)。

// Before
'MST_ACCT': { headers: ["有効フラグ",...,"科目名","説明"], color: "#666666" },

// After
'MST_ACCT': {
  headers: ["有効フラグ","主科目コード","諸表区分","大分類","表示区分","表示科目",
            "正式科目名","固変区分","デフォルト税率","科目名","説明","研究開発費対象"],
  color: "#666666"
},

冪等: 既存セルに FALSE を代入し破壊的変更は起こさない(DDL は列を追加するのみ)。

Step 2: RD_TAX_CREDIT_RULES 定数追加

000_infra/002_constants.jsTAX_RATES 直後(L30 の }, 直後)に RD_TAX_CREDIT_RULES を追加する。中小企業技術基盤強化税制(資本金 1 億円以下・青色申告書提出法人が対象)の パラメータを定数化する。計算式内へのハードコード禁止。

// 000_infra/002_constants.js L30 以降に追加
RD_TAX_CREDIT_RULES: {
  // 中小企業技術基盤強化税制(中小企業型)
  baseRate: 0.12,          // 基本控除率(試験研究費の12%)
  increaseThreshold: 0.05, // 増減率閾値(前年比5%超で特例加算)
  incrementBonus: 0.10,    // 増減率超過時の加算率上限(最大10%)
  maxCreditRate: 0.25,     // 法人税額に対する控除上限率(25%)
  // 税制改正があった場合はこの定数のみ変更する(計算関数は触らない)
  // 将来課題: 研究開発税制の総額型(大企業向け)ロジックは未実装
},

Step 3: メニュー定義への追加

000_infra/002_constants.js L230-239 の '📋 サイドバー: 📊 マート更新' カテゴリ items に以下を追加する(既存の 📸 前年度P/Lスナップショット の下)。

{ label: '研究開発税制シミュレーションを更新', funcName: 'buildTaxCreditSimulation',
  description: '試験研究費の集計と税額控除シミュレーションを 69_fp_tax_credit_sim に出力' },

onOpen()Constants.MENU_DEFINITION を iterate するため、101_sys_config.js への onOpen() 直接編集は不要(Gemini の示唆したハードコード追記はハルシネーション)。

Step 4: 新規シート 69_fp_tax_credit_sim の DDL 登録

100_config/101_sys_config.jssetupAllSchemas() 内で以下 2 箇所を変更する。

(a) 01_sys_config 登録(L795 PL_VAR の直後に追記):

if (!existKeys.includes('FP_TAX_SIM'))
  confSheet.appendRow(['FP_TAX_SIM', '', '69_fp_tax_credit_sim', 'FP&A: 研究開発税制シミュレーション']);

(b) schemas 辞書に追加(既存の FP&A 系スキーマ近傍):

'FP_TAX_SIM': {
  headers: ["項目", "値", "備考"],
  color: "#1155cc"
},

Step 5: 新規モジュール 600_report/610_datamart_tax_credit.js

ファイル番号 610 は既存 601-609 と衝突しない(CLAUDE.md L71 で 601-608 の範囲が 宣言されているが、609(KPI)が既存、610 は空き)。モジュール構成:

// 600_report/610_datamart_tax_credit.js
function buildTaxCreditSimulation() {
  const FUNC = 'buildTaxCreditSimulation';
  try {
    // 1. キャッシュ無効化(DDL 拡張後の初回実行で古いマップが残る可能性を排除)
    AccountRepository.resetCache();

    // 2. 科目マスタ(全カラム取得: 「研究開発費対象」列を参照するため findAll を使用)
    const acctResult = AccountRepository.findAll();
    const rdColIdx = acctResult.headers.indexOf('研究開発費対象');
    if (rdColIdx === -1) {
      SpreadsheetApp.getActiveSpreadsheet().toast(
        '11_mst_account に「研究開発費対象」列がありません。setupAllSchemas を実行してください。',
        '⚠ F-38 未準備', 10);
      return;
    }

    // 3. 「研究開発費対象=TRUE」の科目名セットを構築
    const rdAccounts = {};
    for (const dto of acctResult.dtos) {
      const flag = dto['有効フラグ'];
      if (flag === false || String(flag).toUpperCase() === 'FALSE') continue;
      const mark = dto['研究開発費対象'];
      if (mark === true || String(mark).toUpperCase() === 'TRUE') {
        rdAccounts[String(dto['科目名'] || '').trim()] = true;
      }
    }

    // 4. 会計年度の範囲(Aug-Jul 起点、既存 602 と同じ計算)
    const today = new Date();
    const startYear = (today.getMonth() + 1 >= 8) ? today.getFullYear() : today.getFullYear() - 1;
    const fyStart = `${startYear}-08`;
    const fyEnd = `${startYear + 1}-07`;
    const prevFyStart = `${startYear - 1}-08`;
    const prevFyEnd = `${startYear}-07`;

    // 5. 仕訳集計(当期試験研究費・前年度試験研究費・税引前利益)
    const jrn = JournalRepository.findAll();
    let rdTotalCurrent = 0, rdTotalPrevious = 0;
    // 税引前利益は既存 dmCalcPl_ と同じ方式で:
    //   (収益系科目合計 - 費用系科目合計) を「法人税等」セクション手前まで集計
    // ここでは簡易版として AccountRepository.findAsMap の stmt/cat でフィルタ
    const acctMap = AccountRepository.findAsMap();
    let preTaxProfit = 0;
    for (const dto of jrn.dtos) {
      const acc = String(dto['科目名'] || '').trim();
      const amt = Number(dto['税抜金額_実績'] || 0);
      const pYm = Utils.ymOf(dto['発生日(P/L計上日)']);  // YYYY-MM 文字列化ヘルパー

      // 試験研究費の集計
      if (rdAccounts[acc]) {
        if (pYm >= fyStart && pYm <= fyEnd) rdTotalCurrent += amt;
        else if (pYm >= prevFyStart && pYm <= prevFyEnd) rdTotalPrevious += amt;
      }

      // 税引前利益の集計(法人税等セクションを除く)
      const mapped = acctMap[acc];
      if (!mapped || pYm < fyStart || pYm > fyEnd) continue;
      const disp = mapped.stmt || '';
      if (disp !== 'P/L') continue;
      const cat = mapped.cat || '';
      if (cat.includes('法人税')) continue;  // 法人税等は控除前利益からは除く
      // 収益 = 正、費用 = 負
      const shuushi = String(dto['収支区分'] || '');
      preTaxProfit += (shuushi === '収入') ? amt : -amt;
    }

    // 6. 法人税額(= 国税+地方税)を Constants.TAX_RATES から再計算
    const tr = Constants.TAX_RATES;
    let corpTax = 0;
    let remain = Math.max(0, preTaxProfit);
    let from = 0;
    for (const b of tr.brackets) {
      const slice = Math.min(remain, b.upTo - from);
      if (slice <= 0) break;
      corpTax += slice * (b.national + b.local);
      remain -= slice;
      from = b.upTo;
      if (remain <= 0) break;
    }
    // 地方税均等割(赤字でも発生)は控除計算では無視(控除対象は法人税額のみ)

    // 7. 控除率の算出(前年比)
    const r = Constants.RD_TAX_CREDIT_RULES;
    let creditRate = r.baseRate;
    let incRate = null;
    if (rdTotalPrevious > 0) {
      incRate = (rdTotalCurrent - rdTotalPrevious) / rdTotalPrevious;
      if (incRate > r.increaseThreshold) {
        // 増減率超過時の加算(簡略化: baseRate + bonus。本来は段階的計算)
        creditRate = r.baseRate + r.incrementBonus;
      }
    }

    // 8. 控除額・控除上限・適用額
    const rawCredit = rdTotalCurrent * creditRate;
    const maxCredit = corpTax * r.maxCreditRate;
    const appliedCredit = Math.min(rawCredit, maxCredit);

    // 9. 出力(sheet.clearContents() で冪等化、A1:C2 は赤背景免責事項)
    const ss = getWebSpreadsheet_();
    const sheet = ss.getSheetByName('69_fp_tax_credit_sim');
    if (!sheet) throw new Error('69_fp_tax_credit_sim シートがありません。setupAllSchemas を実行してください。');
    sheet.clearContents();
    sheet.clearFormats();

    // A1:C2 免責事項
    const disclaimer = '【重要】本シミュレーションは参考値であり、実際の税額控除の適用を保証するものではありません。最終的な税務申告については、必ず顧問税理士にご確認ください。';
    sheet.getRange('A1:C2').merge().setValue(disclaimer)
      .setBackground('#cc0000').setFontColor('#ffffff').setFontWeight('bold')
      .setWrap(true).setVerticalAlignment('middle');

    // 4行目以降: 項目・値・備考
    const rows = [
      ['項目', '値', '備考'],
      ['対象会計年度', `${fyStart} 〜 ${fyEnd}`, ''],
      ['当期試験研究費', rdTotalCurrent, `「研究開発費対象=TRUE」科目の税抜金額_実績合計`],
      ['前年度試験研究費', rdTotalPrevious, '増減率の基礎。データなしなら baseRate を適用'],
      ['増減率', incRate, incRate === null ? '前年度データなし' : ''],
      ['適用控除率', creditRate, incRate !== null && incRate > r.increaseThreshold ? 'baseRate + incrementBonus' : 'baseRate'],
      ['控除額(上限適用前)', rawCredit, '試験研究費 × 適用控除率'],
      ['当期税引前利益', preTaxProfit, '仕訳ベースで再計算'],
      ['当期法人税額', corpTax, 'Constants.TAX_RATES で再計算(均等割除く)'],
      ['控除上限額', maxCredit, `法人税額 × ${r.maxCreditRate * 100}%`],
      ['最終控除適用額', appliedCredit, 'MIN(控除額, 控除上限額)'],
    ];
    sheet.getRange(4, 1, rows.length, 3).setValues(rows);
    // 数値フォーマット
    sheet.getRange(5, 2, rows.length - 1, 1).setNumberFormat(Constants.NUMBER_FORMATS.CURRENCY);

    SpreadsheetApp.getActiveSpreadsheet().toast(
      `✅ 試験研究費: ${Math.round(rdTotalCurrent).toLocaleString()}円 / 最終控除: ${Math.round(appliedCredit).toLocaleString()}円`,
      'F-38 完了', 10);
    Utils.logInfo(FUNC, `RD総額=${rdTotalCurrent} 控除=${appliedCredit}`);
  } catch (e) {
    Utils.logError(FUNC, e);
    SpreadsheetApp.getActiveSpreadsheet().toast('❌ F-38 失敗: ' + e.message, 'エラー', 15);
  }
}

影響範囲

変更種別ファイル変更量(概算)
新規600_report/610_datamart_tax_credit.js+180 行
追加000_infra/002_constants.js+10 行(RD_TAX_CREDIT_RULES +1 メニュー項目)
追加100_config/101_sys_config.js+5 行(MST_ACCT ヘッダー +1 列 / FP_TAX_SIM DDL +2 箇所)

破壊的変更なし:

  • 42_trn_journal / 11_mst_account / 69_fp_tax_credit_sim への書き込みは 本機能からは 11_mst_account の列追加(DDL)と 69_fp_tax_credit_sim の上書きのみ。
  • 既存の TAX_RATES / RPA_DEFAULTS 等の定数オブジェクトは変更しない。
  • 既存仕訳・既存レポートへの影響なし(読み取り専用)。

注意事項

  1. F-34 未実装期の暫定運用: 「研究開発費対象」列は F-34(R&D 費用の自動分類・集計) の完成まで手動設定となる。運用ガイドに「新規科目を 11_mst_account に追加したら、 研究開発費対象かを TRUE/FALSE で明示する」を明記する。

  2. AccountRepository のキャッシュ: buildTaxCreditSimulation() 冒頭で AccountRepository.resetCache() を必ず呼ぶ。DDL 実行で列追加直後はキャッシュが stale になっている可能性があるため。

  3. 仕訳フィールド名の固定: JournalEntryDTO の「科目名」「税抜金額_実績」 「発生日(P/L計上日)」「収支区分」は 000_infra/003_contracts.js L97+ の @typedef の実在文字列と完全一致すること。

  4. 税務判断の代替禁止: 赤背景・白字の免責事項をシート先頭 A1:C2 に必ず挿入する。 免責文言は本仕様書 Step 5 の disclaimer を正とする。

  5. 増減率計算の簡略化: 本仕様では「増減率 > 閾値なら +incrementBonus、 そうでなければ baseRate のみ」の 2 段階モデル。実際の制度は段階的逓増だが、 税制改正への追従で随時見直す前提とし、計算ロジックを簡潔に保つ。

  6. 均等割の扱い: Constants.TAX_RATES.localMinimumAnnual(年 7 万円)は 赤字法人でも発生する最低税額だが、税額控除の対象外(控除は法人税額 × 25%)。 控除計算では corpTax に含めない。

エッジケース

条件表示値 / 振る舞い理由
試験研究費がゼロ(TRUE 設定科目に仕訳なし)当期試験研究費: 0 / 控除額: 0 / 最終控除適用額: 0計算対象費用がなく rawCredit = 0 × rate = 0
法人税額がゼロまたはマイナス(赤字)控除上限額: 0 / 最終控除適用額: 0Math.max(0, preTaxProfit) で 0 に丸め、corpTax × maxCreditRate = 0
前年度試験研究費データなし(設立 1 期目 等)増減率: 空白 / 適用控除率: baseRaterdTotalPrevious === 0 で増減率計算をスキップ
前年度試験研究費 > 0 かつ増減率 ≤ 閾値適用控除率: baseRateincRate <= increaseThreshold の分岐
11_mst_account に「研究開発費対象」列が未追加(DDL 未実行)Toast で警告表示・処理中断acctResult.headers.indexOf('研究開発費対象') === -1 で early return
69_fp_tax_credit_sim シートが未作成エラー throw(Utils.logError + Toast)setupAllSchemas 未実行を検知して明示エラー
研究開発費対象=TRUE の有効フラグ FALSE 科目集計対象外findAll() の結果を 有効フラグ で事前フィルタ
仕訳ステータス=「仕訳振替」の仕訳集計対象(意図的)会計上は試験研究費として計上済み。ただし二重計上が疑われる場合は手動確認
外貨仕訳(通貨 ≠ JPY)税抜金額_実績(JPY 換算済み)を使用GAS 会計は全仕訳を JPY 換算後に記帳する設計
複数年の仕訳が混在pYm で会計年度(Aug-Jul)に絞り込み前年度比較のため当期/前期を厳密に分離

実データ検証

実装前に MCP/GAS エディタ等で確認する項目:

  1. 11_mst_account の現状ヘッダー列数確認 — DDL 変更前のヘッダーを取得し、末尾に 「研究開発費対象」を追加しても既存データ行の列ズレが起きないこと(11 列 → 12 列)。
  2. 「研究開発費対象」候補科目のサンプル確認42_trn_journal の「科目名」列で、 「研究開発費」「試験研究費」「R&D」等を含む科目に実仕訳が存在するか。最低 1 件の サンプルがないと結果 = 0 となり検証困難。
  3. 会計年度切替の検証today.getMonth() + 1 >= 8 の分岐で 7 月末と 8 月初で startYear が変わる。月替わり時の動作を dev 環境で手動検証(日時をモック or overrideBoundary 的な機構は本機能にはないため、startYear を一時定数化してテスト)。
  4. 前年度仕訳の有無prevFyStartprevFyEnd に該当する仕訳が存在するか (設立 1 期目なら存在しない → baseRate 適用パスの検証が必要)。
  5. 税引前利益の符号確認 — 赤字期における Math.max(0, preTaxProfit) の動作を 手動サンプルで確認(控除上限が 0 になることを確認)。

関連ドキュメント

仕様書 / リソース関連箇所
CLAUDE.mdGAS ファイル番号体系(600_report/610 の番号割当)、setupAllSchemas 動作確認ルール
F-01 予実差異分析FP&A レポートの仕様書フォーマット・テンプレート
F-36 R&D ROI ダッシュボード研究開発費集計の概念整合(「研究開発費対象」列の共有)
TODO_future.md F-34前提案件「R&D 費用の自動分類・集計」(未実装。本案件は手動運用を前提)
統合テスト手順DDL 変更後の setupAllSchemas 実行・再描画検証
中小企業技術基盤強化税制(国税庁)税制の公式出典(税制改正時の一次ソース)

人間が検討すべき事項

  • 税制改正への追従方法: Constants.RD_TAX_CREDIT_RULES の値を改正通知の都度更新する 運用を定義する。担当者(CFO / 税理士連携担当)・タイミング(毎年 3 月末〜4 月)・ 改訂時の動作確認フロー(dev での試算 → 顧問税理士レビュー → prod 反映)を明記する。 ※ 本仕様では値のみ定数化し、計算ロジック自体は関数で固定している点に注意。 段階的逓増ロジックや法改正で計算構造が変わる場合は本関数の改修が必要。
  • 顧問税理士との適用判定フロー: 本機能は参考値の自動計算のみ。実際の適用判定・ 申告書作成は税理士の領域であることを免責事項で明示し、運用ルールで以下を徹底する。
    • シミュレーション結果の PDF / スクリーンショットを税理士に共有
    • 税理士の判定を受けて申告書を確定
    • 最終数値と本シミュレーションの差分理由を年 1 回レビュー
  • F-34 未実装期間中の代替手順: 「研究開発費対象」列の TRUE/FALSE 設定フロー。 新規科目登録時のチェックリスト化・四半期ごとの洗替えレビューを推奨する。 F-34 実装後は RPA ベースの自動判定に移行する前提。
  • 将来の総額型対応: 大企業(資本金 1 億円超)向けの「総額型」は控除率算出方法が 大きく異なる(売上高試験研究費割合・平均売上金額等)。将来的に総額型を追加する場合は RD_TAX_CREDIT_RULES{ smeType: {...}, generalType: {...} } に分離し、 buildTaxCreditSimulation() に法人種別パラメータを追加する設計変更が必要。
  • 複数年シミュレーションの拡張可否: 現仕様は単年度のみ。3 年分の比較表示 (過年度実績 2 期 + 当期予測)を求める場合は出力行数を拡張し、前々年度データの 有無フラグを追加する。

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

あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者です。
案件 F-38「研究開発税制の適用シミュレーション」を実装してください。

## 実行前タスク
以下のファイルを Read し、実装に使う固有名詞・行番号を確定させること(推測禁止):
- `000_infra/002_constants.js`:
    - `TAX_RATES` の定義行(L21-30)を確認
    - `RD_TAX_CREDIT_RULES` の追加位置(`TAX_RATES` 直後)
    - `MENU_DEFINITION` の `'📋 サイドバー: 📊 マート更新'` カテゴリ(L230-239)
- `000_infra/003_contracts.js`:
    - `JournalEntryDTO`(L96-)の「科目名」「税抜金額_実績」「発生日(P/L計上日)」
      「収支区分」「有効フラグ」の正確な文字列
- `200_data/202_repository.js`:
    - `JournalRepository.findAll()`(L270)の戻り値構造
    - `AccountRepository.findAll()`(L315)・`findAsMap()`(L323)・`resetCache()`(L347)
- `100_config/101_sys_config.js`:
    - `onOpen()`(L323)は `Constants.MENU_DEFINITION` をループする実装
      → メニュー追加は `002_constants.js` 側で行う
    - `setupAllSchemas()` 内の `confSheet.appendRow` 群(L770-824)と
      `MST_ACCT` ヘッダー(L827)・`schemas` 辞書
- `600_report/602_datamart_main.js`:
    - 会計年度起点の算出ロジック(L205-207: `startYear` / `targetMonths`)

## 修正対象ファイル
- `000_infra/002_constants.js` — `RD_TAX_CREDIT_RULES` 追加 + メニュー項目追加
- `100_config/101_sys_config.js` — `MST_ACCT` ヘッダー拡張 + `FP_TAX_SIM` DDL 追加
- `600_report/610_datamart_tax_credit.js` — 新規作成(`buildTaxCreditSimulation()`)

## 実装内容

### (1) 000_infra/002_constants.js: 定数・メニュー追加

**(1-a) `TAX_RATES` 直後に `RD_TAX_CREDIT_RULES` を追加**:

  RD_TAX_CREDIT_RULES: {
    // 中小企業技術基盤強化税制(中小企業型)
    baseRate: 0.12,          // 基本控除率(試験研究費の12%)
    increaseThreshold: 0.05, // 増減率閾値(前年比5%超で特例加算)
    incrementBonus: 0.10,    // 増減率超過時の加算率上限(最大10%)
    maxCreditRate: 0.25,     // 法人税額に対する控除上限率(25%)
    // 税制改正時はこの定数のみ変更する(計算関数は触らない)
    // 将来課題: 総額型(大企業向け)ロジックは未実装
  },

**(1-b) `MENU_DEFINITION` の `'📋 サイドバー: 📊 マート更新'` カテゴリに追加**:

  `📸 前年度P/Lスナップショット` の直後に以下を追加する:

  { label: '研究開発税制シミュレーションを更新', funcName: 'buildTaxCreditSimulation',
    description: '試験研究費の集計と税額控除シミュレーションを 69_fp_tax_credit_sim に出力' },

### (2) 100_config/101_sys_config.js の変更

**(2-a) `MST_ACCT` DDL ヘッダー末尾に「研究開発費対象」列を追加** (L827 付近):

  Before: headers: ["有効フラグ",..."科目名","説明"]
  After:  headers: ["有効フラグ",..."科目名","説明","研究開発費対象"]

**(2-b) `01_sys_config` 登録** — `PL_VAR` の直後(L795 付近)に追加:

  if (!existKeys.includes('FP_TAX_SIM'))
    confSheet.appendRow(['FP_TAX_SIM', '', '69_fp_tax_credit_sim', 'FP&A: 研究開発税制シミュレーション']);

**(2-c) `schemas` 辞書に `FP_TAX_SIM` を追加** (既存 FP&A 系スキーマ近傍):

  'FP_TAX_SIM': { headers: ["項目", "値", "備考"], color: "#1155cc" },

### (3) 600_report/610_datamart_tax_credit.js の新規作成

以下の仕様で `buildTaxCreditSimulation()` を実装する(本文冒頭で `AccountRepository.resetCache()`
を必ず呼ぶ):

1. `AccountRepository.resetCache()` → `AccountRepository.findAll()` でヘッダー +
   DTO 配列を取得
2. `acctResult.headers.indexOf('研究開発費対象')` が -1 なら Toast エラーで return
3. `有効フラグ !== FALSE` かつ `研究開発費対象 === TRUE` の科目名を Set に構築
4. 会計年度を算出:
     const today = new Date();
     const startYear = (today.getMonth() + 1 >= 8) ? today.getFullYear() : today.getFullYear() - 1;
     const fyStart = `${startYear}-08`;
     const fyEnd = `${startYear + 1}-07`;
     const prevFyStart = `${startYear - 1}-08`;
     const prevFyEnd = `${startYear}-07`;
5. `JournalRepository.findAll()` で全仕訳 DTO を取得し、各仕訳について:
     - 「発生日(P/L計上日)」を YYYY-MM 文字列化(`Utils.ymOf` 等を使用、
       存在しなければ `Utils.formatYm` / インライン実装)
     - `研究開発費対象` の科目かつ当期なら `rdTotalCurrent += 税抜金額_実績`
     - `研究開発費対象` の科目かつ前期なら `rdTotalPrevious += 税抜金額_実績`
     - `AccountRepository.findAsMap()` で `stmt === 'P/L'` かつ大分類に「法人税」を
       含まない科目なら、`収支区分==='収入'` は `+額`、`===支出` は `-額` を
       `preTaxProfit` に加算
6. `Constants.TAX_RATES.brackets` でループして法人税額を再計算
   (`(national + local)` をブラケットごとに適用、均等割は除外)
7. `Constants.RD_TAX_CREDIT_RULES` を参照して控除率・控除額・控除上限・適用額を算出
     - 前年度 = 0 なら `incRate = null, creditRate = baseRate`
     - 前年度 > 0 かつ `(curr - prev)/prev > increaseThreshold` なら
       `creditRate = baseRate + incrementBonus`
     - それ以外は `creditRate = baseRate`
     - `rawCredit = rdTotalCurrent * creditRate`
     - `maxCredit = corpTax * maxCreditRate`
     - `appliedCredit = Math.min(rawCredit, maxCredit)`
8. `getWebSpreadsheet_().getSheetByName('69_fp_tax_credit_sim')` を取得し、
   `sheet.clearContents()` + `sheet.clearFormats()` で冪等化
9. A1:C2 をマージして以下の免責事項を赤背景・白字・太字・折返しで設定:
     【重要】本シミュレーションは参考値であり、実際の税額控除の適用を保証するものではありません。
     最終的な税務申告については、必ず顧問税理士にご確認ください。
10. 4 行目以降に項目・値・備考(11 行分)を setValues()、金額列には
    `Constants.NUMBER_FORMATS.CURRENCY` を適用
11. 完了を `SpreadsheetApp.getActiveSpreadsheet().toast()` で通知し、
    `Utils.logInfo()` で結果をログ出力。エラー時は `Utils.logError()` + Toast

## 制約
- `42_trn_journal` / `11_mst_account` / `69_fp_tax_credit_sim` への書き込みは、
  **本機能からは `11_mst_account` への DDL 列追加**(`setupAllSchemas` 経由)と
  **`69_fp_tax_credit_sim` への上書き**のみ。`42_trn_journal` は完全読み取り専用
- 既存の `TAX_RATES` / `RPA_DEFAULTS` 等の定数オブジェクトは変更しない
- 計算ロジックのパラメータは `RD_TAX_CREDIT_RULES` にのみ定義し、関数内にハードコードしない
- 免責事項(A1:C2)の記載は省略しない
- メニュー文字列は `Constants.MENU_DEFINITION` に宣言的に追加する(`onOpen()` を直接編集しない)
- F-34 未実装のため「研究開発費対象=TRUE」の設定は手動運用前提

## エッジケース
| 条件 | 表示値 / 振る舞い |
|------|------------------|
| 試験研究費ゼロ | 控除額 0 / 最終控除適用額 0 |
| 法人税額ゼロまたはマイナス(赤字) | 控除上限 0 / 最終控除適用額 0 |
| 前年度データなし(設立 1 期目等) | baseRate を適用(増減率計算をスキップ) |
| 「研究開発費対象」列が未追加 | Toast で警告・処理中断 |
| `69_fp_tax_credit_sim` シートが未作成 | Utils.logError + Toast でエラー通知 |
| 有効フラグ FALSE の科目 | 集計対象外 |

## 動作確認
1. `npm run push:dev` で dev 環境にデプロイ
2. GAS エディタで `setupAllSchemas()` を実行し:
    - `11_mst_account` に「研究開発費対象」列(12 列目)が追加されたこと
    - `69_fp_tax_credit_sim` タブが作成されヘッダー `[項目, 値, 備考]` を持つこと
3. `11_mst_account` で数件の科目(例: 試験研究費・ソフトウェア開発費 等)の
   「研究開発費対象」を TRUE に設定
4. サイドバーまたはメニューから「研究開発税制シミュレーションを更新」を実行
5. `69_fp_tax_credit_sim` に:
    - A1:C2 に赤背景・白字の免責事項
    - 4 行目以降に項目・値・備考(当期試験研究費・控除額・最終控除適用額 等)
   が出力されることを確認
6. エッジケースを手動検証:
    - 対象 TRUE の仕訳を全削除 → 試験研究費 0 / 控除額 0
    - 赤字期の仕訳セットで実行 → 控除上限 0
7. `buildTaxCreditSimulation()` を 2 回連続実行し、`clearContents()` により前回データが
   消去されて二重出力されないことを確認

### 拡張思考の使用状況
| フェーズ | 拡張思考 | 備考 |
|---------|---------|------|
| Phase 1 調査(既読) | あり | 既存 TAX_RATES・MENU_DEFINITION・JournalEntryDTO の読込み |
| Phase 2 実装 | なし | 本仕様書の Step 1-5 を順次清書する |

推奨実行モデル

工程推奨モデル理由
全工程(定数追加 + DDL 変更 + 新規モジュール作成)Claude Sonnet 4.6複数ファイル横断だが仕様書で設計が完全定義済み。会計ロジック(税額計算・税引前利益の集計)の理解が必要だが、各ステップは本仕様の Step 1-5 を順に書き下すのみ

個別 Step に細分化する場合の推奨は以下:

  • Step 1-4(定数追加・DDL 変更): Claude Haiku 4.5(判断要素なし)
  • Step 5(新規モジュール作成): Claude Sonnet 4.6(税額計算ロジック・DTO 集計の理解が必要)

変更履歴

日付変更内容
2026-04-21初版作成。中小企業技術基盤強化税制の控除額シミュレーションロジック設計、DDL 変更(11_mst_account 研究開発費対象列追加 / 69_fp_tax_credit_sim 新設)、新規モジュール 600_report/610_datamart_tax_credit.js の設計を記載

仕様書作成プロンプト(再現性・監査性のため必ず記録)

展開して表示
【タイムアウト回避・実行原則(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: 骨格 Write(見出しのみ、~20行)
   - 2-2: 概要〜注意事項 Edit/Bash(~300行)
   - 2-3a: エッジケース〜人間が検討すべき事項 Edit/Bash(~200行)
   - 2-3b: 実装プロンプト〜変更履歴 Edit/Bash(~250行)
   - 2-4: `<details>` に投入プロンプト全文記録 Edit/Bash(最重量・必ず独立 Step)
4. **各 Step で何を書くかを具体指示**: 各 Step 内で設計判断を再考せず、Phase 1 で確定した内容の清書に徹する。

======================================================================
あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者兼仕様書ライターです。
案件 F-38「研究開発税制の適用シミュレーション」の開発仕様書を作成してください。
仕様書作成後、`docs/_config.json` の `nav` 配列 §E.5 にも必ず追記してください。

---

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

以下をすべて Read/Grep で調査し、**Phase 2 で使う固有名詞・行番号・型定義を完全に確定させること**。「名前から類推して書く」ことは禁止。推測した瞬間に Read する(失敗パターン #18-#20)。

### 1-A: 案件定義の取得
- `docs/_internal/TODO_future.md` で F-38 の概要・連動案件・人間が検討すべき事項を取得する。

### 1-B: 既存仕様書テンプレートの読み込み
- `docs/dev/dev_F-01_variance_analysis.md` を Read し、FP&A 新機能仕様書のセクション構成・フォーマットを把握する。(F-24 も参考資料として参照可だが、一次テンプレートは F-01)

### 1-C: コアコードの調査(以下をすべて Read すること)

**① `000_infra/002_constants.js`**
- `TAX_RATES` の構造(`brackets` / `national` / `local` / `localMinimumAnnual` 等)を把握し、新規追加する `RD_TAX_CREDIT_RULES` オブジェクトの設計方針(フィールド名・配置場所)を確定する。
- 既存の定数オブジェクト追加パターン(末尾 or 任意位置)を確認する。

**② `000_infra/003_contracts.js`**
- `JournalEntryDTO` の `@typedef` を Read し、`科目名` / `税抜金額_実績` / `発生日(P/L計上日)` 等のフィールド名の正確な文字列を確認する(仕様書のコード例に直接使用するため)。

**③ `200_data/202_repository.js`**
- `JournalRepository.findAll()` の戻り値型 `{ headers: string[], dtos: JournalEntryDTO[] }` を確認する。
- `AccountRepository.findAsMap()` の戻り値構造 `{ stmt: string, cat: string }` と `AccountRepository.resetCache()` の存在を確認する(キャッシュ影響の考慮に必要)。
- `readSheetAsDtos_` / `appendDtosToSheet_` 等のプライベートヘルパーのシグネチャを確認し、新規レポートシートへの書き込みパターンを把握する。

**④ `100_config/101_sys_config.js`**
- **`onOpen()` を Read し、FP&A 系メニューの正確な文字列(絵文字含む)を確認する。** Gemini が示した「📊 FP&Aレポート更新」はハルシネーションリスクあり。実在する文字列のみ引用すること。
- `setupAllSchemas()` の DDL 定義を Read し、`11_mst_account` のヘッダー列リストを確認する。「研究開発費対象」列の追加位置と既存 DDL 定義パターン(配列への追記方法)を確定する。
- 既存の `600_report/` 系シート(`61_pl_monthly` 等)の DDL スキーマ定義パターンを確認し、新規 `69_fp_tax_credit_sim` シートの DDL 定義方法を把握する。

**⑤ `600_report/` 配下の既存データマートファイル**(`601_datamart_ingest.js` 等、1〜2 ファイル)
- 既存レポート生成関数のシートクリア → 書き込みフロー(`clearContents()` の使用箇所)を確認する。
- **集計基準年月の取得方法(参照シート・セル番地・関数名)をコードから直接確認すること。** Gemini が示した「`61_pl_monthly` シートの C1 セル」は要確認(ハルシネーションリスクあり)。実在する取得方法を Read で特定する。
- `61_pl_monthly` または同等シートに「法人税、住民税及び事業税」行が実在するか、セル位置はどこかを確認する。

### 1-D: ファイル番号の確認
- `600_report/` 配下の既存ファイル番号(`601` 〜 `608` が CLAUDE.md に記載)を確認し、新規 `610_datamart_tax_credit.js` に番号の衝突がないことを確認する。

---

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

出力先: `docs/dev/dev_F-38_rd_tax_credit_sim.md`
**1 回のツール呼び出しで全内容を出力しない。以下の Step に厳密に分割して実行すること。**

### Step 2-1: 骨格の作成(Write、~20行)

以下の見出しのみを出力する(本文空で可):

```
# F-38: 研究開発税制の適用シミュレーション
## 概要
## 目的
## 現在のコード
## 修正方針
## 影響範囲
## 注意事項
## エッジケース
## 実データ検証
## 関連ドキュメント
## 人間が検討すべき事項
## 実装プロンプト(Claude Code 用)
## 推奨実行モデル
## 変更履歴
## 仕様書作成プロンプト(再現性・監査性のため必ず記録)
```

### Step 2-2: 概要〜注意事項の追記(Edit/Bash、~300行)

Phase 1 で確定した情報のみを使用して以下を記述する:

- **概要テーブル**: 案件ID=F-38, カテゴリ=FP&A, Phase・優先度は TODO_future.md から転記, 対象ファイル(`000_infra/002_constants.js` / `100_config/101_sys_config.js` / `600_report/610_datamart_tax_credit.js`〈新規〉), 前提案件(F-34: R&D費用自動分類・集計、未実装)
- **目的**: 何を・なぜ変更するかを 1〜3 文で記述
- **現在のコード**: Phase 1 で Read した実在コードのスニペット(`TAX_RATES` の構造、`AccountRepository.findAsMap()` の戻り値型、`JournalEntryDTO` の関連フィールド等)をファイル名・行番号付きで記載
- **修正方針**:
  - **Step 1: 前提 DDL 変更** — `100_config/101_sys_config.js` の `setupAllSchemas()` 内で `11_mst_account` の DDL ヘッダー配列に「研究開発費対象」列(TRUE/FALSE)を追加する。追加位置は Phase 1-C-④で確認した実際のヘッダー末尾または適切な位置を明記する。
  - **Step 2: 新規定数追加** — `000_infra/002_constants.js` に `RD_TAX_CREDIT_RULES` オブジェクトを追加する。中小企業技術基盤強化税制(中小企業型)の控除率・増減率閾値・控除上限率等のフィールドを定義する。**計算式内へのハードコード禁止**。総額型は将来課題としてコメントで明記する。フィールド名・配置は Phase 1-C-①で確認した `TAX_RATES` のパターンに倣う。
  - **Step 3: 新規モジュール作成** — `600_report/610_datamart_tax_credit.js` を作成し、`buildTaxCreditSimulation()` を実装する:
    1. `AccountRepository.resetCache()` を呼び出してキャッシュをリセットしてから `AccountRepository.findAsMap()` で科目マップを取得する
    2. `JournalRepository.findAll()` で `42_trn_journal` の全仕訳を取得する
    3. 科目マップを参照し「研究開発費対象=TRUE」の科目に紐づく仕訳の `税抜金額_実績`(フィールド名は Phase 1-C-②で確認した実在の名称を使用)を試験研究費として集計する
    4. 集計基準年月は Phase 1-C-⑤で確認した実在の取得方法を使用する(要確認箇所)
    5. 法人税額は Phase 1-C-⑤で確認した実在のシート・セルを参照する(要確認箇所)
    6. `RD_TAX_CREDIT_RULES` を参照して控除額・控除適用額を計算する
    7. `69_fp_tax_credit_sim` シートに出力する前に `sheet.clearContents()` でクリアする
    8. 出力シートの先頭(A1〜A2 等)に以下の免責事項を赤背景・白字で記載する:「【重要】本シミュレーションは参考値であり、実際の税額控除の適用を保証するものではありません。最終的な税務申告については、必ず顧問税理士にご確認ください。」
  - **Step 4: メニュー追加** — `100_config/101_sys_config.js` の `onOpen()` で Phase 1-C-④で確認した実在の FP&A メニュー文字列に「研究開発税制シミュレーションを更新」サブメニューを追加し `buildTaxCreditSimulation()` を呼び出す
  - **Step 5: 新規シート DDL** — `setupAllSchemas()` に `69_fp_tax_credit_sim` のスキーマ定義を追加する(Phase 1-C-④で確認した既存パターンに倣う)
- **影響範囲**: 変更ファイル一覧・変更量の概算・この機能は読み取り専用レポートであり `42_trn_journal` / `11_mst_account` への書き込みは行わないため既存データへの破壊的変更なし
- **注意事項**:
  - F-34(R&D費用自動分類・集計)が未実装のため、「研究開発費対象」列は手動でマスタに設定が必要な暫定運用であることを明記する
  - `AccountRepository` はキャッシュを持つため、`buildTaxCreditSimulation()` 冒頭で `AccountRepository.resetCache()` を呼び出すこと
  - 仕訳フィールド名は `003_contracts.js` の `JournalEntryDTO` `@typedef` の実在文字列を使用すること(`税抜金額_実績` 等)
  - 計算ロジックは税務判断を代替しない。免責事項の実装は必須

### Step 2-3a: エッジケース〜人間が検討すべき事項の追記(Edit/Bash、~200行)

- **エッジケーステーブル**(条件 | 表示値 | 理由):

  | 条件 | 表示値 | 理由 |
  |------|--------|------|
  | 試験研究費がゼロ | 控除額: 0円、控除適用額: 0円 | 計算対象となる費用がない |
  | 法人税額がゼロまたはマイナス(赤字) | 控除上限額: 0円、最終控除適用額: 0円 | 控除上限 = 法人税額 × 上限率 がゼロ以下になるため |
  | 前年の試験研究費データなし | 増減率計算をスキップし、`RD_TAX_CREDIT_RULES` に定義した基本控除率を適用 | 増減率が算出不能な場合は基本控除率を使用するよう定数で定義する |
  | `11_mst_account` に「研究開発費対象」列が未追加(DDL 未実行) | Toast でエラー通知し処理中断 | `AccountRepository.findAsMap()` の戻り値 `{ stmt, cat }` に当該フィールドが存在しないため |

- **実データ検証**: 実装前に MCP 等で確認すべき項目:
  - `11_mst_account` の現在のヘッダー列数(DDL 変更前の現状確認)
  - `42_trn_journal` の「科目名」列に試験研究費候補データが存在するか(サンプル確認)
  - Phase 1-C-⑤で特定した法人税額参照セルに実データが存在するか

- **関連ドキュメント**(テーブル: 仕様書リンク | 関連箇所)

- **人間が検討すべき事項**:
  - `TODO_future.md` F-38 から転記した事項をすべて記載する
  - 税制改正への追従方法(`RD_TAX_CREDIT_RULES` の更新手順・担当者・タイミング)
  - 顧問税理士との適用判定フローの整理(本機能は税務判断を代替しないことの明示)
  - F-34 未実装期間中の代替手順(手動での「研究開発費対象」列 TRUE 設定フロー)
  - 将来の総額型対応時の設計変更方針

### Step 2-3b: 実装プロンプト〜変更履歴の追記(Edit/Bash、~250行)

**【注意】実装プロンプトはバッククォートで囲まず、行頭4スペースインデントで出力すること。**

    あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者です。
    案件 F-38「研究開発税制の適用シミュレーション」を実装してください。

    ## 実行前タスク
    以下のファイルを Read し、実装に使う固有名詞・行番号を確定させること(推測禁止):
    - `100_config/101_sys_config.js`: onOpen() のFP&A系メニューの正確な文字列、
      setupAllSchemas() 内の 11_mst_account ヘッダー配列の末尾行番号、
      69_fp_tax_credit_sim DDL 定義の追加位置
    - `600_report/` 配下の既存ファイル: 集計基準年月の取得方法(シート名・セル番地・関数名)、
      法人税額の参照元(シート名・行ラベル・列番号)
    - `000_infra/002_constants.js`: RD_TAX_CREDIT_RULES の追加位置(既存定数末尾)
    - `000_infra/003_contracts.js`: JournalEntryDTO の「科目名」「税抜金額_実績」「発生日(P/L計上日)」フィールド名の正確な文字列

    ## 修正対象ファイル
    - `000_infra/002_constants.js` — RD_TAX_CREDIT_RULES 定数オブジェクトの追加
    - `100_config/101_sys_config.js` — onOpen() メニュー追加、setupAllSchemas() DDL 変更
    - `600_report/610_datamart_tax_credit.js` — 新規作成(buildTaxCreditSimulation() を実装)

    ## 実装内容
    ### (1) 002_constants.js: RD_TAX_CREDIT_RULES の追加
    既存の定数末尾に以下の構造で追加する(値は現行の中小企業技術基盤強化税制に従う):
      RD_TAX_CREDIT_RULES: {
        // 中小企業技術基盤強化税制(中小企業型)
        baseRate: 0.12,          // 基本控除率(試験研究費の12%)
        increaseThreshold: 0.05, // 増減率閾値(前年比5%超で特例加算)
        incrementBonus: 0.10,    // 増減率閾値超過時の加算率上限(最大10%)
        maxCreditRate: 0.25,     // 法人税額に対する控除上限率(25%)
        // 将来課題: 総額型控除ロジックは未実装
      },

    ### (2) 100_config/101_sys_config.js の変更
    ① setupAllSchemas() の 11_mst_account DDL ヘッダー配列末尾に「研究開発費対象」を追加する
      (既存列の位置は Read で確認した行番号を使用する)
    ② setupAllSchemas() に 69_fp_tax_credit_sim のスキーマ定義を追加する
      (列構成: A=免責事項ラベル, B=項目名, C=金額/率)
    ③ onOpen() で Read した実在の FP&A メニューに addItem() でサブメニューを追加する:
      .addItem('研究開発税制シミュレーションを更新', 'buildTaxCreditSimulation')

    ### (3) 600_report/610_datamart_tax_credit.js の新規作成
    function buildTaxCreditSimulation() を実装する:
      1. AccountRepository.resetCache() を呼び出してキャッシュをリセットする
      2. AccountRepository.findAsMap() で科目マップを取得する
      3. JournalRepository.findAll() で全仕訳 DTO を取得する
      4. 「研究開発費対象」列が科目マップに存在しない場合は Utils.toastResult() でエラーを通知して return する
      5. 集計基準年月を Phase 1 で確認した実在の方法で取得する
      6. 科目マップで「研究開発費対象=TRUE」の科目名セットを構築し、
         仕訳 DTO の「科目名」フィールドで絞り込み、「税抜金額_実績」を合算して試験研究費合計を算出する
      7. 法人税額を Phase 1 で確認した実在のシート・セルから取得する
      8. RD_TAX_CREDIT_RULES を参照して控除額・控除適用額を計算する
         (前年試験研究費がゼロまたは未取得の場合は増減率計算をスキップし baseRate を使用)
      9. getWebSpreadsheet_().getSheetByName('69_fp_tax_credit_sim') でシートを取得し、
         sheet.clearContents() でクリアする
      10. A1:A2 に免責事項を setBackground('#cc0000') / setFontColor('#ffffff') / setFontWeight('bold') で記載する
      11. 計算結果(試験研究費合計・控除額・控除適用額等)を B 列以降に書き込む
      12. Utils.toastResult() で完了を通知する

    ## 制約
    - `42_trn_journal` / `11_mst_account` / `42_trn_journal` への書き込みは一切禁止(読み取り専用)
    - 既存の TAX_RATES / RPA_DEFAULTS 等の定数オブジェクトは変更しない
    - 計算ロジックのパラメータは RD_TAX_CREDIT_RULES にのみ定義し、関数内にハードコードしない
    - 免責事項の記載は省略しない
    - メニュー文字列は Read で確認した実在の文字列のみ使用する(造語禁止)

    ## エッジケース
    (Step 2-3a のエッジケーステーブルを転記)

    ## 動作確認
    1. npm run push:dev でデプロイする
    2. GAS エディタで setupAllSchemas() を実行し、11_mst_account に「研究開発費対象」列が追加されたことを確認する
    3. 11_mst_account で数件の科目に「研究開発費対象」を TRUE に設定する
    4. Phase 1 で確認したメニューから「研究開発税制シミュレーションを更新」を実行する
    5. 69_fp_tax_credit_sim シートに免責事項(赤背景)と計算結果が出力されることを確認する
    6. エッジケースを手動でテストする(試験研究費ゼロ → 0円表示、法人税額ゼロ → 0円表示)
    7. buildTaxCreditSimulation() を 2 回連続実行し、前回のデータが clearContents() でクリアされて二重出力されないことを確認する

    ### 拡張思考の使用状況
    | フェーズ | 拡張思考 | 備考 |
    |---------|---------|------|
    | Phase 1 調査 | あり | ファイル読み込みと設計確定に活用 |
    | Phase 2 実装 | なし | Phase 1 確定内容の清書に徹する |

続いて以下を記述する:

- **推奨実行モデルテーブル**:
  | 工程 | 推奨モデル | 理由 |
  |------|-----------|------|
  | 全工程 | Claude Sonnet | 複数ファイル横断だが仕様書で設計が完全定義済み。会計ロジック(税額計算)の理解が必要 |

- **変更履歴テーブル**: 本日日付(2026-04-20)・初版作成エントリを記載

### Step 2-4: 仕様書作成プロンプトの記録(Edit/Bash、独立 Step・省略禁止)

末尾の `## 仕様書作成プロンプト(再現性・監査性のため必ず記録)` セクションに `<details><summary>展開して表示</summary>` ブロックを設け、この `<instruction>` 全文(添削後の最終版)をそのまま記録する。

---

## Phase 3: 後処理(仕様書完成後に実行)

### 3-A: `docs/_config.json` への追記
`docs/_config.json` の §E.5 (FP&A・レポーティング) に以下を追加する:
```json
{ "file": "dev/dev_F-38_rd_tax_credit_sim.md", "title": "E.5.X F-38 研究開発税制の適用シミュレーション" }
```
追記後、JSON 構文エラーがないことを確認する(`node -e "require('./docs/_config.json')"` 等で検証)。

### 3-B: `docs/_internal/changelog.md` への追記
先頭行(ヘッダー直後)に追記する:
```
| 2026-04-20 | [dev_F-38_rd_tax_credit_sim.md](dev_mas-038_rd_tax_credit_sim.md) | 初版作成。研究開発税制シミュレーション機能の仕様書 |
```

### 3-C: コミット&プッシュ
```bash
git add docs/dev/dev_F-38_rd_tax_credit_sim.md docs/_internal/changelog.md docs/_config.json
git commit -m "docs: F-38 研究開発税制の適用シミュレーション仕様書を作成

中小企業技術基盤強化税制の控除額シミュレーションロジック設計、
DDL変更(11_mst_account 研究開発費対象列追加)、
新規モジュール 610_datamart_tax_credit.js の設計を記載

https://claude.ai/code/session_XXXXX"
git push -u origin {現在のブランチ名}
```

📌 取り込み時の注記 (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)。 実装時にファイル番号の再割当が必要。