From 8dacb981bfc52128106e8fedf81d0b1b0ec7c150 Mon Sep 17 00:00:00 2001 From: Filipriec Date: Sun, 23 Aug 2026 22:28:53 +0200 Subject: [PATCH] conversions7 --- client | 2 +- common/proto/exchange_rates.proto | 18 +++++-- common/proto/table_definition.proto | 4 +- common/src/proto/descriptor.bin | Bin 188295 -> 189229 bytes common/src/proto/komp_ac.exchange_rates.rs | 53 ++++++++++++------- common/src/proto/komp_ac.table_definition.rs | 4 +- server | 2 +- web/locales/cs/main.ftl | 4 +- web/locales/en/main.ftl | 4 +- web/locales/sk/main.ftl | 4 +- web/src/authz.rs | 7 ++- web/src/pages/admin/admin/state.rs | 2 +- web/src/pages/permissions/grants/loader.rs | 4 +- web/src/pages/permissions/grants/state.rs | 4 +- web/src/pages/permissions/roles/state.rs | 10 ++-- 15 files changed, 75 insertions(+), 47 deletions(-) diff --git a/client b/client index b36ef1de..9dbcd82f 160000 --- a/client +++ b/client @@ -1 +1 @@ -Subproject commit b36ef1decba9a4ed5e965938372ae72036b5566c +Subproject commit 9dbcd82f37a22f94fb7dce29dba49f13eaa8e530 diff --git a/common/proto/exchange_rates.proto b/common/proto/exchange_rates.proto index 07589265..bd9c82d8 100644 --- a/common/proto/exchange_rates.proto +++ b/common/proto/exchange_rates.proto @@ -22,6 +22,10 @@ message ExchangeRateSelection { optional string specific_rate_date = 3; optional string manual_foreign_units = 4; string reason = 5; + // Omit to use the profile's current default source for the currency. + // An explicit different source must be enabled for the same direct pair and + // is recorded as an audited per-event override. + optional string rate_source_id = 6; } service ExchangeRateService { @@ -31,7 +35,7 @@ service ExchangeRateService { returns (ListConversionEvidenceResponse); rpc GetProfileExchangeRateSettings(GetProfileExchangeRateSettingsRequest) returns (ProfileExchangeRateSettings); - rpc AddProfileForeignCurrency(AddProfileForeignCurrencyRequest) + rpc AddProfileCurrencySource(AddProfileCurrencySourceRequest) returns (ProfileForeignCurrency); } @@ -121,6 +125,12 @@ message GetProfileExchangeRateSettingsRequest { string profile_name = 1; } message ProfileForeignCurrency { string currency = 1; + string default_source_id = 2; + repeated ProfileCurrencySource sources = 3; +} + +message ProfileCurrencySource { + string source_id = 1; bool coverage_complete = 2; optional string verified_from_date = 3; optional string verified_through_date = 4; @@ -129,11 +139,13 @@ message ProfileForeignCurrency { message ProfileExchangeRateSettings { string profile_name = 1; string accounting_currency = 2; - string rate_source_id = 3; + repeated string enabled_source_ids = 3; repeated ProfileForeignCurrency foreign_currencies = 4; } -message AddProfileForeignCurrencyRequest { +message AddProfileCurrencySourceRequest { string profile_name = 1; string currency = 2; + string source_id = 3; + bool make_default = 4; } diff --git a/common/proto/table_definition.proto b/common/proto/table_definition.proto index 59f52604..f0c79d1a 100644 --- a/common/proto/table_definition.proto +++ b/common/proto/table_definition.proto @@ -104,8 +104,8 @@ message PostTableDefinitionRequest { // to this one when they reach the ledger. string accounting_currency = 8; - // Compiled direct-rate provider selected when the profile is created. - // The server validates this identifier against its compiled provider registry. + // Initial compiled direct-rate provider. It is enabled for each initial + // foreign currency below; more provider/currency bindings may be appended later. string rate_source_id = 11; // Append-only foreign currencies initially enabled for this profile. diff --git a/common/src/proto/descriptor.bin b/common/src/proto/descriptor.bin index 0b721938652186eacb4ae64e6a3ac4634316b12a..9fe5cba29738ba965001b4c1c70ff994a4dc64f9 100644 GIT binary patch delta 6617 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 delta 5695 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 Nx7GXaxAnO*{|8Ca(c%CA diff --git a/common/src/proto/komp_ac.exchange_rates.rs b/common/src/proto/komp_ac.exchange_rates.rs index 0ea8af54..56ea9ddf 100644 --- a/common/src/proto/komp_ac.exchange_rates.rs +++ b/common/src/proto/komp_ac.exchange_rates.rs @@ -11,6 +11,11 @@ pub struct ExchangeRateSelection { pub manual_foreign_units: ::core::option::Option<::prost::alloc::string::String>, #[prost(string, tag = "5")] pub reason: ::prost::alloc::string::String, + /// Omit to use the profile's current default source for the currency. + /// An explicit different source must be enabled for the same direct pair and + /// is recorded as an audited per-event override. + #[prost(string, optional, tag = "6")] + pub rate_source_id: ::core::option::Option<::prost::alloc::string::String>, } #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct PreviewDirectConversionRequest { @@ -166,10 +171,19 @@ pub struct GetProfileExchangeRateSettingsRequest { #[prost(string, tag = "1")] pub profile_name: ::prost::alloc::string::String, } -#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +#[derive(Clone, PartialEq, ::prost::Message)] pub struct ProfileForeignCurrency { #[prost(string, tag = "1")] pub currency: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub default_source_id: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "3")] + pub sources: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct ProfileCurrencySource { + #[prost(string, tag = "1")] + pub source_id: ::prost::alloc::string::String, #[prost(bool, tag = "2")] pub coverage_complete: bool, #[prost(string, optional, tag = "3")] @@ -183,17 +197,21 @@ pub struct ProfileExchangeRateSettings { pub profile_name: ::prost::alloc::string::String, #[prost(string, tag = "2")] pub accounting_currency: ::prost::alloc::string::String, - #[prost(string, tag = "3")] - pub rate_source_id: ::prost::alloc::string::String, + #[prost(string, repeated, tag = "3")] + pub enabled_source_ids: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, #[prost(message, repeated, tag = "4")] pub foreign_currencies: ::prost::alloc::vec::Vec, } #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] -pub struct AddProfileForeignCurrencyRequest { +pub struct AddProfileCurrencySourceRequest { #[prost(string, tag = "1")] pub profile_name: ::prost::alloc::string::String, #[prost(string, tag = "2")] pub currency: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub source_id: ::prost::alloc::string::String, + #[prost(bool, tag = "4")] + pub make_default: bool, } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] #[repr(i32)] @@ -445,9 +463,9 @@ pub mod exchange_rate_service_client { ); self.inner.unary(req, path, codec).await } - pub async fn add_profile_foreign_currency( + pub async fn add_profile_currency_source( &mut self, - request: impl tonic::IntoRequest, + request: impl tonic::IntoRequest, ) -> std::result::Result< tonic::Response, tonic::Status, @@ -462,14 +480,14 @@ pub mod exchange_rate_service_client { })?; let codec = tonic_prost::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( - "/komp_ac.exchange_rates.ExchangeRateService/AddProfileForeignCurrency", + "/komp_ac.exchange_rates.ExchangeRateService/AddProfileCurrencySource", ); let mut req = request.into_request(); req.extensions_mut() .insert( GrpcMethod::new( "komp_ac.exchange_rates.ExchangeRateService", - "AddProfileForeignCurrency", + "AddProfileCurrencySource", ), ); self.inner.unary(req, path, codec).await @@ -510,9 +528,9 @@ pub mod exchange_rate_service_server { tonic::Response, tonic::Status, >; - async fn add_profile_foreign_currency( + async fn add_profile_currency_source( &self, - request: tonic::Request, + request: tonic::Request, ) -> std::result::Result< tonic::Response, tonic::Status, @@ -750,16 +768,15 @@ pub mod exchange_rate_service_server { }; Box::pin(fut) } - "/komp_ac.exchange_rates.ExchangeRateService/AddProfileForeignCurrency" => { + "/komp_ac.exchange_rates.ExchangeRateService/AddProfileCurrencySource" => { #[allow(non_camel_case_types)] - struct AddProfileForeignCurrencySvc( + struct AddProfileCurrencySourceSvc( pub Arc, ); impl< T: ExchangeRateService, - > tonic::server::UnaryService< - super::AddProfileForeignCurrencyRequest, - > for AddProfileForeignCurrencySvc { + > tonic::server::UnaryService + for AddProfileCurrencySourceSvc { type Response = super::ProfileForeignCurrency; type Future = BoxFuture< tonic::Response, @@ -768,12 +785,12 @@ pub mod exchange_rate_service_server { fn call( &mut self, request: tonic::Request< - super::AddProfileForeignCurrencyRequest, + super::AddProfileCurrencySourceRequest, >, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::add_profile_foreign_currency( + ::add_profile_currency_source( &inner, request, ) @@ -788,7 +805,7 @@ pub mod exchange_rate_service_server { let max_encoding_message_size = self.max_encoding_message_size; let inner = self.inner.clone(); let fut = async move { - let method = AddProfileForeignCurrencySvc(inner); + let method = AddProfileCurrencySourceSvc(inner); let codec = tonic_prost::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) .apply_compression_config( diff --git a/common/src/proto/komp_ac.table_definition.rs b/common/src/proto/komp_ac.table_definition.rs index c4456156..59af8e83 100644 --- a/common/src/proto/komp_ac.table_definition.rs +++ b/common/src/proto/komp_ac.table_definition.rs @@ -51,8 +51,8 @@ pub struct PostTableDefinitionRequest { /// to this one when they reach the ledger. #[prost(string, tag = "8")] pub accounting_currency: ::prost::alloc::string::String, - /// Compiled direct-rate provider selected when the profile is created. - /// The server validates this identifier against its compiled provider registry. + /// Initial compiled direct-rate provider. It is enabled for each initial + /// foreign currency below; more provider/currency bindings may be appended later. #[prost(string, tag = "11")] pub rate_source_id: ::prost::alloc::string::String, /// Append-only foreign currencies initially enabled for this profile. diff --git a/server b/server index d21be00a..0a8cf005 160000 --- a/server +++ b/server @@ -1 +1 @@ -Subproject commit d21be00ac243bd9b16a855f9acab7dec5b2e88af +Subproject commit 0a8cf005d853b760eb295f57c223ea4f27cd42fc diff --git a/web/locales/cs/main.ftl b/web/locales/cs/main.ftl index ce656ed5..9cf8073c 100644 --- a/web/locales/cs/main.ftl +++ b/web/locales/cs/main.ftl @@ -507,7 +507,7 @@ grants-note-all-tables = Každá tabulka v každém profilu včetně těch, kter grants-label-all-journals = Všechny deníky grants-note-all-journals = Účetní deník každého profilu. grants-label-all-rates = Všechny devizové kurzy -grants-note-all-rates = Kurzy ECB importuje server, takže je lze pouze číst. +grants-note-all-rates = Oficiální kurzy importuje server, takže je lze pouze číst. grants-note-profile-tables = Každá tabulka v tomto profilu včetně těch, které přibudou později. grants-label-journal = Deník grants-note-journal = Účetní deník tohoto profilu. @@ -755,7 +755,7 @@ builder-choose-profile = Vyberte profil builder-new-profile = Nový profil… builder-new-profile-name = Název nového profilu builder-accounting-currency = Účetní měna -builder-accounting-currency-hint = Účetnictví může být vedeno v jakékoli měně, pro kterou ECB zveřejňuje referenční kurz. Jiná měna než EUR se v případě potřeby přepočítá přes EUR, protože kurzy ECB jsou vždy vyjádřeny vůči euru. +builder-accounting-currency-hint = Vybraný počáteční zdroj kurzů musí zveřejňovat přímý kurz mezi touto měnou a každou počáteční cizí měnou. Další zdroje a měny lze přidat později. builder-table-name = Název tabulky builder-add-column-heading = Přidat sloupec builder-add-column-button = Přidat sloupec diff --git a/web/locales/en/main.ftl b/web/locales/en/main.ftl index 8ecf2d68..0145edd9 100644 --- a/web/locales/en/main.ftl +++ b/web/locales/en/main.ftl @@ -500,7 +500,7 @@ grants-note-all-tables = Every table in every profile, including ones added late grants-label-all-journals = All journals grants-note-all-journals = Every profile's accounting journal. grants-label-all-rates = All exchange rates -grants-note-all-rates = ECB rates are imported by the server, so they can only be read. +grants-note-all-rates = Official rates are imported by the server, so they can only be read. grants-note-profile-tables = Every table in this profile, including ones added later. grants-label-journal = Journal grants-note-journal = This profile's accounting journal. @@ -743,7 +743,7 @@ builder-choose-profile = Choose a profile builder-new-profile = New profile… builder-new-profile-name = New profile name builder-accounting-currency = Accounting currency -builder-accounting-currency-hint = Books can be kept in any currency ECB publishes a reference rate for. A currency other than EUR is converted through EUR when needed, since ECB rates are always quoted against the euro. +builder-accounting-currency-hint = The selected initial rate source must publish a direct quote between this currency and every initial foreign currency. Additional sources and currencies can be added later. builder-table-name = Table name builder-add-column-heading = Add a column builder-add-column-button = Add column diff --git a/web/locales/sk/main.ftl b/web/locales/sk/main.ftl index 5949c941..480b1656 100644 --- a/web/locales/sk/main.ftl +++ b/web/locales/sk/main.ftl @@ -507,7 +507,7 @@ grants-note-all-tables = Každá tabuľka v každom profile vrátane tých, ktor grants-label-all-journals = Všetky denníky grants-note-all-journals = Účtovný denník každého profilu. grants-label-all-rates = Všetky devízové kurzy -grants-note-all-rates = Kurzy ECB importuje server, takže ich možno len čítať. +grants-note-all-rates = Oficiálne kurzy importuje server, takže ich možno len čítať. grants-note-profile-tables = Každá tabuľka v tomto profile vrátane tých, ktoré pribudnú neskôr. grants-label-journal = Denník grants-note-journal = Účtovný denník tohto profilu. @@ -753,7 +753,7 @@ builder-choose-profile = Vyberte profil builder-new-profile = Nový profil… builder-new-profile-name = Názov nového profilu builder-accounting-currency = Účtovná mena -builder-accounting-currency-hint = Účtovníctvo môže byť vedené v akejkoľvek mene, pre ktorú ECB zverejňuje referenčný kurz. Iná mena ako EUR sa v prípade potreby prepočíta cez EUR, keďže kurzy ECB sú vždy vyjadrené voči euru. +builder-accounting-currency-hint = Vybraný počiatočný zdroj kurzov musí zverejňovať priamy kurz medzi touto menou a každou počiatočnou cudzou menou. Ďalšie zdroje a meny možno pridať neskôr. builder-table-name = Názov tabuľky builder-add-column-heading = Pridať stĺpec builder-add-column-button = Pridať stĺpec diff --git a/web/src/authz.rs b/web/src/authz.rs index 42d43931..fe6ae853 100644 --- a/web/src/authz.rs +++ b/web/src/authz.rs @@ -9,9 +9,8 @@ pub(crate) const STRUCT_USER: &str = "struct:user"; pub(crate) const MANAGE: &str = "manage"; pub(crate) const READ: &str = "read"; -/// Every ECB object, which is what the pipeline status is checked against: -/// one importer feeds every profile, so reading its health is not a -/// per-profile question. +/// Every exchange-rate object. Provider pipeline health is a global rather +/// than per-profile concern. pub(crate) const ALL_EXCHANGE_RATES: &str = "exchange-rates:*"; /// The two bulk-transfer actions, mirroring @@ -43,7 +42,7 @@ pub(crate) fn can_manage(snapshot: &AuthorizationSnapshot, area: &str) -> bool { } /// Whether the caller may see the reference-rate pipeline. Mirrors the -/// server's own check in `server/src/ecb/grpc.rs`. +/// server's exchange-rate authorization check. pub(crate) fn can_read_exchange_rates(snapshot: &AuthorizationSnapshot) -> bool { permits(snapshot, ALL_EXCHANGE_RATES, READ) } diff --git a/web/src/pages/admin/admin/state.rs b/web/src/pages/admin/admin/state.rs index 4b3497fb..4fe30b4d 100644 --- a/web/src/pages/admin/admin/state.rs +++ b/web/src/pages/admin/admin/state.rs @@ -26,7 +26,7 @@ pub(crate) struct AdminPageState { pub can_import: bool, pub can_export: bool, /// Whether the exchange-rate pipeline is visible to this caller. Not a - /// structural area: it is granted through the ECB object, like the + /// structural area: it is granted through the exchange-rate object, like the /// conversions it reports on. pub can_exchange_rates: bool, } diff --git a/web/src/pages/permissions/grants/loader.rs b/web/src/pages/permissions/grants/loader.rs index 58922446..d292ba6c 100644 --- a/web/src/pages/permissions/grants/loader.rs +++ b/web/src/pages/permissions/grants/loader.rs @@ -181,7 +181,7 @@ fn describe(locale: Locale, object: &GrantableObject) -> (String, String) { tr!(locale, "grants-label-all-journals"), tr!(locale, "grants-note-all-journals"), ), - "global_ecb" => ( + "global_exchange_rates" => ( tr!(locale, "grants-label-all-rates"), tr!(locale, "grants-note-all-rates"), ), @@ -193,7 +193,7 @@ fn describe(locale: Locale, object: &GrantableObject) -> (String, String) { tr!(locale, "grants-label-journal"), tr!(locale, "grants-note-journal"), ), - "ecb" => ( + "exchange_rates" => ( tr!(locale, "grants-label-rates"), tr!(locale, "grants-note-rates"), ), diff --git a/web/src/pages/permissions/grants/state.rs b/web/src/pages/permissions/grants/state.rs index e905684e..83a3d1c1 100644 --- a/web/src/pages/permissions/grants/state.rs +++ b/web/src/pages/permissions/grants/state.rs @@ -229,7 +229,7 @@ mod tests { }, ObjectRow { object: "exchange-rates:acme".to_string(), - label: "ECB rates".to_string(), + label: "Exchange rates".to_string(), note: String::new(), wildcard: true, cells: cells(&["read"], &[]), @@ -249,7 +249,7 @@ mod tests { fn a_profile_shortcut_grants_the_wildcards_and_never_an_action_the_server_refuses() { let group = group(); // The table row is covered by data:acme/*, so it is not granted again, - // and ECB rates only ever offer read. + // and official rates only ever offer read. assert_eq!( group.all_pairs(), vec![ diff --git a/web/src/pages/permissions/roles/state.rs b/web/src/pages/permissions/roles/state.rs index d80b12ba..ed6be465 100644 --- a/web/src/pages/permissions/roles/state.rs +++ b/web/src/pages/permissions/roles/state.rs @@ -71,8 +71,8 @@ impl RoleRow { /// /// These are wildcard objects on purpose: they keep covering profiles and /// tables added later, which is what "everything" has to mean for a role -/// created before the data exists. ECB rates are written by the server, so they -/// are readable and nothing more. +/// created before the data exists. Official rates are written by the server, +/// so they are readable and nothing more. pub(crate) fn starter_grants( locale: Locale, access: &str, @@ -110,7 +110,7 @@ mod tests { use super::*; #[test] - fn starter_access_never_hands_out_a_write_on_ecb_rates() { + fn starter_access_never_hands_out_a_write_on_exchange_rates() { let locale = Locale::default(); assert!(starter_grants(locale, "none").unwrap().is_empty()); assert!(starter_grants(locale, "nonsense").is_err()); @@ -121,12 +121,12 @@ mod tests { grants .iter() .all(|(object, action)| *object != "exchange-rates:*" || *action == "read"), - "{level} granted a write on ECB rates" + "{level} granted a write on exchange rates" ); } assert_eq!(starter_grants(locale, "read").unwrap().len(), 3); - // Four row actions on two wildcard objects, ECB read, and the two + // Four row actions on two wildcard objects, rate read, and the two // transfer actions, which apply to tables alone. assert_eq!(starter_grants(locale, "full").unwrap().len(), 11); }