Skip to Content

Odoo 19 document numbers from prefix, year and counter – module document_number_prefix_year and the trap with simultaneous creation

3 November 2026 | Odoo 19 Community | In-house development
3 November 2026 by
Odoo 19 document numbers from prefix, year and counter – module document_number_prefix_year and the trap with simultaneous creation

Odoo 19 module document_number_prefix_year: document numbers from prefix, year and counter

“AN26000001” says more than “S00001”: it is a quotation, it dates from 2026, and it is the first one. Many companies, especially those moving to Odoo from an older system, want to keep their document numbers exactly like this. Odoo itself numbers quotations, orders and delivery notes with its own sequences (S00001, WH/OUT/00001, P00001) and invoices with the journal prefix and the year (INV/2026/00001).

We wrote the module Document Numbering by Prefix and Year (document_number_prefix_year) for Odoo 19 for this purpose. It is available as free software under the LGPL-3 licence on GitHub:

github.com/Alex0176/odoo-document-number-prefix-year

An honest note up front: We do not use the module at Biricon IT Services. We are publishing it because it is finished, documented and equipped with tests, and can save others work. Anyone who uses it should read the sections on Limits and Tests .

The numbering scheme

Every number consists of three parts: a prefix per document type, the year and a counter with a fixed number of digits. You set the prefix, the year format (26 or 2026) and the number of digits per company under Settings → Document numbering.

Structure of the document number AN26000001 from prefix, year and counter, with a table of document types

Quotations, customer orders, customer invoices and credit notes, delivery notes, goods receipts and purchase orders are covered. The prefixes in the figure are only examples: AN, AU, RE, GU, LI, WE, BE. If you leave a prefix empty, Odoo's standard numbering is kept for that document type. The module therefore does nothing until you configure it.

How the numbers behave

  • New every year. The counter starts again at 1 at the turn of the year.
  • Continuing after imported numbers. The module looks for the highest existing number with the same prefix and year, regardless of its length. If AN26119 from the legacy system is in the database, the next quotation is AN26000120. This is why the module is suitable for migrations: old documents are imported with their original numbers, and new documents follow on seamlessly.
  • New order number on confirmation. On request, a confirmed quotation receives a number from the order sequence. The quotation number is retained in the Quotation number field on the order and is visible in the form, list and search.
  • Invoices on posting. Customer invoices and credit notes receive their number only on posting, not on creation. Deleted drafts therefore leave no gap. The year comes from the invoice date: an invoice dated 30 December 2025 that is posted on 2 January 2026 is called RE25….
  • Everything else stays. Vendor bills, payments, journal entries and manufacturing orders keep Odoo's standard sequences. Existing names are never rewritten, even if you switch the module off again.

The tricky part: two users at the same time

A custom numbering system looks simple: find the highest number, add one. It is not, as soon as two users create a quotation at the same moment. Both read “highest number = 0”, and both assign AN26000001. For its own sequences, Odoo has database sequences. A self-built number has no such thing, and for quotations and purchase orders Odoo also has no uniqueness check on the name: two documents with the same number are simply saved.

The obvious protection is a lock. The module takes a database lock (PostgreSQL advisory lock) per company, prefix and year, held until the end of the transaction. The second user waits until the first has finished. But that is not enough, and we found this out through an experiment on a copy of the production database:

Timeline of two users: without a second read, both assign the same number; with a second read, the second user gets the next number

Odoo works with the isolation level REPEATABLE READ. The database state that a transaction sees is frozen at its first query. User B already has this state before A has finished. If B then waits for the lock and gets it, B reads “highest number = 0” with its old state and assigns the number a second time. The lock regulated the order, but did not update the knowledge.

The solution is a second read via a separate, short-lived database connection that sees all rows already committed. The larger of the two values wins. The first read remains necessary, because only it knows numbers that the same transaction has already assigned but not yet committed, for example when importing several documents in one go.

We verified this with two transactions in which the second demonstrably took its snapshot before the first one's commit and then waited for the lock. Without the second read, both orders received the same number; with it, they received numbers 1 and 2.

Important for putting this in context: This experiment is a manual check. It is not part of the automated tests, because that would require two real database connections, which Odoo's test environment (a single transaction per test) does not provide.

Technical implementation

DocumentHook point
Quotationsale.order.create, if the name is empty, / or New
Customer ordersale.order.action_confirm, before the standard confirmation
Invoice, credit noteaccount.move._set_next_sequence, wird beim Buchen aufgerufen
Delivery note, goods receiptstock.picking.create nach Vorgangsart
Purchase orderpurchase.order.create
  • Counter. The highest number is found by a regular expression (^<Stamm>([0-9]{1,15})$, the stem is escaped) per company, including names without a company.
  • Invoices. _set_next_sequence writes the name and calls _compute_split_sequence, so that Odoo's sequence logic (prefix, number, year detection for the journal's next invoice) remains consistent.
  • Settings. The company fields document_number_* are mirrored in res.config.settings. The settings reject prefixes with spaces or a digit at the end.
  • Translation. A German translation is included.

Installation and set-up

  1. Put the folder document_number_prefix_year into the addons path.
  2. Update the apps list and install the module. The prerequisites are Sales, Purchase, Inventory and Accounting.
  3. Enter prefixes, year format and number of digits under Settings → Document numbering.
  4. Try it on a copy first. The number of a document cannot be rolled back automatically later, and for invoices the number is tied to retention and audit obligations.

Limits

Six limits of the module: shopping carts, year format, one series per company, prefix rules, few tests, only the listed documents

  • Quotations are numbered on creation. Deleted or cancelled quotations leave gaps. The same applies to every customer order created by another module: web shop shopping carts (website_sale) are orders in the Quotation state and each consumes a number. Only invoices and credit notes are gap-free, because they are numbered on posting.
  • Do not change the year format during live operation. With the stem AN26, the module reads an existing AN2026000001 as the counter 26000001. Fix the format before the first document.
  • Counter at most 15 digits. Longer digit sequences after the stem are ignored when searching for the highest number.
  • No separate series per journal or warehouse. The counter applies per company, prefix and year. Several sales journals share the invoice counter.
  • Prefixes must not contain a space and must not end in a digit, otherwise prefix, year and counter can no longer be separated.
  • Only the listed document types are handled.

Tests

The module comes with ten automated tests. They check:

  • Quotation and confirmation: quotation and order number, retention of the quotation number
  • Continuing after an imported number (AN26119 → AN26000120)
  • no retrospective renumbering of existing documents
  • Invoice, credit note and change of year by invoice date
  • Purchase order and delivery notes
  • empty prefix: Odoo's standard number remains
  • four-digit year
  • prefix with special characters
  • several numbers in one transaction
  • very long digit sequences are ignored

Call:

odoo-bin -d <Testdatenbank> -i document_number_prefix_year --test-enable --test-tags=/document_number_prefix_year --stop-after-init

The tests run with ordinary transactions. The behaviour when two users create documents simultaneously is not part of them; it was checked by hand on a database copy, as described above. Anyone using the module in a system with many simultaneous users should repeat this experiment on their own database.

Legal note on the invoice number

Under § 11 UStG (the Austrian VAT Act), invoices must bear a consecutive number that is assigned once for identifying the invoice. The module numbers invoices on posting and with its own counter per prefix and year, which fits this. Whether your specific configuration (several journals, credit notes with their own prefix, change of year) meets your tax adviser's requirements, you must clarify with them. This article is not legal advice.

Frequently asked questions

Why not simply adapt Odoo's sequences?

For invoices that is possible via the journal prefix. For quotations, orders, delivery notes and purchase orders, however, there is no common scheme of prefix, year and counter with a change of year, and the counter does not know imported legacy numbers. That is where the module comes in.

Is existing numbering overwritten?

No. The module only assigns new numbers. Names that have already been saved remain unchanged, even when it is switched off.

How do I switch it off again?

Empty the prefix of the document type. Odoo then numbers it itself again. Uninstalling does not change any numbers already assigned either.

Do you use it yourselves?

No. We run Odoo's standard numbers. We have tested the module with the automated tests and the manual experiment described, but not in live operation with many users.

Does it run with Odoo 17 or 18?

We have only tested it with Odoo 19.0 Community. For other versions it may or may not fit; we have not tested it.

Under which licence is it released?

Under the LGPL-3. Bug reports and improvements are welcome as an issue in the GitHub repository .

Support from Biricon IT Services

We look after Odoo installations for small and medium-sized businesses: from implementation through customisations and migrations to ongoing operation. If you want to keep your document numbers when switching, have the number assignment tested under load, or want to bring a module like this one safely into your production instance, we will be glad to help. More in-house developments: Firmenbuch and address validation for Austrian customers, Product sets in the quotation as a section and Web RBL, the blocklist for attack traffic.

Get in touch now

As of: 3 November 2026. Module version 19.0.1.0.0. All numbers in the examples and figures are freely chosen.