AWRA OpsHub Search

One Rate Per Country Is a Modelling Choice, Not a Fact

A table with one row per country is a claim that each country has one rate. For most of the world that is true. For the ones where it is not, the row is not slightly wrong — it is a category error, and it gets written into an organization's settings and left there.

Accounting Insights Washingtone Aura 10 min read

Somewhere in almost every business application there is a table of countries and tax rates. Ours has ninety-one rows. It is the most boring file in the repository and it is read by code that writes real numbers onto real invoices, which makes it one of the two or three most consequential. This is about what that table asserts, where the assertion fails, and what happens downstream when it does — because the answer turned out to be worse than "the number is wrong".

What one row per country actually claims

A table keyed on country, holding a rate, is a claim with three parts. Each part is true often enough that nobody examines it.

  • That the country has a rate — one number that is the answer for a normal domestic sale.
  • That the rate is a property of the country, rather than of a region within it or of the thing being sold.
  • That the rate is a property of the seller's country, rather than the buyer's.

For most of the world all three hold. Kenya has a VAT rate. So do Uganda, Rwanda, Egypt, Singapore, the Philippines. The model is not lazy — it is correct for the majority of rows, which is exactly why the minority is dangerous. A model that is wrong everywhere gets fixed. A model that is right eighty times and wrong eleven gets trusted.

Four ways the claim fails, and they fail differently

These came out of writing market pages, which is a slower and more useful process than auditing a config file, because writing a page forces you to state the rate out loud to a reader who lives there.

The country Which part of the claim breaks What our row says
Ireland One rate. Five run at once, and the right one depends on what was sold. The standard rate, 23%, which is correct and incomplete.
Canada A property of the country. It is federal plus provincial, and the provincial part varies by province. The federal rate, 5%, which is correct and is not what most Canadian sales carry.
United States All three. There is no national rate, the rate is a state and local matter, and it follows the buyer. An average — labelled as one in the file — of numbers that never apply together.
Brazil A property of the country. The main indirect tax is state-levied and varies by the movement of goods. A single figure that cannot be right in this shape, and a comment saying so.

The temptation, and why we did not take it

The obvious response to a wrong-looking row is to type a better number into it. For Canada that would mean putting a harmonised rate there — right in Ontario, wrong in Alberta, and half the story in Vancouver. Any value we chose would be wrong in most of the country, so the edit would move the error rather than remove it. The defect is the shape of the data, not the value in it, and that distinction is the whole post.

The part that makes this a product defect rather than a content one

A wrong reference figure on a page is embarrassing. A wrong reference figure that is written into an organization's own settings is something else, and that is what ours does. The path is short:

  1. An organization switches its consumption tax on

    A perfectly ordinary thing to do on the day you start invoicing.

  2. The resolver looks up the country and returns one rate

    One country, one row, one number. It has no way to ask which region, which item, or which class of supply, because none of those are in the key.

  3. That rate is written as the organization's default

    Not held as a suggestion or a reference value — stored, as this organization's own default rate, in its own settings.

  4. The provisioning routine returns early, forever

    Once a default row exists it stops looking. There is no re-check, no refresh, and no event that revisits the question. A figure correct for a country and wrong for a business is now that business's configuration.

A reference table that only informs can be wrong and then corrected. A reference table that provisions has already made a decision on somebody's behalf, and correcting the table does not undo it.

That last step is the one worth copying into your own review, whatever you have built. There is a real difference between a lookup that answers a question each time and a lookup that answers it once and persists the answer. The second kind turns every future correction into a data migration, and it does it silently, because the organization's settings look deliberate afterwards. Nobody can tell by looking whether a rate was chosen or provisioned.

How we found it, which is the uncomfortable part

Not from monitoring, and not from a customer report. From writing pages. Stating a country's tax rate on a page that people in that country will read is a much harder test than any validation we had, because it forces the question "is this the number a business here would actually charge?" — which is not the same question as "does this match the authority's headline rate".

Over six waves that exercise has corrected rates in eight countries, changed a tax instrument's classification in three, found a lookup that keyed on a country name with no alias handling — which quietly provisioned four countries at nought per cent — and produced the shape argument above. Every one of those was found by writing prose, and none of them by running the software.

What to check in your own system this afternoon

  • Find the table. It exists, and it probably has a country column and a rate column.
  • Ask whether anything writes from it into per-organization or per-company settings, as opposed to reading it at the moment of calculation.
  • If it writes, find out whether it ever writes again. "Only if empty" is the answer that produces permanent defects.
  • List the countries you support where one rate is not the answer: anywhere federal and regional taxes stack, anywhere multiple rates are in ordinary use, anywhere the rate follows the buyer.
  • For each of those, look at what a real organization there has stored today. Not what should have been configured — what the field contains.

The honest position

We have not fixed the shape. The table still has one row per country, because the fix is a data model that can hold a region and a class of supply, and that is a piece of engineering rather than an afternoon. What we have done is stop pretending the rows are equivalent: the four hard countries carry comments explaining why their value cannot be corrected in this shape, the market pages for them say so in public, and this post exists so the reasoning is somewhere a reader can find it without reading our configuration.

The country tax table, as it stands

What AWRA OpsHub does today

  • Ninety-one country profiles with the tax instrument named rather than assumed — VAT, GST, SST, sales tax and consumption tax are not interchangeable and the file distinguishes them.
  • Alias-tolerant country lookup, added after a name-keyed match silently provisioned four countries at nought per cent.
  • Per-levy arithmetic with its own base, so a stack of levies produces a decomposable total rather than a blended percentage.
  • Comments on the rows that cannot be right in this shape, naming which part of the claim fails for each.

What it does not do

  • A region below country. There is a jurisdiction column on our rate table and nothing selects a rate by it.
  • A rate per class of supply. A country with several rates in ordinary use is represented by its standard rate and nothing else.
  • Any re-provisioning. Once an organization has a default rate, no correction to this file reaches it.
  • A destination-based rate, where the rate follows the buyer rather than the seller.

Not ours, by choice

  • We will not type a plausible number into a row that has no correct single value. It would make the table look complete and make the error harder to find.
  • We will not maintain a rate table for a country with thousands of taxing jurisdictions. That is a specialist service's job and pretending otherwise would be the most expensive kind of confidence.

The work is a jurisdiction and a class of supply in the key, a rate selected on the line rather than on the organization, and a provisioning path that can be revisited when the reference data changes. It is a schema change and we would quote it as one. The precedent that market-specific tax work gets finished here is Kenya: a live tax-authority integration and a maintained statutory payroll engine, both built to specification. Written specification, timeline and price agreed before anything starts.

If you run our product in Ireland, Canada, the United States or Brazil, the practical advice is one sentence: look at what your default tax rate says before you trust a total. It was very likely provisioned rather than chosen, and it looks identical either way.

This is scope, not a ceiling

What is not built for Ireland today can still be built for you

Anything described above as not built is a statement about what ships in the standard product today — not a limit on what AWRA OpsHub can do in Ireland. Kenya's eTIMS integration and its maintained payroll engine exist because Kenyan clients needed them and commissioned them; neither appeared by itself. The same door is open here. If a rate per line rather than per organization, a bank or mobile money feed, a statutory return format, a rule your own operation needs that the standard one does not have, or a link to a system you already run is what stands between you and a decision, tell us and we will scope it as a build — written spec, timeline and price — before you commit to anything.

The three builds, and the first one is the whole thing

A rate selected per line from a set the organization defines, rather than one default rate with a zero override — because a country with five live VAT rates cannot be served by a model with two outcomes, and today an item treatment our resolver does not recognise silently produces the standard rate. Then a stored distinction between zero-rated and exempt, which currently collapse to the same nil on a line and mean different things on a return. Then a return assembled from both. The first build is a schema and resolver change, it is the one without which the other two are decorative, and it would improve every multi-rate market in the product rather than only this one — which is the argument for doing it properly rather than for one country.

Banks and payments

SEPA credit transfers and direct debits, plus bank statement feeds wired into the Payments Register, so money in and out reconciles against documents rather than being re-keyed from a bank screen.

The operational work, which is what most commissions actually are

An extra approval stage in a chain that does not match the standard one, a custom field set on employees or assets that only your sector needs, an expiry that has to block an order rather than send an email, a report your board asks for in a shape nothing produces, or a scanner or weighbridge feeding the goods-in door. These are the commissions we are asked for most often and the smallest ones we quote — and unlike a revenue-authority pipeline, none of them waits on a regulator.

Payroll and statutory returns

An Irish payroll engine with PAYE, PRSI and USC computed on live employee records, submission on each pay run, and pension auto-enrolment assessment. None of it exists today; labour cost attribution to projects and cost centres does.

Systems you already run

The accounting package, CRM, online store or custom database you intend to keep — connected through our API so a fact is entered once and appears everywhere it is needed.

How it works: you describe the requirement, we return a written scope, timeline and cost, and once agreed it is built into your environment and maintained as part of the product. No roadmap slide, and no pretending in a demo that something exists when it does not.

Tell us what you need integrated

The one-line version

One row per country is a modelling choice, not a fact about the world. It is right for most countries and a category error for a few, and the question that separates a tidy defect from a permanent one is whether anything writes from that table into settings it will never revisit.

Check your default rate

If you operate somewhere the one-row model does not fit, the field in your settings is the thing to look at — not the documentation about what it should contain.

Read the Ireland page

Help Center

Need a quick answer while you read?

Run inventory, procurement, assets, sales, and field work with approved AWRA guidance for setup, migration, integrations, security, pricing, and support.

Search all approved AWRA public help articles.

Open Help Center