Document Numbering
FlowPOS assigns every document two numbers:
| Field | Scope | Used for |
|---|---|---|
documentNumber | (businessId, documentType) | Business-wide reference, FEL/SAT, AR/AP cross-location |
locationDocumentNumber | (businessId, locationId, documentType) | Display-only — receipts, POS UI, store-local search |
Both numbers are assigned atomically at creation time inside the same DB transaction. No backfill is performed on historical records.
Dual-numbering model
documentNumber
The existing business-scoped sequence. Incremented from the document_counter table keyed on (business_id, document_type). Formatted as {prefix}{000000} (6-digit zero-padded; prefix defaults to "" for most types).
This is the authoritative reference for:
- FEL/SAT electronic invoice payloads (
felNumber,felAuthorization) - Accounts Receivable / Accounts Payable cross-location lookups
- Any external integration (certifiers, integrations)
locationDocumentNumber
A new location-scoped sequence. Incremented from document_location_counter keyed on (business_id, location_id, document_type). Formatted as a bare 6-digit zero-padded number (no prefix).
This is:
- Display-only — shown on receipts, POS UI, and list views for store staff
- Separate per location — location A and location B each have their own
000001counter - Nullable — when a document has no
locationId,locationDocumentNumberisnull(graceful skip) - Never sent to FEL certifiers, SAT, or any financial integration
FEL/SAT boundary
Documents going through FEL certification always use documentNumber only. Code integration points are annotated with:
// FEL: business-scoped documentNumber only — locationDocumentNumber is display-only and not sent to SAT/certifier
Graceful skip (no location)
Some documents allow creation without a locationId (e.g. purchases, credit/debit notes). When locationId is absent or null, only documentNumber is assigned and locationDocumentNumber remains null. No error is thrown. This makes the feature strictly additive — existing API clients are unaffected.
Transfer documents
For the transfer family (inventory_transfer, transfer_request, transfer_dispatch_note, transfer_goods_receipt), the location counter uses originLocationId as the scope. The destination location is not used for counter keying.
Service API
All create flows use the generateDocumentNumbers facade in apps/backend/src/common/services/document-number.service.ts:
const { documentNumber, locationDocumentNumber } = await generateDocumentNumbers({
trx,
businessId,
documentType: DocumentType.SALE,
locationId: sale.locationId ?? undefined, // graceful skip when null
});
The legacy generateDocumentNumber function remains unchanged for excluded flows (AR/AP/service-bookings which have no locationId column).
Counter tables
document_counter (existing)
| Column | Notes |
|---|---|
business_id | FK → business.id |
document_type | varchar |
last_number | integer |
prefix | varchar (optional; rarely used) |
reset_frequency | varchar (column exists but reset logic is deferred — no admin UI yet) |
Unique: (business_id, document_type).
document_location_counter (new)
| Column | Notes |
|---|---|
business_id | FK → business.id |
location_id | FK → location.id |
document_type | varchar |
last_number | integer |
Unique: (business_id, location_id, document_type).
No prefix or reset_frequency columns (reset logic deferred).
Counter configuration
Counters are created automatically on first use via an upsert-style SELECT … FOR UPDATE → INSERT. There is no admin UI for manually configuring starting numbers or prefixes. Direct DB seed is the current approach when needed.
Per-period reset (deferred)
The document_counter.reset_frequency column exists (values: never | daily | monthly | yearly) but the reset logic has not been implemented — there is no admin UI to configure it. This is intentionally deferred until a configuration screen is available.
Excluded document types
The following document types remain business-scoped only (no location counter) because their tables have no location_id column:
accounts_receivable_invoiceaccounts_receivable_receiptaccounts_payable_billaccounts_payable_paymentservice_booking
Where locationDocumentNumber is shown
Backend API
Returned on all list/detail endpoints for in-scope document types. Searchable via locationDocumentNumber query parameter on repositories that already support documentNumber search (quotes, GRN, purchase orders, transfers, production runs, etc.).
Thermal receipts
Rendered as a "Tienda: {number}" line below the business documentNumber line (when present). Label is overridable via ReceiptLayoutOptions.labels.store.
PDFs
Exposed as locationDocumentNumber in PDF data transformers (SalePdfData, OrderPdfData, QuotePdfData, purchase/inventory/transfer types, etc.). Handlebars templates render it as Tienda: when the value is present.
Covered template families (apps + packages/backend/database/src/templates/ mirror):
- Receipts / thermal:
sale-receipt,sale-receipt-thermal,order-bill-receipt,order-bill-receipt-thermal,order-receipt-thermal,cash-register-session,cash-register-session-thermal - Full PDFs:
sale,order,order-bill,quote,purchase,purchase-order,goods-received-note,inventory-adjustment,inventory-transfer,transfer-request,transfer-dispatch-note,transfer-goods-receipt,contractor-assignment,material-consumption,production-run
Out of scope for templates: AR/AP, service bookings, Z-report session rows, static FEL credit/debit note templates, stock-count PDFs.
Print-bridge: Thermal rendering uses @flowpos-workspace/receipt-layout buildDocumentReceiptLines, which includes the same Tienda: line for sale, order, order-bill, and cash-register-closing payloads.
DB-custom templates: Merchants who override templates in the database must add the {{#if locationDocumentNumber}} block manually; seeded defaults include it after migration.
PWA
Store locationDocumentNumber is the primary identifier in POS headers, toolbars, and search/list tables. Business documentNumber appears as a muted secondary line when both numbers exist.
Sale text search matches both documentNumber and locationDocumentNumber (same as quotes).