Three-way matching passes on quantity and fails on price more often than anyone plans for. The quantity is what the receipt proves. The price is what the vendor typed. A bill for the right number of units at the wrong unit cost reconciles cleanly against the goods receipt and posts, and nobody sees it until the margin review.
Matching runs against real purchase orders, receipts, and bills in Odoo. Nothing posts until you approve, and each run is capped at 50 records or $100k.
Purchase order, goods receipt and vendor bill are one flow on one ledger, requisition through to payment, so the three documents sit in the same system rather than either side of a connector. Received quantity is not typed in: for stocked lines it is totalled from completed stock moves, net of returns. Each product carries a control policy that decides whether the amount available to bill is what was ordered or what actually arrived. And a dedicated matching screen lists purchase order lines and vendor bill lines together, keyed on product and vendor, with ordered quantity, billed quantity and untaxed amount on both sides.
The three documents and the figures that let you compare them are already in Odoo, nothing to add, no upgrade tier to reach, no extra fee per user, and the deployment is yours. NetSuite bills three-way matching as part of a paid procurement tier; in Odoo the ordering, receiving and billing that feed the check are already included, and that difference is the product.
The comparison itself is not. Odoo's settings even offer a switch labelled "3-way matching: purchases, receptions and bills", but the piece that would actually enforce it is not in the base system at all. So you get every number the check needs and no check. Someone opens the matching screen, reads two columns, and decides, or does not open it, which is the same thing with worse consequences.
We have not identified a free add-on that closes this, and this page does not imply an install closes it. What closes it is the next block.
It refuses. For each bill it pulls the purchase order, the goods receipt and the invoice and checks they agree before anything is approved for payment. When quantity or unit price falls outside tolerance it does not force the match and does not post. It names which of the three documents disagrees and by how much, holds that bill out of the payment run, records why, and finishes the rest of the batch.
Then it proves it, on camera: approved count against clean-match count, the held bill absent from the payment schedule, totals reconciled. Its own check, not a claim.
This is a real recorded demo run, not a claim of production use at a customer -- del.ai has completed no migrations for a customer yet.
Three-way matching is the check that a vendor bill agrees with two other documents before it is paid: the purchase order, which records what was ordered and at what price, and the goods receipt, which records what physically arrived. A bill passes when all three agree on quantity and on price. The check exists because each document is created by a different party at a different moment, the buyer raises the PO, the warehouse confirms the receipt, the vendor issues the bill, so agreement between them is evidence rather than assertion.
A two-way match compares the bill against the purchase order alone, or against the receipt alone. The receipt is the document that proves quantity, because someone counted what arrived. It says nothing about price. So a bill for the correct number of units at an incorrect unit cost reconciles cleanly against the receipt and posts without objection. The error is not caught by the ledger, which balances perfectly, and surfaces later as a margin variance with no obvious cause.
It should stop and escalate the specific discrepancy, rather than resolve it. The tempting design is the opposite: treat the mismatch as an exception to be processed automatically, so the payment run is not held up. That reasoning optimises for throughput on precisely the transactions that need a human, because a mismatch is either a vendor error, a receiving error or a renegotiated price, and only the last one should be paid. An agent that names which of the three documents disagrees, and by how much, converts a silent posting into a decision someone makes on purpose. NetSuite's accounts payable page states it the other way round: "Invoices don't match purchase orders or receipts? NetSuite simplifies and automates exception processing." Automating the exception is how a wrong price gets paid quickly.
It depends on whether the system holds the numbers or enforces the comparison, and those are separate capabilities that the same phrase covers. Most ledgers that manage purchasing hold everything the check needs: the quantity and price on the order, the quantity confirmed at receipt, and the quantity and price on the bill, usually with a screen that shows the order lines and the bill lines together. Holding the numbers is not the same as blocking a payment when they disagree, that requires a rule about tolerance, a state on the bill that means "not payable yet", and something that refuses to move past it. When evaluating any system, the question worth asking is not "do you support three-way matching" but "show me what happens to a bill that fails it", because the first question is answered by a screen and the second by a gate.