[
{
"name": "mixed standard, zero and discounted lines",
"args": [
[
{ "description": "Consultancy, 3 days", "unitPrice": { "minor": 50000, "currency": "GBP" }, "quantity": 3, "discountBasisPoints": 0, "taxCategory": "STANDARD" },
{ "description": "Printed report", "unitPrice": { "minor": 1250, "currency": "GBP" }, "quantity": 2, "discountBasisPoints": 0, "taxCategory": "ZERO" },
{ "description": "Software licence", "unitPrice": { "minor": 9999, "currency": "GBP" }, "quantity": 1, "discountBasisPoints": 1000, "taxCategory": "STANDARD" }
],
"GB",
"2026-09-16"
],
"expect": {
"currency": "GBP",
"lines": [
{ "description": "Consultancy, 3 days", "net": { "minor": 150000, "currency": "GBP" }, "tax": { "minor": 30000, "currency": "GBP" }, "gross": { "minor": 180000, "currency": "GBP" }, "basisPoints": 2000 },
{ "description": "Printed report", "net": { "minor": 2500, "currency": "GBP" }, "tax": { "minor": 0, "currency": "GBP" }, "gross": { "minor": 2500, "currency": "GBP" }, "basisPoints": 0 },
{ "description": "Software licence", "net": { "minor": 8999, "currency": "GBP" }, "tax": { "minor": 1800, "currency": "GBP" }, "gross": { "minor": 10799, "currency": "GBP" }, "basisPoints": 2000 }
],
"subtotal": { "minor": 161499, "currency": "GBP" },
"taxTotal": { "minor": 31800, "currency": "GBP" },
"total": { "minor": 193299, "currency": "GBP" },
"taxBreakdown": [
{ "basisPoints": 0, "taxable": { "minor": 2500, "currency": "GBP" }, "tax": { "minor": 0, "currency": "GBP" } },
{ "basisPoints": 2000, "taxable": { "minor": 158999, "currency": "GBP" }, "tax": { "minor": 31800, "currency": "GBP" } }
]
}
},
{
"name": "hospitality invoice during the covid rate cut",
"args": [
[{ "description": "Dinner, 4 covers", "unitPrice": { "minor": 5000, "currency": "GBP" }, "quantity": 4, "discountBasisPoints": 0, "taxCategory": "HOSPITALITY" }],
"GB",
"2020-12-25"
],
"expect": {
"currency": "GBP",
"lines": [{ "description": "Dinner, 4 covers", "net": { "minor": 20000, "currency": "GBP" }, "tax": { "minor": 1000, "currency": "GBP" }, "gross": { "minor": 21000, "currency": "GBP" }, "basisPoints": 500 }],
"subtotal": { "minor": 20000, "currency": "GBP" },
"taxTotal": { "minor": 1000, "currency": "GBP" },
"total": { "minor": 21000, "currency": "GBP" },
"taxBreakdown": [{ "basisPoints": 500, "taxable": { "minor": 20000, "currency": "GBP" }, "tax": { "minor": 1000, "currency": "GBP" } }]
}
},
{
"name": "the same goods invoiced in 2009 use the 2009 rate",
"args": [
[{ "description": "Widget", "unitPrice": { "minor": 10000, "currency": "GBP" }, "quantity": 1, "discountBasisPoints": 0, "taxCategory": "STANDARD" }],
"GB",
"2009-06-01"
],
"expect": {
"currency": "GBP",
"lines": [{ "description": "Widget", "net": { "minor": 10000, "currency": "GBP" }, "tax": { "minor": 1500, "currency": "GBP" }, "gross": { "minor": 11500, "currency": "GBP" }, "basisPoints": 1500 }],
"subtotal": { "minor": 10000, "currency": "GBP" },
"taxTotal": { "minor": 1500, "currency": "GBP" },
"total": { "minor": 11500, "currency": "GBP" },
"taxBreakdown": [{ "basisPoints": 1500, "taxable": { "minor": 10000, "currency": "GBP" }, "tax": { "minor": 1500, "currency": "GBP" } }]
}
},
{
"name": "a credit note is an invoice with negative quantities",
"args": [
[{ "description": "Returned widget", "unitPrice": { "minor": 10000, "currency": "GBP" }, "quantity": -1, "discountBasisPoints": 0, "taxCategory": "STANDARD" }],
"GB",
"2026-09-16"
],
"expect": {
"currency": "GBP",
"lines": [{ "description": "Returned widget", "net": { "minor": -10000, "currency": "GBP" }, "tax": { "minor": -2000, "currency": "GBP" }, "gross": { "minor": -12000, "currency": "GBP" }, "basisPoints": 2000 }],
"subtotal": { "minor": -10000, "currency": "GBP" },
"taxTotal": { "minor": -2000, "currency": "GBP" },
"total": { "minor": -12000, "currency": "GBP" },
"taxBreakdown": [{ "basisPoints": 2000, "taxable": { "minor": -10000, "currency": "GBP" }, "tax": { "minor": -2000, "currency": "GBP" } }]
}
},
{
"name": "an Irish invoice uses Irish rates",
"args": [
[{ "description": "Consultancy", "unitPrice": { "minor": 100000, "currency": "EUR" }, "quantity": 1, "discountBasisPoints": 0, "taxCategory": "STANDARD" }],
"IE",
"2026-09-16"
],
"expect": {
"currency": "EUR",
"lines": [{ "description": "Consultancy", "net": { "minor": 100000, "currency": "EUR" }, "tax": { "minor": 23000, "currency": "EUR" }, "gross": { "minor": 123000, "currency": "EUR" }, "basisPoints": 2300 }],
"subtotal": { "minor": 100000, "currency": "EUR" },
"taxTotal": { "minor": 23000, "currency": "EUR" },
"total": { "minor": 123000, "currency": "EUR" },
"taxBreakdown": [{ "basisPoints": 2300, "taxable": { "minor": 100000, "currency": "EUR" }, "tax": { "minor": 23000, "currency": "EUR" } }]
}
},
{
"name": "an invoice with no lines is an error",
"args": [[], "GB", "2026-09-16"],
"expectError": "at least one line"
},
{
"name": "mixing currencies across lines is an error",
"args": [
[
{ "description": "In pounds", "unitPrice": { "minor": 1000, "currency": "GBP" }, "quantity": 1, "discountBasisPoints": 0, "taxCategory": "STANDARD" },
{ "description": "In euros", "unitPrice": { "minor": 1000, "currency": "EUR" }, "quantity": 1, "discountBasisPoints": 0, "taxCategory": "STANDARD" }
],
"GB",
"2026-09-16"
],
"expectError": "currency mismatch"
},
{
"name": "VAT is rounded per line, then added up: three 3p lines carry 3p of VAT, not the 2p a grouped total gives",
"args": [
[
{
"description": "Sticker",
"unitPrice": {
"minor": 3,
"currency": "GBP"
},
"quantity": 1,
"discountBasisPoints": 0,
"taxCategory": "STANDARD"
},
{
"description": "Sticker",
"unitPrice": {
"minor": 3,
"currency": "GBP"
},
"quantity": 1,
"discountBasisPoints": 0,
"taxCategory": "STANDARD"
},
{
"description": "Sticker",
"unitPrice": {
"minor": 3,
"currency": "GBP"
},
"quantity": 1,
"discountBasisPoints": 0,
"taxCategory": "STANDARD"
}
],
"GB",
"2026-09-16"
],
"expect": {
"currency": "GBP",
"lines": [
{
"description": "Sticker",
"net": {
"minor": 3,
"currency": "GBP"
},
"tax": {
"minor": 1,
"currency": "GBP"
},
"gross": {
"minor": 4,
"currency": "GBP"
},
"basisPoints": 2000
},
{
"description": "Sticker",
"net": {
"minor": 3,
"currency": "GBP"
},
"tax": {
"minor": 1,
"currency": "GBP"
},
"gross": {
"minor": 4,
"currency": "GBP"
},
"basisPoints": 2000
},
{
"description": "Sticker",
"net": {
"minor": 3,
"currency": "GBP"
},
"tax": {
"minor": 1,
"currency": "GBP"
},
"gross": {
"minor": 4,
"currency": "GBP"
},
"basisPoints": 2000
}
],
"subtotal": {
"minor": 9,
"currency": "GBP"
},
"taxTotal": {
"minor": 3,
"currency": "GBP"
},
"total": {
"minor": 12,
"currency": "GBP"
},
"taxBreakdown": [
{
"basisPoints": 2000,
"taxable": {
"minor": 9,
"currency": "GBP"
},
"tax": {
"minor": 3,
"currency": "GBP"
}
}
]
}
},
{
"name": "the summary is ordered by rate, whatever order the lines come in",
"args": [
[
{
"description": "Labour",
"unitPrice": {
"minor": 10000,
"currency": "GBP"
},
"quantity": 1,
"discountBasisPoints": 0,
"taxCategory": "STANDARD"
},
{
"description": "Fuel",
"unitPrice": {
"minor": 5000,
"currency": "GBP"
},
"quantity": 1,
"discountBasisPoints": 0,
"taxCategory": "REDUCED"
},
{
"description": "Book",
"unitPrice": {
"minor": 1500,
"currency": "GBP"
},
"quantity": 1,
"discountBasisPoints": 0,
"taxCategory": "ZERO"
}
],
"GB",
"2026-09-16"
],
"expect": {
"currency": "GBP",
"lines": [
{
"description": "Labour",
"net": {
"minor": 10000,
"currency": "GBP"
},
"tax": {
"minor": 2000,
"currency": "GBP"
},
"gross": {
"minor": 12000,
"currency": "GBP"
},
"basisPoints": 2000
},
{
"description": "Fuel",
"net": {
"minor": 5000,
"currency": "GBP"
},
"tax": {
"minor": 250,
"currency": "GBP"
},
"gross": {
"minor": 5250,
"currency": "GBP"
},
"basisPoints": 500
},
{
"description": "Book",
"net": {
"minor": 1500,
"currency": "GBP"
},
"tax": {
"minor": 0,
"currency": "GBP"
},
"gross": {
"minor": 1500,
"currency": "GBP"
},
"basisPoints": 0
}
],
"subtotal": {
"minor": 16500,
"currency": "GBP"
},
"taxTotal": {
"minor": 2250,
"currency": "GBP"
},
"total": {
"minor": 18750,
"currency": "GBP"
},
"taxBreakdown": [
{
"basisPoints": 0,
"taxable": {
"minor": 1500,
"currency": "GBP"
},
"tax": {
"minor": 0,
"currency": "GBP"
}
},
{
"basisPoints": 500,
"taxable": {
"minor": 5000,
"currency": "GBP"
},
"tax": {
"minor": 250,
"currency": "GBP"
}
},
{
"basisPoints": 2000,
"taxable": {
"minor": 10000,
"currency": "GBP"
},
"tax": {
"minor": 2000,
"currency": "GBP"
}
}
]
}
},
{
"name": "zero-rated and exempt lines share the 0% group",
"args": [
[
{
"description": "Food",
"unitPrice": {
"minor": 1000,
"currency": "GBP"
},
"quantity": 1,
"discountBasisPoints": 0,
"taxCategory": "ZERO"
},
{
"description": "Stamps",
"unitPrice": {
"minor": 500,
"currency": "GBP"
},
"quantity": 1,
"discountBasisPoints": 0,
"taxCategory": "EXEMPT"
}
],
"GB",
"2026-09-16"
],
"expect": {
"currency": "GBP",
"lines": [
{
"description": "Food",
"net": {
"minor": 1000,
"currency": "GBP"
},
"tax": {
"minor": 0,
"currency": "GBP"
},
"gross": {
"minor": 1000,
"currency": "GBP"
},
"basisPoints": 0
},
{
"description": "Stamps",
"net": {
"minor": 500,
"currency": "GBP"
},
"tax": {
"minor": 0,
"currency": "GBP"
},
"gross": {
"minor": 500,
"currency": "GBP"
},
"basisPoints": 0
}
],
"subtotal": {
"minor": 1500,
"currency": "GBP"
},
"taxTotal": {
"minor": 0,
"currency": "GBP"
},
"total": {
"minor": 1500,
"currency": "GBP"
},
"taxBreakdown": [
{
"basisPoints": 0,
"taxable": {
"minor": 1500,
"currency": "GBP"
},
"tax": {
"minor": 0,
"currency": "GBP"
}
}
]
}
},
{
"name": "the discount is rounded before VAT: 12.5% off 9.99 is 1.25, then VAT on 8.74",
"args": [
[
{
"description": "Licence",
"unitPrice": {
"minor": 999,
"currency": "GBP"
},
"quantity": 1,
"discountBasisPoints": 1250,
"taxCategory": "STANDARD"
}
],
"GB",
"2026-09-16"
],
"expect": {
"currency": "GBP",
"lines": [
{
"description": "Licence",
"net": {
"minor": 874,
"currency": "GBP"
},
"tax": {
"minor": 175,
"currency": "GBP"
},
"gross": {
"minor": 1049,
"currency": "GBP"
},
"basisPoints": 2000
}
],
"subtotal": {
"minor": 874,
"currency": "GBP"
},
"taxTotal": {
"minor": 175,
"currency": "GBP"
},
"total": {
"minor": 1049,
"currency": "GBP"
},
"taxBreakdown": [
{
"basisPoints": 2000,
"taxable": {
"minor": 874,
"currency": "GBP"
},
"tax": {
"minor": 175,
"currency": "GBP"
}
}
]
}
},
{
"name": "a line discounted by 100% is free and carries no VAT",
"args": [
[
{
"description": "Sample",
"unitPrice": {
"minor": 5000,
"currency": "GBP"
},
"quantity": 2,
"discountBasisPoints": 10000,
"taxCategory": "STANDARD"
}
],
"GB",
"2026-09-16"
],
"expect": {
"currency": "GBP",
"lines": [
{
"description": "Sample",
"net": {
"minor": 0,
"currency": "GBP"
},
"tax": {
"minor": 0,
"currency": "GBP"
},
"gross": {
"minor": 0,
"currency": "GBP"
},
"basisPoints": 2000
}
],
"subtotal": {
"minor": 0,
"currency": "GBP"
},
"taxTotal": {
"minor": 0,
"currency": "GBP"
},
"total": {
"minor": 0,
"currency": "GBP"
},
"taxBreakdown": [
{
"basisPoints": 2000,
"taxable": {
"minor": 0,
"currency": "GBP"
},
"tax": {
"minor": 0,
"currency": "GBP"
}
}
]
}
},
{
"name": "a return that cancels a sale leaves a zero invoice with a zero group",
"args": [
[
{
"description": "Widget",
"unitPrice": {
"minor": 10000,
"currency": "GBP"
},
"quantity": 1,
"discountBasisPoints": 0,
"taxCategory": "STANDARD"
},
{
"description": "Widget returned",
"unitPrice": {
"minor": 10000,
"currency": "GBP"
},
"quantity": -1,
"discountBasisPoints": 0,
"taxCategory": "STANDARD"
}
],
"GB",
"2026-09-16"
],
"expect": {
"currency": "GBP",
"lines": [
{
"description": "Widget",
"net": {
"minor": 10000,
"currency": "GBP"
},
"tax": {
"minor": 2000,
"currency": "GBP"
},
"gross": {
"minor": 12000,
"currency": "GBP"
},
"basisPoints": 2000
},
{
"description": "Widget returned",
"net": {
"minor": -10000,
"currency": "GBP"
},
"tax": {
"minor": -2000,
"currency": "GBP"
},
"gross": {
"minor": -12000,
"currency": "GBP"
},
"basisPoints": 2000
}
],
"subtotal": {
"minor": 0,
"currency": "GBP"
},
"taxTotal": {
"minor": 0,
"currency": "GBP"
},
"total": {
"minor": 0,
"currency": "GBP"
},
"taxBreakdown": [
{
"basisPoints": 2000,
"taxable": {
"minor": 0,
"currency": "GBP"
},
"tax": {
"minor": 0,
"currency": "GBP"
}
}
]
}
},
{
"name": "a tax category with no rate is an error",
"args": [
[
{
"description": "Yacht",
"unitPrice": {
"minor": 100000,
"currency": "GBP"
},
"quantity": 1,
"discountBasisPoints": 0,
"taxCategory": "LUXURY"
}
],
"GB",
"2026-09-16"
],
"expectError": "no VAT rule"
},
{
"name": "a discount over 100% is an error",
"args": [
[
{
"description": "Too generous",
"unitPrice": {
"minor": 1000,
"currency": "GBP"
},
"quantity": 1,
"discountBasisPoints": 10001,
"taxCategory": "STANDARD"
}
],
"GB",
"2026-09-16"
],
"expectError": "must be between 0 and 10000"
}
]