Article

経費精算の自動化で月間100時間削減する方法

高田晃太郎
経費精算の自動化で月間100時間削減する方法

1件の経費精算あたりの処理コストは26.63ドル、エラー訂正には52.50ドルという古典的データ(Aberdeen Group)[1]に加え、申請から払い出しまでのリードタイムが数営業日を超える組織は今も少なくありません[2]。国内SaaSの公開事例やベンダーベンチマークを横断的に見ると、月間150名規模の企業で発生する申請件数はおよそ180〜220件、1件あたりのヒト時間は申請者・承認者・経理を合わせて平均30〜45分という報告が多く、一般的な目安になります(公開情報を踏まえた参考レンジ)。この前提で、ワークフロー自動化、OCR(文字認識)による明細読取、会計API(システム間連携の仕組み)連携を組み合わせるだけで、目安として月間100時間規模の削減を狙える構図が見えてきます[3]。

なぜ100時間削減できるのか:ボトルネックの分解と根拠

まずは現状の時間配分をシンプルな試算で可視化します。150名の会社で、月間の申請率を70%、一人あたり平均1.3件とすると、総件数はおよそ136件です。1件あたりの時間は、申請者がレシート撮影と入力に12分、承認者の確認に5分、経理の検査と会計システム転記に20分、合計37分という仮定は一般的な運用感覚に近いレンジでしょう。すると月間の総所要時間は約84時間に達します。さらに、差戻し率を15%、差戻し1件あたりの再工数を8分とすると、追加で約3時間が積み上がり、経理の月次締め前後に集中するチェック時間を10〜20時間見込むと、全体では100時間前後のヒト時間が発生しうる、というのが試算の一例です(いずれも公開事例に基づく一般的なレンジ)。このオーダー感は、国内の導入記事でも前処理の大幅短縮やリードタイム短縮として多数紹介されています[5]。

自動化の効果は、申請者の入力負荷をOCRとテンプレートで半減すること、承認者の判断材料を標準化して確認時間を三割以上圧縮すること、経理の検査と転記をAPI連携と重複検知で大幅に短縮することにあります。上記と同じ前提に当てはめると、申請者の時間は12分から6分、承認は5分から3分、経理は20分から6分まで低減し、差戻し率もメタデータ標準化で15%からひと桁台に下げられる可能性があります。結果として、総工数は約45時間程度まで圧縮され、締め前の臨時対応も半減するため、全体で月間100時間規模の削減が実務的な目標として現実味を帯びます[4]。

この規模の削減がなぜ実現しやすいのかをもう一段深掘りすると、入力の自由記述を排し、商号、日付、税込金額、税区分、プロジェクト、勘定科目、領収書画像の6点をコアスキーマに絞る設計が効いてきます。項目が確定すればOCRや会計APIのエラー領域が狭まり、承認者の「見るべきところ」も固定化されるため、レビューが流れ作業化します。ここにポリシーエンジンで上限金額や重複検知、深夜・休日利用といったリスクシグナルを自動マーキングすると、例外処理のみ人が判断する運用へ段階的に移せます。

アーキテクチャ設計:IDP、ワークフロー、会計API、統制の四層

実務に耐える経費精算の自動化は四層で組みます。最下層はIDP(Intelligent Document Processing:非構造文書の構造化)で、レシート画像から構造化データを取り出し、通貨、日付形式、税区分を正規化します。中間層はワークフローで、チャットやモバイルからの申請と承認を即時に捌き、ポリシーを評価して例外のみ人に上げます。上位層は会計システム連携で、仕訳の自動作成、部門・プロジェクト・科目のマスタ同期、締め処理の期日管理を担います。横断するのが統制層で、監査証跡、PII(個人情報)の保護、削除・保管ポリシー、重複や不正の検知などを束ねます。四層を疎結合に保つことが運用の肝で、OCRを別サービスへ切り替える、会計SaaSを刷新する、といったイベントにも耐えられる構造になります。

性能要件は申請ピークと締め期に引っ張られます。一般的にOCRのスループットは実装とワーカー数に依存し、料金は1枚あたり0.01〜0.1ドルが目安です[6]。承認のインタラクティブ体験はサブ秒応答(1秒未満)が望ましく、Webhook(イベント通知)やキュー(非同期処理列)を利用して非同期化し、ユーザーの待ちを作らない設計が重要です。会計APIのスロットリング(レート制限)はベンダーごとに異なりますが、毎分数十〜数百リクエストの帯域が多く、バックオフ(再試行間隔を延ばす)とリトライ、バッチ確定方式を採用すると障害耐性と締めの確実性が高まります(一般的なベンダー仕様の範囲)。

セキュリティと統制面では、領収書画像や氏名、支払手段といったPIIをS3などの暗号化バケットに保存し、KMS(AWS Key Management Service)によるサーバーサイド暗号化を標準にします。アクセスはロールベースで最小権限に絞り、取り回しは署名付きURLを短時間で発行する方式に統一します。監査証跡はイベントログをWORM(Write Once Read Many)ストレージに書き込み、誰がいつ何を承認したか、どのポリシーにヒットしたかを後から再現できるようにします。

IDP層の実装例(Python + AWS Textract)

領収書画像からコアスキーマへ正規化する最小構成の例です。エラー時は指数バックオフで再試行し、抜け値はポリシーエンジンへ例外として伝播します。

import boto3
import time
from decimal import Decimal

textract = boto3.client("textract")

def call_textract_with_retry(bucket, key, max_retries=5):
    delay = 0.5
    for attempt in range(max_retries):
        try:
            return textract.analyze_expense(
                Document={"S3Object": {"Bucket": bucket, "Name": key}}
            )
        except textract.exceptions.ThrottlingException:
            time.sleep(delay)
            delay = min(delay * 2, 8)
    raise RuntimeError("Textract throttling: max retries exceeded")

def normalize(expense):
    fields = {item["Type"]["Text"]: item.get("ValueDetection", {}).get("Text")
              for doc in expense.get("ExpenseDocuments", [])
              for item in doc.get("SummaryFields", [])}
    amount = Decimal(fields.get("TOTAL", "0").replace(",", ""))
    vendor = fields.get("VENDOR_NAME") or fields.get("SUPPLIER")
    date = fields.get("INVOICE_RECEIPT_DATE")
    tax = fields.get("TAX_AMOUNT") or "auto"
    return {
        "vendor": vendor,
        "date": date,
        "amount": float(amount),
        "tax": tax,
        "currency": fields.get("CURRENCY", "JPY"),
    }

def extract_receipt(bucket, key):
    resp = call_textract_with_retry(bucket, key)
    data = normalize(resp)
    if not data["vendor"] or not data["date"]:
        data["needs_review"] = True
    return data

チャット起点の承認(TypeScript + Slack Bolt)

モバイルで撮影した領収書をSlackにドロップし、補助入力を促して承認へ自動ルーティングします。承認はインタラクティブメッセージで即応し、例外は経理キューへ移送します。

import { App } from "@slack/bolt";
import fetch from "node-fetch";

const app = new App({ token: process.env.SLACK_BOT_TOKEN!, signingSecret: process.env.SLACK_SIGNING_SECRET! });

app.event("file_shared", async ({ event, client, logger }) => {
  try {
    const fileId = (event as any).file_id;
    const file = await client.files.info({ file: fileId });
    const url = file.file?.url_private_download as string;
    const upload = await fetch(process.env.INGEST_URL!, { method: "POST", headers: { Authorization: `Bearer ${process.env.INGEST_TOKEN}` }, body: JSON.stringify({ url }) });
    const { receiptId, preview } = await upload.json();
    await client.chat.postMessage({ channel: file.file?.user as string, text: "申請内容を確認してください", blocks: [ { type: "section", text: { type: "mrkdwn", text: `店舗: ${preview.vendor}\n日付: ${preview.date}\n金額: ${preview.amount}円` } }, { type: "actions", elements: [ { type: "button", text: { type: "plain_text", text: "申請" }, action_id: "submit", value: receiptId }, { type: "button", text: { type: "plain_text", text: "修正" }, action_id: "edit", value: receiptId } ] } ] });
  } catch (e) { logger.error(e); }
});

app.action("submit", async ({ ack, body, client }) => {
  await ack();
  const receiptId = (body.actions?.[0] as any).value;
  await fetch(`${process.env.WORKFLOW_URL}/start`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ receiptId }) });
  await client.chat.postMessage({ channel: body.user.id, text: "承認フローを開始しました" });
});

(async () => { await app.start(process.env.PORT || 3000); })();

重複検知とリスクシグナル(SQL)

金額、店舗、日付の近接で重複の可能性をスコアリングし、例外レビューに回します。プロジェクトや出張期間に紐付く利用での正当性も同時に推定できます。

WITH base AS (
  SELECT r.id, r.user_id, r.vendor, r.amount, r.tx_date::date AS d
  FROM receipts r WHERE r.status IN ('draft','submitted')
)
SELECT a.id AS a_id, b.id AS b_id,
       CASE WHEN a.vendor = b.vendor THEN 0.6 ELSE 0 END +
       CASE WHEN a.amount = b.amount THEN 0.3 ELSE 0 END +
       CASE WHEN ABS(DATE_PART('day', a.d - b.d)) <= 3 THEN 0.2 ELSE 0 END AS dup_score
FROM base a JOIN base b ON a.user_id = b.user_id AND a.id < b.id
WHERE (a.vendor = b.vendor OR a.amount = b.amount) AND ABS(DATE_PART('day', a.d - b.d)) <= 7
HAVING CASE WHEN a.vendor = b.vendor THEN 0.6 ELSE 0 END + CASE WHEN a.amount = b.amount THEN 0.3 ELSE 0 END + CASE WHEN ABS(DATE_PART('day', a.d - b.d)) <= 3 THEN 0.2 ELSE 0 END >= 0.7;

インフラの最小構成(Terraform)

領収書保管用の暗号化バケットと、処理用ロールを最小権限で定義します。署名付きURLのみ許可し、パブリックアクセスは全面遮断します。

resource "aws_s3_bucket" "receipts" { bucket = var.bucket_name force_destroy = false }
resource "aws_s3_bucket_public_access_block" "receipts" { bucket = aws_s3_bucket.receipts.id block_public_acls = true block_public_policy = true ignore_public_acls = true restrict_public_buckets = true }
resource "aws_s3_bucket_server_side_encryption_configuration" "receipts" { bucket = aws_s3_bucket.receipts.id rule { apply_server_side_encryption_by_default { sse_algorithm = "aws:kms" } } }
resource "aws_iam_role" "worker" { name = "expense-worker" assume_role_policy = data.aws_iam_policy_document.assume.json }
resource "aws_iam_policy" "worker_s3" { name = "expense-worker-s3" policy = jsonencode({ Version = "2012-10-17", Statement = [ { Effect = "Allow", Action = ["s3:GetObject","s3:PutObject"], Resource = ["${aws_s3_bucket.receipts.arn}/*"] } ] }) }
resource "aws_iam_role_policy_attachment" "attach" { role = aws_iam_role.worker.name policy_arn = aws_iam_policy.worker_s3.arn }

検証パイプライン(GitHub Actions)

申請データのスキーマ検証と重複チェックをCIに組み込み、誤配信や不整合を締め前に検知します。

name: validate-expense
on: [push]
jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: '20' }
      - run: npm ci
      - run: npm run lint && npm test
      - name: schema
        run: node scripts/validate-schema.js data/receipts/*.json

KPIとROI:導入の意思決定を数字で固める

削減時間を定量化するための一次KPI(重要業績評価指標)は三つに集約できます。申請から承認までの中位リードタイム、1件あたりの経理処理時間、差戻し率の三点です。導入前のベースラインを2週間観測し、導入後は週次で推移を追うと、効果の因果を部門横断で合意しやすくなります。例えば、前述の前提における導入前後の比較では、リードタイムは2.4営業日から0.8営業日に短縮、経理処理時間は20分から6分に圧縮、差戻し率は15%からひと桁台に低下する、というのが現実的なレンジです[2]。人件費5,000円/時の標準原価という仮定を置くと、月間100時間の削減は50万円の変動費相当を圧縮したのと同義になり、年間換算ではおよそ600万円の効果という試算になります(ROI:投資対効果の考え方)。

費用側の見積りは、OCR・ワークフロー・会計連携のSaaS費とイベント駆動の実行基盤、実装保守の内製コストに分けます。OCR費は1枚0.05ドル、月2,000枚で約1.5万円[6]、ワークフローは席課金で月5〜15万円、会計SaaSのAPI利用は本体費用に内包されることが多く、インフラの実行コストはイベント駆動構成で月1万円台に収まる、というのが一般的な目安です。初期実装は要件によりますが、2名×6週間での内製なら人件費見積でおよそ300〜400万円の範囲が現実的です。これに対して年間600万円規模の効果が見込めるなら、単純回収期間は6〜8カ月が一つの目安になります。

品質指標としては、OCRのフィールド正答率、承認の一次通過率、例外レビュー件数の三つが効きます。OCRの正答率はベースの八割台から、店舗辞書やレイアウト学習の追加で九割前後まで引き上げられることが多いです。承認一次通過率はテンプレートとポリシーの調整で七割から九割台へ、例外レビューは総件数の一割未満に抑えると経理の体感が一気に軽くなります。

落とし穴と対策:スケール、変更管理、監査対応

自動化の成否を分ける落とし穴は、スケール時のボトルネックと変更管理、監査対応の三点に集中します。まずスケールの観点では、締め期のピーク集中に合わせてIDPワーカーを水平スケールできるか、会計APIのスロットリングを確実に回避できるかが鍵です。この点はキューベースの非同期処理と、バッチ確定の採用、レートリミットに応じた逐次バッチングで解決します。次に変更管理では、勘定科目やプロジェクトのマスタ改定が承認ルールやマッピングに影響しやすく、往々にして異常系の温床になります。スキーマとルールをコード化し、レビューとテストを通す運用へ移すことで、属人化を防げます。最後に監査対応では、誰がどの情報を見て何を承認したかの再現性が必須で、チャットやモバイルのインタラクションも含めた完全なイベントログをWORMにアーカイブする設計が不可欠です。

この領域での具体的な回避策として、例外を認めるフローを意図的に用意することが挙げられます。現場は常にグレーで、深夜タクシーや海外出張の通貨変換など、定義からこぼれるケースが一定割合で発生します。例外は悪ではなく、可視化と説明責任を付与することで統制はむしろ強化されます。画面上では例外タグを明示し、承認の根拠と添付資料を必須化するだけで、監査での説明は格段に容易になります。

会計API連携の堅牢化(Node.js)

レート制限と冪等性(同一操作の繰り返しでも結果が変わらない性質)を考慮した仕訳作成の例です。バッチ単位で確定し、障害時は再実行しても二重計上されません。

import fetch from "node-fetch";

async function postWithRetry(url, payload, idempotencyKey, max = 5) {
  let delay = 500;
  for (let i = 0; i < max; i++) {
    const res = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json", "Idempotency-Key": idempotencyKey }, body: JSON.stringify(payload) });
    if (res.status === 429 || res.status >= 500) { await new Promise(r => setTimeout(r, delay)); delay = Math.min(delay * 2, 8000); continue; }
    if (!res.ok) throw new Error(`API error: ${res.status}`);
    return res.json();
  }
  throw new Error("Max retries exceeded");
}

export async function createJournal(entries, batchId) {
  const chunks = Array.from({ length: Math.ceil(entries.length / 50) }, (_, i) => entries.slice(i * 50, i * 50 + 50));
  for (const [idx, chunk] of chunks.entries()) {
    await postWithRetry(process.env.ACCOUNTING_URL + "/journals", { entries: chunk }, `${batchId}-${idx}`);
  }
}

ケーススタディの輪郭と到達点

150名規模の開発会社を想定し、紙とExcelを併用していた経費運用を刷新するケースの一般的な進め方を描くと、チャット起点の申請とOCR、重複検知、会計API連携を段階導入し、1〜2カ月でパイロット、2〜3カ月で全社展開に移行するのが一つのパターンです。導入数カ月後の到達レンジとしては、申請から承認までの中位リードタイムが営業日ベースで半減前後、経理の1件あたり処理時間は20分台から個別要件に応じて一桁分台へ、差戻し率は二桁台からひと桁台へ低下することが期待できます。これにより、月間の純削減時間はおおむね100時間前後を目標に置けるケースが多く、年間効果は数百万円規模という試算が成り立ちます。現場の声としては、承認待ちの可視化や、例外の説明欄が実務にフィットする、というフィードバックがしばしば挙がります[3]。

まとめ:小さく始めて、例外を設計に取り込む

経費精算の自動化は、完璧なルールを先に作るより、コアスキーマを固定し、例外を人に委ねる線引きを先に決めると進みが早くなります。OCRで七割を機械に寄せ、承認の判断材料を標準化し、会計APIで転記を無人化するという、三つのレールを同時に敷くことで、月間100時間の削減は現場の摩擦なく十分に狙えます。次の一歩として、今月の申請件数、承認リードタイム、差戻し率の三つを計測し、来月に向けてコアスキーマとポリシーを仮置きで稼働させてみてください。数字が動けば、現場の合意も動きます。さらに深掘りした設計や具体的なワークフローパターンは、社内の開発基盤に合わせて調整が必要です。まずは一つの部門で一サイクル回し、測定し、ルールを磨き上げるところから始めてみませんか。

参考文献

  1. SEC EDGAR filing (Xerox 2010 Form 10-K) citing Aberdeen Group: average expense report processing cost and error correction cost. https://www.sec.gov/Archives/edgar/data/1066026/000119312511315702/d223869d10k.htm#:~:text=the%20Aberdeen%20Group%2C%20the%20average,such%20as%20ours%2C%20that%20automate
  2. ITmedia エンタープライズ「3時間かかっていた経費精算作業を30分に短縮」事例記事. https://www.itmedia.co.jp/enterprise/articles/1910/28/news001.html#:~:text=3%E6%99%82%E9%96%93%E3%81%8B%E3%81%8B%E3%81%A3%E3%81%A6%E3%81%84%E3%81%9F%E7%B5%8C%E8%B2%BB%E7%B2%BE%E7%AE%97%E4%BD%9C%E6%A5%AD%E3%82%9230%E5%88%86%E3%81%AB%E7%9F%AD%E7%B8%AE
  3. ONESブログ「RPA導入で月100時間の業務時間削減」事例まとめ. https://ones.com/ja/blog/knowledge/rpa-efficiency-case-study/#:~:text=%E8%BF%91%E5%B9%B4%E3%80%81%E5%A4%9A%E3%81%8F%E3%81%AE%E4%BC%81%E6%A5%AD%E3%81%8C%E4%BA%8B%E5%8B%99%E4%BD%9C%E6%A5%AD%E3%81%AE%E5%8A%B9%E7%8E%87%E5%8C%96%E3%81%AB%E5%8F%96%E3%82%8A%E7%B5%90%E3%82%93%E3%81%A7%E3%81%84%E3%81%BE%E3%81%99%E3%80%82%E3%81%9D%E3%81%AE%E4%B8%AD%E3%81%A7%E3%82%82%E3%80%81RPA%EF%BC%88Robotic%20Process%20Automation%EF%BC%89%E3%81%AE%E5%B0%8E%E5%85%A5%E3%81%AB%E3%82%88%E3%82%8B%E4%BA%8B%E5%8B%99%E4%BD%9C%E6%A5%AD%E5%8A%B9%E7%8E%87%E5%8C%96%E4%BA%8B%E4%BE%8B%E3%81%8C%20%E6%B3%A8%E7%9B%AE%E3%82%92%E9%9B%86%E3%82%81%E3%81%A6%E3%81%84%E3%81%BE%E3%81%99%E3%80%82
  4. TOMAコンサルタンツグループ「経理現場でAI・RPA活用による業務改善事例」. https://toma.co.jp/blog/software_it/biz-improvement-due-to-rpa-p4/#:~:text=%E3%80%96RPA%E5%B0%8E%E5%85%A5%E4%BA%8B%E4%BE%8B%E3%80%97%E7%B5%8C%E7%90%86%E7%8F%BE%E5%A0%B4%E3%81%A7AI
  5. note(BizteX)「経理担当者の請求書処理時間が1/3に」事例. https://note.com/biztex/n/na1540be06fdf#:~:text=%E7%B5%8C%E7%90%86%E6%8B%85%E5%BD%93%E8%80%85%E3%81%AE%E8%AB%8B%E6%B1%82%E6%9B%B8%E5%87%A6%E7%90%86%E3%81%AB%E5%85%85%E3%81%A6%E3%82%8B%E6%99%82%E9%96%93%E3%81%8C1%2F3%E7%A8%8B%E5%BA%A6%E3%81%AB%E5%89%8A%E6%B8%9B
  6. AWS Textract 料金ページ(AnalyzeExpenseの価格例). https://aws.amazon.com/en/textract/pricing/#:~:text=Let%E2%80%99s%20assume%20you%20want%20to,See%20the%20calculation%20below