[ { "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" } ]