概要

項目内容
案件IDMAS-083
カテゴリパイプライン / 会計指針
PhaseP1
優先度★★
所要時間90分
対象ファイル000_infra/002_constants.js(定数・SHEET_DEFAULTS)
100_config/101_sys_config.jsDDL・マイグレーションメニュー)
300_ui/301_ui_assist.js(onEdit 消費税自動計算)
400_domain/406_rpa_pipeline.jsRPAINVマッピング)
800_ops/809_migration_s11_pipeline_tax.js(新規マイグレーション)
前提案件MAS-080(21タブ管理ID早期採番)、MAS-116(マイグレーションスクリプト基盤)

目的

パイプライン売上シート(21_bud_pipeline)に消費税関連列(税区分 / 消費税額_スポット / 消費税額_継続月額)を追加し、パイプラインRPA(generatePipelineInvoices)によるINV自動生成時に、課税事業者 / 免税事業者の区分(03_sys_paramsCFG_TAX_PAYER_STATUS)に応じた正確な税額を InvoiceDTO税抜金額_計画 / 消費税額_計画 / 税込金額_計画 / 税区分 にマッピングする。現状は税抜金額をそのまま税込額としてINVに流しており、将来の課税事業者移行時にP/LB/S計上値が不正確になる問題を解消する。

現在のコード

1. 000_infra/002_constants.jsSHEET_DEFAULTS の 21_bud_pipeline エントリ(L77)

{ pattern: '21_bud_pipeline', prefix: 'PIP_', defaults: {
  '契約形態': 'スポット(狩猟)',
  '売上科目': '売上高',
  '確度(ヨミ)': 'Aヨミ (確度80%)',
  '継続月数': 1,
  '取引先名': '',
  '決済手段': '',
  'CF計上': '予算',
  '入金ラグ(月)': 1,
  'スポット売上・初期費用': 0,
  '継続月額(MRR)': 0,
  _dynamic: { '計上開始年月': 'nextYm' }
}}

2. 000_infra/002_constants.jsDEFAULT_TAX_RATE 定義(L9-10)

/** @deprecated TAX_RATES を使用してください */
DEFAULT_TAX_RATE: 0.30,

DEFAULT_TAX_RATE は法人税率(非推奨)。消費税率の定数はまだ定義されていない。

3. 400_domain/406_rpa_pipeline.js — INV生成時の金額取得・マッピング

列名取得(L27-30)

const col = {};
['有効フラグ', '管理ID', 'PJ・案件名', '契約形態', '売上科目', '確度(ヨミ)',
 '計上開始年月', 'スポット売上・初期費用', '継続月額(MRR)', '継続月数',
 '取引先名', '決済手段', '入金ラグ(月)', '入金日', '組織名', '最終起票年月日', '備考'
].forEach(function(name) { col[name] = h.indexOf(name); });

スポット売上INV生成(L167-197)

var spot = col['スポット売上・初期費用'] !== -1 ? (Number(row[col['スポット売上・初期費用']]) || 0) : 0;
if (spot > 0 && startYm <= rowTargetYm) {
  var spotMemo = '【RPA:PIPE】' + startYm + ' ' + pjName + ' スポット';
  if (!isDuplicate_(invData, invHeaders, spotMemo)) {
    drafts.push(buildInvRow_(invHeaders, {
      // ...
      '税区分': '対象外',
      '税抜金額_計画': spot,
      '消費税額_計画': 0,
      '税込金額_計画': spot,
      '未決済残高(自動計算)': spot,
      // ...
    }));
  }
}

継続月額MRR INV生成(L200-238)

var mrr = col['継続月額(MRR)'] !== -1 ? (Number(row[col['継続月額(MRR)']]) || 0) : 0;
// ... 月次ループで buildInvRow_ に下記をマッピング
'税区分': '対象外',
'税抜金額_計画': mrr,
'消費税額_計画': 0,
'税込金額_計画': mrr,

冪等性チェックは isDuplicate_(invData, invHeaders, spotMemo) で摘要文字列ベースに行う。摘要キーは '【RPA:PIPE】' + startYm + ' ' + pjName + ' スポット' / '【RPA:PIPE】' + curYm + ' ' + pjName + ' MRR ' + (mm+1) + '/' + dur + 'ヶ月目' であり、金額列名(スポット売上・初期費用税抜_スポット売上)のリネームは冪等性に影響しない。

4. 100_config/101_sys_config.js — DDL定義(L662)

'BUD_PIPE': { headers: ["有効フラグ","管理ID","PJ・案件名","契約形態","売上科目","確度(ヨミ)","計上開始年月","スポット売上・初期費用","継続月額(MRR)","継続月数","取引先名","決済手段","CF計上","入金ラグ(月)","入金日","休日調整","組織名","起票ターゲット月","最終起票年月日","備考"], color: "#e69138" },

5. templates/operations_sidebar.html — マイグレーションメニュー(L88-94)

<div class="section">
  <h3>🔧 マイグレーション</h3>
  <button class="btn" onclick="run('migrationD01D03', this)">D-01〜D-03 科目マスタ追加</button>
  <button class="btn" onclick="run('migrationD04D06', this)">D-04/D-06 説明列データ</button>
  <button class="btn" onclick="run('migrationI10', this)">I-10 取引先略称</button>
  <button class="btn" onclick="run('migrationI24', this)">I-24 立替精算STL</button>
  <button class="btn danger" onclick="run('cleanupEmptyRows', this)">🧹 全シート空行物理削除</button>
</div>

6. 300_ui/301_ui_assist.js — 既存 onEdit の21タブハンドラ(L251-276)

} else if (sName.includes('21_bud_pipeline')) {
  // S-08: PJ・案件名入力で管理ID早期採番
  if (colName === 'PJ・案件名' && val && sheet.getRange(row, 2).isBlank()) {
    // ... 管理ID採番 + SHEET_DEFAULTS の setIfBlank
  }
  if (colName === '契約形態') {
    const durCell = sheet.getRange(row, headers.indexOf('継続月数') + 1);
    const mrrCell = sheet.getRange(row, headers.indexOf('継続月額(MRR)') + 1);
    // ... 表示制御
  }
}

修正方針

Step 1: 消費税率定数と事業者区分パラメータの追加

(1) 000_infra/002_constants.jsCONSUMPTION_TAX_RATE 追加

DEFAULT_TAX_RATE: 0.30,(L10)の直後に以下を追加する。

/** @deprecated TAX_RATES を使用してください */
DEFAULT_TAX_RATE: 0.30,

/** 消費税率(標準税率 10%)。免税/課税判定は 03_sys_params の CFG_TAX_PAYER_STATUS で制御 */
CONSUMPTION_TAX_RATE: 0.10,

(2) 100_config/101_sys_config.js03_sys_params への CFG_TAX_PAYER_STATUS 初期値追加

03_sys_params は DDL管理外(setupAllSchemas で上書きしない方針。docs/master/mst_sys_config.md 2.2節参照)。初期値投入は マイグレーションスクリプト migrationS11PipelineTax()で実施する(setupAllSchemas には触れない)。

(3) 参照側のコード

RPA・onEdit 両方で、事業者区分の読み出しは以下に統一する。

var payerStatus = Constants.getParam('CFG_TAX_PAYER_STATUS', '免税事業者');
var isTaxPayer = (payerStatus === '課税事業者');

Step 2: DDL変更・SHEET_DEFAULTS更新・データ移行

(1) 100_config/101_sys_config.js の DDL定義変更(L662)

- 'BUD_PIPE': { headers: ["有効フラグ","管理ID","PJ・案件名","契約形態","売上科目","確度(ヨミ)","計上開始年月","スポット売上・初期費用","継続月額(MRR)","継続月数","取引先名","決済手段","CF計上","入金ラグ(月)","入金日","休日調整","組織名","起票ターゲット月","最終起票年月日","備考"], color: "#e69138" },
+ 'BUD_PIPE': { headers: ["有効フラグ","管理ID","PJ・案件名","契約形態","売上科目","確度(ヨミ)","計上開始年月","税抜_スポット売上","税抜_継続月額(MRR)","継続月数","税区分","消費税額_スポット","消費税額_継続月額","取引先名","決済手段","CF計上","入金ラグ(月)","入金日","休日調整","組織名","起票ターゲット月","最終起票年月日","備考"], color: "#e69138" },

変更点:

  • スポット売上・初期費用税抜_スポット売上(リネーム)
  • 継続月額(MRR)税抜_継続月額(MRR)(リネーム)
  • 税区分 / 消費税額_スポット / 消費税額_継続月額 の3列を「継続月数」の直後に追加

(2) 000_infra/002_constants.jsSHEET_DEFAULTS の 21_bud_pipeline 更新(L77)

- { pattern: '21_bud_pipeline', prefix: 'PIP_', defaults: {
-   '契約形態': 'スポット(狩猟)', '売上科目': '売上高', '確度(ヨミ)': 'Aヨミ (確度80%)',
-   '継続月数': 1, '取引先名': '', '決済手段': '', 'CF計上': '予算',
-   '入金ラグ(月)': 1, 'スポット売上・初期費用': 0, '継続月額(MRR)': 0,
-   _dynamic: { '計上開始年月': 'nextYm' } } },
+ { pattern: '21_bud_pipeline', prefix: 'PIP_', defaults: {
+   '契約形態': 'スポット(狩猟)', '売上科目': '売上高', '確度(ヨミ)': 'Aヨミ (確度80%)',
+   '継続月数': 1, '取引先名': '', '決済手段': '', 'CF計上': '予算',
+   '入金ラグ(月)': 1, '税抜_スポット売上': 0, '税抜_継続月額(MRR)': 0,
+   '税区分': '課税', '消費税額_スポット': 0, '消費税額_継続月額': 0,
+   _dynamic: { '計上開始年月': 'nextYm' } } },

(3) マイグレーションスクリプト 800_ops/809_migration_s11_pipeline_tax.js 新規作成

  • 関数名: migrationS11PipelineTax()

  • 冪等性:

    • 21_bud_pipeline税区分 列が既に存在する場合は列追加ステップをスキップ
    • 03_sys_paramsCFG_TAX_PAYER_STATUS が既に存在する場合は初期値追加をスキップ
  • 処理内容:

    1. 21_bud_pipeline の既存ヘッダーを取得し、スポット売上・初期費用 / 継続月額(MRR) を新列名に更新(setValue でヘッダー行を書き換え)
    2. 新列 税区分 / 消費税額_スポット / 消費税額_継続月額 を「継続月数」の直後に挿入(insertColumnAfter
    3. 既存の有効行について、税区分 = '課税' をデフォルト投入
    4. 事業者区分が課税の場合に限り、既存行の消費税額を Math.floor(税抜金額 * Constants.CONSUMPTION_TAX_RATE) で一括計算して書き込む(免税の場合は 0)
    5. 03_sys_paramsCFG_TAX_PAYER_STATUS が未登録なら ['CFG_TAX_PAYER_STATUS', '免税事業者'] を appendRow
    6. Constants._paramsCache = null でキャッシュをクリア
  • ログ: Utils.logInfo(FUNC, summary) + SpreadsheetApp.getUi().alert('マイグレーション完了', summary, ...) の両方

  • メニュー登録: templates/operations_sidebar.html の「🔧 マイグレーション」セクション末尾(L93の migrationI24 行の直後)に以下を追加する。

    <button class="btn" onclick="run('migrationS11PipelineTax', this)">S-11 パイプライン消費税対応</button>
    

Step 3: 消費税自動計算ロジックの追加(onEdit)

300_ui/301_ui_assist.js の L251-276(sName.includes('21_bud_pipeline') ブロック)の末尾契約形態 制御の後に以下を追加する。

// S-11: 税抜金額 / 税区分 編集時に消費税額を自動計算
var taxCols = {
  '税抜_スポット売上':    '消費税額_スポット',
  '税抜_継続月額(MRR)':   '消費税額_継続月額'
};
var payerStatus = Constants.getParam('CFG_TAX_PAYER_STATUS', '免税事業者');
var isTaxPayer = (payerStatus === '課税事業者');

if (taxCols[colName] !== undefined) {
  var amount = Number(val);
  var taxClassIdx = headers.indexOf('税区分');
  var taxClass = (taxClassIdx !== -1) ? String(sheet.getRange(row, taxClassIdx + 1).getValue()).trim() : '';
  var taxIdx = headers.indexOf(taxCols[colName]);
  if (taxIdx !== -1 && !isNaN(amount)) {
    var tax = (isTaxPayer && taxClass === '課税') ? Math.floor(amount * Constants.CONSUMPTION_TAX_RATE) : 0;
    sheet.getRange(row, taxIdx + 1).setValue(tax);
  }
}

if (colName === '税区分') {
  ['税抜_スポット売上', '税抜_継続月額(MRR)'].forEach(function(amtName) {
    var amtIdx = headers.indexOf(amtName);
    var taxIdx = headers.indexOf(taxCols[amtName]);
    if (amtIdx === -1 || taxIdx === -1) return;
    var amt = Number(sheet.getRange(row, amtIdx + 1).getValue()) || 0;
    var tax = (isTaxPayer && val === '課税') ? Math.floor(amt * Constants.CONSUMPTION_TAX_RATE) : 0;
    sheet.getRange(row, taxIdx + 1).setValue(tax);
  });
}
  • 税抜金額 列が編集された場合: 同行の 税区分 と事業者区分を見て対応する消費税額列に書き込む
  • 税区分 列が編集された場合: 両方の税抜列を読み直して消費税額を再計算
  • 免税事業者 または 税区分課税 以外の場合: 消費税額を 0 にリセット

Step 4: RPAロジックの修正(400_domain/406_rpa_pipeline.js

(1) 列名取得の更新(L27-30)

  ['有効フラグ', '管理ID', 'PJ・案件名', '契約形態', '売上科目', '確度(ヨミ)',
-  '計上開始年月', 'スポット売上・初期費用', '継続月額(MRR)', '継続月数',
+  '計上開始年月', '税抜_スポット売上', '税抜_継続月額(MRR)', '継続月数',
+  '税区分', '消費税額_スポット', '消費税額_継続月額',
   '取引先名', '決済手段', '入金ラグ(月)', '入金日', '組織名', '最終起票年月日', '備考'
  ].forEach(function(name) { col[name] = h.indexOf(name); });

(2) 事業者区分の読み込み(ループ外、L52付近)

var payerStatus = Constants.getParam('CFG_TAX_PAYER_STATUS', '免税事業者');
var isTaxPayer = (payerStatus === '課税事業者');

(3) ORD用金額集計(L125-129)

税抜ベースで算出する。税抜_スポット売上 / 税抜_継続月額(MRR) を読み、ORDの 税抜金額_発注 / 消費税額_発注 / 税込金額_発注 を事業者区分に応じて設定する。

(4) スポット売上INV生成(L167-197)

var spot    = col['税抜_スポット売上']  !== -1 ? (Number(row[col['税抜_スポット売上']])  || 0) : 0;
var spotTax = col['消費税額_スポット']  !== -1 ? (Number(row[col['消費税額_スポット']]) || 0) : 0;
var taxClassRow = col['税区分'] !== -1 ? String(row[col['税区分']]).trim() : '課税';

// InvoiceDTO マッピング
var invNet   = spot;
var invTax   = isTaxPayer ? spotTax : 0;
var invGross = invNet + invTax;
var invTaxClass = isTaxPayer ? (taxClassRow || '課税') : '対象外';

drafts.push(buildInvRow_(invHeaders, {
  // ...
  '税区分': invTaxClass,
  '税抜金額_計画': invNet,
  '消費税額_計画': invTax,
  '税込金額_計画': invGross,
  '未決済残高(自動計算)': invGross,
  // ...
}));

(5) 継続月額MRR INV生成(L200-238) — スポットと同じ考え方で 税抜_継続月額(MRR) / 消費税額_継続月額 を読んで InvoiceDTO にマッピングする。

マッピングルール(まとめ):

事業者区分InvoiceDTO.税区分InvoiceDTO.税抜金額_計画InvoiceDTO.消費税額_計画InvoiceDTO.税込金額_計画
課税事業者行の 税区分(課税 / 非課税 / 対象外 のいずれか)税抜金額消費税額(行の列値をそのまま)税抜 + 消費税
免税事業者対象外税抜金額0税抜金額

影響範囲

ファイル変更内容変更量概算
000_infra/002_constants.jsCONSUMPTION_TAX_RATE 追加、SHEET_DEFAULTS 21タブ更新+5行 / 変更2行
100_config/101_sys_config.jsBUD_PIPE DDL列定義更新変更1行
300_ui/301_ui_assist.jsonEdit に消費税自動計算ロジック追加+25行
400_domain/406_rpa_pipeline.js列名更新・税額マッピング修正変更/追加 約20行
800_ops/809_migration_s11_pipeline_tax.js新規作成+90行
templates/operations_sidebar.htmlマイグレーションメニュー追加+1行

注意事項

  1. 実行順序の厳守: setupAllSchemas(DDL再実行)は BUD_PIPE のヘッダーを上書きするため、先に migrationS11PipelineTax() を実行して既存行データ(税抜金額の列値)を新列配置に整合させてから DDL を更新すること。順序を逆にすると、旧列名の金額データが新ヘッダー配下に残って読み違える可能性がある。
  2. SHEET_DEFAULTS の列名更新忘れ防止: SHEET_DEFAULTS21_bud_pipeline エントリの列名を DDL と合わせて更新しないと、smartAddRow で旧列名 (スポット売上・初期費用 / 継続月額(MRR)) に対するデフォルト値がセットされ、新列 (税抜_スポット売上 / 税抜_継続月額(MRR)) には初期値が入らない。
  3. Constants.getParam() のキャッシュリセット: getParam_paramsCache で初回読み込み結果をキャッシュする(L146-167)。マイグレーションスクリプト内で 03_sys_paramsCFG_TAX_PAYER_STATUS を追加した後、同一実行セッション内で再取得する場合は Constants._paramsCache = null でキャッシュをクリアすること。スプレッドシートを開き直すと自然にリセットされる。
  4. onEdit の発火条件: onEdit トリガーは手入力のみで発火し、一括貼り付け・スクリプトによる書き込み・RPA 経由の更新では発火しない。既存パイプライン行の消費税額は migrationS11PipelineTax() 内の一括計算で補完する。
  5. 冪等性チェックキーとの非干渉: RPA の冪等性チェックは摘要('【RPA:PIPE】' + startYm + ' ' + pjName + ' スポット' 等)で行うため、金額列のリネームの影響を受けない。MAS-078(パイプライン→84タブ合流)のキー設計も摘要ベースのため同様に影響なし。

エッジケース

条件表示値・動作理由
CFG_TAX_PAYER_STATUS03_sys_params に未設定免税事業者として動作(消費税額_計画=0、InvoiceDTO.税区分=対象外Constants.getParam('CFG_TAX_PAYER_STATUS', '免税事業者') のデフォルト値が採用される
税区分 列が空白onEdit 消費税自動計算では 課税 以外と判定し 消費税額=0 に設定SHEET_DEFAULTS でデフォルト '課税' を設定しているため通常は空にならないが、古い行や手動削除時の保険
金額がゼロ消費税額 = 0Math.floor(0 * 0.10) = 0正常動作
金額がマイナス(値引き等)消費税額 もマイナス追従(例: 税抜 -1000 → 消費税額 -100)。入力時点で警告はしないマイナスINV(返品・値引き)を起票した場合と会計処理の符号を一致させるため
税区分 = '非課税' / '対象外'(手動変更)onEdit は 課税 以外と判定し 消費税額 = 0 を書き込む。RPAも課税事業者の場合のみ行の 税区分 をそのまま InvoiceDTO.税区分 に渡す非課税売上(例: 一部の社会保険診療)の入力余地を残す
課税事業者 → 免税事業者へ変更後既存パイプライン行の 消費税額_スポット / 消費税額_継続月額 は自動ではリセットされないonEdit は発火しないため。Constants._paramsCache = null リセット後に migrationS11PipelineTax() を再実行して既存行の消費税額を 0 にリセットする
免税事業者 → 課税事業者へ変更後既存行の消費税額は 0 のまま。onEdit で再入力するか、再マイグレーション実行で補完同上。migrationS11PipelineTax() は冪等性チェックで「列追加」はスキップするが、「消費税額の再計算」は常に実行する実装とする
RPA の冪等性(二重INV防止)isDuplicate_ の摘要キー('【RPA:PIPE】' + ym + ' ' + pjName + ' スポット' / MRR n/dヶ月目)は列名リネームの影響を受けない摘要文字列は金額列の値ではなく計上開始年月・PJ名・月数インデックスに依存
旧名の列が DDL 再実行後に残留DDL の headers 配列は「定義通りに headers を setValue」する実装。余分な旧列がある場合は手動で列削除する必要があるsetupAllSchemas は列の物理削除までは行わない(MAS-134 高速化仕様準拠)
マイグレーション実行後に setupAllSchemas を実行新DDL列定義と既存ヘッダーが一致するため影響なし(S-62 setupAllSchemas 高速化: ハッシュ一致時はスキップ)マイグレーションで書き込んだ新列ヘッダーが DDL と整合する

実データ検証

実装開始前に MCP(またはGASエディタ)で以下を確認する:

確認項目手段備考
21_bud_pipeline の現行ヘッダーsheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0]スポット売上・初期費用 / 継続月額(MRR) が期待列位置(8列目 / 9列目)にあるかを確認
21_bud_pipeline の有効行件数getDataRange().getValues() を filterマイグレーション対象件数の把握(所要時間見積もり)
03_sys_paramsCFG_TAX_PAYER_STATUS 未登録の確認同シートをスキャン二重 appendRow 防止
現行の RPA で生成された INV の 税区分32_wrk_invoice の直近RPA起票分現状 対象外 で統一されていることを確認
MAS-078(84タブ合流)との互換性生成 INV の列値変更が 84_cf_daily_plan に伝播するかを既存テストで確認税込金額_計画 を基準に集計しているはずなので整合性は保たれる想定

関連ドキュメント

仕様書関連箇所
CLAUDE.md会計ロジック(税区分の完全一致)・800_ops マイグレーションガイドライン
mst_sys_config.md03_sys_params は DDL管理外 / Constants.getParam キャッシュ挙動
dev_mas-080_pipeline_early_id.md21タブ管理ID早期採番・onEdit 21タブハンドラの前提
dev_mas-078_pipeline_cf_integration.md84タブへのパイプライン合流(税込金額_計画ベース)
dev_mas-089_consumption_tax_exclusive_method.md消費税 税抜方式への切替え(MAS-089 との整合)
dev_mas-116_migration_framework.md800_ops マイグレーションスクリプトの実装パターン

人間が検討すべき事項

TODO_future.md の MAS-083 行から転記: 課税事業者への移行時期の確認

追加事項:

  1. 課税事業者への移行時期と CFG_TAX_PAYER_STATUS 変更タイミング: 移行前後でパイプラインRPAを一時停止するかの判断。課税事業者になる決算年度の初日より前のINV生成を停止しておかないと、課税期間をまたぐINVの税額計算が期またぎで二重管理になる可能性がある。
  2. 809_migration_s11_pipeline_tax.js の実行タイミング: dev 環境で検証 → npm run push:prod → prod で管理者が手動実行、の順序を厳守する。DDL 再実行 (setupAllSchemas) とマイグレーションのどちらを先に実行するかは「注意事項1」を参照。
  3. 税区分のデフォルト値を '課税' とする影響: 現在免税事業者として運用しているユーザーは、税区分=課税 をデフォルト投入しても CFG_TAX_PAYER_STATUS=免税事業者 であれば消費税額は0になる。ただし「将来の課税移行に備えて税区分を先行入力しておく」という運用意図をユーザーに伝える必要がある。
  4. MAS-089(税抜方式への切替え)との統合: MAS-089は会計処理方式(税込方式 ↔ 税抜方式)の切替えを扱うため、MAS-083の 消費税額_計画 の導入は MAS-089 の前提データとしても使える。両案件のスケジュール調整(MAS-083 を先に実装しておく)の検討。
  5. 非課税売上・対象外売上の扱い: パイプラインの性質上ほぼ全てが 課税 となる想定だが、一部業種(医療・介護・教育等)で非課税売上が発生する場合、税区分 列を '非課税' に手動変更すると onEdit は消費税を 0 にする。この挙動がユーザーの期待と一致するかを確認。

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

あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者です。
案件 MAS-083「パイプライン売上の消費税対応」を実装してください。

## 実行前タスク
- `000_infra/002_constants.js` を Read: `SHEET_DEFAULTS` の `21_bud_pipeline` エントリの
  現在の列名とデフォルト値(L77)、`DEFAULT_TAX_RATE` の定義位置(L9-10)、
  `Constants.getParam()` のシグネチャ・キャッシュ挙動(L146-167)を確認する
- `400_domain/406_rpa_pipeline.js` を Read: INV生成箇所・行番号(L27-30 列名取得 /
  L125-129 ORD金額 / L167-197 スポットINV / L200-238 MRR INV)・
  冪等性チェックキー(摘要ベース `isDuplicate_`)を特定する
- `300_ui/301_ui_assist.js` を Read: onEdit 21タブハンドラ(L251-276)・
  `setIfBlank` ヘルパーのスコープ・`Constants.getParam` 呼び出しパターンを確認する
- `100_config/101_sys_config.js` を Read: `BUD_PIPE` DDL定義(L662)・
  `03_sys_params` が setupAllSchemas の管理対象外であることを確認する
- `templates/operations_sidebar.html` を Read: 「🔧 マイグレーション」セクション(L88-94)の
  末尾行を特定する(`migrationI24` の直後が挿入位置)
- `000_infra/003_contracts.js` を Read: `InvoiceDTO` の税関連プロパティ
  (`税区分` / `税抜金額_計画` / `消費税額_計画` / `税込金額_計画`)を確認する
- `800_ops/808_migration_i24.js` を Read: 確立済みマイグレーションパターン
  (冪等性チェック / auditLog / logInfo / alert の呼び順)を参考にする
- MCP等で `21_bud_pipeline` の実際の列構成を確認し、`スポット売上・初期費用` /
  `継続月額(MRR)` が期待位置にあることを確認する

## 修正対象ファイル
- `000_infra/002_constants.js`: `CONSUMPTION_TAX_RATE: 0.10` 追加、
  `SHEET_DEFAULTS` 21タブ列名更新、税区分/消費税額デフォルト値追加
- `100_config/101_sys_config.js`: `BUD_PIPE` DDL列定義更新(新列3つを「継続月数」の直後に配置)
- `300_ui/301_ui_assist.js`: 21タブ onEdit ハンドラ末尾に消費税自動計算ブロック追加
- `400_domain/406_rpa_pipeline.js`: 列名更新、事業者区分に応じた InvoiceDTO 税額マッピング
- `800_ops/809_migration_s11_pipeline_tax.js`: 新規作成(関数名 `migrationS11PipelineTax`)
- `templates/operations_sidebar.html`: マイグレーションメニュー末尾にボタン追加

## 実装内容
仕様書「修正方針」の Step 1〜4 に従い、以下の順で実装する:
Step 1(定数・パラメータ追加) → Step 2(DDL更新・SHEET_DEFAULTS更新・マイグレーションスクリプト作成)
→ Step 3(onEdit 消費税自動計算) → Step 4(RPA 税額マッピング修正)。

マッピングルール(RPA):
- 課税事業者: 行の税抜・消費税・税区分を InvoiceDTO にそのまま渡し、税込 = 税抜 + 消費税
- 免税事業者: 税抜を `税抜金額_計画` と `税込金額_計画` の両方に、消費税=0、税区分=`対象外`

## 制約
- `406_rpa_pipeline.js` 以外のRPAファイル(`401_rpa_hc.js` 等)は変更禁止
- DDL変更(`setupAllSchemas` 再実行)は `migrationS11PipelineTax()` 実行後に行う順序を厳守する
- 列参照はヘッダー名ベース(`indexOf` / `buildHeaderIndex_`)。列番号ハードコード禁止
- `03_sys_params` の直接読み取りは禁止。必ず `Constants.getParam()` 経由で取得する
- `PropertiesService.getScriptProperties()` を直接呼ばない。環境依存値は `Env` モジュール経由で取得
- `03_sys_params` の更新後は `Constants._paramsCache = null` でキャッシュをリセットする
- マイグレーションスクリプトは冪等性必須(`税区分` 列の存在チェック + `CFG_TAX_PAYER_STATUS` 二重登録防止)

## エッジケース
仕様書「エッジケース」テーブルを参照する。特に以下を確認する:
- `CFG_TAX_PAYER_STATUS` 未設定時のフォールバック(免税事業者として動作、消費税額=0)
- 冪等性チェックキー(`isDuplicate_` 摘要ベース)が列名リネームの影響を受けないこと
- 課税 → 免税 の事業者区分変更時、既存行の消費税額をリセットする運用(再マイグレーション)

## 動作確認
1. `npm run push:dev` でデプロイする
2. GASエディタで `migrationS11PipelineTax()` を実行し、以下を確認:
   - `21_bud_pipeline` に `税区分` / `消費税額_スポット` / `消費税額_継続月額` 列が追加される
   - 既存行の `税区分` が `課税` で埋められる
   - `03_sys_params` に `CFG_TAX_PAYER_STATUS = 免税事業者` が投入される
3. `21_bud_pipeline` に `税区分=課税` / `税抜_スポット売上=1000` を手入力し、
   `消費税額_スポット` が 0 になること(現在 `CFG_TAX_PAYER_STATUS=免税事業者`)
4. `03_sys_params` の `CFG_TAX_PAYER_STATUS` を `課税事業者` に変更 →
   GASエディタで `Constants._paramsCache = null` を実行 → パイプラインRPA実行 →
   生成 INV の `税抜金額_計画`=1000, `消費税額_計画`=100, `税込金額_計画`=1100,
   `税区分`=`課税` になることを確認
5. 同シートで `税抜_スポット売上=2000` を手入力し、`消費税額_スポット`=200 になること(onEdit)
6. `CFG_TAX_PAYER_STATUS` を `免税事業者` に戻し、同RPA実行で
   `消費税額_計画`=0, `税区分`=`対象外` になることを確認
7. `900_test/901_test_runner.js` に関連テストがある場合はテストを実行する

### 拡張思考の使用状況
| フェーズ | 拡張思考 | 備考 |
|---------|---------|------|
| 実行前Read調査 | あり | 行番号・列名・InvoiceDTO構造の確定に活用 |
| 実装(コード書き込み) | なし | 仕様書で確定済みの書き下しに徹する |

推奨実行モデル

工程推奨モデル理由
全体(Step 1〜4)Claude Sonnet 4.6複数ファイル横断だが仕様書で修正内容・列名・行番号が具体化済み。会計ロジックの判断は事業者区分の分岐のみ
Step 1(定数追加)Claude Haiku 4.5定数1行追加・SHEET_DEFAULTS 変更のみ。判断要素なし
Step 2(DDL・マイグレーション新規)Claude Sonnet 4.6DDL列並び順・マイグレーションの冪等性設計に判断が必要
Step 3(onEdit)Claude Sonnet 4.6既存パターン踏襲だが税区分分岐あり
Step 4(RPA)Claude Sonnet 4.6事業者区分 x 税区分のマッピング分岐あり

変更履歴

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

仕様書作成プロンプト

展開して表示
<instruction>
【タイムアウト回避・実行原則(v1.7・必ず遵守すること)】
1. **拡張思考の使い分け**: Phase 1(設計)では拡張思考をフル活用し、ファイル名・エッジケース一覧・Step分割粒度・固有名詞(関数名/シート名/列名/行番号)を完全に確定させる。Phase 2(清書)の各Step内では拡張思考を最小限に抑え、Phase 1で確定した内容の書き下しに徹する。出力途中で再考しない。
2. **テキスト報告の禁止**: 「〜を作成します」等のtextのみで tool_use なしに turn を終了しない。説明は1文以内。直ちに tool を呼ぶ。
3. **4-5分割の Write/Edit 実行**: Step 2-1(骨格 ~20行)→ 2-2(概要〜注意事項 ~300行)→ 2-3a(エッジケース〜人間検討事項 ~200行)→ 2-3b(実装プロンプト〜変更履歴 ~250行)→ 2-4(`<details>` プロンプト全文記録)の順に分割実行する。
4. **各Stepで何を書くかを具体指示**: 設計判断をPhase 2実行時に持ち込まない。

======================================================================
あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者兼仕様書ライターです。
案件 S-11「パイプライン売上の消費税対応」の開発仕様書を作成してください。
作成後は `docs/_config.json` の `nav` 配列の適切なセクション(§E.2 バグ修正・バリデーション)にも必ず追記してください。

---

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

以下を順に実行し、固有名詞・行番号・型を全て確定させてから Phase 2 に進む。
**Grep は「どこにあるか」の発見まで。「どう書くか」の判断は必ず Read。推測した瞬間に手を止めて Read する。**

### 1-A: 案件定義の読み込み
`docs/_internal/TODO_future.md` で S-11 の「案件名・概要・期待効果・人間が検討すべき事項」を取得する。

### 1-B: 関連コードの Read(全て Read で裏取り)

**① `000_infra/002_constants.js`**
- `Constants.getParam(key, defaultVal)` の実装(引数・デフォルト値の扱い・`03_sys_params` 参照ロジック・`_paramsCache` のリセット条件)を確認する
- `SHEET_DEFAULTS` の `21_bud_pipeline` エントリ(現在の列名 `スポット売上・初期費用` / `継続月額(MRR)` およびデフォルト値)を確認し、リネーム後の更新内容を確定する
- `DEFAULT_TAX_RATE`(法人税率)の定義位置を確認し、`CONSUMPTION_TAX_RATE: 0.10`(消費税率)の追記位置を決定する

**② `400_domain/406_rpa_pipeline.js`**(パイプライン売上→INV生成ロジック)
- INV生成箇所・行番号を特定する
- 現在どの列名から金額を取得しているか(`スポット売上・初期費用` / `継続月額(MRR)` 等)を確認する
- 冪等性チェックのキーとなる列名を確認し、列名リネーム後に影響を受けないか検証する
- `InvoiceRepository.append()` への引き渡し箇所と `InvoiceDTO` のプロパティマッピング箇所を行番号付きで特定する

**③ `000_infra/003_contracts.js`**
- `InvoiceDTO` の税関連プロパティ(`税抜金額_計画` / `消費税額_計画` / `税込金額_計画` / `税区分`)のプロパティ名を正確に確認し、RPAマッピングの設計根拠とする

**④ `300_ui/301_ui_assist.js`**
- 既存の `onEdit` ハンドラでシート名判定・列判定を行っているパターン(コード形式)を確認する
- 消費税自動計算ロジックを追加する挿入位置(行番号)を特定する

**⑤ `100_config/101_sys_config.js`**
- `setupAllSchemas` 内の `21_bud_pipeline` DDL列定義箇所を行番号付きで特定する
- `🔧 マイグレーション` メニューの末尾行を確認し、`809_migration_s11_pipeline_tax` エントリの挿入位置を特定する
- `03_sys_params` への `CFG_TAX_PAYER_STATUS` 初期値追加をどの関数で行うか(`setupAllSchemas` 内か別関数か)確認する

**⑥ `docs/dev/dev_mas-116_migration_framework.md`**
- S-44で確立したマイグレーションスクリプトの実装パターン(冪等性チェック / `Utils.logInfo` + `SpreadsheetApp.getUi().alert` / メニュー登録)を確認する

### 1-C: テンプレート確認
`docs/dev/dev_mas-080_pipeline_early_id.md`(onEdit拡張・パイプライン系)を Read し、仕様書フォーマットを把握する。

---

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

出力先: `docs/dev/dev_mas-083_pipeline_tax.md`

**【厳守】1回の tool_use で全内容を書き切らないこと。必ず以下の Step に分割して実行する。**

---

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

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

```
# MAS-083: パイプライン売上の消費税対応
## 概要 / ## 目的 / ## 現在のコード / ## 修正方針 / ## 影響範囲
## 注意事項 / ## エッジケース / ## 実データ検証 / ## 関連ドキュメント
## 人間が検討すべき事項 / ## 実装プロンプト / ## 推奨実行モデル
## 変更履歴 / ## 仕様書作成プロンプト
```

---

### Step 2-2: 前半セクションの追記(Edit または Bash heredoc, ~300行)

Phase 1で確定した内容を書き下す。以下のセクションを記述する:

**## 概要**: テーブル(案件ID, カテゴリ, Phase, 優先度, 所要時間, 対象ファイル, 前提案件)

**## 目的**: 1〜3文。パイプライン売上シートに消費税関連列を追加し、RPAによるINV生成時に課税/免税事業者の区分に応じた正確な税額をマッピングすることで、P/L・B/S計上値の正確性を確保する目的を記述する。

**## 現在のコード**: Phase 1で特定した以下の箇所をスニペット付きで記述する:
- `002_constants.js`: `SHEET_DEFAULTS` の `21_bud_pipeline` エントリ(現在の `スポット売上・初期費用` / `継続月額(MRR)` の列名とデフォルト値)
- `406_rpa_pipeline.js`: INV生成時の金額取得箇所(Phase 1で特定した行番号付き)
- `101_sys_config.js`: `21_bud_pipeline` のDDL列定義箇所(Phase 1で特定した行番号付き)

**## 修正方針**: 以下の4ステップ構成で記述する:

**Step 1: 消費税率定数と事業者区分設定の追加**
- `002_constants.js` の `DEFAULT_TAX_RATE` 直下(Phase 1で確認した位置)に `CONSUMPTION_TAX_RATE: 0.10` を追加する
- `101_sys_config.js` の `setupAllSchemas`(または Phase 1で確認した初期化関数)に `03_sys_params` シートへ `CFG_TAX_PAYER_STATUS` パラメータを追加する処理を追記する
- ロジック内からは `Constants.getParam('CFG_TAX_PAYER_STATUS', '免税事業者')` で読み出す(デフォルト値: 免税事業者)

**Step 2: DDL変更とデータ移行**
- `101_sys_config.js` の `setupAllSchemas` 内 `21_bud_pipeline` DDL定義(Phase 1で特定した行番号)を以下の通り更新する:
  - `スポット売上・初期費用` → `税抜_スポット売上` にリネーム
  - `継続月額(MRR)` → `税抜_継続月額(MRR)` にリネーム
  - `税区分` / `消費税額_スポット` / `消費税額_継続月額` の3列を追加
- `002_constants.js` の `SHEET_DEFAULTS` の `21_bud_pipeline` エントリの列名を同様に更新し、`'税区分': '課税'` をデフォルト値に追加する(`smartAddRow` で旧列名のデフォルトが設定されるのを防ぐ)
- DDL変更とデータ移行は `800_ops/809_migration_s11_pipeline_tax.js` として実装する(CLAUDE.md: 次のマイグレーションは809から)
  - 関数名: `migrationS11PipelineTax()`
  - 冪等性: `税区分` 列が既に存在する場合はスキップ
  - 既存行の消費税額を `税区分`=`課税` の場合に `Math.floor(税抜金額 * Constants.CONSUMPTION_TAX_RATE)` で一括計算して書き込む
  - ログ: `Utils.logInfo` + `SpreadsheetApp.getUi().alert` の両方
  - メニュー登録: `101_sys_config.js` の `🔧 マイグレーション` メニュー末尾(Phase 1で特定した行番号)に追加

**Step 3: 消費税自動計算ロジックの追加(onEdit)**
- `300_ui/301_ui_assist.js` の `onEdit` ハンドラに、`21_bud_pipeline` シート上で `税抜_スポット売上` / `税抜_継続月額(MRR)` / `税区分` 列が編集された場合の消費税自動計算を追加する(Phase 1で特定した挿入位置)
- `Constants.getParam('CFG_TAX_PAYER_STATUS', '免税事業者') === '課税事業者'` の場合のみ: `消費税額 = Math.floor(税抜金額 * Constants.CONSUMPTION_TAX_RATE)`
- `免税事業者` の場合: 対応する消費税額列を `0` にセットする

**Step 4: RPAロジックの修正(406_rpa_pipeline.js)**
- Phase 1で特定した INV生成箇所を修正する
- **課税事業者の場合**: `税抜_スポット売上` → `InvoiceDTO.税抜金額_計画`、`消費税額_スポット` → `InvoiceDTO.消費税額_計画`、税込 = 税抜 + 消費税 → `InvoiceDTO.税込金額_計画`、`税区分` はそのままマッピング
- **免税事業者の場合**: `税抜_スポット売上` → `InvoiceDTO.税抜金額_計画` と `InvoiceDTO.税込金額_計画` の両方にセット、`InvoiceDTO.消費税額_計画 = 0`、`InvoiceDTO.税区分 = '対象外'`(会計上の整合性のため `InvoiceDTO` の構造を崩さない)

**## 影響範囲**: 変更ファイル一覧と変更量概算をテーブルで記述する

**## 注意事項**: 番号付きリストで以下を記述する:
1. `setupAllSchemas`(DDL再実行)は既存データを上書きする可能性があるため、`migrationS11PipelineTax()` を先に実行してからDDLを更新する順序を厳守する
2. `SHEET_DEFAULTS` の `21_bud_pipeline` エントリの列名更新を忘れると、`smartAddRow` で旧列名のデフォルト値が設定されて新列に入らない
3. `Constants.getParam()` は内部で `_paramsCache` を使用する。`03_sys_params` シートを変更した後は `Constants._paramsCache = null` でキャッシュをリセットしてから再取得する
4. `onEdit` トリガーは一括貼り付け・スクリプトによる書き込みでは発火しない。既存行の消費税額はマイグレーションスクリプト内の一括計算で補完する

---

### Step 2-3a: エッジケース〜人間検討事項の追記(Edit または Bash, ~200行)

**## エッジケース**: 以下をテーブル形式(| 条件 | 表示値・動作 | 理由 |)で記述する:

| 条件 | 表示値・動作 | 理由 |
|------|------------|------|
| `CFG_TAX_PAYER_STATUS` が `03_sys_params` 未設定 | 免税事業者として動作 | `Constants.getParam('CFG_TAX_PAYER_STATUS', '免税事業者')` のデフォルト値が採用される |
| `税区分` 列が空白 | `onEdit` でスキップし消費税額を変更しない | `SHEET_DEFAULTS` で `'課税'` をデフォルト設定しているため通常は空にならない |
| 金額がゼロ | `消費税額 = 0`(`Math.floor(0 * 0.10) = 0`) | 正常動作 |
| 金額がマイナス(値引き等) | `消費税額` もマイナス追従(例: -1000 → -100)。入力バリデーションで弾くことが望ましい | マイナスINVを生成した場合の会計処理と一致させるため |
| 課税事業者→免税事業者へ変更後 | 既存パイプライン行の消費税額列は自動リセットされない。`Constants._paramsCache = null` リセット後にマイグレーションスクリプトを再実行する必要がある | `onEdit` は発火しないため |
| RPAの冪等性(二重INV防止) | `406_rpa_pipeline.js` 既存の冪等性チェックキーが列名変更(`スポット売上・初期費用` → `税抜_スポット売上`)の影響を受けないことを Phase 1で確認済みであること | 影響がある場合はチェックキーも合わせて更新する |

**## 実データ検証**(実装前にMCPで確認すべき項目):
- `21_bud_pipeline` の実際の列構成(`スポット売上・初期費用` / `継続月額(MRR)` の列インデックスがDDL定義と一致しているか)
- `03_sys_params` シートに `CFG_TAX_PAYER_STATUS` が既に存在しないか(二重追加防止)
- 既存パイプライン行の有効行数(マイグレーション対象件数の把握)

**## 関連ドキュメント**: テーブル(仕様書リンク | 関連箇所)で記述する

**## 人間が検討すべき事項**: `TODO_future.md` から転記した項目に加えて以下を追記する:
- 課税事業者への移行時期と `CFG_TAX_PAYER_STATUS` 変更タイミングの確認(移行前後でRPAを一時停止するか否か)
- `809_migration_s11_pipeline_tax.js` の実行タイミング(devで検証→prodは管理者が手動実行)
- 税区分のデフォルト値を `'課税'` とすることが、現在免税事業者として運用しているユーザーに与える影響の確認

---

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

**## 実装プロンプト(Claude Code 用)**(行頭4スペースインデント・バッククォートで囲まない):

    あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者です。
    案件 S-11「パイプライン売上の消費税対応」を実装してください。

    ## 実行前タスク
    - `000_infra/002_constants.js` を Read: `SHEET_DEFAULTS` の `21_bud_pipeline` エントリの現在の列名と
      デフォルト値、`Constants.getParam()` のシグネチャ(引数・キャッシュ挙動)を確認する
    - `400_domain/406_rpa_pipeline.js` を Read: INV生成箇所・行番号・冪等性チェックのキー列を特定する
    - `300_ui/301_ui_assist.js` を Read: `onEdit` ハンドラのシート名判定・列判定パターンと挿入位置を確認する
    - `100_config/101_sys_config.js` を Read: `21_bud_pipeline` DDL列定義箇所の行番号と
      `🔧 マイグレーション` メニューの末尾行を特定する
    - MCP等で `21_bud_pipeline` の実際の列構成を確認し、`スポット売上・初期費用` / `継続月額(MRR)` の存在を確認する

    ## 修正対象ファイル
    - `000_infra/002_constants.js`: `CONSUMPTION_TAX_RATE` 追加、`SHEET_DEFAULTS` 列名更新
    - `100_config/101_sys_config.js`: DDL列定義更新、`03_sys_params` 初期化追加、マイグレーションメニュー追加
    - `300_ui/301_ui_assist.js`: `onEdit` への消費税自動計算ロジック追加
    - `400_domain/406_rpa_pipeline.js`: INV生成ロジックの税額マッピング修正
    - `800_ops/809_migration_s11_pipeline_tax.js`: 新規作成

    ## 実装内容
    仕様書「修正方針」の Step 1〜4 に従い、以下の順で実装する:
    Step 1(定数・設定追加)→ Step 2(DDL更新・マイグレーションスクリプト作成)→
    Step 3(onEdit追加)→ Step 4(RPA修正)

    ## 制約
    - `406_rpa_pipeline.js` 以外のRPAファイル(`401_rpa_hc.js` 等)は変更禁止
    - DDL変更(setupAllSchemas再実行)は `migrationS11PipelineTax()` 実行後に行う順序を厳守する
    - 列参照はヘッダー名ベース(`indexOf` / `buildHeaderIndex_` 等を使用。列番号ハードコード禁止)
    - `03_sys_params` の直接読み取りは禁止。必ず `Constants.getParam()` 経由で取得する
    - `PropertiesService.getScriptProperties()` を直接呼ばない。`Env` モジュール経由で取得する

    ## エッジケース
    仕様書「エッジケース」テーブルを参照する。特に以下を確認する:
    - `CFG_TAX_PAYER_STATUS` 未設定時のフォールバック(免税事業者として動作)
    - 冪等性チェックキーが列名リネームの影響を受けていないこと

    ## 動作確認
    1. `npm run push:dev` でデプロイする
    2. GASエディタで `migrationS11PipelineTax()` を実行し、`21_bud_pipeline` に
       `税区分` / `消費税額_スポット` / `消費税額_継続月額` 列が追加されることを確認する
    3. `21_bud_pipeline` シートで `税区分`=`課税`、`税抜_スポット売上`=1000 を入力し、
       `消費税額_スポット` が 100 になることを確認する(onEditorトリガー)
    4. `03_sys_params` の `CFG_TAX_PAYER_STATUS` を `課税事業者` に設定後、
       パイプラインRPAを実行し、生成された INV の `消費税額_計画` が正しくマッピングされることを確認する
    5. `CFG_TAX_PAYER_STATUS` を `免税事業者` に設定(`Constants._paramsCache = null` でリセット後)、
       同RPA実行で `消費税額_計画`=0、`税区分`=`対象外` になることを確認する
    6. `900_test/901_test_runner.js` に関連テストがある場合はテストを実行する

    ### 拡張思考の使用状況
    | フェーズ | 拡張思考 | 備考 |
    |---------|---------|------|
    | 実行前Read調査 | あり | 行番号・列名の確定に活用 |
    | 実装(コード書き込み) | なし | 調査確定内容の書き下しに徹する |

**## 推奨実行モデル**:

| 工程 | 推奨モデル | 理由 |
|------|----------|------|
| 全体(Step 1〜4) | Claude Sonnet | 複数ファイル横断だが仕様書で修正内容・行番号が具体化済み。会計ロジックの判断は最小限 |

**## 変更履歴**:

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

---

### Step 2-4: 仕様書作成プロンプトの記録(Edit または Bash)

末尾の `## 仕様書作成プロンプト` セクションに以下を追記する:

```
<details><summary>展開して表示</summary>

(この <instruction> 全文をここにそのまま記録する)

</details>
```

---

## Phase 3: 後処理(各ステップ完了後、即座に実行)

1. **`docs/_config.json` への追記**: `nav` 配列の §E.2 セクションに以下を追加する:
   ```json
   { "file": "dev/dev_mas-083_pipeline_tax.md", "title": "E.2.X MAS-083 パイプライン売上の消費税対応" }
   ```

2. **`docs/_internal/changelog.md` への追記**: ヘッダー直後の先頭行に追記する:
   ```
   | 2026-04-19 | [dev_mas-083_pipeline_tax.md](dev_mas-083_pipeline_tax.md) | 初版作成。パイプライン売上の消費税対応仕様書 |
   ```

3. **コミット&プッシュ**:
   ```
   git add docs/dev/dev_mas-083_pipeline_tax.md docs/_internal/changelog.md docs/_config.json
   git commit -m "docs: MAS-083 パイプライン売上の消費税対応の開発仕様書を作成"
   git push -u origin {現在のブランチ}
   ```
</instruction>