diff --git a/Cargo.lock b/Cargo.lock index 6e02c186..28bea8cc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -207,7 +207,7 @@ dependencies = [ "objc2-foundation", "parking_lot", "percent-encoding", - "windows-sys 0.59.0", + "windows-sys 0.52.0", "x11rb", ] @@ -1161,7 +1161,7 @@ checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" [[package]] name = "client" -version = "0.8.57" +version = "0.8.58" dependencies = [ "anyhow", "async-trait", @@ -1282,7 +1282,7 @@ dependencies = [ [[package]] name = "common" -version = "0.8.57" +version = "0.8.58" dependencies = [ "icu_casemap", "prost", @@ -2574,7 +2574,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2738,7 +2738,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -4248,7 +4248,7 @@ dependencies = [ [[package]] name = "komp-app" -version = "0.8.57" +version = "0.8.58" dependencies = [ "anyhow", "common", @@ -4749,7 +4749,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -5934,7 +5934,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -6618,7 +6618,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -6675,7 +6675,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -6782,7 +6782,7 @@ checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" [[package]] name = "search" -version = "0.8.57" +version = "0.8.58" dependencies = [ "anyhow", "common", @@ -6970,7 +6970,7 @@ dependencies = [ [[package]] name = "server" -version = "0.8.57" +version = "0.8.58" dependencies = [ "anyhow", "bcrypt", @@ -7963,7 +7963,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -8577,7 +8577,7 @@ checksum = "e78122066b0cb818b8afd08f7ed22f7fdbc3e90815035726f0840d0d26c0747a" [[package]] name = "tui-canvas" -version = "0.8.57" +version = "0.8.58" dependencies = [ "anyhow", "arboard", @@ -8608,7 +8608,7 @@ dependencies = [ [[package]] name = "tui-canvas-validation-core" -version = "0.8.57" +version = "0.8.58" dependencies = [ "regex", "serde", @@ -8618,7 +8618,7 @@ dependencies = [ [[package]] name = "tui-pages" -version = "0.8.57" +version = "0.8.58" dependencies = [ "criterion", "crossterm", @@ -8864,7 +8864,7 @@ dependencies = [ [[package]] name = "ux-config" -version = "0.8.57" +version = "0.8.58" dependencies = [ "toml", ] @@ -9338,7 +9338,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] diff --git a/common/proto/table_definition.proto b/common/proto/table_definition.proto index 40f5bf67..a94dc887 100644 --- a/common/proto/table_definition.proto +++ b/common/proto/table_definition.proto @@ -122,6 +122,9 @@ message GetModuleConfigurationRequest { message SetModuleConfigurationRequest { ModuleScope scope = 1; repeated string enabled_modules = 2; + // Initializes an existing profile's bookkeeping currency. Empty leaves it unchanged. + // Global scope and changes to an already configured currency are rejected. + string accounting_currency = 3; } message ModuleConfiguration { @@ -130,6 +133,7 @@ message ModuleConfiguration { repeated string selected_modules = 3; repeated string inherited_modules = 4; repeated string effective_modules = 5; + string accounting_currency = 6; } message CreateCustomExchangeRatesTableRequest { @@ -172,11 +176,11 @@ message PostTableDefinitionRequest { // Empty means the row is identified by its id alone. repeated string row_display_columns = 7; - // ISO-4217 currency the profile keeps its accounting in. A profile is one - // accounting entity and keeps one set of books, so this is required only when - // the request creates the profile, and is ignored afterwards. It is unrelated - // to individual MONEY-column currencies: tables may hold money in any currency, and amounts convert - // to this one when they reach the ledger. + // Default ISO-4217 currency for this table's MONEY columns. Explicit column + // currencies take precedence; when omitted, the profile currency is used. + // The resolved default is saved for columns added later. When this request + // creates a profile, it also initializes that profile's bookkeeping currency. + // Supplying a table default never changes an existing profile's currency. string accounting_currency = 8; // Initial compiled direct-rate provider. It is enabled for each initial @@ -187,7 +191,7 @@ message PostTableDefinitionRequest { repeated string foreign_currencies = 12; // When true, the table is stored once in the global physical schema and is - // visible from every profile. profile_name and accounting_currency are ignored. + // visible from every profile. profile_name is ignored. bool global = 9; // Names for the columns a definition row generates, in place of the ones the @@ -398,8 +402,8 @@ message ColumnDefinition { // from quantity-ledger contributions. bool quantity_ledger = 4; - // Canonical uppercase ISO-4217 currency code. Required for MONEY and forbidden - // for every other field type. + // Canonical uppercase ISO-4217 currency code. MONEY inherits the table/profile + // default when omitted. Forbidden for field types without currency support. string currency = 5; // When true, the server rejects omitted or explicitly null values. diff --git a/common/src/proto/descriptor.bin b/common/src/proto/descriptor.bin index eb6c1214..9312d235 100644 Binary files a/common/src/proto/descriptor.bin and b/common/src/proto/descriptor.bin differ diff --git a/common/src/proto/komp_ac.table_definition.rs b/common/src/proto/komp_ac.table_definition.rs index 4b4b2f93..56c4c31f 100644 --- a/common/src/proto/komp_ac.table_definition.rs +++ b/common/src/proto/komp_ac.table_definition.rs @@ -29,6 +29,10 @@ pub struct SetModuleConfigurationRequest { pub scope: ::core::option::Option, #[prost(string, repeated, tag = "2")] pub enabled_modules: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + /// Initializes an existing profile's bookkeeping currency. Empty leaves it unchanged. + /// Global scope and changes to an already configured currency are rejected. + #[prost(string, tag = "3")] + pub accounting_currency: ::prost::alloc::string::String, } #[derive(serde::Serialize, serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] @@ -43,6 +47,8 @@ pub struct ModuleConfiguration { pub inherited_modules: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, #[prost(string, repeated, tag = "5")] pub effective_modules: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(string, tag = "6")] + pub accounting_currency: ::prost::alloc::string::String, } #[derive(serde::Serialize, serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] @@ -91,11 +97,11 @@ pub struct PostTableDefinitionRequest { /// Empty means the row is identified by its id alone. #[prost(string, repeated, tag = "7")] pub row_display_columns: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, - /// ISO-4217 currency the profile keeps its accounting in. A profile is one - /// accounting entity and keeps one set of books, so this is required only when - /// the request creates the profile, and is ignored afterwards. It is unrelated - /// to individual MONEY-column currencies: tables may hold money in any currency, and amounts convert - /// to this one when they reach the ledger. + /// Default ISO-4217 currency for this table's MONEY columns. Explicit column + /// currencies take precedence; when omitted, the profile currency is used. + /// The resolved default is saved for columns added later. When this request + /// creates a profile, it also initializes that profile's bookkeeping currency. + /// Supplying a table default never changes an existing profile's currency. #[prost(string, tag = "8")] pub accounting_currency: ::prost::alloc::string::String, /// Initial compiled direct-rate provider. It is enabled for each initial @@ -106,7 +112,7 @@ pub struct PostTableDefinitionRequest { #[prost(string, repeated, tag = "12")] pub foreign_currencies: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, /// When true, the table is stored once in the global physical schema and is - /// visible from every profile. profile_name and accounting_currency are ignored. + /// visible from every profile. profile_name is ignored. #[prost(bool, tag = "9")] pub global: bool, /// Names for the columns a definition row generates, in place of the ones the @@ -347,8 +353,8 @@ pub struct ColumnDefinition { /// from quantity-ledger contributions. #[prost(bool, tag = "4")] pub quantity_ledger: bool, - /// Canonical uppercase ISO-4217 currency code. Required for MONEY and forbidden - /// for every other field type. + /// Canonical uppercase ISO-4217 currency code. MONEY inherits the table/profile + /// default when omitted. Forbidden for field types without currency support. #[prost(string, tag = "5")] pub currency: ::prost::alloc::string::String, /// When true, the server rejects omitted or explicitly null values. diff --git a/search/src/lib.rs b/search/src/lib.rs index 315d9370..d4610e04 100644 --- a/search/src/lib.rs +++ b/search/src/lib.rs @@ -59,16 +59,27 @@ impl AccountPathProjection { }); } - let qualified_accounts = format!( - "\"{}\".\"{}\"", - profile_name.replace('"', "\"\""), - LEDGER_ACCOUNTS_TABLE_NAME, - ); + let (schema_id, storage_schema, storage_relation) = sqlx::query_as::<_, (i64, String, String)>( + r#"SELECT profile.id, binding.storage->>'schema', binding.storage->>'relation' + FROM schemas profile + JOIN table_definitions definition ON definition.schema_id = profile.id + JOIN table_storage_bindings binding ON binding.table_definition_id = definition.id + WHERE profile.name = $1 AND definition.table_name = $2 + AND definition.table_kind = 'system' AND definition.deleted = FALSE"#, + ) + .bind(profile_name) + .bind(LEDGER_ACCOUNTS_TABLE_NAME) + .fetch_optional(pool) + .await + .map_err(|error| Status::internal(format!("Account storage lookup failed: {error}")))? + .ok_or_else(|| Status::failed_precondition("Account tracking is not enabled for profile"))?; + let qualified_accounts = qualify_profile_table(&storage_schema, &storage_relation); let paths = sqlx::query_as::<_, (i64, String)>(AssertSqlSafe(format!( "SELECT id, full_path FROM {qualified_accounts} - WHERE deleted = FALSE" + WHERE schema_id = $1 AND deleted = FALSE" ))) + .bind(schema_id) .fetch_all(pool) .await .map_err(|error| Status::internal(format!("Account path lookup failed: {error}")))? diff --git a/server b/server index 77419e36..3c9829e6 160000 --- a/server +++ b/server @@ -1 +1 @@ -Subproject commit 77419e36fcbce3519b0a89c9576162363f43ea60 +Subproject commit 3c9829e6df6e5c7fa6de571b06050081c9663c8d