default accounting currency

This commit is contained in:
Priec
2026-09-12 19:33:21 +02:00
parent 017ca87c67
commit 156d1c4464
6 changed files with 62 additions and 41 deletions

36
Cargo.lock generated
View File

@@ -207,7 +207,7 @@ dependencies = [
"objc2-foundation", "objc2-foundation",
"parking_lot", "parking_lot",
"percent-encoding", "percent-encoding",
"windows-sys 0.59.0", "windows-sys 0.52.0",
"x11rb", "x11rb",
] ]
@@ -1161,7 +1161,7 @@ checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
[[package]] [[package]]
name = "client" name = "client"
version = "0.8.57" version = "0.8.58"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@@ -1282,7 +1282,7 @@ dependencies = [
[[package]] [[package]]
name = "common" name = "common"
version = "0.8.57" version = "0.8.58"
dependencies = [ dependencies = [
"icu_casemap", "icu_casemap",
"prost", "prost",
@@ -2574,7 +2574,7 @@ dependencies = [
"libc", "libc",
"option-ext", "option-ext",
"redox_users", "redox_users",
"windows-sys 0.59.0", "windows-sys 0.61.2",
] ]
[[package]] [[package]]
@@ -2738,7 +2738,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.59.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@@ -4248,7 +4248,7 @@ dependencies = [
[[package]] [[package]]
name = "komp-app" name = "komp-app"
version = "0.8.57" version = "0.8.58"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"common", "common",
@@ -4749,7 +4749,7 @@ version = "0.50.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
dependencies = [ dependencies = [
"windows-sys 0.59.0", "windows-sys 0.61.2",
] ]
[[package]] [[package]]
@@ -5934,7 +5934,7 @@ dependencies = [
"once_cell", "once_cell",
"socket2", "socket2",
"tracing", "tracing",
"windows-sys 0.59.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@@ -6618,7 +6618,7 @@ dependencies = [
"errno", "errno",
"libc", "libc",
"linux-raw-sys", "linux-raw-sys",
"windows-sys 0.59.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@@ -6675,7 +6675,7 @@ dependencies = [
"security-framework", "security-framework",
"security-framework-sys", "security-framework-sys",
"webpki-root-certs", "webpki-root-certs",
"windows-sys 0.59.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@@ -6782,7 +6782,7 @@ checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
[[package]] [[package]]
name = "search" name = "search"
version = "0.8.57" version = "0.8.58"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"common", "common",
@@ -6970,7 +6970,7 @@ dependencies = [
[[package]] [[package]]
name = "server" name = "server"
version = "0.8.57" version = "0.8.58"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bcrypt", "bcrypt",
@@ -7963,7 +7963,7 @@ dependencies = [
"getrandom 0.4.2", "getrandom 0.4.2",
"once_cell", "once_cell",
"rustix", "rustix",
"windows-sys 0.59.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@@ -8577,7 +8577,7 @@ checksum = "e78122066b0cb818b8afd08f7ed22f7fdbc3e90815035726f0840d0d26c0747a"
[[package]] [[package]]
name = "tui-canvas" name = "tui-canvas"
version = "0.8.57" version = "0.8.58"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"arboard", "arboard",
@@ -8608,7 +8608,7 @@ dependencies = [
[[package]] [[package]]
name = "tui-canvas-validation-core" name = "tui-canvas-validation-core"
version = "0.8.57" version = "0.8.58"
dependencies = [ dependencies = [
"regex", "regex",
"serde", "serde",
@@ -8618,7 +8618,7 @@ dependencies = [
[[package]] [[package]]
name = "tui-pages" name = "tui-pages"
version = "0.8.57" version = "0.8.58"
dependencies = [ dependencies = [
"criterion", "criterion",
"crossterm", "crossterm",
@@ -8864,7 +8864,7 @@ dependencies = [
[[package]] [[package]]
name = "ux-config" name = "ux-config"
version = "0.8.57" version = "0.8.58"
dependencies = [ dependencies = [
"toml", "toml",
] ]
@@ -9338,7 +9338,7 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [ dependencies = [
"windows-sys 0.59.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]

View File

@@ -122,6 +122,9 @@ message GetModuleConfigurationRequest {
message SetModuleConfigurationRequest { message SetModuleConfigurationRequest {
ModuleScope scope = 1; ModuleScope scope = 1;
repeated string enabled_modules = 2; 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 { message ModuleConfiguration {
@@ -130,6 +133,7 @@ message ModuleConfiguration {
repeated string selected_modules = 3; repeated string selected_modules = 3;
repeated string inherited_modules = 4; repeated string inherited_modules = 4;
repeated string effective_modules = 5; repeated string effective_modules = 5;
string accounting_currency = 6;
} }
message CreateCustomExchangeRatesTableRequest { message CreateCustomExchangeRatesTableRequest {
@@ -172,11 +176,11 @@ message PostTableDefinitionRequest {
// Empty means the row is identified by its id alone. // Empty means the row is identified by its id alone.
repeated string row_display_columns = 7; repeated string row_display_columns = 7;
// ISO-4217 currency the profile keeps its accounting in. A profile is one // Default ISO-4217 currency for this table's MONEY columns. Explicit column
// accounting entity and keeps one set of books, so this is required only when // currencies take precedence; when omitted, the profile currency is used.
// the request creates the profile, and is ignored afterwards. It is unrelated // The resolved default is saved for columns added later. When this request
// to individual MONEY-column currencies: tables may hold money in any currency, and amounts convert // creates a profile, it also initializes that profile's bookkeeping currency.
// to this one when they reach the ledger. // Supplying a table default never changes an existing profile's currency.
string accounting_currency = 8; string accounting_currency = 8;
// Initial compiled direct-rate provider. It is enabled for each initial // Initial compiled direct-rate provider. It is enabled for each initial
@@ -187,7 +191,7 @@ message PostTableDefinitionRequest {
repeated string foreign_currencies = 12; repeated string foreign_currencies = 12;
// When true, the table is stored once in the global physical schema and is // 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; bool global = 9;
// Names for the columns a definition row generates, in place of the ones the // Names for the columns a definition row generates, in place of the ones the
@@ -398,8 +402,8 @@ message ColumnDefinition {
// from quantity-ledger contributions. // from quantity-ledger contributions.
bool quantity_ledger = 4; bool quantity_ledger = 4;
// Canonical uppercase ISO-4217 currency code. Required for MONEY and forbidden // Canonical uppercase ISO-4217 currency code. MONEY inherits the table/profile
// for every other field type. // default when omitted. Forbidden for field types without currency support.
string currency = 5; string currency = 5;
// When true, the server rejects omitted or explicitly null values. // When true, the server rejects omitted or explicitly null values.

Binary file not shown.

View File

@@ -29,6 +29,10 @@ pub struct SetModuleConfigurationRequest {
pub scope: ::core::option::Option<ModuleScope>, pub scope: ::core::option::Option<ModuleScope>,
#[prost(string, repeated, tag = "2")] #[prost(string, repeated, tag = "2")]
pub enabled_modules: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, 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(serde::Serialize, serde::Deserialize)]
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] #[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>, pub inherited_modules: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
#[prost(string, repeated, tag = "5")] #[prost(string, repeated, tag = "5")]
pub effective_modules: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, 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(serde::Serialize, serde::Deserialize)]
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
@@ -91,11 +97,11 @@ pub struct PostTableDefinitionRequest {
/// Empty means the row is identified by its id alone. /// Empty means the row is identified by its id alone.
#[prost(string, repeated, tag = "7")] #[prost(string, repeated, tag = "7")]
pub row_display_columns: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, 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 /// Default ISO-4217 currency for this table's MONEY columns. Explicit column
/// accounting entity and keeps one set of books, so this is required only when /// currencies take precedence; when omitted, the profile currency is used.
/// the request creates the profile, and is ignored afterwards. It is unrelated /// The resolved default is saved for columns added later. When this request
/// to individual MONEY-column currencies: tables may hold money in any currency, and amounts convert /// creates a profile, it also initializes that profile's bookkeeping currency.
/// to this one when they reach the ledger. /// Supplying a table default never changes an existing profile's currency.
#[prost(string, tag = "8")] #[prost(string, tag = "8")]
pub accounting_currency: ::prost::alloc::string::String, pub accounting_currency: ::prost::alloc::string::String,
/// Initial compiled direct-rate provider. It is enabled for each initial /// Initial compiled direct-rate provider. It is enabled for each initial
@@ -106,7 +112,7 @@ pub struct PostTableDefinitionRequest {
#[prost(string, repeated, tag = "12")] #[prost(string, repeated, tag = "12")]
pub foreign_currencies: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, 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 /// 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")] #[prost(bool, tag = "9")]
pub global: bool, pub global: bool,
/// Names for the columns a definition row generates, in place of the ones the /// 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. /// from quantity-ledger contributions.
#[prost(bool, tag = "4")] #[prost(bool, tag = "4")]
pub quantity_ledger: bool, pub quantity_ledger: bool,
/// Canonical uppercase ISO-4217 currency code. Required for MONEY and forbidden /// Canonical uppercase ISO-4217 currency code. MONEY inherits the table/profile
/// for every other field type. /// default when omitted. Forbidden for field types without currency support.
#[prost(string, tag = "5")] #[prost(string, tag = "5")]
pub currency: ::prost::alloc::string::String, pub currency: ::prost::alloc::string::String,
/// When true, the server rejects omitted or explicitly null values. /// When true, the server rejects omitted or explicitly null values.

View File

@@ -59,16 +59,27 @@ impl AccountPathProjection {
}); });
} }
let qualified_accounts = format!( let (schema_id, storage_schema, storage_relation) = sqlx::query_as::<_, (i64, String, String)>(
"\"{}\".\"{}\"", r#"SELECT profile.id, binding.storage->>'schema', binding.storage->>'relation'
profile_name.replace('"', "\"\""), FROM schemas profile
LEDGER_ACCOUNTS_TABLE_NAME, 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!( let paths = sqlx::query_as::<_, (i64, String)>(AssertSqlSafe(format!(
"SELECT id, full_path "SELECT id, full_path
FROM {qualified_accounts} FROM {qualified_accounts}
WHERE deleted = FALSE" WHERE schema_id = $1 AND deleted = FALSE"
))) )))
.bind(schema_id)
.fetch_all(pool) .fetch_all(pool)
.await .await
.map_err(|error| Status::internal(format!("Account path lookup failed: {error}")))? .map_err(|error| Status::internal(format!("Account path lookup failed: {error}")))?

2
server

Submodule server updated: 77419e36fc...3c9829e6df