feat(currencies): ship sterling, and migrate it into existing datasets
GBP joins the currencies the app comes with: £, two decimals, and a committed fallback rate of 8.704735 DKK to the pound. build.ts now fetches it too, so a build with a network bakes a live GBP rate and one without still covers it — the coverage check derives from the seed table, so it demanded GBP as soon as the table listed it. The seed table alone would not have delivered this. Baked currencies and rates seed a NEW dataset and nothing else, so an existing dataset would have been left typing the code, name, symbol and decimals by hand. A 4 -> 5 migration adds sterling to a dataset that lacks it, with its rate. Supplying a rate is the one thing the "rates never backstop a cleared rate" rule permits here: it forbids re-supplying a number the owner deleted, not introducing one for a currency they have never had. A GBP the owner already defined, or a GBP rate they already hold, is left exactly as it is — asserted, including the case where their definition is "wrong" (zero decimals), because it is theirs. This exposed a real latent bug. The 2 -> 3 step seeded the live seed-currency table BY REFERENCE, so adding sterling there would have retroactively changed what an old dataset received from a step it had already been through — silently, and only for whoever had not migrated yet. Every step now carries its own frozen copy, and a source-level guard fails the build if a live default is imported again. Scoping that guard took two goes: matching the whole file flagged the comment explaining the rule, and then flagged the frozen local copy that is the fix. Two test fixtures used GBP as their example of an UNSUPPORTED currency and now use CHF, which no seed table lists. Verified in a browser by writing a schemaVersion 4 dataset into IndexedDB and reloading: the currency table gains "British pound / £ / 2" with "manual · 2026-09-02", the post currency selector offers GBP, and the income and post survive untouched. Note for the next person — a migration applies in memory on load and is written back on the next mutation, so reading IndexedDB straight after a reload still shows the OLD version. That is recorded in docs/TODO.md, because it made this verification look like a failure at first. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
89f2f4fd19
commit
f4223573e4
14 files changed
+216
-25
No files matched your search
@@ -88,8 +88,9 @@ describe("validation", () => {
|
||||
|
||||
test("rejects an unsupported currency", () => {
|
||||
const data = populated() as any;
|
||||
data.settings.baseCurrency = "GBP";
|
||||
expect(() => parseDatasetJson(JSON.stringify(data))).toThrow(/GBP/);
|
||||
// CHF, not GBP: GBP is one of the currencies the app ships with now.
|
||||
data.settings.baseCurrency = "CHF";
|
||||
expect(() => parseDatasetJson(JSON.stringify(data))).toThrow(/CHF/);
|
||||
});
|
||||
|
||||
test("rejects a malformed MonthId", () => {
|
||||
|
||||
Reference in new issue
Block a user