0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

電帳法のファイル名付け、もう手作業でやめた。Claude Skillで請求書PDFを自動リネームする

0
Posted at

この記事で手に入るもの

  • 電子帳簿保存法(以下、電帳法)が求める「検索要件」を、ファイル名だけで満たす実装方法
  • 請求書・領収書・契約書PDFを読み取り、YYYYMMDD_取引先名_金額円_書類種別.pdf に自動リネームする仕組み
  • それを毎回ゼロから指示するのではなく、Claude の「スキル」として固定化して再現性を持たせる考え方

経理・バックオフィスのデジタル化を担当する中で、毎月発生する「受領した請求書PDFのファイル名付け替え」を自動化した記録です。同じ作業に消耗している方の参考になれば。

コードはそのまま動かせる粒度で載せています。pdftotext / pdftoppm(poppler-utils)と Python が動く環境を前提にしています。


まず結論:Before / After

手作業だと、ダウンロードしたPDFはこんなファイル名で溜まっていきます。

invoice_001.pdf
20240115-final(2).pdf
スキャン_0312.pdf
receipt.pdf

これを、内容を読み取って電帳法準拠のファイル名に変換します。

20240131_株式会社サンプル_110000円_請求書.pdf
20240315_山田太郎_55000円_領収書.pdf
20231201_ABC商事_330000円_契約書.pdf

ファイル名に「取引年月日・取引先・取引金額」が入っているので、OS標準のファイル検索だけで税務調査の求めに応じられる状態になります。


なぜファイル名で済むのか(最小限の制度解説)

電帳法の電子取引データ保存では、保存したデータを**「取引年月日」「取引金額」「取引先」で検索できる状態**にしておくことが求められます。2022年の改正でこの3項目に整理され(それ以前は「勘定科目」が含まれていました)、2024年1月からは電子取引データの電子保存が完全に義務化されています。

専用システムを入れていれば自動的に満たせますが、システムがない場合の最も手軽な代替手段として、国税庁自身がファイル名への記録項目の付与を認めています。つまり「日付・取引先・金額」を規則的にファイル名へ入れておけば、OSの検索機能で要件を満たせる、という整理です。

出典:国税庁「電子帳簿保存法一問一答【電子取引関係】」、同「電子取引データの保存方法をご確認ください」

免除規定に注意:前々年(前々事業年度)の売上高が5,000万円以下で、かつ税務職員のダウンロードの求めに応じられる事業者は、そもそも検索要件が不要です。また税制は毎年改正されます。自社が対象かどうかは顧問税理士・国税庁の最新資料で必ず確認してください。本記事は実装の知見であり、税務上の助言ではありません。


なぜ「手順書」ではなく「スキル」にしたのか

最初は手順をメモして、毎回 Claude に貼り付けて指示していました。ですが、

  • 命名規則(禁止文字の置換、金額の表記、書類種別の統一)を毎回言語化するのが面倒
  • 担当者が変わるとルールの解釈がブレる
  • 「相手方はどっち?」「税込はどれ?」の判断基準が暗黙知のまま

という問題が残ります。そこで、判断ロジックごと Claude のスキル(再利用可能な手順定義)として固定しました。一度書けば、PDFを渡して「電帳法のファイル名にして」と言うだけで、同じ品質で変換されます。属人化していた判断を仕組みに落とす、というのがバックオフィスDXの肝だと考えています。


実装

① 命名規則を定義する

スキルの中核は、ブレやすいルールを明文化したこの表です。

項目 内容 ルール
取引年月日 YYYYMMDD(8桁) -/ は使わない
取引金額 税込・整数・円 カンマなし(例 110000円
取引先名 相手方の名称 スペースは _、法人格は省略しない
書類種別 請求書/領収書/見積書/契約書/納品書/発注書 表記を統一する
推奨フォーマット: YYYYMMDD_取引先名_金額円_書類種別.pdf

使用禁止文字(/ \ : * ? " < > | とスペース)は、ファイルシステムで弾かれるので必ず置換します。

② PDFの内容を読み取る

テキストが埋め込まれたPDFは pdftotext で抽出します。

# テキストPDFからの抽出(レイアウト保持)
pdftotext -layout "$PDF_PATH" - | head -100

スキャンされた画像PDF(テキストが取れない)の場合は、ページを画像化して視覚的に読み取らせます。

# スキャンPDFは1ページ目を画像にしてから読む
pdftoppm -jpeg -r 150 -f 1 -l 1 "$PDF_PATH" /tmp/pdf_page
ls /tmp/pdf_page-*.jpg

③ 必要な4項目を抽出する

読み取ったテキストから、日付・金額・取引先・書類種別を特定します。ここで一番つまずくのが金額です。請求書には「小計」「消費税」「合計」など複数の数字が並ぶため、税込の最終合計を優先的に拾う正規表現を、優先度順に並べておきます。

import re

# 金額抽出パターン(上から優先)。税込合計を最優先で拾う
amount_patterns = [
    r'合計[(\(]?税込[)\)]?\s*[¥¥]?\s*([\d,]+)',   # 合計(税込)¥110,000
    r'税込[合計]?\s*[¥¥]?\s*([\d,]+)',              # 税込合計 ¥110,000
    r'御請求金額\s*[¥¥]?\s*([\d,]+)',               # 御請求金額
    r'ご請求金額\s*[¥¥]?\s*([\d,]+)',               # ご請求金額
    r'[¥¥]\s*([\d,]+)[(\(]?税込',                  # ¥33,000(税込)
    r'金額[ \s]*[¥¥]\s*([\d,]+)',                  # 金額 ¥33,000(領収書形式)
    r'領収金額\s*[¥¥]?\s*([\d,]+)',                 # 領収金額
    r'合計\s*[¥¥]\s*([\d,]+)',                      # 合計 ¥110,000
]

def extract_amount(text: str) -> int | None:
    for pat in amount_patterns:
        m = re.search(pat, text)
        if m:
            return int(m.group(1).replace(',', ''))
    return None

取引先(相手方)の判断ロジックも明文化しておくと精度が安定します。

  • 請求書・見積書・納品書 → 発行元(自社に請求してきた側)
  • 領収書 → 発行元(自社が支払った先)
  • 契約書 → 契約の相手方(甲乙の自社でない側)

ポイントは「自社名は取引先にしない」こと。自社が発行した書類はそもそもこの処理の対象外(電子取引データ保存の区分が異なる)なので、受領した書類かどうかを最初に切り分けます。

④ ファイル名を生成する

抽出した4項目を、禁止文字を処理しながら組み立てます。

import re

def generate_filename(date_str: str, partner_name: str, amount: int, doc_type: str) -> str:
    """電帳法対応ファイル名を生成する

    date_str:     'YYYYMMDD'
    partner_name: 取引先名(生の文字列)
    amount:       取引金額(税込・整数)
    doc_type:     '請求書' など
    """
    # 禁止文字・全角/半角スペースをアンダースコアに置換
    forbidden = r'[/\\:*?"<>|  ]'
    partner_clean = re.sub(forbidden, '_', partner_name)

    # 連続アンダースコアを1つに整理し、前後を除去
    partner_clean = re.sub(r'_+', '_', partner_clean).strip('_')

    # 30文字を超える取引先名は切り詰める
    if len(partner_clean) > 30:
        partner_clean = partner_clean[:30]

    amount_str = f"{int(amount)}"  # カンマなし整数 + 円
    return f"{date_str}_{partner_clean}_{amount_str}_{doc_type}.pdf"


# 実行例
print(generate_filename("20240131", "株式会社サンプル", 110000, "請求書"))
# → 20240131_株式会社サンプル_110000円_請求書.pdf

⑤ リネームを実行する(必ず確認してから)

ファイルシステムへの変更は不可逆なので、実行前に必ず確認を挟む設計にしています。同名ファイルが既にある場合は連番を付けて衝突を避けます。

import os
import shutil

def rename_pdf(original_path: str, new_filename: str) -> str:
    dir_path = os.path.dirname(original_path)
    new_path = os.path.join(dir_path, new_filename)

    # 同名ファイルがあれば連番を付ける
    if os.path.exists(new_path) and new_path != original_path:
        base, ext = os.path.splitext(new_filename)
        counter = 1
        while os.path.exists(new_path):
            new_path = os.path.join(dir_path, f"{base}_{counter}{ext}")
            counter += 1

    shutil.move(original_path, new_path)
    return new_path

複数PDFをまとめて処理するときは、リネーム前に変換結果を一覧表で提示し、まとめて承認をもらう運用にしています。

## 電帳法対応ファイル名の変換結果

| # | 元のファイル名 | 変換後 | 抽出情報 |
|---|---|---|---|
| 1 | invoice_001.pdf | 20240131_株式会社ABC_110000円_請求書.pdf | 2024/01/31 / ¥110,000 / 株式会社ABC |
| 2 | receipt_0315.pdf | 20240315_山田商店_3300円_領収書.pdf | 2024/03/15 / ¥3,300 / 山田商店 |

⚠️ 1番:金額に消費税が含まれているか確認してください

リネームを実行してよいですか?(はい / いいえ)

つまずいたポイントと対処

  • 税込か税抜か問題:合計が複数あると税抜を拾ってしまう。→ 正規表現の優先順位で「税込合計」系を先頭に置く。それでも怪しいものは「要確認」と明示してユーザー判断に委ねる。
  • スキャンPDFで文字が取れない:→ pdftoppm で画像化して視覚的に読む経路を用意。
  • 取引先が自社になる:→ 書類種別ごとに「相手方はどちら側か」を定義。受領書類かどうかを最初に確認。
  • 情報が欠落している:→ 日付不明は 00000000、金額不明は 0円、取引先不明は 不明先 として生成し、必ず手動修正を促す。空欄で止めるより「要確認マーカー付きで前に進める」方が運用が回ります。

まとめ

電帳法のファイル名要件は、突き詰めると「日付・取引先・金額をファイル名に規則的に入れる」だけです。難しいのはルールそのものより、毎月ブレずに同じ品質で回し続けること。だからこそ、判断ロジックごとスキルとして固定する価値があります。

「制度要件 × 自動化」は地味ですが、現場の工数を確実に削り、監査対応のスピードも上げてくれる領域です。

次回は、この手の社内ツールを Slack / Notion / Google Workspace 横断で1つのエージェントから操作する(MCP連携) 構成について書く予定です。


※本記事のファイル名規則・コードは一般化した実装例です。電帳法の適用要件は事業規模や取引形態によって異なり、税制改正もあります。実際の運用は最新の国税庁資料および顧問税理士にご確認ください。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?