Task: Reconcile currency's flag image handling and wire the piloted combo-field mechanism

Table of Contents

This page documents a task in the Commission: currency story. It captures the goal, current status, acceptance, and any notes or results.

Goal

Three reconciliation items grouped together (steps 6-8 of the regenerate plan, task 6E0CD16E), none requiring new template mechanisms: (1) migrate currency's flag image handling onto the template's existing has_flag_icon mechanism; (2) wire currency's 3 soft-FK combo fields to the already-piloted populateDynamicCombo<Entity> helper via the small per-field codegen emit; (3) decide and land the fetch-failure-signalling gap flagged during the combo-field pilot (task 8AA363F8) before finalizing the emit.

Status

Field Value
State DONE
Parent story Commission: currency
Now Nothing.
Waiting on Nothing.
Next Nothing.
Last touched 2026-07-08

Acceptance

  • Currency's flag image handling calls the template's has_flag_icon mechanism instead of hand-rolled code.
  • Currency's 3 combo fields are wired through the per-field codegen emit calling populateDynamicCombo<Entity>.
  • Fetch-failure signalling has an explicit, documented decision (surfaced error vs. silent empty combo) reflected in the emit.
  • Build passes.

Plan

Unlike the 5 prior capability-landing tasks in this story, this one was scoped as a genuine call-site migration of currency's real files — not a land-template-then-verify-by-diff-then-discard pass. Two independent pieces of work:

1. Flag image handling migration

DetailDialogBase already has a complete, mature flag mechanism (initFlagButton, entityImageId()=/=keyFlagField()=/=keyFlagIcon() virtual accessors, onSelectFlagClicked, updateFlagDisplay, flagChanged()=/=selectedImageId()=/=resetFlagChanged()) — confirmed by CountryDetailDialog already using it end to end. Currency's ~180 lines of hand-rolled equivalent (flagButton_, pendingImageId_, flagChanged_, onCurrencyImageSet, manual updateFlagDisplay) were 100% redundant duplication, migrated to match Country's pattern exactly:

  • initFlagButton(ui_->iconGroup->layout()) replaces the hand-built flag button/container.
  • entityImageId()=/=keyFlagField()=/=keyFlagIcon() overrides added (matching Country's).
  • getCurrency() now uses flagChanged()=/=selectedImageId() instead of the local pendingImageId_ staging.
  • Dropped currency's extra onCurrencyImageSet NATS-confirmation toast handling — Country doesn't have this either; the generic imagesLoaded=/=allLoadedupdateFlagDisplay wiring (already in base's setImageCache) covers the refresh.
  • Also dropped the "no-flag placeholder ID" auto-seeding quirk in the old setCurrency — unique to currency, inconsistent with how every other flag-icon entity handles a null image_id.

2. Combo-field codegen wiring

Landed a new combo_domain_type-gated branch in the detail_dialog template (cpp_qt_detail_dialog.cpp.mustache) generating a populateDynamicCombo<Entity> call from per-field org properties (combo_domain_type, combo_fetch_fn, combo_watcher_name, combo_code_field, combo_tooltip_field, combo_sort_field, combo_label, combo_setter_pascal) — distinct from the pre-existing (unused) combo_helper=/=combo_fetch_fn branches, which don't match the piloted helper's real 9-argument signature. Added currency's 3 combo fields (rounding_type, monetary_nature, market_tier) to a new "Detail fields" org table section (only these 3 — the other 11 currency fields stay unmodelled until the final sync task populates the full table).

Process note: initially hand-typed the on_error callback addition directly into CurrencyDetailDialog.cpp before adding the template capability — caught mid-edit (thanks to a review prompt) and reverted. Correct order followed after: land the template capability first, regenerate + diff to verify the generated populate*Combo() bodies match currency's real code, then copy the verified generated snippet in (adjusting only the real file's currentCurrency_ member name vs. the model's generic item_var naming — a pre-existing, unrelated drift).

3. Fetch-failure-signalling decision

The pilot (task 8AA363F8) flagged that populateDynamicCombo's bare std::vector<Entity> fetch signature can't distinguish "fetch failed" from "legitimately empty" — both render a silently-empty combo. Decision: land the fix, not just document a deferral. Changed populateDynamicCombo's fetch parameter to std::function<std::expected<std::vector<Entity>, QString>(ClientManager*)> and added an on_error callback (default no-op) plus a distinct "Failed to load" combo placeholder. Updated the 3 LookupFetcher functions currency uses (confirmed sole callers) to return std::expected, propagating process_authenticated_request's existing error channel instead of silently swallowing it. Currency's 3 combo call sites (both hand and generated) now pass an on_error callback that emits errorMessage().

Scoped to just the 3 functions currency uses — did not touch the rest of LookupFetcher's many other bare-vector fetchers (out of scope, no other entity affected).

Notes

Verified with a real regenerate + rebuild of ores.qt.refdata.lib (not a diff-only check): CurrencyDetailDialog.cpp compiles clean with the migrated flag handling and the generated combo methods. Also regenerated Country (no dynamic-combo fields) to confirm the new code() accessor generalises cleanly with zero combo_domain_type leakage into peers without it. All regenerated trees discarded after verification per this branch's actual deliverable (the hand-migrated CurrencyDetailDialog.{hpp,cpp}, the template, and the DynamicComboSetup=/=LookupFetcher API changes).

PRs

PR Title
#1468 [codegen,qt] Reconcile currency flag/combo handling

Review

# Comment summary File Decision Notes
1 Debug log lines use human-readable field names ("rounding type") but the codegen template emits snake_case ("rounding_type") — checked-in code doesn't byte-match what regeneration produces CurrencyDetailDialog.cpp Fixed Fixed in 48b1b0b0f. Three independent review passes found no other issues.

Result

All three items landed. Currency's flag handling now calls DetailDialogBase's generic mechanism (real code change, ~180 lines of duplication removed). The 3 soft-FK combo fields are wired through a new combo_domain_type codegen emit calling populateDynamicCombo<Entity>, verified equivalent to currency's hand-migrated code via regenerate+diff, then applied for real. The fetch-failure-signalling gap is closed with a real std::expected propagation + on_error callback, not just documented. Full whole-app build (cmake --build --preset linux-clang-debug-make) passes clean. This closes the last of the 6 prerequisites for "Sync Qt codegen for currency" (EA647CBC) — that task is now unblocked.

Emacs 29.3 (Org mode 9.6.15)