case_625 物流・倉庫

実地棚卸のデータ分析をClaude Codeで自動化|差異の理由分類

実地棚卸のデータ分析をClaude Codeで自動化|差異の理由分類

実地棚卸のデータ分析(差異分析)をClaude Codeで仕組み化。カウント表と在庫台帳の突合、差異理由コードの機械分類、高額差異の優先調査リスト、棚卸前チェックリスト生成をコード例つきで解説。計算はコードに固定、AIは分類候補の提案だけ。

結論:実地棚卸の差異分析は、カウント表と在庫台帳をExcelで目視突合する仕事ではなく「棚卸カウント表・在庫台帳(理論在庫)・品目マスタ」の3点を入力にした突合パイプラインとして、Claude Codeで構築できます。数量差異の算出と金額換算は決定的なコードに固定し、AIに任せるのは品目名・ロケーション表記の揺らぎ吸収、差異理由コードの分類候補提案、調査メモの下書きまでです。在庫の評価額に関する会計判断(評価損の計上可否など)はAIに任せません。

  • 要点1:棚卸差異の調査が終わらない最大の原因は、差異の「発見」ではなく「仕分け」——数百行の差異リストを、計上漏れ・出庫未処理・破損・場所違いといった理由別に人手で振り分ける作業に時間が溶ける。理由コードの分類ルールを外部ファイル化し、AIに一次分類させるのが実装の核になる
  • 要点2:差異の数量計算・金額換算・高額順の並べ替えはただの算数であり、AIに計算させる必要はない。Pythonの決定的なコードに固定し、Claude Codeには「この差異はどの理由コードに該当しそうか」という候補提案と根拠の言語化だけを任せる
  • 要点3:成果物を「高額差異の優先調査リスト」まで含めて設計すると、棚卸が単なる数合わせから在庫管理プロセスの改善材料に変わる。全差異の羅列ではなく、金額インパクトの大きい品目だけを調査担当・理由候補つきで出す

対象読者:製造・物流・小売の在庫管理部門とAI活用を進める開発者・PM。実地棚卸のたびに差異調査で残業が発生している会社

今日やること:前回の棚卸の差異リストを開き、「差異の理由が最後まで特定できなかった行」が何割あったか数えてみる。それがそのまま自動化で取り返せる調査時間の目安になります

先に断っておくと、本記事は複数の支援経験をもとに一般化した実装パターン解説です。登場する数値や工数はいずれも想定モデルケースの目安で、特定企業の実測値ではありません。効果は品目数・拠点数・在庫管理システムの成熟度によって大きく変わります。

8月はお盆前後の生産・出荷の谷間に合わせて実地棚卸を組む会社が多い時期です。バックオフィスのAI活用支援で在庫管理の方と話していると、「カウント自体は1日で終わるのに、差異の調査で1週間かかる」という声をよく聞きます。棚卸表と台帳の数字が合わない行が数百件出て、1件ずつ入出庫履歴を遡り、現場に聞き取りに行き、締め切りに追われて「原因不明・台帳修正」で処理した行が翌回もまた差異になる——この繰り返しです。

この記事では、実地棚卸の差異分析を「毎回ゼロから調べる調査」から「理由コードを育てながら回すパイプライン」に置き換える実装を、コードと運用の両面から解説します。入金消込の差異分類月次の予実突合と同じ「判定はコードに固定し、AIは揺らぎの吸収と分類を担う」パターンの物理在庫版です。なお、同じ「棚卸」でもSaaSアカウントの棚卸はIT資産の話で、突合対象がAPIで取れる分だけ設計が異なります。本記事は倉庫や店舗に現物がある物理在庫に特化します。

なぜ棚卸差異の調査は終わらないのか——「数える」より「仕分ける」が重い

実地棚卸の作業は大きく「カウント」と「差異調査」に分かれます。カウントは人手と時間をかければ確実に終わる作業です。終わらないのは差異調査のほうで、原因は差異リストの1行1行に対して「なぜ合わないのか」を人が推理しなければならない点にあります。典型的な差異理由は次の4系統に集約されます。

理由コード 内容 典型的な痕跡
計上漏れ 入庫・仕入の実物は届いているが台帳への計上が漏れている 現物が台帳より多い。直近の入荷予定・検収待ちに該当品目がある
出庫未処理 出荷・払出は済んでいるが台帳の出庫処理が漏れている 現物が台帳より少ない。出荷指示や受注履歴に未消込の該当品目がある
破損・廃棄未処理 破損や期限切れで実質使えないが廃棄処理されず台帳に残っている 現物が少ない、または不良品置き場に現物がある。廃棄記録が空
場所違い 数量は合っているがロケーション(棚番・倉庫)が台帳と違う ある棚でマイナス差異、別の棚で同品目のプラス差異が同時に出る

重要なのは、この4系統のうち「場所違い」と「計上漏れ・出庫未処理の一部」は、入出庫履歴と突合すれば機械的に候補を絞れることです。それなのに多くの現場では、差異リストがただの数量差の羅列になっていて、痕跡データとの突合を人が1件ずつやっている。ここが自動化の主戦場になります。

実装の全体像——正規化・突合・分類・優先度づけの4層

パイプラインは4層に分けます。層を分ける理由は、揺らぎを吸収してよい場所(正規化・分類候補)と、絶対に揺らいではいけない場所(数量突合・金額換算)を分離するためです。

  1. 正規化層 — 現場で記入された棚卸カウント表(Excelや紙のOCR結果)と、在庫管理システムから出力した在庫台帳CSVを、同じ列構造の中間データ(品目コード・ロケーション・数量)に変換する。品目名の表記揺れ・単位の不一致(ケースとバラ等)もここで吸収する
  2. 突合層 — 品目×ロケーションごとに実地数量と理論数量の差異を計算し、品目マスタの単価を掛けて金額換算する。ここは決定的なPythonコードのみで、AIは介在させない
  3. 分類層 — 差異行ごとに、入出庫履歴・出荷指示・廃棄記録という痕跡データと突合し、理由コードの候補にフラグを立てる。機械的に絞れない行だけをClaude Codeに渡し、分類候補と根拠の下書きを出させる
  4. 優先度層 — 差異金額の絶対値で並べ、調査リソースを配分する優先調査リストを出力する。少額多数の差異は理由コード別に集計だけして、個別調査の対象から外す判断材料にする

Claude Codeの使いどころは3つです。1つ目は、この4層のコード自体を対話しながら書かせること。2つ目は、機械分類で絞れなかった差異行への理由候補の提案。3つ目は、棚卸の事前チェックリスト生成——前回差異の多かった品目や未処理伝票の残っている品目を洗い出し、カウント当日の混乱を減らす使い方です。これは後半で扱います。

実装コード例1:棚卸カウント表と在庫台帳を正規化する

まず入力を揃えます。在庫管理システムの台帳エクスポートは製品によって列構成が異なり、カウント表は現場の記入フォーマットに依存するため、ここでは「品目コード・ロケーション・数量の3列に落とせればよい」という方針だけをコードに固定します。自社のレイアウトへの適合は、実際のファイルをClaude Codeに見せて調整するのが早いです。

# normalize.py — カウント表と在庫台帳を共通形式に正規化する
import pandas as pd
import unicodedata

def clean_text(s: str) -> str:
    """品目名・棚番の表記を統一する(NFKC正規化+空白除去)"""
    return unicodedata.normalize("NFKC", str(s)).replace(" ", "").strip()

def load_count_sheet(path: str) -> pd.DataFrame:
    """棚卸カウント表Excel → (品目コード, ロケーション, 実地数量)"""
    df = pd.read_excel(path, sheet_name="カウント表", dtype={"品目コード": str})
    df = df.rename(columns={"品目コード": "item", "棚番": "loc", "実数": "counted"})
    df["item"] = df["item"].map(clean_text)
    df["loc"] = df["loc"].map(clean_text)
    df["counted"] = pd.to_numeric(df["counted"], errors="coerce")
    bad = df[df["counted"].isna()]
    if len(bad) > 0:
        raise ValueError(f"数量が読めない行が{len(bad)}件あります: {bad['item'].tolist()[:5]}")
    # 同一品目×同一棚番の二重記入は合算せず停止(二重カウントの検出)
    dup = df[df.duplicated(subset=["item", "loc"], keep=False)]
    if len(dup) > 0:
        raise ValueError(f"同一品目×棚番の重複記入: {dup[['item','loc']].to_dict('records')[:5]}")
    return df[["item", "loc", "counted"]]

def load_ledger(path: str) -> pd.DataFrame:
    """在庫台帳CSV → (品目コード, ロケーション, 理論数量)"""
    df = pd.read_csv(path, encoding="utf-8-sig", dtype={"品目コード": str})
    df = df.rename(columns={"品目コード": "item", "ロケーション": "loc", "在庫数": "book_qty"})
    df["item"] = df["item"].map(clean_text)
    df["loc"] = df["loc"].map(clean_text)
    return df[["item", "loc", "book_qty"]]

ポイントは2つです。第一に、数量が数値として読めない行や二重記入は黙って補正せず停止させること。カウント表の記入ミスは正規化層で握りつぶすと、後工程の差異がすべて疑わしくなります。第二に、品目コードを文字列として読むこと。先頭ゼロ落ち(「00123」が「123」になる)は棚卸突合の古典的な事故で、dtype={"品目コード": str}の1行が数十件の偽差異を防ぎます。

実装コード例2:突合と差異の金額換算

突合層は完全に決定的なコードです。品目×ロケーションで外部結合し、どちらか一方にしか存在しない行も差異として拾います。

# reconcile.py — 実地と台帳の突合、金額換算
import pandas as pd

def reconcile(counted: pd.DataFrame, ledger: pd.DataFrame,
              master: pd.DataFrame) -> pd.DataFrame:
    """品目×ロケーションで突合し、差異数量と差異金額を算出する"""
    m = counted.merge(ledger, on=["item", "loc"], how="outer")
    m["counted"] = m["counted"].fillna(0)      # 台帳にあるが現物ゼロ
    m["book_qty"] = m["book_qty"].fillna(0)    # 現物があるが台帳にない
    m["diff_qty"] = m["counted"] - m["book_qty"]
    m = m.merge(master[["item", "item_name", "unit_cost"]], on="item", how="left")
    unknown = m[m["unit_cost"].isna()]
    if len(unknown) > 0:
        raise ValueError(f"品目マスタに単価がない品目: {unknown['item'].unique().tolist()[:10]}")
    m["diff_amount"] = m["diff_qty"] * m["unit_cost"]
    return m[m["diff_qty"] != 0].sort_values("diff_amount", key=abs, ascending=False)

ここで使う単価は、あくまで差異の調査優先度を決めるための目安です。棚卸差異を会計上どう処理するか——棚卸減耗損や商品評価損をいくら計上するか、評価方法をどうするか——は税務・会計の判断であり、このパイプラインの守備範囲外です。決算に反映する数字は必ず経理部門と顧問税理士・会計士の判断を通してください。AIはその判断の材料となる差異リストを整えるところまでを担います。

実装コード例3:差異理由コードの機械分類——AIに渡す前に絞る

分類層の設計思想は「機械で判定できるものは機械で、残りだけAIへ」です。場所違いの検出は入出庫履歴すら不要で、突合結果だけから判定できます。

# classify.py — 理由コードの機械分類(一次判定)
import pandas as pd

def flag_location_mismatch(diff: pd.DataFrame) -> pd.DataFrame:
    """同一品目でプラス差異とマイナス差異が別ロケーションに同時発生
    → 数量が相殺されるなら「場所違い」候補"""
    g = diff.groupby("item")["diff_qty"].agg(["sum", "count"])
    candidates = g[(g["sum"] == 0) & (g["count"] >= 2)].index
    diff.loc[diff["item"].isin(candidates), "reason_code"] = "場所違い候補"
    return diff

def flag_pending_transactions(diff: pd.DataFrame,
                              pending_in: pd.DataFrame,
                              pending_out: pd.DataFrame) -> pd.DataFrame:
    """未処理伝票との突合。
    プラス差異×検収待ち入荷あり → 計上漏れ候補
    マイナス差異×未消込の出荷指示あり → 出庫未処理候補"""
    plus = (diff["diff_qty"] > 0) & diff["item"].isin(pending_in["item"])
    minus = (diff["diff_qty"] < 0) & diff["item"].isin(pending_out["item"])
    diff.loc[plus & diff["reason_code"].isna(), "reason_code"] = "計上漏れ候補"
    diff.loc[minus & diff["reason_code"].isna(), "reason_code"] = "出庫未処理候補"
    return diff

この2つの関数だけで、想定モデルケース(数百行規模の差異リスト)では相当数の行に一次判定がつきます。残った「reason_codeが空の行」だけをClaude Codeに渡し、品目の特性(期限の有無・壊れやすさ・過去の差異履歴)から理由候補と調査の当たりを付けさせます。全行をいきなりAIに投げないのは、機械判定できる行にAIの推測を混ぜると、確実な判定と推測の区別がリストから消えてしまうからです。機械判定と AI提案は必ず別の列に分けて持ちます

実装コード例4:高額差異の優先調査リストを生成する

優先度層は、調査時間という有限のリソースをどこに配分するかを決める層です。差異金額の絶対値で降順に並べ、上位を調査対象、下位を集計のみに切り分けます。

# prioritize.py — 優先調査リストの生成
def build_priority_list(diff: pd.DataFrame,
                        investigate_threshold: int = 50000,
                        top_n: int = 30) -> dict:
    """閾値以上 or 金額上位N件を個別調査対象に、残りは理由コード別集計に"""
    diff["abs_amount"] = diff["diff_amount"].abs()
    hot = diff[diff["abs_amount"] >= investigate_threshold].head(top_n)
    rest = diff[~diff.index.isin(hot.index)]
    summary = rest.groupby("reason_code", dropna=False)["diff_amount"].agg(["count", "sum"])
    return {"priority": hot, "summary": summary}

閾値の5万円は例であり、自社の在庫単価の分布に合わせて決めます。大事なのは「全件を同じ濃度で調査しない」と最初に決めておくことです。少額差異を1件ずつ追う調査は、投入時間に対して得られる改善が小さく、締め切りだけを圧迫します。少額差異は理由コード別の集計値として傾向を見る——たとえば「出庫未処理候補が毎回同じ部署で多発している」といったプロセス改善のシグナルとして使うほうが実りがあります。

棚卸前の事前チェックリスト生成——差異を「起きる前」に減らす

差異分析のパイプラインが一度できると、同じデータで棚卸の準備を自動化できます。カウント当日に差異の種になるものを、前週のうちに洗い出しておく使い方です。

  1. 未処理伝票の残高一覧 — 検収待ちの入荷、未消込の出荷指示、承認待ちの廃棄申請を品目別に一覧化する。カウント前に処理を締め切れば、その分の差異は発生自体が消える
  2. 前回差異品目の再掲 — 前回の棚卸で差異が出た品目・棚番のリスト。同じ場所で繰り返す差異は保管ルールの問題であることが多く、カウント時に重点確認の印を付けられる
  3. マスタ不備の検出 — 単価未設定・単位不明・ロケーション未登録の品目を事前に検出する。当日に発覚すると突合が止まる系のエラーを前倒しで潰す

この3点をまとめたMarkdownのチェックリストをClaude Codeに生成させ、棚卸1週間前に在庫管理チームへ配る運用にすると、当日の差異件数そのものが減っていきます。差異分析の高度化より、差異の発生源を減らすほうが効くというのは、支援の現場で繰り返し見る構図です。

Claude Codeへの指示プロンプト集——構築と棚卸当日で使う5本

実際の構築・運用で使うプロンプトを5本示します。いずれも、そのまま貼って自社の状況に合わせて書き換えてください。

プロンプト1:パイプラインの初期構築

この2つのファイルを見てください。
- tanaoroshi_count.xlsx(棚卸カウント表・現場記入)
- zaiko_ledger.csv(在庫管理システムの台帳エクスポート)
両者を品目コード×ロケーションで突合し、差異数量を出すPythonスクリプトを書いてください。
要件:
- 数量が数値として読めない行、同一品目×棚番の重複記入は補正せずエラーで停止
- 品目コードは文字列として読む(先頭ゼロを保持)
- どちらか一方にしか存在しない行も差異として出力
不足している情報があれば、最初に質問してから作業を開始してください。

プロンプト2:理由コード分類ルールの設計

差異リスト(diff.csv)と、未処理伝票一覧(pending.csv)を渡します。
「場所違い」「計上漏れ」「出庫未処理」「破損・廃棄未処理」の4系統について、
機械的に判定できる条件をコードに落としてください。
機械判定できない行はreason_codeを空のまま残し、推測で埋めないでください。
仮定した点は必ず"仮定"と明記してください。

プロンプト3:残存差異への理由候補の提案

機械分類で理由が付かなかった差異行を渡します。
各行について、理由コードの候補(複数可)と、そう考える根拠、
調査するなら誰に何を確認すべきかを表形式で出してください。
これは調査の当たり付けであり確定分類ではないので、
出力の列名は「AI候補」「AI根拠」とし、機械判定の列とは分けてください。
数字と固有名詞は、根拠(出典/計算式)を添えてください。

プロンプト4:棚卸前チェックリストの生成

台帳・未処理伝票・前回差異リストの3ファイルから、
棚卸1週間前に在庫管理チームへ配る事前チェックリストをMarkdownで作ってください。
構成: ①締め切るべき未処理伝票の一覧 ②前回差異品目の重点確認リスト
③単価・ロケーション未設定などマスタ不備の一覧
各項目に担当者記入欄と完了チェックボックスを付けてください。

プロンプト5:差異報告書の下書き

確定した差異リスト(理由コード入り)から、経理部門向けの差異報告書の
下書きをMarkdownで作ってください。
構成: 差異の総額と件数、理由コード別の内訳表、高額差異上位10件の明細と調査結果。
会計処理(棚卸減耗損・評価損の計上判断)には言及せず、
「会計処理は経理部門・顧問税理士の判断による」と明記してください。

運用の3フェーズ——最初の1回は「並走」で回す

Phase 1(初回の棚卸・並走):従来の手作業調査とパイプラインを並走させ、機械分類の的中率を人の調査結果で検証する。ここで分類条件の穴(自社特有の差異パターン)が見つかります。

Phase 2(2回目・主従逆転):パイプラインの出力を正とし、人は優先調査リストの上位だけを追う。理由コードの体系を自社版に拡張する——たとえば「委託先預け在庫」「サンプル出庫」など、自社の商流に固有のコードを足していきます。

Phase 3(3回目以降・前倒し):事前チェックリストの運用を定着させ、差異の発生源対策に軸足を移す。棚卸のたびに差異リストと理由コードの履歴が蓄積されるので、循環棚卸(サイクルカウント)の対象選定にもデータが使えるようになります。

【要注意】よくある失敗パターンと回避策

❌ 失敗1:差異の分類までAIに一括で任せる
差異リスト全行をそのままAIに渡して理由を推測させると、確からしい文章の分類理由が並び、機械的に確定できたはずの行まで「推測」で上書きされます。
⭕ 回避策:機械判定できる条件(場所違いの相殺検出・未処理伝票との突合)を先にコードへ固定し、AIには残った行の「候補提案」だけを別列で出させる。確定と推測をリスト上で混ぜない。

❌ 失敗2:カウント表の記入ミスをコードで黙って補正する
「数量が空欄なら0扱い」「重複記入は合算」のような気の利いた補正は、二重カウントや記入漏れという現場の問題を見えなくします。
⭕ 回避策:正規化層は不正な入力で停止させ、エラーメッセージに行番号と品目を出す。カウント表の品質はコードでなく記入ルールの改善で上げる。

❌ 失敗3:評価額の会計判断までパイプラインに組み込む
差異金額の算出と、棚卸減耗損・評価損としていくら計上するかは別の問題です。単価マスタ×差異数量の目安金額を、そのまま決算数値としてレポートに載せると誤解の元になります。
⭕ 回避策:レポートには「調査優先度算定用の概算」と明記し、会計処理は経理部門と顧問税理士・会計士の判断に渡す。在庫の評価に関わる判断はAIに任せない。

❌ 失敗4:初回から全品目・全拠点で導入する
拠点ごとにカウント表の様式も台帳の出力形式も違うことが多く、初回から全拠点対応のパーサを書くと正規化層が複雑化して破綻します。
⭕ 回避策:差異金額の大きい1拠点(または1カテゴリ)で始め、正規化層の変換ルールが安定してから横展開する。様式の統一はパイプライン導入と並行して進める。

よくある質問

Q. WMS(倉庫管理システム)に棚卸機能があるのに、なぜ外部で突合するのですか?

WMSの棚卸機能は、数量差異の検出までは担ってくれます。外部パイプラインが効くのはその先——理由コードの分類、未処理伝票との突合、高額差異の優先度づけ、棚卸前チェックリストの生成といった「調査と準備」の工程です。また、カウント表がExcelや紙で運用されている現場、複数システムに在庫が分かれている会社では、突合そのものから外部で組む価値があります。

Q. ハンディターミナルでカウントしていれば、この仕組みは不要ですか?

ハンディ導入で入力ミス系の差異は減りますが、計上漏れ・出庫未処理・破損未処理といった業務プロセス起因の差異はカウント精度と無関係に発生します。むしろカウントデータが正確になる分、差異リストの信頼性が上がり、理由分類の自動化が効きやすくなります。

Q. 差異の理由分類をAIに任せて、間違った分類をされるリスクは?

本記事の設計では、AIの出力は「候補」と「調査の当たり」に限定し、確定分類は人が行います。機械判定できる行はコードで確定させ、AIの提案は別列に分けるので、どこまでが確実でどこからが推測かはリスト上で常に区別できます。AIの分類候補を無検証で確定させる運用にはしないでください。

Q. 循環棚卸(サイクルカウント)にも使えますか?

使えます。突合・分類のパイプラインは一斉棚卸と同じで、入力が「全品目のカウント表」から「当日対象品目のカウント表」に変わるだけです。むしろ差異履歴が蓄積されると、差異の出やすい品目を高頻度でカウントする対象選定にデータが使えるため、循環棚卸との相性は良好です。

Q. 導入にはどれくらいの期間がかかりますか?

想定モデルケース(1拠点・数千品目・在庫システム1系統)で、初期構築に1〜2週間、初回の並走を経て運用が安定するまで棚卸2〜3回分が目安です。最大の変数はカウント表の様式の揺れと未処理伝票データの取りやすさで、コードを書く時間より「理由コードの体系を自社の商流に合わせて確定させる」議論のほうが長くなる傾向があります。

まとめ:今日から始める3つのアクション

  1. 今日やること:前回の棚卸差異リストを開き、「原因不明のまま台帳修正した行」の件数と金額を数える。それが自動化で取り返せる調査の規模感です
  2. 今週中:在庫台帳のCSVエクスポートと、カウント表のExcelをClaude Codeに見せて、突合スクリプトの初版を作らせる。プロンプト1をそのまま使えます
  3. 次回の棚卸まで:未処理伝票一覧から棚卸前チェックリストを生成し、カウント1週間前にチームへ配る。差異を「分析する」前に「減らす」ほうが、最初の効果を実感しやすいはずです

実地棚卸の差異分析は、入金消込の差異分類予実突合と同じ「突合はコードに固定し、揺らぎの吸収と分類候補にAIを置く」パターンの一員です。金額系の突合と違うのは、現物という物理世界の痕跡——場所違い・破損・未処理伝票——が分類の手がかりになること。この構造を一度コードにすれば、棚卸は毎回ゼロから推理する調査ではなく、履歴を育てながら精度が上がる仕組みになります。


著者:佐藤傑(さとう・すぐる)
株式会社Uravation代表取締役。X(@SuguruKun_ai)フォロワー約10万人。100社以上の企業向けAI研修・導入支援。著書『AIエージェント仕事術』(SBクリエイティブ)。

参考・出典

ご質問・ご相談は お問い合わせフォーム からお気軽にどうぞ。

関連記事: 取引先マスタの名寄せ・重複統合をClaude Codeで仕組み化する実装パターン

Next Step

この事例を、自社の業務に置き換える。

対象業務、利用データ、評価基準、社内展開の順番まで整理すると、AI開発ツール導入の失敗を減らせます。

導入を相談する

チームで学ぶなら: Claude Code 法人研修(2日間ハンズオン) / 1人で習得するなら: 個別指導(週1マンツーマン)