Generating unique invoice numbers in Ruby on Rails
I’m currently building a subscription tracker with reminders. While building the subscription part of this service I of course need to generate invoices. In The Netherlands we’re required to have a unique invoice number on them. How do you build something like that in Ruby on Rails?
We first need to validate that the numbers we store are actually unique. Validations in Ruby have some race conditions, where two might be generated at the same time without either one noticing. So, there is only one place where you can be sure of uniqueness: in the database. I’ve added a unique index on the invoice number column to fail hard if something might generate a duplicate.
add_index :invoices, :number, unique: true
Next we need to build the actual generation of unique numbers. I’ve chosen to build an InvoiceService which is responsible for creating invoices and generating unique numbers. The numbers are in the format of year-number, for example 2021-123. For this I’ve made a function that that looks up the previous invoice number parts. Something like this works:
def last_invoice_number_parts current_year = Time.zone.now.year.to_s last_invoice = Invoice.order(Arel.sql('substring(number from 6)::int')).last if last_invoice&.number&.start_with?(current_year) last_invoice.number.split('-') else [current_year, 0] end end
Now the tricky part, how do we generate a new number and make sure it’s unique when stored? Regenerate and save again when you catch the ActiveRecord::RecordNotUnique exception on save. If that happens it means we need to generate a new incremented number and should thus try again. Something like this should work:
def generate_invoice_number parts = last_invoice_number_parts @invoice.number = "#{parts.first}-#{parts.last.to_i + 1}" @invoice.save! rescue ActiveRecord::RecordNotUnique retry # another database record was just with the same number end
That’s it. Let’s hope this function will generate a lot of unique invoice numbers for us!