donation item, shop soft open
All checks were successful
Deploy / build-deploy (push) Successful in 4m11s

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Jorijn van der Graaf 2026-08-17 11:04:03 +02:00
commit abbd616b40
23 changed files with 2898 additions and 209 deletions

View file

@ -82,14 +82,23 @@ std::string BuildInvoiceMarkdown(const OrderRecord& o,
md += "## Amounts\n\n";
md += "| Description | Qty | Amount |\n|---|---|---|\n";
if (o.vatIncluded) {
// EU supply: net amounts per line, VAT once over the taxable total —
// the same line-total rounding the checkout charged with.
// EU supply: VAT once over the taxable total — the same rounding the
// checkout charged with — so the subtotal and VAT rows are fixed.
// Only the goods line rounds on its own; shipping is printed as the
// REMAINDER of the subtotal, never a third independent rounding.
// Three half-up roundings need not sum (about a quarter of the real
// price grid lands a cent apart), and a signed tax document whose
// columns disagree with themselves invites exactly the scrutiny it
// exists to settle. Shipping absorbs the cent rather than the device
// because it is the ancillary line: worst case it prints one cent
// off the carrier's ex-VAT rate, a number no buyer sees elsewhere.
const std::int64_t net = Money::NetFromGross(o.totalMinor);
const std::int64_t vat = o.totalMinor - net;
const std::int64_t goodsNet = Money::NetFromGross(o.goodsMinor);
md += std::format("| {} | {} | {} |\n", item, o.quantity,
Money::FormatEuro(Money::NetFromGross(o.goodsMinor)));
Money::FormatEuro(goodsNet));
md += std::format("| Shipping | 1 | {} |\n",
Money::FormatEuro(Money::NetFromGross(o.shippingMinor)));
Money::FormatEuro(net - goodsNet));
md += std::format("| Subtotal (ex VAT) | | {} |\n", Money::FormatEuro(net));
md += std::format("| VAT 21% (NL) | | {} |\n", Money::FormatEuro(vat));
md += std::format("| **Total (incl. VAT)** | | **{}** |\n",