# invoice (adapter system)

Invoice capability pack: build a full invoice object from line items with correct per-line and global tax/discount math, compute just the totals, roll invoice numbers ('INV-0006' → 'INV-0007'), apply...


Billing capability pack — Studio-aligned, monetizable CRM tools. Pure JS, **zero deps**. The
money-true core for quoting and billing: build invoices from line items, compute totals, roll invoice
numbers, apply late fees, and render a printable invoice.

**Careful money math:** all arithmetic runs in **integer cents** internally, then converts back to
2-decimal dollars on output — so totals never drift (`0.1 + 0.2 ≠ 0.3` can't bite here) even across
many line items. Per-line math (qty × unit → line discount → line tax) happens first; global
tax/discount then fill in for lines that didn't specify their own, and a global discount is distributed
proportionally so per-line tax stays fair.

## Tools (`adapters`)

| tool | args | result |
|------|------|--------|
| `build` | `{ lineItems:[{description,quantity,unitPrice,taxRate?,discount?}], currency='USD', taxRate?, discount?, notes?, from?, to?, invoiceNumber? }` | full invoice `{ invoiceNumber, currency, lineItems:[{...,lineTotal}], subtotal, discountTotal, taxTotal, total, ... }` |
| `nextInvoiceNumber` | `{ last, prefix?, pad=4 }` | `{ next, number, prefix }` — e.g. `'INV-0006'` → `'INV-0007'` |
| `totals` | `{ lineItems, taxRate?, discount? }` | `{ subtotal, discountTotal, taxTotal, total }` |
| `applyLateFee` | `{ total, daysLate, dailyRate }` | `{ originalTotal, lateFee, total }` |
| `toHtml` | `{ invoice }` | `{ html }` — a clean printable HTML invoice string |
| `summary` | `{ invoice }` | `{ summary }` — one-line text summary |

**Discount forms:** a bare number `< 1` is a percent fraction (`0.1` = 10%); a number `≥ 1` is a flat
cash amount; or pass `{ type: 'percent'|'flat', value }` explicitly. `taxRate` is a decimal
(`0.08` = 8%). A per-line `taxRate`/`discount` overrides the global one for that line.

## Usage

```js
const m = await import('./index.js');
const invoice = m.default.adapters;

const inv = invoice.build({
  lineItems: [
    { description: 'Design', quantity: 10, unitPrice: 100 },
    { description: 'Hosting', quantity: 1, unitPrice: 50 },
  ],
  taxRate: 0.1,
});
// inv.subtotal = 1050, inv.taxTotal = 105, inv.total = 1155

invoice.nextInvoiceNumber({ last: 'INV-0006' }); // → { next: 'INV-0007', ... }
invoice.toHtml({ invoice: inv });                // → { html: '<div class="invoice">…' }
```

## Rendering deferred to a-templates

`toHtml` is a pure, dependency-free template literal — good enough to print or email. For **richer,
branded, themeable rendering** (logos, custom layouts, PDF pipelines) defer to the **a-templates**
system rather than growing this pack. `invoice` stays the money-true data core; a-templates owns the
presentation layer.

## CRM fit

`build`/`totals` are the billing core behind quoting and invoicing surfaces in Studio/Web;
`nextInvoiceNumber` and `applyLateFee` handle the lifecycle. Gate/meter via `@leumas/auth` like any
other adapter.

## DRY boundary

`finance` owns generic money math (loans, TVM, currency formatting, ROI). `invoice` owns
document-shaped billing (line items, totals, numbering, HTML). No overlap.


---
Source: shared/engines/adapters/domain/invoice/README.md
Canonical: https://docs.leumas.tech/p/adapters/domain/invoice
