概要

項目内容
案件IDMAS-080
カテゴリパイプライン
PhaseP1
優先度★★★
所要時間30分
対象ファイル300_ui/301_ui_assist.js

目的

受注前の案件にも管理ID(PIP_NNNN)を自動採番し、案件トラッキングを強化する。現状、管理IDはパイプラインRPAgeneratePipelineInvoices)実行時に初めて採番されるため、受注確定前の案件にはIDがなく、他タブからの参照やトレーサビリティが確保できない。

発見事項: generateNextIdForAssist_() の未定義

調査の結果、301_ui_assist.js で7箇所から呼ばれている generateNextIdForAssist_() 関数がどのファイルにも定義されていないことが判明した。

呼び出し箇所プレフィックス
2816_wrk_masterREQ_
5770_bud_resourceRSC_
6323_bud_subscriptionSUB_
10031_wrk_orderORD_
12932_wrk_invoiceINV_
21533_wrk_bank (INV入力)STL_
22433_wrk_bank (金額入力)STL_

これらの呼び出しは全て onEdit トリガー経由で、現在ランタイムエラー(ReferenceError)が発生する状態にある。MAS-080の実装として、まずこの関数を定義し、その上で21タブの採番を追加する。

現在のコード

21タブの onEdit 処理(301_ui_assist.js L243-248)

} else if (sName.includes('21_bud_pipeline')) {
  if (colName === '契約形態') {
    // 継続月数/MRRの表示制御のみ。ID採番なし
  }
}

パイプラインRPAでの既存採番ロジック(401_bat_rpa.js L1831-1882)

// 最大番号をスキャン
var mgrIdMax = 0;
for (var m = 1; m < pipeData.length; m++) {
  var mid = String(pipeData[m][idxMgrId]).trim();
  if (mid.startsWith('PIP_')) {
    var num = parseInt(mid.replace('PIP_', ''), 10);
    if (!isNaN(num) && num > mgrIdMax) mgrIdMax = num;
  }
}
// 空なら採番
if (!pipeMgrId) {
  mgrIdMax++;
  pipeMgrId = 'PIP_' + String(mgrIdMax).padStart(4, '0');
}

ID_PREFIX_MAP の21タブ定義(002_constants.js)

{ pattern: '21_bud_pipeline', prefix: 'PIP_', digit: 4, isDate: false }

SHEET_DEFAULTS の21タブ初期値(002_constants.js)

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

修正方針

Step 1: generateNextIdForAssist_() の実装

301_ui_assist.js の末尾(またはヘルパー関数セクション)に追加。既存の7箇所の呼び出しを全て解決する汎用関数。

/**
 * 指定シートのID列をスキャンし、次の管理IDを生成する
 * @param {GoogleAppsScript.Spreadsheet.Sheet} sheet
 * @param {string} prefix - IDプレフィックス ('PIP_', 'ORD_' 等)
 * @param {number} idCol - ID列の1始まり列番号
 * @param {number} digit - 連番の桁数 (通常4)
 * @param {boolean} [isDate=false] - true なら 'PREFIX_YYYYMMDD_NNNN' 形式
 * @returns {string} 次のID
 */
function generateNextIdForAssist_(sheet, prefix, idCol, digit, isDate) {
  var data = sheet.getRange(2, idCol, sheet.getLastRow() - 1, 1).getValues();
  var maxNum = 0;
  var today = isDate ? Utilities.formatDate(new Date(), Session.getScriptTimeZone(), 'yyyyMMdd') : '';
  var searchPrefix = isDate ? prefix + today + '_' : prefix;

  for (var i = 0; i < data.length; i++) {
    var id = String(data[i][0]).trim();
    if (id.startsWith(searchPrefix)) {
      var numPart = id.substring(searchPrefix.length);
      var num = parseInt(numPart, 10);
      if (!isNaN(num) && num > maxNum) maxNum = num;
    }
  }
  return searchPrefix + String(maxNum + 1).padStart(digit, '0');
}

Step 2: 21タブの onEdit 採番追加

L243-248 の既存処理を拡張し、「PJ・案件名」(C列) 入力時にID採番 + 初期値セットを追加。

} else if (sName.includes('21_bud_pipeline')) {
  const idCell = sheet.getRange(row, 2); // B列: 管理ID

  // S-08: PJ・案件名(C列)入力で管理ID早期採番
  if (colName === 'PJ・案件名' && val && idCell.isBlank()) {
    idCell.setValue(generateNextIdForAssist_(sheet, 'PIP_', 2, 4));
    sheet.getRange(row, 1).setValue(true); // 有効フラグ
    // SHEET_DEFAULTS の初期値を適用
    setIfBlank('契約形態', 'スポット(狩猟)');
    setIfBlank('売上科目', '売上高');
    setIfBlank('確度(ヨミ)', 'Aヨミ (確度80%)');
    setIfBlank('継続月数', 1);
    setIfBlank('CF計上', '予算');
    setIfBlank('入金ラグ(月)', 1);
    setIfBlank('スポット売上・初期費用', 0);
    setIfBlank('継続月額(MRR)', 0);
  }

  // 既存: 契約形態変更時の表示制御
  if (colName === '契約形態') {
    // ... (既存コードそのまま)
  }
}

パイプラインRPAとの整合性

RPA(generatePipelineInvoices)は既に「管理IDが空なら採番」のロジックを持つ(L1831-1882)。MAS-080で onEdit 採番を追加すると、RPA実行時には既にIDが存在するため、RPAの採番ロジックはスキップされる。既存RPAコードの変更は不要

影響範囲

  • 変更ファイル: 301_ui_assist.js のみ
  • Step 1: generateNextIdForAssist_() 関数追加(約20行)→ 既存7箇所の呼び出しが全て動作するようになる
  • Step 2: 21タブの onEdit ハンドラ拡張(約15行)
  • 既存機能への影響: Step 1 は既存の壊れた呼び出しを修復するため、16/23/31/32/33/70タブのID自動採番が有効化される(現状は未定義エラーで無動作)

注意事項

  1. setIfBlank ヘルパーは handleUxAssist 内でローカル定義されている(L93付近)。21タブ処理からも参照可能か確認すること
  2. generateNextIdForAssist_isDate パラメータ: PIP_ は isDate: false(連番のみ)。ORD_/INV_/STL_ は isDate: true(日付+連番)
  3. sheet.getLastRow() が1(ヘッダーのみ)の場合のエッジケースに対応すること
  4. RPAの既存採番ロジック(401_bat_rpa.js L1831-1882)は変更しない。onEdit で先に採番されていればRPA側はスキップする

関連ドキュメント

仕様書関連箇所
CLAUDE.mdシートへの書き込み位置は列B(ID列)で最終行を判定

人間が検討すべき事項

なし(UX改善。即実装可)


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

Step 1: generateNextIdForAssist_() の実装

あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者です。
案件 S-08 の Step 1 として、未定義の `generateNextIdForAssist_()` 関数を実装してください。

## 実行前タスク

以下のファイルを読み込んでください:
1. `300_ui/301_ui_assist.js` — 既存の7箇所の呼び出しパターン(L28, 57, 63, 100, 129, 215, 224)。引数の形式を全て確認
2. `000_infra/002_constants.js` — `Constants.ID_PREFIX_MAP` の定義。各エントリの `prefix`, `digit`, `isDate` を確認
3. `400_domain/401_bat_rpa.js` — L1831-1882の既存ID採番ロジック(参考実装)
4. `CLAUDE.md`
5. `docs/dev/dev_mas-080_pipeline_early_id.md`

## 修正対象ファイル

`300_ui/301_ui_assist.js` に関数を追加。

## 実装内容

`handleUxAssist` 関数の**外側**(ファイル末尾のヘルパーセクション)に `generateNextIdForAssist_()` を追加する。

**要件:**
- 引数: `(sheet, prefix, idCol, digit, isDate)`
  - `sheet`: 対象シート
  - `prefix`: IDプレフィックス('PIP_', 'ORD_' 等)
  - `idCol`: ID列の1始まり列番号
  - `digit`: 連番の桁数(通常4)
  - `isDate`: true なら `PREFIX_YYYYMMDD_NNNN` 形式、false なら `PREFIX_NNNN` 形式
- ID列を全行スキャンし、該当プレフィックスの最大連番を取得
- isDate=true の場合、当日日付 `YYYYMMDD` をプレフィックスに含めてスキャン(同一日の最大値+1)
- `sheet.getLastRow()` が1以下(ヘッダーのみ)の場合は連番1を返す

**既存呼び出しとの整合性確認:**
| 呼び出し | prefix | idCol | digit | isDate |
|---------|--------|:-----:|:-----:|:------:|
| L28 (16_wrk_master) | REQ_ | 1 | 4 | true |
| L57 (70_bud_resource) | RSC_ | 2 | 4 | なし(=false) |
| L63 (23_bud_subscription) | SUB_ | 2 | 3 | なし(=false) |
| L100 (31_wrk_order) | ORD_ | 2 | 4 | true |
| L129 (32_wrk_invoice) | INV_ | 2 | 4 | true |
| L215, L224 (33_wrk_bank) | STL_ | 2 | 4 | true |

## 制約
- 既存の呼び出し箇所(7箇所)は変更しない
- 401_bat_rpa.js の採番ロジックも変更しない
- `isDate` パラメータが省略された場合は `false` として扱う

Step 2: 21タブ onEdit 採番追加

あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者です。
案件 S-08 の Step 2 として、21_bud_pipeline の onEdit でPJ・案件名入力時に管理IDを自動採番する処理を追加してください。

## 実行前タスク

以下のファイルを読み込んでください:
1. `300_ui/301_ui_assist.js` — L243-248の既存21タブ処理、他タブのID採番パターン(L98-107のORD、L127-136のINV等)
2. `000_infra/002_constants.js` — `Constants.SHEET_DEFAULTS` の21タブ初期値定義
3. Step 1 で追加した `generateNextIdForAssist_()` のシグネチャ
4. `docs/dev/dev_mas-080_pipeline_early_id.md`

## 修正対象ファイル

`300_ui/301_ui_assist.js` の L243-248(21タブ処理セクション)を拡張。

## 実装内容

既存の `if (colName === '契約形態')` ブロックの**前**に、PJ・案件名入力時のID採番ブロックを追加:

1. `colName === 'PJ・案件名'` かつ値あり かつ `idCell.isBlank()` の場合:
   - `generateNextIdForAssist_(sheet, 'PIP_', 2, 4)` でID採番
   - 有効フラグ = true
   - SHEET_DEFAULTS に定義された初期値をセット(setIfBlank使用)
2. `setIfBlank` ヘルパーが21タブ処理スコープから参照可能か確認。参照できない場合は、同スコープにidCellと同じパターンでsetIfBlankを定義

**初期値リスト(SHEET_DEFAULTS準拠):**
- 契約形態: 'スポット(狩猟)'
- 売上科目: '売上高'
- 確度(ヨミ): 'Aヨミ (確度80%)'
- 継続月数: 1
- CF計上: '予算'
- 入金ラグ(月): 1
- スポット売上・初期費用: 0
- 継続月額(MRR): 0

## 動作確認

`npm run push:dev` 後:
1. 21_bud_pipeline の C列(PJ・案件名)に新しい案件名を入力
2. B列に `PIP_0001` 等のIDが自動採番されること
3. A列(有効フラグ)が TRUE になること
4. 初期値(契約形態、売上科目等)がセットされること
5. 既にIDがある行でPJ・案件名を変更してもIDが上書きされないこと

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

| フェーズ | 拡張思考 | 備考 |
|---------|:--------:|------|
| Step 1: generateNextIdForAssist_ 実装 | あり | isDate=true/false の分岐、日付フォーマット、エッジケース(空シート、連番溢れ)の考慮 |
| Step 2: 21タブ採番追加 | なし | 既存パターン(31/32タブ)の踏襲 |

推奨実行モデル

工程推奨モデル理由
仕様書作成(本ドキュメント)Claude Opus 4.6未定義関数の発見と既存7呼び出しの整合性分析に高い推論力が必要
Step 1 実装Claude Sonnet 4.6isDate=true/false の日付フォーマット分岐、7呼び出しとの整合性確認が必要
Step 2 実装Claude Haiku 4.5既存パターン(ORD/INV)の踏襲。判断要素が少ない

変更履歴

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