Task: Migrate ores.qt.party's history dialogs to the generic HistoryDialog
Table of Contents
This page documents a task in the Migrate ores.qt.refdata onto the generic HistoryDialog story. It captures the goal, current status, acceptance, and any notes or results.
Goal
Retire the 4 hand-rolled *HistoryDialog classes in
projects/ores.qt/party (BusinessUnitHistoryDialog,
BusinessUnitTypeHistoryDialog, PartyIdSchemeHistoryDialog,
PartyStatusHistoryDialog), wiring business_unit,
business_unit_type, party_id_scheme, and party_status onto the
generic HistoryDialog. Scope expanded substantially mid-task — see
Plan — into moving all 4 entities out of ores.qt.party (deleted)
into ores.qt.refdata, and then bringing all 4 entities onto full
codegen (domain/SQL/repository/service/protocol/generator/Qt/eventing),
including authoring a brand-new codegen model for business_unit_type
(which never had one) and a new codegen template paste-point.
Status
| Field | Value |
|---|---|
| State | DONE |
| Parent story | Migrate ores.qt.refdata onto the generic HistoryDialog |
| Now | Nothing. |
| Waiting on | Nothing. |
| Next | Nothing. |
| Last touched | 2026-07-17 |
Acceptance
- All 4 entities' history windows use the generic
HistoryDialog(no per-entity*HistoryDialogclass remains). ✅ ores.qt.partyplugin is deleted; its 4 entities live inores.qt.refdata=/=RefdataPlugininstead. ✅- All 4 entities have a complete codegen stack (domain/SQL/repository/service/protocol/generator/Qt/eventing), diffed layer-by-layer against the prior hand-written code. ✅
- Local build passes clean. ✅
- PR #1609 merged. ✅
Plan
Original scope (per the task title) was a frontend-only rewire, same shape as currency's PR #1553. Investigation and explicit direction mid-task expanded it in three stages:
Stage 1 — backend generic-history support + frontend rewire
No backend generic-history support existed for any of the 4 entities
(no entity_type_of, no *_history_field_mapper, no
register_history_provider registration in registrar.cpp) — unlike
currency, which already had it. Added these by hand initially, and
rewired all 4 controllers' showHistoryWindow=/=onOpenVersion=/
=onRevertVersion to the generic HistoryDialog, following
CurrencyController's pattern. Deleted the 4 *HistoryDialog
classes.
Stage 2 — ores.qt.party is not a real plugin; dissolve it
ores.qt.party's own doc comment says "contributes to the shared
Reference Data menu; no standalone menu"; all 4 entities are
ores.refdata.api::domain::* with no ores.party backend at all; and
the product backlog already had a matching deferred capture
(Dissolve ores.qt.party plugin) tracking exactly this. Moved all 4
entities' Qt code into ores.qt.refdata via git mv, wired the 4
controllers into RefdataPlugin, deleted ores.qt.party entirely
(plugin class, CMake wiring, modeling docs), and updated the
architecture docs.
Stage 3 — full codegen, not hand-wiring
Explicit direction: every entity touched here should have a complete codegen stack, not hand-maintained backend glue. Re-did stage 1's hand-written pieces properly:
- business_unit, party_id_scheme, party_status: regenerated
domain/repository/service/protocol/generator/presentation/Qt layer
by layer, diffing each against the prior hand-written code before
accepting. SQL regeneration was skipped for
party_id_schemeandparty_status— both are dual-file entities (a_table.orgcounterpart already owns their SQL; regenerating from the domain_entity file is a documented unsafe path percodegen-model-unification.org, and neither needed schema changes).business_unit's SQL was regenerated cleanly. - business_unit_type: had no codegen model at all. Authored one from
scratch (using
business_unit.org=/=business_centre.orgas templates), including its full stack. Itscoding_scheme_codeFK is always system-tenant-scoped (unlike ordinary per-tenant soft FKs) and it exports a validator function (ores_refdata_validate_business_unit_type_fn) thatbusiness_unit's own trigger depends on — neither fits the standard per-column FK knobs, so both are expressed as trailing-paste-block custom SQL. That required adding a new, backward-compatible trailing paste marker to the sharedsql_schema_domain_entity_createtemplate (verified as a no-op against an unrelated already-generated entity,currency, before relying on it). - NATS eventing: discovered mid-stage that
business_unit_typeandparty_id_schemenever had eventing wired at all (Qt already subscribed to changed-events that nothing published), and thatbusiness_unit's own eventing was hand-wired directly inapplication.cpprather than through the generated per-entity*_event_registrarpattern. Generated and wired all three properly (PostgresNOTIFYtrigger → generated*_event_registrar→event_registrar.cpp→ NATS publish), and removed the hand-written block forbusiness_unitfromapplication.cpp.
Real pre-existing bugs found and fixed while regenerating (not introduced by this task, surfaced by actually exercising codegen for the first time on stale/never-generated models):
- Old hand-written repositories had no tenant_id filtering at all on any query — a multi-tenancy isolation gap; the current template scopes every query by tenant.
- A model generator snippet assigned a raw
boost::uuids::uuid()(nil, but an engaged optional) to an optional-uuid field instead ofstd::nullopt, which would have failed FK validation on every synthetic-data insert. - Two models had a wrong include path (
ores.refdata/domain/...missing.api), never caught because their Qt facet had never actually been generated before. - Regenerating
BusinessUnitControllersilently droppedIBusinessUnitBrowser, a cross-plugin interfaceTradingPlugin's Org Explorer view depends on for its business-unit edit/history actions — restored by hand after each regen (the interface isn't representable in the model). Also found that wiring was already passingnullptrfor it before this task — a separate, older pre-existing gap, now fixed for real. party_id_scheme.orghad a* Check constraintsheading that the domain_entity SQL template silently ignores (the real heading is* SQL / ** Checks); moot here since SQL regen was skipped for that entity, but worth knowing.default ""(double-quoted empty string) is invalid SQL string literal syntax; fixed to omit the default entirely.- A faker API rename (
faker::company::name()doesn't exist;companyName()does) inbusiness_unit.org's generator snippet — pre-existing, never caught because the generator had never run.
Deliberately accepted, disclosed simplifications: the old
BusinessUnitDetailDialog showed a read-only "level" label next to
the unit-type picker as a side effect of selection; the regenerated
dynamic_combo doesn't support that auxiliary display. Not restored
(minor UX nicety, not core functionality).
Notes
Test Scenarios
Manual QA scenarios (scaffolded via compass add test_scenario, run
through the QA Validation Runner panel) that verify this task. Link
new ones here as they're created; the scenario doc itself links back
via its "Verifies task" field.
| Scenario | State | Notes |
|---|---|---|
PRs
| PR | Title |
|---|---|
| #1609 | [refdata,qt,codegen] Migrate ores.qt.party entities to generic HistoryDialog + full codegen |
Review
| # | Comment summary | File | Decision | Notes |
|---|---|---|---|---|
| 1 | Stale ores.qt.party links break site build |
component_overview.org (refdata, application) | Accepted | Fixed in 3a0d4ae3, before this round. |
| 2 | No test coverage for new repository methods | business_unit(_type)_repository tests | Accepted | Added repository_business_unit_repository_tests.cpp (new file) and new cases (pagination, count, at-version, batch remove) to the existing business_unit_type test file. |
| 3 | Dropped !empty() guard before lexical_cast<uuid> |
business_unit_mapper.cpp | Declined | Matches existing convention — verified identical pattern in portfolio_mapper.cpp (has_value() only, no empty check); DB columns are typed uuid null, not text, so an empty string can't reach this path. |
| 4 | Stale prose "ores.qt.party contributes party-related items" |
ores.qt/refdata component_overview.org | Accepted | Reworded to describe the party-domain entities directly, no more reference to the deleted plugin. |
Result
All 4 entities (business_unit, business_unit_type,
party_id_scheme, party_status) now use the generic HistoryDialog,
live in ores.qt.refdata=/=RefdataPlugin (ores.qt.party deleted
entirely), and have complete, codegen-generated stacks rather than
hand-maintained backend glue — including a brand-new model for
business_unit_type and a new backward-compatible trailing paste
point added to the shared SQL create template. Several real
pre-existing bugs (missing tenant scoping, a nil-vs-nullopt generator
bug, two wrong include paths, a silently-dropped cross-plugin
interface, missing/hand-wired NATS eventing for 3 entities) were found
and fixed along the way. Local build (linux-clang-debug-make) passes
clean. PR #1609 merged 2026-07-17.