c6df2ab8f5
contracts/openapi.yaml (OpenAPI 3.1, E-1..E-33, error shape and the exhaustive error code enum, device bearer scheme), contracts/schema/ server-v1.sql (SPEC §14.1 verbatim), seven test-vector files per §14.5.5, 80 examples (every JSON body of §14.3.4, every error code, sync pull/push, every FCM push type), and tools/contracts-check.py (stdlib-only reference of §9.1, §9.3, §9.4 and §14.5.4 plus shape checks; the M1 gate). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1173 lines
28 KiB
JSON
1173 lines
28 KiB
JSON
{
|
|
"format": 1,
|
|
"function": "spend_series",
|
|
"cases": [
|
|
{
|
|
"name": "monthly across a year boundary, household, by category; edge buckets clipped, empty January kept, rows outside the range ignored",
|
|
"input": {
|
|
"expenses": [
|
|
{
|
|
"date": "2026-11-14",
|
|
"account_id": "x",
|
|
"category_id": "c1",
|
|
"amount_ore": 1000,
|
|
"shares": {
|
|
"a": 500,
|
|
"b": 500
|
|
}
|
|
},
|
|
{
|
|
"date": "2026-11-15",
|
|
"account_id": "x",
|
|
"category_id": "c1",
|
|
"amount_ore": 2000,
|
|
"shares": {
|
|
"a": 1000,
|
|
"b": 1000
|
|
}
|
|
},
|
|
{
|
|
"date": "2026-11-30",
|
|
"account_id": "y",
|
|
"category_id": "c2",
|
|
"amount_ore": 3000,
|
|
"shares": {
|
|
"a": 3000,
|
|
"b": 0
|
|
}
|
|
},
|
|
{
|
|
"date": "2026-12-01",
|
|
"account_id": "x",
|
|
"category_id": "c2",
|
|
"amount_ore": 4000,
|
|
"shares": {
|
|
"a": 1000,
|
|
"b": 3000
|
|
}
|
|
},
|
|
{
|
|
"date": "2027-02-10",
|
|
"account_id": "y",
|
|
"category_id": "c1",
|
|
"amount_ore": 500,
|
|
"shares": {
|
|
"a": 250,
|
|
"b": 250
|
|
}
|
|
},
|
|
{
|
|
"date": "2027-02-11",
|
|
"account_id": "x",
|
|
"category_id": "c1",
|
|
"amount_ore": 700,
|
|
"shares": {
|
|
"a": 350,
|
|
"b": 350
|
|
}
|
|
}
|
|
],
|
|
"range": {
|
|
"start": "2026-11-15",
|
|
"end_inclusive": "2027-02-10"
|
|
},
|
|
"granularity": "month",
|
|
"measure": "household",
|
|
"group_by": "category"
|
|
},
|
|
"expected": {
|
|
"ok": {
|
|
"buckets": [
|
|
{
|
|
"key": "2026-11",
|
|
"start": "2026-11-15",
|
|
"end_inclusive": "2026-11-30",
|
|
"totals": {
|
|
"c1": 2000,
|
|
"c2": 3000
|
|
},
|
|
"total": 5000
|
|
},
|
|
{
|
|
"key": "2026-12",
|
|
"start": "2026-12-01",
|
|
"end_inclusive": "2026-12-31",
|
|
"totals": {
|
|
"c2": 4000
|
|
},
|
|
"total": 4000
|
|
},
|
|
{
|
|
"key": "2027-01",
|
|
"start": "2027-01-01",
|
|
"end_inclusive": "2027-01-31",
|
|
"totals": {},
|
|
"total": 0
|
|
},
|
|
{
|
|
"key": "2027-02",
|
|
"start": "2027-02-01",
|
|
"end_inclusive": "2027-02-10",
|
|
"totals": {
|
|
"c1": 500
|
|
},
|
|
"total": 500
|
|
}
|
|
],
|
|
"group_totals": {
|
|
"c1": 2500,
|
|
"c2": 7000
|
|
},
|
|
"grand_total": 9500
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"name": "monthly, share of a, by account",
|
|
"input": {
|
|
"expenses": [
|
|
{
|
|
"date": "2026-11-14",
|
|
"account_id": "x",
|
|
"category_id": "c1",
|
|
"amount_ore": 1000,
|
|
"shares": {
|
|
"a": 500,
|
|
"b": 500
|
|
}
|
|
},
|
|
{
|
|
"date": "2026-11-15",
|
|
"account_id": "x",
|
|
"category_id": "c1",
|
|
"amount_ore": 2000,
|
|
"shares": {
|
|
"a": 1000,
|
|
"b": 1000
|
|
}
|
|
},
|
|
{
|
|
"date": "2026-11-30",
|
|
"account_id": "y",
|
|
"category_id": "c2",
|
|
"amount_ore": 3000,
|
|
"shares": {
|
|
"a": 3000,
|
|
"b": 0
|
|
}
|
|
},
|
|
{
|
|
"date": "2026-12-01",
|
|
"account_id": "x",
|
|
"category_id": "c2",
|
|
"amount_ore": 4000,
|
|
"shares": {
|
|
"a": 1000,
|
|
"b": 3000
|
|
}
|
|
},
|
|
{
|
|
"date": "2027-02-10",
|
|
"account_id": "y",
|
|
"category_id": "c1",
|
|
"amount_ore": 500,
|
|
"shares": {
|
|
"a": 250,
|
|
"b": 250
|
|
}
|
|
},
|
|
{
|
|
"date": "2027-02-11",
|
|
"account_id": "x",
|
|
"category_id": "c1",
|
|
"amount_ore": 700,
|
|
"shares": {
|
|
"a": 350,
|
|
"b": 350
|
|
}
|
|
}
|
|
],
|
|
"range": {
|
|
"start": "2026-11-15",
|
|
"end_inclusive": "2027-02-10"
|
|
},
|
|
"granularity": "month",
|
|
"measure": {
|
|
"share": "a"
|
|
},
|
|
"group_by": "account"
|
|
},
|
|
"expected": {
|
|
"ok": {
|
|
"buckets": [
|
|
{
|
|
"key": "2026-11",
|
|
"start": "2026-11-15",
|
|
"end_inclusive": "2026-11-30",
|
|
"totals": {
|
|
"x": 1000,
|
|
"y": 3000
|
|
},
|
|
"total": 4000
|
|
},
|
|
{
|
|
"key": "2026-12",
|
|
"start": "2026-12-01",
|
|
"end_inclusive": "2026-12-31",
|
|
"totals": {
|
|
"x": 1000
|
|
},
|
|
"total": 1000
|
|
},
|
|
{
|
|
"key": "2027-01",
|
|
"start": "2027-01-01",
|
|
"end_inclusive": "2027-01-31",
|
|
"totals": {},
|
|
"total": 0
|
|
},
|
|
{
|
|
"key": "2027-02",
|
|
"start": "2027-02-01",
|
|
"end_inclusive": "2027-02-10",
|
|
"totals": {
|
|
"y": 250
|
|
},
|
|
"total": 250
|
|
}
|
|
],
|
|
"group_totals": {
|
|
"x": 2000,
|
|
"y": 3250
|
|
},
|
|
"grand_total": 5250
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"name": "monthly, share of b, by category: a zero share is omitted from totals",
|
|
"input": {
|
|
"expenses": [
|
|
{
|
|
"date": "2026-11-14",
|
|
"account_id": "x",
|
|
"category_id": "c1",
|
|
"amount_ore": 1000,
|
|
"shares": {
|
|
"a": 500,
|
|
"b": 500
|
|
}
|
|
},
|
|
{
|
|
"date": "2026-11-15",
|
|
"account_id": "x",
|
|
"category_id": "c1",
|
|
"amount_ore": 2000,
|
|
"shares": {
|
|
"a": 1000,
|
|
"b": 1000
|
|
}
|
|
},
|
|
{
|
|
"date": "2026-11-30",
|
|
"account_id": "y",
|
|
"category_id": "c2",
|
|
"amount_ore": 3000,
|
|
"shares": {
|
|
"a": 3000,
|
|
"b": 0
|
|
}
|
|
},
|
|
{
|
|
"date": "2026-12-01",
|
|
"account_id": "x",
|
|
"category_id": "c2",
|
|
"amount_ore": 4000,
|
|
"shares": {
|
|
"a": 1000,
|
|
"b": 3000
|
|
}
|
|
},
|
|
{
|
|
"date": "2027-02-10",
|
|
"account_id": "y",
|
|
"category_id": "c1",
|
|
"amount_ore": 500,
|
|
"shares": {
|
|
"a": 250,
|
|
"b": 250
|
|
}
|
|
},
|
|
{
|
|
"date": "2027-02-11",
|
|
"account_id": "x",
|
|
"category_id": "c1",
|
|
"amount_ore": 700,
|
|
"shares": {
|
|
"a": 350,
|
|
"b": 350
|
|
}
|
|
}
|
|
],
|
|
"range": {
|
|
"start": "2026-11-15",
|
|
"end_inclusive": "2027-02-10"
|
|
},
|
|
"granularity": "month",
|
|
"measure": {
|
|
"share": "b"
|
|
},
|
|
"group_by": "category"
|
|
},
|
|
"expected": {
|
|
"ok": {
|
|
"buckets": [
|
|
{
|
|
"key": "2026-11",
|
|
"start": "2026-11-15",
|
|
"end_inclusive": "2026-11-30",
|
|
"totals": {
|
|
"c1": 1000
|
|
},
|
|
"total": 1000
|
|
},
|
|
{
|
|
"key": "2026-12",
|
|
"start": "2026-12-01",
|
|
"end_inclusive": "2026-12-31",
|
|
"totals": {
|
|
"c2": 3000
|
|
},
|
|
"total": 3000
|
|
},
|
|
{
|
|
"key": "2027-01",
|
|
"start": "2027-01-01",
|
|
"end_inclusive": "2027-01-31",
|
|
"totals": {},
|
|
"total": 0
|
|
},
|
|
{
|
|
"key": "2027-02",
|
|
"start": "2027-02-01",
|
|
"end_inclusive": "2027-02-10",
|
|
"totals": {
|
|
"c1": 250
|
|
},
|
|
"total": 250
|
|
}
|
|
],
|
|
"group_totals": {
|
|
"c1": 1250,
|
|
"c2": 3000
|
|
},
|
|
"grand_total": 4250
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"name": "share of a user absent from every shares map counts 0",
|
|
"input": {
|
|
"expenses": [
|
|
{
|
|
"date": "2026-11-14",
|
|
"account_id": "x",
|
|
"category_id": "c1",
|
|
"amount_ore": 1000,
|
|
"shares": {
|
|
"a": 500,
|
|
"b": 500
|
|
}
|
|
},
|
|
{
|
|
"date": "2026-11-15",
|
|
"account_id": "x",
|
|
"category_id": "c1",
|
|
"amount_ore": 2000,
|
|
"shares": {
|
|
"a": 1000,
|
|
"b": 1000
|
|
}
|
|
},
|
|
{
|
|
"date": "2026-11-30",
|
|
"account_id": "y",
|
|
"category_id": "c2",
|
|
"amount_ore": 3000,
|
|
"shares": {
|
|
"a": 3000,
|
|
"b": 0
|
|
}
|
|
},
|
|
{
|
|
"date": "2026-12-01",
|
|
"account_id": "x",
|
|
"category_id": "c2",
|
|
"amount_ore": 4000,
|
|
"shares": {
|
|
"a": 1000,
|
|
"b": 3000
|
|
}
|
|
},
|
|
{
|
|
"date": "2027-02-10",
|
|
"account_id": "y",
|
|
"category_id": "c1",
|
|
"amount_ore": 500,
|
|
"shares": {
|
|
"a": 250,
|
|
"b": 250
|
|
}
|
|
},
|
|
{
|
|
"date": "2027-02-11",
|
|
"account_id": "x",
|
|
"category_id": "c1",
|
|
"amount_ore": 700,
|
|
"shares": {
|
|
"a": 350,
|
|
"b": 350
|
|
}
|
|
}
|
|
],
|
|
"range": {
|
|
"start": "2026-11-15",
|
|
"end_inclusive": "2026-12-31"
|
|
},
|
|
"granularity": "month",
|
|
"measure": {
|
|
"share": "z"
|
|
},
|
|
"group_by": "category"
|
|
},
|
|
"expected": {
|
|
"ok": {
|
|
"buckets": [
|
|
{
|
|
"key": "2026-11",
|
|
"start": "2026-11-15",
|
|
"end_inclusive": "2026-11-30",
|
|
"totals": {},
|
|
"total": 0
|
|
},
|
|
{
|
|
"key": "2026-12",
|
|
"start": "2026-12-01",
|
|
"end_inclusive": "2026-12-31",
|
|
"totals": {},
|
|
"total": 0
|
|
}
|
|
],
|
|
"group_totals": {},
|
|
"grand_total": 0
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"name": "weekly across the 2026/2027 boundary: ISO week 2026-W53 runs 28 Dec 2026 to 3 Jan 2027; first and last weeks clipped, empty W52 kept",
|
|
"input": {
|
|
"expenses": [
|
|
{
|
|
"date": "2026-12-19",
|
|
"account_id": "x",
|
|
"category_id": "c1",
|
|
"amount_ore": 50,
|
|
"shares": {
|
|
"a": 25,
|
|
"b": 25
|
|
}
|
|
},
|
|
{
|
|
"date": "2026-12-20",
|
|
"account_id": "x",
|
|
"category_id": "c1",
|
|
"amount_ore": 100,
|
|
"shares": {
|
|
"a": 50,
|
|
"b": 50
|
|
}
|
|
},
|
|
{
|
|
"date": "2026-12-31",
|
|
"account_id": "x",
|
|
"category_id": "c1",
|
|
"amount_ore": 200,
|
|
"shares": {
|
|
"a": 100,
|
|
"b": 100
|
|
}
|
|
},
|
|
{
|
|
"date": "2027-01-01",
|
|
"account_id": "y",
|
|
"category_id": "c2",
|
|
"amount_ore": 300,
|
|
"shares": {
|
|
"a": 300,
|
|
"b": 0
|
|
}
|
|
},
|
|
{
|
|
"date": "2027-01-03",
|
|
"account_id": "y",
|
|
"category_id": "c2",
|
|
"amount_ore": 400,
|
|
"shares": {
|
|
"a": 0,
|
|
"b": 400
|
|
}
|
|
},
|
|
{
|
|
"date": "2027-01-04",
|
|
"account_id": "x",
|
|
"category_id": "c1",
|
|
"amount_ore": 500,
|
|
"shares": {
|
|
"a": 250,
|
|
"b": 250
|
|
}
|
|
},
|
|
{
|
|
"date": "2027-01-12",
|
|
"account_id": "y",
|
|
"category_id": "c2",
|
|
"amount_ore": 600,
|
|
"shares": {
|
|
"a": 300,
|
|
"b": 300
|
|
}
|
|
},
|
|
{
|
|
"date": "2027-01-13",
|
|
"account_id": "y",
|
|
"category_id": "c2",
|
|
"amount_ore": 900,
|
|
"shares": {
|
|
"a": 450,
|
|
"b": 450
|
|
}
|
|
}
|
|
],
|
|
"range": {
|
|
"start": "2026-12-20",
|
|
"end_inclusive": "2027-01-12"
|
|
},
|
|
"granularity": "week",
|
|
"measure": "household",
|
|
"group_by": "category"
|
|
},
|
|
"expected": {
|
|
"ok": {
|
|
"buckets": [
|
|
{
|
|
"key": "2026-W51",
|
|
"start": "2026-12-20",
|
|
"end_inclusive": "2026-12-20",
|
|
"totals": {
|
|
"c1": 100
|
|
},
|
|
"total": 100
|
|
},
|
|
{
|
|
"key": "2026-W52",
|
|
"start": "2026-12-21",
|
|
"end_inclusive": "2026-12-27",
|
|
"totals": {},
|
|
"total": 0
|
|
},
|
|
{
|
|
"key": "2026-W53",
|
|
"start": "2026-12-28",
|
|
"end_inclusive": "2027-01-03",
|
|
"totals": {
|
|
"c1": 200,
|
|
"c2": 700
|
|
},
|
|
"total": 900
|
|
},
|
|
{
|
|
"key": "2027-W01",
|
|
"start": "2027-01-04",
|
|
"end_inclusive": "2027-01-10",
|
|
"totals": {
|
|
"c1": 500
|
|
},
|
|
"total": 500
|
|
},
|
|
{
|
|
"key": "2027-W02",
|
|
"start": "2027-01-11",
|
|
"end_inclusive": "2027-01-12",
|
|
"totals": {
|
|
"c2": 600
|
|
},
|
|
"total": 600
|
|
}
|
|
],
|
|
"group_totals": {
|
|
"c1": 800,
|
|
"c2": 1300
|
|
},
|
|
"grand_total": 2100
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"name": "weekly across the 2026/2027 boundary, share of b, by account",
|
|
"input": {
|
|
"expenses": [
|
|
{
|
|
"date": "2026-12-19",
|
|
"account_id": "x",
|
|
"category_id": "c1",
|
|
"amount_ore": 50,
|
|
"shares": {
|
|
"a": 25,
|
|
"b": 25
|
|
}
|
|
},
|
|
{
|
|
"date": "2026-12-20",
|
|
"account_id": "x",
|
|
"category_id": "c1",
|
|
"amount_ore": 100,
|
|
"shares": {
|
|
"a": 50,
|
|
"b": 50
|
|
}
|
|
},
|
|
{
|
|
"date": "2026-12-31",
|
|
"account_id": "x",
|
|
"category_id": "c1",
|
|
"amount_ore": 200,
|
|
"shares": {
|
|
"a": 100,
|
|
"b": 100
|
|
}
|
|
},
|
|
{
|
|
"date": "2027-01-01",
|
|
"account_id": "y",
|
|
"category_id": "c2",
|
|
"amount_ore": 300,
|
|
"shares": {
|
|
"a": 300,
|
|
"b": 0
|
|
}
|
|
},
|
|
{
|
|
"date": "2027-01-03",
|
|
"account_id": "y",
|
|
"category_id": "c2",
|
|
"amount_ore": 400,
|
|
"shares": {
|
|
"a": 0,
|
|
"b": 400
|
|
}
|
|
},
|
|
{
|
|
"date": "2027-01-04",
|
|
"account_id": "x",
|
|
"category_id": "c1",
|
|
"amount_ore": 500,
|
|
"shares": {
|
|
"a": 250,
|
|
"b": 250
|
|
}
|
|
},
|
|
{
|
|
"date": "2027-01-12",
|
|
"account_id": "y",
|
|
"category_id": "c2",
|
|
"amount_ore": 600,
|
|
"shares": {
|
|
"a": 300,
|
|
"b": 300
|
|
}
|
|
},
|
|
{
|
|
"date": "2027-01-13",
|
|
"account_id": "y",
|
|
"category_id": "c2",
|
|
"amount_ore": 900,
|
|
"shares": {
|
|
"a": 450,
|
|
"b": 450
|
|
}
|
|
}
|
|
],
|
|
"range": {
|
|
"start": "2026-12-20",
|
|
"end_inclusive": "2027-01-12"
|
|
},
|
|
"granularity": "week",
|
|
"measure": {
|
|
"share": "b"
|
|
},
|
|
"group_by": "account"
|
|
},
|
|
"expected": {
|
|
"ok": {
|
|
"buckets": [
|
|
{
|
|
"key": "2026-W51",
|
|
"start": "2026-12-20",
|
|
"end_inclusive": "2026-12-20",
|
|
"totals": {
|
|
"x": 50
|
|
},
|
|
"total": 50
|
|
},
|
|
{
|
|
"key": "2026-W52",
|
|
"start": "2026-12-21",
|
|
"end_inclusive": "2026-12-27",
|
|
"totals": {},
|
|
"total": 0
|
|
},
|
|
{
|
|
"key": "2026-W53",
|
|
"start": "2026-12-28",
|
|
"end_inclusive": "2027-01-03",
|
|
"totals": {
|
|
"x": 100,
|
|
"y": 400
|
|
},
|
|
"total": 500
|
|
},
|
|
{
|
|
"key": "2027-W01",
|
|
"start": "2027-01-04",
|
|
"end_inclusive": "2027-01-10",
|
|
"totals": {
|
|
"x": 250
|
|
},
|
|
"total": 250
|
|
},
|
|
{
|
|
"key": "2027-W02",
|
|
"start": "2027-01-11",
|
|
"end_inclusive": "2027-01-12",
|
|
"totals": {
|
|
"y": 300
|
|
},
|
|
"total": 300
|
|
}
|
|
],
|
|
"group_totals": {
|
|
"x": 400,
|
|
"y": 700
|
|
},
|
|
"grand_total": 1100
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"name": "weekly where the ISO year is ahead of the calendar year: 30 Dec 2024 is in 2025-W01",
|
|
"input": {
|
|
"expenses": [
|
|
{
|
|
"date": "2024-12-29",
|
|
"account_id": "x",
|
|
"category_id": "c1",
|
|
"amount_ore": 100,
|
|
"shares": {
|
|
"a": 50,
|
|
"b": 50
|
|
}
|
|
},
|
|
{
|
|
"date": "2024-12-30",
|
|
"account_id": "y",
|
|
"category_id": "c1",
|
|
"amount_ore": 200,
|
|
"shares": {
|
|
"a": 100,
|
|
"b": 100
|
|
}
|
|
},
|
|
{
|
|
"date": "2025-01-06",
|
|
"account_id": "x",
|
|
"category_id": "c2",
|
|
"amount_ore": 300,
|
|
"shares": {
|
|
"a": 150,
|
|
"b": 150
|
|
}
|
|
}
|
|
],
|
|
"range": {
|
|
"start": "2024-12-28",
|
|
"end_inclusive": "2025-01-06"
|
|
},
|
|
"granularity": "week",
|
|
"measure": "household",
|
|
"group_by": "account"
|
|
},
|
|
"expected": {
|
|
"ok": {
|
|
"buckets": [
|
|
{
|
|
"key": "2024-W52",
|
|
"start": "2024-12-28",
|
|
"end_inclusive": "2024-12-29",
|
|
"totals": {
|
|
"x": 100
|
|
},
|
|
"total": 100
|
|
},
|
|
{
|
|
"key": "2025-W01",
|
|
"start": "2024-12-30",
|
|
"end_inclusive": "2025-01-05",
|
|
"totals": {
|
|
"y": 200
|
|
},
|
|
"total": 200
|
|
},
|
|
{
|
|
"key": "2025-W02",
|
|
"start": "2025-01-06",
|
|
"end_inclusive": "2025-01-06",
|
|
"totals": {
|
|
"x": 300
|
|
},
|
|
"total": 300
|
|
}
|
|
],
|
|
"group_totals": {
|
|
"x": 400,
|
|
"y": 200
|
|
},
|
|
"grand_total": 600
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"name": "month boundaries in a leap year: last and first days land in their own months, 29 Feb counted",
|
|
"input": {
|
|
"expenses": [
|
|
{
|
|
"date": "2028-01-31",
|
|
"account_id": "x",
|
|
"category_id": "c1",
|
|
"amount_ore": 100,
|
|
"shares": {
|
|
"a": 50,
|
|
"b": 50
|
|
}
|
|
},
|
|
{
|
|
"date": "2028-02-01",
|
|
"account_id": "x",
|
|
"category_id": "c1",
|
|
"amount_ore": 200,
|
|
"shares": {
|
|
"a": 100,
|
|
"b": 100
|
|
}
|
|
},
|
|
{
|
|
"date": "2028-02-29",
|
|
"account_id": "x",
|
|
"category_id": "c2",
|
|
"amount_ore": 300,
|
|
"shares": {
|
|
"a": 150,
|
|
"b": 150
|
|
}
|
|
},
|
|
{
|
|
"date": "2028-03-01",
|
|
"account_id": "x",
|
|
"category_id": "c1",
|
|
"amount_ore": 400,
|
|
"shares": {
|
|
"a": 200,
|
|
"b": 200
|
|
}
|
|
}
|
|
],
|
|
"range": {
|
|
"start": "2028-01-31",
|
|
"end_inclusive": "2028-03-01"
|
|
},
|
|
"granularity": "month",
|
|
"measure": "household",
|
|
"group_by": "category"
|
|
},
|
|
"expected": {
|
|
"ok": {
|
|
"buckets": [
|
|
{
|
|
"key": "2028-01",
|
|
"start": "2028-01-31",
|
|
"end_inclusive": "2028-01-31",
|
|
"totals": {
|
|
"c1": 100
|
|
},
|
|
"total": 100
|
|
},
|
|
{
|
|
"key": "2028-02",
|
|
"start": "2028-02-01",
|
|
"end_inclusive": "2028-02-29",
|
|
"totals": {
|
|
"c1": 200,
|
|
"c2": 300
|
|
},
|
|
"total": 500
|
|
},
|
|
{
|
|
"key": "2028-03",
|
|
"start": "2028-03-01",
|
|
"end_inclusive": "2028-03-01",
|
|
"totals": {
|
|
"c1": 400
|
|
},
|
|
"total": 400
|
|
}
|
|
],
|
|
"group_totals": {
|
|
"c1": 700,
|
|
"c2": 300
|
|
},
|
|
"grand_total": 1000
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"name": "month boundaries in a common year: February has 28 days",
|
|
"input": {
|
|
"expenses": [
|
|
{
|
|
"date": "2027-02-28",
|
|
"account_id": "x",
|
|
"category_id": "c1",
|
|
"amount_ore": 100,
|
|
"shares": {
|
|
"a": 50,
|
|
"b": 50
|
|
}
|
|
},
|
|
{
|
|
"date": "2027-03-01",
|
|
"account_id": "x",
|
|
"category_id": "c1",
|
|
"amount_ore": 200,
|
|
"shares": {
|
|
"a": 100,
|
|
"b": 100
|
|
}
|
|
}
|
|
],
|
|
"range": {
|
|
"start": "2027-02-01",
|
|
"end_inclusive": "2027-03-31"
|
|
},
|
|
"granularity": "month",
|
|
"measure": "household",
|
|
"group_by": "category"
|
|
},
|
|
"expected": {
|
|
"ok": {
|
|
"buckets": [
|
|
{
|
|
"key": "2027-02",
|
|
"start": "2027-02-01",
|
|
"end_inclusive": "2027-02-28",
|
|
"totals": {
|
|
"c1": 100
|
|
},
|
|
"total": 100
|
|
},
|
|
{
|
|
"key": "2027-03",
|
|
"start": "2027-03-01",
|
|
"end_inclusive": "2027-03-31",
|
|
"totals": {
|
|
"c1": 200
|
|
},
|
|
"total": 200
|
|
}
|
|
],
|
|
"group_totals": {
|
|
"c1": 300
|
|
},
|
|
"grand_total": 300
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"name": "a single-day range with no expenses: one empty, clipped bucket",
|
|
"input": {
|
|
"expenses": [],
|
|
"range": {
|
|
"start": "2026-10-17",
|
|
"end_inclusive": "2026-10-17"
|
|
},
|
|
"granularity": "month",
|
|
"measure": "household",
|
|
"group_by": "category"
|
|
},
|
|
"expected": {
|
|
"ok": {
|
|
"buckets": [
|
|
{
|
|
"key": "2026-10",
|
|
"start": "2026-10-17",
|
|
"end_inclusive": "2026-10-17",
|
|
"totals": {},
|
|
"total": 0
|
|
}
|
|
],
|
|
"group_totals": {},
|
|
"grand_total": 0
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"name": "weekly with no expenses: whole weeks, all empty",
|
|
"input": {
|
|
"expenses": [],
|
|
"range": {
|
|
"start": "2026-10-12",
|
|
"end_inclusive": "2026-10-25"
|
|
},
|
|
"granularity": "week",
|
|
"measure": "household",
|
|
"group_by": "category"
|
|
},
|
|
"expected": {
|
|
"ok": {
|
|
"buckets": [
|
|
{
|
|
"key": "2026-W42",
|
|
"start": "2026-10-12",
|
|
"end_inclusive": "2026-10-18",
|
|
"totals": {},
|
|
"total": 0
|
|
},
|
|
{
|
|
"key": "2026-W43",
|
|
"start": "2026-10-19",
|
|
"end_inclusive": "2026-10-25",
|
|
"totals": {},
|
|
"total": 0
|
|
}
|
|
],
|
|
"group_totals": {},
|
|
"grand_total": 0
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"name": "several expenses in the same group and bucket are summed",
|
|
"input": {
|
|
"expenses": [
|
|
{
|
|
"date": "2026-10-01",
|
|
"account_id": "x",
|
|
"category_id": "c1",
|
|
"amount_ore": 100,
|
|
"shares": {
|
|
"a": 50,
|
|
"b": 50
|
|
}
|
|
},
|
|
{
|
|
"date": "2026-10-17",
|
|
"account_id": "y",
|
|
"category_id": "c1",
|
|
"amount_ore": 250,
|
|
"shares": {
|
|
"a": 125,
|
|
"b": 125
|
|
}
|
|
},
|
|
{
|
|
"date": "2026-10-31",
|
|
"account_id": "x",
|
|
"category_id": "c1",
|
|
"amount_ore": 1,
|
|
"shares": {
|
|
"a": 1,
|
|
"b": 0
|
|
}
|
|
}
|
|
],
|
|
"range": {
|
|
"start": "2026-10-01",
|
|
"end_inclusive": "2026-10-31"
|
|
},
|
|
"granularity": "month",
|
|
"measure": "household",
|
|
"group_by": "category"
|
|
},
|
|
"expected": {
|
|
"ok": {
|
|
"buckets": [
|
|
{
|
|
"key": "2026-10",
|
|
"start": "2026-10-01",
|
|
"end_inclusive": "2026-10-31",
|
|
"totals": {
|
|
"c1": 351
|
|
},
|
|
"total": 351
|
|
}
|
|
],
|
|
"group_totals": {
|
|
"c1": 351
|
|
},
|
|
"grand_total": 351
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"name": "tombstoned rows excluded: callers pass only non-deleted expenses (AC-14.9); archived accounts and categories are ordinary ids and are counted",
|
|
"input": {
|
|
"expenses": [
|
|
{
|
|
"date": "2026-10-05",
|
|
"account_id": "archived-account",
|
|
"category_id": "archived-category",
|
|
"amount_ore": 1200,
|
|
"shares": {
|
|
"a": 600,
|
|
"b": 600
|
|
}
|
|
}
|
|
],
|
|
"range": {
|
|
"start": "2026-10-01",
|
|
"end_inclusive": "2026-10-31"
|
|
},
|
|
"granularity": "month",
|
|
"measure": "household",
|
|
"group_by": "account"
|
|
},
|
|
"expected": {
|
|
"ok": {
|
|
"buckets": [
|
|
{
|
|
"key": "2026-10",
|
|
"start": "2026-10-01",
|
|
"end_inclusive": "2026-10-31",
|
|
"totals": {
|
|
"archived-account": 1200
|
|
},
|
|
"total": 1200
|
|
}
|
|
],
|
|
"group_totals": {
|
|
"archived-account": 1200
|
|
},
|
|
"grand_total": 1200
|
|
}
|
|
}
|
|
}
|
|
]
|
|
} |