diff --git a/common/proto/ecb.proto b/common/proto/ecb.proto index efea97d..007e49f 100644 --- a/common/proto/ecb.proto +++ b/common/proto/ecb.proto @@ -2,16 +2,62 @@ syntax = "proto3"; package komp_ac.ecb; -// Read-only access to ECB conversion audit evidence. +// Read-only access to ECB conversion previews and audit evidence. // -// This service exists for manual visual verification. It exposes conversions -// already recorded by backend posting workflows and never recalculates money. +// This service exists for manual visual verification before and after posting. +// Preview performs the authoritative unrounded backend calculation without +// writing; evidence exposes conversions already recorded by posting workflows. service EcbService { + // Preview the exact conversion the backend would currently perform. + // This reads local verified ECB data and never persists conversion evidence. + rpc PreviewEcbConversion(PreviewEcbConversionRequest) + returns (PreviewEcbConversionResponse); + // List immutable ECB conversion evidence for one money value. rpc ListEcbConversionEvidence(ListEcbConversionEvidenceRequest) returns (ListEcbConversionEvidenceResponse); } +// Accounting date rule used to select an ECB publication. +enum EcbConversionContext { + ECB_CONVERSION_CONTEXT_UNSPECIFIED = 0; + ECB_CONVERSION_CONTEXT_ORDINARY_TRANSACTION = 1; + ECB_CONVERSION_CONTEXT_FINANCIAL_STATEMENT = 2; + ECB_CONVERSION_CONTEXT_DECISIVE_DATE = 3; +} + +// Exact inputs for a conversion preview. Decimal values are strings so the +// client never loses precision through binary floating point. +message PreviewEcbConversionRequest { + string original_amount = 1; + string original_currency = 2; + EcbConversionContext conversion_context = 3; + + // ISO calendar date (YYYY-MM-DD). Its meaning is selected by + // conversion_context: transaction, statement, or decisive date. + string anchor_date = 4; +} + +// The conversion and immutable local ECB observation that would be used now. +// No rounding is performed and no conversion evidence is persisted. +message PreviewEcbConversionResponse { + string original_amount = 1; + string original_currency = 2; + string eur_amount = 3; + EcbConversionContext conversion_context = 4; + string anchor_date = 5; + string determination_method = 6; + string rounding_method = 7; + optional string rate_date = 8; + optional string units_per_eur = 9; + optional int64 rate_observation_id = 10; + optional string observation_hash = 11; + optional string source_payload_hash = 12; + optional string rate_fetched_at = 13; + optional int64 import_batch_id = 14; + optional string source_endpoint = 15; +} + // Identify one logical money cell whose conversion history should be inspected. message ListEcbConversionEvidenceRequest { // Required. Profile containing the table. diff --git a/common/src/proto/descriptor.bin b/common/src/proto/descriptor.bin index 13e1018..13766f4 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.ecb.rs b/common/src/proto/komp_ac.ecb.rs index 4b08e82..11e2913 100644 --- a/common/src/proto/komp_ac.ecb.rs +++ b/common/src/proto/komp_ac.ecb.rs @@ -1,4 +1,54 @@ // This file is @generated by prost-build. +/// Exact inputs for a conversion preview. Decimal values are strings so the +/// client never loses precision through binary floating point. +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct PreviewEcbConversionRequest { + #[prost(string, tag = "1")] + pub original_amount: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub original_currency: ::prost::alloc::string::String, + #[prost(enumeration = "EcbConversionContext", tag = "3")] + pub conversion_context: i32, + /// ISO calendar date (YYYY-MM-DD). Its meaning is selected by + /// conversion_context: transaction, statement, or decisive date. + #[prost(string, tag = "4")] + pub anchor_date: ::prost::alloc::string::String, +} +/// The conversion and immutable local ECB observation that would be used now. +/// No rounding is performed and no conversion evidence is persisted. +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct PreviewEcbConversionResponse { + #[prost(string, tag = "1")] + pub original_amount: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub original_currency: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub eur_amount: ::prost::alloc::string::String, + #[prost(enumeration = "EcbConversionContext", tag = "4")] + pub conversion_context: i32, + #[prost(string, tag = "5")] + pub anchor_date: ::prost::alloc::string::String, + #[prost(string, tag = "6")] + pub determination_method: ::prost::alloc::string::String, + #[prost(string, tag = "7")] + pub rounding_method: ::prost::alloc::string::String, + #[prost(string, optional, tag = "8")] + pub rate_date: ::core::option::Option<::prost::alloc::string::String>, + #[prost(string, optional, tag = "9")] + pub units_per_eur: ::core::option::Option<::prost::alloc::string::String>, + #[prost(int64, optional, tag = "10")] + pub rate_observation_id: ::core::option::Option, + #[prost(string, optional, tag = "11")] + pub observation_hash: ::core::option::Option<::prost::alloc::string::String>, + #[prost(string, optional, tag = "12")] + pub source_payload_hash: ::core::option::Option<::prost::alloc::string::String>, + #[prost(string, optional, tag = "13")] + pub rate_fetched_at: ::core::option::Option<::prost::alloc::string::String>, + #[prost(int64, optional, tag = "14")] + pub import_batch_id: ::core::option::Option, + #[prost(string, optional, tag = "15")] + pub source_endpoint: ::core::option::Option<::prost::alloc::string::String>, +} /// Identify one logical money cell whose conversion history should be inspected. #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct ListEcbConversionEvidenceRequest { @@ -72,6 +122,43 @@ pub struct ListEcbConversionEvidenceResponse { #[prost(bool, tag = "3")] pub has_more: bool, } +/// Accounting date rule used to select an ECB publication. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum EcbConversionContext { + Unspecified = 0, + OrdinaryTransaction = 1, + FinancialStatement = 2, + DecisiveDate = 3, +} +impl EcbConversionContext { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unspecified => "ECB_CONVERSION_CONTEXT_UNSPECIFIED", + Self::OrdinaryTransaction => "ECB_CONVERSION_CONTEXT_ORDINARY_TRANSACTION", + Self::FinancialStatement => "ECB_CONVERSION_CONTEXT_FINANCIAL_STATEMENT", + Self::DecisiveDate => "ECB_CONVERSION_CONTEXT_DECISIVE_DATE", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "ECB_CONVERSION_CONTEXT_UNSPECIFIED" => Some(Self::Unspecified), + "ECB_CONVERSION_CONTEXT_ORDINARY_TRANSACTION" => { + Some(Self::OrdinaryTransaction) + } + "ECB_CONVERSION_CONTEXT_FINANCIAL_STATEMENT" => { + Some(Self::FinancialStatement) + } + "ECB_CONVERSION_CONTEXT_DECISIVE_DATE" => Some(Self::DecisiveDate), + _ => None, + } + } +} /// Generated client implementations. pub mod ecb_service_client { #![allow( @@ -83,10 +170,11 @@ pub mod ecb_service_client { )] use tonic::codegen::*; use tonic::codegen::http::Uri; - /// Read-only access to ECB conversion audit evidence. + /// Read-only access to ECB conversion previews and audit evidence. /// - /// This service exists for manual visual verification. It exposes conversions - /// already recorded by backend posting workflows and never recalculates money. + /// This service exists for manual visual verification before and after posting. + /// Preview performs the authoritative unrounded backend calculation without + /// writing; evidence exposes conversions already recorded by posting workflows. #[derive(Debug, Clone)] pub struct EcbServiceClient { inner: tonic::client::Grpc, @@ -167,6 +255,34 @@ pub mod ecb_service_client { self.inner = self.inner.max_encoding_message_size(limit); self } + /// Preview the exact conversion the backend would currently perform. + /// This reads local verified ECB data and never persists conversion evidence. + pub async fn preview_ecb_conversion( + &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.ecb.EcbService/PreviewEcbConversion", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new("komp_ac.ecb.EcbService", "PreviewEcbConversion"), + ); + self.inner.unary(req, path, codec).await + } /// List immutable ECB conversion evidence for one money value. pub async fn list_ecb_conversion_evidence( &mut self, @@ -212,6 +328,15 @@ pub mod ecb_service_server { /// Generated trait containing gRPC methods that should be implemented for use with EcbServiceServer. #[async_trait] pub trait EcbService: std::marker::Send + std::marker::Sync + 'static { + /// Preview the exact conversion the backend would currently perform. + /// This reads local verified ECB data and never persists conversion evidence. + async fn preview_ecb_conversion( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; /// List immutable ECB conversion evidence for one money value. async fn list_ecb_conversion_evidence( &self, @@ -221,10 +346,11 @@ pub mod ecb_service_server { tonic::Status, >; } - /// Read-only access to ECB conversion audit evidence. + /// Read-only access to ECB conversion previews and audit evidence. /// - /// This service exists for manual visual verification. It exposes conversions - /// already recorded by backend posting workflows and never recalculates money. + /// This service exists for manual visual verification before and after posting. + /// Preview performs the authoritative unrounded backend calculation without + /// writing; evidence exposes conversions already recorded by posting workflows. #[derive(Debug)] pub struct EcbServiceServer { inner: Arc, @@ -301,6 +427,52 @@ pub mod ecb_service_server { } fn call(&mut self, req: http::Request) -> Self::Future { match req.uri().path() { + "/komp_ac.ecb.EcbService/PreviewEcbConversion" => { + #[allow(non_camel_case_types)] + struct PreviewEcbConversionSvc(pub Arc); + impl< + T: EcbService, + > tonic::server::UnaryService + for PreviewEcbConversionSvc { + type Response = super::PreviewEcbConversionResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::preview_ecb_conversion(&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 = PreviewEcbConversionSvc(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.ecb.EcbService/ListEcbConversionEvidence" => { #[allow(non_camel_case_types)] struct ListEcbConversionEvidenceSvc(pub Arc); diff --git a/server b/server index fd73fe9..b0c7082 160000 --- a/server +++ b/server @@ -1 +1 @@ -Subproject commit fd73fe9b2a1bb449addafa20e5c116127939b99f +Subproject commit b0c708244bec8d9673e1159e1051ae21447b860a