Accounts Payable (AP)
AP is the vendor side of the ledger: what you owe, to whom, when, and whether you should actually pay it.
The process
Purchase Order ──▶ Goods Receipt ──▶ Invoice Receipt ──▶ Approval ──▶ Payment ──▶ Bank Reconciliation
│ │ │
GR/IR Cr GR/IR Dr AP Dr
Stock Dr AP Cr Bank Cr
Three-way match
The core AP control. An invoice is only paid if three documents agree:
| Document | Says |
|---|---|
| Purchase order | What we agreed to buy, at what price |
| Goods receipt | What actually arrived |
| Invoice | What the supplier is charging |
Match on quantity and price, within configured tolerances:
PO: 100 units @ 50.00 = 5,000
GR: 98 units (2 short)
Invoice: 100 units @ 51.00 = 5,100
Quantity variance: 2 units → blocked (invoiced more than received)
Price variance: +1.00/unit = +2% → within 3% tolerance, but absolute 100 > limit 50 → blocked
Tolerance config typically has both a percentage and an absolute limit, and blocks if either is exceeded. Small variances post to a price difference account automatically instead of blocking.
Variants:
- Two-way match — PO + invoice only (services, or where no receipt exists)
- Four-way match — adds the quality inspection result
Blocked invoices sit in a queue until someone releases them or the PO is corrected. “Blocked invoice ageing” is a standard AP KPI.
GR/IR: the account that explains everything
Goods receipt (before invoice arrives):
Inventory Dr 5,000
GR/IR clearing Cr 5,000 ← "we owe someone, invoice not here yet"
Invoice receipt:
GR/IR clearing Dr 5,000 ← clears the above
Accounts payable Cr 5,000 ← now we owe this specific vendor
Why this exists: goods arrive on the 28th, the invoice arrives on the 5th. Without GR/IR, either your inventory or your liability would be wrong at month end. GR/IR bridges the gap so both are right.
GR/IR is the most-analysed clearing account in any ERP. Aged balances mean:
- Debit balance (goods received, no invoice) → accrue the liability at period end
- Credit balance (invoiced, no goods) → possible fraud, or a receipt never posted
Invoice types
| Type | Notes |
|---|---|
| PO invoice | Referenced to a purchase order, three-way matched |
| Non-PO invoice | No PO — utilities, rent, professional fees. Requires coding to GL account + cost object and an approval workflow instead of a match |
| Credit memo | Vendor owes you — returns, overcharges |
| Down payment / advance | Paid before delivery; sits as an asset until cleared against the real invoice |
| Subsequent debit/credit | Adjusts a value without changing quantity (freight billed later, retro price change) |
| Recurring | Rent, subscriptions — template posts monthly |
| Self-billing / ERS | You generate the invoice from the goods receipt; the vendor never sends one. Common in automotive |
| Consignment settlement | Settled on consumption, not on receipt |
Invoice capture and automation
Modern AP is heavily automated:
- Capture — email, EDI, supplier portal, scan
- Extract — OCR + ML/LLM pulls vendor, invoice number, date, amounts, tax, PO number, line items
- Validate — vendor identified, duplicate check, tax arithmetic
- Match — automatic three-way match
- Route — exceptions and non-PO invoices to approvers
- Post — into AP
- Pay — in the payment run
Straight-through-processing rate (invoices posted with zero human touch) is the headline metric; best-in-class is 70–90% for PO invoices.
Duplicate invoice detection deserves care — it’s a real money leak. Check (vendor, invoice_number) exactly, plus fuzzy (vendor, amount, date ±5 days). Standard ERPs have this built in; verify it’s switched on.
Payment run
The batch process that decides what to pay:
Inputs: payment run date, next run date, company codes, payment methods,
vendor selection, cash available
Steps:
1. Select open items due before next run date (or with discount worth taking)
2. Group by vendor + payment method + bank + currency
3. Apply blocks (payment block on vendor, on invoice, on approval)
4. Calculate cash discount if within discount period
5. Check minimum/maximum payment amounts
6. Propose payments ← human reviews and edits the proposal
7. Post payments: Dr AP, Cr Bank clearing; clear the AP open items
8. Generate payment media: bank file (ISO 20022 pain.001, NACHA, BACS, local formats), cheques, remittance advices
9. Send remittance advice to vendors
The payment proposal step is a control: a human reviews before money leaves. Never automate away the proposal review without explicit CFO sign-off.
Cash discount
2/10 net 30 — 2% off if paid within 10 days. The payment run decides whether taking it is worth the cash. Two accounting treatments:
- Gross method — book full amount, record discount as income when taken
- Net method — book net of discount, record lost discount as expense if missed (better management information; the loss is visible)
Withholding tax
Many jurisdictions require the payer to withhold tax and remit it to the government (TDS in India/Bangladesh, backup withholding in the US, WHT across Asia and Africa).
Invoice 100,000, WHT 10%:
Expense Dr 100,000
Accounts payable Cr 90,000 ← what the vendor gets
WHT payable Cr 10,000 ← what you owe the tax office
Complexity: rates vary by vendor type, service type, and threshold; certificates must be issued to vendors; a periodic return is filed. This is heavy localisation — see Localization.
AP controls that matter
| Control | Why |
|---|---|
| Segregation of duties — creating a vendor ≠ approving an invoice ≠ releasing payment | The classic fraud triangle |
| Vendor bank detail change = dual approval + audit | Business Email Compromise is the most common corporate fraud; the fake “our bank details changed” email is the vector |
| Three-way match with tight tolerances | Prevents overbilling |
| Duplicate detection | Prevents double payment |
| Payment proposal review | Last human check before cash leaves |
| Positive pay / bank file hash | Prevents tampering with the payment file after approval |
| Blocked vendors | Sanctions, disputes, tax non-compliance |
Key AP data model
ap_invoice(
id, company_id, vendor_id, invoice_number, invoice_date, posting_date,
currency, gross_amount, tax_amount, net_amount, payment_terms,
baseline_date, due_date, discount_date, discount_amount,
payment_block, payment_method, house_bank,
status, -- PARKED|POSTED|BLOCKED|APPROVED|PAID|REVERSED
UNIQUE(company_id, vendor_id, invoice_number) ← duplicate prevention
)
ap_invoice_line(invoice_id, line_no, po_id, po_line, gl_account, cost_object,
quantity, unit_price, amount, tax_code)
ap_open_item(invoice_id, amount_open, due_date, cleared_by, cleared_at)
That UNIQUE(company_id, vendor_id, invoice_number) constraint is the single cheapest duplicate-payment control you can add.
Reports AP people live in
- AP ageing — open payables by bucket (current, 1–30, 31–60, 61–90, 90+)
- Blocked invoices by reason and age
- GR/IR ageing
- Due date forecast / cash requirement
- Vendor open item list and vendor statement reconciliation
- Discount taken vs lost
- Duplicate payment analysis