Sales Invoice — Chart of Accounts & Tax Routing

Accounts required at invoice creation

The Sales Invoice flow creates a core InvoiceModel inside django-ledger, so the standard account requirements apply when creating a sales invoice:

  • Cash account (role: ASSET_CA_CASH) — records cash receipts on payment

  • Receivable account (role: ASSET_CA_RECEIVABLES) — records the amount owed by the customer

  • Deferred revenue account (role: LIABILITY_CL_DEFERRED_REVENUE) — holds revenue until the invoice is approved

Accounts required on sellable items

For every product or service added as a line item, the underlying ItemModel must have the following accounts assigned before it can appear on an invoice:

  • Earnings account — where revenue is recognized (e.g., Sales Revenue)

  • COGS account (products only) — cost of goods sold

  • Inventory account (physical products only) — stock value

These are set on the item record, not on the invoice itself.

Choosing the right income account per line item

When you add a line item to an invoice you select an earnings account — this is the account that gets credited in the general ledger when the invoice is approved. The choice determines which bucket revenue lands in on your Income Statement (Profit & Loss).

Income account roles

Role (django-ledger)

What it means

Correct use

INCOME_OPERATIONAL

Operational / trading revenue

Product sales, service fees, shipping charges to customers, e-commerce sales

INCOME_PASSIVE

Passive / investing income

Rental income, royalties, licensing fees

INCOME_INTEREST

Interest earned

Bank interest, loan interest received

INCOME_CAPITAL_GAIN_LOSS

Asset disposal gain or loss

Selling a vehicle, equipment, or investment at a profit

INCOME_OTHER

Miscellaneous income

One-off items that don’t fit the categories above

Important

Picking the wrong role does not break the books (debits still equal credits), but your P&L will misrepresent the business — for example, regular service revenue appearing under Capital Gains.

Project sales account presets

The project now provisions dedicated sales accounts automatically for each entity default Chart of Accounts. This happens during migrations, when a default CoA is populated, and when the Sales Invoice flow is opened for an existing entity.

Code

Account Name

Used for

4010

Sales Income

Existing generic sales fallback

4011

Product Sales

Physical goods sold to customers

4012

Service Revenue

Labour, consulting, creative services

4013

Freight Revenue

Shipping charges billed to customers

4014

E-commerce Sales

Online channel revenue (Shopify, Amazon)

The line-item Account dropdown defaults to Product Sales when available, then falls back to Sales Income.

Note

Freight Revenue (4013) is the correct account for the Shipping line item. When a customer is charged for shipping, credit Freight Revenue. When you pay the carrier, that is a separate transaction debiting a Freight Out expense account (under COGS or EXPENSES).

Tax payable accounts (GST / PST / HST)

Project tax payable presets

The project provisions these current-liability accounts automatically under the LIABILITY_CL_TAXES_PAYABLE role:

Code

Account Name

Role

2080

Sales Tax Payable

Taxes Payable / LC

2081

GST Payable

Taxes Payable / LC

2082

PST/QST Payable

Taxes Payable / LC

2083

HST Payable

Taxes Payable / LC

2084

US Sales Tax Payable

Taxes Payable / LC

The provisioning step also creates or updates the entity’s SalesTaxAccountMapping:

  • default_tax_payable_account -> 2080 Sales Tax Payable

  • gst_account -> 2081 GST Payable

  • pst_account -> 2082 PST/QST Payable

  • hst_account -> 2083 HST Payable

  • us_sales_tax_account -> 2084 US Sales Tax Payable

If a code is already used by a different account, the project picks the next available nearby code and still maps the tax code to the created account.

What the sales invoice stores

SalesInvoiceProfile.recalculate_snapshot() writes:

  • SalesInvoiceTaxComponent rows — aggregate GST/PST amounts for the whole invoice

  • SalesInvoiceLineTaxComponent rows — per-line breakdown for each item

  • SalesInvoiceProfile.tax_total_amount — total tax collected

  • auto-generated invoice item transactions for tax payable accounts

This data is used for reporting and display. The underlying InvoiceModel total (amount_due) includes the tax amount so the customer is billed correctly, and the tax portion uses the mapped Taxes Payable accounts instead of ordinary sales income accounts.

How tax flows through the journal (double-entry)

When an invoice is approved and migrate_state() runs, django-ledger posts the following journal entries automatically:

Dr  Accounts Receivable        $560.00   (full amount incl. tax)
    Cr  Sales Revenue          $500.00   (pre-tax line items)
    Cr  GST Payable             $25.00   (5% × $500 taxable)
    Cr  PST Payable             $35.00   (7% × $500 taxable)

The mapping is implemented through auto-generated tax item transactions. Each tax item uses its mapped Taxes Payable account as the item earnings account, so django-ledger’s normal invoice migration credits the correct liability account.

Tax-exempt items

An item is treated as tax-exempt when its additional_info dict contains:

{"sales_tax_exempt": True}

Set this on the ItemModel record. The recalculate_snapshot method reads this flag per line item and skips tax calculation for exempt items. Exempt lines display an Exempt badge in the line items editor.

Discount allocation

When a discount is applied, it is pro-rated across taxable line items before tax is calculated. A $50 discount on two taxable lines of $300 and $200 is split $30/$20 respectively, reducing the taxable base for each line and therefore reducing the GST/PST amounts proportionally.

Summary flow

Customer Order
     │
     ▼
SalesInvoiceProfile (created with InvoiceModel)
     │  ship_to_* + tax_calculation_mode + tax_preset
     │
     ▼
Add Line Items  ──►  ItemTransactionModel saved
     │                      │
     │               post_save signal
     │                      │
     ▼                      ▼
recalculate_snapshot()  ──►  SalesInvoiceLineSummary
                             SalesInvoiceLineTaxComponent  (per-line GST/PST)
                             SalesInvoiceTaxComponent      (aggregate)
                             SalesInvoiceProfile totals    (subtotal/tax/total)
     │
     ▼  (on approval)
migrate_state()  ──►  Revenue entries posted to sales accounts
                      Tax entries posted to mapped payable accounts