概要

項目内容
案件IDMAS-120
カテゴリDDL・マスタ
PhaseP2
優先度★★
所要時間4〜6 時間(3 Step)
対象ファイル100_config/101_sys_config.js, 000_infra/003_contracts.js, 200_data/202_repository.js, 300_ui/301_ui_assist.js, 800_ops/810_migration_partner_payment_terms.js(新規)
前提案件MAS-110(日付自動補完), MAS-116(マイグレーション基盤), MAS-118(起票ターゲット月列)

目的

21_bud_pipeline / 23_bud_subscription / 26_bud_adhoc の各予算タブで「取引先ごとにほぼ固定の決済条件(決済手段・決済ラグ・支払基準日・休日調整・CF計上)」がレコードごとに手入力されている構造を解消する。12_mst_partner に 5 つの「標準〜」列を新設し、取引先名入力時に onEdit で自動補完することで、表記ゆれ・設定漏れによる決済日計算のズレを防ぎ、取引先の支払条件変更時はマスタ 1 箇所の更新で一貫性を担保する。

現在のコード

MST_PART スキーマ(100_config/101_sys_config.js L478)

'MST_PART': { headers: ["有効フラグ","取引先コード","法人番号","略称_4文字","取引先名_正式","略称","銀行摘要名","UI用取引先名","取引先区分"], color: "#666666" },

現状 9 列。本案件で 5 列追加して 14 列にする。

20番台予算タブの決済条件列(100_config/101_sys_config.js

タブキー決済条件に該当する列(マスタとの対応)
BUD_PIPE (21)L495決済手段 / CF計上 / 入金ラグ(月) / 入金日 / 休日調整
BUD_SUBS (23)L496決済手段 / 決済ラグ(月) / 支払基準日 / 休日調整 / CF計上
BUD_ADHOC (26)L493決済手段 / 決済ラグ(月) / 支払基準日 (休日調整 / CF計上は列なし

注意: BUD_PIPE は収入系のため列名が「入金ラグ / 入金日」。BUD_ADHOC は 3 列のみ。

onEdit 補完の既存パターン(300_ui/301_ui_assist.js

  • handleUxAssist() L15-277 が onEdit から呼ばれる
  • 21_bud_pipeline: L251-276 で PJ・案件名 入力時に管理ID発番 + 契約形態 等の初期値 setIfBlank パターン
  • 23_bud_subscription: L68-97 で 3 列目入力時に SUB_ 発番 + 取引先名 等の初期値セット
  • 26_bud_adhoc: ハンドラなし(本案件で新設)
  • 32_wrk_invoice L165-180 に OrderRepository.findAll() 経由で取引先名を補完する先例あり

PartnerRepository / PartnerDTO の状態

200_data/202_repository.js の既存 Repository は以下の 5 つのみ: OrderRepository / InvoiceRepository / BankTxRepository / JournalRepository / AccountRepository

PartnerRepository は未実装。 000_infra/003_contracts.js にも PartnerDTO@typedef は存在しない(マスタ系 DTO は AccountRepository がインライン定義のみ)。本案件で新規作成する。

修正方針

Step 1: DDL スキーマ拡張(MST_PART + 検証範囲)

101_sys_config.js L478 の MST_PART headers に以下 5 列を末尾追加:

'MST_PART': { headers: [
  "有効フラグ","取引先コード","法人番号","略称_4文字","取引先名_正式","略称","銀行摘要名","UI用取引先名","取引先区分",
  "標準決済手段","標準決済ラグ(月)","標準支払基準日","標準休日調整","標準CF計上区分"
], color: "#666666" },

影響する他の DDL 処理:

  • L740 if (key === 'MST_PART') { inputCols = [1,2,3,...,9]; }[1,2,...,14] に拡張
  • L984 setVali('MST_PART', 9, 'N', '12_mst_partner') は 9 列目(取引先区分)のまま維持。新列のプルダウン検証は後続で追加:
    • 列 10(標準決済手段)→ BUD_SUBS/BUD_PIPE の決済手段プルダウンと同じソース S 列を参照(UI決済手段
    • 列 13(標準休日調整)→ UI休日調整 等に倣う(既存のプルダウンリストを流用)
    • 列 14(標準CF計上区分)→ UICF計上(「実績」「予算」等)
    • 列 11/12(ラグ・基準日)は自由入力(数値)

Step 2: PartnerRepository / PartnerDTO の新設

000_infra/003_contracts.js に以下を追加(他の DTO と同じ @typedef 形式):

/**
 * 12_mst_partner — 取引先マスタ
 * @typedef {Object} PartnerDTO
 * @property {boolean} 有効フラグ
 * @property {string}  取引先コード
 * @property {string}  法人番号
 * @property {string}  略称_4文字
 * @property {string}  取引先名_正式
 * @property {string}  略称
 * @property {string}  銀行摘要名
 * @property {string}  UI用取引先名
 * @property {string}  取引先区分
 * @property {string}  標準決済手段
 * @property {number}  標準決済ラグ(月)
 * @property {string|number} 標準支払基準日
 * @property {string}  標準休日調整
 * @property {string}  標準CF計上区分
 */

200_data/202_repository.jsAccountRepository と同形で PartnerRepository を新設:

var PartnerRepository = {
  _getSheet: function() { return Utils.getSheetByKey('MST_PART', '12_mst_partner'); },

  findAll: function() { return readSheetAsDtos_(PartnerRepository._getSheet()); },

  /**
   * UI用取引先名(または取引先名_正式)をキーとする決済条件マップを返す(キャッシュ付き)。
   * @returns {Object.<string, {決済手段, 決済ラグ月, 支払基準日, 休日調整, CF計上}>}
   */
  findPaymentTermsMap: function() {
    if (PartnerRepository._termsCache) return PartnerRepository._termsCache;
    var result = PartnerRepository.findAll();
    var map = {};
    for (var i = 0; i < result.dtos.length; i++) {
      var dto = result.dtos[i];
      var flag = dto['有効フラグ'];
      if (flag === false || String(flag).toUpperCase() === 'FALSE') continue;
      // UI用取引先名 優先、なければ 取引先名_正式 でキーを張る(両方で引けるようにする)
      var uiName = String(dto['UI用取引先名'] || '').trim();
      var formal = String(dto['取引先名_正式'] || '').trim();
      var terms = {
        決済手段: String(dto['標準決済手段'] || '').trim(),
        決済ラグ月: dto['標準決済ラグ(月)'],
        支払基準日: dto['標準支払基準日'],
        休日調整:  String(dto['標準休日調整'] || '').trim(),
        CF計上:    String(dto['標準CF計上区分'] || '').trim(),
      };
      if (uiName) map[uiName] = terms;
      if (formal && !map[formal]) map[formal] = terms;
    }
    PartnerRepository._termsCache = map;
    return map;
  },

  _termsCache: null,
  resetCache: function() { PartnerRepository._termsCache = null; },
};

キャッシュ方針: AccountRepository.findAsMap() と同じく「同一実行内で複数回呼ばれる前提」でキャッシュ。onEdit は都度別実行なので、セッション跨ぎで古い値を返す問題は構造上起きない。

Step 3: handleUxAssist への自動補完ロジック追加

301_ui_assist.js で以下 3 シートのハンドラに「取引先名入力 → 決済条件 5 項目を補完」ロジックを追加。

共通ヘルパーhandleUxAssist 内に定義 or 外出し):

// シート別の列マッピング(マスタ項目 → シート上の列名)
var PARTNER_TERMS_COL_MAP = {
  '21_bud_pipeline':     { 決済手段: '決済手段', 決済ラグ月: '入金ラグ(月)', 支払基準日: '入金日',   休日調整: '休日調整', CF計上: 'CF計上' },
  '23_bud_subscription': { 決済手段: '決済手段', 決済ラグ月: '決済ラグ(月)', 支払基準日: '支払基準日', 休日調整: '休日調整', CF計上: 'CF計上' },
  '26_bud_adhoc':        { 決済手段: '決済手段', 決済ラグ月: '決済ラグ(月)', 支払基準日: '支払基準日' }, // 休日調整・CF計上 列なし
};

function applyPartnerPaymentTerms_(sheet, row, headers, partnerName) {
  if (!partnerName) return;
  var sName = sheet.getName();
  var mapKeyName = Object.keys(PARTNER_TERMS_COL_MAP).find(function(k) { return sName.indexOf(k) !== -1; });
  if (!mapKeyName) return;
  var colMap = PARTNER_TERMS_COL_MAP[mapKeyName];
  var termsMap = PartnerRepository.findPaymentTermsMap();
  var terms = termsMap[String(partnerName).trim()];
  if (!terms) return; // マスタ未登録 → 何もしない(既存値保持)

  Object.keys(colMap).forEach(function(termKey) {
    var colName = colMap[termKey];
    var cIdx = headers.indexOf(colName);
    if (cIdx === -1) return; // シートに該当列がない(BUD_ADHOC の 休日調整/CF計上 等)→ スキップ
    var cell = sheet.getRange(row, cIdx + 1);
    if (!cell.isBlank()) return; // ★ 基本原則: 空欄のみ補完(上書きしない)
    var v = terms[termKey];
    if (v === '' || v == null) return; // マスタ値が空なら転記しない
    cell.setValue(v);
  });
}

呼び出し位置:

  1. 21_bud_pipeline(L251-276 の if ブロック内に追加)
    • 既存 L267 setIfBlank('CF計上', '予算') の直後に、applyPartnerPaymentTerms_(sheet, row, headers, sheet.getRange(row, headers.indexOf('取引先名')+1).getValue()) を呼び出す
    • さらに、取引先名 列が後から編集された場合にも補完するため、colName === '取引先名' 分岐を追加
  2. 23_bud_subscription(L68-97 の if ブロック内に追加)
    • 既存 L89-91 の setIfBlank('決済手段', '未定・選定中') / setIfBlank('決済ラグ(月)', 1) より前applyPartnerPaymentTerms_ を呼ぶ(マスタ値を優先し、残った空欄のみデフォルト値で埋める)
    • colName === '取引先名' 分岐を追加
  3. 26_bud_adhoc(新規ハンドラ)
    • 既存の validSheets(L20)に '26_bud_adhoc' が含まれていることを確認(現状未登録なら追加)
    • 新規 if (sName.includes('26_bud_adhoc')) { ... } ブロックを else if (sName.includes('21_bud_pipeline'))直後に配置
    • colName === '取引名' or colName === '取引先名' で発火

Human-in-the-Loop の挙動:

状況挙動
初回入力(該当セル空欄)マスタ値で補完
ユーザーが補完後に手動変更 → 別の値上書きしない(既に非空欄のため)
取引先名を変更(A社 → B社)既に非空欄のため上書きしない(ユーザーが明示的にセルをクリアしてから再入力する運用)
マスタ未登録の取引先何もしない(既存値保持)
マスタ登録済だが該当マスタ値が空その列は補完しない(空欄のまま)

Step 4: マイグレーションスクリプト 810_migration_partner_payment_terms.js

800_ops/810_migration_partner_payment_terms.js808_migration_i24.js パターンに準拠して新設。

function migrationS48PartnerPaymentTerms() {
  var FUNC = 'migrationS48PartnerPaymentTerms';
  try {
    // 1. 21/23/26 タブから (取引先名, 決済手段, ラグ, 基準日, 休日調整, CF計上) を収集
    //    BUD_PIPE は 入金ラグ(月) / 入金日 のため列名マッピングで吸収
    //    各タブ 有効フラグ=TRUE かつ 取引先名が空でない行のみ
    // 2. 取引先名ごとに各項目の「最頻値」を抽出
    //    - 文字列項目: Object のキー count 集計 → max
    //    - 数値項目(ラグ・基準日): 同値カウント → max
    //    - 同着の場合は先に出現した値を優先
    // 3. PartnerRepository.findAll() で既存 MST_PART を取得
    // 4. 各取引先について、マスタの標準〜列が空 かつ 最頻値がある場合のみ UPDATE(★冪等性)
    //    既に値が入っている行はスキップ(人間の設定を尊重)
    // 5. 集計結果 + 更新件数 + スキップ件数を Utils.logInfo + SpreadsheetApp.getUi().alert

    Utils.logInfo(FUNC, '完了: 更新=' + updated + ' スキップ=' + skipped + ' 未登録取引先=' + missingPartner);
  } catch (e) {
    Utils.logError(FUNC, e);
    SpreadsheetApp.getUi().alert('エラー', e.message, SpreadsheetApp.getUi().ButtonSet.OK);
  }
}

メニュー登録101_sys_config.js onOpen 系の「🔧 マイグレーション」サブメニュー):

.addItem('S-48: 取引先決済条件マスタ化 (migrationS48PartnerPaymentTerms)', 'migrationS48PartnerPaymentTerms')

影響範囲

変更ファイル変更量内容
100_config/101_sys_config.js約 10 行MST_PART の headers + inputCols 拡張、プルダウン検証 3 行追加、マイグレーションメニュー 1 行追加
000_infra/003_contracts.js約 20 行PartnerDTO @typedef の追加
200_data/202_repository.js約 40 行PartnerRepository の新設(findAll / findPaymentTermsMap / resetCache
300_ui/301_ui_assist.js約 60 行PARTNER_TERMS_COL_MAP / applyPartnerPaymentTerms_ + 3 シートの呼び出し追加
800_ops/810_migration_partner_payment_terms.js約 120 行(新規)最頻値抽出 + 冪等 UPDATE

既存動作への影響:

  • MST_PART に 5 列追加することで、既存の MST_PART 参照コード(略称リネーム処理 L348-470 等)は indexOf ベースのためそのまま動作するが、setupAllSchemas 実行後に列幅・行数の再設定が入るため、手動で入れた値がクリアされないかを dev で事前確認
  • onEdit の処理時間: PartnerRepository.findPaymentTermsMap のキャッシュにより 2 回目以降は O(1)。初回は 12_mst_partner 全件読み込み(通常数十〜数百行)で 100-300ms 程度
  • マイグレーション実行後、20 番台タブの既存データは変更しない(マスタのみ初期化)。以降の新規行追加時に自動補完される

注意事項

  1. BUD_ADHOC は 休日調整 / CF計上 列が存在しないPARTNER_TERMS_COL_MAP['26_bud_adhoc'] に該当キーを含めないこと。applyPartnerPaymentTerms_indexOf で -1 の列をスキップする仕様になっているが、マップ側でも明示的に除外して意図を明確化する。
  2. BUD_PIPE の列名は「入金ラグ(月)」「入金日」 — マスタは「標準決済ラグ(月)」「標準支払基準日」で統一するが、転記先の列名は入金系に変換する(PARTNER_TERMS_COL_MAP で吸収)。
  3. 列参照はヘッダー名ベースindexOf)。列番号ハードコード禁止(CLAUDE.md コーディング規約)。
  4. 有効フラグ=FALSE の取引先はスキップfindPaymentTermsMap でフィルタ済み。マイグレーション側でも同条件で集計対象から除外。
  5. キャッシュの寿命PartnerRepository._termsCacheGAS 実行単位でリセットされる。マイグレーション直後に onEdit が走る場合でも別実行なので古い値を返さない。ただし、手動で MST_PART を直接編集した直後の onEdit では古い値を返す可能性あり → 許容する(onEdit は軽量補完であり、正確性はマスタ側の保守に依存)。
  6. 取引先キーのフォールバックUI用取引先名取引先名_正式 の両方をマップのキーにすることで、MAS-154(略称プレフィックス)付与の有無に関わらず引けるようにする。既存の 20 番台タブは UI用取引先名 を使う運用。
  7. 既存の setIfBlank('決済手段', '未定・選定中')(23_bud_subscription L89)との順序 — マスタ補完が先、デフォルト値 setIfBlank は後。こうすることでマスタ未登録取引先でも最終的に空欄にならない。
  8. 列ズレリスク — MST_PART の列追加は末尾追加のため indexOf ベースのコードは無影響。ただし、列番号ベースで書かれた古い箇所がないか、grep12_mst_partner / MST_PART を全検索して確認する(101_sys_config.jssetVali 9 列目固定は維持すべき)。
  9. 冪等性 — マイグレーションは「標準〜列が空欄の場合のみ UPDATE」。2 回目実行でも既存値を上書きしない。
  10. Repository のキャッシュクリアタイミング — マイグレーション関数の冒頭で if (PartnerRepository.resetCache) PartnerRepository.resetCache() を呼び、最新値を読むようにする(MAS-003 Step 1 失敗例と同じ轍を踏まないため)。

エッジケース

条件補完結果理由
マスタに取引先未登録何もしない既存値を尊重。ユーザーが手入力するか、マスタを追加してから再入力する運用
マスタ登録済、一部の標準〜列のみ空空でない列のみ補完部分補完を許容(CF計上区分が支出種別依存で取引先単位に決まらない場合への対応)
既にシートのセルが非空上書きしないHuman-in-the-Loop: 人間が明示的に設定した値を尊重
取引先名が空欄で onEdit 発火何もしない早期 return
取引先名に前後空白含むtrim() で正規化してマップ検索表記ゆれ吸収
マスタに同一取引先が 2 行 (有効=TRUE×2)後勝ち(for ループで最後に出現した行で上書き)運用上発生すべきでない。警告ログを Utils.logWarn で出すことを推奨
複数パターンの決済条件を持つ取引先(サブスクと単発で異なる)マスタは 1 パターンのみ保持Step 0 の運用で「主要パターンをマスタ登録、例外は手入力上書き」とする(人間が検討すべき事項 参照)
BUD_ADHOC で休日調整列なし自動スキップheaders.indexOf('休日調整') === -1 で補完対象外
マスタの「標準決済ラグ(月)」が "1" (文字列)setValue で数値型に自動変換されない可能性Step 2 の DTO で @property {number} だが実データは文字列の可能性あり → Step 1 DDL で列書式 #,##0 を設定して数値化を強制
マイグレーション時、取引先 A の最頻値が同点 (3 件 JCB / 3 件 銀行振込)先に出現した値(日付昇順ソート後の最初の値)を採用仕様として確定的に動作させる。集計サマリに同点注記を出す
マイグレーション 2 回目実行既存マスタ値はスキップ、未設定のみ追加冪等性確保

実データ検証(MCP での事前確認が必要)

仕様書記述時および実装前に、以下を MCP / GAS エディタで確認すること(MAS-003 失敗パターン #18-#20 の轍を踏まないため):

  1. MST_PART の現在の列数・列名 — DDL 定義(9 列)と実シート(12_mst_partner)の列構成が一致しているか。setupAllSchemas で整合済みか。
  2. 20 番台タブの「決済手段」「決済ラグ(月)」「支払基準日」「休日調整」「CF計上」列の存在確認 — 本仕様書で列名マッピングした通りのヘッダー文字列が実シートに存在するか。特に BUD_PIPE の「入金ラグ(月)」「入金日」、BUD_ADHOC の欠損列(休日調整・CF計上)。
  3. 既存データの取引先名表記ゆれ12_mst_partner.UI用取引先名 と、20 番台タブの 取引先名 列のデータが一致しているか(空白・全角/半角ゆれなど)。Step 4 マイグレーションの集計精度に直結。
  4. 標準〜列のプルダウン候補18_dropdown 等に UI決済手段 / UI休日調整 / UICF計上 のリストが存在するか、名前付き範囲の確認。なければ Step 1 で新設する必要あり。
  5. validSheets 配列(301_ui_assist.js L20)に '26_bud_adhoc' が含まれているか。含まれていなければ onEdit ハンドラが発火しないため、追加必須。
  6. MCP での 12_mst_partner の実データ取得 — 有効フラグ=TRUE の取引先数、標準〜列の空欄比率を事前把握。マイグレーション後の UPDATE 件数の見積もりに使う。

関連ドキュメント

仕様書関連箇所
dev_mas-110_document_date.md日付自動補完アーキテクチャ(onEdit + マイグレーションのパターン)
dev_mas-116_migration_framework.md800_ops マイグレーション基盤(冪等性・ログ・メニュー登録の統一パターン)
dev_mas-118_rpa_target_month_col.md20 番台予算タブの DDL 変更、列ズレ注意事項
dev_mas-154_partner_logical_abbr.mdMST_PART の UI用取引先名 / 略称プレフィックスの扱い
CLAUDE.mdコーディング規約(列参照はヘッダー名ベース)、マイグレーション運用ガイドライン
failure_patterns.md #18-#20固有名詞・列名は Read で実値確認してから記述

人間が検討すべき事項

  • 複数パターンを持つ取引先の扱い — 例: 同一取引先でサブスクと単発契約で決済手段が異なる場合、マスタは「主要パターン」のみ登録し、例外は手入力上書き運用とするか、将来 MAS-135(決済手段マスタの新設)と組み合わせて契約ベース解決するか。
  • オーバーライド後の再選択時の挙動 — 基本仕様は「空欄のみ補完・上書きしない」としたが、「取引先名を A→B に変更した場合は 5 項目も B の値に洗い替えるべき」という意見も想定される。運用で確認し、必要なら Step 5(将来拡張)で「再選択時は洗い替え or 警告表示してから洗い替え」のオプションを追加。
  • CF計上区分の標準値が取引先単位で定まらないケース — 取引先ではなく支出種別(経費 or CapEx 等)に依存する場合、マスタの「標準CF計上区分」は空欄運用とし、補完対象外とする。マイグレーションでも空欄の取引先はスキップ。
  • マイグレーション実行前の MST_PART バックアップ — MAS-201(シートバックアップ)完了後なら自動。未完了時は手動でシートコピーを推奨(本マイグレーションは冪等だが、最頻値集計の元データが変わると結果も変わるため)。
  • MAS-135(決済手段マスタ)との協調 — 優先順位 取引先マスタ (S-48) > 決済手段マスタ (S-63) > 個別入力 の運用。本案件は最上位レイヤーを実装。

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

あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者です。
案件 MAS-120「取引先マスタ拡張による20番台予算タブの入力簡素化」を実装してください。
大規模案件のため、Step 1 → Step 2 → Step 3 → Step 4 の順に段階実装し、各 Step 完了後に dev 動作確認してから次へ進むこと。

## 実行前タスク

以下のファイルを Read し、以下を把握すること(Grep の部分一致だけで構造を推測しない、failure_patterns.md #18-#20 参照):

- `docs/dev/dev_mas-120_partner_payment_terms_autofill.md` — 本仕様書の全文
- `docs/_internal/failure_patterns.md` — 特に #18-#20(固有名詞の Read 確認原則)
- `100_config/101_sys_config.js`
  - L478 `MST_PART` スキーマ定義
  - L493-L496 `BUD_ADHOC` / `BUD_HC` / `BUD_PIPE` / `BUD_SUBS` のヘッダー定義(列名を目視確認)
  - L740 `inputCols` 設定ブロック
  - L970-L990 `setVali` プルダウン検証ブロック
  - `onOpen` 系のマイグレーションメニュー定義箇所
- `000_infra/003_contracts.js` — 既存 `@typedef` の書式(OrderDTO, AccountRepository のインライン DTO)
- `200_data/202_repository.js`
  - L304-L350 `AccountRepository` — キャッシュ付き Repository のパターン
  - `readSheetAsDtos_` / `appendDtosToSheet_` の定義位置(PartnerRepository 新設時に利用)
- `300_ui/301_ui_assist.js`
  - L15-L277 `handleUxAssist` 全体の構造
  - L20 `validSheets` 配列に `26_bud_adhoc` が含まれているか
  - L68-L97 23_bud_subscription の既存 setIfBlank パターン
  - L251-L276 21_bud_pipeline の既存 setIfBlank パターン
  - L165-L180 OrderRepository.findAll 経由の補完パターン(参考)
- `800_ops/808_migration_i24.js` — マイグレーションスクリプトの標準パターン(冪等性、logInfo + alert、エラーハンドリング)

## 修正対象ファイル

1. `100_config/101_sys_config.js` — DDL スキーマ + プルダウン + メニュー(既存コードへの追記のみ)
2. `000_infra/003_contracts.js` — `PartnerDTO` `@typedef` 追加のみ
3. `200_data/202_repository.js` — `PartnerRepository` 新規追加(末尾に追記、既存 Repository は変更しない)
4. `300_ui/301_ui_assist.js` — ヘルパー関数 + 3 シートハンドラへの呼び出し追記
5. `800_ops/810_migration_partner_payment_terms.js` — 新規ファイル作成

## 実装内容

### Step 1: MST_PART DDL 拡張

- L478 の `MST_PART` headers 末尾に 5 列追加:
  `"標準決済手段","標準決済ラグ(月)","標準支払基準日","標準休日調整","標準CF計上区分"`
- L740 の `inputCols` を `[1,2,...,14]` に拡張
- `setVali` で列 10(標準決済手段)・13(標準休日調整)・14(標準CF計上区分)にプルダウン検証を追加
  - 参照元: 既存の 18_dropdown のうち `UI決済手段` / `UI休日調整` / `UICF計上` 等。実データ検証で名前付き範囲を事前確認すること
- 列 11/12(標準決済ラグ(月)/標準支払基準日)は数値書式 `#,##0` を DDL で設定
- setupAllSchemas 実行で dev 環境に反映 → 既存の 12_mst_partner のデータが保持されていることを目視確認

### Step 2: PartnerRepository / PartnerDTO 新設

- `000_infra/003_contracts.js` の既存 `@typedef` 群の末尾(`BudgetDTO` の後)に `PartnerDTO` を追加
- `200_data/202_repository.js` の末尾(`AccountRepository` の後)に `PartnerRepository` を追加
  - `_getSheet` / `findAll` / `findPaymentTermsMap` / `_termsCache` / `resetCache` の 5 メンバ
  - `findPaymentTermsMap` は有効フラグ=TRUE のみ。UI用取引先名 と 取引先名_正式 の両方をキーにする(MAS-154 略称前後の両ケースで引けるように)
  - キャッシュ実装は `AccountRepository.findAsMap` を忠実に踏襲
- `901_test_runner.js` に `PartnerRepository.findAll().dtos.length > 0` の簡易テストを追加

### Step 3: handleUxAssist への自動補完

- 301_ui_assist.js の L20 `validSheets` に `'26_bud_adhoc'` が未登録なら追加
- `PARTNER_TERMS_COL_MAP` 定数(ファイル先頭 or handleUxAssist 直前)を定義
- `applyPartnerPaymentTerms_(sheet, row, headers, partnerName)` ヘルパー関数を新設
- 3 シート(21/23/26)の既存 or 新規ハンドラから `colName === '取引先名'` もしくは管理ID採番直後のタイミングで呼び出す
- 23_bud_subscription は **マスタ補完を既存の setIfBlank より前**に呼ぶ(マスタ優先)
- 26_bud_adhoc は新規ハンドラブロックとして `else if (sName.includes('21_bud_pipeline'))` の直後に追加
- **原則「空欄のみ補完」** — `cell.isBlank()` チェックは必須

### Step 4: マイグレーションスクリプト

- `800_ops/810_migration_partner_payment_terms.js` を新設
- 関数名: `migrationS48PartnerPaymentTerms`
- 冒頭で `PartnerRepository.resetCache()` を呼ぶ
- 21/23/26 タブから (取引先名, 標準〜5項目に相当する列) を有効フラグ=TRUE 行のみ収集
  - BUD_PIPE の列名変換: 入金ラグ(月) → 決済ラグ月、入金日 → 支払基準日
- 取引先ごとの各項目最頻値を抽出(同点は先勝ち)
- MST_PART の該当取引先行について、標準〜列が**空欄の場合のみ** setValue で更新
- 結果を Utils.logInfo + SpreadsheetApp.getUi().alert でサマリ表示(更新/スキップ/未登録取引先数)
- 101_sys_config.js の「🔧 マイグレーション」メニューに `.addItem('S-48: 取引先決済条件マスタ化', 'migrationS48PartnerPaymentTerms')` を追加

## 制約

- 列参照は必ずヘッダー名ベース(`indexOf`)。列番号ハードコード禁止
- 既存の `OrderRepository` / `InvoiceRepository` 等には触らない
- 既存の `handleUxAssist` の他シートロジック(16_wrk_master, 32_wrk_invoice 等)には影響させない
- `setupAllSchemas` で MST_PART の既存データがクリアされないことを dev で事前確認
- BUD_ADHOC に無い列(休日調整・CF計上)を誤って追加しないこと(本案件スコープ外)
- マイグレーションは冪等。2 回目実行で既存マスタ値を上書きしない

## エッジケース

仕様書の「エッジケース」セクションのテーブルを実装時の判定ロジックに反映すること。特に:
- マスタ未登録取引先 → 何もしない(return 早期)
- 該当セルが非空 → 上書きしない(`cell.isBlank()` チェック必須)
- BUD_ADHOC の存在しない列 → `indexOf === -1` でスキップ
- マスタの標準〜値が空 → 転記しない(空で上書きしない)
- マイグレーション時の最頻値同点 → 先出現値採用

## 実データ検証

実装着手前に MCP で以下を確認:
1. 12_mst_partner の現在のヘッダー行と列数(DDL 定義 9 列と一致するか)
2. 21/23/26 タブの決済条件関連列のヘッダー文字列完全一致(特に BUD_PIPE の「入金ラグ(月)」「入金日」)
3. 18_dropdown の名前付き範囲 `UI決済手段` / `UI休日調整` / `UICF計上` の存在(Step 1 プルダウン設定用)
4. 301_ui_assist.js の validSheets 配列に '26_bud_adhoc' が入っているか
5. 有効フラグ=TRUE の取引先件数(マイグレーションでの UPDATE 見積もり)

## 動作確認

1. `npm run push:dev` で開発 GAS にデプロイ
2. GAS エディタで `setupAllSchemas` を実行 → 12_mst_partner に 5 列追加されること、既存データが保持されていること
3. 12_mst_partner に 1 件サンプル登録(例: 取引先名=「テスト社」、標準決済手段=「銀行振込」、標準決済ラグ(月)=1、標準支払基準日=25、標準休日調整=「前営業日」、標準CF計上区分=「実績」)
4. 23_bud_subscription で新規行作成 → サービス・ツール名を入力して管理ID発番 → 取引先名セルに「テスト社」入力
5. 決済手段/決済ラグ(月)/支払基準日/休日調整/CF計上 が自動で「銀行振込/1/25/前営業日/実績」になること
6. 既に「決済手段=JCB」が入っている行で取引先名を「テスト社」に変更 → JCB が維持されること(上書きしないこと)
7. 21_bud_pipeline で同じテストを実施 → 入金ラグ(月)/入金日 に正しく転記されること
8. 26_bud_adhoc で同じテストを実施 → 決済手段/決済ラグ(月)/支払基準日 のみ補完されること(休日調整/CF計上は列なしのためスキップ)
9. マスタ未登録の取引先名を入力 → 5 項目とも変化しないこと
10. `migrationS48PartnerPaymentTerms` を dev で実行 → サマリアラート表示・MST_PART に最頻値が登録されること
11. 2 回目の `migrationS48PartnerPaymentTerms` 実行 → 既存マスタ値が上書きされず、スキップ件数としてカウントされること
12. runAllTests で既存テストが回帰していないこと

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

| フェーズ | 拡張思考 | 備考 |
|---------|:--------:|------|
| Step 1 DDL 拡張 | なし | headers 配列末尾への文字列追加 + inputCols 範囲拡張のみ |
| Step 2 PartnerRepository | あり | AccountRepository のキャッシュパターン踏襲、UI用名/正式名の両キー対応の設計判断 |
| Step 3 handleUxAssist | あり | 3 シートで列名が異なる(特に BUD_PIPE 入金系)マッピング設計、呼び出し位置の判断 |
| Step 4 マイグレーション | あり | 最頻値集計アルゴリズム、冪等性保証、BUD_PIPE の列名変換 |

推奨実行モデル

工程推奨モデル理由
Step 1 DDL 拡張Claude Haiku 4.5headers 配列末尾追加・inputCols 拡張・setVali 行追加のみ、機械的な編集
Step 2 PartnerRepository 新設Claude Sonnet 4.6AccountRepository パターンの踏襲、キャッシュ設計、2 キー対応の中程度判断
Step 3 handleUxAssist 拡張Claude Opus 4.63 シート横断、列名マッピング、呼び出し位置、setIfBlank との順序、validSheets 登録漏れ確認
Step 4 マイグレーションClaude Sonnet 4.6808_migration_i24.js パターン踏襲、最頻値集計は標準的アルゴリズム

変更履歴

日付変更内容
2026-04-17初版作成。取引先マスタ 5 列拡張 + PartnerRepository 新設 + 20 番台予算タブ 3 シートの onEdit 自動補完 + 冪等マイグレーションの 4 ステップ設計
2026-04-20実装完了 (PR #215)。4 ステップ全て dev 検証済み。setIfBlank 既定値の上書き判定は PARTNER_TERMS_OVERRIDABLE_DEFAULTS で管理。マイグレーション冪等性を 2 回実行で確認(2 回目は更新 0 件)。残件: 18_dropdown の UI休日調整 / UICF計上 名前付き範囲追加と、数値書式 #,##0 の設定は MAS-140 として後続対応

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

展開して表示
あなたはGAS会計システム(bizlp-gas-accounting)のシニアアーキテクト兼仕様書ライターです。
案件 MAS-120「取引先マスタ拡張による20番台予算タブの入力簡素化(横断)」の開発仕様書を作成してください。

## 実行前タスク

以下のファイルを読み込み、既存の DDL 定義、Repository パターン、および入力補完ロジックを把握してください。
- `docs/_internal/TODO_future.md` — MAS-120 の概要確認
- `100_config/101_sys_config.js` — `schemas` 定義(MST_PART, BUD_PIPE, BUD_SUBS, BUD_ADHOC)
- `202_repository.js` — `PartnerRepository` の実装
- `300_ui/301_ui_assist.js` — `handleUxAssist` の既存ロジック
- `docs/dev/dev_mas-110_document_date.md` — 参考:onEdit による自動補完の仕様書

## 既存実装の前提知識

- 本システムは `101_sys_config.js` の `schemas` オブジェクトで DDL を管理しており、`setupAllSchemas` で反映する仕組みです。
- 入力補完は `301_ui_assist.js` の `handleUxAssist` が `onEdit` トリガーから呼び出されて実行されます。
- データアクセスは `202_repository.js` の Repository クラスを経由することが推奨されています。

## 仕様書の出力先ファイル名
`docs/dev/dev_mas-120_partner_payment_terms_autofill.md`

## 固有の設計要件

1. **DDL 拡張**:
   - `12_mst_partner` (MST_PART) に以下の5列を追加する。
     - 標準決済手段、標準決済ラグ(月)、標準支払基準日、標準休日調整、標準CF計上区分
2. **Repository 拡張**:
   - `PartnerRepository` および `PartnerDTO` を更新し、新設された5列を取得可能にする。
3. **自動補完ロジック (handleUxAssist)**:
   - 21_bud_pipeline, 23_bud_subscription, 26_bud_adhoc の各シートで「取引先名」が入力された際、マスタから上記5項目を自動転記する。
   - **Human-in-the-Loop**: 自動補完された値はユーザーが手動で上書き可能とし、再選択時に空欄でない場合は上書きするかスキップするかの挙動を定義すること(基本は「空欄の場合のみ補完」を推奨)。
4. **マイグレーションスクリプト**:
   - `810_migration_partner_payment_terms.js` を新設。
   - 既存の 21/23/26 タブのデータから、取引先ごとの決済条件の「最頻値(Mode)」を抽出し、マスタに初期登録するロジックを設計。
5. **エッジケース**:
   - マスタに取引先が存在しない場合。
   - マスタの値が空(未設定)の場合。
   - 1つの取引先で複数の決済条件を使い分けている場合のフォールバック。

## 出力品質の基準

- `docs/_internal/dev_spec_prompt_template.md` のセクション構成に完全準拠すること。
- エッジケーステーブルを必ず含めること。
- 実データ検証セクション(マスタの列インデックス確認等)を含めること。
- 実装プロンプトはバッククォートで囲まず、行頭4スペースインデントで出力すること。
- 過去の失敗パターン(特に列ズレや Repository のキャッシュ関連)を踏まえた注意事項を記載すること。

## 重要な制約
- 必要な指示をすべて自己完結的に含め、既存のドキュメントを破壊しないように配慮してください。

調査中に判明した重要な差分(ユーザー前提との乖離):

  • PartnerRepository / PartnerDTO未実装(既存 Repository は Order/Invoice/BankTx/Journal/Account の 5 つのみ)。本仕様書では「拡張」ではなく「新設」として設計。
  • BUD_ADHOC は決済条件 5 項目のうち 3 項目(決済手段・決済ラグ・支払基準日)のみ列として存在。休日調整・CF計上 は列なし → 部分補完設計。
  • BUD_PIPE は収入系のため列名が「入金ラグ(月)」「入金日」。マスタの「標準決済ラグ(月)」「標準支払基準日」と列名マッピングが必要。