Multi-currency: currency rules, aliases, precision, and internal FX service - #54
Merged
Conversation
ItsThompson
force-pushed
the
mc/02-currency-rules-fx-service
branch
7 times, most recently
from
August 18, 2026 22:38
8ae5465 to
ce43c39
Compare
Base automatically changed from
mc/01-currency-catalog-and-period-currency
to
main
August 18, 2026 23:45
ItsThompson
force-pushed
the
mc/02-currency-rules-fx-service
branch
2 times, most recently
from
August 18, 2026 23:51
e89ede9 to
d8e43fa
Compare
ItsThompson
commented
Aug 19, 2026
- add read-only GetPeriodContext gRPC contract for period lookup - return reporting currency and lock state from Finance service - cover success and not-found status mapping in gRPC tests
- configure Finance period-context client before public expense writes - accept transactionCurrency and rollout currency aliases - add missing-period and alias compatibility coverage
- add transactionCurrency to CreateProRataRequest and proto, relax currency binding - resolve alias in service: transactionCurrency canonical, currency as legacy fallback - send only transactionCurrency from internal expense client to avoid telemetry pollution - add service and REST handler tests for transactionCurrency-only pro-rata
…month - add handler test verifying codes.NotFound and year/month in message - covers should-fix 4 from review: gRPC field detail for missing-period path
…ution - reject both-different with CURRENCY_FIELD_CONFLICT, no schedule or expense written - accept both-equal, canonicalize to transactionCurrency - add ErrCurrencyConflict to finance model errors - add service tests for both-equal and both-different cases
- move fetchPeriodContext into usePeriodContext with a discriminated union - replace loading/missing/error/active flags with one status to drop impossible states - add usePeriodContext tests for active, missing, error, and refetch
- extract loading, missing/error, and form JSX into components/ - slim NewExpenseFeature to an orchestrator that routes on period status - add feature test for the unexpected period load error
ItsThompson
force-pushed
the
mc/02-currency-rules-fx-service
branch
from
August 19, 2026 06:30
6e462ac to
c594e61
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements currency rules, aliases, precision helpers, and the internal FX service. Makes period reporting currency immutable after creation. Gates expense writes behind budget period context. Adds rollout-safe
transactionCurrency/currencyalias resolution. Replaces fixed two-decimal frontend money behavior with catalog-driven precision. Introduces the FX Service as a new internal-only Go module with gRPC conversion contracts, Open Exchange Rates provider, one-hour caching, and decimal math.Base branch:
mc/01-currency-catalog-and-period-currencyWhat changed
Backend: Finance Service
GetPeriodContextgRPC method: returns period ID, user ID, year, month, reportingCurrency, and lock state.REPORTING_CURRENCY_IMMUTABLEfor raw JSONreportingCurrencyorreporting_currencyfields.CreatePeriodWithProRatauses the current default settings currency for each missed period.transactionCurrencywith legacycurrencyalias resolution and conflict detection.Backend: Expense Service
FINANCE_SERVICE_ADDR. CallsGetPeriodContextbefore create-expense writes.PERIOD_NOT_FOUNDwith year and month in the response. Blocks repository writes.transactionCurrencyis the canonical field. Legacycurrencyaccepted as alias withlegacy_currency_alias_usedtelemetry.transactionCurrency. Both fields different: returnsCURRENCY_FIELD_CONFLICT.reportingCurrency. Unsupported currency returnsUNSUPPORTED_CURRENCY.Backend: FX Service (new module
services/fx)FxServicewith three RPCs:CaptureRateSnapshot,ConvertAmount,ConvertWithSnapshot.1without calling the provider.math/big.Ratand half-away-from-zero rounding./api/fxwith 403.Frontend
frontend/packages/core/src/currency.ts: catalog-backedparseInput,formatAmount,getMinorUnitDigits,validateInputPrecision.transactionCurrency. Legacycurrencyfield removed from new submissions.CreatePeriodPromptshows immutable reporting-currency warning.DefaultBudgetSectionshows future-scoped default currency warning.Deployment
docker-compose.ymlanddocker-compose.dev.yml: addedfx-serviceon compute and monitoring networks.monitoring/prometheus/prometheus.yml: added FX scrape target.justfile: added FX to backend tests and proto generation.Tests
Backend
services/fx:go test ./...passes. Converter tests cover USD identity, EUR to USD, USD to JPY, EUR to GBP, rounding halves, unsupported currencies, missing rates, cache hit, cache miss, provider failure with fresh cache, provider failure without cache, and captured snapshot conversion.services/finance:go test ./...passes. Covers period-context gRPC, immutable update rejection, default-currency validation, missed-period currency, and pro-rata alias resolution.services/expense:go test ./...passes. Covers missing-period gate, all alias compatibility rows (canonical only, legacy only, both equal, both different, neither, unsupported).services/accessandservices/gateway: route-guard tests confirm no/api/fxbrowser route or proxy prefix.Frontend
@gofin/core: 4 test files, 78 tests pass. Covers parse, format, precision validation for USD, EUR, JPY, negative values, unsupported currencies.@gofin/api: 11 test files, 179 tests pass.@gofin/finance: 50 test files, 479 tests pass. Covers precision-aware forms, new-expense, pro-rata, correction, settings, and budget editor.tsc --noEmitandeslint.Notes
just test-backendfails in the shared worktree due to unrelated uncommitted Expense/Finance changes referencing stale generated protobuf types (financepb.GetPeriodContext). Per-serviceGOWORK=off go test ./...passes for each individual service.fx-servicerequires Compose buildx 0.17.0+. Local environment has an older version.currencyresponse field mirrorstransactionCurrencyduring rollout for old-client compatibility.References
Tickets:
Changesets:
/Users/thompsontong/Desktop/multi-currency-support-Spec/changesets/changeset-2.md/Users/thompsontong/Desktop/multi-currency-support-Spec/changesets/changeset-3.md/Users/thompsontong/Desktop/multi-currency-support-Spec/changesets/changeset-5.md/Users/thompsontong/Desktop/multi-currency-support-Spec/changesets/changeset-6.md