What is ir.sequence?
Odoo's ir.sequence model generates sequential numbers for documents — invoice numbers, purchase order references, employee IDs, ticket numbers, and any other record that needs auto-incrementing identifiers. Understanding how to configure and use sequences correctly prevents numbering gaps, duplicate numbers, and regulatory compliance issues.
Basic Usage
Getting the Next Number
# In a model's create method
reference = self.env['ir.sequence'].next_by_code('my.model')
# Or with a specific sequence record
sequence = self.env['ir.sequence'].browse(sequence_id)
reference = sequence.next_by_id()Defining a Sequence in XML
<record id="seq_purchase_request" model="ir.sequence">
<field name="name">Purchase Request</field>
<field name="code">purchase.request</field>
<field name="prefix">PR/%(year)s/</field>
<field name="padding">5</field>
<field name="number_increment">1</field>
<field name="number_next_actual">1</field>
</record>This generates numbers like: PR/2026/00001, PR/2026/00002, etc.
Prefix and Suffix Patterns
Available Variables
| Variable | Output | Example |
|---|---|---|
| %(year)s | 4-digit year | 2026 |
| %(y)s | 2-digit year | 26 |
| %(month)s | 2-digit month | 03 |
| %(day)s | 2-digit day | 24 |
| %(doy)s | Day of year | 083 |
| %(woy)s | Week of year | 13 |
| %(h24)s | Hour (24h) | 14 |
| %(min)s | Minutes | 30 |
| %(sec)s | Seconds | 45 |
Common Patterns
<!-- INV/2026/00001 -->
<field name="prefix">INV/%(year)s/</field>
<!-- PO-2026-03-00001 -->
<field name="prefix">PO-%(year)s-%(month)s-</field>
<!-- 00001/2026 (number first, then year suffix) -->
<field name="suffix">/%(year)s</field>
<!-- WO-00001 (no date component) -->
<field name="prefix">WO-</field>Year Reset
Reset the counter to 1 at the start of each year, month, or other period:
<record id="seq_invoice" model="ir.sequence">
<field name="name">Invoice</field>
<field name="code">account.invoice</field>
<field name="prefix">INV/%(year)s/</field>
<field name="padding">4</field>
<field name="use_date_range">True</field>
</record>With use_date_range=True, Odoo creates separate counters per date range. The first invoice of 2027 will be INV/2027/0001 regardless of how many 2026 invoices exist.
No-Gap Sequences
Some regulations require no gaps in numbering (especially for invoices). Odoo supports this:
<field name="implementation">no_gap</field>Key differences between standard and no-gap:
- Standard — Uses PostgreSQL SEQUENCE objects. Fast but may have gaps if transactions roll back.
- No-gap — Uses table-level locks. Slower but guarantees consecutive numbers. No gaps even on rollback.
Use no-gap only when required by regulation. It creates a performance bottleneck under high concurrency because it locks the sequence row.
Company-Specific Sequences
In multi-company setups, create per-company sequences:
<record id="seq_invoice_company_1" model="ir.sequence">
<field name="name">Invoice - Company A</field>
<field name="code">account.invoice</field>
<field name="company_id" ref="base.main_company"/>
<field name="prefix">COMP-A/%(year)s/</field>
<field name="padding">5</field>
</record>When next_by_code() is called, Odoo automatically selects the sequence matching the current user's company.
Using in Model Create
class PurchaseRequest(models.Model):
_name = 'purchase.request'
name = fields.Char(
string='Reference',
readonly=True,
copy=False,
default='New',
)
@api.model_create_multi
def create(self, vals_list):
for vals in vals_list:
if vals.get('name', 'New') == 'New':
vals['name'] = self.env['ir.sequence'].next_by_code(
'purchase.request'
) or 'New'
return super().create(vals_list)Common Pitfalls
- Sequence not found —
next_by_code()returns False if no sequence exists with that code. Always handle the fallback. - Gaps in no-gap — If records are deleted, gaps appear. No-gap prevents gaps from rollbacks, not deletions.
- Date range initialization — First call in a new year auto-creates the date range entry. Verify padding carries over.
- Concurrency with no-gap — Under high load, no-gap sequences become a bottleneck. Use standard sequences unless regulation requires no-gap.