How I believe Xero pulled off implementing their Invoice Numbering System
Tags: accounting, invoicing, ruby, rails
Published: 2020-04-23 14:35:40

---
title: How I believe Xero pulled off implementing their Invoice Numbering System
published: true
description: How I believe Xero pulled off implementing their Invoice Numbering System
tags: accounting, invoicing, ruby, rails
---

During this lockdown, I thought I would learn accounting. I love accounting, or anything with numbers, but never fully get a chance to act on that thought.

[Xero Central](https://central.xero.com/s/) is my goto for anything accounting. You could pretty much read their entire documentation and API to get an understanding of how things work or accounting in general. This is what I did. I've decided to focus on the invoicing section. I also like their invoice interface that I made mine the similar (kids, do not copy 😈):

![Alt Biliable](https://dev-to-uploads.s3.amazonaws.com/i/8begmorx1qvbq5fvf23c.png)

This post is about how I've done the invoice numbering. I know there should be a better way but I could only come up with this method. Using Rails 6 API, I'll show you how I've pulled it off.

## The Invoice Settings

This model is responsible for validating your chosen invoice sequence ie `INV-001`. `INV-` is used as the prefix and `001` is used as a numeric sequence. Sequence can only be numeric. Should you try to enter an alpha-numeric value, it will fail. Note this has nothing to do with the actual invoice number. We'll get to that in a bit.

```rb
class InvoiceSetting < ApplicationRecord
  belongs_to :organisation
  validates  :invoice_number_sequence, :quote_number_sequence, :purchase_order_number_sequence, should_be_sequencialised: true
  validate   :check_for_duplicate_numbers

  private
  # TODO: Check for credit note for invoice number
    def check_for_duplicate_numbers
      exists = Invoice.exists?(['lower(invoice_number) = ? AND organisation_id = ?', "#{(invoice_prefix + invoice_number_sequence).downcase}", organisation_id])
      if exists
        errors.add(:invoice_number_sequence, "invoice number already exists.")
      end
    end
end
```

So what is `should_be_sequencialised`?

We use a Rails concerns to implement this logic. This logic ensures the sequence value is numeric:

```rb
# /models/concerns/should_be_sequencialised_validator.rb

class ShouldBeSequencialisedValidator < ActiveModel::EachValidator
  # The Client should also ensure users only enter numeric values ie 001, 1 etc.
  def validate_each(record, attribute, value)
    begin
      Integer(value)
    rescue StandardError => e
      record.errors.add(attribute, "Invalid number. Please use numeric numbers only")
    end
  end
end
```
`validate_each` accepts three arguments: model, model's column name and the incoming value to be checked. `EachValidator` will loop this method, in our example, 3 times. Later I'll refactor to only validate on `invoice_number_sequence`.

`Integer` will throw an exception so it should be wrapped in catch block. When there's an error, we add the error message back to our model for the front-end to display. Your data will be rolled back. A neat feature with Rails and other frameworks.

`check_for_duplicate_numbers`

Should the first validator succeeds, `check_for_duplicate_numbers` checks for existing invoice numbers already generated. Pretty easy to understand. 

That takes care of the validation but how does an invoice number increments?

[Jörg W Mittag](https://stackoverflow.com/questions/61304958/how-to-increment-the-number-in-a-string-while-keeping-the-appropriate-trailing-l/61305084?noredirect=1#comment108468848_61305084) on Stack overflow says:

> Then it's not a number but rather an "invoice identifier object", and you model it as an InvoiceIdentifier

I've "Googled" what's an InvoiceIdentifier but nothing came up. I should have asked for him to explain but I never wanted to extend the post when I've already selected an answer, so I left it as is. If `invoice_number` is not a string then what is it? I've created a model called `invoice` with a string attribute as `invoice_number`. I've used string because I cannot find information regarding this `InvoiceIdentifier`.

Let's validate our invoice model to ensure `invoice_number` is unique. This is only unique for each of the user's organisation:

```rb
class Invoice < ApplicationRecord
  validates   :invoice_number, uniqueness: { case_sensitive: :false, scope: :organisation_id }
  [...] # Other validations and relationships etc
end
```

We'll now focus our attention over to `invoice_controller.rb`:

## Invoice Controller

```rb
  # POST /organisations/:organisation_id/invoices
  def create
    invoice = @organisation.invoices.build(invoice_params)
    if invoice.save
      return render json: invoice
    end
   
    render json: {error: invoice.errors.messages}
  end
```

I won't explain what's going on here as this is not a beginners guide. At this point we should be able to create an invoice. If it fails then our `invoice_number` is not unique.

## How and when does the invoice number auto increments?

The [Wisper gem](https://github.com/krisleech/wisper) is a great tool for listening to events. We'd listen for two events: create and update (this example for focuses on create). This is when we would increment our invoice number or leave it as is. Let's look at this `IncrementInvoiceNumber` class:

```rb
# /lib/invoices/increment_invoice_number.rb
module Invoices
  class IncrementInvoiceNumber
    include Wisper::Publisher
  
    def create(invoice, organisation)
      if invoice.save
        puts "-----INVOICE CREATED------"
        increment_invoice_number(invoice.invoice_number, organisation)
        broadcast(:invoice_created, invoice)
      else
        puts "-----INVOICE FAILED------"
        broadcast(:invoice_failed, invoice)
      end
    end

    private
      def increment_invoice_number(inv_num, organisation)
        settings = organisation.invoice_setting
        invoice_setting_prefix = settings.invoice_prefix

        arr = inv_num.split(invoice_setting_prefix)
        if arr.count == 2
          # We have an untouched invoice number
          incremented_sequence = arr[1]

          # We need to test if it's a numeric value
          begin
            Integer(incremented_sequence)
            # We can safely increment and save
            incremented_sequence = incremented_sequence.next
            organisation.invoice_setting.update(invoice_number_sequence: incremented_sequence)
          rescue StandardError => e
            # We do nothing
          end
        end

        # At this point, we have not incremented the invoice number that came in as they've entered a custom invoice number.
      end
  end
end
```

Our create method is self-explanatory so let's look at `increment_invoice_number`.

We increment the invoice number only if the prefix is the same as when you're creating a new invoice. See main image, but we still need to check as a user could simply enter `INV-foo`. If so, we save the invoice as is and only increment if the sequence is of a numeric value. At this point, validation has passed and invoice number is unique.

`foo.next` will also increment to `fop` but our validation checks if that section is numeric with `Integer(string)`.

Once you've install Wisper, our `create` method now becomes:

```rb
 # /controllers/invoice_controller.rb

 # POST /organisations/:organisation_id/invoices
  def create
    service = Invoices::IncrementInvoiceNumber.new
    service.on(:invoice_created) { |invoice| render json: invoice }
    service.on(:invoice_failed) { | invoice |render json: {error: invoice.errors.messages} }

    service.create(@organisation.invoices.build(invoice_params), @organisation)
  end
```
Incrementing an invoice number only runs after an invoice is created. Is there a better way? I would love to know. If you're a Xero dev, I'd love to hear your thoughts and it does not matter what programming language you guys are using.