ores.refdata.currency
Table of Contents
- Flags
- Columns
- SQL
- Validation function
- Insert trigger
- C++
- Flags
- Repository
- Domain includes
- Entity includes
- Conventions
- Table display
- Qt
- Calendars tab: detail dialog header includes
- Calendars tab: detail dialog private declarations
- Calendars tab: constructor wiring
- Calendars tab: setupConnections wiring
- Calendars tab: a single change-reason prompt for both writes
- Calendars tab: commit calendar changes after a successful currency save
- Calendars tab: keep read-only state in sync while browsing history
- Calendars tab: implementation
- Custom service constructor init
- Custom service includes
- Custom service .cpp includes
- Custom service methods
- Custom service members
- Custom service implementations
- Custom generator declarations
- Custom generator implementations
- Calendar assignment: custom service constructor init
- Calendar assignment: custom service includes
- Calendar assignment: custom service methods
- Calendar assignment: custom service members
- Calendar assignment: custom service implementations
- Calendar assignment: custom protocol includes
- Calendar assignment: custom protocol messages
- Calendar assignment: custom handler methods
- Calendar assignment: custom registrar subscriptions
- Custom repository methods
- See also
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 ofonSaveClickedentirely 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_successpaste 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
- ores.refdata — component group overview.