Task: Migrate fx_convention consumers to currency_pair and retire it
This page documents a task in the Currency pair support in reference data story. It captures the goal, current status, acceptance, and any notes or results.
Goal
Point every real consumer of ores.refdata.fx_convention — ores.ore's
conventions_mapper::map_fx=/=reverse_fx, the Qt CRUD screens
(FxConventionController=/=DetailDialog=/=MdiWindow=/etc.), and the CLI
registration in =ores.cli/src/app/application.cpp — at currency_pair
instead, per the field mapping settled in
Reconcile currency_pair design with fx_convention. Then delete
fx_convention's SQL table/triggers, repository, service, messaging,
Qt files, and generator — it has no other reason to exist once nothing
references it.
Depends on the currency_pair ores.codegen.entity implementation
existing first (not yet a task in this story — needs scaffolding).
Status
| Field | Value |
|---|---|
| State | DONE |
| Parent story | Currency pair support in reference data |
| Now | Nothing. |
| Waiting on | Nothing. |
| Next | Nothing. |
| Last touched | 2026-07-07 |
Acceptance
[X]ores.ore'sconventions_mapperreads/writescurrency_pair(via itspoints_factor()helper for the divisor form) instead offx_convention; the exported/imported ORE XML is unchanged byte-for-byte for the fields that carry over.[X]fx_convention's Qt CRUD screens and CLI registration are removed, not just left dangling; any equivalent CRUD need is served bycurrency_pair's own generated Qt/CLI surface.[X]fx_convention's SQL table, triggers, repository, service, and messaging are deleted from the codebase, not merely deprecated.[X]No dangling references tofx_conventionremain (grep clean).
Plan
- Rewrote
conventions_mapper::map_fx=/=reverse_fx(ores.ore.core) to mapfxTypeto/from a newmapped_fxtransport struct pairingcurrency_pair+currency_pair_convention.SpotDaysis carried onmapped_fxitself (not persisted on either domain type) because theore roundtripimport/export path has no database access and must still round-trip the value byte-for-byte — see the currency-derived spot-lag note below for why it can't be looked up fromcurrencyhere. Verified byte-for-byte roundtrip againstexternal/ore/examples/Input/conventions.xml's<FX>elements. - Repointed
ores.cli'simport_conventionsatcurrency_pair_repository=/=currency_pair_convention_repositoryinstead offx_convention_repository. - Repointed the Qt "ORE Conventions" menu
(
RefdataPlugin) atCurrencyPairController=/=CurrencyPairConventionControllerinstead ofFxConventionController— these controllers existed (generated) but were never wired into the plugin; wiring them in is what makes the "served by currency_pair's own Qt surface" acceptance criterion literally true, not just aspirational. - Deleted all
fx_conventionfiles: Qt controller/dialogs/windows/ui, SQL create/drop scripts, and theores.refdataapi/core domain, repository, service, messaging, and generator files. Removed its wiring from the hand-maintained NATSregistrar.cpp. - Discovered along the way that
currency_pair's own SQL was never wired into the master =refdata_create.sql=/=refdata_drop.sql=/RLS policy scripts (generated in isolation by a prior task) — the tables didn't exist in the DB at all. Wired them in (create/drop order: classifications and groups before pairs, pairs before conventions; reverse on drop) since the migration is meaningless without them. - Discovered
currency.spot_days(added to the model in an earlier commit but never regenerated) is needed to derivecurrency_pair.spot_daysat read time, per the design in Reconcile currency_pair design with fx_convention. Regenerated onlycurrency's domain/repository/generator/SQL-create facets (skipped Qt/CLI to avoid clashing with the concurrent "Commission: currency" story). A full SQL regen pulled in unrelated template drift (case normalisation, and a dropped tenant-fallback validation branch) so the SQL column additions were hand-applied instead of using the generated file wholesale. - Fixed an unrelated pre-existing build break in
CurrencyDetailDialog.cpp(call to a generator function that no longer exists) as a one-line drive-by, since it blocked verifying the Qt wiring changes compiled. - Retired the
ores.refdata.fx_conventioncodegen model doc (no incoming links) and removed it fromores.refdata.module.org's entity table, adding thecurrency_pairfamily in its place.
Notes
- Not done: the
ores.refdata.api/core/qtPlantUML class diagrams (*.puml) still mentionfx_convention/FxConvention— these are hand-authored/synced diagram sources, not generated from the code touched here, and updating them is a separate follow-up. - Done:
ores.refdata.service's NATS messaging registrar forcurrency_pair,currency_pair_convention, andcurrency_pair_classification— thenats-sub-registrarfacet was written (mustache templates + two DONE tasks in theintroduce-per-entity-sub-registrarsstory) but never actually added tofacet_catalogue.org, so codegen couldn't produce these files and the three entities had handlers with no service wiring (NATS callers got "no service is handling subject"). Added the missing facet entry, tangled, generated the three registrar pairs viaores.cpp.nats-sub-registrar, and wired them intoregistrar.cpp. Verified live: acurrency_pairs.listNATS request now returnsunauthorized(routed and handled) instead of "no service is handling subject". - Also moved the Qt
Currency &Pairsmenu action fromReference Data → Conventions(where it sat alongside actual curve-building conventions) to top level, next toCurrencies— it is core reference data, not a convention.
PRs
| PR | Title |
|---|---|
| #1455 | [refdata,ore,qt,cli] Migrate fx_convention consumers to currency_pair and retire it |
Review
| # | Comment summary | File | Decision | Notes |
|---|---|---|---|---|
| 1 | Corrupted synthetic iso_code prefix (+"-" instead of "X") |
currency_generator.cpp | Fixed | c3e7cdf2c — added explicit primary-key generator block to the model |
| 2 | tick_size set to pip_factor (absolute rate move) instead of a pip count |
conventions_mapper.cpp | Fixed | c3e7cdf2c — defaults to 1.0 |
| 3 | Dangling [[id:...]][fx_convention]] doc links after deleting its model doc |
business_day_convention_type.hpp/.org, refdata_business_day_convention_types_create.sql | Fixed | c3e7cdf2c — repointed at currency_pair_convention |
| 4 | Stray footnote-like artefact ("USD1") in a doc comment | currency.hpp (generated) | Fixed | c3e7cdf2c — reworded model doc to avoid org verbatim stripping |
| 5 | No unit test for map_fx=/=reverse_fx round-trip |
conventions_mapper.cpp | Declined (tracked) | Already covered by BACKLOG task_test-ore-fx-convention-import.org |
| 6 | pip_factor reciprocal is a float round-trip, may drift on export |
conventions_mapper.cpp | Declined (tracked) | Same BACKLOG task as above |
| 7 | Corrupted synthetic code prefix (+"-") recurs in two new generators; same class as #1, root cause in the template |
currency_group_generator.cpp, currency_pair_classification_generator.cpp | Fixed | 07a76974 — added a #generator_expr=/=^generator_expr guard (faker-noun fallback) to the generator_impl template, matching the existing natural_keys pattern, instead of patching each entity's model |
Result
fx_convention is fully retired: ores.ore, the Qt refdata plugin, and
the CLI now read/write currency_pair=/=currency_pair_convention
directly, with the divisor/multiplier pip_factor reciprocal computed
inline rather than stored twice. Along the way, closed two gaps left by
earlier tasks in this story — currency_pair's SQL was never wired into
the master schema scripts, and currency.spot_days was modelled but
never generated — both required for this migration to actually work
end-to-end. Verified via a live DB recreate (tables present, fx tables
gone) and a byte-for-byte ore roundtrip CLI run against a real ORE
conventions.xml.