Functional Weave
Code in Python

hospitality.gross-profit-menu@1.0.0

vectors.json

7,951 bytes · view raw

[
  {"fn": "menuGrossProfit", "name": "a 14.50 dish costing 3.50 at 20% VAT: GP on the net price is 71.03%, not the 75.86% on the menu price", "args": [{"minor": 350, "currency": "GBP"}, {"minor": 1450, "currency": "GBP"}, "GB", "HOSPITALITY", "2026-09-23"], "expect": {"menuPrice": {"minor": 1450, "currency": "GBP"}, "vat": {"minor": 242, "currency": "GBP"}, "netPrice": {"minor": 1208, "currency": "GBP"}, "cost": {"minor": 350, "currency": "GBP"}, "grossProfit": {"minor": 858, "currency": "GBP"}, "gpBasisPoints": 7103, "costBasisPoints": 2897, "vatRateBasisPoints": 2000}},
  {"fn": "menuGrossProfit", "name": "cold takeaway food is zero-rated, so all of the price is net", "args": [{"minor": 120, "currency": "GBP"}, {"minor": 450, "currency": "GBP"}, "GB", "ZERO", "2026-09-23"], "expect": {"menuPrice": {"minor": 450, "currency": "GBP"}, "vat": {"minor": 0, "currency": "GBP"}, "netPrice": {"minor": 450, "currency": "GBP"}, "cost": {"minor": 120, "currency": "GBP"}, "grossProfit": {"minor": 330, "currency": "GBP"}, "gpBasisPoints": 7333, "costBasisPoints": 2667, "vatRateBasisPoints": 0}},
  {"fn": "menuGrossProfit", "name": "the same dish during the 2021 hospitality VAT cut to 5%", "args": [{"minor": 350, "currency": "GBP"}, {"minor": 1450, "currency": "GBP"}, "GB", "HOSPITALITY", "2021-01-15"], "expect": {"menuPrice": {"minor": 1450, "currency": "GBP"}, "vat": {"minor": 69, "currency": "GBP"}, "netPrice": {"minor": 1381, "currency": "GBP"}, "cost": {"minor": 350, "currency": "GBP"}, "grossProfit": {"minor": 1031, "currency": "GBP"}, "gpBasisPoints": 7466, "costBasisPoints": 2534, "vatRateBasisPoints": 500}},
  {"fn": "menuGrossProfit", "name": "Irish tourism and hospitality rate of 13.5%", "args": [{"minor": 400, "currency": "EUR"}, {"minor": 1800, "currency": "EUR"}, "IE", "HOSPITALITY", "2026-09-23"], "expect": {"menuPrice": {"minor": 1800, "currency": "EUR"}, "vat": {"minor": 214, "currency": "EUR"}, "netPrice": {"minor": 1586, "currency": "EUR"}, "cost": {"minor": 400, "currency": "EUR"}, "grossProfit": {"minor": 1186, "currency": "EUR"}, "gpBasisPoints": 7478, "costBasisPoints": 2522, "vatRateBasisPoints": 1350}},
  {"fn": "menuGrossProfit", "name": "a dish sold at a loss has a negative GP and a food cost over 100%", "args": [{"minor": 1500, "currency": "GBP"}, {"minor": 1200, "currency": "GBP"}, "GB", "HOSPITALITY", "2026-09-23"], "expect": {"menuPrice": {"minor": 1200, "currency": "GBP"}, "vat": {"minor": 200, "currency": "GBP"}, "netPrice": {"minor": 1000, "currency": "GBP"}, "cost": {"minor": 1500, "currency": "GBP"}, "grossProfit": {"minor": -500, "currency": "GBP"}, "gpBasisPoints": -5000, "costBasisPoints": 15000, "vatRateBasisPoints": 2000}},
  {"fn": "menuGrossProfit", "name": "a free dish has no GP percentage", "args": [{"minor": 350, "currency": "GBP"}, {"minor": 0, "currency": "GBP"}, "GB", "HOSPITALITY", "2026-09-23"], "expect": {"menuPrice": {"minor": 0, "currency": "GBP"}, "vat": {"minor": 0, "currency": "GBP"}, "netPrice": {"minor": 0, "currency": "GBP"}, "cost": {"minor": 350, "currency": "GBP"}, "grossProfit": {"minor": -350, "currency": "GBP"}, "gpBasisPoints": null, "costBasisPoints": null, "vatRateBasisPoints": 2000}},
  {"fn": "menuGrossProfit", "name": "a negative cost is an error", "args": [{"minor": -1, "currency": "GBP"}, {"minor": 1450, "currency": "GBP"}, "GB", "HOSPITALITY", "2026-09-23"], "expectError": "cost must not be negative"},
  {"fn": "menuGrossProfit", "name": "a negative price is an error", "args": [{"minor": 350, "currency": "GBP"}, {"minor": -1450, "currency": "GBP"}, "GB", "HOSPITALITY", "2026-09-23"], "expectError": "menuPrice must not be negative"},
  {"fn": "menuGrossProfit", "name": "cost and price in different currencies is an error", "args": [{"minor": 350, "currency": "EUR"}, {"minor": 1450, "currency": "GBP"}, "GB", "HOSPITALITY", "2026-09-23"], "expectError": "currency mismatch"},
  {"fn": "menuGrossProfit", "name": "an unknown VAT category is an error", "args": [{"minor": 350, "currency": "GBP"}, {"minor": 1450, "currency": "GBP"}, "GB", "FOOD", "2026-09-23"], "expectError": "no VAT rule for GB/FOOD on 2026-09-23"},
  {"fn": "menuPriceForGp", "name": "70% GP on a 3.50 cost: 11.67 net, 14.00 on the menu (3.50 / 0.3 = 11.67 on the menu would be 64%)", "args": [{"minor": 350, "currency": "GBP"}, 7000, 1, "GB", "HOSPITALITY", "2026-09-23"], "expect": {"menuPrice": {"minor": 1400, "currency": "GBP"}, "vat": {"minor": 233, "currency": "GBP"}, "netPrice": {"minor": 1167, "currency": "GBP"}, "cost": {"minor": 350, "currency": "GBP"}, "grossProfit": {"minor": 817, "currency": "GBP"}, "gpBasisPoints": 7001, "costBasisPoints": 2999, "vatRateBasisPoints": 2000}},
  {"fn": "menuPriceForGp", "name": "rounded up to the next 50p", "args": [{"minor": 400, "currency": "GBP"}, 7000, 50, "GB", "HOSPITALITY", "2026-09-23"], "expect": {"menuPrice": {"minor": 1650, "currency": "GBP"}, "vat": {"minor": 275, "currency": "GBP"}, "netPrice": {"minor": 1375, "currency": "GBP"}, "cost": {"minor": 400, "currency": "GBP"}, "grossProfit": {"minor": 975, "currency": "GBP"}, "gpBasisPoints": 7091, "costBasisPoints": 2909, "vatRateBasisPoints": 2000}},
  {"fn": "menuPriceForGp", "name": "rounded up to the next pound", "args": [{"minor": 400, "currency": "GBP"}, 7000, 100, "GB", "HOSPITALITY", "2026-09-23"], "expect": {"menuPrice": {"minor": 1700, "currency": "GBP"}, "vat": {"minor": 283, "currency": "GBP"}, "netPrice": {"minor": 1417, "currency": "GBP"}, "cost": {"minor": 400, "currency": "GBP"}, "grossProfit": {"minor": 1017, "currency": "GBP"}, "gpBasisPoints": 7177, "costBasisPoints": 2823, "vatRateBasisPoints": 2000}},
  {"fn": "menuPriceForGp", "name": "a 0% target sells at cost plus VAT", "args": [{"minor": 500, "currency": "GBP"}, 0, 1, "GB", "HOSPITALITY", "2026-09-23"], "expect": {"menuPrice": {"minor": 600, "currency": "GBP"}, "vat": {"minor": 100, "currency": "GBP"}, "netPrice": {"minor": 500, "currency": "GBP"}, "cost": {"minor": 500, "currency": "GBP"}, "grossProfit": {"minor": 0, "currency": "GBP"}, "gpBasisPoints": 0, "costBasisPoints": 10000, "vatRateBasisPoints": 2000}},
  {"fn": "menuPriceForGp", "name": "zero-rated takeaway to 65% GP, on a 5p step", "args": [{"minor": 100, "currency": "GBP"}, 6500, 5, "GB", "ZERO", "2026-09-23"], "expect": {"menuPrice": {"minor": 290, "currency": "GBP"}, "vat": {"minor": 0, "currency": "GBP"}, "netPrice": {"minor": 290, "currency": "GBP"}, "cost": {"minor": 100, "currency": "GBP"}, "grossProfit": {"minor": 190, "currency": "GBP"}, "gpBasisPoints": 6552, "costBasisPoints": 3448, "vatRateBasisPoints": 0}},
  {"fn": "menuPriceForGp", "name": "a dish that costs nothing is priced at nothing", "args": [{"minor": 0, "currency": "GBP"}, 7000, 1, "GB", "HOSPITALITY", "2026-09-23"], "expect": {"menuPrice": {"minor": 0, "currency": "GBP"}, "vat": {"minor": 0, "currency": "GBP"}, "netPrice": {"minor": 0, "currency": "GBP"}, "cost": {"minor": 0, "currency": "GBP"}, "grossProfit": {"minor": 0, "currency": "GBP"}, "gpBasisPoints": null, "costBasisPoints": null, "vatRateBasisPoints": 2000}},
  {"fn": "menuPriceForGp", "name": "a 100% target is an error", "args": [{"minor": 350, "currency": "GBP"}, 10000, 1, "GB", "HOSPITALITY", "2026-09-23"], "expectError": "targetGpBasisPoints must be a whole number from 0 to 9999"},
  {"fn": "menuPriceForGp", "name": "a negative target is an error", "args": [{"minor": 350, "currency": "GBP"}, -1, 1, "GB", "HOSPITALITY", "2026-09-23"], "expectError": "targetGpBasisPoints must be a whole number from 0 to 9999"},
  {"fn": "menuPriceForGp", "name": "a zero price step is an error", "args": [{"minor": 350, "currency": "GBP"}, 7000, 0, "GB", "HOSPITALITY", "2026-09-23"], "expectError": "priceStep must be at least 1"},
  {"fn": "menuPriceForGp", "name": "a negative cost is an error", "args": [{"minor": -350, "currency": "GBP"}, 7000, 1, "GB", "HOSPITALITY", "2026-09-23"], "expectError": "cost must not be negative"}
]