ERP

三点照合とは:発注・検収・請求の突合ロジックと許容差・分納・保留をPythonとSQLで実装

三点照合とは:発注・検収・請求の突合ロジックと許容差・分納・保留をPythonとSQLで実装

三点照合とは、発注・検収・請求の3つのデータを明細単位で突き合わせ、内容が一致した請求だけを支払に回す統制です。発注書と請求書の2つだけを比べる二点照合では、物が届いたかどうかを確かめられません。検収の記録を3つ目の根拠に加えることで、未着品への支払、単価の上乗せ、同じ納品への二重請求を支払の前に止められます。この記事では、照合する書類と項目、分納と分割請求を扱う請求可能数の計算、単価と金額の許容差を分けた照合関数、インボイスの端数処理で生じる1円差、保留と差戻しの状態遷移、支払期日の監視までを、PythonとSQLで示します。

まとめ:三点照合の実装で先に決める照合単位・許容差・保留の解除条件

三点照合を作るときに決めることは3つです。1つ目は照合の単位で、請求書の合計ではなく発注明細ごとに、検収済みの累計数量と請求済みの累計数量を比べます。この単位にしておけば、分納や分割請求が混ざっても、判定に使う計算式は同じです。2つ目は許容差で、単価の上振れ率と行金額の円単位の差を別々に持ちます。3つ目は保留の解除条件で、誰がどの差異を承認すれば支払に回せるかを状態として記録します。

実装でつまずきやすいのは、検収前に届く請求書と、消費税の端数処理による1円の差です。前者は保留にして検収の登録時に再照合し、後者は税抜の行金額で照合して税額は請求書単位で確かめます。保留が長引くと、受領日から60日の支払期日を社内の都合で過ぎる恐れがあるため、滞留の監視も照合と同時に作ってください。

三点照合の定義と二点照合・四点照合との違い、照合する書類と項目

三点照合は英語では3-way matching、会計や監査の文脈では三方照合とも呼ばれます。呼び名が違っても、突き合わせる対象と目的は同じです。

三点照合で突き合わせる発注・検収・請求の3データと照合する5項目

照合の対象は、自社が出した発注書(発注データ)、納品物を受け入れた記録(納品書と検収記録)、仕入先から届いた請求書の3つです。紙の書類どうしを並べる作業に見えますが、システムで行う場合はそれぞれの明細行を突き合わせます。照合する項目は次の5つが基本になります。

  • 品目コード:発注した品目と請求された品目が同じか
  • 数量:請求数量が検収済みの数量を超えていないか
  • 単価:請求単価が発注単価から上振れしていないか
  • 金額:数量と単価を掛けた額と請求金額が合うか
  • 支払条件:締め日と支払サイトが発注時の取り決めと同じか

実務ではまず数量と単価の2つを押さえれば、過払いの大半は止まります。品目コードは発注明細と請求明細を紐づけた時点で確定し、支払条件は仕入先マスタから引くため、照合の画面で人が見るのは数量と単価の差異が中心です。三方照合を入れると止まる誤りの種類と、債務管理の業務全体の中での位置づけは債務管理の業務設計と三方照合を解説した記事で整理しています。

二点照合・三点照合・四点照合の違いと、品目ごとに方式を切り替える設計

方式 突き合わせる対象 向く品目
二点照合 発注と請求 役務・定額の契約
三点照合 発注と検収と請求 部材・商品・設備
四点照合 三点に受入検査を追加 品質検査がある部材

全品目を同じ方式にする必要はありません。Microsoft Dynamics 365 Financeの公式文書Three-way matching policiesでは、法人全体を二点照合にしたうえで、品目、品目と仕入先の組合せ、個別の発注明細の単位で三点照合へ引き上げる例が示されています。固定資産になる設備だけを三点照合にし、事務用品は二点照合のまま通す、という分け方です。

自社で作る場合も、照合方式を発注明細の列として持たせると、品目マスタや仕入先の既定値から明細ごとに上書きできます。方式を法人単位の設定1つにしてしまうと、検収の記録が無い役務の請求がすべて保留になり、運用が回りません。

三点照合のデータモデル:発注明細・検収明細・請求明細を結ぶ照合の単位

照合の正確さは、判定のコードよりもデータの持ち方で決まります。請求書の合計金額どうしを比べる作りにすると、どの明細がずれているかを後から特定できません。

照合を明細単位で行うための発注明細・検収明細・請求明細の最小の列

3つの明細はいずれも発注明細のIDを持ち、そこを軸に結びます。検収明細と請求明細は発注明細に対して多対1になり、1つの発注明細に検収が2回、請求が2回あっても扱えます。

テーブル 最小の列
発注明細 明細ID・品目・数量・単価・照合方式
検収明細 発注明細ID・数量・受領日・検収日
請求明細 請求書ID・発注明細ID・数量・単価・金額・状態

検収明細に受領日と検収日を別々に持たせているのは、支払期日の起算に使う日が検収の完了日ではないためです。この点は後半の保留の章で扱います。検収の記録をどの書類で残すか、納品書や請求書との役割の違いは検収書の記載項目と納品書・請求書との違いを参照してください。

分納と分割請求を検収済み累計と請求済み累計の差で扱う請求可能数のSQL

分納と分割請求を1つの式で扱う鍵が、発注明細ごとの請求可能数です。検収済みの累計数量から、照合を通った請求の累計数量を引いた数が、次の請求で受け付けてよい上限になります。

-- 発注明細ごとの「あと何個まで請求を受け付けてよいか」
SELECT p.po_line_id,
       p.qty                                            AS ordered,
       COALESCE(r.received, 0)                          AS received,
       COALESCE(i.billed, 0)                            AS billed,
       COALESCE(r.received, 0) - COALESCE(i.billed, 0)  AS billable
  FROM po_line p
  LEFT JOIN (SELECT po_line_id, SUM(qty) AS received
               FROM receipt_line GROUP BY po_line_id) r USING (po_line_id)
  LEFT JOIN (SELECT po_line_id, SUM(qty) AS billed
               FROM invoice_line
              WHERE status IN ('一致','許容内差異','承認済')
              GROUP BY po_line_id) i USING (po_line_id)
 ORDER BY p.po_line_id;

請求済みの集計から保留と差戻しの行を外しているのが要点です。保留中の請求まで数えると、その請求が差し戻された後に正しい請求書が届いても、請求可能数が0のまま受け付けられません。発注数量ではなく検収済み数量を基準にしているため、未着分への請求はこの時点で弾かれます。

三点照合の判定ロジック:数量・単価・金額の許容差をPythonで実装する手順

請求可能数が出せれば、あとは請求明細1行ずつを判定する関数を書くだけです。結果は「一致」「許容内差異」「保留」の3つに分けます。

請求明細1行を一致・許容内差異・保留に振り分ける照合関数の実装例

金額の計算には浮動小数点数ではなくDecimalを使います。12.35円のような単価をfloatで掛けると、2進数の誤差で金額の比較が不安定になるためです。円未満の扱いはPythonのdecimalモジュールの公式文書にあるquantizeと丸めモードで明示します。

from dataclasses import dataclass
from decimal import Decimal, ROUND_DOWN

@dataclass
class Tolerance:
    price_pct: Decimal    # 単価の上振れ許容率(%)
    amount_yen: Decimal   # 行金額の許容差(円)

def match_line(po_price, received_qty, billed_qty, inv_qty, inv_price, inv_amount, tol):
    """請求明細1行を三点照合し、(判定, 理由のリスト) を返す"""
    reasons, within = [], []

    # 数量:検収済み累計から請求済み累計を引いた数を超えて請求していないか
    billable = received_qty - billed_qty
    if inv_qty > billable:
        reasons.append(f"数量超過 請求{inv_qty} 請求可能{billable}")

    # 単価:発注単価からの上振れ率で判定(下振れは支払側に不利がないので通す)
    if inv_price > po_price:
        rate = (inv_price - po_price) / po_price * 100
        if rate > tol.price_pct:
            reasons.append(f"単価超過 {rate:.2f}%")
        else:
            within.append(f"単価差 {rate:.2f}%")

    # 金額:数量×単価との差。1円未満の扱いの違いはここで吸収する
    expected = (inv_qty * inv_price).quantize(Decimal("1"), rounding=ROUND_DOWN)
    diff = abs(inv_amount - expected)
    if diff > tol.amount_yen:
        reasons.append(f"金額差 {diff}円")
    elif diff:
        within.append(f"金額差 {diff}円")

    if reasons:
        return "保留", reasons
    return ("許容内差異", within) if within else ("一致", [])

許容差を単価2%・金額10円にして、発注単価12.5円の明細で試した結果は次の通りです。検収済み1,000個・請求済み600個の状態で、残り400個を単価12.7円で請求されると上振れ1.60%で「許容内差異」、12.8円なら2.40%で「保留」になります。検収済み600個に対して1,000個を請求されたケースは「数量超過 請求1000 請求可能600」で保留です。この判定はPython 3.14.7で実行して確かめました。

理由を文字列のリストで返しているのは、保留の画面にそのまま出して購買担当が差異の中身を読めるようにするためです。判定結果だけを返す作りにすると、担当者は3つの書類を開き直して差異を探すことになります。

単価の許容差と金額の許容差を分けて持つ理由とDynamics 365の設定例

許容差を1種類だけにすると、どちらかの誤りを見逃します。単価の率だけで判定する方式の弱点は、単価は正しいのに数量×単価の計算を誤った請求を通してしまう点です。金額の差だけで判定すると、単価が1円上がった少額の明細は差額が小さいため素通りします。

先に挙げたMicrosoft Learnの文書でも、単価の照合(Net unit price matching)と行金額の照合(Price totals matching)は別の許容差で判定されています。単価の許容差を2%、金額の許容差を10%とした例では、発注単価40.00のワイヤレスマウスが41.00で請求され、2.5%の上振れで単価照合だけが不合格になりました。金額の照合は合格しています。

許容差の初期値は、0から始めて実データで広げる順序を勧めます。最初から広い値を入れると、本来止めるべき差異がどれだけ通っているかを測る手段がなくなるためです。許容内差異として通った件数と金額を月次で集計し、その分布を見て幅を決めます。

消費税の端数処理で明細の合計と請求書の税額が1円ずれる原因と照合単位

インボイス制度のもとでは、請求書に書く消費税額の端数処理は請求書1枚につき税率ごとに1回です。国税庁のインボイスQ&A問57(令和6年4月改訂)は、商品ごとに税額を計算して端数処理し、その合計を記載する方法を認めないと明記しています。

この規則があるため、自社側で発注明細ごとに税額を計算して合計すると、仕入先の請求書の税額と1円単位でずれることがあります。照合は税抜の行金額で行い、税額は請求書単位・税率ごとに「税抜合計×税率」と請求書の記載額の差を見る、という2段に分けるのが安全です。税込の明細金額どうしを比べる作りにすると、正しい請求書が毎月保留に落ちます。

税抜の行金額にも円未満の差は出ます。333個×12.35円は4,112.55円で、自社が切り捨てて4,112円、仕入先が四捨五入して4,113円なら1円の差です。先の関数はこの請求を「金額差 1円」の許容内差異として通しました。端数の処理方法は仕入先マスタに持たせておくと、差異の理由を説明できます。

分納・検収前の請求・発注外の請求行で三点照合が崩れる3つの場面と対処

単純な「1発注・1納品・1請求」なら照合は難しくありません。崩れるのは、この対応が1対1でなくなる場面です。

分納で検収が2回に分かれた発注1,000個を2枚の請求書と照合する計算例

M8ボルト1,000個を単価12.5円で発注し、10月5日に600個、10月19日に400個が届いたとします。仕入先は月末締めで、10月分を2枚の請求書に分けて送ってきました。

  1. 請求書9001の600個・7,500円:検収済み1,000個、請求済み0個で請求可能数は1,000個。数量・単価・金額とも一致して支払に回る。
  2. 請求書9002の400個・単価12.8円・5,120円:請求済みが600個に増え、請求可能数は400個。数量は通るが単価が2.40%上振れして保留になる。
  3. 請求可能数のSQLを流し直すと、発注明細101は検収済み1,000個・請求済み600個・請求可能400個。保留の9002は請求済みに数えない。

この3行目の数字は、上のSQLを架空データでSQLite 3.50.4により実行して確かめた値です。9002が単価を直した請求書に差し替えられれば、残りの400個をそのまま受け付けられます。

検収前に届いた請求書を保留にし、検収登録の時点で自動で再照合する仕組み

月末締めの請求書は、月末に納品された分の検収より先に届くことがよくあります。この請求を即座に差し戻すと仕入先とのやり取りが増えるため、数量超過の保留として置いておき、検収が登録された時点でもう一度照合するのが実務に合います。再照合の実装は、検収明細の登録処理の最後で、同じ発注明細を持つ保留中の請求明細を探してmatch_lineを呼び直す形です。

オープンソースERPのOdooでは、この考え方を採用するかどうかは設定で選ぶ方式です。Odoo 19.0の公式文書「Control policies」によれば、請求の管理方針を受領数量ベース(On received quantities)にすると、受け取った分だけが請求書の下書きに入ります。三点照合を有効にすると請求書に「Should Be Paid」の欄が出て、下書きの数量や単価を変えるとExceptionになります。ただしOdooは差異を検知しても変更を止めず、エラーも出しません。自社で作る場合は、例外を検知した時点で支払の対象から外す処理まで入れてください。

送料・値引き・返品など発注に無い請求行を照合対象から外す条件と処理

請求書には、発注明細に対応しない行が混ざります。送料、運賃の燃料調整額、値引き、前月の返品分の控除などです。これらを発注明細に無理に紐づけると、品目コードが合わずにすべて保留になります。

対処は、請求明細に「付帯費用」「値引き」の区分を持たせ、三点照合の対象から外したうえで別の規則で判定することです。送料は発注時に取り決めた額との二点照合、値引きと返品控除は金額がマイナスであることと元の請求書番号の参照を条件にします。どの区分にも当てはまらない行は、金額の大小にかかわらず保留にします。

不一致時の保留と例外処理:照合状態の遷移と承認付きで支払を解除する流れ

照合の判定よりも手間がかかるのが、保留にした後の扱いです。誰が、どの差異を、どの根拠で通したかが残らないと、監査で説明できません。

一致・許容内差異・保留・承認済・差戻しの5状態と遷移の条件と担当者

状態 入る契機 支払
一致 照合で差異なし 対象
許容内差異 差異が許容差の内側 対象
保留 許容差を超えた差異 対象外
承認済 保留を承認者が承認 対象
差戻し 仕入先へ再発行を依頼 対象外

保留から承認済へ動かせるのは、発注した部署の責任者か購買部門に限ります。請求書を入力した経理担当が自分で承認できる作りにすると、照合を入れた意味がなくなります。承認には理由の入力を必須にし、承認者と日時を請求明細の行に残してください。承認権限と職務分掌の決め方は購買管理規程と内部統制の設計を解説した記事が扱っています。

差戻しは終端の状態にし、再発行された請求書は新しい請求明細として照合し直します。差戻した行を書き換えて照合をやり直す作りは、元の請求内容が履歴から消えるため避けます。

保留の長期化で受領日から60日の支払期日を過ぎる問題と滞留の監視SQL

保留は支払を止める仕組みですが、止めている間も支払期日は進みます。取適法(中小受託取引適正化法)の支払期日は、給付を受領した日から60日以内で定めるのが原則です。起算日は検収の完了日ではなく受領日であり、取適法の対象外の取引にも令和9年4月1日から同じ60日の考え方の告示が施行されます。公正取引委員会の支払告示のリーフレットは、問題となる行為事例として「社内の検収手続の遅延により、給付を受領した日から60日目まで(2か月以内)に代金を支払わないとき」を挙げています。

三点照合の保留は、まさにこの「社内の手続の遅延」になりえます。同じリーフレットは、給付の内容が委託内容と異なるなど受託事業者の責めに帰すべき理由がある場合を「正当な理由」の例に挙げています。納品物の数量不足や仕様違いで止めた保留はこれにあたりえますが、社内の承認待ちで止まっている保留は理由になりません。保留中の明細を、受領日から60日目までの残り日数が短い順に出す監視を毎日回します。

-- 保留・差戻し中の請求明細を、受領日から60日目までの残り日数が短い順に出す(PostgreSQL)
SELECT i.invoice_id,
       i.invoice_line_id,
       i.status,
       MIN(r.received_on)                      AS first_received,
       MIN(r.received_on) + 60                 AS day_60,
       MIN(r.received_on) + 60 - CURRENT_DATE  AS days_left
  FROM invoice_line i
  JOIN receipt_line r ON r.po_line_id = i.po_line_id
 WHERE i.status IN ('保留','差戻し')
 GROUP BY i.invoice_id, i.invoice_line_id, i.status
HAVING MIN(r.received_on) + 60 - CURRENT_DATE <= 14
 ORDER BY days_left;

分納で受領日が複数ある明細は、最も早い受領日を基準にして安全側で警告を出しています。PostgreSQLのdate型は整数を足すと日付になり、日付どうしを引くと日数の整数になるため、この書き方で得られる値は残り日数です。60日の起算日と月末締め翌月末払いの扱いは下請法・取適法の支払期日と60日ルールの起算日を解説した記事に詳しくまとめています。

三点照合を自動化すべき取引と、二点照合や目視確認で足りる取引の判断基準

三点照合の自動化は、検収データを電子で取れることが前提です。作り込みのぶん保守も増えるため、入れる取引と入れない取引を条件で分けます。

三点照合の自動化を入れるべき条件:月の請求明細数・分納の頻度・品目

次のどれかに当てはまるなら、三点照合をシステムで自動化します。月の請求明細が数百行を超え、経理担当が発注データを横に開いて目視で突き合わせている場合。分納や分割請求が日常的にあり、請求可能数を人が暗算している場合。単価が改定される部材や、数量の過不足が起きやすいばら物を扱う場合です。いずれも目視の照合では、二重請求と単価の上乗せが月に数件ずつ素通りします。

パッケージの購買管理やERPの照合機能では、照合方式の切替単位や許容差の持ち方が業務に合わないこともあります。その場合は、照合と保留の部分を個別に作るか、既存の基幹システムと請求書受領の仕組みをつなぐ開発を検討する段階です。発注・検収・請求の明細を1本の流れで持つ購買管理システム開発では、照合の単位と許容差、保留の承認者を要件定義の段階で先に決めます。購買管理システムの機能と選び方の全体像は購買管理システムの機能・5原則と選び方の記事で確認できます。

三点照合が過剰になる取引と、許容差を広げすぎて過払いを見逃す失敗例

役務の契約、月額固定の保守料、サブスクリプションの利用料には三点照合を入れません。検収にあたる記録が作業完了の報告しかなく、その入力を待つために支払が遅れるだけだからです。これらは発注(契約)と請求の二点照合で十分です。月の請求が十数件で、取引先も固定されている小規模な購買も、表計算での目視照合で回ります。

失敗するのは、保留が多すぎて現場が困るという理由で許容差を広げる対応です。単価の許容差を10%にすると、単価改定の連絡が無いままの値上げがすべて許容内差異として通ります。保留が多い原因はたいてい、発注単価の更新漏れか、税込で照合している設計のどちらかです。許容差を広げる前に、この2つを疑ってください。

よくある質問

三点照合の意味や実装について、検索の多い質問に答えます。

三点照合と三方照合、3-way matchingは同じ意味ですか?

同じ統制を指しています。発注・検収(受入)・請求の3つを突き合わせ、一致したものだけを支払う手続きです。会計や監査の文脈では三方照合、購買の現場では三点照合、ERPの設定画面では3-way matchingと表記されることが多く、照合する対象に違いはありません。書類の名前で「発注書・納品書・請求書の3点照合」と説明される場合も、納品書は受け入れの記録として使われており、意味は同じです。

三点照合の検収側には、納品書と検収書のどちらを使いますか?

自社が受け入れを確認した記録を使います。納品書は仕入先が作る書類なので、それだけでは「本当に届いて合格したか」の根拠になりません。システムでは検収明細として自社が登録した受入数量と日付を使い、紙の運用なら納品書に検収印と日付を入れたもの、または自社で作る検収書を使います。受入検査がある部材は、検査に合格した数量を検収数量として登録します。

三点照合の許容差はどのくらいに設定すればよいですか?

一律の正解はありません。単価の上振れ率と行金額の円単位の差を分けて持ち、0から始めて実データで広げるのが安全です。Microsoft Dynamics 365 Financeの公式例では、単価2%・金額10%や、単価8%・金額15%といった値が使われていますが、これは設定方法を示す例です。端数処理の差を吸収する目的なら、金額は数円から数十円の固定額で足ります。

Excelで三点照合を行うことはできますか?

発注・検収・請求の明細をそれぞれ別のシートに出し、発注明細の番号で突き合わせれば可能です。月の請求明細が数十行で、分納がほとんど無い取引なら、十分に運用できる方法です。限界が来るのは分納と分割請求が増えたときで、請求済みの累計を人が管理すると二重請求を見落とします。保留を誰が承認したかの記録も残しにくいため、監査の指摘を受けやすくなります。

役務やソフトウェアの購入でも三点照合は使えますか?

作業の完了報告や成果物の受入記録を検収として登録できれば使えます。ただし多くの場合は、契約と請求を比べる二点照合で十分です。Odooでも、役務の品目は既定で発注数量ベースの請求管理になっています。開発委託のように成果物の受入検査がある契約は三点照合に向き、月額の保守料や利用料のように毎月同じ額が請求される契約は二点照合に向きます。

関連記事

お気に入りに入れた記事の一覧

この記事は以下の記事からリンクされています

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.10.19 テックブログ コミックシーモア不正アクセスで会員情報流出:再開時期・パスワード再設定と運営側のログ点検
  2. 2026.10.07 テックブログ ファインズの不正アクセスと153万件の予約者情報|予約システムを使う店舗の報告と通知の手順
  3. 2026.10.15 テックブログ リゾートトラスト不正アクセス:会員2.6万件・カハラ横浜3.6万件の対象確認と窓口
  4. 2026.10.16 テックブログ バーガーキング情報漏洩どうしたらいい?約1,118万件の対象確認・補償・退会とメールの真偽
  5. 2026.10.15 テックブログ 銀のさら不正アクセス:最大650万件の対象確認と窓口、パスワード変更・退会の手順

RELATED POSTS 関連記事

目次