ores.refdata.currency

Table of Contents

ISO 4217 currency definitions used for reference data. Currencies are managed per-tenant and drive financial calculations, rounding, and display formatting across the system.

Flags

Columns

iso_code

ISO 4217 alpha-3 code (e.g., "USD", "EUR", "GBP").

The :name generator block below only supplies the literal "X" prefix consumed by the template's counter-based generate_synthetic_currency codepath (X + counter, e.g. "X0", "X1"); the fictional generator (generate_fictional_currencies, injected via custom generator sections below) remains the authoritative test-data source and does not depend on this synthetic path.

std::string("X")

name

Human-readable currency name (e.g., "US Dollar", "Euro").

"Test Currency " + std::to_string(faker::number::integer(1000, 9999))

numeric_code

ISO 4217 numeric code (e.g., "840", "978").

std::to_string(faker::number::integer(10001, 99999))

symbol

Currency symbol displayed in the UI (e.g., "$", "€", "£").

faker::finance::currencySymbol() can return an empty string for some locales; fall back to "$" rather than violate the non-nullable constraint.

[] {
    const auto sym = faker::finance::currencySymbol();
    return sym.empty() ? std::string("$") : std::string(sym);
}()

fraction_symbol

Symbol for the fractional unit (e.g., "¢" for cents, "p" for pence).

std::string("c")

fractions_per_unit

Number of fractional units per whole unit (e.g., 100 for centesimal, 0 for zero-decimal).

100

rounding_type

Rounding method code; soft FK to ores_refdata_rounding_types_tbl.

std::string("Closest")

rounding_precision

Number of decimal places for rounding (e.g., 2 for centesimal currencies).

2

format

Boost.Format pattern for displaying amounts (e.g., "%3% %1$.2f").

std::string("%3% %1$.2f")

monetary_nature

Nature classification; soft FK to ores_refdata_monetary_natures_tbl (e.g., "fiat", "commodity").

std::string("fiat")

market_tier

Market tier classification; soft FK to ores_refdata_currency_market_tiers_tbl (e.g., "g10", "emerging").

std::string("g10")

image_id

Optional reference to a flag or logo image in the images table.

std::nullopt

spot_days

Business days to settlement for a spot trade in this currency (USD is 1, most others are 2). Used to derive currency_pair.spot_days as the max of the two legs' values.

faker::helper::randomElement(std::vector<int>{1, 2})

day_basis

Day-count convention code (e.g. "ACT/360", "ACT/365"). Free-text for now — no aux-type table exists yet; see Currency pair support in reference data for the open question.

std::string("ACT/360")

base_precedence

Ordinal rank in the base-currency precedence table used to determine canonical base/quote order for a pair (lower = takes precedence as base currency).

faker::number::integer(1, 100)

SQL

Flags

Extra drops

drop function if exists ores_refdata_validate_currency_fn;

Validation function

Insert trigger

Validations

column validation_function
rounding_type ores_refdata_validate_rounding_type_fn
monetary_nature ores_refdata_validate_monetary_nature_fn
market_tier ores_refdata_validate_currency_market_tier_fn
change_reason_code ores_dq_validate_change_reason_fn

C++

Flags

Repository

Domain includes

#include <boost/uuid/uuid.hpp>
#include <chrono>
#include <optional>
#include <string>

Entity includes

#include <optional>
#include <string>

Conventions

Table display

column header
iso_code Code
name Currency Name
numeric_code Numeric Code
symbol Symbol
fraction_symbol Fraction
fractions_per_unit Per Unit
rounding_type Rounding Type
rounding_precision Precision
format Format
monetary_nature Monetary Nature
market_tier Market Tier
spot_days Spot Days
day_basis Day Basis
base_precedence Base Precedence
modified_by Modified By
version Version

Qt

Detail fields

All 11 fields currently exposed in CurrencyDetailDialog.ui (3 new domain columns — spot_days, day_basis, base_precedence — aren't in the UI yet; out of scope for this sync, tracked separately). image_id isn't a detail field either (handled generically via has_flag_icon, matching every other flag entity).

field label widget type is_key is_required placeholder spin_min spin_max combo_domain_type combo_fetch_include combo_fetch_fn combo_watcher_name combo_code_field combo_tooltip_field combo_sort_field combo_label combo_setter_pascal badge_key view_group tooltip
iso_code ISO Code isoCodeEdit line_edit true true Enter ISO 4217 code                         General ISO 4217 alpha-3 code used to reference this currency throughout the system.
name Name nameEdit line_edit   true Enter currency name                         General Full descriptive name of the currency.
numeric_code Numeric Code numericCodeEdit line_edit   true Enter numeric code                         General ISO 4217 numeric code, required by some external interfaces and standards.
monetary_nature Asset Class monetaryNatureCombo dynamic_combo           refdata::domain::monetary_nature ores.qt/LookupFetcher.hpp fetch_monetary_natures monetaryNatureWatcher code description display_order monetary natures MonetaryNatureCombo monetary_nature General Classifies the underlying nature of the currency (e.g. fiat, commodity), driving nature-specific business rules.
market_tier Market Tier marketTierCombo dynamic_combo           refdata::domain::currency_market_tier ores.qt/LookupFetcher.hpp fetch_currency_market_tiers marketTierWatcher code description display_order currency market tiers MarketTierCombo currency_market_tier General Classifies the market liquidity/importance tier of the currency (e.g. G10, emerging), used for tiering-based rules and reporting.
symbol Symbol symbolEdit line_edit   true Enter currency symbol                         Formatting Symbol used when formatting monetary amounts in this currency (e.g. $, €, £).
fraction_symbol Fraction Symbol fractionSymbolEdit line_edit     Enter fraction symbol                         Formatting Symbol for the fractional sub-unit of the currency (e.g. cents, pence).
fractions_per_unit Fractions Per Unit fractionsPerUnitSpinBox spin_box       0 10000                     Formatting Number of fractional sub-units per whole unit (e.g. 100 for centesimal currencies, 0 for zero-decimal currencies like JPY).
format Format formatEdit line_edit     Enter display format                         Formatting Boost.Format pattern controlling how amounts in this currency are displayed.
rounding_type Rounding Type roundingTypeCombo dynamic_combo           refdata::domain::rounding_type ores.qt/LookupFetcher.hpp fetch_rounding_types roundingTypeWatcher code description display_order rounding types RoundingTypeCombo   Rounding Rounding method applied to amounts in this currency (e.g. round to closest).
rounding_precision Rounding Precision roundingPrecisionSpinBox spin_box       0 10                     Rounding Number of decimal places amounts in this currency are rounded to.

Columns (Qt model)

enum_name field header type width import_preview is_badge badge_key hidden_by_default tooltip
IsoCode iso_code Code string auto true       ISO 4217 alpha-3 code used to reference this currency throughout the system.
CurrencyName name Currency Name string auto true       Full descriptive name of the currency.
NumericCode numeric_code Numeric Code string 70       true ISO 4217 numeric code, required by some external interfaces and standards.
Symbol symbol Symbol string 60 true       Symbol used when formatting monetary amounts in this currency (e.g. $, €, £).
FractionSymbol fraction_symbol Fraction string 60 true     true Symbol for the fractional sub-unit of the currency (e.g. cents, pence).
FractionsPerUnit fractions_per_unit Per Unit int 70 true     true Number of fractional sub-units per whole unit (0 for zero-decimal currencies like JPY).
RoundingType rounding_type Rounding Type string auto       true Rounding method applied to amounts in this currency.
RoundingPrecision rounding_precision Precision int 70         Number of decimal places amounts in this currency are rounded to.
Format format Format string auto       true Boost.Format pattern controlling how amounts in this currency are displayed.
MonetaryNature monetary_nature Monetary Nature string auto   true monetary_nature   Classifies the underlying nature of the currency (e.g. fiat, commodity).
MarketTier market_tier Market Tier string auto   true currency_market_tier   Classifies the market liquidity/importance tier of the currency (e.g. G10, emerging).
SpotDays spot_days Spot Days int 70       true Number of business days to this currency's spot value date.
DayBasis day_basis Day Basis string 80         This currency's day-count convention code (e.g. ACT/360, ACT/365).
BasePrecedence base_precedence Base Precedence int 100       true This currency's ordinal rank in the base-currency precedence table, used to determine canonical base/quote order for a pair.
Version version Version int 70        
ModifiedBy modified_by Modified By string auto  
RecordedAt recorded_at Recorded At timestamp auto  

Related entity shortcuts

signal icon label tooltip
RoundingTypes Tag Rounding Open Rounding Types list
MonetaryNatures Classification Monetary Natures Open Monetary Natures list
MarketTiers Chart Market Tiers Open Currency Market Tiers list

Calendars tab: detail dialog header includes

#include "ores.qt/CalendarAssignmentWidget.hpp"

Calendars tab: detail dialog private declarations

void setupCalendarsTab();
CalendarAssignmentWidget* calendarWidget_ = nullptr;
QWidget* calendarsTab_ = nullptr;

Calendars tab: constructor wiring

setupCalendarsTab();

Calendars tab: setupConnections wiring

setupConnections() runs before setupCalendarsTab() in the generated constructor (see the base template's ctor body), so any connect(calendarWidget_, ...) placed in this paste point would silently no-op – Qt's static connect() returns an invalid connection with a runtime warning (not an exception or a log we'd see) when the sender is still nullptr, and calendarWidget_ isn't constructed until setupCalendarsTab() runs afterwards. All calendarWidget_-dependent connections live in the calendars_tab_implementation block below instead, after calendarWidget_ actually exists. Left empty deliberately.


Calendars tab: a single change-reason prompt for both writes

Calendar assignments and the currency's own fields must not pop two independent change-reason dialogs for one Save click. Two cases:

  • Only calendar assignments changed (!hasChanges_): commit them here, with their own prompt, and return – skip the rest of onSaveClicked entirely so the currency's own fields aren't re-saved for no real change.
  • Both changed, or only the currency's own fields changed: fall through to the normal save flow below, which prompts once and saves the currency; the calendar commit (using that same prompt result) happens afterwards in the on_save_success paste point once the currency save has actually succeeded – see that block below for why it isn't done here instead.
if (!hasChanges_ && calendarWidget_ && calendarWidget_->hasPendingChanges()) {
    const auto crSel =
        promptChangeReason(ChangeReasonDialog::OperationType::Amend, true, "common");
    if (!crSel)
        return;
    calendarWidget_->commitChanges(
        crSel->reason_code, crSel->commentary, [this](bool success, const QString& message) {
            if (!success)
                MessageBoxHelper::critical(this, "Calendar Assignment Failed", message);
        });
    return;
}

Calendars tab: commit calendar changes after a successful currency save

Runs inside onSaveClicked's async watcher, after the currency save has succeeded and reused its change-reason prompt (crReasonCode=/ =crCommentary, captured into the watcher lambda) – not a second, independent prompt. Only reached when the currency itself had changes to save; the calendar-only case is handled entirely by the calendars_tab_save_guard block above, which returns before this point is ever reached.

if (self->calendarWidget_ && self->calendarWidget_->hasPendingChanges()) {
    self->calendarWidget_->commitChanges(
        crReasonCode, crCommentary, [self](bool success, const QString& message) {
            if (!success)
                MessageBoxHelper::critical(self, "Calendar Assignment Failed", message);
        });
}

Calendars tab: keep read-only state in sync while browsing history

setReadOnly is also invoked when paging through historical versions via the version-nav actions. Without this, a user already on the Calendars tab who pages to a historical version could still stage calendar edits against a read-only entity.

if (calendarWidget_ && calendarsTab_ && tabWidget()->currentWidget() == calendarsTab_)
    calendarWidget_->setReadOnly(readOnly_ || currency_.iso_code.empty());

Calendars tab: implementation

void CurrencyDetailDialog::setupCalendarsTab() {
    calendarWidget_ = new CalendarAssignmentWidget(this);
    calendarWidget_->setCallbacks(
        [](ClientManager* cm,
           const std::string& leftKey) -> CalendarAssignmentWidget::LoadResult {
            refdata::messaging::get_currency_calendars_request request;
            request.currency_iso_code = leftKey;
            auto response = cm->process_authenticated_request(std::move(request));
            if (!response)
                return {.success = false, .message = "Failed to communicate with server"};
            CalendarAssignmentWidget::LoadResult result;
            result.success = response->success;
            result.message = QString::fromStdString(response->message);
            for (const auto& cc : response->calendars)
                result.calendarCodes.push_back(cc.calendar_code);
            return result;
        },
        [](ClientManager* cm,
           const std::string& leftKey,
           const std::string& calendarCode,
           const std::string& changeReasonCode,
           const std::string& changeCommentary) -> CalendarAssignmentWidget::MutateResult {
            refdata::messaging::assign_currency_calendar_request request;
            request.currency_iso_code = leftKey;
            request.calendar_code = calendarCode;
            request.change_reason_code = changeReasonCode;
            request.change_commentary = changeCommentary;
            auto response = cm->process_authenticated_request(std::move(request));
            if (!response)
                return {.success = false, .message = "Failed to communicate with server"};
            return {.success = response->success, .message = QString::fromStdString(response->message)};
        },
        [](ClientManager* cm,
           const std::string& leftKey,
           const std::string& calendarCode) -> CalendarAssignmentWidget::MutateResult {
            refdata::messaging::revoke_currency_calendar_request request;
            request.currency_iso_code = leftKey;
            request.calendar_code = calendarCode;
            auto response = cm->process_authenticated_request(std::move(request));
            if (!response)
                return {.success = false, .message = "Failed to communicate with server"};
            return {.success = response->success, .message = QString::fromStdString(response->message)};
        });

    calendarsTab_ = new QWidget();
    auto* layout = new QVBoxLayout(calendarsTab_);
    layout->addWidget(calendarWidget_);
    // Provenance is the base template's own tab and should stay last for
    // consistency across every entity's detail dialog -- insert Calendars
    // before it rather than appending, which would push Provenance out of
    // its usual final position.
    int provenanceTabIndex = -1;
    for (int i = 0; i < tabWidget()->count(); ++i) {
        if (tabWidget()->tabText(i) == tr("Provenance")) {
            provenanceTabIndex = i;
            break;
        }
    }
    if (provenanceTabIndex >= 0)
        tabWidget()->insertTab(provenanceTabIndex, calendarsTab_, tr("Calendars"));
    else
        tabWidget()->addTab(calendarsTab_, tr("Calendars"));

    // calendarWidget_/calendarsTab_ only exist from this point on -- see
    // the calendars_tab_connections paste point above for why these
    // connections live here instead of setupConnections().
    connect(calendarWidget_,
            &CalendarAssignmentWidget::statusMessage,
            this,
            &CurrencyDetailDialog::statusMessage);
    connect(calendarWidget_,
            &CalendarAssignmentWidget::errorMessage,
            this,
            [this](const QString&, const QString& message) { emit errorMessage(message); });
    connect(calendarWidget_,
            &CalendarAssignmentWidget::assignmentsChanged,
            this,
            [this]() { ui_->saveButton->setEnabled(!readOnly_); });
    connect(tabWidget(), &QTabWidget::currentChanged, this, [this](int) {
        if (!calendarWidget_ || !calendarsTab_ || tabWidget()->currentWidget() != calendarsTab_)
            return;
        calendarWidget_->setClientManager(clientManager_);
        calendarWidget_->setImageCache(imageCache());
        calendarWidget_->setLeftKey(currency_.iso_code);
        calendarWidget_->setReadOnly(readOnly_ || currency_.iso_code.empty());
        calendarWidget_->load();
    });
    // Calendar commits are triggered from onSaveClicked itself (see the
    // calendars_tab_save_guard and on_save_success paste points) so both
    // the currency's own fields and calendar assignments share a single
    // change-reason prompt per Save click -- no separate saveButton
    // connection here.
}

Custom service constructor init

, junction_repo_(ctx_)

Custom service includes

#include <boost/uuid/uuid.hpp>
#include "ores.refdata.core/repository/party_currency_repository.hpp"

Custom service .cpp includes

#include <algorithm>
#include <boost/uuid/uuid_io.hpp>
#include <unordered_set>

Custom service methods

/**
 * @brief Lists currencies visible to a specific party, with pagination.
 *
 * Uses the party_currencies junction to filter the full currency list
 * to only those the given party is permitted to see.
 *
 * @param party_id The UUID of the party.
 * @param offset Number of records to skip.
 * @param limit Maximum number of records to return.
 * @return Vector of visible currencies for the requested page.
 */
std::vector<domain::currency> list_currencies_for_party(const boost::uuids::uuid& party_id,
                                                        std::uint32_t offset,
                                                        std::uint32_t limit);

/**
 * @brief Gets the total count of currencies visible to a specific party.
 *
 * @param party_id The UUID of the party.
 * @return Number of currencies the party is permitted to see.
 */
std::uint32_t count_currencies_for_party(const boost::uuids::uuid& party_id);

Custom service members

repository::party_currency_repository junction_repo_;

Custom service implementations

std::vector<domain::currency> currency_service::list_currencies_for_party(
    const boost::uuids::uuid& party_id, std::uint32_t offset, std::uint32_t limit) {
    BOOST_LOG_SEV(lg(), debug) << "Listing currencies for party: " << party_id
                               << " offset=" << offset << " limit=" << limit;

    const auto junctions = junction_repo_.read_latest_by_party(party_id);
    std::unordered_set<std::string> visible;
    visible.reserve(junctions.size());
    for (const auto& j : junctions)
        visible.insert(j.currency_iso_code);

    auto all = repo_.read_latest(ctx_);
    std::vector<domain::currency> filtered;
    filtered.reserve(visible.size());
    for (auto& c : all) {
        if (visible.count(c.iso_code))
            filtered.push_back(std::move(c));
    }

    if (offset >= filtered.size())
        return {};
    const auto end = std::min<std::size_t>(offset + limit, filtered.size());
    return std::vector<domain::currency>(filtered.begin() + offset, filtered.begin() + end);
}

std::uint32_t currency_service::count_currencies_for_party(const boost::uuids::uuid& party_id) {
    BOOST_LOG_SEV(lg(), debug) << "Counting currencies for party: " << party_id;
    const auto junctions = junction_repo_.read_latest_by_party(party_id);
    return static_cast<std::uint32_t>(junctions.size());
}

Custom generator declarations

/**
 * @brief Generates a set of currencies using Unicode symbols.
 *
 * Used for testing internationalisation and symbol rendering.
 */
ORES_REFDATA_API_EXPORT std::vector<domain::currency>
generate_synthetic_unicode_currencies(utility::generation::generation_context& ctx);

/**
 * @brief Generates N synthetic currencies with unique ISO codes.
 */
ORES_REFDATA_API_EXPORT std::vector<domain::currency>
generate_unique_synthetic_currencies(std::size_t n, utility::generation::generation_context& ctx);

/**
 * @brief Generates a set of fictional currencies.
 *
 * These are intentionally fake currencies with made-up ISO codes that do not
 * correspond to any real ISO 4217 codes. Useful for testing and demo
 * purposes where real currency data should not be used.
 *
 * @param n Number of currencies to generate. If n is 0 or greater than the
 *          available set (50), returns all available fictional currencies.
 */
ORES_REFDATA_API_EXPORT std::vector<domain::currency>
generate_fictional_currencies(std::size_t n, utility::generation::generation_context& ctx);

Custom generator implementations

std::vector<domain::currency>
generate_synthetic_unicode_currencies(utility::generation::generation_context& ctx) {
    const auto modified_by = ctx.env().get_or(generation_keys::modified_by, "system");
    const auto tid = ctx.env().get_or(generation_keys::tenant_id, "system");
    const auto parsed_tid = utility::uuid::tenant_id::from_string(tid);
    const auto tenant_id =
        parsed_tid.has_value() ? parsed_tid.value() : utility::uuid::tenant_id::system();

    static std::atomic<std::size_t> batch{0};
    const auto b = ++batch;
    const auto suffix = "_" + std::to_string(b);
    const auto now = ctx.past_timepoint();

    std::vector<domain::currency> r;
    r.push_back({.tenant_id = tenant_id,
                 .iso_code = "XU" + std::to_string(b),
                 .name = "Test Dollar" + suffix,
                 .numeric_code = "90001",
                 .symbol = "$",
                 .fraction_symbol = "\xC2\xA2",
                 .fractions_per_unit = 100,
                 .rounding_type = "Closest",
                 .rounding_precision = 2,
                 .format = "%3% %1$.2f",
                 .monetary_nature = "fiat",
                 .market_tier = "g10",
                 .modified_by = modified_by,
                 .change_reason_code = "system.test",
                 .change_commentary = "Synthetic test data",
                 .recorded_at = now});

    r.push_back({.tenant_id = tenant_id,
                 .iso_code = "XE" + std::to_string(b),
                 .name = "Test Euro" + suffix,
                 .numeric_code = "90002",
                 .symbol = "\xE2\x82\xAC",
                 .fraction_symbol = "\xC2\xA2",
                 .fractions_per_unit = 100,
                 .rounding_type = "Closest",
                 .rounding_precision = 2,
                 .format = "%3% %1$.2f",
                 .monetary_nature = "fiat",
                 .market_tier = "g10",
                 .modified_by = modified_by,
                 .change_reason_code = "system.test",
                 .change_commentary = "Synthetic test data",
                 .recorded_at = now});

    r.push_back({.tenant_id = tenant_id,
                 .iso_code = "XG" + std::to_string(b),
                 .name = "Test Pound" + suffix,
                 .numeric_code = "90003",
                 .symbol = "\xC2\xA3",
                 .fraction_symbol = "p",
                 .fractions_per_unit = 100,
                 .rounding_type = "Closest",
                 .rounding_precision = 2,
                 .format = "%3% %1$.2f",
                 .monetary_nature = "fiat",
                 .market_tier = "g10",
                 .modified_by = modified_by,
                 .change_reason_code = "system.test",
                 .change_commentary = "Synthetic test data",
                 .recorded_at = now});

    r.push_back({.tenant_id = tenant_id,
                 .iso_code = "XJ" + std::to_string(b),
                 .name = "Test Yen" + suffix,
                 .numeric_code = "90004",
                 .symbol = "\xC2\xA5",
                 .fraction_symbol = "",
                 .fractions_per_unit = 0,
                 .rounding_type = "Closest",
                 .rounding_precision = 0,
                 .format = "%3% %1$.2f",
                 .monetary_nature = "fiat",
                 .market_tier = "g10",
                 .modified_by = modified_by,
                 .change_reason_code = "system.test",
                 .change_commentary = "Synthetic test data",
                 .recorded_at = now});

    r.push_back({.tenant_id = tenant_id,
                 .iso_code = "XI" + std::to_string(b),
                 .name = "Test Rupee" + suffix,
                 .numeric_code = "90005",
                 .symbol = "\xE2\x82\xB9",
                 .fraction_symbol = "\xE0\xA4\xAA",
                 .fractions_per_unit = 100,
                 .rounding_type = "Closest",
                 .rounding_precision = 2,
                 .format = "%3% %1$.2f",
                 .monetary_nature = "fiat",
                 .market_tier = "g10",
                 .modified_by = modified_by,
                 .change_reason_code = "system.test",
                 .change_commentary = "Synthetic test data",
                 .recorded_at = now});

    r.push_back({.tenant_id = tenant_id,
                 .iso_code = "XB" + std::to_string(b),
                 .name = "Test Crypto" + suffix,
                 .numeric_code = "90006",
                 .symbol = "\xE2\x82\xBF",
                 .fraction_symbol = "s",
                 .fractions_per_unit = 100000000,
                 .rounding_type = "Closest",
                 .rounding_precision = 2,
                 .format = "%3% %1$.2f",
                 .monetary_nature = "fiat",
                 .market_tier = "g10",
                 .modified_by = modified_by,
                 .change_reason_code = "system.test",
                 .change_commentary = "Synthetic test data",
                 .recorded_at = now});

    r.push_back({.tenant_id = tenant_id,
                 .iso_code = "XR" + std::to_string(b),
                 .name = "Test Ruble" + suffix,
                 .numeric_code = "90007",
                 .symbol = "\xE2\x82\xBD",
                 .fraction_symbol = "\xD0\xBA",
                 .fractions_per_unit = 100,
                 .rounding_type = "Closest",
                 .rounding_precision = 2,
                 .format = "%3% %1$.2f",
                 .monetary_nature = "fiat",
                 .market_tier = "g10",
                 .modified_by = modified_by,
                 .change_reason_code = "system.test",
                 .change_commentary = "Synthetic test data",
                 .recorded_at = now});
    return r;
}

std::vector<domain::currency>
generate_unique_synthetic_currencies(std::size_t n, utility::generation::generation_context& ctx) {
    std::unordered_set<std::string> seen;
    seen.reserve(n);

    std::vector<domain::currency> r;
    r.reserve(n);

    std::size_t suffix = 0;
    while (r.size() < n) {
        auto currency = generate_synthetic_currency(ctx);
        if (!seen.insert(currency.iso_code).second) {
            auto base_code = currency.iso_code;
            do {
                currency.iso_code = base_code + std::to_string(++suffix);
            } while (!seen.insert(currency.iso_code).second);
        }
        r.push_back(std::move(currency));
    }
    return r;
}

std::vector<domain::currency>
generate_fictional_currencies(std::size_t n, utility::generation::generation_context& ctx) {
    const auto modified_by = ctx.env().get_or(generation_keys::modified_by, "system");
    const auto tid = ctx.env().get_or(generation_keys::tenant_id, "system");
    const auto parsed_tid = utility::uuid::tenant_id::from_string(tid);
    const auto tenant_id =
        parsed_tid.has_value() ? parsed_tid.value() : utility::uuid::tenant_id::system();
    const auto now = ctx.past_timepoint();

    std::vector<domain::currency> all;
    all.reserve(50);

    // Fictional currencies intentionally omit fraction_symbol. These are
    // synthetic test data; no real sub-unit symbol is needed, and
    // fractions_per_unit controls numeric precision in calculations only.
    all.push_back({.tenant_id = tenant_id, .iso_code = "XAE", .name = "Aerilonian Dollar",
                   .numeric_code = "10001", .symbol = "A$", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XAR", .name = "Arcturian Arct",
                   .numeric_code = "10002", .symbol = "Ar", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XBL", .name = "Balthorian Florin",
                   .numeric_code = "10003", .symbol = "Bf", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XBE", .name = "Bellorian Bell",
                   .numeric_code = "10004", .symbol = "Bb", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XCA", .name = "Calandrian Crown",
                   .numeric_code = "10005", .symbol = "Cc", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XCL", .name = "Caledonian Caled",
                   .numeric_code = "10006", .symbol = "Cd", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XDA", .name = "Daelorian Dinar",
                   .numeric_code = "10007", .symbol = "Dd", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XDL", .name = "Delvadian Delv",
                   .numeric_code = "10008", .symbol = "De", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XER", .name = "Eriadoran Euro",
                   .numeric_code = "10009", .symbol = "Er", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XES", .name = "Esterian Est",
                   .numeric_code = "10010", .symbol = "Es", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XFE", .name = "Felorian Franc",
                   .numeric_code = "10011", .symbol = "Ff", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XFN", .name = "Fendarian Fen",
                   .numeric_code = "10012", .symbol = "Fn", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XGA", .name = "Galdorian Galleon",
                   .numeric_code = "10013", .symbol = "Gg", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XGR", .name = "Grendorian Grend",
                   .numeric_code = "10014", .symbol = "Gr", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XHE", .name = "Helvetian Franc",
                   .numeric_code = "10015", .symbol = "Hf", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XHY", .name = "Hydronian Hyd",
                   .numeric_code = "10016", .symbol = "Hy", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XIR", .name = "Iridian Dollar",
                   .numeric_code = "10017", .symbol = "I$", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XIT", .name = "Ithacan Ith",
                   .numeric_code = "10018", .symbol = "It", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XJE", .name = "Jethronian Jet",
                   .numeric_code = "10019", .symbol = "Je", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XJO", .name = "Jorvikian Krona",
                   .numeric_code = "10020", .symbol = "Jk", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XKA", .name = "Kaelorian Krown",
                   .numeric_code = "10021", .symbol = "Kk", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XKR", .name = "Krynnish Krynn",
                   .numeric_code = "10022", .symbol = "Kr", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XLU", .name = "Luminian Lum",
                   .numeric_code = "10023", .symbol = "Lu", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XLY", .name = "Lysandrian Lira",
                   .numeric_code = "10024", .symbol = "Ly", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XMA", .name = "Maldorian Mal",
                   .numeric_code = "10025", .symbol = "Mm", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XMR", .name = "Mariposan Peso",
                   .numeric_code = "10026", .symbol = "Mp", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XNE", .name = "Nektonian Nek",
                   .numeric_code = "10027", .symbol = "Ne", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XNT", .name = "Netharian Naira",
                   .numeric_code = "10028", .symbol = "Nt", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XOR", .name = "Orinocan Bolivar",
                   .numeric_code = "10029", .symbol = "Ob", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XOL", .name = "Orlanthian Orl",
                   .numeric_code = "10030", .symbol = "Ol", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XPL", .name = "Paldorian Peso",
                   .numeric_code = "10031", .symbol = "Pp", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XPY", .name = "Pyrrhian Pyr",
                   .numeric_code = "10032", .symbol = "Py", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XQU", .name = "Quentarian Quen",
                   .numeric_code = "10033", .symbol = "Qu", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XQN", .name = "Quinarian Quetzal",
                   .numeric_code = "10034", .symbol = "Qn", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XRE", .name = "Rendellian Rend",
                   .numeric_code = "10035", .symbol = "Re", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XRI", .name = "Rivenian Ruble",
                   .numeric_code = "10036", .symbol = "Ri", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XSE", .name = "Serendian Shilling",
                   .numeric_code = "10037", .symbol = "Ss", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XSI", .name = "Sildorian Sild",
                   .numeric_code = "10038", .symbol = "Si", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XTA", .name = "Tandorian Taka",
                   .numeric_code = "10039", .symbol = "Ta", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XTE", .name = "Tenebrian Ten",
                   .numeric_code = "10040", .symbol = "Te", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XUL", .name = "Uldorian Uld",
                   .numeric_code = "10041", .symbol = "Ul", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XUT", .name = "Utopian Unit",
                   .numeric_code = "10042", .symbol = "Ut", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XVA", .name = "Valorian Valt",
                   .numeric_code = "10043", .symbol = "Vv", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XVL", .name = "Valtarian Val",
                   .numeric_code = "10044", .symbol = "Vl", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XWI", .name = "Wintervalean Won",
                   .numeric_code = "10045", .symbol = "Ww", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XWY", .name = "Wysterian Wys",
                   .numeric_code = "10046", .symbol = "Wy", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XAN", .name = "Xandrian Xan",
                   .numeric_code = "10047", .symbol = "Xa", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XEN", .name = "Xenorian Xen",
                   .numeric_code = "10048", .symbol = "Xe", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XYS", .name = "Yslandian Yen",
                   .numeric_code = "10049", .symbol = "Ys", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});
    all.push_back({.tenant_id = tenant_id, .iso_code = "XZE", .name = "Zephyrian Zephyr",
                   .numeric_code = "10050", .symbol = "Ze", .fractions_per_unit = 100,
                   .rounding_type = "Closest", .rounding_precision = 2, .format = "%3% %1$.2f",
                   .monetary_nature = "fiat", .market_tier = "g10", .modified_by = modified_by,
                   .change_reason_code = "system.test", .change_commentary = "Synthetic test data",
                   .recorded_at = now});

    if (n == 0 || n >= all.size())
        return all;

    return std::vector<domain::currency>(all.begin(), all.begin() + n);
}

Calendar assignment: custom service constructor init

Second custom-member initialiser, appended after the party_currency junction_repo_ one above (both share the 4F8A2B1E paste point).

, calendar_repo_(ctx_)

Calendar assignment: custom service includes

#include "ores.refdata.core/repository/currency_calendar_repository.hpp"

Calendar assignment: custom service methods

/**
 * @brief Lists the calendars assigned to a currency for spot/settlement
 * date computation, via the currency_calendars junction.
 *
 * @param currency_iso_code The ISO 4217 code of the currency.
 * @return Vector of calendar assignment rows for the currency.
 */
std::vector<ores::refdata::domain::currency_calendar>
list_calendars_for_currency(const std::string& currency_iso_code);

/**
 * @brief Assigns a calendar to a currency (creates the junction row).
 *
 * @param row The currency_calendar row to write; caller is responsible
 * for stamping tenant_id/modified_by/performed_by/change_reason_code
 * before calling (see ores::service::messaging::stamp()).
 */
void assign_calendar_to_currency(const ores::refdata::domain::currency_calendar& row);

/**
 * @brief Revokes a calendar from a currency (removes the junction row).
 *
 * @param currency_iso_code The ISO 4217 code of the currency.
 * @param calendar_code The QuantLib/ORE calendar token to revoke.
 */
void revoke_calendar_from_currency(const std::string& currency_iso_code,
                                   const std::string& calendar_code);

Calendar assignment: custom service members

Second custom member, appended after the party_currency junction_repo_ one above (both share the 047A96EF paste point).

repository::currency_calendar_repository calendar_repo_;

Calendar assignment: custom service implementations

std::vector<ores::refdata::domain::currency_calendar>
currency_service::list_calendars_for_currency(const std::string& currency_iso_code) {
    BOOST_LOG_SEV(lg(), debug) << "Listing calendars for currency: " << currency_iso_code;
    return calendar_repo_.read_latest_by_currency(currency_iso_code);
}

void currency_service::assign_calendar_to_currency(
    const ores::refdata::domain::currency_calendar& row) {
    BOOST_LOG_SEV(lg(), debug) << "Assigning calendar to currency: " << row.currency_iso_code
                               << "/" << row.calendar_code;
    calendar_repo_.write(row);
    BOOST_LOG_SEV(lg(), info) << "Assigned calendar to currency: " << row.currency_iso_code
                              << "/" << row.calendar_code;
}

void currency_service::revoke_calendar_from_currency(const std::string& currency_iso_code,
                                                      const std::string& calendar_code) {
    BOOST_LOG_SEV(lg(), debug) << "Revoking calendar from currency: " << currency_iso_code
                               << "/" << calendar_code;
    calendar_repo_.remove(currency_iso_code, calendar_code);
    BOOST_LOG_SEV(lg(), info) << "Revoked calendar from currency: " << currency_iso_code
                              << "/" << calendar_code;
}

Calendar assignment: custom protocol includes

#include "ores.refdata.api/domain/currency_calendar.hpp"

Calendar assignment: custom protocol messages

struct get_currency_calendars_request {
    using response_type = struct get_currency_calendars_response;
    static constexpr std::string_view nats_subject = "refdata.v1.currencies.calendars.list";
    std::string currency_iso_code;
};

struct get_currency_calendars_response {
    std::vector<ores::refdata::domain::currency_calendar> calendars;
    bool success = false;
    std::string message;
};

struct assign_currency_calendar_request {
    using response_type = struct assign_currency_calendar_response;
    static constexpr std::string_view nats_subject = "refdata.v1.currencies.calendars.assign";
    std::string currency_iso_code;
    std::string calendar_code;
    std::string change_reason_code;
    std::string change_commentary;
};

struct assign_currency_calendar_response {
    bool success = false;
    std::string message;
};

struct revoke_currency_calendar_request {
    using response_type = struct revoke_currency_calendar_response;
    static constexpr std::string_view nats_subject = "refdata.v1.currencies.calendars.revoke";
    std::string currency_iso_code;
    std::string calendar_code;
};

struct revoke_currency_calendar_response {
    bool success = false;
    std::string message;
};

Calendar assignment: custom handler methods

void list_calendars(ores::nats::message msg) {
    BOOST_LOG_SEV(currency_handler_lg(), debug) << "Handling " << msg.subject;
    auto req_ctx_expected = ores::service::service::make_request_context(
        ctx_, msg, verifier_);
    if (!req_ctx_expected) {
        error_reply(nats_, msg, req_ctx_expected.error());
        return;
    }
    const auto& req_ctx = *req_ctx_expected;
    service::currency_service svc(req_ctx);
    if (auto req = decode<get_currency_calendars_request>(msg)) {
        get_currency_calendars_response resp;
        try {
            resp.calendars = svc.list_calendars_for_currency(req->currency_iso_code);
            resp.success = true;
        } catch (const std::exception& e) {
            BOOST_LOG_SEV(currency_handler_lg(), error)
                << msg.subject << " failed: " << e.what();
            resp.success = false;
            resp.message = e.what();
        }
        BOOST_LOG_SEV(currency_handler_lg(), debug) << "Completed " << msg.subject;
        reply(nats_, msg, resp);
    } else {
        BOOST_LOG_SEV(currency_handler_lg(), warn) << "Failed to decode: " << msg.subject;
        error_reply(nats_, msg, ores::service::error_code::bad_request);
    }
}

void assign_calendar(ores::nats::message msg) {
    BOOST_LOG_SEV(currency_handler_lg(), debug) << "Handling " << msg.subject;
    auto req_ctx_expected = ores::service::service::make_request_context(
        ctx_, msg, verifier_);
    if (!req_ctx_expected) {
        error_reply(nats_, msg, req_ctx_expected.error());
        return;
    }
    const auto& req_ctx = *req_ctx_expected;
    if (!has_permission(req_ctx, "refdata::currencies:write")) {
        error_reply(nats_, msg, ores::service::error_code::forbidden);
        return;
    }
    service::currency_service svc(req_ctx);
    if (auto req = decode<assign_currency_calendar_request>(msg)) {
        try {
            domain::currency_calendar row;
            row.currency_iso_code = req->currency_iso_code;
            row.calendar_code = req->calendar_code;
            row.change_reason_code = req->change_reason_code;
            row.change_commentary = req->change_commentary;
            ores::service::messaging::stamp(row, req_ctx);
            svc.assign_calendar_to_currency(row);
            BOOST_LOG_SEV(currency_handler_lg(), debug) << "Completed " << msg.subject;
            reply(nats_, msg, assign_currency_calendar_response{.success = true});
        } catch (const std::exception& e) {
            BOOST_LOG_SEV(currency_handler_lg(), error)
                << msg.subject << " failed: " << e.what();
            reply(nats_, msg, assign_currency_calendar_response{
                .success = false, .message = e.what()});
        }
    } else {
        BOOST_LOG_SEV(currency_handler_lg(), warn) << "Failed to decode: " << msg.subject;
        error_reply(nats_, msg, ores::service::error_code::bad_request);
    }
}

void revoke_calendar(ores::nats::message msg) {
    BOOST_LOG_SEV(currency_handler_lg(), debug) << "Handling " << msg.subject;
    auto req_ctx_expected = ores::service::service::make_request_context(
        ctx_, msg, verifier_);
    if (!req_ctx_expected) {
        error_reply(nats_, msg, req_ctx_expected.error());
        return;
    }
    const auto& req_ctx = *req_ctx_expected;
    if (!has_permission(req_ctx, "refdata::currencies:write")) {
        error_reply(nats_, msg, ores::service::error_code::forbidden);
        return;
    }
    service::currency_service svc(req_ctx);
    if (auto req = decode<revoke_currency_calendar_request>(msg)) {
        try {
            svc.revoke_calendar_from_currency(req->currency_iso_code, req->calendar_code);
            BOOST_LOG_SEV(currency_handler_lg(), debug) << "Completed " << msg.subject;
            reply(nats_, msg, revoke_currency_calendar_response{.success = true});
        } catch (const std::exception& e) {
            BOOST_LOG_SEV(currency_handler_lg(), error)
                << msg.subject << " failed: " << e.what();
            reply(nats_, msg, revoke_currency_calendar_response{
                .success = false, .message = e.what()});
        }
    } else {
        BOOST_LOG_SEV(currency_handler_lg(), warn) << "Failed to decode: " << msg.subject;
        error_reply(nats_, msg, ores::service::error_code::bad_request);
    }
}

Calendar assignment: custom registrar subscriptions

subs.push_back(nats.queue_subscribe(
    get_currency_calendars_request::nats_subject, queue_group,
    [h](ores::nats::message msg) { h->list_calendars(std::move(msg)); }));
subs.push_back(nats.queue_subscribe(
    assign_currency_calendar_request::nats_subject, queue_group,
    [h](ores::nats::message msg) { h->assign_calendar(std::move(msg)); }));
subs.push_back(nats.queue_subscribe(
    revoke_currency_calendar_request::nats_subject, queue_group,
    [h](ores::nats::message msg) { h->revoke_calendar(std::move(msg)); }));

Custom repository methods

Repository impl includes

No extra includes needed — <sqlgen/postgres.hpp>, bitemporal_operations.hpp, and helpers.hpp are already in the standard template output.


Repository implementations

Note: pagination (read_latest with offset/limit), get_total_currency_count, filtered read_all, batch remove, and read_at_timepoint (both overloads) are now generated natively by the base repository template (has_as_of_lookup: true, tenant-aware). Only the unfiltered read_all (used by the CLI's export --all-versions without --key) has no native equivalent and remains a custom paste.

std::vector<domain::currency> currency_repository::read_all(context ctx) {
    const auto tid = ctx.tenant_id().to_string();
    const auto query = sqlgen::read<std::vector<currency_entity>> |
                       where("tenant_id"_c == tid) | order_by("valid_from"_c.desc());

    return execute_read_query<currency_entity, domain::currency>(
        ctx,
        query,
        [](const auto& entities) { return currency_mapper::map(entities); },
        lg(),
        "Reading all currencies.");
}

read_all (unfiltered)

/**
 * @brief Reads all currencies (all versions), unfiltered.
 */
std::vector<domain::currency> read_all(context ctx);

See also

Emacs 29.3 (Org mode 9.6.15)