Skip to content

Multi-currency: pro-rata future application and legacy failures - #58

Open
ItsThompson wants to merge 6 commits into
mc/05-corrections-export-prorata-capturefrom
mc/06-prorata-future-application
Open

Multi-currency: pro-rata future application and legacy failures#58
ItsThompson wants to merge 6 commits into
mc/05-corrections-export-prorata-capturefrom
mc/06-prorata-future-application

Conversation

@ItsThompson

@ItsThompson ItsThompson commented Aug 15, 2026

Copy link
Copy Markdown
Owner

Summary

Reworked the Finance pro-rata future-application flow so pending installments are applied only after their target budget period exists. Reporting amounts derive from the captured schedule snapshot. No live provider rates are called. Legacy schedules without captured snapshots are handled without invented rates. Deterministic failures move a schedule to failed with a typed reason. Transient failures leave the schedule pending for retry. Finance marks a row applied only after the Expense ledger write succeeds.

What changed

Backend: Finance service

  • Rewrote applyPendingProRata to accept the target period, resolve reporting currency locally, and pass trusted period context plus captured snapshot to Expense. Each schedule is applied independently so one failure does not block the rest.
  • Added applyOneProRataSchedule (captured-snapshot path) and applyLegacyProRataSchedule (legacy migration path).
  • Added MarkProRataFailed repository method and SQL query to move schedules to failed with a typed failure_reason.
  • Added classifyProRataExpenseError to distinguish deterministic failures (snapshot_currency_missing) from transient errors.
  • Added ErrSnapshotCurrencyMissing and ErrMissingCapturedRateSnapshot error codes.
  • Added LegacyMigration flag to CreateProRataInstallmentInput and forwarded it over gRPC.
  • Updated CreatePeriodWithProRata to pass the created period object to applyPendingProRata.
  • Updated all existing CreatePeriodWithProRata tests to the new CreateProRataInstallment contract.

Backend: Expense service

  • Added legacy_migration field to CreateProRataInstallmentRequest proto and regenerated bindings.
  • Added legacy-migration path in CreateProRataInstallment: writes a migration snapshot (exchangeRate = "1", exchangeRateSource = "migration") without calling FX.
  • Forwarded LegacyMigration flag from gRPC handler to service layer.

Tests

Finance service (GOWORK=off go test ./... -count=1): all packages pass. New test coverage:

  • Captured snapshot with same target currency applies with snapshot.
  • Captured snapshot with different target currency applies in target currency.
  • Captured snapshot missing target currency marks schedule failed.
  • Transient Expense write failure leaves schedule pending.
  • Legacy same-currency schedule applies with migration snapshot.
  • Legacy different-currency schedule marks failed.
  • No target period leaves legacy schedule pending.
  • Failed schedule is not re-applied.
  • Dashboard totals exclude pending and failed schedules.
  • Expense snapshot_currency_missing failure classified as deterministic.
  • classifyProRataExpenseError table test covering in-process and gRPC error paths.

Expense service (GOWORK=off go test ./... -count=1): all packages pass. Legacy migration and defense-in-depth validation tests added.

go vet clean for both services. Workspace build clean.

Notes

  • The new MarkProRataFailed SQL query is hand-added to the sqlc-generated finance.sql.go (sqlc is not installed). It mirrors the existing MarkProRataApplied pattern. A future sqlc generate run will regenerate it identically.
  • No automatic retry exists for schedules left pending by a transient failure. Application is only triggered during period creation. A manual retry endpoint is out of scope for this ticket.

References

  • Ticket 14: Pro-rata future application and legacy failures
  • Changeset: /Users/thompsontong/Desktop/multi-currency-support-Spec/changesets/changeset-14.md

@ItsThompson ItsThompson changed the title mc/06 prorata future application Multi-currency: pro-rata future application and legacy failures Aug 15, 2026
@ItsThompson
ItsThompson force-pushed the mc/06-prorata-future-application branch 2 times, most recently from 0f70f3c to 5ee4f2f Compare August 15, 2026 22:55
@ItsThompson
ItsThompson force-pushed the mc/06-prorata-future-application branch from 5ee4f2f to 32bdfdb Compare August 15, 2026 23:10
@ItsThompson
ItsThompson force-pushed the mc/06-prorata-future-application branch from 32bdfdb to 5ee38f9 Compare August 15, 2026 23:22
@ItsThompson
ItsThompson force-pushed the mc/06-prorata-future-application branch 2 times, most recently from c4c5c39 to 7d1d9a4 Compare August 15, 2026 23:53
@ItsThompson
ItsThompson force-pushed the mc/06-prorata-future-application branch from 7d1d9a4 to 572a432 Compare August 16, 2026 14:53
@ItsThompson
ItsThompson force-pushed the mc/06-prorata-future-application branch from 572a432 to 0748ac4 Compare August 16, 2026 15:17
@ItsThompson
ItsThompson force-pushed the mc/06-prorata-future-application branch 3 times, most recently from a065b29 to 8917be9 Compare August 18, 2026 23:36
@ItsThompson
ItsThompson force-pushed the mc/06-prorata-future-application branch 2 times, most recently from 37f48f5 to 4e5d83f Compare August 18, 2026 23:51
@ItsThompson
ItsThompson force-pushed the mc/06-prorata-future-application branch from 4e5d83f to b61f42c Compare August 19, 2026 06:30
@ItsThompson
ItsThompson force-pushed the mc/06-prorata-future-application branch from b61f42c to 570136e Compare August 19, 2026 19:05
@ItsThompson
ItsThompson force-pushed the mc/06-prorata-future-application branch from 570136e to ed0f649 Compare August 20, 2026 13:48
@ItsThompson
ItsThompson force-pushed the mc/06-prorata-future-application branch from ed0f649 to 3eee5d9 Compare August 20, 2026 14:58
@ItsThompson
ItsThompson force-pushed the mc/06-prorata-future-application branch from 3eee5d9 to 6c05e3c Compare August 20, 2026 15:10
@ItsThompson
ItsThompson force-pushed the mc/06-prorata-future-application branch 2 times, most recently from 956cbc6 to a813648 Compare August 20, 2026 17:59
@ItsThompson
ItsThompson force-pushed the mc/06-prorata-future-application branch from a813648 to ac432f7 Compare August 23, 2026 19:51
@ItsThompson
ItsThompson force-pushed the mc/06-prorata-future-application branch from ac432f7 to 6deadfb Compare August 23, 2026 20:38
…ror codes

Add the MarkProRataFailed query (status -> 'failed' with a typed
failure_reason) so the pro-rata application flow can move deterministic
schedule failures to failed without a ledger write. Add the
SNAPSHOT_CURRENCY_MISSING and MISSING_CAPTURED_RATE_SNAPSHOT finance error
codes that classify captured-snapshot and legacy schedule failures.
…snapshot

Add a legacy_migration flag to the CreateProRataInstallment contract so
Finance can apply legacy pending schedules whose target period reporting
currency equals the stored schedule currency. Expense writes a migration
snapshot (exchangeRate = "1", exchangeRateSource = "migration") without
calling FX, and re-validates the currency match so a caller error never
invents a conversion. Regenerate the expense proto.
…d snapshot

Rewrite applyPendingProRata to resolve the target reporting currency from
the just-created Finance period, pass trusted period context plus the
captured snapshot to Expense (no reentrant Finance call, no live provider
rate), and mark each schedule applied only after the ledger write
succeeds. Deterministic failures (missing snapshot currency, legacy
differing currency) move the row to failed with a typed reason; transient
Expense write/conversion failures leave the row pending so it can be
retried, with no partial expense row visible. Each schedule is applied
independently so one failure does not block the rest.
…ailures

Add finance service tests for captured-snapshot same/different target
currency, missing snapshot currency, expense write failure, legacy
same-currency migration application, legacy different-currency failure,
and the no-target-period-remains-pending invariant, plus a dashboard
excludes-pending-and-failed assertion. Add expense service tests for the
legacy migration write, currency-mismatch rejection, different-target
snapshot conversion, missing-target-currency rejection, and expense
write failure. Update existing CreatePeriodWithProRata tests to the new
CreateProRataInstallment contract and wire the MarkProRataFailed mock.
…stic

Expense-side defense-in-depth can surface a snapshot_currency_missing
failure that Finance's target-only pre-check does not cover (e.g. the
transaction currency is missing from the captured snapshot). Previously
any Expense error left the schedule pending, stranding it for a retry that
would fail identically. classifyProRataExpenseError now inspects the
returned error (in-process *apierr.Error or wrapped gRPC
codes.FailedPrecondition) and moves the row to failed with
snapshot_currency_missing when the failure is deterministic; transient
errors still leave the row pending. Also add a failed count to the
apply summary log for at-a-glance operator correlation.

Addresses review-14 should-fix S1.
- Use int64Ptr for the now-pointer BudgetAmount in the pro-rata test helper
- Read the migration drift-guard catalog from the shared currency package instead of the deleted catalog.json
@ItsThompson
ItsThompson force-pushed the mc/06-prorata-future-application branch from 6deadfb to e22a057 Compare August 24, 2026 06:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant