Numbering sequences
The per-company generator behind every readable document number — jobs, change orders, payments, and more.
Every document Carbon creates gets a : a job is J000001, an engineering change order is ECO-000001, a payment is PAY-2026-07-000001. Those numbers come from — one small counter per document type, per company. Each sequence knows its prefix, its suffix, how wide the number is, how much to step by, and what value comes next. When a document is created, Carbon reads the sequence, formats the next value, and advances the counter.
Sequences live under Settings → Sequences. Every company gets a full set seeded when it's created, and you can retune any of them — change the prefix, restart the counter, widen the padding — without touching code.
What a sequence is
A sequence is a row in the sequence table, keyed by ("table", "companyId") — one counter per document type per company (packages/database/supabase/migrations/20230525025310_sequences.sql:1, primary key moved to ("table", "companyId") in 20240704105128_rfq.sql:120). The table column names the document type it counts ("job", "changeOrder", "payment"), and name is the label you see in the UI ("Job", "Change Order", "Payment").
When something needs a new number, Carbon assembles it from three parts:
{prefix}{next + step, zero-padded to size}{suffix}So a job sequence with prefix J, size 6, step 1, and next at 0 produces J000001, then J000002, and so on. The counter is advanced in the same operation that reads it, so two documents never collide (packages/database/supabase/functions/shared/get-next-sequence.ts:23).
The number is stored on the document, not recomputed
Once a document is created, its number is written to the record and never changes — even if you later edit the sequence. Retuning a sequence only affects documents created after the change. It never renumbers what already exists.
Fields
These are the columns you can tune per sequence. All of them are editable from the sequence form except the document type itself.
Text prepended to the number. Optional. Supports date tokens (below), so PAY-%{yyyy}-%{mm}- renders as PAY-2026-07-. Stored in the prefix column.
The last value handed out — the next column. The next document gets Current + Step. Set this to restart or fast-forward numbering. Must be ≥ 0 (apps/erp/app/modules/settings/settings.models.ts:288).
How many digits the number is zero-padded to. Size 6 gives 000001; size 4 gives 0001. Accepted range is 1–20 (apps/erp/app/modules/settings/settings.models.ts:291). Most sequences ship at 6.
How much the counter climbs each time. Almost always 1. Must be ≥ 1.
Text appended after the number. Optional. Supports the same date tokens as the prefix. Stored in the suffix column.
The sequence form previews the result live as you type, so you can see exactly what the next number will look like before saving (apps/erp/app/modules/settings/ui/Sequences/SequenceForm.tsx:38).
Date tokens
Prefix and suffix can embed the current date, which is how payments and journal entries get their year/month partitioning. The tokens are interpolated when the number is generated, not when you save the sequence:
2026.26.01–12.01–31.00–23.00–59.Hour and second tokens don't work for every document type
Carbon has two code paths that expand these tokens. The document-creation edge functions support all six (packages/database/supabase/functions/lib/utils.ts:33). But some numbers — customers, suppliers, and quotes, which are stamped by a database trigger — go through the get_next_sequence database function, which only understands the four date tokens %{yyyy}, %{yy}, %{mm}, and %{dd} (packages/database/supabase/migrations/20241115101526_rpc-get-next-sequence.sql:42). Putting %{hh} or %{ss} in one of those sequences leaves the literal token in the number. Stick to date tokens unless you know the document type is generated by an edge function.
Which documents use a sequence
A new company is seeded with a sequence for every numbered document type (packages/database/supabase/functions/lib/seed.data.ts:222). These are the defaults — the prefix and starting values you'd see on a fresh company:
| Document type | table | Prefix | Example |
|---|---|---|---|
| Job | job | J | J000001 |
| Quote | quote | Q | Q000001 |
| Sales order | salesOrder | SO | SO000001 |
| Sales RFQ | salesRfq | RFQ | RFQ000001 |
| Sales invoice | salesInvoice | AR | AR000001 |
| Purchase order | purchaseOrder | PO | PO000001 |
| Purchase invoice | purchaseInvoice | AP | AP000001 |
| Purchasing RFQ | purchasingRfq | PRFQ | PRFQ000001 |
| Supplier quote | supplierQuote | SQ | SQ000001 |
| Receipt | receipt | RE | RE000001 |
| Shipment | shipment | SHP | SHP000001 |
| Warehouse transfer | warehouseTransfer | WT | WT000001 |
| Stock transfer | stockTransfer | ST | ST000001 |
| Picking list | pickingList | PL | PL000001 |
| Inventory count | inventoryCount | IC | IC000001 |
| Change order | changeOrder | ECO- | ECO-000001 |
| Issue (NCR) | nonConformance | NCR | NCR000001 |
| Inbound inspection | inboundInspection | II | II000001 |
| Gauge | gauge | G | G00001 |
| Maintenance dispatch | maintenanceDispatch | MAIN | MAIN000001 |
| Customer | customer | CUS | CUS000001 |
| Supplier | supplier | SUP | SUP000001 |
| Fixed asset | fixedAsset | FA | FA000001 |
| Depreciation run | depreciationRun | DR | DR000001 |
| Journal entry | journalEntry | JE-%{yyyy}-%{mm}- | JE-2026-07-000001 |
| Payment | payment | PAY-%{yyyy}-%{mm}- | PAY-2026-07-000001 |
| Credit memo | creditMemo | CR-%{yyyy}-%{mm}- | CR-2026-07-000001 |
| Debit memo | debitMemo | DR-%{yyyy}-%{mm}- | DR-2026-07-000001 |
Payments are a good example of the pattern in action. A payment, a creditMemo, and a debitMemo each have their own sequence; the payment-creation path picks the right one by the memo's direction, so a credit memo draws from CR- and a debit memo from DR- (packages/database/supabase/migrations/20260630093809_ar-ap-payments.sql:567).
A few sequences were re-seeded for existing companies with different prefixes
The seed table above is what a brand-new company gets. When some sequences were added after launch, the migration that backfilled them for existing companies used a different prefix — jobs, for instance, were backfilled as WO for older companies (packages/database/supabase/migrations/20240909194622_jobs.sql:363) even though new companies seed as J. If a prefix on your company doesn't match the table above, it's likely one of these backfilled defaults. Either way, you can change it under Settings → Sequences.
How a number gets assigned
The flow is the same everywhere a number is minted:
A document is created — you convert a sales order line to a job, open a change order, or record a payment.
Carbon looks up the sequence for that document type and company, computes next + step, zero-pads it to size, and wraps it in the (date-interpolated) prefix and suffix.
The counter advances in the same operation — next is written back before the number is returned — so concurrent creates never share a number (packages/database/supabase/functions/shared/get-next-sequence.ts:28).
The formatted number is saved on the new document and stays fixed for its lifetime.
Accounting posts are the heaviest user of this machinery: every GL post — from shipments, receipts, invoices, inventory adjustments, and production events — pulls a fresh journalEntry number as it books, which is why the journal-entry sequence carries a month token by default.
Sequences are company-scoped like everything else in Carbon, so two companies in the same group number their documents independently, even when they share a chart of accounts.