Functional Weave
Code in Rust

finance.invoice.calculate@1.0.1

vectors.json

18,644 bytes · view raw

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