Functional Weave
Code in Python

finance.payment-terms-due-date@1.0.0

vectors.json

5,070 bytes · view raw

[
  {"name": "net 30 counts calendar days: 15 January is due 14 February", "args": ["2026-01-15", {"kind": "net", "n": 30, "discountBasisPoints": 0, "discountDays": 0}], "expect": "2026-02-14"},
  {"name": "net 30 from 31 January is 2 March in 2026, not 28 February (adding a month gets this wrong)", "args": ["2026-01-31", {"kind": "net", "n": 30, "discountBasisPoints": 0, "discountDays": 0}], "expect": "2026-03-02"},
  {"name": "net 30 from 31 January 2024 crosses 29 February", "args": ["2024-01-31", {"kind": "net", "n": 30, "discountBasisPoints": 0, "discountDays": 0}], "expect": "2024-03-01"},
  {"name": "net 0 is due on the invoice date", "args": ["2026-03-10", {"kind": "net", "n": 0, "discountBasisPoints": 0, "discountDays": 0}], "expect": "2026-03-10"},
  {"name": "net 60 across a year end", "args": ["2026-11-15", {"kind": "net", "n": 60, "discountBasisPoints": 0, "discountDays": 0}], "expect": "2027-01-14"},
  {"name": "end of month, n 0: the invoice month's own last day", "args": ["2026-02-10", {"kind": "end-of-month", "n": 0, "discountBasisPoints": 0, "discountDays": 0}], "expect": "2026-02-28"},
  {"name": "end of month following, invoiced on the 31st", "args": ["2026-01-31", {"kind": "end-of-month", "n": 1, "discountBasisPoints": 0, "discountDays": 0}], "expect": "2026-02-28"},
  {"name": "end of month following in a leap year", "args": ["2024-01-15", {"kind": "end-of-month", "n": 1, "discountBasisPoints": 0, "discountDays": 0}], "expect": "2024-02-29"},
  {"name": "end of the second month following rolls the year", "args": ["2026-11-20", {"kind": "end-of-month", "n": 2, "discountBasisPoints": 0, "discountDays": 0}], "expect": "2027-01-31"},
  {"name": "30 days after month end", "args": ["2026-01-15", {"kind": "days-after-month-end", "n": 30, "discountBasisPoints": 0, "discountDays": 0}], "expect": "2026-03-02"},
  {"name": "30 days after month end is the same for an invoice on the last day", "args": ["2026-01-31", {"kind": "days-after-month-end", "n": 30, "discountBasisPoints": 0, "discountDays": 0}], "expect": "2026-03-02"},
  {"name": "0 days after month end is the month end", "args": ["2026-04-05", {"kind": "days-after-month-end", "n": 0, "discountBasisPoints": 0, "discountDays": 0}], "expect": "2026-04-30"},
  {"name": "20th of next month across a year end", "args": ["2026-12-05", {"kind": "day-of-next-month", "n": 20, "discountBasisPoints": 0, "discountDays": 0}], "expect": "2027-01-20"},
  {"name": "31st of next month is clamped to 28 February", "args": ["2026-01-10", {"kind": "day-of-next-month", "n": 31, "discountBasisPoints": 0, "discountDays": 0}], "expect": "2026-02-28"},
  {"name": "day of next month from the last day of a month", "args": ["2026-01-31", {"kind": "day-of-next-month", "n": 15, "discountBasisPoints": 0, "discountDays": 0}], "expect": "2026-02-15"},
  {"name": "2/10 net 30: the discount does not move the due date", "args": ["2026-03-01", {"kind": "net", "n": 30, "discountBasisPoints": 200, "discountDays": 10}], "expect": "2026-03-31"},
  {"name": "a discount open until the due date itself is allowed", "args": ["2026-03-01", {"kind": "net", "n": 30, "discountBasisPoints": 200, "discountDays": 30}], "expect": "2026-03-31"},
  {"name": "negative days are refused", "args": ["2026-03-01", {"kind": "net", "n": -1, "discountBasisPoints": 0, "discountDays": 0}], "expectError": "payment terms n must be 0 or more"},
  {"name": "negative months are refused", "args": ["2026-03-01", {"kind": "end-of-month", "n": -1, "discountBasisPoints": 0, "discountDays": 0}], "expectError": "payment terms n must be 0 or more"},
  {"name": "day 0 of next month is refused", "args": ["2026-03-01", {"kind": "day-of-next-month", "n": 0, "discountBasisPoints": 0, "discountDays": 0}], "expectError": "day-of-next-month needs a day from 1 to 31"},
  {"name": "day 32 of next month is refused", "args": ["2026-03-01", {"kind": "day-of-next-month", "n": 32, "discountBasisPoints": 0, "discountDays": 0}], "expectError": "day-of-next-month needs a day from 1 to 31"},
  {"name": "a discount over 100 percent is refused", "args": ["2026-03-01", {"kind": "net", "n": 30, "discountBasisPoints": 10001, "discountDays": 10}], "expectError": "discount must be 0 to 10000 basis points"},
  {"name": "negative discount days are refused", "args": ["2026-03-01", {"kind": "net", "n": 30, "discountBasisPoints": 200, "discountDays": -1}], "expectError": "discount days must be 0 or more"},
  {"name": "2/40 net 30: a discount that outlasts the due date is refused", "args": ["2026-03-01", {"kind": "net", "n": 30, "discountBasisPoints": 200, "discountDays": 40}], "expectError": "the discount period ends after the due date 2026-03-31"},
  {"name": "an impossible invoice date is refused", "args": ["2026-02-30", {"kind": "net", "n": 30, "discountBasisPoints": 0, "discountDays": 0}], "expectError": "not a real calendar date"},
  {"name": "an unknown kind is refused", "args": ["2026-03-01", {"kind": "weekly", "n": 1, "discountBasisPoints": 0, "discountDays": 0}], "expectError": "unknown payment terms kind"}
]