Invoicing software charging tax on zero-rated sales
A sale that should carry no tax could be invoiced at 18%, because a zero rate was mistaken for no rate. Nothing warned anyone.
Some of your sales should carry no tax: goods that are zero-rated, customers who are exempt, or a business below the registration threshold. Your invoicing software is told so, and can still send an invoice with 18% on it. Nothing warns anyone. The first person to notice is the customer, or an accountant, weeks later, reconciling a month of invoices at the wrong rate.
That is the fault we found in the sale code of a cold-press oil manufacturer’s system, the one behind Sazinga Factory. It is one line, and it is worth describing because an owner can check for its symptoms.
What was actually going on
The tax rate on a sale was worked out like this: use the rate sent with the request, otherwise the organisation’s default rate, otherwise a figure typed into the code. In that language, a rate of zero counts as “nothing supplied”. So a request saying “zero tax” was treated as if it had said nothing, skipped to the organisation’s default, which was not zero because it was set when the account was created, and from there, if that were missing too, to a number a developer had typed in.
Three separate mistakes sit in that one line.
Zero is a value, not an absence. For anything where zero is meaningful, such as a rate, a discount or a quantity, the software has to tell “not specified” from “specified as nothing”. They are different requests, and once they are mixed, every rule after that inherits the error.
A legal figure does not belong typed into the code. The last fallback was one country’s rate on the day someone wrote the line, with no note saying where it came from. The same number also sits as a default on the organisation record, and again under another name on the purchases record: three copies of one country’s rate that do not know about each other. That is harmless with one country and a migration with two, which came up here, because the product was being prepared for a market with a different rate, a different currency and different identifiers on the invoice.
A chain of fallbacks hides missing settings. An organisation with no rate configured still gets invoices, at a rate nobody chose, and nothing reports that the setting is missing. The silence is how it was designed.
What was right, and what is still open
One thing in the design was correct. The rate is copied onto each sale at the moment it is created, next to the tax amount, instead of being looked up from settings when the invoice is printed. When a rate changes, every invoice already issued keeps its old one. Anything that goes onto a financial document should be a copy taken at the time, because every one of those details can later change in the master record.
What is still open is the fix itself. We recommended that a missing rate should stop an invoice being issued and say which setting is missing, and that zero be accepted as a real answer. As at this writing the original line is still in the code we reviewed, so treat this as a finding and not a repair.
What it did not fix
There is also a second, quieter wrinkle. Whether tax is charged at all depends on a yes-or-no setting saying the business is registered. When it is off, the tax amount is zero, but the rate column on the sale is still filled with the default. So some documents carry a non-zero rate and a zero tax amount, and anyone reconciling by multiplying the taxable amount by the rate will get a figure that does not match the stored tax. The honest version is to write a zero rate when no tax was charged.
Existing records were left as they were. Filling in a rate after the event means deciding what the rate was, and the only honest source for that is the document itself. Where the stored rate and the stored tax disagree, the tax is what the customer paid, and that number stays.
The pattern, for anyone issuing invoices from software
Raise a test invoice for an exempt customer and another for a zero-rated item, and read the tax line. Then ask where your jurisdiction’s numbers live: the rate, the threshold, the start month of the financial year. If the answer is “in several places”, changing country or rate will mean finding every copy.
Ask, too, what the software does when a setting is missing. It should refuse and say which one. An annoying error on the day a customer is set up costs far less than a month of invoices at a rate nobody chose.
Where this ends up
The tax rate and tax amount in Sazinga Factory are copied onto the sale record at the moment it is created, so a later change to a rate never reaches back into an invoice that has already gone out.