# Procure to pay (AP)

> The complete payables stack: requisition → PO → receipt → bill → approval → payment → reconciliation. AP is where fraud happens and where controls matter most. The design consequence: Every AP screen is a control surface. Approval, segregation of duties, and duplicate detection are not features bolt

Source: https://lopifi.com/docs/domain/06-procure-to-pay
Part of: Lopifi — US GAAP books on autopilot, checked by a CPA (https://lopifi.com)

---

# Procure to pay (AP)

The complete payables stack: requisition → PO → receipt → bill → approval → payment → reconciliation.

AP is where fraud happens and where controls matter most. The design consequence:

> **Every AP screen is a control surface.** Approval, segregation of duties, and duplicate detection are not features bolted onto a form — they are the reason the form exists.

---

## 1. The lifecycle

```
Requisition → PO → Goods receipt → Bill → Match → Approve → Schedule → Pay → Reconcile
      │        │         │           │       │        │                  │
      │        │         │           │       └────────┴─ the control gate │
      │        │         │           └─ Dr GR/IR or Expense / Cr AP       │
      │        │         └─ Dr Inventory/Expense / Cr GR/IR (if used)     │
      │        └─ commitment; no GL impact                    Dr AP / Cr Cash
      └─ no GL impact
```

**No GL impact from requisitions or POs.** The GL moves at goods receipt (if GR/IR is used) or at the bill. POs are commitments. Showing committed spend in an expense figure overstates it; showing only booked spend understates the obligation. Both matter — a spend view renders **booked, accrued, and committed as three distinct series**, never summed into one "spend" number.

---

## 2. Three-way match

The core AP control. Three documents must agree before payment.

```
  Purchase order        100 units @ $12.00   =  $1,200.00
  Goods receipt          98 units received
  Vendor invoice         100 units @ $12.00  =  $1,200.00
                        ─────────────────────────────────
  Quantity variance      2 units short           ($24.00)   ⚠ requires disposition
```

| Match type | Compares | Used for |
|---|---|---|
| **Two-way** | PO ↔ invoice | Services, no physical receipt |
| **Three-way** | PO ↔ receipt ↔ invoice | Goods |
| **Four-way** | + inspection/quality | Regulated or high-value goods |

**UI rules:**
- **Tolerances are configurable and displayed.** "Matching within $50 or 2%" — a reviewer needs to know what is auto-passing.
- **A variance requires a disposition**, never a silent pass: accept (with reason), request a vendor credit, partial-pay, or reject.
- **The three documents are shown side by side**, line by line, with differences highlighted at the line level, not just the header. A header total that matches while lines don't is a common way overbilling hides.
- **`MatchExceptionQueue` is the AP clerk's worklist**, sorted by age and amount.

---

## 3. The bill

```ts
type Bill = {
  vendorId: string;
  vendorInvoiceNumber: string;     // THEIR number — the duplicate-detection key
  billDate: Date;
  dueDate: Date;
  terms: PaymentTerms;
  currency: string;
  lines: BillLine[];               // each with account + full dimensions
  taxes: TaxLine[];
  total: Money;
  poReference?: string;
  approvals: Approval[];
  status: BillStatus;
  paymentSchedule?: Date;
  attachments: Document[];         // the source invoice — required
};
```

**UI rules:**

- **Duplicate detection runs before the form is saved**, keyed on `vendor + vendorInvoiceNumber`, with a secondary fuzzy check on `vendor + amount + date ± 5 days`. **Duplicate payment is one of the most common and most preventable AP losses**, and the check must be a blocking warning showing the suspected duplicate side by side.
- **The source document is required.** A bill with no attachment cannot be approved. This is the evidence an auditor asks for and it should never be optional.
- **The expense account and full dimensions are per line**, not per bill. A single invoice legitimately hits four departments.
- **Default coding is suggested from history** — this vendor's last bills — and the suggestion is visibly a suggestion, with the confidence and the basis shown.
- **Early-payment discounts are surfaced as a decision.** "Pay by 12 Oct to save $240 (2%)" with the annualized return, because 2/10 net 30 is a ~37% annualized return and finance teams miss it constantly.

---

## 4. Approval

```ts
type ApprovalPolicy = {
  thresholds: { upTo: Money; approvers: Role[]; requireAll: boolean }[];
  requireSeparateApprover: true;   // preparer ≠ approver. Never configurable to false.
  dimensionApprovers?: Record<string, Role>;   // department owner must approve their spend
  delegations: { from: string; to: string; until: Date }[];
};
```

**UI rules:**
- **Segregation of duties is enforced, not advisory.** The person who created a bill cannot approve it. This is not a setting.
- **The approval chain is visible before submission** — who has to approve, in what order, and what's already done. An approval that disappears into a queue with no visibility is the reason AP teams chase people on Slack.
- **Threshold escalation is shown at entry.** "Bills over $10,000 require CFO approval" appears while typing the amount, not after submitting.
- **Delegation has an expiry.** Permanent delegation is a control failure and the UI requires an end date.
- **Rejection requires a reason and routes back to the preparer** with it visible.
- **Approval is on a specific version.** If a bill is edited after approval, approvals are voided and re-requested — with the change shown. Silent post-approval editing is a fraud vector.

---

## 5. Payment runs

```ts
type PaymentRun = {
  runDate: Date;
  paymentDate: Date;
  method: "ach" | "wire" | "check" | "card" | "rtp";
  bills: string[];
  totalAmount: Money;
  fundingAccount: string;
  status: "draft" | "approved" | "submitted" | "settled" | "partially-failed";
};
```

**UI rules:**
- **The run is previewed in full before submission**: vendor, amount, method, bank details, and the resulting cash balance after the run. The most important number is **the cash left afterwards** — a payment run that overdraws is a very expensive UI failure.
- **Bank detail changes are the highest-risk event in AP.** A vendor bank change within N days of a payment is flagged, requires re-verification through an out-of-band channel, and is displayed prominently in the run preview. This is how vendor-impersonation fraud works and the UI is the last line of defense.
- **Payment runs use `HoldToConfirm`.** They are irreversible movements of money, often large.
- **Partial failures are a first-class state.** A run where 40 of 42 payments settled is not "failed"; the UI shows exactly which two and what to do.
- **Positive pay files** and check numbering are tracked; voided checks are recorded, never deleted.
- **Cash requirements forecast** — what's due in the next 7/14/30 days versus available cash — is the view AP managers actually plan from.

---

## 6. Vendor master

```ts
type Vendor = {
  id: string; name: string; legalName: string;
  taxId?: string;                    // W-9 / TIN
  form1099?: { required: boolean; box: string };
  bankDetails: BankDetail[];         // versioned, never overwritten
  defaultTerms: PaymentTerms;
  defaultAccount?: string;
  currency: string;
  status: "active" | "on-hold" | "inactive";
  approvedBy?: string;               // vendor onboarding is itself an approval
};
```

**UI rules:**
- **Bank details are versioned, never overwritten.** The history — who changed them, when, and what verification was performed — is the audit trail that matters most in AP.
- **Vendor onboarding requires approval** and is separate from bill entry. Whoever can create a vendor should not be able to pay one.
- **1099 tracking flags missing W-9s** before year-end, when it's still fixable. The Form 1099-NEC / 1099-MISC threshold is $600 for payments made through 2025 and $2,000 for payments made from 1 January 2026 (One Big Beautiful Bill Act, indexed for inflation from 2027) — the threshold is a dated table, not a constant. Payments by card or a third-party network are reported by the processor on Form 1099-K, not by the payer. A payee who will not provide a TIN triggers 24% backup withholding.
- **Duplicate vendor detection** on name similarity and tax ID — duplicate vendors are how duplicate payments survive duplicate-invoice checks.
- **Sanctions/OFAC screening status** is displayed where the integration exists. Paying a sanctioned entity is a legal event, not an accounting one.

---

## 7. Expense management

Employee-initiated spend. Same control model, different ergonomics — it's mobile-first, because receipts are photographed on phones.

**UI rules:**
- **Receipt capture is camera-first**, with OCR pre-filling amount, date, and merchant — all shown as editable suggestions with the extracted value visible next to the image.
- **Policy violations are shown at entry**, not at approval. "Meals over $75 require a business purpose" appears while typing, so the fix is cheap.
- **Corporate card transactions are matched to receipts**, not re-entered. The unmatched-receipt and unmatched-transaction queues are both first-class.
- **Personal vs. reimbursable vs. corporate-card-paid** are three different settlement paths and the UI never guesses.
- **Mileage and per diem** use rate tables with effective dates — the rate that applied on the trip date, not today's.

---

## 8. Accruals from AP

At period end, goods and services received but not invoiced must be accrued. This is the bridge from AP to the close.

**The GR/IR (goods received / invoice received) clearing account** holds the gap:
```
  Receipt:  Dr Inventory/Expense  / Cr GR-IR clearing
  Invoice:  Dr GR-IR clearing     / Cr Accounts payable
  Balance in GR-IR = received not yet invoiced = the accrual
```

**UI rules:**
- **The GR/IR balance is a schedule that must be aged and cleared.** Old balances mean either a missing invoice or a receipt that never happened, and both are findings.
- **`UninvoicedReceiptsReport` drives the period-end accrual entry** — it is the evidence for it.
- **Open POs at period end are not accrued.** Only significant unconditional or noncancelable purchase obligations are disclosed (ASC 440), and a loss on a firm commitment is accrued. Different treatment, and the UI must not conflate them.

---

## 9. AP metrics

| Metric | Formula | Watch out |
|---|---|---|
| **DPO** | `(AP ÷ COGS) × days in period` | Higher isn't automatically better — it may mean you're late, not efficient |
| **CCC** | `DSO + DIO − DPO` | The working-capital cycle in days |
| **On-time payment rate** | `Bills paid by due date ÷ bills paid` | Vendor relationship health |
| **Early-discount capture** | `Discounts taken ÷ discounts available` | Usually shockingly low; high-ROI to fix |
| **Invoice cycle time** | Receipt → approval → payment, in days | Where the bottleneck is |
| **Cost per invoice** | Processing cost ÷ invoice count | The automation business case |
| **Duplicate rate** | Duplicates caught ÷ invoices | The control's effectiveness |

**DPO is the metric most likely to be misread.** Rising DPO looks like improved cash management and may be deteriorating vendor relationships. Lopifi renders it with polarity `neutral` and the on-time payment rate adjacent, always — one number without the other is misleading.

---

## 10. AP component set

| Component | Purpose |
|---|---|
| `BillForm` | Entry with duplicate detection, line coding, and dimension capture |
| `BillDetail` | Full bill with the source document, match, approvals, and audit trail |
| `BillRegister` | All bills, filterable, with aging |
| `ThreeWayMatch` | PO / receipt / invoice side by side, line-level variances |
| `MatchExceptionQueue` | The clerk's worklist |
| `ApprovalChain` | Who, in what order, what's done, what's blocking |
| `ApprovalInbox` | The approver's queue, mobile-first, with the document inline |
| `PaymentRunBuilder` | Select bills, preview, show the resulting cash balance |
| `PaymentRunPreview` | Final confirmation with hold-to-confirm |
| `CashRequirements` | Due in 7/14/30 days vs. available cash |
| `VendorMaster` | With versioned bank details and change history |
| `BankDetailChangeAlert` | The fraud-prevention surface |
| `ExpenseCapture` | Camera-first, OCR-assisted, mobile |
| `CardTransactionMatch` | Corporate card ↔ receipt matching |
| `UninvoicedReceiptsReport` | GR/IR aging, drives the accrual |
| `AgingTable` | AP aging, tied to the control account |
