バクラクの新規取引先チェックをAIに任せたら、合併で消えた会社を見つけてきた

読了 約9分 たび

経理には「新規の取引先、この会社ほんとに実在する?商号合ってる?インボイス番号は正しい?」を確認する地味な作業がある。国税庁のサイトで番号を一件ずつ引く、あれだ。これをAIに“チェック担当”として任せてみた。バクラクの申請を渡すと、AIが国税庁に照合してOK/要確認を返してくる。やってみたら、人が見落としがちな“消えた会社”まで拾ってきた

たびたび

丸投げで放置、じゃない。AIに担当させて、自分は最後の判断だけやる。やってみたら想像以上に実用だった。

そもそも、何を確認する作業か

新規取引先の登録申請が来るたび、確認するのはこの4点——実在するか/商号は正しいか/インボイス登録番号は整合するか/法人番号は妥当か。単純だが件数があり、合併で閉鎖された番号のような落とし穴は目視だと普通に見逃す。

AIに「チェック担当」として渡してみた

やったことはシンプル。AIに申請とツール(API)を渡して、こう動かす。まずバクラク側から対象の申請を取ってくる(ここはベンダーのAPIなので擬似コード)。

# バクラクAPIで「申請中の新規取引先申請」を取得し、
# 各申請から 法人番号・取引先名・インボイス番号 を取り出す
applications = bakuraku.fetch_requests(status="IN_PROGRESS", form="新規取引先申請")

次が本題。法人番号は“それっぽい13桁”でも、チェックディジットで真偽が分かる。ここは国が公開している計算式なので、実コードを載せる。

def validate_corporate_number(number: str) -> bool:
    """法人番号13桁のチェックディジットを検証する。"""
    if not number or len(number) != 13 or not number.isdigit():
        return False
    check_digit = int(number[0])
    base = number[1:]                      # 下位12桁が基礎番号
    total = 0
    for i, ch in enumerate(reversed(base)):
        weight = 2 if i % 2 == 1 else 1    # 右から 奇数桁=1, 偶数桁=2
        total += int(ch) * weight
    return (9 - (total % 9)) == check_digit

そして実在・商号・“閉鎖”は、国税庁の公式Web-APIに当てる。AIが数字を捏造する余地がないのは、ここで一次情報に照合しているからだ。

import csv, requests

NTA_API = "https://api.houjin-bangou.nta.go.jp"

def nta_lookup(numbers: list[str], app_id: str, version: str = "4") -> dict:
    """国税庁 法人番号Web-APIで 実在・商号・閉鎖 を照会する。"""
    resp = requests.get(
        f"{NTA_API}/{version}/num",
        params={
            "id": app_id,                 # 無料発行のアプリID
            "number": ",".join(numbers),  # 最大10件まとめて
            "type": "02",                 # CSV(Unicode)で受け取る
            "history": "0",               # 最新の登記のみ
        },
    )
    resp.raise_for_status()
    records = {}
    for row in csv.reader(resp.content.decode("utf-8").splitlines()):
        if len(row) <= 20:
            continue                      # ヘッダー行などをskip
        records[row[1]] = {               # row[1] = 法人番号
            "name":      row[6],          # 商号
            "closeDate": row[18],         # 閉鎖年月日(合併・解散など)
            "successor": row[20],         # 承継先の法人番号
        }
    return records

最後に、結果を「OK」と「要確認」に振り分ける。人が見るのは要確認だけだ。

issues = []
if not validate_corporate_number(corp):
    issues.append("法人番号CD不正")
if invoice and invoice != "T" + corp:
    issues.append("インボイス番号不整合")
if record is None:
    issues.append("国税庁に該当なし")
elif record["closeDate"]:                       # ← ここが効いた
    issues.append("閉鎖/合併(承継先: " + (record["successor"] or "不明") + ")")
elif normalize(client_name) != normalize(record["name"]):
    issues.append("商号不一致")

verdict = "要確認" if issues else "OK"

やってみて分かったこと

数字の検算も国税庁照合も、AIが“道具”(計算式・公式API)を使って機械的にやっている。AIが法人番号を勝手に作るわけじゃないから、捏造が起きない。

やってみたら、消えた会社を拾ってきた

実際に回したら、ある申請の法人番号が合併ですでに閉鎖済みのものだった。存続する会社は別の番号を持っている。名前は合っているので目視なら通しかねないところを、閉鎖年月日と承継先番号を見て自動で「要確認」に落とした。差し戻したのは自分だ。

APIキーが無くても、AIがWebで代替照合する

実はこのチェック、国税庁の公式Web-APIには無料のアプリIDが要る。発行が間に合わないこともある。でも止まらない。APIが使えないときは、AIに法人番号データベース(houjin.info など)で一件ずつ実在・商号・閉鎖を調べさせる。やることは人間の手作業と同じ——番号で引いて、登記の商号と申請名を見比べ、閉鎖していないか確認する。

しかもこの手順はプロジェクトのREADMEに書いてあって、AIはそれを読んでそのまま実行する。人に手順書を渡して仕事を任せるのと同じだ。要点はこれだけ。

  • houjin.info で登記の商号・所在地・登記状態(現存/閉鎖)・承継先番号を読む
  • houjin.jp でもクロスチェック(※旧住所が出ることがある→最新は houjin.info を優先)
  • 申請名・住所と照合し、商号不一致/閉鎖/所在地相違なら「要確認」
  • 番号はAIが生成しない/参照URLを根拠に残す/承認は人

やってみて分かったこと(代替手段)

APIが理想だが、無くてもAIが“人と同じ手作業”を代行できる。しかも手順をREADMEに書いておけば、AIは毎回それを読んで同じ品質で動く。“道具が無いから止まる”が減る。

どこまで任せて、どこは自分がやるか

任せたのは「全件照合して、要確認を上げてくる」ところまで。承認・差し戻しは自分

役割分担

作業(照合・振り分け)はAI。判断(承認・差し戻し)と責任は人。AIは“優秀な確認担当”、自分は“最終承認者”。

おまけ:このコード、全部AIに書かせた

最後に正直に言うと、ここに載せたコードは自分でゴリゴリ書いていない。要件(何を確認したいか)をAIに渡して、実装はAI、レビューと検証は自分、という分担だ。経理がコードを書けなくても、AIを相棒にすればこの手の道具は作れる。

まとめ

バクラクの新規取引先チェックは「全件照合して要確認だけ上げるAI担当」になり、閉鎖済みの番号も自動検知。任せるのは作業、判断は自分。実装すらAIに任せられる時代だ。

次は「レシートと明細の照合」をAIにやらせた話を書く(画像をAIに読ませる、より“AIらしい”処理だ)。