endpoint for preview ecb, instead of only commited one

This commit is contained in:
Priec
2026-07-18 15:50:11 +02:00
parent 0c113831ef
commit dd8f94c9ee
4 changed files with 228 additions and 10 deletions

View File

@@ -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.

Binary file not shown.

View File

@@ -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<i64>,
#[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<i64>,
#[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<Self> {
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<T> {
inner: tonic::client::Grpc<T>,
@@ -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<super::PreviewEcbConversionRequest>,
) -> std::result::Result<
tonic::Response<super::PreviewEcbConversionResponse>,
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<super::PreviewEcbConversionRequest>,
) -> std::result::Result<
tonic::Response<super::PreviewEcbConversionResponse>,
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<T> {
inner: Arc<T>,
@@ -301,6 +427,52 @@ pub mod ecb_service_server {
}
fn call(&mut self, req: http::Request<B>) -> Self::Future {
match req.uri().path() {
"/komp_ac.ecb.EcbService/PreviewEcbConversion" => {
#[allow(non_camel_case_types)]
struct PreviewEcbConversionSvc<T: EcbService>(pub Arc<T>);
impl<
T: EcbService,
> tonic::server::UnaryService<super::PreviewEcbConversionRequest>
for PreviewEcbConversionSvc<T> {
type Response = super::PreviewEcbConversionResponse;
type Future = BoxFuture<
tonic::Response<Self::Response>,
tonic::Status,
>;
fn call(
&mut self,
request: tonic::Request<super::PreviewEcbConversionRequest>,
) -> Self::Future {
let inner = Arc::clone(&self.0);
let fut = async move {
<T as EcbService>::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<T: EcbService>(pub Arc<T>);