Handling Purchase Orders and Net-30 Invoicing
An enterprise invoice that arrives without a purchase-order number is not late — it is rejected, silently, by an accounts-payable system that will never tell you. The invoice sits unpaid, your dunning process assumes the customer is delinquent, and the account manager discovers three weeks later that a missing field is the entire problem. This guide sits under Enterprise Contracts & Quote-to-Cash and covers invoice-based billing end to end, from PO capture to remittance matching.
The mental shift is that the payer and the user are different people in different systems. A card subscription’s payer is the person clicking; an enterprise invoice’s payer is an accounts-payable team that has never used your product, follows its own rules, and communicates only through structured documents. Everything about the billing flow has to accommodate that.
Trade-offs
| Collection method | Cash timing | Failure signal | Automation ceiling | Fits |
|---|---|---|---|---|
| Card on file | Immediate | Decline within seconds | Very high | Self-serve, SMB |
| Bank debit mandate | 2–5 days | Return code within a week | High | EU/UK recurring |
| Invoice, net 30 | 30–60 days typical | Silence past due date | Medium | Enterprise |
| Invoice, net 60/90 | 60–120 days | Silence past due date | Low | Large enterprise, public sector |
The automation ceiling column is the honest constraint. Invoice collections can be instrumented, measured, and prompted, but the final step is frequently a person calling another person. Design for that: the software’s job is to make sure the right human is prompted at the right time with the right information, not to close the loop unaided.
Step-by-Step Implementation
1. Capture and validate the PO before issuing
ALTER TABLE billing_profiles
ADD COLUMN collection_method TEXT NOT NULL DEFAULT 'charge_automatically',
ADD COLUMN payment_terms_days INT NOT NULL DEFAULT 0,
ADD COLUMN po_number TEXT,
ADD COLUMN po_required BOOLEAN NOT NULL DEFAULT FALSE,
ADD COLUMN po_valid_until DATE,
ADD COLUMN ap_contact_email TEXT;
-- Refuse to issue an invoice that will be rejected on arrival.
ALTER TABLE invoices
ADD CONSTRAINT invoice_po_present
CHECK (collection_method <> 'send_invoice' OR po_required IS NOT TRUE OR po_number IS NOT NULL);
A purchase order frequently has its own expiry and its own approved amount. Both matter: an invoice against an expired PO is rejected, and an invoice that would take cumulative billing past the PO’s approved value is rejected for the excess. Track both and check them at issue time rather than discovering the problem in the ageing report.
2. Compute the due date deliberately
from datetime import date, timedelta
def due_date(issued_on: date, terms_days: int, calendar) -> date:
"""Net terms run in calendar days; the result is nudged to a business day."""
d = issued_on + timedelta(days=terms_days)
while not calendar.is_business_day(d):
d += timedelta(days=1)
return d
Terms like “net 30 from end of month” and “net 45 from receipt” exist and mean different things; store the rule, not just the number. Getting this wrong by a few days produces invoices that appear overdue in your system and on time in the customer’s, which is a credibility problem in exactly the conversation where credibility matters.
3. Send to accounts payable
The invoice must reach the AP contact, in the format their system accepts, with the PO number in the field their system reads. That usually means a PDF attachment plus a structured copy, and increasingly a portal upload. Record delivery as an event with a timestamp — “we sent it on the 3rd” is a claim you will need to substantiate.
Never send enterprise invoices only to the product user. They are not the payer, they will not forward it reliably, and they cannot resolve a rejection.
4. Match remittances to invoices
Partial payments are normal and must be first-class: an invoice can be part-paid, and the ageing bucket applies to the remaining balance. Modelling payment as a boolean forces the team into workarounds that corrupt the ledger.
5. Escalate on a human timeline
Reminders before the due date, a courteous notice on the day, then escalation at 15, 30, and 60 days past due, moving from automated email to the account manager to a formal notice. Suppress every card-oriented message for these accounts — an enterprise customer receiving “update your payment method” for a wire transfer is a small failure that costs disproportionate trust.
Ageing, Reporting, and When to Suspend
The ageing report is the operational heart of invoice billing, and it should be derived from invoice data rather than maintained separately.
SELECT CASE
WHEN current_date <= due_date THEN 'current'
WHEN current_date - due_date <= 30 THEN '1-30'
WHEN current_date - due_date <= 60 THEN '31-60'
WHEN current_date - due_date <= 90 THEN '61-90'
ELSE '90+'
END AS bucket,
count(*) AS invoices,
sum(amount_remaining_minor) AS outstanding_minor
FROM invoices
WHERE status = 'open' AND collection_method = 'send_invoice'
GROUP BY 1;
Suspension is the question this report eventually forces, and enterprise contracts usually constrain it. Many order forms specify a notice period before service can be withheld, and suspending inside that period is a breach that undermines your own collection position. Encode the contractual notice period on the order form and let the escalation ladder read it, so the system cannot suspend earlier than the contract allows.
Separate genuine delinquency from process friction before escalating. A large share of “overdue” enterprise invoices are stuck on a missing PO, a wrong AP address, a mismatch between the invoice and the PO amount, or a portal upload nobody completed. Track a resolution reason on every overdue invoice, and the distribution will show that most of the ageing balance is fixable by your team rather than by the customer’s.
Instrument the ladder so that each rung’s effectiveness is visible. Recording which step preceded payment, across a few hundred invoices, usually shows that the courtesy reminder before the due date recovers more than every later step combined — which argues for investing there rather than in more aggressive escalation. It also exposes the accounts that only ever pay after the account manager calls, and those are the ones to move onto a different collection method at renewal.
Verification & Testing
Test the due-date arithmetic across the cases that actually occur: terms from issue date, terms from end of month, a due date landing on a weekend, and a due date landing on a public holiday in the customer’s jurisdiction. Each is a one-line function and a handful of assertions, and each has been the cause of a real dispute somewhere.
Test the PO constraint by attempting to issue an invoice for a PO-required account without a PO and asserting the write fails rather than producing an invoice that will be rejected downstream. Then test the expiry and amount-ceiling checks with a PO that expires mid-term and one whose approved amount is exhausted by the third invoice.
Snapshot the rendered invoice document and assert the presence and position of the PO number, the due date, the remittance details, and the absence of card-related language. AP systems parse these documents; a layout regression that moves the PO number out of the expected block causes rejections that look like customer delinquency in every report you have.
Cash Application and the Credit Balance
Once payment stops being a one-to-one match with an invoice, the account needs a credit balance — a small piece of ledger machinery that most card-first billing systems lack and every invoice-billed business needs within months.
Three ordinary situations require it. A customer pays a round number that exceeds the invoice by a small amount and expects the difference held. A customer pays two invoices with one transfer, one of which is not yet issued. A concession is agreed mid-term and applied against whatever the next invoice happens to be. In all three, the money exists on the account before it has an invoice to attach to.
Model the credit balance as an account-level ledger with its own movements — funded by overpayment or concession, consumed by application to an invoice — and apply it automatically at invoice issue before any collection attempt. Applying it at issue rather than at payment matters, because the amount due printed on the document should be the amount the customer actually owes; an invoice for the full amount accompanied by an email explaining that a credit will be applied is exactly the kind of ambiguity that stalls in an accounts-payable queue.
Never let the credit balance become a dumping ground for unmatched cash. Money that cannot be attributed to a customer belongs in a suspense account with a human queue, not on an account balance where it will eventually be applied to the wrong invoice. The distinction is between “we know whose money this is and not yet what it pays for” and “we do not know whose money this is” — the first is a credit balance, the second is an unresolved item.
Report the aggregate credit balance monthly alongside the ageing report. A growing balance means cash is arriving that your invoicing is not keeping pace with, which is usually a signal that invoices are being issued late rather than that customers are overpaying.
Gotchas & Production Pitfalls
- Card dunning applied to invoice accounts. The single most common defect. Branch every dunning entry point on
collection_methodand assert it in tests. - PO expiry ignored. An invoice against an expired PO is rejected and ages silently. Check expiry and remaining approved amount at issue time.
- Overpayment treated as an error. Customers pay round numbers and pay early. Model an account credit balance and apply it to the next invoice rather than refusing the cash.
- Currency rounding on the remittance. A transfer arriving a few units short because of intermediary bank fees should be matched with a small write-off rule, not left open forever.
- Invoice numbering restarted or reused. Enterprise invoices are legal documents; the sequence must be gapless and unique, as covered in generating compliant sequential invoice numbers.
Frequently Asked Questions
Can an account be on both card and invoice? Yes, and it is common during transitions — a self-serve account that grows into an enterprise contract. Make collection_method a property of the billing profile with a clear cutover date, and never let both paths attempt collection for the same invoice.
Should net terms be offered to anyone who asks? They are effectively unsecured credit. Gate them on contract value and a credit decision, and keep the default as automatic collection. The operational cost of invoice billing is significant enough that it should be a deliberate concession.
How do I handle a customer’s AP portal that requires manual upload? Treat it as a delivery channel with its own record: who uploaded, when, and the portal’s own reference. Automating the upload is sometimes possible and rarely worth it below a handful of large accounts.
What about early-payment discounts? Terms like 2/10 net 30 are straightforward to express — a discount if paid within ten days — but they need the payment date, not the invoice date, to resolve. Compute the discount at application time and record it as a credit against the invoice rather than reissuing the document.