Skip to content

feat: total subscriptions in a home currency at ECB rates and suggest known services - #40

Merged
AmrMsCLL merged 1 commit into
mainfrom
feat/exchange-rates
Oct 5, 2026
Merged

AmrMsCLL merged 1 commit into
mainfrom
feat/exchange-rates

Conversation

@AmrMsCLL

@AmrMsCLL AmrMsCLL commented Oct 5, 2026

Copy link
Copy Markdown
Member

What changed

The two Recurring decisions from .claude/.Plans/as2026-10-02-strata-phase-6-recurring-design.md, which Amr approved. Stacked on #39. No new dependency.

  • ECB exchange rates:
    • a maintenance step fetches the ECB daily reference rates (eurofxref-daily.xml) when the stored ones are over 12 hours old, and keeps them in exchange_rates;
    • if the ECB can't be reached, it logs a warning and tries again on the next 5-minute run;
    • GET /exchange-rates lists the date and the currencies.
  • Home currency:
    • users.home_currency is new, set from Settings → Profile, with "None, keep each currency separate" as the default;
    • with a home currency, GET /subscriptions/summary adds home with the converted monthly and yearly totals, the rates' date, and any currencies with no rate, which are left out rather than guessed;
    • conversion goes through the euro and respects each currency's decimal places (JPY has none);
    • Recurring shows "All together, in EUR, ≈ €20.00 a month" above the per-currency cards, when you pay in more than one currency.
  • Known services:
    • the subscription name field suggests 27 common services;
    • when adding a subscription, choosing one fills in its category and cancel link if those are still empty;
    • each cancel link was checked to respond. Links that answered 404, were blocked by bot protection (GitHub, Xbox, Paramount+, Canva, Claude, Tidal, Nord) or pointed only at a home page (Notion, ChatGPT, Slack) were left out;
    • there are no typical prices, because they vary by country and would mislead.

Migration (runs on deploy)

20261005000000_exchange_rates:

ALTER TABLE "users" ADD COLUMN "home_currency" TEXT;
CREATE TABLE "exchange_rates" ("currency" TEXT PRIMARY KEY, "per_euro" DOUBLE PRECISION NOT NULL, "published_on" DATE NOT NULL, "updated_at" TIMESTAMP(3) NOT NULL);

It only adds a nullable column and a new table.

Verification

  • Lint, the server and frontend builds, and tsc --noEmit pass.

  • The new test/exchange-rates.spec.ts covers parsing the ECB file, refusing a page with no rates, and converting EUR, USD, GBP and JPY, with a missing currency giving null.

  • The new test/integration/exchange-rates.spec.ts stubs the ECB. It covers:

    • one fetch, no repeat within 12 hours, and a fetch again after;
    • the currency list;
    • no home total until one is chosen;
    • a lowercase code refused;
    • the EUR total from USD and GBP subscriptions, with XAU reported as missing;
    • clearing the setting.

    It and subscriptions.spec.ts pass against Postgres on the local stack.

  • test/subscriptions.spec.tsx adds cases for typing "netflix", which fills the category and cancel link, and for the combined total. settings.spec.tsx passes.

  • Running the integration spec locally replaces the dev database's rates with the stub rates until the next refresh, 12 hours later. CI's database is thrown away after each run.

@github-actions github-actions Bot added the size/l 201-600 changed lines, excluding lockfiles label Oct 5, 2026
@AmrMsCLL
AmrMsCLL force-pushed the feat/weather-news-widgets branch from b4dfe42 to 46910c1 Compare October 5, 2026 19:02
Base automatically changed from feat/weather-news-widgets to main October 5, 2026 19:03
@AmrMsCLL
AmrMsCLL force-pushed the feat/exchange-rates branch from d7c5ae4 to 8459ce4 Compare October 5, 2026 19:03
@AmrMsCLL
AmrMsCLL merged commit 085538b into main Oct 5, 2026
5 checks passed
@AmrMsCLL
AmrMsCLL deleted the feat/exchange-rates branch October 5, 2026 19:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/l 201-600 changed lines, excluding lockfiles

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant