領収書マッチング・立替精算消込 — 35_wrk_receipt
1. 概要
焦点質問: 領収書PDFからの自動読取と立替精算STLの消込はどう動作するか?
背景・課題
立替精算の領収書・請求書は紙やPDFで届くため、手動で金額・取引先を転記しSTLと紐づける作業が煩雑だった。Gemini APIによるPDF自動解析と33_wrk_bankの立替精算STLとの自動マッチングにより、帳票との紐づけを半自動化する。
処理フロー(マトリクス)
| 項番 | 処理フェーズ | 入力(論理名) | 入力(物理名: タブ.列) | 処理詳細 / 変換ロジック | 出力(論理名) | 出力(物理名: タブ.列) | 例外処理 |
|---|---|---|---|---|---|---|---|
| S0.1 | Step 0: PDF読み込み | Google Drive PDF | Drive フォルダ | importReceiptPdfs() — Gemini APIで解析→35タブに出力。処理済みPDFはprocessedに移動 | 領収書データ | 35_wrk_receipt (新規行) | ⚠️ PDF解析失敗→スキップ+ログ |
| S1.1 | Step 1: マッチング確認 | 領収書 × 立替STL | 35_wrk_receipt × 33_wrk_bank | importReceiptStatement() — 立替精算STLとマッチング。結果を35タブに表示(消込しない) | 処理結果, STL情報 | 35_wrk_receipt.T〜Y列 | SKIP:金額なし |
| S1.2 | ユーザー確認 | — | 35_wrk_receipt | MATCHED行: 確認FLG=TRUE。UNMATCHED行: 手動STL_ID入力 or データ修正→再実行 | 確認FLG | 35_wrk_receipt.S列 | — |
| S2.1 | Step 2: 消込実行 | 確認FLG=TRUE行 | 35_wrk_receipt.S列 | applyReceiptSettlement() — 確認FLG=TRUE行を処理 | — | — | — |
| S2.2 | 33タブ更新 | マッチSTL情報 | 35_wrk_receipt.U列 | 消込手段="帳票", 立替日=領収書の決済日_実績(決済ステータスは未処理のまま) | 消込手段, 立替日 | 33_wrk_bank.P,E列 | ※1 |
| S2.3 | 35タブ更新 | — | — | 処理結果→消込済, 背景色=#D9EAD3(薄緑) | 処理結果 | 35_wrk_receipt.T列 | — |
| S2.4 | ユーザー手動消込 | — | 33_wrk_bank | ユーザーが決済ステータスを"消込済"に変更、決済日_実績を入力 | ステータス, 決済日 | 33_wrk_bank.G,F列 | — |
| S3.1 | Step 3: Action B | — | — | 通常のAction Bで仕訳生成 | — | 42_trn_journal | — |
脚注:
- ※1: 領収書消込はクレカ消込と異なり、決済ステータスを自動で消込済にしない。立替精算は実際の立替者への振込(別のタイミング)が必要なため
2. 設計判断
| 選択肢 | メリット | デメリット | 選定 |
|---|---|---|---|
| 案A: OCR + ルールベース抽出 | 決定的・OCR 精度高 | 非定形対応にテンプレ別パーサー必要 | — |
| 案B: LLM (Gemini) で PDF 直接解析 | GAS 親和性・非定形対応・要件変更容易 | 非決定性・コスト変動・レート制限 | 採用 |
→ 詳細: ADR-0007 Gemini APIを領収書解析に使用する判断
3. 変更内容
3.1 スキーマ (35_wrk_receipt タブレイアウト)
データ列 (A〜R列)
| # | 列 | ヘッダー | 型 | 入力/自動 | 説明 | 制約 |
|---|---|---|---|---|---|---|
| 1 | A | 管理ID | 文字列 | 自動 | RCP_0001, RCP_0002, ... (自動採番) | RCP_NNNN形式 |
| 2 | B | 処理日時 | 日時 | 自動 | データ投入日時 | — |
| 3 | C | 証憑種別 | 文字列 | 自動 | 領収書 / 請求書 (Gemini が自動判定) | — |
| 4 | D | 取引先名 | 文字列 | 自動 | 発行元の会社名 | — |
| 5 | E | 🏢住所 | 文字列 | 自動 | 発行元住所 (市区町村まで) | — |
| 6 | F | 税込金額_決済 | 数値 | 自動 | 税込金額 | — |
| 7 | G | 税抜金額_決済 | 数値 | 自動 | 税抜金額 | — |
| 8 | H | 消費税額_決済 | 数値 | 自動 | 消費税 | — |
| 9 | I | 源泉税額 | 数値 | 自動 | 源泉徴収税 | — |
| 10 | J | T番号 | 文字列 | 自動 | インボイス登録番号 (Tで始まる13桁) | ^T\d{13}$ |
| 11 | K | 帳票番号 | 文字列 | 自動 | 請求書番号 / 領収書番号 / 注文番号 | — |
| 12 | L | 発行日 | 日付 | 自動 | 帳票の発行日 | YYYY-MM-DD |
| 13 | M | 発生日(P/L計上日) | 日付 | 自動 | 請求期間の終了日 (= P/L 計上日) | YYYY-MM-DD |
| 14 | N | 決済日_実績 | 日付 | 自動 | 支払日 | YYYY-MM-DD |
| 15 | O | 決済手段 | 文字列 | 自動 | クレジットカード等 | — |
| 16 | P | 摘要 | 文字列 | 自動 | 商品名・サービス名 (50字以内) | — |
| 17 | Q | ファイル名 | 文字列 | 自動 | 元PDFのファイル名 (複数ページなら (pN) 付き) | — |
| 18 | R | 証跡リンク | URL | 自動 | Google Drive の PDF リンク | — |
GAS管理列 (マッチ結果: S〜Y列)
| # | 列 | ヘッダー | 型 | 入力/自動 | 説明 |
|---|---|---|---|---|---|
| 19 | S | 確認FLG | BOOL | 入力 | ユーザーがマッチ結果を確認後にTRUEにする |
| 20 | T | 処理結果 | 文字列 | 自動 | MATCHED / UNMATCHED / SKIP:金額なし / 消込済 / 未登録 / 26タブ登録済 |
| 21 | U | マッチ決済ID(STL) | 文字列 | 自動 | マッチしたSTL_ID (UNMATCHEDの場合 候補:STL_XXXX) |
| 22 | V | STL決済日_計画 | 日付 | 自動 | マッチSTLの決済日_計画 |
| 23 | W | STL税込金額_決済 | 数値 | 自動 | マッチSTLの税込金額 |
| 24 | X | STL取引先名 | 文字列 | 自動 | マッチSTLの取引先名 |
| 25 | Y | STL摘要 | 文字列 | 自動 | マッチSTLの摘要 |
33_wrk_bank「立替日」列
33_wrk_bank の E列 (決済日の左隣) に「立替日」を追加。
| 項目 | 説明 |
|---|---|
| 立替日 | 社員がポケットマネーやカードで支払った日 (経費発生日) |
| 決済日 (C/F実績日) | 会社が社員に精算金を振り込んだ日 |
receipt消込実行時に立替日をセット。決済日は会社精算時に手動入力。
3.2 ロジック
3.2.1 領収書PDF読み込み (Gemini API) — importReceiptPdfs()
スクリプトプロパティ:
| キー | 値 |
|---|---|
GEMINI_API_KEY | Gemini API キー |
RECEIPT_FOLDER_ID | 領収書PDFを入れるDriveフォルダID (初回ダイアログで設定) |
処理ロジック:
| STEP | 処理 | 入力 | 出力 | 条件 |
|---|---|---|---|---|
| 1 | PDFファイル取得 | Drive フォルダ | PDF配列 | DriveApp.getFilesByType(MimeType.PDF) |
| 2 | PDF→base64→Gemini API送信 | 各PDF | 構造化JSON配列 | モデル: gemini-2.5-flash、temperature=0.1、maxOutputTokens=8192 |
| 3 | JSON配列をページ単位で35タブに書き込み | JSON配列 | 35_wrk_receipt行 | 1ページ=1レコード。空ページ(vendor・totalAmount共に空)はスキップ |
| 4 | 管理ID自動採番 | 既存最大連番 | RCP_NNNN | 0パディング4桁 |
| 5 | 証跡リンク付与 | file.getId() | Drive URL | — |
| 6 | 処理済みPDFをprocessedサブフォルダに移動 | — | — | 成功時のみ。失敗時は元フォルダに残る |
| 7 | 同一取引先の突合補正 | 全レコード | 補正済みレコード | 後述 |
Geminiプロンプトの主要指示:
| フィールド | 抽出ルール |
|---|---|
docType | 領収書 or 請求書 (書類タイトルから判定) |
invoiceNumber (T番号) | Tで始まる13桁の登録番号のみ |
docNumber (帳票番号) | 請求書番号/領収書番号/注文番号。INVで始まる番号はここ |
accrualDate (発生日) | 請求期間の終了日。期間未記載なら発行日と同じ |
| 複数ページ | ページごとに1つのJSONオブジェクト。同一内容の重複は1つに統合。空白・目次ページはスキップ |
JSON応答パース:
| ケース | 処理 |
|---|---|
[...] 配列 | そのまま使用 |
{...} 単一オブジェクト | [{...}] に配列ラップ |
| ````json ... ` `` でラップ | 正規表現で [...] or {...} を抽出 |
| 不正な制御文字 | \n \r \t 以外の制御文字を除去 |
| 不完全なJSON (途中切断) | 未閉じの { [ を補完して再パース。途中のキー:値は切り捨て |
後処理: 同一取引先の突合補正 (postProcessReceiptData_):
全PDF読み込み後、同一取引先名のレコードを多数決で補正:
- T番号: 2件以上一致する値で少数派を上書き
- 住所: 空欄を多数派の値で補完 (空欄→多数派値のみ。既存値は変更しない)
エラーハンドリング:
| エラー条件 | 処理 | ユーザー通知 |
|---|---|---|
| 503 (高負荷) | 最大3回リトライ (5秒間隔) | — |
| JSON解析失敗 | エラー詳細をダイアログに表示 | ダイアログ |
| 失敗したPDF | 元フォルダに残る | 結果ダイアログにファイル名とエラー表示 |
| 大容量PDF (>100KB) で抽出1件 | ログ警告 (複数ページの可能性) | ログのみ |
3.2.2 マッチング確認 — importReceiptStatement()
マッチング対象 STL (33_wrk_bank):
| # | 条件 |
|---|---|
| 1 | 有効フラグ = TRUE |
| 2 | 決済口座が "立替精算" を含む |
| 3 | 決済ステータス = "未処理" のみ |
マッチ成立条件 (全てAND):
| # | 条件 | 詳細 |
|---|---|---|
| 1 | 金額一致 | receipt.税込金額_決済 が STL.税込金額_決済 の ±10%以内 |
| 2 | 名前やや一致 | 以下のいずれかを満たす(normalizeMerchant_ で正規化後に比較): |
| - receipt.取引先名 が STL.取引先名 に直接含まれる (or 逆) | ||
| - receipt.摘要 が STL.摘要 に直接含まれる (or 逆) | ||
| - トークン分割での部分一致 (2文字以上) |
マッチング優先度 (同一金額・取引先の場合):
| 優先度 | 条件 | スコア |
|---|---|---|
| 1 | STL摘要に 発生日の年月 + 利用分 が含まれる | +100 |
| 2 | 日数差 ≤ 7日 (発生日 vs STL決済日_計画) | +80 |
| 2 | 日数差 ≤ 30日 | +60 |
| 2 | 日数差 ≤ 60日 | +40 |
| 2 | 日数差 ≤ 90日 | +20 |
| 3 | 金額完全一致 (差分率=0) | +50 |
同額・同取引先の月次SaaS で正しい月の STL を選ぶために、摘要の
YYYY-MM利用分と receipt の発生日年月を照合。
UNMATCHED時の候補表示:
マッチ不成立の場合、緩和条件で候補STLを提示する:
| 条件 | スコア |
|---|---|
| 名前一致あり | +50 |
| 金額差 ≤ 10% | +30 |
| 金額差 ≤ 20% | +15 |
候補がある場合: マッチ決済ID(STL)に 候補:STL_XXXX を表示。
行の背景色:
| 結果 | 背景色 |
|---|---|
| MATCHED | #FFFFFF (白) |
| UNMATCHED | #FCE5CD (薄橙) |
スキップ条件 (マッチング確認):
| 条件 | 結果 |
|---|---|
| 確認FLG = TRUE | スキップ (確認済み行は一切触らない) |
| 処理結果 = 消込済 or SKIPで始まる | スキップ |
| 税込金額_決済 = 0 or 空 | SKIP:金額なし |
3.2.3 消込実行 — applyReceiptSettlement()
スキップ条件 (消込実行):
| 条件 | 結果 |
|---|---|
| 処理結果 = 消込済 | スキップ |
| 確認FLG ≠ TRUE | スキップ |
| マッチ決済ID(STL) が STL_ で始まらない | スキップ |
33タブ更新 (STL_IDごとに合算):
同一STL_IDに対して複数の領収書がある場合、金額を合算して処理する。
| 項目 | 更新内容 |
|---|---|
| 決済ステータス | 変更しない (未処理のまま) |
| 消込手段 | 帳票 をセット |
| 立替日 | receipt の決済日_実績をセット (空の場合のみ) |
| 税込金額_決済 | 合算額と差額があれば補正 |
| 差額(手数料等) | 差額がある場合にセット |
| 行の背景色 | #D9EAD3 (薄緑) |
35タブ更新:
| 項目 | 更新内容 |
|---|---|
| 処理結果 | 消込済 |
| STL情報列 (V〜Y) | 33タブの最新値で再更新 |
| 行の背景色 | #D9EAD3 (薄緑) |
ユーザーが内容確認後に手動で「消込済」に変更 → Action B で仕訳作成
3.2.4 未登録領収書→26タブ登録 — transferReceiptToAdhoc()
35タブで処理結果=「未登録」の領収書を26_bud_adhocに費用登録する機能。
フィールドマッピング:
| 35タブ | → | 26タブ | 備考 |
|---|---|---|---|
| - | → | 有効フラグ = TRUE | 固定 |
| - | → | 管理ID = ADH_NNNN | 連番自動採番 |
| 発生日+摘要+取引先名 | → | 取引名 | YYYYMMDD_摘要_取引先名 |
| - | → | 取引先名 | 単発_その他汎用ダミー固定 |
| 税抜金額_決済 | → | 税抜金額_計画 | 税抜が0の場合は税込金額を使用 |
| 消費税額_決済 | → | 消費税額_計画 | |
| 税込金額_決済 | → | 税込金額_計画 | |
| - | → | 税区分 | 対象外固定 |
| 発生日(P/L計上日) | → | 発生日(P/L計上日) | そのまま |
| - | → | 収支区分 | 支出固定 |
| 決済手段 | → | 決済手段 | マッピング変換 (後述) |
| - | → | 決済ラグ(月) | 1固定 |
| 決済日_実績 | → | 支払基準日 | DD(日)のみ |
| 証跡リンク | → | 備考 | 【領収書】RCP_NNNN + リンク |
決済手段マッピング:
| 元の値 (部分一致) | → | 26タブの値 |
|---|---|---|
| クレジット / クレカ | → | クレカ |
| 振込 / 銀行 | → | 口座振込 |
| 立替 | → | 立替精算 |
| その他 | → | 立替精算 (デフォルト) |
重複防止: 35タブの処理結果を「26タブ登録済」に更新 (背景色 #D9D2E9 薄紫)。再実行しても重複しない。
科目名・PJ名: 空欄のまま登録。ユーザーが26タブで後入力し、「📥 単発予算の自動起票」でINV生成。
3.3 UI (メニュー)
🔍 消込・マッチング
├─ 💳 クレカ明細マッチング確認
├─ 💳 クレカ消込実行 (確認FLG=TRUE)
├─ ───
├─ 📄 領収書PDFの読み込み (Drive)
├─ 🧾 領収書マッチング確認
└─ 🧾 領収書消込実行 (確認FLG=TRUE)
📝 費用登録
└─ 📥 未登録領収書→26タブ登録
4. 影響範囲
| 影響対象 | 影響内容 | 対応 |
|---|---|---|
502_receipt_reader.js | 領収書 PDF 読込(Gemini API) | 新規作成 |
501_cc_importer.js | 領収書マッチング・消込 + 26 タブ登録 | 関数追加 |
002_constants.js | CC_MERCHANT_MAP 名寄せ辞書 | 辞書エントリ追加 |
101_sys_config.js | メニュー登録 + 33 タブスキーマ拡張 | DDL 変更 |
403_subledger_engine.js | STL 作成時の列対応 + Action B 連携 | 既存拡張 |
| 33_wrk_bank | E 列「立替日」+ P 列「消込手段」に 帳票 値 | スキーマ変更 |
| 35_wrk_receipt | 新規タブ(A〜Y 列) | 新規作成 |
§4 詳細関数: 502 =
importReceiptPdfs/callGeminiForReceipt_/postProcessReceiptData_、501 =importReceiptStatement/applyReceiptSettlement/transferReceiptToAdhoc
5. テスト仕様
| テストID | テスト名 | 前提条件 | 期待結果 |
|---|---|---|---|
| RCP-T01 | PDF読み込み正常系 | フォルダに1枚のPDFあり | 35タブに1行追加。RCP_NNNN採番。処理済みPDFがprocessedフォルダに移動 |
| RCP-T02 | 複数ページPDF | 3ページのPDF | 35タブに3行追加。ファイル名に (p1) (p2) (p3) が付与 |
| RCP-T03 | PDF解析失敗 | 画像のみPDF (テキスト抽出不可) | エラーダイアログに失敗ファイル名が表示。PDFは元フォルダに残る |
| RCP-T04 | 同一取引先の突合補正 | 同一取引先の領収書3枚 (T番号が2枚一致・1枚不一致) | 不一致の1枚のT番号が多数派に補正される |
| RCP-T05 | マッチング正常系 | 金額一致・取引先名一致のSTLが存在 | 処理結果=MATCHED。STL情報が表示。背景白 |
| RCP-T06 | マッチング金額範囲 | 金額差がちょうど10%のSTL | マッチ成立 (±10%以内) |
| RCP-T07 | マッチング金額超過 | 金額差が11%のSTL | マッチ不成立 (UNMATCHED) |
| RCP-T08 | UNMATCHED候補表示 | 名前一致・金額差15%のSTL | UNMATCHED。マッチ決済IDに 候補:STL_XXXX が表示 |
| RCP-T09 | SaaS月次マッチング | 同額・同取引先のSTLが3件。うち1件の摘要にreceipt発生日の YYYY-MM利用分 あり | 摘要一致のSTLが最優先でマッチ |
| RCP-T10 | 消込実行正常系 | 確認FLG=TRUE、STL_IDあり | 33タブ: 消込手段=帳票、立替日セット。35タブ: 処理結果=消込済、背景薄緑 |
| RCP-T11 | 消込で決済ステータス不変 | 消込実行後の33タブ | 決済ステータスが「未処理」のまま変化しない |
| RCP-T12 | 消込金額差額補正 | receipt金額とSTL金額に差額あり | 33タブ: 税込金額_決済が補正、差額(手数料等)にセット |
| RCP-T13 | 同一STLへの複数領収書合算 | 確認FLG=TRUEの2行が同じSTL_IDに紐づく | 33タブ: 税込金額_決済が合算額に更新 |
| RCP-T14 | スキップ: 確認FLG=TRUE再実行 | マッチング確認を再実行 | 確認FLG=TRUE行はスキップされ上書きされない |
| RCP-T15 | 未登録→26タブ登録 | 処理結果=「未登録」の行あり | 26タブにADH_NNNN行が追加。35タブの処理結果=「26タブ登録済」。背景薄紫 |
| RCP-T16 | 26タブ登録の重複防止 | 処理結果=「26タブ登録済」で再実行 | 対象なしダイアログ。重複行は作成されない |
| RCP-T17 | 決済手段マッピング | 決済手段=「クレジットカード」の領収書 | 26タブの決済手段=「クレカ」 |
付録
付録A: 改修ファイル一覧
| ファイル | 主要関数 | 内容 |
|---|---|---|
502_receipt_reader.js | importReceiptPdfs 等 3 関数 | 領収書 PDF 読込(Gemini API) |
501_cc_importer.js | importReceiptStatement 等 3 関数 | 領収書マッチング・消込 + 26 タブ登録 |
002_constants.js | CC_MERCHANT_MAP | 名寄せ辞書 |
101_sys_config.js | — | メニュー + 33 タブスキーマ拡張 |
403_subledger_engine.js | — | STL 作成時の列対応 + Action B 連携 |