[ { "name": "\u00a39,535 in equal thirds: the odd penny on the first instalment", "args": [ { "minor": 953500, "currency": "GBP" }, [ 1, 1, 1 ], [ "2025-10-01", "2026-01-15", "2026-04-20" ] ], "expect": [ { "number": 1, "dueDate": "2025-10-01", "amount": { "minor": 317834, "currency": "GBP" }, "cumulative": { "minor": 317834, "currency": "GBP" }, "remaining": { "minor": 635666, "currency": "GBP" } }, { "number": 2, "dueDate": "2026-01-15", "amount": { "minor": 317833, "currency": "GBP" }, "cumulative": { "minor": 635667, "currency": "GBP" }, "remaining": { "minor": 317833, "currency": "GBP" } }, { "number": 3, "dueDate": "2026-04-20", "amount": { "minor": 317833, "currency": "GBP" }, "cumulative": { "minor": 953500, "currency": "GBP" }, "remaining": { "minor": 0, "currency": "GBP" } } ] }, { "name": "\u00a39,535 split 25/25/50 across three terms", "args": [ { "minor": 953500, "currency": "GBP" }, [ 25, 25, 50 ], [ "2025-10-01", "2026-01-15", "2026-04-20" ] ], "expect": [ { "number": 1, "dueDate": "2025-10-01", "amount": { "minor": 238375, "currency": "GBP" }, "cumulative": { "minor": 238375, "currency": "GBP" }, "remaining": { "minor": 715125, "currency": "GBP" } }, { "number": 2, "dueDate": "2026-01-15", "amount": { "minor": 238375, "currency": "GBP" }, "cumulative": { "minor": 476750, "currency": "GBP" }, "remaining": { "minor": 476750, "currency": "GBP" } }, { "number": 3, "dueDate": "2026-04-20", "amount": { "minor": 476750, "currency": "GBP" }, "cumulative": { "minor": 953500, "currency": "GBP" }, "remaining": { "minor": 0, "currency": "GBP" } } ] }, { "name": "\u00a31,000.01 split 25/25/50: the odd penny goes to the 50% share, the largest remainder, not the first", "args": [ { "minor": 100001, "currency": "GBP" }, [ 25, 25, 50 ], [ "2025-10-01", "2026-01-15", "2026-04-20" ] ], "expect": [ { "number": 1, "dueDate": "2025-10-01", "amount": { "minor": 25000, "currency": "GBP" }, "cumulative": { "minor": 25000, "currency": "GBP" }, "remaining": { "minor": 75001, "currency": "GBP" } }, { "number": 2, "dueDate": "2026-01-15", "amount": { "minor": 25000, "currency": "GBP" }, "cumulative": { "minor": 50000, "currency": "GBP" }, "remaining": { "minor": 50001, "currency": "GBP" } }, { "number": 3, "dueDate": "2026-04-20", "amount": { "minor": 50001, "currency": "GBP" }, "cumulative": { "minor": 100001, "currency": "GBP" }, "remaining": { "minor": 0, "currency": "GBP" } } ] }, { "name": "shares in basis points give the same schedule", "args": [ { "minor": 100001, "currency": "GBP" }, [ 2500, 2500, 5000 ], [ "2025-10-01", "2026-01-15", "2026-04-20" ] ], "expect": [ { "number": 1, "dueDate": "2025-10-01", "amount": { "minor": 25000, "currency": "GBP" }, "cumulative": { "minor": 25000, "currency": "GBP" }, "remaining": { "minor": 75001, "currency": "GBP" } }, { "number": 2, "dueDate": "2026-01-15", "amount": { "minor": 25000, "currency": "GBP" }, "cumulative": { "minor": 50000, "currency": "GBP" }, "remaining": { "minor": 50001, "currency": "GBP" } }, { "number": 3, "dueDate": "2026-04-20", "amount": { "minor": 50001, "currency": "GBP" }, "cumulative": { "minor": 100001, "currency": "GBP" }, "remaining": { "minor": 0, "currency": "GBP" } } ] }, { "name": "\u00a3100 in thirds: 33.34, 33.33, 33.33", "args": [ { "minor": 10000, "currency": "GBP" }, [ 1, 1, 1 ], [ "2025-10-01", "2026-01-15", "2026-04-20" ] ], "expect": [ { "number": 1, "dueDate": "2025-10-01", "amount": { "minor": 3334, "currency": "GBP" }, "cumulative": { "minor": 3334, "currency": "GBP" }, "remaining": { "minor": 6666, "currency": "GBP" } }, { "number": 2, "dueDate": "2026-01-15", "amount": { "minor": 3333, "currency": "GBP" }, "cumulative": { "minor": 6667, "currency": "GBP" }, "remaining": { "minor": 3333, "currency": "GBP" } }, { "number": 3, "dueDate": "2026-04-20", "amount": { "minor": 3333, "currency": "GBP" }, "cumulative": { "minor": 10000, "currency": "GBP" }, "remaining": { "minor": 0, "currency": "GBP" } } ] }, { "name": "\u00a3100.01 in thirds: the two leftover pennies to the first two", "args": [ { "minor": 10001, "currency": "GBP" }, [ 1, 1, 1 ], [ "2025-10-01", "2026-01-15", "2026-04-20" ] ], "expect": [ { "number": 1, "dueDate": "2025-10-01", "amount": { "minor": 3334, "currency": "GBP" }, "cumulative": { "minor": 3334, "currency": "GBP" }, "remaining": { "minor": 6667, "currency": "GBP" } }, { "number": 2, "dueDate": "2026-01-15", "amount": { "minor": 3334, "currency": "GBP" }, "cumulative": { "minor": 6668, "currency": "GBP" }, "remaining": { "minor": 3333, "currency": "GBP" } }, { "number": 3, "dueDate": "2026-04-20", "amount": { "minor": 3333, "currency": "GBP" }, "cumulative": { "minor": 10001, "currency": "GBP" }, "remaining": { "minor": 0, "currency": "GBP" } } ] }, { "name": "a single payment in full", "args": [ { "minor": 925000, "currency": "GBP" }, [ 1 ], [ "2025-09-22" ] ], "expect": [ { "number": 1, "dueDate": "2025-09-22", "amount": { "minor": 925000, "currency": "GBP" }, "cumulative": { "minor": 925000, "currency": "GBP" }, "remaining": { "minor": 0, "currency": "GBP" } } ] }, { "name": "a zero share collects nothing", "args": [ { "minor": 900000, "currency": "GBP" }, [ 0, 1, 1 ], [ "2025-10-01", "2026-01-15", "2026-04-20" ] ], "expect": [ { "number": 1, "dueDate": "2025-10-01", "amount": { "minor": 0, "currency": "GBP" }, "cumulative": { "minor": 0, "currency": "GBP" }, "remaining": { "minor": 900000, "currency": "GBP" } }, { "number": 2, "dueDate": "2026-01-15", "amount": { "minor": 450000, "currency": "GBP" }, "cumulative": { "minor": 450000, "currency": "GBP" }, "remaining": { "minor": 450000, "currency": "GBP" } }, { "number": 3, "dueDate": "2026-04-20", "amount": { "minor": 450000, "currency": "GBP" }, "cumulative": { "minor": 900000, "currency": "GBP" }, "remaining": { "minor": 0, "currency": "GBP" } } ] }, { "name": "a zero fee is a schedule of zeros", "args": [ { "minor": 0, "currency": "GBP" }, [ 1, 1 ], [ "2025-10-01", "2026-01-15" ] ], "expect": [ { "number": 1, "dueDate": "2025-10-01", "amount": { "minor": 0, "currency": "GBP" }, "cumulative": { "minor": 0, "currency": "GBP" }, "remaining": { "minor": 0, "currency": "GBP" } }, { "number": 2, "dueDate": "2026-01-15", "amount": { "minor": 0, "currency": "GBP" }, "cumulative": { "minor": 0, "currency": "GBP" }, "remaining": { "minor": 0, "currency": "GBP" } } ] }, { "name": "euros keep their currency", "args": [ { "minor": 1000, "currency": "EUR" }, [ 1, 1, 1 ], [ "2025-10-01", "2026-01-15", "2026-04-20" ] ], "expect": [ { "number": 1, "dueDate": "2025-10-01", "amount": { "minor": 334, "currency": "EUR" }, "cumulative": { "minor": 334, "currency": "EUR" }, "remaining": { "minor": 666, "currency": "EUR" } }, { "number": 2, "dueDate": "2026-01-15", "amount": { "minor": 333, "currency": "EUR" }, "cumulative": { "minor": 667, "currency": "EUR" }, "remaining": { "minor": 333, "currency": "EUR" } }, { "number": 3, "dueDate": "2026-04-20", "amount": { "minor": 333, "currency": "EUR" }, "cumulative": { "minor": 1000, "currency": "EUR" }, "remaining": { "minor": 0, "currency": "EUR" } } ] }, { "name": "ten monthly payments of \u00a3925", "args": [ { "minor": 925000, "currency": "GBP" }, [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ], [ "2025-10-01", "2025-11-01", "2025-12-01", "2026-01-01", "2026-02-01", "2026-03-01", "2026-04-01", "2026-05-01", "2026-06-01", "2026-07-01" ] ], "expect": [ { "number": 1, "dueDate": "2025-10-01", "amount": { "minor": 92500, "currency": "GBP" }, "cumulative": { "minor": 92500, "currency": "GBP" }, "remaining": { "minor": 832500, "currency": "GBP" } }, { "number": 2, "dueDate": "2025-11-01", "amount": { "minor": 92500, "currency": "GBP" }, "cumulative": { "minor": 185000, "currency": "GBP" }, "remaining": { "minor": 740000, "currency": "GBP" } }, { "number": 3, "dueDate": "2025-12-01", "amount": { "minor": 92500, "currency": "GBP" }, "cumulative": { "minor": 277500, "currency": "GBP" }, "remaining": { "minor": 647500, "currency": "GBP" } }, { "number": 4, "dueDate": "2026-01-01", "amount": { "minor": 92500, "currency": "GBP" }, "cumulative": { "minor": 370000, "currency": "GBP" }, "remaining": { "minor": 555000, "currency": "GBP" } }, { "number": 5, "dueDate": "2026-02-01", "amount": { "minor": 92500, "currency": "GBP" }, "cumulative": { "minor": 462500, "currency": "GBP" }, "remaining": { "minor": 462500, "currency": "GBP" } }, { "number": 6, "dueDate": "2026-03-01", "amount": { "minor": 92500, "currency": "GBP" }, "cumulative": { "minor": 555000, "currency": "GBP" }, "remaining": { "minor": 370000, "currency": "GBP" } }, { "number": 7, "dueDate": "2026-04-01", "amount": { "minor": 92500, "currency": "GBP" }, "cumulative": { "minor": 647500, "currency": "GBP" }, "remaining": { "minor": 277500, "currency": "GBP" } }, { "number": 8, "dueDate": "2026-05-01", "amount": { "minor": 92500, "currency": "GBP" }, "cumulative": { "minor": 740000, "currency": "GBP" }, "remaining": { "minor": 185000, "currency": "GBP" } }, { "number": 9, "dueDate": "2026-06-01", "amount": { "minor": 92500, "currency": "GBP" }, "cumulative": { "minor": 832500, "currency": "GBP" }, "remaining": { "minor": 92500, "currency": "GBP" } }, { "number": 10, "dueDate": "2026-07-01", "amount": { "minor": 92500, "currency": "GBP" }, "cumulative": { "minor": 925000, "currency": "GBP" }, "remaining": { "minor": 0, "currency": "GBP" } } ] }, { "name": "a negative total is an error", "args": [ { "minor": -100, "currency": "GBP" }, [ 1, 1 ], [ "2025-10-01", "2026-01-15" ] ], "expectError": "total must not be negative, received -100" }, { "name": "more shares than dates is an error", "args": [ { "minor": 100, "currency": "GBP" }, [ 1, 1, 1 ], [ "2025-10-01", "2026-01-15" ] ], "expectError": "shares and dueDates must be the same length, received 3 and 2" }, { "name": "no instalments is an error", "args": [ { "minor": 100, "currency": "GBP" }, [], [] ], "expectError": "shares must name at least one instalment" }, { "name": "a negative share is an error", "args": [ { "minor": 100, "currency": "GBP" }, [ 1, -1 ], [ "2025-10-01", "2026-01-15" ] ], "expectError": "each share must be a whole number of 0 or more, received -1" }, { "name": "a fractional share is an error", "args": [ { "minor": 100, "currency": "GBP" }, [ 1, 1.5 ], [ "2025-10-01", "2026-01-15" ] ], "expectError": "each share must be a whole number of 0 or more, received 1.5" }, { "name": "all-zero shares are an error", "args": [ { "minor": 100, "currency": "GBP" }, [ 0, 0 ], [ "2025-10-01", "2026-01-15" ] ], "expectError": "shares must not all be zero" }, { "name": "dates out of order are an error", "args": [ { "minor": 100, "currency": "GBP" }, [ 1, 1 ], [ "2026-01-15", "2025-10-01" ] ], "expectError": "dueDates must be in strictly ascending order: 2025-10-01 follows 2026-01-15" }, { "name": "two instalments on one day are an error", "args": [ { "minor": 100, "currency": "GBP" }, [ 1, 1 ], [ "2025-10-01", "2025-10-01" ] ], "expectError": "dueDates must be in strictly ascending order" }, { "name": "an impossible due date is an error", "args": [ { "minor": 100, "currency": "GBP" }, [ 1 ], [ "2026-02-30" ] ], "expectError": "\"2026-02-30\" is not a real calendar date" } ]