bus-invoices

bus invoices — create and manage sales and purchase invoices

bus invoices stores invoice headers and invoice lines as workspace data. Use it when you create sales invoices, record purchase invoices, import invoice history, validate invoice totals, or prepare invoice-driven posting data for downstream accounting.

It is the invoice owner in BusDK. Evidence files belong in bus-attachments, PDF rendering goes through bus-pdf, and posting flows continue into bus-journal or bus-reconcile.

Common tasks

Create the baseline invoice datasets:

bus invoices init

Create one sales invoice and add a line to it:

bus invoices add \
  --type sales \
  --invoice-id INV-2026-001 \
  --invoice-date 2026-02-16 \
  --due-date 2026-03-16 \
  --counterparty "Acme Corp"

bus invoices INV-2026-001 add \
  --desc "Consulting" \
  --quantity 10 \
  --unit-price 100 \
  --income-account "Consulting Income" \
  --vat-rate 25.5 \
  --vat-treatment domestic_standard

If the workspace source_kinds mapping says s -> sales_invoice and p -> purchase_invoice, the same header add can also use a shorthand invoice id:

bus invoices add s6204 --invoice-date 2026-02-16 --counterparty "Acme Corp"

Validate all invoices and list unpaid ones:

bus invoices validate
bus invoices list --status unpaid --due-to 2026-03-31

Validate one invoice only:

bus invoices INV-2026-001 validate

Render one invoice as PDF:

bus invoices pdf INV-2026-001 --out INV-2026-001.pdf

Import ERP invoice history with a reusable profile:

bus invoices import \
  --profile ./profiles/erp-invoices.yaml \
  --source ./exports/invoices.tsv \
  --source-lines ./exports/invoice-lines.tsv \
  --year 2025

Emit invoice-driven posting rows for downstream accounting:

bus invoices postings

Synopsis

bus invoices init [-C <dir>] [global flags]
bus invoices add [<invoice-shorthand>] [--type <sales|purchase>] [--invoice-id <id>] --invoice-date <YYYY-MM-DD> [--due-date <YYYY-MM-DD>] --counterparty <name> [-C <dir>] [global flags]
bus invoices list [--type <sales|purchase>] [--status <status>] [--month <YYYY-M>] [--from <YYYY-MM-DD>] [--to <YYYY-MM-DD>] [--due-from <YYYY-MM-DD>] [--due-to <YYYY-MM-DD>] [--counterparty <entity-id>] [--invoice-id <id>] [-C <dir>] [global flags]
bus invoices import --profile <path> --source <path> [--source-lines <path>] [--year <YYYY>] [-C <dir>] [global flags]
bus invoices validate [-C <dir>] [global flags]
bus invoices classify [--min-confidence <0..1>] [--apply] [--fail-on-missing-evidence] [-C <dir>] [global flags]
bus invoices pdf <invoice-id> --out <path> [-C <dir>] [global flags]
bus invoices <invoice-id> add [line options] [-C <dir>] [global flags]
bus invoices <invoice-id> validate [-C <dir>] [global flags]
bus invoices postings [-C <dir>] [global flags]

Which command should you use?

Use init once per workspace.

Use add for invoice headers and <invoice-id> add for invoice lines. Header add accepts either explicit --type plus --invoice-id or one shorthand invoice selector resolved through workspace source_kinds. Use --counterparty for the party name on both sales and purchase invoices; --customer still works as a compatibility alias.

Use validate before you trust totals or downstream posting output.

Use list for operational filtering such as unpaid invoices, one month, or one counterparty.

Use pdf when you want the rendered document.

Use import when a profile-driven ERP history import is easier than manual creation.

Use postings when you want the workspace-level invoice-postings.csv dataset for downstream journal work.

Use classify mainly for recurring purchase-line categorization workflows.

Typical workflow

A simple sales-invoice flow often looks like this:

bus invoices add \
  --type sales \
  --invoice-id INV-2026-001 \
  --invoice-date 2026-02-16 \
  --due-date 2026-03-16 \
  --counterparty "Acme Corp"

bus invoices INV-2026-001 add \
  --desc "Consulting" \
  --quantity 10 \
  --unit-price 100 \
  --income-account "Consulting Income" \
  --vat-rate 25.5

bus invoices validate
bus invoices pdf INV-2026-001 --out INV-2026-001.pdf

For migration work, the more common flow is:

bus invoices import \
  --profile ./profiles/erp-invoices.yaml \
  --source ./exports/invoices.tsv \
  --source-lines ./exports/invoice-lines.tsv \
  --year 2025

bus invoices validate
bus invoices list --type sales --month 2025-12

Important behavior

Invoice totals and VAT totals are validated against the line data when those totals are present in the datasets.

list combines filters with logical AND.

--month is the short path for calendar-month filtering. Use --from and --to when you need an explicit date range instead. --month cannot be combined with --from or --to.

--if-missing and --upsert are replay-oriented helpers. They cannot be combined. For line-level <invoice-id> add, they also require an explicit --line-no.

--legacy-replay exists for old non-normalized source data. Keep it as a migration/replay tool rather than your default normal workflow.

If the source system already knows the VAT treatment, store it on the invoice line with --vat-treatment or via the import profile. That canonical line-level field is what downstream VAT tooling should prefer before any journal fallback.

pdf uses its own --out flag instead of the global --output flag.

postings writes invoice-postings.csv and invoice-postings.schema.json into the workspace. It does not stream the posting rows to stdout.

Files

This module owns the sales and purchase invoice header and line datasets at the workspace root. It can also emit invoice-postings.csv and its schema for downstream posting flows.

Output and flags

These commands use Standard global flags. In practice, list is the main command that benefits from output capture, filters, and TSV output. init, add, import, classify --apply, and postings are the main mutation-producing commands where --dry-run is useful.

For the full option and filter matrix, run bus invoices --help.

Exit status

0 on success. Non-zero on invalid usage, schema violations, invalid invoice references, total mismatches, or failed imports.

Using from .bus files

Inside a .bus file, write this module target without the bus prefix.

# same as: bus invoices list --status unpaid --due-to 2026-03-31
invoices list --status unpaid --due-to 2026-03-31

# same as: bus invoices INV-2026-004 add --desc "Support retainer" --quantity 1 --unit-price 1500 --income-account "Services" --vat-rate 25.5
invoices INV-2026-004 add --desc "Support retainer" --quantity 1 --unit-price 1500 --income-account "Services" --vat-rate 25.5

# same as: bus invoices import --profile ./profiles/erp-invoices.yaml --source ./exports/invoices.tsv --source-lines ./exports/invoice-lines.tsv --year 2025
invoices import --profile ./profiles/erp-invoices.yaml --source ./exports/invoices.tsv --source-lines ./exports/invoice-lines.tsv --year 2025