[
{
"name": "3 days at 250.00 and 400.00 of materials marked up 20%, standard-rated: the markup is on materials only",
"args": [
300,
{
"minor": 25000,
"currency": "GBP"
},
{
"minor": 40000,
"currency": "GBP"
},
2000,
"STANDARD",
"2026-09-23"
],
"expect": {
"labour": {
"minor": 75000,
"currency": "GBP"
},
"materials": {
"minor": 48000,
"currency": "GBP"
},
"net": {
"minor": 123000,
"currency": "GBP"
},
"vat": {
"minor": 24600,
"currency": "GBP"
},
"gross": {
"minor": 147600,
"currency": "GBP"
},
"vatBasisPoints": 2000
}
},
{
"name": "2.5 days at 180.00, no materials, not VAT registered",
"args": [
250,
{
"minor": 18000,
"currency": "GBP"
},
{
"minor": 0,
"currency": "GBP"
},
1500,
null,
"2026-09-23"
],
"expect": {
"labour": {
"minor": 45000,
"currency": "GBP"
},
"materials": {
"minor": 0,
"currency": "GBP"
},
"net": {
"minor": 45000,
"currency": "GBP"
},
"vat": {
"minor": 0,
"currency": "GBP"
},
"gross": {
"minor": 45000,
"currency": "GBP"
},
"vatBasisPoints": 0
}
},
{
"name": "a third of a day at 199.99 is 65.9967, rounded half up to 66.00; 12.34 of materials at 15% is 14.191, 14.19; VAT 20% of 80.19 is 16.038, 16.04",
"args": [
33,
{
"minor": 19999,
"currency": "GBP"
},
{
"minor": 1234,
"currency": "GBP"
},
1500,
"STANDARD",
"2026-09-23"
],
"expect": {
"labour": {
"minor": 6600,
"currency": "GBP"
},
"materials": {
"minor": 1419,
"currency": "GBP"
},
"net": {
"minor": 8019,
"currency": "GBP"
},
"vat": {
"minor": 1604,
"currency": "GBP"
},
"gross": {
"minor": 9623,
"currency": "GBP"
},
"vatBasisPoints": 2000
}
},
{
"name": "reduced-rated installation at 5%",
"args": [
100,
{
"minor": 20000,
"currency": "GBP"
},
{
"minor": 100000,
"currency": "GBP"
},
0,
"REDUCED",
"2026-09-23"
],
"expect": {
"labour": {
"minor": 20000,
"currency": "GBP"
},
"materials": {
"minor": 100000,
"currency": "GBP"
},
"net": {
"minor": 120000,
"currency": "GBP"
},
"vat": {
"minor": 6000,
"currency": "GBP"
},
"gross": {
"minor": 126000,
"currency": "GBP"
},
"vatBasisPoints": 500
}
},
{
"name": "zero-rated work",
"args": [
200,
{
"minor": 15000,
"currency": "GBP"
},
{
"minor": 30000,
"currency": "GBP"
},
1000,
"ZERO",
"2026-09-23"
],
"expect": {
"labour": {
"minor": 30000,
"currency": "GBP"
},
"materials": {
"minor": 33000,
"currency": "GBP"
},
"net": {
"minor": 63000,
"currency": "GBP"
},
"vat": {
"minor": 0,
"currency": "GBP"
},
"gross": {
"minor": 63000,
"currency": "GBP"
},
"vatBasisPoints": 0
}
},
{
"name": "a quote for a 2010 job uses the 17.5% rate then in force",
"args": [
100,
{
"minor": 10000,
"currency": "GBP"
},
{
"minor": 0,
"currency": "GBP"
},
0,
"STANDARD",
"2010-06-30"
],
"expect": {
"labour": {
"minor": 10000,
"currency": "GBP"
},
"materials": {
"minor": 0,
"currency": "GBP"
},
"net": {
"minor": 10000,
"currency": "GBP"
},
"vat": {
"minor": 1750,
"currency": "GBP"
},
"gross": {
"minor": 11750,
"currency": "GBP"
},
"vatBasisPoints": 1750
}
},
{
"name": "materials only: no labour days",
"args": [
0,
{
"minor": 25000,
"currency": "GBP"
},
{
"minor": 5000,
"currency": "GBP"
},
1000,
"STANDARD",
"2026-09-23"
],
"expect": {
"labour": {
"minor": 0,
"currency": "GBP"
},
"materials": {
"minor": 5500,
"currency": "GBP"
},
"net": {
"minor": 5500,
"currency": "GBP"
},
"vat": {
"minor": 1100,
"currency": "GBP"
},
"gross": {
"minor": 6600,
"currency": "GBP"
},
"vatBasisPoints": 2000
}
},
{
"name": "half a day at 250.00",
"args": [
50,
{
"minor": 25000,
"currency": "GBP"
},
{
"minor": 0,
"currency": "GBP"
},
0,
null,
"2026-09-23"
],
"expect": {
"labour": {
"minor": 12500,
"currency": "GBP"
},
"materials": {
"minor": 0,
"currency": "GBP"
},
"net": {
"minor": 12500,
"currency": "GBP"
},
"vat": {
"minor": 0,
"currency": "GBP"
},
"gross": {
"minor": 12500,
"currency": "GBP"
},
"vatBasisPoints": 0
}
},
{
"name": "half a day at 250.01 is 125.005, rounded half up to 125.01",
"args": [
50,
{
"minor": 25001,
"currency": "GBP"
},
{
"minor": 0,
"currency": "GBP"
},
0,
null,
"2026-09-23"
],
"expect": {
"labour": {
"minor": 12501,
"currency": "GBP"
},
"materials": {
"minor": 0,
"currency": "GBP"
},
"net": {
"minor": 12501,
"currency": "GBP"
},
"vat": {
"minor": 0,
"currency": "GBP"
},
"gross": {
"minor": 12501,
"currency": "GBP"
},
"vatBasisPoints": 0
}
},
{
"name": "materials sold at cost: no markup",
"args": [
100,
{
"minor": 20000,
"currency": "GBP"
},
{
"minor": 7777,
"currency": "GBP"
},
0,
"STANDARD",
"2026-09-23"
],
"expect": {
"labour": {
"minor": 20000,
"currency": "GBP"
},
"materials": {
"minor": 7777,
"currency": "GBP"
},
"net": {
"minor": 27777,
"currency": "GBP"
},
"vat": {
"minor": 5555,
"currency": "GBP"
},
"gross": {
"minor": 33332,
"currency": "GBP"
},
"vatBasisPoints": 2000
}
},
{
"name": "negative labour days",
"args": [
-50,
{
"minor": 25000,
"currency": "GBP"
},
{
"minor": 0,
"currency": "GBP"
},
0,
null,
"2026-09-23"
],
"expectError": "labourDaysHundredths must be a whole number of hundredths, not negative"
},
{
"name": "fractional hundredths",
"args": [
2.5,
{
"minor": 25000,
"currency": "GBP"
},
{
"minor": 0,
"currency": "GBP"
},
0,
null,
"2026-09-23"
],
"expectError": "labourDaysHundredths must be a whole number of hundredths, not negative"
},
{
"name": "a negative day rate",
"args": [
100,
{
"minor": -1,
"currency": "GBP"
},
{
"minor": 0,
"currency": "GBP"
},
0,
null,
"2026-09-23"
],
"expectError": "dayRate must not be negative"
},
{
"name": "negative materials",
"args": [
100,
{
"minor": 25000,
"currency": "GBP"
},
{
"minor": -1,
"currency": "GBP"
},
0,
null,
"2026-09-23"
],
"expectError": "cost must not be negative"
},
{
"name": "a markup below -100%",
"args": [
100,
{
"minor": 25000,
"currency": "GBP"
},
{
"minor": 100,
"currency": "GBP"
},
-10001,
null,
"2026-09-23"
],
"expectError": "a markup of -10001 basis points gives a negative price"
},
{
"name": "a fractional markup",
"args": [
100,
{
"minor": 25000,
"currency": "GBP"
},
{
"minor": 100,
"currency": "GBP"
},
1.5,
null,
"2026-09-23"
],
"expectError": "must be an integer"
},
{
"name": "day rate and materials in different currencies",
"args": [
100,
{
"minor": 25000,
"currency": "GBP"
},
{
"minor": 100,
"currency": "EUR"
},
0,
null,
"2026-09-23"
],
"expectError": "currency mismatch"
},
{
"name": "an unknown VAT category",
"args": [
100,
{
"minor": 25000,
"currency": "GBP"
},
{
"minor": 0,
"currency": "GBP"
},
0,
"LUXURY",
"2026-09-23"
],
"expectError": "no VAT rule for GB/LUXURY"
},
{
"name": "a labour product beyond 2^53 - 1",
"args": [
10000000,
{
"minor": 1000000000000,
"currency": "GBP"
},
{
"minor": 0,
"currency": "GBP"
},
0,
null,
"2026-09-23"
],
"expectError": "exceeds 2^53 - 1"
}
]