ores.refdata.party
Table of Contents
- 1. Flags
- 2. Columns
- 3. Foreign keys
- 4. Insert trigger
- 5. SQL
- 6. Messages
- 7. C++
- 8. See also
Internal legal entities (the organisation and its subsidiaries) that
participate in financial transactions. Parties form a hierarchy through
parent_party_id, with exactly one root party per tenant representing the
organisation itself.
1. Flags
Cross-cutting properties. Consumed by both C++ and SQL codegen.
2. Columns
Logical, human-meaningful identifiers (full_name, short_code,
flagged :natural_key: true below) are inserted into the C++ struct
alongside the primary key, and exposed in SQL as additional
unique-indexed columns.
2.1. id
UUID uniquely identifying this party.
Surrogate key for the party record.
2.2. full_name
Full legal name of the party.
The official registered name of the entity.
std::string(faker::company::companyName())
2.3. short_code
Short code for quick reference.
A brief mnemonic code used in trading systems.
std::string(faker::string::alpha(6))
2.4. codename
Globally unique human-readable codename (adjective_noun).
Used in operator tooling. Immutable once assigned; auto-generated by the SQL trigger if not supplied on insert.
""
2.5. transliterated_name
ASCII transliteration of the entity name.
Populated from GLEIF data for entities with non-Latin names (CJK, Cyrillic, Arabic, etc.). Null for entities already in Latin script.
std::nullopt
2.6. party_category
Structural classification of this party.
References the party_category lookup table. Values: 'System' (one per tenant, auto-created during provisioning) or 'Operational' (business entities created during normal system operation). Not user-editable via the UI – every party created through a detail form is 'Operational'; 'System' parties are exclusively auto-created during tenant provisioning. Defaults here so a freshly-constructed party (the Add form, with no UI field for this column) always carries a value the party_category-validation trigger accepts.
std::string("Operational")
2.7. party_type
Classification of this party.
References the party_type lookup table.
std::string("Corporate")
2.8. parent_party_id
Parent party for hierarchy.
References the parent party record for group structures. Null for root parties.
std::nullopt
2.9. business_center_code
Business center location code.
FpML business center code indicating primary location. Synthetic
parties default to WRLD (the global sentinel business centre,
guaranteed seeded for every tenant by the system-tenant bootstrap –
see the same reasoning in ores.refdata.book.org), not a real FpML
city code like GBLO: real city codes only exist once the large,
optional FpML business-centre reference dataset has been loaded, so
using one here would make a freshly-generated party fail its
FK-validation trigger in any environment that hasn't loaded it.
std::string("WRLD")
2.10. status
Current lifecycle status.
References the party_status lookup table.
std::string("Active")
2.11. image_id
Optional reference to a logo/branding image in the images table.
std::nullopt
2.12. is_registration_default
Whether a self-registered account in this tenant joins this party. At most one live party per tenant may hold it.
The flag sits on the party row rather than on the tenant because this table
is tenant-isolated: a tenant administrator may write their own parties, so
the choice is theirs to make. A default_party_id column on the tenant row
would be unreachable to them, because only a system-tenant session may write
a tenant row.
false
3. Foreign keys
3.1. parent_party_id
3.2. image_id
4. Insert trigger
4.1. Validations
| column | validation_function |
|---|---|
| party_category | ores_refdata_validate_party_category_fn |
| party_type | ores_refdata_validate_party_type_fn |
| status | ores_refdata_validate_party_status_fn |
| business_center_code | ores_refdata_validate_business_centre_fn |
5. SQL
Everything below is consumed by the SQL codegen pipeline only.
5.1. Flags
5.2. Checks
| expression |
|---|
| length("codename") <= 32 |
| "codename" = '' or "codename" ~ '^[a-z][a-z_]+_[0-9]{4}$' |
5.3. Indexes
| name | columns | unique | current_only | where_extra |
|---|---|---|---|---|
| full_name | tenant_id, full_name | false | true | |
| codename_uniq | codename | true | true | codename <> '' |
| root_party_uniq | tenant_id | true | false | parent_party_id is null and party_category <> 'System' and valid_to = ores_utility_infinity_timestamp_fn() |
| system_party_uniq | tenant_id | true | false | party_category = 'System' and valid_to = ores_utility_infinity_timestamp_fn() |
| registration_default_party_uniq | tenant_id | true | false | is_registration_default and valid_to = ores_utility_infinity_timestamp_fn() |
5.4. Codename sequence
Sequence used to generate a unique auto-generated codename. nextval() is
outside MVCC: it advances even within a multi-row INSERT statement, so every
row in a batch gets a different value, avoiding duplicate-key collisions that a
NOT EXISTS loop cannot prevent due to the statement-level snapshot in READ
COMMITTED. ores_utility_whimsical_codename_fn turns each value into a
whimsical name and a four-digit number, such as silent_meadow_4217, so two
different values never give the same codename.
-- Sequence used to generate a unique auto-generated codename. nextval() is -- outside MVCC: it advances even within a multi-row INSERT statement, so every -- row in a batch gets a different value, avoiding duplicate-key collisions that -- a NOT EXISTS loop cannot prevent due to the statement-level snapshot in READ -- COMMITTED. create sequence if not exists ores_refdata_party_codename_seq;
drop sequence if exists ores_refdata_party_codename_seq;
5.5. Codename immutability (update path)
Codename is immutable: restore it from the active row on every update, rather than letting a client-supplied value silently overwrite it.
-- Codename is immutable: restore from active row on every update. select codename into NEW.codename from "ores_refdata_parties_tbl" where tenant_id = NEW.tenant_id and id = NEW.id and valid_to = ores_utility_infinity_timestamp_fn();
5.6. Codename generation and queue provisioning (insert path)
Auto-generates codename (a whimsical name and a four-digit number, from a
globally-unique sequence value) when the client leaves it blank, validates the
final format, and provisions the per-party report-event queue – all only on
first insert, since codename is immutable thereafter (see above).
-- Auto-generate the codename from a sequence value: a whimsical name -- and a four-digit number. The sequence is outside MVCC (nextval -- always advances), so every row in a multi-row INSERT gets a -- distinct value, and distinct values give distinct codenames -- without a NOT EXISTS check that cannot see sibling rows. if NEW.codename = '' or NEW.codename is null then NEW.codename := ores_utility_whimsical_codename_fn( nextval('ores_refdata_party_codename_seq')); end if; -- Validate the final codename. if NEW.codename !~ '^[a-z][a-z_]+_[0-9]{4}$' then raise exception 'codename must match ^[a-z][a-z_]+_[0-9]{4}$ got: %', NEW.codename using errcode = '23514'; end if; -- Provision the per-party report event queue. perform ores_mq_queues_create_fn( NEW.tenant_id, NEW.id, 'party', 'task', 'report_events', 'Per-party report scheduling queue', current_user);
6. Messages
The composite_as_of pair, declared here so both the header and its
TypeScript twin render it.
6.1. get_party_composite_as_of_request
/** * @brief Reads a party as it stood at a specific version, together with its * identifiers and contact information as they stood during that same * version's [valid_from, valid_to) window. See the "Temporal composite * entity versioning" architecture doc. */
6.1.1. id
6.1.2. version
6.2. get_party_composite_as_of_response
6.2.1. success
6.2.2. message
6.2.3. party
6.2.4. identifiers
6.2.5. contacts
6.3. put_party_composite_request
/** * @brief Writes a party and the children a confirmed review staged, in one * transaction. A refusal or a failure leaves none of them behind. */
6.3.1. intent
6.3.2. party
6.3.3. identifiers
6.3.4. contacts
6.4. put_party_composite_response
6.4.1. result
6.4.2. party
7. C++
Everything below is consumed by the C++ codegen pipeline only.
7.1. Flags
7.2. Repository
Reads are scoped to the caller's tenant, as for every tenant-scoped entity, the system tenant included. IAM's party cache, which names parties in the sign-in chooser, reads each tenant's partition inside that tenant with a token it issues itself, so it needs no wider read.
The scope sits in the code as well as in the policy: the list read and the count name the caller's tenant in the query. A read that must cross tenants therefore changes the query and the policy, not the policy alone.
7.3. Domain includes
#include <string> #include <optional> #include <boost/uuid/uuid.hpp>
7.4. Entity includes
#include <string> #include <optional>
7.5. Conventions
7.6. Table display
| column | header |
|---|---|
| short_code | Code |
| full_name | Name |
| party_type | Type |
| status | Status |
| business_center_code | Business Center |
| modified_by | Modified By |
| version | Version |
7.7. Presentation
7.7.1. Detail fields
| field | label | widget | type | is_key | is_required | placeholder | flag_source | combo_allow_blank | combo_fetch | combo_domain_type | combo_display_field | 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 | combo_blank_label |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| short_code | Short Code | codeEdit | line_edit | true | true | Enter short code | |||||||||||||||
| full_name | Full Name | nameEdit | line_edit | false | true | Enter full name | |||||||||||||||
| party_type | Party Type | partyTypeCombo | dynamic_combo | false | refdata::domain::party_type | fetch_party_types | partyTypeWatcher | code | description | display_order | party types | PartyTypeCombo | party_type | ||||||||
| status | Status | statusCombo | dynamic_combo | false | refdata::domain::party_status | fetch_party_statuses | partyStatusWatcher | code | description | display_order | party statuses | PartyStatusCombo | party_status | ||||||||
| business_center_code | Business Center | businessCenterCombo | flagged_combo | false | business_centre | false | fetch_business_centre_codes | ||||||||||||||
| parent_party_id | Parent Party | parentPartyCombo | dynamic_combo | false | true | refdata::domain::party | full_name | fetch_parties | parentPartyWatcher | id | short_code | version | parties | ParentPartyCombo | No Parent |
7.7.2. Columns
| enum_name | field | header | type | width | is_badge | badge_key | formatter | formatter_include |
|---|---|---|---|---|---|---|---|---|
| ShortCode | short_code | Code | string | 120 | ||||
| FullName | full_name | Name | string | 250 | ||||
| PartyType | party_type | Type | string | 120 | true | party_type | ||
| Status | status | Status | string | 100 | true | party_status | ||
| BusinessCenterCode | business_center_code | Business Center | string | 130 | ||||
| Version | version | Version | int | 80 | ||||
| ModifiedBy | modified_by | Modified By | string | 120 | ||||
| RecordedAt | recorded_at | Recorded At | timestamp | 150 |
7.7.3. Icon columns
Business center gets its own flag icon, same as any other single-business-centre cell (e.g. Book's rates centre code column).
| column | accessor | field1 | field2 |
|---|---|---|---|
| BusinessCenterCode | business_centre_flag_icon | business_center_code |
7.8. Custom repository methods
The two methods below are beyond the standard repository template because
they need raw SQL access: read_system_party (calls a stored procedure)
and read_descendants (a recursive CTE over the hierarchy). The
paginated read_latest(offset, limit) and get_total_party_count() used
to be hand-maintained here too, but are now covered by the generic
:service_pagination: true facet — removed to avoid duplicate
declarations. The codegen mechanism injects the remaining two at
template-rendered markers.
7.8.1. read_system_party
Calls ores_refdata_read_system_party_fn stored procedure. Every tenant has
exactly one system party (party_category'System'=) which serves as the
root of the party hierarchy.
std::vector<domain::party> read_system_party(context ctx, const std::string& tenant_id);
#include <array> #include <boost/uuid/uuid_io.hpp> #include <boost/lexical_cast.hpp> #include "ores.utility/uuid/tenant_id.hpp"
std::vector<domain::party> party_repository::read_system_party(context ctx, const std::string& tenant_id) { BOOST_LOG_SEV(lg(), debug) << "Reading system party for tenant: " << tenant_id; const std::string sql = "SELECT * FROM ores_refdata_read_system_party_fn('" + tenant_id + "'::uuid)"; const auto rows = query_builder::execute_raw_multi_column_query(ctx, sql, lg(), "Reading system party by tenant"); std::vector<domain::party> result; result.reserve(rows.size()); static constexpr std::array required_columns = {0, 1, 2, 3, 4, 5, 6, 9, 10, 11, 12, 13, 14}; for (const auto& row : rows) { if (row.size() >= 16 && std::ranges::all_of(required_columns, [&row](int i) { return static_cast<bool>(row[i]); })) { domain::party p; p.id = boost::lexical_cast<boost::uuids::uuid>(*row[0]); p.tenant_id = utility::uuid::tenant_id::from_string(*row[1]).value(); p.version = std::stoi(*row[2]); p.full_name = *row[3]; p.short_code = *row[4]; p.party_category = *row[5]; p.party_type = *row[6]; if (row[7]) p.parent_party_id = boost::lexical_cast<boost::uuids::uuid>(*row[7]); if (row[8]) p.business_center_code = *row[8]; p.status = *row[9]; p.modified_by = *row[10]; result.push_back(p); } } return result; }
7.8.2. read_descendants
Traverses the party hierarchy via a recursive CTE starting from root_id.
Returns the root plus every descendant (active records only).
std::vector<boost::uuids::uuid> read_descendants(context ctx, const boost::uuids::uuid& root_id);
#include <boost/uuid/uuid_io.hpp> #include <boost/lexical_cast.hpp>
std::vector<boost::uuids::uuid> party_repository::read_descendants(context ctx, const boost::uuids::uuid& root_id) { BOOST_LOG_SEV(lg(), debug) << "Reading party descendants. Root: " << root_id; const auto id_str = boost::uuids::to_string(root_id); const std::string sql = "WITH RECURSIVE party_tree AS (" " SELECT id FROM ores_refdata_parties_tbl" " WHERE id = '" + id_str + "' AND valid_to = '" + MAX_TIMESTAMP + "'" " UNION ALL" " SELECT p.id FROM ores_refdata_parties_tbl p" " JOIN party_tree pt ON p.parent_party_id = pt.id" " WHERE p.valid_to = '" + MAX_TIMESTAMP + "'" ") SELECT id FROM party_tree"; const auto rows = query_builder::execute_raw_multi_column_query(ctx, sql, lg(), "Reading party descendants"); std::vector<boost::uuids::uuid> result; result.reserve(rows.size()); for (const auto& row : rows) if (!row.empty() && row[0]) result.push_back(boost::lexical_cast<boost::uuids::uuid>(*row[0])); return result; }
7.8.3. read_registration_default
Reads the party flagged is_registration_default in the caller's tenant,
when one is set. At most one live party per tenant may hold the flag, so
this is one row or none. A registration into a tenant with no flagged party
creates a pending account rather than failing.
std::optional<domain::party> read_registration_default(context ctx);
std::optional<domain::party> party_repository::read_registration_default(context ctx) { static constexpr auto max = db_timestamp::max(); const auto tid = ctx.tenant_id().to_string(); const auto query = query_builder::read<std::vector<party_entity>> | query_builder::where("tenant_id"_c == tid && "is_registration_default"_c == true && "valid_to"_c == max) | query_builder::limit(1); const auto rows = query_builder::execute_read_query<party_entity, domain::party>( ctx, query, [](const auto& entities) { return party_mapper::map(entities); }, lg(), "Reading the registration default party."); if (rows.empty()) return std::nullopt; return rows.front(); }
7.9. Custom NATS protocol messages
party declares one message pair beyond the standard CRUD surface and
generic facets, in the top-level * Messages section: composite_as_of
(reads a party as it stood at a specific version, together with its
identifiers and contact information as they stood during that same
version's window — see the "Temporal composite entity versioning"
architecture doc). The header and its TypeScript twin both render it from
there.
#include "ores.refdata.api/domain/party_contact_information.hpp" #include "ores.refdata.api/domain/party_identifier.hpp"
7.10. Custom NATS handler methods
Hand-written handler method for composite_as_of, dispatching to
party_service=/=party_identifier_service=/=party_contact_information_service.
#include "ores.refdata.core/service/party_contact_information_service.hpp" #include "ores.refdata.core/service/party_identifier_service.hpp" #include "ores.database/service/tenant_context.hpp" #include "ores.service/messaging/workflow_helpers.hpp" #include "ores.utility/codec/wire_traits_fwd.hpp" #include <boost/uuid/uuid_io.hpp> #include <chrono> #include "ores.database/repository/unit_of_work.hpp"
save's workflow-step-command branch (below) special-cases orchestration
commands ahead of the generated standard flow: it bypasses JWT auth and
uses the X-Tenant-Id header instead, replays a cached result if the step
already completed (idempotency), and reports completion/failure back to
the workflow engine rather than replying to the caller directly.
using ores::service::messaging::is_workflow_command; using ores::service::messaging::extract_workflow_header; using ores::service::messaging::publish_step_completion; using ores::service::messaging::check_step_idempotency; using ores::workflow::messaging::step_id_header; using ores::workflow::messaging::instance_id_header; using ores::workflow::messaging::tenant_id_header; // Workflow step command: bypass JWT auth; use X-Tenant-Id for context. if (is_workflow_command(msg)) { const auto step_id = extract_workflow_header(msg, step_id_header); const auto inst_id = extract_workflow_header(msg, instance_id_header); const auto tenant_id = extract_workflow_header(msg, tenant_id_header); // Idempotency guard: replay cached result if this step already completed. if (auto cached = check_step_idempotency(nats_, step_id, tenant_id)) { publish_step_completion(nats_, step_id, inst_id, cached->outcome, cached->result_json, cached->error_message, cached->log); return; } auto req = decode<put_party_request>(msg); if (!req) { publish_step_completion(nats_, step_id, inst_id, ores::workflow::messaging::step_outcome::failed, "", "Failed to decode put_party_request"); return; } try { using ores::database::service::tenant_context; auto wf_ctx = tenant_context::with_tenant(ctx_, tenant_id); service::party_service svc(wf_ctx); // The service answers the canonical response, whose result // states the outcome. A refusal is the step failing. const auto resp = svc.put_party(*req); if (resp.result.outcome != ores::utility::domain::outcome::ok) { publish_step_completion(nats_, step_id, inst_id, ores::workflow::messaging::step_outcome::failed, "", resp.result.message); return; } BOOST_LOG_SEV(party_handler_lg(), debug) << "Workflow step completed: " << msg.subject; publish_step_completion(nats_, step_id, inst_id, ores::workflow::messaging::step_outcome::completed, ores::utility::codec::wire_traits<put_party_response>::write_json(resp), ""); } catch (const std::exception& e) { BOOST_LOG_SEV(party_handler_lg(), error) << "Workflow step failed: " << msg.subject << " — " << e.what(); publish_step_completion(nats_, step_id, inst_id, ores::workflow::messaging::step_outcome::failed, "", e.what()); } return; }
void composite_as_of(ores::nats::message msg) { BOOST_LOG_SEV(party_handler_lg(), debug) << "Handling " << msg.subject; auto ctx_expected = ores::service::service::make_request_context(ctx_, msg, verifier_); if (!ctx_expected) { error_reply(nats_, msg, ctx_expected.error()); return; } const auto& ctx = *ctx_expected; if (!has_permission(ctx, "refdata::parties:read")) { error_reply(nats_, msg, ores::service::error_code::forbidden); return; } auto req = decode<get_party_composite_as_of_request>(msg); if (!req) { BOOST_LOG_SEV(party_handler_lg(), warn) << "Failed to decode: " << msg.subject; reply(nats_, msg, get_party_composite_as_of_response{.success = false, .message = "Failed to decode request"}); return; } try { service::party_service party_svc(ctx); const auto version = static_cast<std::uint32_t>(req->version); auto current = party_svc.get_party_at_version(req->id, version); if (!current) { reply(nats_, msg, get_party_composite_as_of_response{.success = false, .message = "No such party version: " + boost::uuids::to_string(req->id) + " v" + std::to_string(version)}); return; } // Windows are contiguous by construction: the next version's // valid_from is this version's valid_to. If there is no next // version, this is the current one — its window is still open, // so bound it with a safely-far-future instant instead (see the // "Temporal composite entity versioning" architecture doc; the // domain object does not surface valid_to directly). auto next = party_svc.get_party_at_version(req->id, version + 1); const auto window_end = next ? next->recorded_at : std::chrono::system_clock::now() + std::chrono::hours(24 * 365 * 100); service::party_identifier_service identifier_svc(ctx); auto identifiers = identifier_svc.list_party_identifiers_by_party_id_as_of( boost::uuids::to_string(req->id), current->recorded_at, window_end); service::party_contact_information_service contact_svc(ctx); auto contacts = contact_svc.list_party_contact_informations_by_party_id_as_of( boost::uuids::to_string(req->id), current->recorded_at, window_end); BOOST_LOG_SEV(party_handler_lg(), debug) << "Completed " << msg.subject; reply(nats_, msg, get_party_composite_as_of_response{.success = true, .party = std::move(*current), .identifiers = std::move(identifiers), .contacts = std::move(contacts)}); } catch (const std::exception& e) { BOOST_LOG_SEV(party_handler_lg(), error) << msg.subject << " failed: " << e.what(); reply(nats_, msg, get_party_composite_as_of_response{.success = false, .message = e.what()}); } } void put_composite(ores::nats::message msg) { BOOST_LOG_SEV(party_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::parties:write")) { error_reply(nats_, msg, ores::service::error_code::forbidden); return; } auto req = decode<put_party_composite_request>(msg); if (!req) { BOOST_LOG_SEV(party_handler_lg(), warn) << "Failed to decode: " << msg.subject; error_reply(nats_, msg, ores::service::error_code::bad_request); return; } try { // One transaction over every row the confirm staged. The unit of // work rolls back when it is destroyed uncommitted, so a refusal // anywhere leaves none of the writes behind. Each service is built // on the unit of work's context, which is what makes its repository // calls join the transaction rather than open their own. ores::database::repository::unit_of_work uow(req_ctx); const auto& uctx = uow.ctx(); const auto reason = req->intent.reason_code.empty() ? std::string(ores::service::messaging::change_reasons::new_record) : req->intent.reason_code; const auto apply_intent = [&](auto& row) { if (row.change_reason_code.empty()) row.change_reason_code = reason; row.change_commentary = req->intent.commentary; }; auto party = req->party; apply_intent(party); service::party_service parties(uctx); parties.save_party(party); auto identifiers = req->identifiers; for (auto& row : identifiers) apply_intent(row); service::party_identifier_service identifier_service(uctx); identifier_service.save_party_identifiers(identifiers); auto contacts = req->contacts; for (auto& row : contacts) apply_intent(row); service::party_contact_information_service contact_service(uctx); contact_service.save_party_contact_informations(contacts); uow.commit(); // Read the row back from the committed transaction, so the reply // carries the version and the audit stamps the store gave it. service::party_service reader(req_ctx); auto written = reader.find_party(party.id); BOOST_LOG_SEV(party_handler_lg(), debug) << "Completed " << msg.subject; reply(nats_, msg, put_party_composite_response{.result = {}, .party = written ? std::move(*written) : std::move(party)}); } catch (const std::exception& e) { BOOST_LOG_SEV(party_handler_lg(), error) << msg.subject << " failed: " << e.what(); put_party_composite_response failure; failure.result.outcome = ores::utility::domain::outcome::failed; failure.result.code = "internal_error"; failure.result.message = e.what(); reply(nats_, msg, failure); } }
7.11. Custom NATS subscriptions
Hand-crafted subjects wired into the generated party_registrar.cpp via the
nats-sub-registrar facet's custom-subscription paste point.
subs.push_back(nats.queue_subscribe(
get_party_composite_as_of_request::nats_subject, party_queue_group, [h](ores::nats::message msg) {
h->composite_as_of(std::move(msg));
}));
subs.push_back(nats.queue_subscribe(
put_party_composite_request::nats_subject, party_queue_group, [h](ores::nats::message msg) {
h->put_composite(std::move(msg));
}));
7.12. Cache aux index
The generated party_cache (nats-event-cache facet, cached_by:
iam) carries a parent/child aux index alongside the plain
tenant/id → party map, so IAM can answer "which parties are visible
from this root" without a DB round-trip. See the cache_aux_type flag
under * C++ / ** Flags and the facet doc's "Optional aux index"
section.
using children_map = immer::map<boost::uuids::uuid, std::vector<boost::uuids::uuid>, key_hash>;
auto children_t = children_map{}.transient(); for (const auto& [id, p] : entries) { if (p.parent_party_id) { const auto* existing = children_t.find(*p.parent_party_id); auto siblings = existing ? *existing : std::vector<boost::uuids::uuid>{}; siblings.push_back(id); children_t.set(*p.parent_party_id, std::move(siblings)); } } auto aux = children_t.persistent();
std::vector<boost::uuids::uuid> compute_visible_party_ids(const std::string& tenant_id, const boost::uuids::uuid& root_id) const { const auto snap = cache_.snapshot(tenant_id); if (!snap) return {root_id}; std::vector<boost::uuids::uuid> result; std::vector<boost::uuids::uuid> stack{root_id}; while (!stack.empty()) { const auto node = stack.back(); stack.pop_back(); result.push_back(node); const auto* siblings = snap->aux.find(node); if (siblings) for (const auto& child : *siblings) stack.push_back(child); } return result; }
8. See also
- ores.refdata — component group overview.