Contracts for 14 default categories and the category suggestion (UX-3, UX-8)
- contracts/schema/migrations/0002_categories_v2.sql: server migration 0002 rebuilds categories with the 14-key default_key CHECK, inserts housing, insurance, children and fees with server_versions taken from the global sequence, sets schema_version 2. server-v1.sql unchanged. - contracts/category-keywords.csv: the keyword table of the suggestion (148 entries, from docs/ux). - contracts/test-vectors/suggest_category.json: 65 Kotlin-only cases of CS-4 to CS-6. - contracts/examples/E-1-health: schema_version 2. - tools/contracts-check.py: reference suggestCategory, keyword-table checks, 0002 load checks on a fresh and an in-use database, plain icon/color check, new icon and colour tokens. - android/domain: suggestCategory (CS-4 to CS-6, code points, platform NFC and categories through UnicodeText), vector runner for suggest_category, eight vector files. - server/crates/kvitt-domain/tests/vectors.rs: suggest_category is Kotlin-only. The OpenAPI DefaultCategoryKey extension and the two new category examples are held back until the server's wire types know the new keys (M12b). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
keyword,match,default_key,source
|
||||
barn,prefix,children,data
|
||||
blöjor,token,children,curated
|
||||
fritids,token,children,curated
|
||||
förskol,prefix,children,data
|
||||
leksak,prefix,children,curated
|
||||
napp,prefix,children,data
|
||||
resevagn,token,children,data
|
||||
bio,token,entertainment,data
|
||||
entre,token,entertainment,data
|
||||
entré,token,entertainment,data
|
||||
filmstaden,token,entertainment,curated
|
||||
karaoke,token,entertainment,data
|
||||
konsert,prefix,entertainment,data
|
||||
museet,token,entertainment,curated
|
||||
museum,token,entertainment,data
|
||||
netflix,token,entertainment,curated
|
||||
padel,token,entertainment,curated
|
||||
spotify,token,entertainment,data
|
||||
teater,prefix,entertainment,curated
|
||||
bankavgift,token,fees,curated
|
||||
kvarskatt,token,fees,curated
|
||||
ränteåterbäring,token,fees,data
|
||||
skatt,token,fees,curated
|
||||
skatteverket,token,fees,curated
|
||||
alkohol,token,groceries,data
|
||||
bondens marknad,phrase,groceries,data
|
||||
bröd,token,groceries,data
|
||||
bubbel,token,groceries,data
|
||||
city gross,phrase,groceries,curated
|
||||
coop,token,groceries,data
|
||||
frukt,token,groceries,curated
|
||||
grönsaker,token,groceries,data
|
||||
handling,prefix,groceries,data
|
||||
hemköp,token,groceries,data
|
||||
ica,token,groceries,data
|
||||
julmat,token,groceries,data
|
||||
lidl,token,groceries,curated
|
||||
livs,token,groceries,data
|
||||
livsmedel,prefix,groceries,curated
|
||||
mat,token,groceries,data
|
||||
mjölk,token,groceries,data
|
||||
netto,token,groceries,data
|
||||
ost,token,groceries,data
|
||||
snacks,token,groceries,data
|
||||
storhandling,token,groceries,data
|
||||
systembolag,prefix,groceries,data
|
||||
systemet,token,groceries,data
|
||||
willys,token,groceries,data
|
||||
apotek,prefix,health,data
|
||||
kry,token,health,curated
|
||||
läkare,token,health,curated
|
||||
medicin,prefix,health,curated
|
||||
sjukhus,token,health,curated
|
||||
tandläkare,token,health,curated
|
||||
tandvård,token,health,curated
|
||||
vårdcentral,token,health,curated
|
||||
bauhaus,token,home,data
|
||||
byggmax,token,home,curated
|
||||
clas ohlson,phrase,home,data
|
||||
elgiganten,token,home,curated
|
||||
färg,token,home,data
|
||||
garderob,prefix,home,data
|
||||
gardin,prefix,home,data
|
||||
hylla,token,home,data
|
||||
ikea,token,home,data
|
||||
jysk,token,home,curated
|
||||
lamp,prefix,home,data
|
||||
möbler,token,home,curated
|
||||
ohlson,token,home,data
|
||||
rusta,token,home,data
|
||||
amortering,prefix,housing,curated
|
||||
avgift,token,housing,data
|
||||
bolån,token,housing,curated
|
||||
brf,token,housing,data
|
||||
hyra,token,housing,data
|
||||
lgh,token,housing,data
|
||||
ränta,token,housing,data
|
||||
barnförsäkring,token,insurance,curated
|
||||
bilförsäkring,token,insurance,curated
|
||||
folksam,token,insurance,curated
|
||||
försäkring,prefix,insurance,data
|
||||
hemförsäkring,token,insurance,data
|
||||
länsförsäkringar,token,insurance,curated
|
||||
trygg hansa,phrase,insurance,curated
|
||||
bagel,prefix,restaurants,data
|
||||
bageri,prefix,restaurants,data
|
||||
bar,token,restaurants,curated
|
||||
burger king,phrase,restaurants,curated
|
||||
cafe,token,restaurants,curated
|
||||
café,token,restaurants,data
|
||||
dumplings,token,restaurants,data
|
||||
fika,prefix,restaurants,data
|
||||
foodora,token,restaurants,curated
|
||||
glass,token,restaurants,data
|
||||
hamburgare,token,restaurants,curated
|
||||
kaffe,token,restaurants,data
|
||||
kebab,token,restaurants,data
|
||||
lunch,token,restaurants,data
|
||||
max,token,restaurants,data
|
||||
mcdonalds,token,restaurants,curated
|
||||
middag,token,restaurants,data
|
||||
pizz,prefix,restaurants,data
|
||||
ramen,token,restaurants,data
|
||||
restaurang,prefix,restaurants,curated
|
||||
seml,prefix,restaurants,data
|
||||
streetfood,token,restaurants,data
|
||||
sushi,token,restaurants,data
|
||||
thai,token,restaurants,data
|
||||
uber eats,phrase,restaurants,curated
|
||||
amazon,token,shopping,curated
|
||||
h&m,token,shopping,curated
|
||||
kappahl,token,shopping,curated
|
||||
lindex,token,shopping,curated
|
||||
present,prefix,shopping,data
|
||||
skor,token,shopping,curated
|
||||
stadium,token,shopping,curated
|
||||
zalando,token,shopping,curated
|
||||
bensin,token,transport,curated
|
||||
bil,token,transport,data
|
||||
buss,prefix,transport,curated
|
||||
flygbuss,token,transport,curated
|
||||
färj,prefix,transport,data
|
||||
laddning,prefix,transport,data
|
||||
okq8,token,transport,curated
|
||||
parkering,prefix,transport,data
|
||||
preem,token,transport,curated
|
||||
sj,token,transport,curated
|
||||
sl,token,transport,curated
|
||||
tanka,token,transport,curated
|
||||
taxi,token,transport,data
|
||||
trängselskatt,token,transport,curated
|
||||
tåg,token,transport,data
|
||||
uber,token,transport,curated
|
||||
airbnb,token,travel,data
|
||||
boende,token,travel,data
|
||||
booking,token,travel,curated
|
||||
flyg,token,travel,curated
|
||||
hotel,token,travel,data
|
||||
hotell,token,travel,data
|
||||
sas,token,travel,curated
|
||||
bredband,token,utilities,curated
|
||||
elräkning,token,utilities,curated
|
||||
internet,token,utilities,data
|
||||
städning,prefix,utilities,curated
|
||||
tele2,token,utilities,curated
|
||||
telenor,token,utilities,curated
|
||||
telia,token,utilities,curated
|
||||
vattenfall,token,utilities,curated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"status": "ok",
|
||||
"server_version": "0.1.0",
|
||||
"schema_version": 1,
|
||||
"schema_version": 2,
|
||||
"db_ok": true
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
-- Kvitt server schema v2: four new default categories (housing, insurance, children, fees). Do not edit; add a new
|
||||
-- migration and bump the schema version (SPEC §14.8). Applied on top of server-v1.sql (0001_init.sql).
|
||||
--
|
||||
-- The default_key CHECK of categories cannot be altered in place in SQLite, so the table is rebuilt: copy the rows
|
||||
-- aside, drop the table, recreate it with the new CHECK and copy the rows back, ids and server_version unchanged.
|
||||
-- The migration runs inside one transaction (sqlx wraps every migration in one) with foreign_keys = ON (SPEC §8.9).
|
||||
-- defer_foreign_keys = ON lets expenses and recurring_templates point at categories while the table is briefly
|
||||
-- absent; the deferred check at commit still fails the migration if any reference is left dangling. It resets
|
||||
-- itself when the transaction ends. No other table, index or trigger refers to categories by anything but name.
|
||||
PRAGMA defer_foreign_keys = ON;
|
||||
|
||||
CREATE TABLE categories_v1_copy AS SELECT * FROM categories;
|
||||
DROP TABLE categories;
|
||||
|
||||
CREATE TABLE categories (
|
||||
id TEXT PRIMARY KEY NOT NULL,
|
||||
default_key TEXT UNIQUE CHECK (default_key IS NULL OR default_key IN
|
||||
('groceries', 'restaurants', 'home', 'utilities', 'transport',
|
||||
'travel', 'entertainment', 'health', 'shopping', 'other',
|
||||
'housing', 'insurance', 'children', 'fees')),
|
||||
name TEXT CHECK (name IS NULL OR length(name) BETWEEN 1 AND 40),
|
||||
icon TEXT NOT NULL,
|
||||
color TEXT NOT NULL,
|
||||
sort_order INTEGER NOT NULL,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL,
|
||||
archived_at TEXT,
|
||||
deleted_at TEXT,
|
||||
server_version INTEGER NOT NULL UNIQUE,
|
||||
CHECK (name IS NOT NULL OR default_key IS NOT NULL)
|
||||
) STRICT;
|
||||
|
||||
INSERT INTO categories (id, default_key, name, icon, color, sort_order, created_at, updated_at, archived_at, deleted_at, server_version)
|
||||
SELECT id, default_key, name, icon, color, sort_order, created_at, updated_at, archived_at, deleted_at, server_version
|
||||
FROM categories_v1_copy;
|
||||
DROP TABLE categories_v1_copy;
|
||||
|
||||
-- Each new row takes the next server_version from the global sequence, as every server write does (SPEC §14.1), so
|
||||
-- phones receive the rows on their next pull whatever their cursor.
|
||||
UPDATE sync_sequence SET value = value + 1 WHERE id = 1;
|
||||
INSERT INTO categories (id, default_key, name, icon, color, sort_order, created_at, updated_at, archived_at, deleted_at, server_version)
|
||||
SELECT '01920000-0000-7000-8000-000000000011', 'housing', NULL, 'key', 'indigo', 25,
|
||||
'2026-10-18T00:00:00.000Z', '2026-10-18T00:00:00.000Z', NULL, NULL, value
|
||||
FROM sync_sequence WHERE id = 1;
|
||||
|
||||
UPDATE sync_sequence SET value = value + 1 WHERE id = 1;
|
||||
INSERT INTO categories (id, default_key, name, icon, color, sort_order, created_at, updated_at, archived_at, deleted_at, server_version)
|
||||
SELECT '01920000-0000-7000-8000-000000000012', 'insurance', NULL, 'shield', 'slate', 45,
|
||||
'2026-10-18T00:00:00.000Z', '2026-10-18T00:00:00.000Z', NULL, NULL, value
|
||||
FROM sync_sequence WHERE id = 1;
|
||||
|
||||
UPDATE sync_sequence SET value = value + 1 WHERE id = 1;
|
||||
INSERT INTO categories (id, default_key, name, icon, color, sort_order, created_at, updated_at, archived_at, deleted_at, server_version)
|
||||
SELECT '01920000-0000-7000-8000-000000000013', 'children', NULL, 'child_care', 'lime', 85,
|
||||
'2026-10-18T00:00:00.000Z', '2026-10-18T00:00:00.000Z', NULL, NULL, value
|
||||
FROM sync_sequence WHERE id = 1;
|
||||
|
||||
UPDATE sync_sequence SET value = value + 1 WHERE id = 1;
|
||||
INSERT INTO categories (id, default_key, name, icon, color, sort_order, created_at, updated_at, archived_at, deleted_at, server_version)
|
||||
SELECT '01920000-0000-7000-8000-000000000014', 'fees', NULL, 'account_balance', 'amber', 95,
|
||||
'2026-10-18T00:00:00.000Z', '2026-10-18T00:00:00.000Z', NULL, NULL, value
|
||||
FROM sync_sequence WHERE id = 1;
|
||||
|
||||
UPDATE meta SET value = '2' WHERE key = 'schema_version';
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user