Two invoices issued in the same second, one number. That is not a race condition you fix later — that is an accounting document you now have to explain to an auditor.
Most teams treat the invoice number as a formatting concern. Take a counter, add a prefix, pad it to five digits, render it on the PDF. It works in development, where requests arrive one at a time.
Then two workers hit the same series concurrently. You get a duplicate, or you get a gap — and a gap is not neutral either. In several VAT regimes the sequence itself is the thing being audited: numbers within a series and period must not repeat and must not skip. A missing number reads as a deleted invoice until you prove otherwise.
The fix is boring and structural. The number is allocated by the store, per series and per period, under a lock the database owns — not computed in application code and hoped to be unique. Application code decides which lines appear: domestic sale, private buyer, reverse charge. The store decides which number is legal.
We pulled this apart while building euinvoice, our open-source invoicing work. The split that made it survive: VAT logic is a pure function of the transaction, numbering is a transactional guarantee of the storage layer. Mixing them is how teams end up retro-editing issued documents.
One consequence worth planning for early: a failed invoice after allocation still consumes a number. You cancel it, you do not reuse it.
How does your system handle an invoice that fails validation after the number is already allocated?