Skip to content
Devix Open Source

Guide

Getting started

npm install @devix-labs/vat-calculator
import { vat, extract, invoice } from '@devix-labs/vat-calculator';

vat(100, { country: 'SA' });
// { net: '100.00', vat: '15.00', gross: '115.00', rate: 0.15, currency: 'SAR', … }

extract(115, { country: 'SA' });
// the same figures, worked backwards out of a gross amount

Every figure comes back as a string, because that is what goes on an invoice and into a tax return. minor carries the same numbers as whole fils for any arithmetic that follows.

The three things this gets right

The tax inside a gross amount is not the gross times the rate. 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 the tax by the rate squared. extract() does it properly.

Money is 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. Every amount becomes an integer number of fils the moment it arrives.

Rates have history. Saudi Arabia went from 5% to 15% on 1 July 2020, so a credit note against a 2019 invoice needs 5%:

vat(100, { country: 'SA', on: '2019-06-01' }).vat;   // '5.00'
vat(100, { country: 'SA' }).vat;                      // '15.00'

A whole invoice

invoice([
  { amount: '10.00', quantity: 2, description: 'Consulting' },
  { amount: '50.00', treatment: 'zero-rated', description: 'Exported goods' },
  { amount: '30.00', treatment: 'exempt', description: 'Financial services' },
], { country: 'SA' });
{
  net: '100.00', vat: '3.00', gross: '103.00',
  breakdown: [
    { rate: 0.15, treatment: 'standard',   net: '20.00', vat: '3.00', gross: '23.00' },
    { rate: 0,    treatment: 'zero-rated', net: '50.00', vat: '0.00', gross: '50.00' },
    { rate: 0,    treatment: 'exempt',     net: '30.00', vat: '0.00', gross: '30.00' },
  ],
  lines: [ … ],
}

The breakdown is one entry per rate and treatment — which is what a return asks for, and why zero-rated and exempt do not collapse into each other even though both are 0%.

Countries

Standard rate Since
AE United Arab Emirates 5% 2018-01-01
SA Saudi Arabia 15% 2020-07-01 (5% before)
BH Bahrain 10% 2022-01-01 (5% before)
OM Oman 5% 2021-04-16
PK Pakistan 18% 2023-02-15 (17% before)
EG Egypt 14% 2017-07-01
JO Jordan 16% 2018-01-01
QA Qatar none yet
KW Kuwait none yet

Qatar and Kuwait return null rather than 0, because an invoice there carries no tax line at all — which is a different thing from a zero-rated one.

These are a starting point, not tax advice. Rates change, sectors have their own treatment, and Pakistan's provinces tax services at their own rates. Every rate can be overridden:

vat(100, { rate: 0.16 });   // whatever applies to you

Where to go next

Updated 15 Sep 2026