From 7f93865f7dbec36fa771b70d7ae88d1a954a5ad8 Mon Sep 17 00:00:00 2001 From: Priec Date: Wed, 5 Aug 2026 12:07:04 +0200 Subject: [PATCH] ACCOUNTING_TRANSFER --- Cargo.lock | 2 + common/proto/accounting.proto | 51 ++++++ common/proto/table_definition.proto | 3 + common/src/proto/descriptor.bin | Bin 141228 -> 145579 bytes common/src/proto/komp_ac.accounting.rs | 169 +++++++++++++++++++ common/src/proto/komp_ac.table_definition.rs | 3 + server | 2 +- 7 files changed, 229 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index f6504e7b..e39183a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8766,6 +8766,8 @@ dependencies = [ "askama", "axum", "axum-extra", + "isocountry", + "jiff", "prost", "prost-types", "rusty-money", diff --git a/common/proto/accounting.proto b/common/proto/accounting.proto index 4003240d..534324ba 100644 --- a/common/proto/accounting.proto +++ b/common/proto/accounting.proto @@ -57,6 +57,13 @@ service Accounting { returns (ListAccountingPeriodsResponse); rpc ListPeriodBalances(ListPeriodBalancesRequest) returns (ListPeriodBalancesResponse); + // Materialize one row per directly transferable source account in a table + // containing an ACCOUNTING_TRANSFER definition. Source fields are frozen + // accounting facts; target fields start as an unchanged same-path copy and + // may then be changed by the table's Steel logic or by the user. + rpc GenerateAccountingTransfer(GenerateAccountingTransferRequest) + returns (GenerateAccountingTransferResponse); + // Administratively map one account in a predecessor profile to one account // in the current profile. Existing activity on the target account is allowed. rpc MapOpeningBalanceAccount(MapOpeningBalanceAccountRequest) @@ -382,8 +389,35 @@ message PeriodBalance { string closing_balance = 7; } +message PeriodDenominationBalance { + int64 period_id = 1; + // Exact denominated account. Unlike book balances, this quantity is not + // rolled up into parent accounts with a different denomination. + repeated string account_segments = 2; + string denomination_currency = 3; + // Signed nets use debit-positive convention and remain in the denomination + // currency without conversion during carry-forward. + string opening_quantity = 4; + string period_debit_quantity = 5; + string period_credit_quantity = 6; + string closing_quantity = 7; +} + message ListPeriodBalancesResponse { repeated PeriodBalance balances = 1; + repeated PeriodDenominationBalance denomination_balances = 2; +} + +message GenerateAccountingTransferRequest { + string profile_name = 1; + string table_name = 2; + int64 source_period_id = 3; + int64 target_period_id = 4; +} + +message GenerateAccountingTransferResponse { + int64 table_definition_id = 1; + int64 generated_row_count = 2; } message MapOpeningBalanceAccountRequest { @@ -431,6 +465,11 @@ message OpeningBalanceAccountMapping { OpeningBalanceStatus status = 12; string created_at = 13; string created_by_user_id = 14; + string denomination_currency = 15; + string denomination_opening_quantity = 16; + string denomination_period_debit_quantity = 17; + string denomination_period_credit_quantity = 18; + string denomination_current_quantity = 19; } // A portion of a predecessor account's value that no mapping carries into the @@ -448,6 +487,16 @@ message UnmappedSourceBalance { OpeningBalanceStatus status = 6; } +message UnmappedSourceDenominationBalance { + int64 source_period_id = 1; + string source_profile_name = 2; + repeated string source_account_segments = 3; + string denomination_currency = 4; + // Exact foreign-currency quantity that no mapping carries forward. + string unmapped_quantity = 5; + OpeningBalanceStatus status = 6; +} + message ListOpeningBalanceAccountsResponse { repeated OpeningBalanceAccountMapping mappings = 1; @@ -456,4 +505,6 @@ message ListOpeningBalanceAccountsResponse { // mapping only, which happens when the predecessor belongs to another // profile or when the period has at least one mapping of its own. repeated UnmappedSourceBalance unmapped_source_balances = 2; + repeated UnmappedSourceDenominationBalance + unmapped_source_denomination_balances = 3; } diff --git a/common/proto/table_definition.proto b/common/proto/table_definition.proto index 38197ffd..8df16a08 100644 --- a/common/proto/table_definition.proto +++ b/common/proto/table_definition.proto @@ -174,6 +174,9 @@ message ColumnDefinition { // account always selects a row from the profile's managed accounts table; // name is limited to 10 characters and one stored row contributes // one line to the profile journal) + // ACCOUNTING_TRANSFER (creates automatic source-period/account/book/ + // denomination fields and writable target equivalents; one + // table may contain only one accounting transfer definition) // INT // BIGINT // DATE diff --git a/common/src/proto/descriptor.bin b/common/src/proto/descriptor.bin index 0f01c03114bb7ddbca49fec543892650a39fab10..0cb612c9a2d95ccf021c598f5c643da0d0ed808b 100644 GIT binary patch delta 19376 zcmaK!d0)&HNFd-EjE69UO4AcXA!Ap~u3?Ot02+=WtHTWm2v)L2X~0sGpw{;mpj zsVhFMk1G~jDvMjuYSmJAQK`6tR+Or^QpKg4V*7s1%$!_mfA7m*`QAC-GiT16IWu?e zd04rwX2l0JadF|9_SX2Kfde<#;^3+6-5p)CC$)EWo!HUYHm{?rvwXpTsf!lunI_|k z^<8t@J3Bg$n{m?ow$6DS^G+EQmrP4xTEFo8JK=G%dvU;Ar)b9P_L&{?GWjYe-`_V* zlD1yXp2dNIGpoCO_V%Q_kS0xQ^vx4pYcCC}X3gp9`8TVoy;b44ijMldDgR>N^pVNT z;(*!P4RFTHwmEH`v)X$G*;3R}yL)SS(dYq_|HBUxywtSD?YZG$#;;>i8mJKMY4 z=C$uLYgX5M{L^{dVcl(=J;%0pSMIAa2bCKJH|`u^S~RV1PuKkJS?yC@CkM_>T8j1a z+PaTxpEo18pkwx+xP0rN0nH0dan%2=cUpVT+^)`^_R8;$F}v+DzS#e`V8ZMf-CaML z;mlxE(q%($)3mN1O%(f9Z5{Z$iC?KM4!yW#z^YdUb=mUQOq1!i=&GhPnNS?I{Wxd* z$IRC|9}n4H=-WOz)ZfX$#ZlWg>)1XQ5)Dq#i0vh(rosE$$H(O2j@!$qw$BGk;?$M4 z7xixbT-q6LFZj9Xiior?|ggXrfMMZGqt_VPYMC&sVZ;;8brv7^e%hpyT^IolM+ zsMri2L#(iDt!AMdxlsOM`J_@~dDmfmciG+~jpR|P>OZYsb@;Hc*4~t?`hO!#_3(2| zvFU#g!t%(m1OKn)?~EPxe>AT;xp|o>o_~WWRwq@t+-K!+EhEiAs}62?sgN$W#W;Cw}|zDTf_6W&at6P1|S6q5JJSjR;DQ zML<&E_>%Wf>DZ3;IkS68ZQbpqW4pV4+}>GK<)Y}=wpsIfzD^Ial2x_`IIq+Osk1b{ zbJp=~oyWD$F7>pX*xov~ZQk)1GWQhPW*18*wqgG9?VY8W?IkCdA~@4M^Hz`TDIGem zy?st;PSl;M-@J~K+i@iET%Nr9Icb8rN%`SHL4vwTBfhB!b^G?)86?xxKZ*CV z`|lCj`or6=*Npz~_UkpHKfL`h!}A)G_yBvL@*3eC(93H~As*06Y=n3q#A4CLxg|oHoV9dAiZPov~pq3yVGgCy;|g_6`T)60QqSnN4Y_mBtAU% zeLo0;)ZsP#oHa%UafI#XIt0-D5w=Ga;tBeYBp9z+l=(}M>=!o!0yea2$*(RRmRR}Agw-p*raM+ZkD2<>R^ z$SR^?MsAjKWR*n2jH-InrE!%DhhzM_3ZU&69~^=h=rO)y5KV1%uyicIp+-drw(T%T zETj!EvsZ`EW_o8yle7_1OH9#L;qdsl+z-o#eR)ZGT-6ZAb~Agtl|@rk1%Q3LZB_*P z_;EW0L7F=3=+GqgbcA6rl?Zl>+A#^LV6fPGTv6a@REu`R_>4BO3oPkG^2M-S_Y(2mH)?N$uI^VFg zd?#_9ho)9dWRU=ej?`8(oNow$m0kk|UR+-G&6~zwoON0Sq>GKs2cc>>i%s=Fg>V*| z!IDR1E$5e*+zm{9EeTz`u*B31qA89eLNs1#><*3x!X99qLS&bkzKKG#E;R!N7S9vo zqbdo2>≪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 delta 15732 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, + #[prost(string, tag = "3")] + pub denomination_currency: ::prost::alloc::string::String, + /// Signed nets use debit-positive convention and remain in the denomination + /// currency without conversion during carry-forward. + #[prost(string, tag = "4")] + pub opening_quantity: ::prost::alloc::string::String, + #[prost(string, tag = "5")] + pub period_debit_quantity: ::prost::alloc::string::String, + #[prost(string, tag = "6")] + pub period_credit_quantity: ::prost::alloc::string::String, + #[prost(string, tag = "7")] + pub closing_quantity: ::prost::alloc::string::String, +} #[derive(Clone, PartialEq, ::prost::Message)] pub struct ListPeriodBalancesResponse { #[prost(message, repeated, tag = "1")] pub balances: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "2")] + pub denomination_balances: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct GenerateAccountingTransferRequest { + #[prost(string, tag = "1")] + pub profile_name: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub table_name: ::prost::alloc::string::String, + #[prost(int64, tag = "3")] + pub source_period_id: i64, + #[prost(int64, tag = "4")] + pub target_period_id: i64, +} +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] +pub struct GenerateAccountingTransferResponse { + #[prost(int64, tag = "1")] + pub table_definition_id: i64, + #[prost(int64, tag = "2")] + pub generated_row_count: i64, } #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct MapOpeningBalanceAccountRequest { @@ -475,6 +516,16 @@ pub struct OpeningBalanceAccountMapping { pub created_at: ::prost::alloc::string::String, #[prost(string, tag = "14")] pub created_by_user_id: ::prost::alloc::string::String, + #[prost(string, tag = "15")] + pub denomination_currency: ::prost::alloc::string::String, + #[prost(string, tag = "16")] + pub denomination_opening_quantity: ::prost::alloc::string::String, + #[prost(string, tag = "17")] + pub denomination_period_debit_quantity: ::prost::alloc::string::String, + #[prost(string, tag = "18")] + pub denomination_period_credit_quantity: ::prost::alloc::string::String, + #[prost(string, tag = "19")] + pub denomination_current_quantity: ::prost::alloc::string::String, } /// A portion of a predecessor account's value that no mapping carries into the /// target period. Closing the target period drops this value, so each entry must @@ -499,6 +550,24 @@ pub struct UnmappedSourceBalance { #[prost(enumeration = "OpeningBalanceStatus", tag = "6")] pub status: i32, } +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct UnmappedSourceDenominationBalance { + #[prost(int64, tag = "1")] + pub source_period_id: i64, + #[prost(string, tag = "2")] + pub source_profile_name: ::prost::alloc::string::String, + #[prost(string, repeated, tag = "3")] + pub source_account_segments: ::prost::alloc::vec::Vec< + ::prost::alloc::string::String, + >, + #[prost(string, tag = "4")] + pub denomination_currency: ::prost::alloc::string::String, + /// Exact foreign-currency quantity that no mapping carries forward. + #[prost(string, tag = "5")] + pub unmapped_quantity: ::prost::alloc::string::String, + #[prost(enumeration = "OpeningBalanceStatus", tag = "6")] + pub status: i32, +} #[derive(Clone, PartialEq, ::prost::Message)] pub struct ListOpeningBalanceAccountsResponse { #[prost(message, repeated, tag = "1")] @@ -509,6 +578,10 @@ pub struct ListOpeningBalanceAccountsResponse { /// profile or when the period has at least one mapping of its own. #[prost(message, repeated, tag = "2")] pub unmapped_source_balances: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "3")] + pub unmapped_source_denomination_balances: ::prost::alloc::vec::Vec< + UnmappedSourceDenominationBalance, + >, } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] #[repr(i32)] @@ -1205,6 +1278,39 @@ pub mod accounting_client { ); self.inner.unary(req, path, codec).await } + /// Materialize one row per directly transferable source account in a table + /// containing an ACCOUNTING_TRANSFER definition. Source fields are frozen + /// accounting facts; target fields start as an unchanged same-path copy and + /// may then be changed by the table's Steel logic or by the user. + pub async fn generate_accounting_transfer( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic_prost::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/komp_ac.accounting.Accounting/GenerateAccountingTransfer", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new( + "komp_ac.accounting.Accounting", + "GenerateAccountingTransfer", + ), + ); + self.inner.unary(req, path, codec).await + } /// Administratively map one account in a predecessor profile to one account /// in the current profile. Existing activity on the target account is allowed. pub async fn map_opening_balance_account( @@ -1422,6 +1528,17 @@ pub mod accounting_server { tonic::Response, tonic::Status, >; + /// Materialize one row per directly transferable source account in a table + /// containing an ACCOUNTING_TRANSFER definition. Source fields are frozen + /// accounting facts; target fields start as an unchanged same-path copy and + /// may then be changed by the table's Steel logic or by the user. + async fn generate_accounting_transfer( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; /// Administratively map one account in a predecessor profile to one account /// in the current profile. Existing activity on the target account is allowed. async fn map_opening_balance_account( @@ -2308,6 +2425,58 @@ pub mod accounting_server { }; Box::pin(fut) } + "/komp_ac.accounting.Accounting/GenerateAccountingTransfer" => { + #[allow(non_camel_case_types)] + struct GenerateAccountingTransferSvc(pub Arc); + impl< + T: Accounting, + > tonic::server::UnaryService< + super::GenerateAccountingTransferRequest, + > for GenerateAccountingTransferSvc { + type Response = super::GenerateAccountingTransferResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request< + super::GenerateAccountingTransferRequest, + >, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::generate_accounting_transfer( + &inner, + request, + ) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let method = GenerateAccountingTransferSvc(inner); + let codec = tonic_prost::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } "/komp_ac.accounting.Accounting/MapOpeningBalanceAccount" => { #[allow(non_camel_case_types)] struct MapOpeningBalanceAccountSvc(pub Arc); diff --git a/common/src/proto/komp_ac.table_definition.rs b/common/src/proto/komp_ac.table_definition.rs index a0900e5a..2500097c 100644 --- a/common/src/proto/komp_ac.table_definition.rs +++ b/common/src/proto/komp_ac.table_definition.rs @@ -144,6 +144,9 @@ pub struct ColumnDefinition { /// account always selects a row from the profile's managed accounts table; /// name is limited to 10 characters and one stored row contributes /// one line to the profile journal) + /// ACCOUNTING_TRANSFER (creates automatic source-period/account/book/ + /// denomination fields and writable target equivalents; one + /// table may contain only one accounting transfer definition) /// INT /// BIGINT /// DATE diff --git a/server b/server index 45e95554..5d1a2b21 160000 --- a/server +++ b/server @@ -1 +1 @@ -Subproject commit 45e95554c9a9ba82d6b421bf5599e2a74f966a3e +Subproject commit 5d1a2b2186f3d6099ebc48930d62e5a441aa6ae8