๐ The Core Concept: Invoice = Obligation, Payment = Settlement
In ERP systems such as SAP, Odoo, Microsoft Dynamics, and any serious custom-built ERP, Invoice and Payment are related but they are never the same transaction. An invoice creates a financial obligation. A payment settles that obligation. Conflating the two is one of the most common design mistakes when building an ERP or POS from scratch.
A simple business transaction unfolds like this:
Customer buys goods โ Sale happens โ Invoice is created
โ Customer owes money โ Customer pays โ Payment is recorded
โ Invoice becomes paid
Sale/Order โ Invoice โ Receivable โ Payment โ Reconciliation โ Invoice Paid
For a supplier, the mirror image applies: Purchase/Order โ Vendor Bill โ Payable โ Payment โ Reconciliation โ Bill Paid.
| Concept | Invoice | Payment |
|---|---|---|
| Meaning | Money that is owed | Money that has actually moved |
| Creates obligation? | Yes | No |
| Customer side | Accounts Receivable | Receives money |
| Supplier side | Accounts Payable | Pays money |
| Can exist without the other? | Yes | Yes |
| Changes outstanding balance? | Creates balance | Reduces balance |
| Status values | Draft, Posted, Partially Paid, Paid, Cancelled | Pending, Posted, Reconciled, Cancelled |
Invoice โ Payment directly. It should think Business Transaction โ Document โ Accounting Document โ Receivable/Payable โ Payment โ Reconciliation.
๐บ๏ธ How It Works: The Entity Relationship Map
Before any workflow or flowchart makes sense, the underlying relationships have to be right. The single biggest mistake is wiring Invoice and Payment together with a direct foreign key. In a real ERP, nothing connects to a payment directly โ everything passes through an allocation record, and every posted document mirrors itself into a journal entry. The map below shows the actual shape of the data for the customer/sales side (the supplier/purchase side is the exact mirror image, with Supplier, VendorBill and Accounts Payable in place of Customer, Invoice and Accounts Receivable).
Customer
- id
- name
- credit_terms
- credit_limit
Invoice
- id, number, total
- status, due_date
Payment
- id, amount, method
- status, reference
PaymentAllocation
- invoice_id (FK)
- payment_id (FK)
- allocated_amount
Reconciliation
- ar_debit_je_id
- ar_credit_je_id
General Ledger
- account_id
- debit / credit
Invoice is a record of an obligation; a Payment is a record of money movement. Neither one "contains" the other โ they are two independent facts joined by a third record, PaymentAllocation, which is the only place the ERP is allowed to say "this much of that payment settles this much of that invoice."๐ง Main concept: Every balance you display to a user โ outstanding amount, PAID/PARTIAL status, customer statement โ is derived at read time from the chain
Invoice โ PaymentAllocation โ Reconciliation โ GL. Nothing is hard-coded as a single mutable field that different modules race to update.
๐ Sales Flow: Order โ Delivery โ Invoice โ Payment
Sales is the commercial side of the transaction โ it answers what did we sell, to whom, and on what terms. Not every ERP needs every step; a simple POS can collapse straight to Sale โ Invoice โ Payment, while an enterprise flow keeps every stage distinct and auditable.
Worked example โ a customer buys เงณ10,000 of goods on credit:
Invoice INV-00001
Subtotal เงณ9,000
VAT เงณ1,000
---------------------
Total เงณ10,000
Journal Entry:
DR Accounts Receivable 10,000
CR Sales Revenue 9,000
CR VAT Payable 1,000
Customer pays เงณ4,000 (partial):
DR Cash 4,000
CR Accounts Receivable 4,000
Outstanding = 10,000 - 4,000 = 6,000 โ status: PARTIALLY PAID
Customer pays remaining เงณ6,000:
DR Cash 6,000
CR Accounts Receivable 6,000
Outstanding = 0 โ status: PAID
๐ฆ Purchase Flow: PO โ Receipt โ Vendor Bill โ Payment
The purchase side mirrors sales exactly, but from the supplier's perspective:
Because goods receipt and vendor billing can arrive at different times, mature ERPs also track Goods Received Not Invoiced (GRNI) as an accrued liability until the vendor bill lands.
Vendor Bill
Goods เงณ100,000
VAT เงณ15,000
---------------------
Total เงณ115,000
DR Inventory / Expense 100,000
DR Input VAT 15,000
CR Accounts Payable 115,000
Payment 1 = เงณ80,000
Payment 2 = เงณ35,000
------------------------
Outstanding = 0 โ status: PAID
๐ Activity Diagram: The Invoice-to-Cash Swimlane
A flow diagram shows what happens to the documents. An activity diagram shows who does each step and where responsibility hands off between actors โ the Customer, Sales, Accounting, and Finance. Read the numbered cards left-to-right, top-to-bottom; each handoff tag is where the process crosses into the next lane.
DR Accounts Receivable / CR Revenueโ AR created
DR Cash/Bank / CR AR (pre-allocation)โท hands off to Accounting
Notice that Accounting touches the process twice โ once to record the obligation (step 4) and once to record the settlement (step 7) โ and that neither touch is triggered by the other directly. Each is triggered by its own upstream document (the Invoice, then the Payment), which is exactly why the two must stay independent tables joined by an allocation record, as shown in the relationship map above.
๐ณ Accounts Receivable & Accounts Payable
AR is money customers owe your company. AP is money your company owes suppliers. Neither is simply a flag on the invoice table โ each is effectively a subledger: a running balance per business partner, derived from every invoice, credit note, and payment posted against them.
Customer A statement
Opening Balance เงณ0
+ Invoice 1 เงณ100,000
+ Invoice 2 เงณ50,000
- Payment เงณ80,000
-----------------------------
Closing Balance เงณ70,000
The AR subledger total must always reconcile with the AR control account in the General Ledger:
SUM(all customer balances) = GL "Accounts Receivable" balance
SUM(all supplier balances) = GL "Accounts Payable" balance
โ๏ธ Double-Entry Accounting Fundamentals
Every financial event must have equal total debits and credits. This single invariant is what keeps an ERP's books internally consistent no matter how many modules write to them.
Assets = Liabilities + Equity + Revenue - Expenses
| Account type | Increase | Decrease |
|---|---|---|
| Asset | Debit | Credit |
| Expense | Debit | Credit |
| Liability | Credit | Debit |
| Equity | Credit | Debit |
| Revenue | Credit | Debit |
"Debit = increase, credit = decrease" is a common misconception โ it depends entirely on the account type, as the table above shows. A few canonical entries:
# Sale on credit
DR Accounts Receivable
CR Sales Revenue
# Cash received against an invoice
DR Cash
CR Accounts Receivable
# Pay a supplier
DR Accounts Payable
CR Bank
๐ Journal Entries & the General Ledger
Every financial event should ultimately become a journal entry โ the accounting truth behind the business document.
JE-2026-000001
Date: 27-Aug-2026 Reference: INV-2026-001
Account Debit Credit
--------------------------------------------------
Accounts Receivable 55,000
Sales Revenue 50,000
VAT Payable 5,000
--------------------------------------------------
TOTAL 55,000 55,000
A clean schema separates the journal entry header from its lines, because one entry always has two or more balanced lines:
journal_entry
--------------------------
id
date
reference
source_type -- e.g. "invoice", "payment", "credit_note"
source_id
description
status
posted_at
journal_entry_line
--------------------------
id
journal_entry_id (FK)
account_id (FK -> chart of accounts)
debit
credit
partner_id
currency
exchange_rate
The General Ledger is simply the aggregation of every posted journal entry line, grouped by account:
Business Document (Invoice / Payment / Bill)
โ
Accounting Event
โ
Journal Entry (balanced: total debit = total credit)
โ
General Ledger
๐ Payment Allocation: Many-to-Many by Design
This is the concept most custom ERPs get wrong first. Don't model invoice.payment_id as a one-to-one link โ in reality, one invoice can be settled by many payments, and one payment can settle many invoices.
Invoice INV-001 Payment PAY-1001
Total = เงณ100,000 Amount = เงณ100,000
โ
โโโ Payment 1 = เงณ30,000 โโโ Invoice A โ เงณ40,000
โโโ Payment 2 = เงณ20,000 โโโ Invoice B โ เงณ35,000
โโโ Payment 3 = เงณ50,000 โโโ Invoice C โ เงณ25,000
Total Paid = เงณ100,000 (Payment applied across 3 invoices)
Balance = เงณ0
The relationship Invoice โโ Payment is therefore many-to-many, resolved through an allocation table:
payment
----------------
id
customer_id
payment_date
amount
payment_method
bank_account_id
reference
status
payment_allocation
------------------
id
payment_id (FK)
invoice_id (FK)
allocated_amount
allocation_date
If a payment isn't fully allocated โ say a customer pays เงณ100,000 against เงณ90,000 of open invoices โ the remaining เงณ10,000 becomes an on-account credit balance rather than being force-fit onto an invoice it doesn't belong to.
๐งฎ Reconciliation
Allocation and reconciliation are related but distinct. Allocation answers how much of this payment applies to this invoice? Reconciliation answers which AR journal entries offset each other?
Invoice Journal Entry: AR +100,000
Payment Journal Entry: AR -100,000
โโโโโโโโ
Reconciled AR balance: 0 โ Invoice = PAID
Partial reconciliation works the same way, just leaving a remainder:
AR Debit (invoice) 100,000
AR Credit (payment) 40,000
-------------------------------
Remaining outstanding 60,000 โ status: PARTIALLY PAID
๐ Invoice & Payment Lifecycle
A professional ERP derives status from a controlled state machine rather than letting any code path set status freely.
Invoice lifecycle Payment lifecycle
DRAFT Created
โ โ
CONFIRMED Pending
โ โ
POSTED โโโบ JOURNAL ENTRY Posted
โ โ
OUTSTANDING Allocated
โ (payment received) โ
PARTIALLY PAID Reconciled
โ (fully allocated)
PAID
The outstanding balance is always derived, never stored as a naive flag:
Outstanding Balance =
Invoice Total
- Credit Notes
- Allocated Payments
+ Debit Adjustments
Outstanding = 0 โ PAID
๐๏ธ The Multi-Dimensional Status Model
A common mistake is cramming everything into one invoice.status field. In practice an invoice has four independent dimensions โ whether the document itself is finalized, how much of it is settled, whether it's overdue, and (separately) where the payment that's settling it stands. Mixing them together produces states like "overdue_partial_cancelled" that are impossible to query cleanly.
| Dimension | Applies to | Possible values | What it answers |
|---|---|---|---|
| Document status | Invoice / Bill | DRAFT, CONFIRMED, POSTED, CANCELLED, REVERSED | Is the document itself finalized and posted to the ledger? |
| Settlement status | Invoice / Bill | UNPAID, PARTIALLY_PAID, PAID, OVERPAID, WRITTEN_OFF, CREDITED | How much of the obligation has actually been cleared? |
| Due status | Invoice / Bill | NOT_DUE, DUE_TODAY, OVERDUE | Has the payment-term deadline passed? |
| Payment status | Payment | DRAFT, PENDING, POSTED, PARTIALLY_ALLOCATED, FULLY_ALLOCATED, REVERSED, CANCELLED, FAILED | Where does the payment itself stand, independent of which invoice(s) it touches? |
POSTED + PARTIALLY_PAID + OVERDUE all at once โ three true facts, three separate columns (or derived properties), each independently queryable for reporting, dunning, and aging without regex-parsing a single mashed-together string.
๐ Flowchart: The Settlement Decision Logic
This is the actual decision tree an ERP runs every time a payment is recorded against an invoice โ the logic behind the lifecycle and the status model above, made explicit.
AR + Invoice Total
create PaymentAllocation against the invoice
Reconciliation posted to the General Ledger
๐งพ Credit Notes, Refunds, Overpayments & Advances
Credit note
Never edit a posted invoice to reflect a return. Issue a credit note instead, preserving the audit trail:
Original Invoice เงณ100,000
Credit Note -เงณ20,000
----------------------------------
Net Receivable เงณ80,000
Refund
Different from a credit note โ the ERP should preserve all three transactions rather than netting them away:
Invoice เงณ100,000
Paid เงณ100,000
Refund -เงณ20,000
--------------------------
Net received เงณ80,000
Overpayment
Invoice = เงณ10,000
Payment = เงณ15,000
Allocation to invoice = เงณ10,000 โ invoice PAID
Unallocated = เงณ5,000 โ customer on-account credit
Advance payment
Customers can pay before an invoice even exists โ the advance becomes a credit that later invoices draw down:
Advance payment เงณ50,000 โ Customer Credit
Later invoice เงณ80,000
Applied from advance -เงณ50,000
--------------------------------------
Outstanding remaining เงณ30,000
๐งช Scenario Playbook: A เงณ1,000 Invoice Under 40+ Conditions
The formula from the lifecycle section โ Outstanding = Invoice Total โ Credits/Adjustments โ Allocated Payments + Debit Adjustments โ has to survive every real condition a customer, cashier, or bank can throw at it. Below is a single เงณ1,000 invoice run through the full range of scenarios an ERP must handle correctly. The same logic mirrors exactly onto the AP/supplier side.
1. Partial payment progression
The core case: any amount between เงณ0 and เงณ1,000 leaves the invoice PARTIALLY_PAID โ there is no special threshold at 25%, 50%, or 90%. Only Outstanding = 0 flips it to PAID.
| Paid | % of invoice | Outstanding | Settlement status |
|---|---|---|---|
| เงณ0 | 0% | เงณ1,000 | UNPAID |
| เงณ10 | 1% | เงณ990 | PARTIALLY_PAID |
| เงณ100 | 10% | เงณ900 | PARTIALLY_PAID |
| เงณ250 | 25% | เงณ750 | PARTIALLY_PAID |
| เงณ500 | 50% | เงณ500 | PARTIALLY_PAID |
| เงณ900 | 90% | เงณ100 | PARTIALLY_PAID |
| เงณ999 | 99.9% | เงณ1 | PARTIALLY_PAID |
| เงณ1,000 | 100% | เงณ0 | PAID |
| เงณ1,200 | 120% | เงณ0 | PAID + เงณ200 customer credit |
2. Many payments settling one invoice
There is no limit on how many payments can close one invoice โ the allocation table simply accumulates rows until the sum reaches the total.
| Breakdown | Payments | Sum | Result |
|---|---|---|---|
| Two payments | 600 + 400 | 1,000 | PAID |
| Three installments | 300 + 300 + 400 | 1,000 | PAID |
| Four equal installments | 250 ร 4 | 1,000 | PAID |
| Eight small receipts | 50+50+50+100+150+200+200+200 | 1,000 | PAID |
3. One payment settling many invoices
The mirror case, exercising the many-to-many allocation from the allocation section.
| Open invoices | Payment | Allocation | Result |
|---|---|---|---|
| A=1,000, B=2,000, C=3,000 | เงณ4,000 | Aโ1,000, Bโ2,000, Cโ1,000 | A, B PAID; C PARTIALLY_PAID (2,000 left) |
| A=1,000, B=1,000 | เงณ1,500 | Aโ1,000, Bโ500 | A PAID; B PARTIALLY_PAID (500 left) |
| A=1,000, B=2,000, C=3,000 | เงณ7,000 | Aโ1,000, Bโ2,000, Cโ3,000 | All PAID + เงณ1,000 customer credit |
4. Overpayment, advances & credit balances
| Scenario | Numbers | Result |
|---|---|---|
| Overpayment | Invoice 1,000 / Payment 1,200 | PAID + เงณ200 credit balance |
| Large overpayment | Invoice 1,000 / Payment 2,000 | PAID + เงณ1,000 credit balance |
| Advance before invoice | Advance 1,000, later Invoice 1,500 | Advance applied โ Outstanding 500 |
| Advance larger than invoice | Advance 2,000, Invoice 1,000 | PAID + เงณ1,000 remains as credit |
| New payment spills into credit | Balance 200 due, Payment 500 | 200 โ invoice (PAID), 300 โ credit |
| Opening balance | Migrated AR opening = 1,000 | Posted as an AR opening entry, not a new invoice |
| Existing credit offsets new invoice | Credit 500, new Invoice 1,000 | Customer owes only เงณ500 |
| Refund of credit balance | Credit 500, refund 300 | Remaining credit = เงณ200 |
5. Credit notes, discounts, write-offs & tax
| Scenario | Numbers | Result |
|---|---|---|
| Partial credit note | Invoice 1,000, Credit Note 100, Payment 500 | Outstanding = 400 |
| Full credit note | Invoice 1,000, Credit Note 1,000 | Net invoice = 0 โ SETTLED, no payment needed |
| Settlement discount | Invoice 1,000, Discount 50, Payment 950 | Outstanding = 0 (DR Sales Discount 50) |
| Small write-off | Invoice 1,000, Payment 995 | เงณ5 written off โ PAID + WRITTEN_OFF |
| Large write-off (needs approval) | Invoice 1,000, Payment 700 | เงณ300 written off on management approval |
| Payment + credit + write-off combined | Payment 850 + Credit 100 + Write-off 50 | 1,000 total โ Outstanding = 0 |
| VAT-inclusive posting | Total 1,000 = Net 909.09 + VAT 90.91 | DR AR 1,000 / CR Sales 909.09 / CR VAT 90.91 |
6. Reversals, cancellations & corrections
| Scenario | Correct handling |
|---|---|
| Draft invoice cancelled | No GL impact โ it was never posted, safe to delete/cancel outright |
| Posted, paid invoice needs cancelling | Never delete โ issue a Credit Note, then a Refund if cash already moved |
| Payment reversed (bounced cheque / bank reject) | Reversal JE: DR AR / CR Bank โ invoice returns to OUTSTANDING |
| Posted payment cancelled by mistake | Reverse it (create an offsetting JE) โ never hard-delete a posted payment |
| Duplicate payment received | Second payment stays unallocated, then refunded or applied to a future invoice |
| Payment allocated to the wrong invoice | Reverse the allocation, create a new one on the correct invoice โ both logged in the audit trail |
7. Timing, currency & operational scenarios
| Scenario | Correct handling |
|---|---|
| Gateway payment pending/failed | Invoice stays OUTSTANDING until the gateway confirms โ never mark PAID on "pending" |
| Gateway clearing & fee | Route through a Clearing Account, then split Bank + Fee on settlement (see Chart of Accounts) |
| Multi-currency invoice | Store invoice & payment at their own exchange rates; the difference posts as FX Gain/Loss |
| Cash shortage/overage at till | Post the difference to an approved Cash Over/Short account โ never silently adjust the customer payment |
| Paid early vs. paid late | Track days_early / days_late separately for DSO, aging, and collection reports |
| Allocation ordering | Configurable rule: oldest-invoice-first (FIFO), due-date order, or manual user selection |
8. Every scenario mirrors onto Accounts Payable
Nothing above is customer-specific โ replace CustomerโSupplier, InvoiceโVendor Bill, and Accounts ReceivableโAccounts Payable, and the exact same allocation, reconciliation, and status logic applies.
| AR scenario | AP mirror |
|---|---|
| Invoice 1,000, Payment 0 | Bill 1,000, Payment 0 โ AP = 1,000 |
| Invoice 1,000, Payment 500 | Bill 1,000, Payment 500 โ AP = 500 |
| Invoice 1,000, Payment 1,000 | Bill 1,000, Payment 1,000 โ AP = 0, PAID |
| Overpayment โ customer credit | Overpayment โ supplier advance/credit |
| One payment across A/B/C invoices | One payment across A/B/C vendor bills |
Outstanding is a derived value, allocations are many-to-many, and every adjustment (credit, write-off, discount, FX difference) is its own typed transaction rather than a hand-edit to paid_amount.
๐ Three-Way Matching
On the purchase side, mature ERPs cross-check the Purchase Order, Goods Receipt, and Vendor Bill before releasing payment:
Purchase Order 100 units @ เงณ1,000
Goods Receipt 95 units received
Vendor Bill 100 units billed
โโโโโโโโโโโโโโโโโ
Mismatch = 5 units โ flagged for approval
The sales-side equivalent compares Ordered vs. Delivered vs. Invoiced quantities before an invoice is allowed to post.
๐ก๏ธ Audit Trail: Why You Never Delete Posted Documents
A posted invoice is referenced by AR, the General Ledger, tax reports, and customer statements. Deleting it silently breaks all of them. Enterprise systems only allow Draft โ Edit, but Posted โ Reverse / Cancel / Credit Note โ never Posted โ Delete.
audit_log
--------------------
id
user_id
action -- CREATE, UPDATE, POST, CANCEL
entity_type -- "invoice", "payment", ...
entity_id
timestamp
old_values (jsonb)
new_values (jsonb)
reason
Corrections to posted financial data should be made by reversing the original entry and posting a new one โ never by rewriting history:
Original JE: DR Expense 50,000 / CR Bank 50,000
Reversal JE: DR Bank 50,000 / CR Expense 50,000
Corrected JE: (the accurate entry)
๐๏ธ Recommended Database Schema (Django/PostgreSQL)
For a Django/PostgreSQL ERP, keep Sales, Purchase, and Accounting as separate layers connected through documents and journal entries โ not as tightly-coupled tables reaching into each other's internals.
Customer โโโฌโโ Sales Order โโ Order Lines
โ
โโโ Invoice โโ Invoice Lines
โ
โ
Accounts Receivable
โ
โ
Payment Allocation โโโ Payment
โ
โ
Journal Entry โโ Journal Entry Lines
โ
โ
GL Accounts
Supplier โโโฌโโ Purchase Order โโ PO Lines
โ
โโโ Vendor Bill โโ Bill Lines
โ
โ
Accounts Payable
โ
โ
Payment Allocation โโโ Supplier Payment
โ
โ
Journal Entry โโ Journal Entry Lines
โ
โ
GL Accounts
Core entities worth modeling explicitly:
Customer, Supplier
ChartOfAccount, Account
SalesOrder, SalesOrderLine
Delivery, DeliveryLine
Invoice, InvoiceLine
PurchaseOrder, PurchaseOrderLine
GoodsReceipt, GoodsReceiptLine
VendorBill, VendorBillLine
Payment, PaymentAllocation
CreditNote, CreditNoteLine, DebitNote
JournalEntry, JournalEntryLine
Reconciliation, ReconciliationLine
CashAccount, BankAccount
Tax, TaxRate, Currency, ExchangeRate
AuditLog, DocumentSequence
INV-2026-000123, PAY-2026-000045, PO-2026-000012 via a DocumentSequence table, and keep id internal.
๐ Chart of Accounts & Control Accounts
Double-entry accounting needs a structured Chart of Accounts underneath it:
1000 Assets
โโโ 1100 Cash
โโโ 1200 Bank
โโโ 1300 Accounts Receivable (control account)
โโโ 1400 Inventory
2000 Liabilities
โโโ 2100 Accounts Payable (control account)
โโโ 2200 VAT Payable
3000 Equity
โโโ 3100 Owner Capital
4000 Revenue
โโโ 4100 Product Sales
โโโ 4200 Service Revenue
5000 Expenses
โโโ 5100 Salary
โโโ 5200 Rent
โโโ 5300 Utilities
For payment methods like bKash, Nagad, or card gateways, model them as real financial accounts rather than free-text strings โ and route gateway payments through a clearing account so gateway fees reconcile cleanly:
Customer pays เงณ10,000 by card:
DR Card Clearing Account 10,000
CR Accounts Receivable 10,000
Bank settles, minus a เงณ200 gateway fee:
DR Bank 9,800
DR Payment Gateway Fee 200
CR Card Clearing Account 10,000
Card Clearing Account balance = 0
โ Best Practices Checklist
Five concepts to master first
- Document lifecycle โ Order โ Delivery โ Invoice โ Payment, each a distinct, independently-auditable step.
- Double-entry accounting โ every financial event balances debits and credits.
- AR / AP โ who owes whom, tracked as subledgers reconciled against GL control accounts.
- Allocation & reconciliation โ a many-to-many link between payments and invoices, never a single foreign key.
- Audit trail โ posted financial documents are reversed or credited, never silently edited or deleted.
Once these five are correct, Sales, Purchase, POS, Inventory, Tax, and Expense modules can all be built on top of the same financial foundation, with the accounting engine as the central authority rather than `invoice`, `payment`, `sales`, and `purchase` tables reaching directly into each other.
Final words
The most important architectural rule: don't make the invoice table responsible for everything. Business modules generate financial events; a dedicated accounting engine records and reconciles those events. That separation is what makes an ERP scale, audit cleanly, and survive years of feature growth without turning into a tangle of ad-hoc status flags.