MAS-146: カード明細の高信頼度自動消込
概要
| 項目 | 内容 |
|---|---|
| 案件ID | MAS-146 |
| カテゴリ | 外部データ取込 |
| Phase | P1.5 |
| 優先度 | ★★★ |
| 所要時間 | 2時間 |
| 対象ファイル | 500_import/501_cc_importer.js (修正), 100_config/101_sys_config.js (メニュー追加) |
| 前提案件 | なし(既存コードの小改修) |
目的
既存のクレカ明細マッチング(importCcStatement)に「自動承認モード」を追加し、金額完全一致+名寄せ辞書ヒットの高信頼度マッチについては確認FLGを自動でTRUEに設定する。これにより、CC消込の目視確認を約95%削減し、月次決算のリードタイムを短縮する。
現行の2段階処理(マッチング確認→消込実行)のアーキテクチャは変更せず、Step 1 のマッチング確認時に「高信頼度」と判定された行のみ確認FLGを自動でTRUEにする。ユーザーは自動承認された行をざっと確認し、問題なければそのまま消込実行できる。
現在のコード
マッチング判定ロジック(501_cc_importer.js L226-253)
// --- Pass 1: クレカ AND 日付一致 AND 金額±20% AND 名前やや一致 ---
var bestIdx = -1;
var bestDiffRate = Infinity;
for (var s = 0; s < candidates.length; s++) {
if (candidates[s].matched) continue;
if (!candidates[s].isCC) continue;
if (cardPayDate !== candidates[s].dueDate) continue;
var stlA = candidates[s].amount;
if (stlA === 0) continue;
var dr1 = Math.abs(cardPayAmt - stlA) / Math.abs(stlA);
var dr2 = cardUseAmt ? Math.abs(cardUseAmt - stlA) / Math.abs(stlA) : Infinity;
var bestRate = Math.min(dr1, dr2);
if (bestRate > 0.20) continue;
// 辞書で取引先が特定できている場合は、その取引先のSTLのみマッチ (誤マッチ防止)
var nameOk;
if (resolvedVendor) {
nameOk = (candidates[s].vendor === resolvedVendor) ||
(candidates[s].memo && candidates[s].memo.indexOf(resolvedVendor) !== -1);
} else {
nameOk = isNameFuzzyMatch_(cardMerchantNorm, '',
candidates[s].vendor, candidates[s].memo);
}
if (!nameOk) continue;
if (bestRate < bestDiffRate) { bestDiffRate = bestRate; bestIdx = s; }
}
マッチ結果書き込み(501_cc_importer.js L255-269)
if (bestIdx >= 0) {
var m = candidates[bestIdx];
m.matched = true;
var hasDiff = (cardPayAmt !== m.amount);
if (hasDiff) amountFixCount++;
// card に結果書き込み (消込はしない)
cardSheet.getRange(rowNum, ext.iConfirm + 1).setValue(false); // ← 常にfalse
cardSheet.getRange(rowNum, ext.iResult + 1).setValue('MATCHED');
// ... STL情報の書き込み
}
確認FLGの現在の動作
- Step 1(
importCcStatement): マッチした行の確認FLGを常にfalseに設定(L262) - Step 2(
applyCcSettlement): 確認FLG=TRUE かつ 処理結果=MATCHED の行のみ消込実行 - ユーザーが手動で確認FLGにチェックを入れる運用
メニュー構成(101_sys_config.js L315-322)
ui.createMenu('🔍 消込・マッチング')
.addItem('💳 クレカ明細マッチング確認', 'importCcStatement')
.addItem('💳 クレカ消込実行 (確認FLG=TRUE)', 'applyCcSettlement')
// ...
修正方針
Step 1: 高信頼度判定ロジックの追加(501_cc_importer.js)
マッチング成功時に「高信頼度(auto-approvable)」かどうかを判定し、高信頼度の場合は確認FLGを自動でTRUEに設定する。
高信頼度の条件(AND条件)
- 金額完全一致:
cardPayAmt === m.amount(差額ゼロ) - 辞書ヒット:
resolvedVendorが空でない(resolveMerchant_()でCC_MERCHANT_MAPにヒットした) - 日付一致: Pass 1 の既存条件で保証済み(
cardPayDate === candidates[s].dueDate)
閾値パラメータの 03_sys_params 管理
将来の条件拡張(日付±N日許容等)に備え、自動承認の有効/無効を 03_sys_params で制御可能にする。
| パラメータキー | デフォルト値 | 説明 |
|---|---|---|
CC_AUTO_APPROVE | TRUE | 自動承認モードの有効/無効 |
コード変更箇所
501_cc_importer.js の importCcStatement() 内、L255-269 付近:
if (bestIdx >= 0) {
var m = candidates[bestIdx];
m.matched = true;
var hasDiff = (cardPayAmt !== m.amount);
if (hasDiff) amountFixCount++;
// 高信頼度判定: 金額完全一致 AND 辞書ヒット
var isHighConfidence = !hasDiff && !!resolvedVendor;
var autoApprove = Constants.getParam('CC_AUTO_APPROVE', 'TRUE') === 'TRUE';
var shouldAutoApprove = autoApprove && isHighConfidence;
// card に結果書き込み
cardSheet.getRange(rowNum, ext.iConfirm + 1).setValue(shouldAutoApprove);
cardSheet.getRange(rowNum, ext.iResult + 1).setValue(
shouldAutoApprove ? 'AUTO_MATCHED' : 'MATCHED'
);
cardSheet.getRange(rowNum, ext.iStlId + 1).setValue(m.stlId);
cardSheet.getRange(rowNum, ext.iStlDue + 1).setValue(m.dueDate);
cardSheet.getRange(rowNum, ext.iStlAmt + 1).setValue(m.amount);
cardSheet.getRange(rowNum, ext.iStlVendor + 1).setValue(m.vendor);
cardSheet.getRange(rowNum, ext.iStlMemo + 1).setValue(m.memo);
// 背景色: 自動承認=薄緑、手動確認待ち=白
cardSheet.getRange(rowNum, 1, 1, cardMaxCol).setBackground(
shouldAutoApprove ? '#D9EAD3' : '#FFFFFF'
);
matchCount++;
if (shouldAutoApprove) autoApproveCount++;
}
Step 2: 結果ダイアログの拡張(501_cc_importer.js)
自動承認件数をダイアログに表示する。
L189 付近に変数追加:
var matchCount = 0, amountFixCount = 0, unmatchCount = 0, skipCount = 0;
var autoApproveCount = 0; // ← 追加
L304-308 のダイアログメッセージ変更:
var msg = '✅ マッチ: ' + matchCount + '件' +
(autoApproveCount > 0 ? ' (自動承認: ' + autoApproveCount + '件)' : '') +
(amountFixCount > 0 ? ' (金額差あり: ' + amountFixCount + '件)' : '') +
'\n⚠️ アンマッチ: ' + unmatchCount + '件' +
'\n⏭️ スキップ: ' + skipCount + '件' +
'\n\n自動承認済みの行は薄緑で表示されています。' +
'\n確認後、「💳 クレカ消込実行」を実行してください。';
Step 3: applyCcSettlement() の対応
applyCcSettlement() は確認FLG=TRUE の行を処理するため、コード変更は不要。AUTO_MATCHED の処理結果も MATCHED と同様に STL_ で始まるSTL_IDを持つため、既存の判定ロジック(L370)がそのまま動作する。
ただし、消込実行後の処理結果値は既存の '消込済' をそのまま使用する(AUTO_MATCHED → 消込済 に上書きされる)。これは importCcStatement() のスキップ判定(L200-201)が '消込済' を見ているため、整合性が保たれる。
Step 4: メニュー変更は不要
既存のメニュー構成(マッチング確認→消込実行)はそのまま維持。自動承認は importCcStatement 内で透過的に動作するため、ユーザーの操作フローに変更はない。
影響範囲
| ファイル | 変更内容 | 変更量 |
|---|---|---|
500_import/501_cc_importer.js | importCcStatement() に高信頼度判定+自動承認ロジック追加 | 約15行追加・5行変更 |
100_config/101_sys_config.js | 変更なし | 0行 |
既存動作への影響
applyCcSettlement(): 影響なし。確認FLG=TRUE の行を処理する既存ロジックがそのまま動作importReceiptStatement()/applyReceiptSettlement(): 影響なし。別関数で独立CC_AUTO_APPROVEパラメータ未設定時: デフォルトTRUEで自動承認が有効。FALSEに設定すれば従来どおり全件手動確認- スキップ判定:
AUTO_MATCHEDは'消込済'でも'SKIP'でもないため、再実行時に再マッチング対象になる。ただし確認FLG=TRUE のため L196-197 でスキップされる
注意事項
- 確認FLG=TRUE でも消込前にユーザーが確認可能: 自動承認はあくまで確認FLGのプリセット。ユーザーは消込実行前にシート上で結果を確認し、誤マッチがあれば確認FLGを手動でFALSEに戻せる
- 辞書未登録の取引先は自動承認されない:
CC_MERCHANT_MAPにエントリがない取引先はresolvedVendorが空になるため、金額が完全一致でも手動確認が必要 - 金額差がある場合は自動承認されない: 手数料・為替差額等で金額差がある場合は安全側に倒して手動確認
AUTO_MATCHEDとMATCHEDの区別: 処理結果列で自動承認か手動確認かを区別可能。消込実行後は両方とも'消込済'に上書きされる- 既存の
importCcStatementスキップ条件との整合性: 確認FLG=TRUE の行は L196-197 でスキップされるため、再実行しても自動承認済みの行は再処理されない
エッジケース
| 条件 | 動作 | 理由 |
|---|---|---|
03_sys_params に CC_AUTO_APPROVE 未登録 | 自動承認有効(デフォルト TRUE) | Constants.getParam のデフォルト値機構で対応 |
CC_AUTO_APPROVE = FALSE | 全件手動確認(従来動作) | 自動承認を無効化。処理結果は全て MATCHED |
| 金額完全一致だが辞書未ヒット | 手動確認(MATCHED) | 辞書ヒットなしは取引先同定の信頼度が不十分 |
| 辞書ヒットだが金額差あり | 手動確認(MATCHED) | 金額差は手数料・為替・別取引の可能性がある |
| 同一STLに複数のcard明細がマッチ | 各card行で独立に高信頼度判定 | applyCcSettlement のPass 1で合算処理されるため問題なし |
| 取消明細(訂正サイン=取消) | SKIP:取消 で除外 | 既存ロジック(L204-209)がそのまま動作 |
| 自動承認後にユーザーが確認FLGをFALSEに戻す | 消込対象から除外 | applyCcSettlement の確認FLG=TRUE チェック(L367)で制御 |
実データ検証(MCP でのデータ確認が必要な場合)
| 確認項目 | 確認方法 | 目的 |
|---|---|---|
CC_MERCHANT_MAP のカバレッジ | 002_constants.js の辞書エントリ数と、34_wrk_card の過去明細の取引先名の突合 | 辞書ヒット率(=自動承認率)の見積もり |
03_sys_params の既存パラメータ | MCP で 03_sys_params タブの内容を確認 | CC_AUTO_APPROVE キーの追加位置・重複確認 |
| 過去マッチング実績 | 34_wrk_card の処理結果列で MATCHED の行のうち、金額差ありの件数 | 金額完全一致の割合を確認(自動承認率の推定) |
関連ドキュメント
| 仕様書 | 関連箇所 |
|---|---|
| spec_cc_import.md | クレカ明細マッチング仕様の全体像。名寄せ辞書・2段階処理の設計根拠 |
| dev_mas-145_bank_csv_import.md | 銀行CSV取込。同じ「ステージング+確認FLG」パターンの横展開先 |
| prd.md | Human-in-the-Loop ポリシー。自動承認でもユーザーが最終確認可能な設計 |
人間が検討すべき事項
- 自動消込の閾値拡張: 現設計は「金額完全一致+辞書ヒット」の厳格な条件。将来的に「日付±N日も許容」を条件に含めるかは、誤消込のリスクとのトレードオフで判断が必要
- 誤消込時のロールバック手順: 自動承認→消込実行後に誤りが発覚した場合の修正手順。現状は33タブの決済ステータスを手動で「未処理」に戻し、34タブの処理結果を手動クリアする必要がある。専用のロールバック機能の要否を検討
CC_MERCHANT_MAPの拡充: 自動承認率は辞書のカバレッジに直結する。新しいクレカ利用先が出るたびに辞書エントリを追加する運用フローの整備
実装プロンプト(Claude Code 用)
あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者です。
案件 MAS-146「カード明細の高信頼度自動消込」を実装してください。
## 実行前タスク
- `500_import/501_cc_importer.js` を読み込み、`importCcStatement()` のマッチング結果書き込み部分(L255-269)と結果ダイアログ(L304-308)を確認
- `000_infra/002_constants.js` を読み込み、`Constants.getParam()` の使い方と `CC_MERCHANT_MAP` の構造を確認
- `100_config/101_sys_config.js` のメニュー構成を確認(変更不要だが参照用)
- `CLAUDE.md` のコーディング規約(列参照はヘッダー名ベース、有効フラグ=FALSE スキップ等)を確認
## 修正対象ファイル
- `500_import/501_cc_importer.js` のみ
## 実装内容
### 1. `importCcStatement()` の変数初期化部に `autoApproveCount` を追加(L189付近)
`var matchCount = 0, amountFixCount = 0, unmatchCount = 0, skipCount = 0;` の次の行に:
```js
var autoApproveCount = 0;
```
### 2. マッチ結果書き込み部を変更(L255-269付近)
`if (bestIdx >= 0) {` ブロック内の確認FLG書き込み部分を以下に変更:
既存:
```js
cardSheet.getRange(rowNum, ext.iConfirm + 1).setValue(false);
cardSheet.getRange(rowNum, ext.iResult + 1).setValue('MATCHED');
```
変更後:
```js
// 高信頼度判定: 金額完全一致 AND 辞書ヒット
var isHighConfidence = !hasDiff && !!resolvedVendor;
var autoApprove = Constants.getParam('CC_AUTO_APPROVE', 'TRUE') === 'TRUE';
var shouldAutoApprove = autoApprove && isHighConfidence;
cardSheet.getRange(rowNum, ext.iConfirm + 1).setValue(shouldAutoApprove);
cardSheet.getRange(rowNum, ext.iResult + 1).setValue(
shouldAutoApprove ? 'AUTO_MATCHED' : 'MATCHED'
);
```
### 3. 背景色の分岐(L269付近)
既存:
```js
cardSheet.getRange(rowNum, 1, 1, cardMaxCol).setBackground('#FFFFFF');
```
変更後:
```js
cardSheet.getRange(rowNum, 1, 1, cardMaxCol).setBackground(
shouldAutoApprove ? '#D9EAD3' : '#FFFFFF'
);
if (shouldAutoApprove) autoApproveCount++;
```
### 4. 結果ダイアログのメッセージ変更(L304-308付近)
既存:
```js
var msg = '✅ マッチ: ' + matchCount + '件' +
(amountFixCount > 0 ? ' (金額差あり: ' + amountFixCount + '件)' : '') +
'\n⚠️ アンマッチ: ' + unmatchCount + '件' +
'\n⏭️ スキップ: ' + skipCount + '件' +
'\n\n確認後、「確認FLG」にチェックを入れて「💳 クレカ消込実行」を実行してください。';
```
変更後:
```js
var msg = '✅ マッチ: ' + matchCount + '件' +
(autoApproveCount > 0 ? ' (自動承認: ' + autoApproveCount + '件)' : '') +
(amountFixCount > 0 ? ' (金額差あり: ' + amountFixCount + '件)' : '') +
'\n⚠️ アンマッチ: ' + unmatchCount + '件' +
'\n⏭️ スキップ: ' + skipCount + '件' +
'\n\n自動承認済みの行は薄緑で表示されています。' +
'\n確認後、「💳 クレカ消込実行」を実行してください。';
```
## 制約
- `applyCcSettlement()` は変更しない(確認FLG=TRUE の行を処理する既存ロジックがそのまま動作する)
- `importReceiptStatement()` / `applyReceiptSettlement()` は変更しない
- `101_sys_config.js` のメニュー構成は変更しない
- `CC_MERCHANT_MAP` の辞書エントリは変更しない
- `matchCount++` の位置を変えない(autoApproveCount は matchCount と別にカウント)
## エッジケース
| 条件 | 動作 | 理由 |
|------|------|------|
| `CC_AUTO_APPROVE` 未設定 | 自動承認有効 | `getParam` デフォルト値 `'TRUE'` |
| `CC_AUTO_APPROVE = FALSE` | 全件手動確認 | 従来どおり確認FLG=false |
| 金額完全一致 + 辞書未ヒット | 手動確認 | `resolvedVendor` 空 → `isHighConfidence = false` |
| 辞書ヒット + 金額差あり | 手動確認 | `hasDiff = true` → `isHighConfidence = false` |
## 実データ検証
- `03_sys_params` タブに `CC_AUTO_APPROVE` キーが存在しないことを確認(未設定=デフォルト TRUE で動作することの確認)
- `34_wrk_card` タブに既存データがある場合、テスト前にバックアップを推奨
## 動作確認
1. `npm run push:dev` で開発環境にデプロイ
2. GASエディタで `34_wrk_card` タブにテスト用のクレカ明細を準備(`CC_MERCHANT_MAP` に登録済みの取引先で金額完全一致のパターン)
3. メニュー「🔍 消込・マッチング」→「💳 クレカ明細マッチング確認」を実行
4. 確認ポイント:
- 辞書ヒット+金額一致の行: 確認FLG=TRUE、処理結果=`AUTO_MATCHED`、背景色=薄緑
- 辞書未ヒット or 金額差あり: 確認FLG=FALSE、処理結果=`MATCHED`、背景色=白
- ダイアログに自動承認件数が表示される
5. 「💳 クレカ消込実行」を実行し、AUTO_MATCHED の行が正常に消込されることを確認
6. 消込後、`34_wrk_card` の処理結果が `消込済` に更新されていることを確認
### 拡張思考の使用状況
| フェーズ | 拡張思考 | 備考 |
|---------|---------|------|
| コード読み込み | なし | 対象箇所は明確に特定済み |
| 実装 | なし | 変更は局所的で判断要素が少ない |
| 動作確認 | なし | 手順が明確 |
推奨実行モデル
| 工程 | 推奨モデル | 理由 |
|---|---|---|
| 仕様書の理解 | — | 仕様書が自己完結的 |
| 実装 | Claude Haiku 4.5 | 変更箇所が完全定義済み。importCcStatement() 内の局所的な変更のみで判断要素がない |
| 動作確認 | 人間 | GAS上でのUI操作が必要 |
変更履歴
| 日付 | 変更内容 |
|---|---|
| 2026-04-16 | 初版作成。既存importCcStatementへの高信頼度自動承認ロジック追加の設計 |