Skip to content
Devix Open Source
Library v1.0.0 MIT Alpha

VAT Calculator

VAT and GST maths for the UAE, Saudi Arabia and Pakistan.

npm install @devix-labs/vat-calculator
Vanilla JS PHP WordPress
An invoice, rounded two ways Open

The work in this space is EU work: the biggest package has 4.5 million installs and covers the EU, while the Gulf has had VAT since 2018 with nothing for it in either registry. This has rates with effective dates, so a credit note against a 2019 Saudi invoice uses 5% and not 15%; extraction done as gross × rate ÷ (1 + rate) rather than the gross times the rate, which overstates tax by the rate squared; integer minor-unit arithmetic so a thousand lines do not drift; and per-line against per-total rounding as a decision rather than an accident, because the two differ and authorities specify which. Zero-rated, exempt and reverse charge stay distinct through the breakdown. 2.0 kB, zero dependencies.

What you get

Rates that have a history

Saudi Arabia went 5% to 15% on 1 July 2020, Bahrain 5% to 10% in 2022, Pakistan 17% to 18% in 2023. A credit note against an older invoice needs the older rate, and one number per country gets that wrong silently.

The tax inside a gross amount

115 × 0.15 is 17.25. The tax actually inside 115 at 15% is 15.00. The correct sum is gross × rate ÷ (1 + rate), and getting it wrong overstates by the rate squared while looking entirely reasonable.

Never a float

0.1 + 0.2 is not 0.3, and an invoice of a hundred lines drifts by a fils or two — enough for the invoice to disagree with the return. Everything is whole fils from the moment it arrives.

Line rounding is a decision

Ten lines of 0.30 at 5% give 0.20 taxed per line and 0.15 taxed on the total. Both are correct somewhere, authorities specify which, and here it is an option rather than an accident.

Zero-rated is not exempt

Both are 0% and they are not the same: one recovers input tax, one does not, and reverse charge moves the liability entirely. The breakdown keeps them apart, because a return does.

The right number of decimals

Two for the dirham, riyal, rupee and pound; three for the Bahraini, Omani, Kuwaiti and Jordanian dinar. Taken from the country, or set yourself.

It refuses rather than guesses

A thousands separator or Arabic-Indic digits throw instead of silently parsing to something wrong. An exception at the point of entry beats a plausible figure on an invoice.

Straight into a ZATCA QR code

The gross total and the tax come out as the strings the Saudi e-invoice QR wants, so the two packages line up without depending on each other.

VAT Calculator — overview

Why it exists

Search either registry for VAT calculation and you find EU packages: the biggest has four and a half million installs and covers the EU; the next has fifteen thousand a week and covers the EU and the US; one was last published in 2015.

The UAE and Saudi Arabia have had VAT since 2018, Bahrain since 2019, Oman since 2021, and Pakistan's sales tax has been in place for decades. There is nothing for any of them.

What it does differently

  • Rates with effective dates. Saudi Arabia went from 5% to 15% on 1 July 2020; Bahrain from 5% to 10% in January 2022; Pakistan from 17% to 18% in February 2023. A credit note against an older invoice must use the rate that applied then. A single number per country produces a plausible wrong answer that only surfaces in an audit.
  • Extraction done properly. The tax inside a gross amount is gross × rate ÷ (1 + rate), not gross × rate — an error that overstates the tax by the rate squared and looks entirely reasonable on the invoice.
  • Integer arithmetic throughout. Every amount becomes a whole number of fils, halalas or paisa on arrival. Floats drift, and a drifting invoice disagrees with the return it belongs to.
  • Rounding as a decision, not an accident. Taxing each line and summing gives a different answer from taxing the total — 0.20 against 0.15 on ten lines of 0.30 — and authorities specify which. Both are available and the difference is documented.
  • Treatments kept apart. Zero-rated, exempt, out-of-scope and reverse charge are all 0% and all different; two of them change what input tax you can reclaim. The breakdown reports them separately, because a return does.
  • 2.0 kB, no dependencies, and the totals drop straight into a ZATCA e-invoice QR code.

Not in 1.0

Place-of-supply rules. Whether a cross-border service is taxed where the supplier is or where the customer is, and when reverse charge applies, is a discipline rather than a lookup. treatment: 'reverse-charge' records the decision; it does not make it.

Sector rates and provincial services tax. Pakistan's provinces tax services at their own rates and the Gulf has sector-specific treatment. Encoding those would mean a table that is out of date before it ships, so the standard rates are there and everything is overridable.

Being an authority. Rates change. The table carries effective dates, rate overrides it, and the docs say to verify against the tax authority — which is the honest position for a library that cannot be updated the day a budget passes.

How it compares

Questions

Is this tax advice?

No. Rates change, sectors have their own treatment, Pakistan's provinces tax services at their own rates, and place-of-supply rules are a discipline of their own. The table carries effective dates and is plain data, every rate can be overridden with a rate of your own, and you should verify against the authority — which is the honest position for a library that cannot be updated the day a budget passes.

Why does my total differ from my accounting system by a fils?

Almost certainly line-level against invoice-level rounding. Taxing each line and summing gives a different answer from taxing the summed total — 0.20 against 0.15 on ten lines of 0.30 — and both are correct somewhere. Set rounding_level to match whichever your authority and your system use, because the invoice and the return have to agree.

Why are amounts strings?

Because that is what goes on an invoice and into a return, and because a float cannot hold them exactly. Internally everything is a whole number of fils; the result carries those in minor so the next calculation does not start from a parse.

What does null mean for Qatar and Kuwait?

That they have announced VAT and not introduced it, so an invoice there carries no tax line at all. That is a different thing from a zero-rated supply, which is taxable at zero, so the answer is null rather than 0 and asking for a calculation throws.

Does it validate VAT numbers?

No — that is Devix Validators, which covers UAE TRN, Saudi VAT numbers, CNIC, NTN, IBAN and the rest with reason codes. This one does the arithmetic.