# INVOICE

請求書受取・転送業務の自動化

業務委託先・発注先から届く月次請求書のメール受信・PDF保存・Slack転送を完全自動化。GAS+Gmail+Drive+Slackの連携で、月末に集中していた手動処理4時間分を消去した。

工数削減 4時間/月
開発期間 1Week
開発費用 ¥100,000

01_BACKGROUND

業務委託メンバー・発注先からの月次請求書は、担当者がGmailを開いて確認し、PDFをダウンロードして経理担当者にSlackで転送する作業が毎月発生していた。月末に集中するこの対応が、月あたり4時間の工数を占め、本業の中断を繰り返していた。

「請求書処理のせいで仕事が止まる」が、月末の常態になっていた。
月末は仕事が進まない。請求書処理のせいで…

送信元の会社名がメールごとに異なること、Slack通知のためのWebhook設定など、複数の変数が絡むことが自動化の壁になっていた。チェック漏れも起きており、担当者の注意力に依存した運用が続いていた。

02_APPROACH

Gmail監視・Drive保存・Slack通知の3フローを1つのGASプロジェクトで完結させた。会社名の揺れは「ドメインマスタ照合→本文自動抽出→不明」のハイブリッド方式で対応し、「送信元がバラバラで自動化できない」という障壁を消去した。複数ユーザーへの展開はスプレッドシートごとコピーするテンプレート方式で対応し、GAS固有のトリガー・プロパティ設定を各自が完結できる構成にした。

03_RESULT

月4時間の手動処理を消去。請求書の受取・保存・通知がすべて自動で完結し、担当者は月末の対応から解放された。人間がするのは、一覧表のチェックボックスを押す一操作だけになった。

BEFORE
Gmailを開いてPDFを手動でダウンロードし、SlackでDM転送していた
AFTER
1時間ごとに自動検知・DriveへPDF保存・一覧表への転記まで全自動
BEFORE
月末4時間を請求書処理に費やし、本業が中断されていた
AFTER
チェックボックスをONにするだけでSlack通知。月末の中断はゼロ

SYSTEM_ARCHITECTURE

■ 処理フロー(時間トリガー:1時間おき) checkInvoiceMails() ├─ LockService でスクリプトロック取得(5秒待機) │ └─ 多重実行防止:前回が終わっていない場合はスキップ ├─ 設定シートD列からキーワード取得(例:"請求書" OR "invoice") ├─ 一覧表G列(messageId)を読み込み → 処理済みID集合を生成 ├─ Gmailクエリを自動組み立て │ └─ to:{実行者自身} subject:(kw1 OR kw2) has:attachment newer_than:31d ├─ GmailApp.search()(最大50スレッド) └─ 各メッセージに対して processMessage_() を実行 ├─ messageId が処理済み → スキップ(重複防止) ├─ PDF添付を抽出(Content-Type or .pdf拡張子判定) ├─ PDF以外の添付は全スキップ ├─ resolveCompanyName_() で会社名を3段階で解決(下記) └─ PDFごとに saveAttachmentAndLog_() を実行 ├─ 受信月から yyyy-MM フォルダを取得 or 作成(Drive) ├─ ファイル名を "{会社名}_{元のファイル名}.pdf" で組み立て ├─ Drive にファイル作成(copyBlob → setName) └─ 一覧表に appendRow(10列:会社名/差出人/受信日時/Slack/月/URL/messageId/取得方法/ファイル名/エラー) └─ D列(Slack)に insertCheckboxes() でチェックボックス書式を付与 ■ 会社名解決ロジック(resolveCompanyName_) ① 差出人メールアドレスからドメインを抽出 └─ 正規表現 /@([a-zA-Z0-9.-]+)/ → 例: "abc-corp.co.jp" ② 設定シートA/B列(取引先マスタ)と小文字完全一致 └─ 一致 → 会社名を採用、取得方法="マスタ一致" ③ 不一致の場合、メール本文を正規表現でスキャン └─ /^.{0,20}(株式会社|合同会社|有限会社|\(株\)|(株)).{0,20}$/m └─ マッチ → 取得方法="自動抽出" ④ どちらも失敗 → 会社名="不明"、取得方法="未取得" ■ Slack通知フロー(インストーラブル onEdit トリガー) onEditInstallable(e) ├─ 一覧表シート以外の編集 → return ├─ Slack列(D列)以外の編集 → return ├─ チェックを外した(FALSE) → return ├─ 1行目(ヘッダー)→ return ├─ 該当行から 会社名・DriveURL・保存ファイル名 を取得 └─ Incoming Webhook へ POST ■ 初期セットアップ(Menu.gs) setupSheets() ← カスタムメニュー「初期セットアップ」から実行 ├─ 確認ダイアログ(誤操作防止) ├─ 既存の「設定」「一覧表」シートを削除して再作成 ├─ 「設定」シート:ヘッダー + 記入例(サンプル取引先・キーワード)を自動挿入 └─ 「一覧表」シート:10列ヘッダー + 列幅調整 + 先頭行を固定表示 ■ スクリプトプロパティ(PropertiesService で管理) DRIVE_PARENT_FOLDER_ID : PDF格納先の親フォルダID(共有ドライブ) SLACK_WEBHOOK_URL : Slack Incoming Webhook URL ■ 使用サービス GmailApp / DriveApp / SpreadsheetApp UrlFetchApp / LockService / PropertiesService / Session
> START_CONSULTING _