概要

項目内容
案件IDMAS-167
カテゴリ事後監査 (Post-audit)
PhaseP2
優先度★★(Top 15 昇格済)
所要時間1-2時間
実装ステータス📝 仕様書段階・実装未着手 (2026-04-28 監査時点)
対象ファイル100_config/101_sys_config.js(既存 cleanupDuplicateRows 拡張または新関数追加)、templates/operations_sidebar.html(ボタン追加)
前提案件なし(独立実装可)
実装日未実装

目的

33_wrk_bank タブの STL 重複行を定期スキャンし、Action A の多重実行等によるデータ不整合を事後検知・修復可能にする。既存の cleanupDuplicateRows は ID 単純一致による削除のみだが、本案件では「同一 INV_ID に対して複数の未処理 STL が存在する(Action A 多重実行パターン)」「決済金額が同額かつ状態が同じ重複行」など STL 固有の重複パターンを追加検出し、安全に削除できるツールを提供する。

現在のコード

既存の重複行削除ロジック(100_config/101_sys_config.js L25-84)

// 100_config/101_sys_config.js L22-84
function cleanupDuplicateRows() {
  var targets = [
    { key: 'WRK_ORDR', fallback: '31_wrk_order', idHeader: '発注ID(ORD)' },
    { key: 'WRK_INVC', fallback: '32_wrk_invoice', idHeader: '請求ID(INV)' },
    { key: 'WRK_BANK', fallback: '33_wrk_bank', idHeader: '決済ID(STL)' }
  ];
  // ...
  // 同一 ID の 2 行目以降を赤ハイライトしてアラート → 手動削除
  if (totalDeleted === 0) {
    ui.alert('✅', '重複行はありませんでした。', ui.ButtonSet.OK);
  } else {
    ui.alert('⚠️ 重複行を赤色でマークしました。確認後、手動で削除してください。\n\n' + details.join('\n'));
  }
}

現在の問題点:

  1. STL_ID が異なっていても「同一 INV_ID に複数の未処理 STL」が作られる Action A 多重実行パターンを検出できない
  2. 赤ハイライトのみで、実際の削除は手動(ユーザー負担)
  3. 31/32/33 全タブを対象とするため、33 タブ固有のパターン分析が薄い

既存の整合性チェック(100_config/101_sys_config.js L91-220)

checkInvStlConsistencyINV↔STL 整合性チェックを実装済み。しかし重複行の検出・削除は対象外。

操作パネルのボタン(templates/operations_sidebar.html L74)

<button class="btn warn" onclick="run('cleanupDuplicateRows', this)">🧹 重複行削除</button>

修正方針

Step 1: 33タブ専用 STL 重複スキャン関数の追加

100_config/101_sys_config.jscleanupDuplicateRows 直後(L85付近)に新関数 scanStlDuplicates_ を追加する。

検出すべき重複パターン:

パターン検出条件優先保持行
A: STL_ID 完全一致同一 決済ID(STL) が 2 行以上最初の行
B: 同一INV の未処理STL重複同一 消込対象請求ID(INV) かつ 決済ステータス=未処理 が 2 件以上最初の行(作成日昇順)
C: 金額同額の消込済STL重複同一 消込対象請求ID(INV) かつ 決済ステータス=消込済 かつ 税込金額_決済 が同額自動仕訳JNL_ID が空でない行(空なら最初)

Step 2: Human-in-the-Loop 対応の確認ダイアログ

プロダクトポリシーに従い、削除前に確認ダイアログを表示する。

【STL重複行の検出結果】
パターンA (STL_ID重複): 0件
パターンB (同一INV未処理重複): 2件
  - STL_20250401_0002 (INV_20250401_0001)
  - STL_20250401_0003 (INV_20250401_0001)
パターンC (消込済金額重複): 0件

削除予定: 2件(赤ハイライト行)
保持:     残行

削除してよいですか?
[削除実行] [キャンセル]

Step 3: 実際の削除処理

確認後、下から上の順(行番号降順)に sheet.deleteRow(rowNum) を実行する。

削除前の監査ログ記録:

Utils.auditLog('DELETE', '33_wrk_bank', stlId, '', 'cleanupStlDuplicates',
  { reason: patternType, duplicateOf: keepStlId },
  null,
  'I-25 STL重複削除');

Step 4: 操作パネルへの追加

templates/operations_sidebar.html L75 付近に新ボタンを追加する:

<button class="btn warn" onclick="run('cleanupStlDuplicates', this)">🔍 STL重複検出・削除</button>

Step 5: 公開関数として定義

/**
 * I-25: 33_wrk_bank の STL 重複行を検出・削除するツール
 * Human-in-the-Loop: 削除前に確認ダイアログを表示
 */
function cleanupStlDuplicates() { ... }

影響範囲

対象変更内容変更量
100_config/101_sys_config.jscleanupStlDuplicates 関数追加(L85付近に挿入)約 80-100 行追加
templates/operations_sidebar.htmlSTL重複スキャンボタン追加(L74-75 付近)1 行追加
000_infra/002_constants.jsMENU_DEFINITION への項目追加(任意・MAS-214 メニューカタログ連携)1 エントリ追加

既存の cleanupDuplicateRows は変更しない。新関数を追加することで後方互換を維持する。

注意事項

  1. 行番号降順での削除: 行削除は上から行うと後続行のインデックスがずれる。deleteRows は必ず行番号の降順(大→小)で実行すること
  2. 消込済STLの慎重な扱い: パターンC(消込済金額重複)は 自動仕訳JNL_ID が埋まっている行が存在する場合、その行を必ず保持すること。JNL_ID のある行を削除すると TRN との整合性が崩れる
  3. 有効フラグ=FALSE の行はスキップ: 論理削除済みの行は重複判定から除外する
  4. STL_ID が空の行はスキップ: ヘッダー行や不完全行を誤検出しないよう、決済ID(STL) が空の行は処理対象外
  5. 監査ログ必須: 削除前に Utils.auditLog('DELETE', ...) を記録すること(MAS-179 対応)
  6. dev 環境で先行テスト: npm run push:dev でデプロイ後、開発用スプレッドシートで実行確認してから prod に適用する

エッジケース

条件動作理由
33_wrk_bank にデータが 1 行以下(ヘッダーのみ)"重複行はありませんでした" アラートで終了スキャン不要
STL_ID が空の行スキップ(重複判定対象外)不完全行を誤削除しない
有効フラグ=FALSE の行スキップ論理削除済みは対象外
パターンB: 同一INV未処理STLが 3 件以上最初の 1 件のみ保持、残り全件を削除対象にAction A の多重実行を完全除去
パターンC: 消込済STLで両行に JNL_ID がある両行を削除候補として表示するが、ユーザー確認ダイアログで「キャンセル」を推奨手動判断が必要なケース
パターンC: 消込済STLで片方に JNL_ID があるJNL_ID のある行を保持、もう一方を削除対象TRN 整合性を最優先
重複が 0 件の場合"STL の重複行はありませんでした" アラートで終了正常系
ユーザーが確認ダイアログで「キャンセル」削除を中断、ハイライトのみ残すHuman-in-the-Loop ポリシー準拠
33_wrk_bank シートが見つからないエラーアラートを表示して終了Utils.getSheetByKey でシートキーを解決できない場合

実データ検証

実装前に開発用スプレッドシートで以下を確認すること:

確認項目手順
33_wrk_bank のヘッダー行(列名)決済ID(STL), 消込対象請求ID(INV), 決済ステータス, 税込金額_決済, 自動仕訳JNL_ID, 有効フラグ が存在するか確認
パターンB の再現dev 環境で Action A を同一 INV に対して 2 回実行し、STL が重複生成されるか確認
削除後の TRN 整合性checkInvStlConsistency で孤立 TRN が発生しないことを確認
監査ログ記録削除実行後、98_audit_log タブに DELETE ログが記録されていること

関連ドキュメント

ドキュメント関連箇所
E.6.11 MAS-166 立替精算STLの振込先名データ改善STL 生成ロジック(Action A)の参考
E.1.8 MAS-179 監査証跡の強化削除時の Utils.auditLog の使い方
E.2.17 MAS-122 ORD↔INV 消化状況の整合性チェック整合性チェック実装のパターン参考
仕様書: 4.4.1 Action A / Action BSTL 自動生成の詳細仕様

人間が検討すべき事項

  1. 削除方式: 物理削除(sheet.deleteRow)か論理削除(有効フラグを FALSE に更新)か。物理削除はシートをすっきりさせるが、証跡が残らない。監査ログに記録するならどちらでも可だが、誤削除リスクを考慮すると「有効フラグ=FALSE + 赤ハイライト → 別途物理削除」の 2 段階が安全
  2. パターンC の自動削除可否: 消込済STLが重複する場合(Action B 多重実行)、JNL_ID の有無だけで保持/削除を自動判断してよいか。TRN 側も重複していれば手動対応が必要
  3. 定期実行トリガーの要否: TODO_future.md では「定期スキャン」と記載あり。時間トリガーで週次実行し、重複検出時にメール通知する仕様にするかどうか(初期実装はメニュー手動実行で十分)
  4. 31/32 タブへの波及: STL 固有ツールとして 33 タブ専用にするか、既存の cleanupDuplicateRows を拡張して全タブ対応にするか

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

あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者です。
案件 MAS-167「33タブSTL重複行の検出・削除ツール」を実装してください。

## 実行前タスク

以下のファイルを Read して内容を把握してから実装を開始すること:
- `100_config/101_sys_config.js` L22-84: 既存の `cleanupDuplicateRows` 関数(挿入位置・スタイル確認)
- `100_config/101_sys_config.js` L86-220: `checkInvStlConsistency` 関数(パターン)
- `templates/operations_sidebar.html` L70-80: ボタン追加位置の確認
- `000_infra/003_contracts.js` L69-89: `BankTxDTO` の列名一覧(ヘッダー名の正確な確認)
- `000_infra/004_utils.js`: `Utils.auditLog` の引数仕様の確認

## 修正対象ファイル

1. `100_config/101_sys_config.js` に `cleanupStlDuplicates` 関数を追加(L85 付近、`cleanupDuplicateRows` 直後)
2. `templates/operations_sidebar.html` に STL重複スキャンボタンを追加(L74 の「重複行削除」ボタンの直後)

## 実装内容

### 1. `cleanupStlDuplicates` 関数 (`100_config/101_sys_config.js`)

関数の概要:
- `Utils.getSheetByKey('WRK_BANK', '33_wrk_bank')` でシートを取得
- `sheet.getDataRange().getValues()` でデータを一括取得
- `buildHeaderIndex_` と同等のヘッダーMap構築(`data[0].indexOf(列名)` で各列インデックスを取得)
- 有効フラグ=FALSE の行・STL_ID が空の行はスキップ
- 以下の 3 パターンで重複を検出:
  - パターンA: 同一 `決済ID(STL)` が 2 行以上
  - パターンB: 同一 `消込対象請求ID(INV)` かつ `決済ステータス='未処理'` が 2 件以上
  - パターンC: 同一 `消込対象請求ID(INV)` かつ `決済ステータス='消込済'` かつ `税込金額_決済` が同額
- 削除対象行を赤ハイライト(`#F4CCCC`)でマーク
- 確認ダイアログ(`ui.alert` + `ui.ButtonSet.YES_NO`)で一覧を表示
- ユーザーが「はい」を選択した場合のみ物理削除を実行(行番号降順で `sheet.deleteRow`)
- 削除前に各行の `Utils.auditLog('DELETE', '33_wrk_bank', stlId, '', 'cleanupStlDuplicates', ...)` を記録

パターンC の保持ルール:
- `自動仕訳JNL_ID` が空でない行を保持し、空の行を削除対象にする
- 両行とも JNL_ID が埋まっている場合は削除対象をマークするが、ダイアログで「手動確認推奨」を表示

### 2. `templates/operations_sidebar.html` ボタン追加

L74 の既存ボタン直後に以下を追加:
```html
<button class="btn warn" onclick="run('cleanupStlDuplicates', this)">🔍 STL重複スキャン</button>
```

## 制約

- `cleanupDuplicateRows` 関数(L25-84)は変更しないこと
- 列番号のハードコード禁止。必ず `data[0].indexOf('列名')` でヘッダーベースの参照を使う
- 削除は `sheet.deleteRow(行番号)` を行番号降順で実行すること(昇順だとインデックスがずれる)
- `SpreadsheetApp.getUi()` は関数の先頭で一度だけ取得すること

## エッジケース

| 条件 | 動作 |
|------|------|
| データが 1 行以下(ヘッダーのみ) | "重複行はありませんでした" で終了 |
| 重複が 0 件 | "STL の重複行はありませんでした" アラートで終了 |
| ユーザーがキャンセル | 削除中断、赤ハイライトのみ残す |
| パターンC で両行 JNL_ID あり | 削除候補をマークしダイアログに「要手動確認」を表示 |
| シートが見つからない | エラーアラートを表示して return |

## 実データ検証

1. dev 環境で同一 INV_ID に対して Action A(processInvoiceApprovals)を 2 回実行し、33 タブに STL が 2 件生成されることを確認
2. `cleanupStlDuplicates` を実行し、2 件目の STL が赤ハイライトされることを確認
3. 確認ダイアログで「はい」を選択し、2 件目が物理削除されることを確認
4. 98_audit_log タブに DELETE ログが記録されていることを確認
5. `checkInvStlConsistency` を実行し、整合性エラーが出ないことを確認

## 動作確認

1. `npm run push:dev` でデプロイ
2. 開発用スプレッドシートの 33_wrk_bank タブで Action A を同一 INV に対して 2 回実行
3. 操作パネル → 「🔍 STL重複スキャン」ボタンをクリック
4. 確認ダイアログに重複内容が表示されることを確認
5. 「はい」を選択して削除実行
6. 33_wrk_bank タブで重複行が削除されていることを確認
7. 98_audit_log タブで DELETE ログが記録されていることを確認
8. `checkInvStlConsistency` で整合性エラーが出ないことを確認

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

| フェーズ | 拡張思考 | 備考 |
|----------|---------|------|
| Phase 1(設計・調査) | あり | ファイル構造・パターン設計の確定 |
| Phase 2(清書・実装) | なし | 確定済み内容の書き下しに徹する |

推奨実行モデル

工程推奨モデル理由
実装全体Claude Sonnet 4.6挿入位置の特定・既存パターン(cleanupDuplicateRows)の踏襲が必要。ロジックは中程度の複雑さ

変更履歴

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

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

展開して表示

あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者兼仕様書ライターです。

作業ディレクトリ: /workspaces/bizlp-gas-accounting 現在のブランチ: docs/add-spec-i-25 (main から分岐済・クリーン状態)

指示書 /workspaces/bizlp-gas-accounting/tasks/prompts/task_I-25.md を Read して、その内容を厳密に実行してください。

(注: task_I-25.md が存在しなかったため、docs/_internal/TODO_future.md の MAS-167 エントリ(「33タブSTL重複行の検出・削除ツール」)から案件定義を読み取り、関連コードを調査した上で仕様書を作成した。)

調査した主なファイル

  • docs/_internal/TODO_future.md — MAS-167 案件定義の確認
  • 100_config/101_sys_config.js L22-220 — cleanupDuplicateRows, checkInvStlConsistency の実装確認
  • 400_domain/410_subledger_engine.js L1-706 — Action A/B の STL 生成ロジック(重複原因の理解)
  • 000_infra/003_contracts.js L69-89 — BankTxDTO の列名一覧
  • templates/operations_sidebar.html L70-80 — ボタン追加位置の確認
  • docs/_internal/dev_spec_prompt_template.md — 仕様書構成・フォーマットの確認

実行スコープ

  • Phase 1(Read による関連ファイル調査): 完全実行
  • Phase 2(仕様書の分割作成 Step 2-1〜2-4): 完全実行
  • Phase 3: Step 1(docs/_config.json 追記)と Step 2(docs/_internal/changelog.md 追記)のみ実行
  • Phase 3 の git commit / push / PR 作成は実行しないこと。呼び出し元が処理します。