Sequences (Record Numbering)
The Sequence model generates gapless, formatted, auto-incrementing reference numbers —
invoice numbers, order references, lot numbers, and so on. You seed a Sequence as a data
record with a prefix/suffix pattern and padding, then ask for the next value in code.
Not the view ordering field. This page is about the
Sequencemodel for numbering records. It is unrelated to the integersequencefield used to order rows in list/kanban views — that one is just a sort key, not a number generator.
Getting the Next Number
Section titled “Getting the Next Number”The entry point is the classmethod Sequence.get_next:
async def get_next(cls, identifier=None, date=None) -> str: ...identifier— the uniqueidentifierof theSequencerecord (required; raises aUserErrorif missing or unknown).date— the date used to fill date tokens in the pattern and to bucket per-period resets. Defaults to the current date/time when omitted.
It increments the sequence and returns the formatted string. Typical use is inside a create
override, when the record has no name yet:
class SaleOrder(Model): async def create(cls, records): Sequence = get_model("Sequence") for record in records: if not record.get("name") or record.get("name") == "Draft": record_date = record.get("date") or date.today() if isinstance(record_date, str): record_date = date.fromisoformat(record_date) record["name"] = await Sequence.get_next("sale_order_sequence", record_date) return await super().create(records)Always call it as a classmethod on the model — await Sequence.get_next("...") — never by
instantiating it.
Pattern Tokens
Section titled “Pattern Tokens”The prefix and suffix are Python percent-format patterns. The following tokens are
substituted from the date you pass (or now). A pattern with no tokens is used verbatim.
| Token | Meaning | Example |
|---|---|---|
%(year)s | 4-digit year | 2026 |
%(y)s | 2-digit year | 26 |
%(month)s | Zero-padded month | 06 |
%(day)s | Zero-padded day of month | 25 |
%(doy)s | Day of year | 176 |
%(woy)s | Week of year | 25 |
%(weekday)s | Weekday number (0 = Sunday) | 4 |
%(h24)s | Hour (24h) | 14 |
%(h12)s | Hour (12h) | 02 |
%(min)s | Minute | 09 |
%(sec)s | Second | 30 |
The final value is prefix + zero-padded-number + suffix.
Zero-Padded Size
Section titled “Zero-Padded Size”size controls the zero-padding of the numeric portion (default 5). With size: 5 and the
counter at 42, the number renders as 00042. So a sequence with prefix: "INV" and
size: 5 produces INV00042; one with prefix: "PKG/%(year)s/" produces PKG/2026/00042.
Per-Period Resets
Section titled “Per-Period Resets”reset_frequency controls when the counter resets back to 1. Choices:
Never, Daily, Weekly, Monthly, Yearly (default Never).
-
With
Never, the sequence uses a single global counter (next_numberon theSequence). -
With any other value, the counter is tracked per period in
SequenceValuerows keyed by a date key derived from thedateyou pass:Frequency Date key format Example Yearly%Y2026Monthly%Y-%m2026-06Weekly%Y-W%U2026-W25Daily%Y-%m-%d2026-06-25The first call within a new period starts that period’s counter at 1. Combine a reset with a matching date token in the pattern (e.g.
prefix: "INV/%(year)s/"+reset_frequency: Yearly) so the visible number and the reset bucket line up:INV/2026/00001,INV/2026/00002, …, thenINV/2027/00001next year.
Seeding a Sequence (YAML)
Section titled “Seeding a Sequence (YAML)”Ship data_type: Sequence records in your module’s data/ directory.
- data_type: Sequence identifier: package_sequence name: Package Sequence prefix: PKG/%(year)s/ next_number: 1 size: 5
- data_type: Sequence identifier: serial_number_sequence name: Serial Number Sequence prefix: "SN/" next_number: 1 size: 5Fields
Section titled “Fields”| Field | Type | Meaning |
|---|---|---|
identifier | Char | Unique handle passed to get_next (snake_case). |
name | Char | Human-readable label (required). |
prefix | Char | Pattern prepended to the number. |
suffix | Char | Pattern appended to the number. |
next_number | Integer | Next value for the global counter (default 1; used when reset_frequency is Never). |
size | Integer | Zero-padding width of the number (default 5). |
reset_frequency | Selection | Never / Daily / Weekly / Monthly / Yearly (default Never). |
model | ManyToOne (ModelRegistry) | Optional. The model this sequence numbers (informational/grouping). |
How a Transactional Module Uses It
Section titled “How a Transactional Module Uses It”The invoicing module seeds one sequence per document kind and resolves the right one when a document is created:
- data_type: Sequence name: Invoices identifier: customer_invoice_seq model: FinancialDocument prefix: INV- data_type: Sequence name: Credit Notes identifier: credit_note_seq model: FinancialDocument prefix: CNIn the model’s create override, the document’s number is filled by calling
Sequence.get_next("<identifier>", document_date) for the matching kind — the same pattern
shown above for SaleOrder. get_next runs under elevated privileges, reads the current
counter, formats the value, and writes the incremented counter back within the caller’s
transaction.