conversions5

This commit is contained in:
Filipriec
2026-08-23 15:50:03 +02:00
parent af7016e4c7
commit 0c33d1fa5c
20 changed files with 1197 additions and 845 deletions

View File

@@ -233,6 +233,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
"proto/common.proto",
"proto/document_data.proto",
"proto/ecb.proto",
"proto/exchange_rates.proto",
"proto/analytics.proto",
"proto/auth.proto",
"proto/backup.proto",
@@ -259,6 +260,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
"proto/common.proto",
"proto/document_data.proto",
"proto/ecb.proto",
"proto/exchange_rates.proto",
"proto/analytics.proto",
"proto/auth.proto",
"proto/backup.proto",

View File

@@ -1,7 +1,7 @@
syntax = "proto3";
package komp_ac.accounting;
import "ecb.proto";
import "exchange_rates.proto";
// Mutable informational journals. A journal may contain only debits, only
// credits, or any non-zero balance. Balance is reported but never enforced.
@@ -167,7 +167,7 @@ message JournalLineInput {
string description = 4;
// Empty uses previous publication and ECB. Set this per line when the
// accountant needs a different rule, a saved custom rate, or a one-off rate.
optional komp_ac.ecb.ExchangeRateSelection exchange_rate_selection = 5;
optional komp_ac.exchange_rates.ExchangeRateSelection exchange_rate_selection = 5;
}
message CloseJournalRequest {

View File

@@ -2,293 +2,46 @@ syntax = "proto3";
package komp_ac.ecb;
// How the publication date is selected from the accounting basis date.
enum ExchangeRateDateRule {
EXCHANGE_RATE_DATE_RULE_UNSPECIFIED = 0;
// Latest TARGET publication strictly before the basis date.
EXCHANGE_RATE_DATE_RULE_PREVIOUS_PUBLICATION = 1;
// Latest TARGET publication on or before the basis date.
EXCHANGE_RATE_DATE_RULE_ON_OR_BEFORE_DATE = 2;
// The exact publication date supplied in specific_rate_date.
EXCHANGE_RATE_DATE_RULE_SPECIFIC_PUBLICATION_DATE = 3;
}
enum ExchangeRateSource {
EXCHANGE_RATE_SOURCE_UNSPECIFIED = 0;
// The rate source's own published reference rate (ECB's, today).
EXCHANGE_RATE_SOURCE_OFFICIAL = 1;
// A reusable rate from the profile's custom_exchange_rates table.
EXCHANGE_RATE_SOURCE_SAVED_CUSTOM = 2;
// A one-off rate recorded only in this posting's immutable evidence.
EXCHANGE_RATE_SOURCE_MANUAL = 3;
}
// Accountant-controlled conversion treatment. Exceptional choices remain
// explicit and auditable rather than being inferred from a legal label.
message ExchangeRateSelection {
ExchangeRateDateRule date_rule = 1;
ExchangeRateSource source = 2;
// Required for SPECIFIC_PUBLICATION_DATE and MANUAL; YYYY-MM-DD.
optional string specific_rate_date = 3;
// Required only for MANUAL. Exact foreign-currency units per one unit of
// the rate source's pivot currency (EUR for ECB).
optional string manual_units_per_pivot = 4;
// Required for SPECIFIC_PUBLICATION_DATE and MANUAL.
string reason = 5;
}
// Read-only access to ECB conversion previews and audit evidence.
//
// 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.
// ECB-only operational details. Conversion selection and evidence live in the
// provider-neutral exchange_rates contract.
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);
// Health of the reference-rate import pipeline: how far verified coverage
// reaches, how far it should reach by now, and how the recent import
// attempts went. Not profile-scoped -- one pipeline feeds every profile.
rpc GetEcbPipelineStatus(GetEcbPipelineStatusRequest)
returns (GetEcbPipelineStatusResponse);
}
message GetEcbPipelineStatusRequest {
// Optional. How many recent import attempts to return. Zero uses 20; the
// maximum is 100.
int32 batch_limit = 1;
}
message GetEcbPipelineStatusRequest { int32 batch_limit = 1; }
// One row of the append-only import audit log.
message EcbImportBatch {
int64 batch_id = 1;
// "running", "succeeded" or "failed".
string status = 2;
string requested_from = 3;
string requested_through = 4;
string endpoint = 5;
string started_at = 6;
optional string completed_at = 7;
// Present on a batch that advanced coverage.
optional string verified_through_date = 8;
optional int32 observation_count = 9;
// May be lower than observation_count: an observation already recorded by an
// earlier batch is kept rather than replaced.
optional int32 inserted_observation_count = 10;
optional string error_message = 11;
}
message EcbCurrencyCoverage {
string currency = 1;
string status = 2;
optional string verified_from_date = 3;
optional string verified_through_date = 4;
}
message GetEcbPipelineStatusResponse {
// Latest date a successful batch verified. Absent when nothing has ever
// succeeded, which is what a pipeline that has never run looks like.
optional string verified_through_date = 1;
// The date coverage should have reached by now, derived from the same
// publication rule the importer schedules against. Comparing the two is what
// "healthy" means.
string latest_verifiable_date = 2;
// True when verified coverage has reached latest_verifiable_date. A
// conversion whose publication date falls beyond coverage is refused, so
// this answers "will posting work right now".
bool healthy = 3;
// Publication days between coverage and latest_verifiable_date. Zero when
// healthy.
int32 days_behind = 4;
// True while a batch holds the single-running lock.
bool import_running = 5;
// When the scheduler next wakes, from the same cutoff the importer uses.
string next_import_at = 6;
// Newest first, running and failed attempts included.
repeated EcbImportBatch batches = 7;
// Distinct currencies observed on verified_through_date, so a coverage gap
// for one currency is visible even when the date itself is covered.
repeated string covered_currencies = 8;
// Current coverage restated as the question a reader actually has: what may
// be posted right now. Both are the latest basis date whose derived
// publication date is still covered, under the two rules
// required_publication_date applies -- transactions look strictly before the
// basis date, statements and decisive dates look at it directly, so the
// transaction bound is the later of the two. Derived here rather than by the
// caller so it cannot disagree with the calendar the conversion path uses.
// Absent when nothing has ever been verified and nothing can be posted.
optional string transactions_postable_through = 9;
optional string statements_postable_through = 10;
}
// 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;
// ISO calendar date (YYYY-MM-DD) of the accounting case.
string conversion_basis_date = 3;
// Required. Profile whose rates apply. Consulted when the selection requests
// a saved custom rate, and always required so the preview names its books.
string profile_name = 4;
// Empty uses previous publication and ECB.
optional ExchangeRateSelection exchange_rate_selection = 5;
}
// 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 converted_amount = 3;
string conversion_basis_date = 4;
string determination_method = 5;
string rounding_method = 6;
optional string rate_date = 7;
optional string units_per_pivot = 8;
optional int64 rate_observation_id = 9;
optional string observation_hash = 10;
optional string source_payload_hash = 11;
optional string rate_fetched_at = 12;
optional int64 import_batch_id = 13;
optional string source_endpoint = 14;
// Present only when determination_method is custom_rate. rate_date and
// units_per_pivot then describe the hand-entered row identified here, and
// the ECB observation fields above are empty.
optional int64 custom_rate_table_definition_id = 15;
optional int64 custom_rate_record_id = 16;
optional int64 custom_rate_row_revision = 17;
optional string custom_rate_note = 18;
// Accountant-supplied justification for an exceptional publication date or
// one-off manual rate. Empty for the normal/default rule.
ExchangeRateDateRule applied_date_rule = 19;
ExchangeRateSource applied_source = 20;
string selection_reason = 21;
// The currency converted_amount is stated in: the profile's accounting
// currency (EUR unless the profile keeps its books in something else).
string converted_currency = 22;
// True when reaching converted_currency needed two hops (original currency
// to EUR, then EUR to converted_currency) because neither is EUR. The
// fields above then describe only the second, final hop; the full
// provenance of both hops is available from ListEcbConversionEvidence
// after posting.
bool via_two_hop_conversion = 23;
}
// Identify one logical money cell whose conversion history should be inspected.
message ListEcbConversionEvidenceRequest {
// Required. Profile containing the table.
string profile_name = 1;
// Required. Logical table name within the profile.
string table_name = 2;
// Required. Dynamic table row id.
int64 record_id = 3;
// Required. Current display name of the money column. The server resolves it
// to the stable physical column name used by immutable evidence.
string column_name = 4;
// Optional. Maximum rows to return. Zero uses 100; maximum is 500.
int32 limit = 5;
// Optional cursor. Return evidence ids lower than this value.
optional int64 before_evidence_id = 6;
}
// One immutable explanation of how a posted EUR value was determined.
message EcbConversionEvidence {
int64 evidence_id = 1;
// Exact committed version of the target dynamic-table row.
int64 row_revision = 2;
string original_amount = 3;
string original_currency = 4;
string converted_amount = 5;
string conversion_context = 6;
string conversion_basis_date = 7;
string determination_method = 8;
string rounding_method = 9;
optional string rate_date = 10;
optional string units_per_pivot = 11;
optional int64 rate_observation_id = 12;
optional string observation_hash = 13;
optional string source_payload_hash = 14;
optional string rate_fetched_at = 15;
optional int64 import_batch_id = 16;
optional string source_endpoint = 17;
string evidence_created_at = 18;
// Present only when determination_method is custom_rate. The hand-entered row
// that supplied rate_date and units_per_pivot. The row itself stays editable, so
// the figures above are the immutable record of what was actually applied.
optional int64 custom_rate_table_definition_id = 19;
optional int64 custom_rate_record_id = 20;
optional int64 custom_rate_row_revision = 21;
optional string custom_rate_note = 22;
// Immutable audit data for an accountant-controlled exception.
string selection_reason = 23;
optional string selected_by_user_id = 24;
// The currency converted_amount is stated in. Always EUR for the
// original-currency-to-EUR leg; the profile's native currency for the
// EUR-to-native leg that follows it when that native currency is not EUR.
string converted_currency = 25;
// Set only on the EUR-to-native-currency leg of a two-hop conversion. Names
// the original-currency-to-EUR leg's evidence_id, which produced this row's
// original_amount/original_currency (always EUR).
optional int64 derived_from_evidence_id = 26;
// The pivot currency of the rate source that priced this hop (EUR for
// every hop today). A rate-based hop always has this currency on exactly
// one side of original_currency/converted_currency.
string pivot_currency = 27;
// Which rate source's policy governs this hop's profile ("ecb" for every
// hop today). Absent only for already_same_currency, where no policy
// needed to be consulted. This is about which policy applies, not about
// who actually supplied this specific rate -- see observation_source_id
// for that.
optional string policy_source_id = 28;
// Which rate source actually supplied this hop's rate as its own official
// observation. Present only when determination_method is
// official_reference_rate; absent for custom_rate, manual_rate, and
// already_same_currency, since none of those were priced by the policy's
// source itself -- a human entered them.
optional string observation_source_id = 29;
// Exact immutable profile policy version selected for this conversion's
// accounting date. Absent only when no conversion policy was needed because
// determination_method is already_same_currency.
optional int64 rate_source_policy_id = 30;
}
message ListEcbConversionEvidenceResponse {
// True when at least one evidence row for this cell used an ECB, saved
// custom, or one-off manual rate.
bool has_exchange = 1;
// Newest-first immutable history for this page.
repeated EcbConversionEvidence evidence = 2;
// True when another page exists using the last evidence_id as the cursor.
bool has_more = 3;
repeated EcbCurrencyCoverage currency_coverage = 11;
}

View File

@@ -0,0 +1,139 @@
syntax = "proto3";
package komp_ac.exchange_rates;
enum ExchangeRateDateRule {
EXCHANGE_RATE_DATE_RULE_UNSPECIFIED = 0;
EXCHANGE_RATE_DATE_RULE_PREVIOUS_PUBLICATION = 1;
EXCHANGE_RATE_DATE_RULE_ON_OR_BEFORE_DATE = 2;
EXCHANGE_RATE_DATE_RULE_SPECIFIC_PUBLICATION_DATE = 3;
}
enum ExchangeRateSource {
EXCHANGE_RATE_SOURCE_UNSPECIFIED = 0;
EXCHANGE_RATE_SOURCE_OFFICIAL = 1;
EXCHANGE_RATE_SOURCE_SAVED_CUSTOM = 2;
EXCHANGE_RATE_SOURCE_MANUAL = 3;
}
message ExchangeRateSelection {
ExchangeRateDateRule date_rule = 1;
ExchangeRateSource source = 2;
optional string specific_rate_date = 3;
optional string manual_foreign_units = 4;
string reason = 5;
}
service ExchangeRateService {
rpc PreviewDirectConversion(PreviewDirectConversionRequest)
returns (PreviewDirectConversionResponse);
rpc ListConversionEvidence(ListConversionEvidenceRequest)
returns (ListConversionEvidenceResponse);
rpc GetProfileExchangeRateSettings(GetProfileExchangeRateSettingsRequest)
returns (ProfileExchangeRateSettings);
rpc AddProfileForeignCurrency(AddProfileForeignCurrencyRequest)
returns (ProfileForeignCurrency);
}
message PreviewDirectConversionRequest {
string original_amount = 1;
string original_currency = 2;
string conversion_basis_date = 3;
string profile_name = 4;
optional ExchangeRateSelection exchange_rate_selection = 5;
}
message PreviewDirectConversionResponse {
string original_amount = 1;
string original_currency = 2;
string converted_amount = 3;
string accounting_currency = 4;
string conversion_basis_date = 5;
string determination_method = 6;
string rounding_method = 7;
optional string rate_date = 8;
optional string accounting_units = 9;
optional string foreign_currency = 10;
optional string foreign_units = 11;
string source_id = 12;
optional int64 rate_observation_id = 13;
optional string observation_hash = 14;
optional string source_payload_hash = 15;
optional string rate_fetched_at = 16;
optional int64 import_batch_id = 17;
optional string source_endpoint = 18;
optional int64 custom_rate_table_definition_id = 19;
optional int64 custom_rate_record_id = 20;
optional int64 custom_rate_row_revision = 21;
optional string custom_rate_note = 22;
ExchangeRateDateRule applied_date_rule = 23;
ExchangeRateSource applied_source = 24;
string selection_reason = 25;
}
message ListConversionEvidenceRequest {
string profile_name = 1;
string table_name = 2;
int64 record_id = 3;
string column_name = 4;
int32 limit = 5;
optional int64 before_evidence_id = 6;
}
message ConversionEvidence {
int64 evidence_id = 1;
int64 row_revision = 2;
string original_amount = 3;
string original_currency = 4;
string converted_amount = 5;
string accounting_currency = 6;
string accounting_units = 7;
string foreign_currency = 8;
string foreign_units = 9;
string conversion_context = 10;
string conversion_basis_date = 11;
string determination_method = 12;
string rounding_method = 13;
optional string rate_date = 14;
string source_id = 15;
optional int64 rate_observation_id = 16;
optional string observation_hash = 17;
optional string source_payload_hash = 18;
optional string rate_fetched_at = 19;
optional int64 import_batch_id = 20;
optional string source_endpoint = 21;
string evidence_created_at = 22;
optional int64 custom_rate_table_definition_id = 23;
optional int64 custom_rate_record_id = 24;
optional int64 custom_rate_row_revision = 25;
optional string custom_rate_note = 26;
string selection_reason = 27;
optional string selected_by_user_id = 28;
}
message ListConversionEvidenceResponse {
bool has_exchange = 1;
repeated ConversionEvidence evidence = 2;
bool has_more = 3;
}
message GetProfileExchangeRateSettingsRequest { string profile_name = 1; }
message ProfileForeignCurrency {
string currency = 1;
bool coverage_complete = 2;
optional string verified_from_date = 3;
optional string verified_through_date = 4;
}
message ProfileExchangeRateSettings {
string profile_name = 1;
string accounting_currency = 2;
string rate_source_id = 3;
repeated ProfileForeignCurrency foreign_currencies = 4;
}
message AddProfileForeignCurrencyRequest {
string profile_name = 1;
string currency = 2;
}

View File

@@ -104,6 +104,13 @@ message PostTableDefinitionRequest {
// to this one when they reach the ledger.
string accounting_currency = 8;
// Compiled direct-rate provider selected when the profile is created.
// Currently only "ecb" is available, and it requires EUR accounting.
string rate_source_id = 11;
// Append-only foreign currencies initially enabled for this profile.
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.
bool global = 9;

View File

@@ -3,7 +3,7 @@ syntax = "proto3";
package komp_ac.tables_data;
import "common.proto";
import "ecb.proto";
import "exchange_rates.proto";
import "google/protobuf/struct.proto";
// Read and write row data for user-defined tables inside profiles (schemas).
@@ -166,7 +166,7 @@ map<string, google.protobuf.Value> data = 3;
message PostAccountingTableDataRequest {
PostTableDataRequest row = 1;
komp_ac.ecb.ExchangeRateSelection exchange_rate_selection = 2;
komp_ac.exchange_rates.ExchangeRateSelection exchange_rate_selection = 2;
}
// Insert response.
@@ -259,7 +259,7 @@ message PutTableDataRequest {
message PutAccountingTableDataRequest {
PutTableDataRequest update = 1;
komp_ac.ecb.ExchangeRateSelection exchange_rate_selection = 2;
komp_ac.exchange_rates.ExchangeRateSelection exchange_rate_selection = 2;
bool confirmed = 3;
repeated string expected_affected_profiles = 4;
}

View File

@@ -31,6 +31,9 @@ pub mod proto {
pub mod ecb {
include!("proto/komp_ac.ecb.rs");
}
pub mod exchange_rates {
include!("proto/komp_ac.exchange_rates.rs");
}
pub mod table_structure {
include!("proto/komp_ac.table_structure.rs");
}

View File

@@ -15,14 +15,6 @@ pub use rusty_money::iso;
/// whitespace, and any length but three. Normalising here instead would let a
/// value be written in one spelling and compared in another.
pub fn require_iso_currency_code(currency_code: &str) -> Result<&'static iso::Currency, String> {
if currency_code.len() != 3
|| !currency_code
.chars()
.all(|character| character.is_ascii_uppercase())
{
return Err("Currency must be a canonical uppercase ISO-4217 code".to_string());
}
iso::find(currency_code).ok_or_else(|| format!("Unknown ISO-4217 currency: {currency_code}"))
}
@@ -42,7 +34,18 @@ mod tests {
#[test]
fn non_canonical_or_unknown_codes_are_rejected() {
for currency_code in [
"", "E", "EU", "EURO", "eur", "Eur", " EUR", "EUR ", "E R", "E\u{20AC}R", "AAA", "123",
"",
"E",
"EU",
"EURO",
"eur",
"Eur",
" EUR",
"EUR ",
"E R",
"E\u{20AC}R",
"AAA",
"123",
] {
assert!(
require_iso_currency_code(currency_code).is_err(),

Binary file not shown.

View File

@@ -72,7 +72,7 @@ pub struct JournalLineInput {
/// accountant needs a different rule, a saved custom rate, or a one-off rate.
#[prost(message, optional, tag = "5")]
pub exchange_rate_selection: ::core::option::Option<
super::ecb::ExchangeRateSelection,
super::exchange_rates::ExchangeRateSelection,
>,
}
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]

View File

@@ -1,36 +1,13 @@
// This file is @generated by prost-build.
/// Accountant-controlled conversion treatment. Exceptional choices remain
/// explicit and auditable rather than being inferred from a legal label.
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct ExchangeRateSelection {
#[prost(enumeration = "ExchangeRateDateRule", tag = "1")]
pub date_rule: i32,
#[prost(enumeration = "ExchangeRateSource", tag = "2")]
pub source: i32,
/// Required for SPECIFIC_PUBLICATION_DATE and MANUAL; YYYY-MM-DD.
#[prost(string, optional, tag = "3")]
pub specific_rate_date: ::core::option::Option<::prost::alloc::string::String>,
/// Required only for MANUAL. Exact foreign-currency units per one unit of
/// the rate source's pivot currency (EUR for ECB).
#[prost(string, optional, tag = "4")]
pub manual_units_per_pivot: ::core::option::Option<::prost::alloc::string::String>,
/// Required for SPECIFIC_PUBLICATION_DATE and MANUAL.
#[prost(string, tag = "5")]
pub reason: ::prost::alloc::string::String,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)]
pub struct GetEcbPipelineStatusRequest {
/// Optional. How many recent import attempts to return. Zero uses 20; the
/// maximum is 100.
#[prost(int32, tag = "1")]
pub batch_limit: i32,
}
/// One row of the append-only import audit log.
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct EcbImportBatch {
#[prost(int64, tag = "1")]
pub batch_id: i64,
/// "running", "succeeded" or "failed".
#[prost(string, tag = "2")]
pub status: ::prost::alloc::string::String,
#[prost(string, tag = "3")]
@@ -43,59 +20,44 @@ pub struct EcbImportBatch {
pub started_at: ::prost::alloc::string::String,
#[prost(string, optional, tag = "7")]
pub completed_at: ::core::option::Option<::prost::alloc::string::String>,
/// Present on a batch that advanced coverage.
#[prost(string, optional, tag = "8")]
pub verified_through_date: ::core::option::Option<::prost::alloc::string::String>,
#[prost(int32, optional, tag = "9")]
pub observation_count: ::core::option::Option<i32>,
/// May be lower than observation_count: an observation already recorded by an
/// earlier batch is kept rather than replaced.
#[prost(int32, optional, tag = "10")]
pub inserted_observation_count: ::core::option::Option<i32>,
#[prost(string, optional, tag = "11")]
pub error_message: ::core::option::Option<::prost::alloc::string::String>,
}
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct EcbCurrencyCoverage {
#[prost(string, tag = "1")]
pub currency: ::prost::alloc::string::String,
#[prost(string, tag = "2")]
pub status: ::prost::alloc::string::String,
#[prost(string, optional, tag = "3")]
pub verified_from_date: ::core::option::Option<::prost::alloc::string::String>,
#[prost(string, optional, tag = "4")]
pub verified_through_date: ::core::option::Option<::prost::alloc::string::String>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct GetEcbPipelineStatusResponse {
/// Latest date a successful batch verified. Absent when nothing has ever
/// succeeded, which is what a pipeline that has never run looks like.
#[prost(string, optional, tag = "1")]
pub verified_through_date: ::core::option::Option<::prost::alloc::string::String>,
/// The date coverage should have reached by now, derived from the same
/// publication rule the importer schedules against. Comparing the two is what
/// "healthy" means.
#[prost(string, tag = "2")]
pub latest_verifiable_date: ::prost::alloc::string::String,
/// True when verified coverage has reached latest_verifiable_date. A
/// conversion whose publication date falls beyond coverage is refused, so
/// this answers "will posting work right now".
#[prost(bool, tag = "3")]
pub healthy: bool,
/// Publication days between coverage and latest_verifiable_date. Zero when
/// healthy.
#[prost(int32, tag = "4")]
pub days_behind: i32,
/// True while a batch holds the single-running lock.
#[prost(bool, tag = "5")]
pub import_running: bool,
/// When the scheduler next wakes, from the same cutoff the importer uses.
#[prost(string, tag = "6")]
pub next_import_at: ::prost::alloc::string::String,
/// Newest first, running and failed attempts included.
#[prost(message, repeated, tag = "7")]
pub batches: ::prost::alloc::vec::Vec<EcbImportBatch>,
/// Distinct currencies observed on verified_through_date, so a coverage gap
/// for one currency is visible even when the date itself is covered.
#[prost(string, repeated, tag = "8")]
pub covered_currencies: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
/// Current coverage restated as the question a reader actually has: what may
/// be posted right now. Both are the latest basis date whose derived
/// publication date is still covered, under the two rules
/// required_publication_date applies -- transactions look strictly before the
/// basis date, statements and decisive dates look at it directly, so the
/// transaction bound is the later of the two. Derived here rather than by the
/// caller so it cannot disagree with the calendar the conversion path uses.
/// Absent when nothing has ever been verified and nothing can be posted.
#[prost(string, optional, tag = "9")]
pub transactions_postable_through: ::core::option::Option<
::prost::alloc::string::String,
@@ -104,292 +66,8 @@ pub struct GetEcbPipelineStatusResponse {
pub statements_postable_through: ::core::option::Option<
::prost::alloc::string::String,
>,
}
/// 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,
/// ISO calendar date (YYYY-MM-DD) of the accounting case.
#[prost(string, tag = "3")]
pub conversion_basis_date: ::prost::alloc::string::String,
/// Required. Profile whose rates apply. Consulted when the selection requests
/// a saved custom rate, and always required so the preview names its books.
#[prost(string, tag = "4")]
pub profile_name: ::prost::alloc::string::String,
/// Empty uses previous publication and ECB.
#[prost(message, optional, tag = "5")]
pub exchange_rate_selection: ::core::option::Option<ExchangeRateSelection>,
}
/// 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 converted_amount: ::prost::alloc::string::String,
#[prost(string, tag = "4")]
pub conversion_basis_date: ::prost::alloc::string::String,
#[prost(string, tag = "5")]
pub determination_method: ::prost::alloc::string::String,
#[prost(string, tag = "6")]
pub rounding_method: ::prost::alloc::string::String,
#[prost(string, optional, tag = "7")]
pub rate_date: ::core::option::Option<::prost::alloc::string::String>,
#[prost(string, optional, tag = "8")]
pub units_per_pivot: ::core::option::Option<::prost::alloc::string::String>,
#[prost(int64, optional, tag = "9")]
pub rate_observation_id: ::core::option::Option<i64>,
#[prost(string, optional, tag = "10")]
pub observation_hash: ::core::option::Option<::prost::alloc::string::String>,
#[prost(string, optional, tag = "11")]
pub source_payload_hash: ::core::option::Option<::prost::alloc::string::String>,
#[prost(string, optional, tag = "12")]
pub rate_fetched_at: ::core::option::Option<::prost::alloc::string::String>,
#[prost(int64, optional, tag = "13")]
pub import_batch_id: ::core::option::Option<i64>,
#[prost(string, optional, tag = "14")]
pub source_endpoint: ::core::option::Option<::prost::alloc::string::String>,
/// Present only when determination_method is custom_rate. rate_date and
/// units_per_pivot then describe the hand-entered row identified here, and
/// the ECB observation fields above are empty.
#[prost(int64, optional, tag = "15")]
pub custom_rate_table_definition_id: ::core::option::Option<i64>,
#[prost(int64, optional, tag = "16")]
pub custom_rate_record_id: ::core::option::Option<i64>,
#[prost(int64, optional, tag = "17")]
pub custom_rate_row_revision: ::core::option::Option<i64>,
#[prost(string, optional, tag = "18")]
pub custom_rate_note: ::core::option::Option<::prost::alloc::string::String>,
/// Accountant-supplied justification for an exceptional publication date or
/// one-off manual rate. Empty for the normal/default rule.
#[prost(enumeration = "ExchangeRateDateRule", tag = "19")]
pub applied_date_rule: i32,
#[prost(enumeration = "ExchangeRateSource", tag = "20")]
pub applied_source: i32,
#[prost(string, tag = "21")]
pub selection_reason: ::prost::alloc::string::String,
/// The currency converted_amount is stated in: the profile's accounting
/// currency (EUR unless the profile keeps its books in something else).
#[prost(string, tag = "22")]
pub converted_currency: ::prost::alloc::string::String,
/// True when reaching converted_currency needed two hops (original currency
/// to EUR, then EUR to converted_currency) because neither is EUR. The
/// fields above then describe only the second, final hop; the full
/// provenance of both hops is available from ListEcbConversionEvidence
/// after posting.
#[prost(bool, tag = "23")]
pub via_two_hop_conversion: bool,
}
/// Identify one logical money cell whose conversion history should be inspected.
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct ListEcbConversionEvidenceRequest {
/// Required. Profile containing the table.
#[prost(string, tag = "1")]
pub profile_name: ::prost::alloc::string::String,
/// Required. Logical table name within the profile.
#[prost(string, tag = "2")]
pub table_name: ::prost::alloc::string::String,
/// Required. Dynamic table row id.
#[prost(int64, tag = "3")]
pub record_id: i64,
/// Required. Current display name of the money column. The server resolves it
/// to the stable physical column name used by immutable evidence.
#[prost(string, tag = "4")]
pub column_name: ::prost::alloc::string::String,
/// Optional. Maximum rows to return. Zero uses 100; maximum is 500.
#[prost(int32, tag = "5")]
pub limit: i32,
/// Optional cursor. Return evidence ids lower than this value.
#[prost(int64, optional, tag = "6")]
pub before_evidence_id: ::core::option::Option<i64>,
}
/// One immutable explanation of how a posted EUR value was determined.
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct EcbConversionEvidence {
#[prost(int64, tag = "1")]
pub evidence_id: i64,
/// Exact committed version of the target dynamic-table row.
#[prost(int64, tag = "2")]
pub row_revision: i64,
#[prost(string, tag = "3")]
pub original_amount: ::prost::alloc::string::String,
#[prost(string, tag = "4")]
pub original_currency: ::prost::alloc::string::String,
#[prost(string, tag = "5")]
pub converted_amount: ::prost::alloc::string::String,
#[prost(string, tag = "6")]
pub conversion_context: ::prost::alloc::string::String,
#[prost(string, tag = "7")]
pub conversion_basis_date: ::prost::alloc::string::String,
#[prost(string, tag = "8")]
pub determination_method: ::prost::alloc::string::String,
#[prost(string, tag = "9")]
pub rounding_method: ::prost::alloc::string::String,
#[prost(string, optional, tag = "10")]
pub rate_date: ::core::option::Option<::prost::alloc::string::String>,
#[prost(string, optional, tag = "11")]
pub units_per_pivot: ::core::option::Option<::prost::alloc::string::String>,
#[prost(int64, optional, tag = "12")]
pub rate_observation_id: ::core::option::Option<i64>,
#[prost(string, optional, tag = "13")]
pub observation_hash: ::core::option::Option<::prost::alloc::string::String>,
#[prost(string, optional, tag = "14")]
pub source_payload_hash: ::core::option::Option<::prost::alloc::string::String>,
#[prost(string, optional, tag = "15")]
pub rate_fetched_at: ::core::option::Option<::prost::alloc::string::String>,
#[prost(int64, optional, tag = "16")]
pub import_batch_id: ::core::option::Option<i64>,
#[prost(string, optional, tag = "17")]
pub source_endpoint: ::core::option::Option<::prost::alloc::string::String>,
#[prost(string, tag = "18")]
pub evidence_created_at: ::prost::alloc::string::String,
/// Present only when determination_method is custom_rate. The hand-entered row
/// that supplied rate_date and units_per_pivot. The row itself stays editable, so
/// the figures above are the immutable record of what was actually applied.
#[prost(int64, optional, tag = "19")]
pub custom_rate_table_definition_id: ::core::option::Option<i64>,
#[prost(int64, optional, tag = "20")]
pub custom_rate_record_id: ::core::option::Option<i64>,
#[prost(int64, optional, tag = "21")]
pub custom_rate_row_revision: ::core::option::Option<i64>,
#[prost(string, optional, tag = "22")]
pub custom_rate_note: ::core::option::Option<::prost::alloc::string::String>,
/// Immutable audit data for an accountant-controlled exception.
#[prost(string, tag = "23")]
pub selection_reason: ::prost::alloc::string::String,
#[prost(string, optional, tag = "24")]
pub selected_by_user_id: ::core::option::Option<::prost::alloc::string::String>,
/// The currency converted_amount is stated in. Always EUR for the
/// original-currency-to-EUR leg; the profile's native currency for the
/// EUR-to-native leg that follows it when that native currency is not EUR.
#[prost(string, tag = "25")]
pub converted_currency: ::prost::alloc::string::String,
/// Set only on the EUR-to-native-currency leg of a two-hop conversion. Names
/// the original-currency-to-EUR leg's evidence_id, which produced this row's
/// original_amount/original_currency (always EUR).
#[prost(int64, optional, tag = "26")]
pub derived_from_evidence_id: ::core::option::Option<i64>,
/// The pivot currency of the rate source that priced this hop (EUR for
/// every hop today). A rate-based hop always has this currency on exactly
/// one side of original_currency/converted_currency.
#[prost(string, tag = "27")]
pub pivot_currency: ::prost::alloc::string::String,
/// Which rate source's policy governs this hop's profile ("ecb" for every
/// hop today). Absent only for already_same_currency, where no policy
/// needed to be consulted. This is about which policy applies, not about
/// who actually supplied this specific rate -- see observation_source_id
/// for that.
#[prost(string, optional, tag = "28")]
pub policy_source_id: ::core::option::Option<::prost::alloc::string::String>,
/// Which rate source actually supplied this hop's rate as its own official
/// observation. Present only when determination_method is
/// official_reference_rate; absent for custom_rate, manual_rate, and
/// already_same_currency, since none of those were priced by the policy's
/// source itself -- a human entered them.
#[prost(string, optional, tag = "29")]
pub observation_source_id: ::core::option::Option<::prost::alloc::string::String>,
/// Exact immutable profile policy version selected for this conversion's
/// accounting date. Absent only when no conversion policy was needed because
/// determination_method is already_same_currency.
#[prost(int64, optional, tag = "30")]
pub rate_source_policy_id: ::core::option::Option<i64>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ListEcbConversionEvidenceResponse {
/// True when at least one evidence row for this cell used an ECB, saved
/// custom, or one-off manual rate.
#[prost(bool, tag = "1")]
pub has_exchange: bool,
/// Newest-first immutable history for this page.
#[prost(message, repeated, tag = "2")]
pub evidence: ::prost::alloc::vec::Vec<EcbConversionEvidence>,
/// True when another page exists using the last evidence_id as the cursor.
#[prost(bool, tag = "3")]
pub has_more: bool,
}
/// How the publication date is selected from the accounting basis date.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
#[repr(i32)]
pub enum ExchangeRateDateRule {
Unspecified = 0,
/// Latest TARGET publication strictly before the basis date.
PreviousPublication = 1,
/// Latest TARGET publication on or before the basis date.
OnOrBeforeDate = 2,
/// The exact publication date supplied in specific_rate_date.
SpecificPublicationDate = 3,
}
impl ExchangeRateDateRule {
/// 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 => "EXCHANGE_RATE_DATE_RULE_UNSPECIFIED",
Self::PreviousPublication => "EXCHANGE_RATE_DATE_RULE_PREVIOUS_PUBLICATION",
Self::OnOrBeforeDate => "EXCHANGE_RATE_DATE_RULE_ON_OR_BEFORE_DATE",
Self::SpecificPublicationDate => {
"EXCHANGE_RATE_DATE_RULE_SPECIFIC_PUBLICATION_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 {
"EXCHANGE_RATE_DATE_RULE_UNSPECIFIED" => Some(Self::Unspecified),
"EXCHANGE_RATE_DATE_RULE_PREVIOUS_PUBLICATION" => {
Some(Self::PreviousPublication)
}
"EXCHANGE_RATE_DATE_RULE_ON_OR_BEFORE_DATE" => Some(Self::OnOrBeforeDate),
"EXCHANGE_RATE_DATE_RULE_SPECIFIC_PUBLICATION_DATE" => {
Some(Self::SpecificPublicationDate)
}
_ => None,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
#[repr(i32)]
pub enum ExchangeRateSource {
Unspecified = 0,
/// The rate source's own published reference rate (ECB's, today).
Official = 1,
/// A reusable rate from the profile's custom_exchange_rates table.
SavedCustom = 2,
/// A one-off rate recorded only in this posting's immutable evidence.
Manual = 3,
}
impl ExchangeRateSource {
/// 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 => "EXCHANGE_RATE_SOURCE_UNSPECIFIED",
Self::Official => "EXCHANGE_RATE_SOURCE_OFFICIAL",
Self::SavedCustom => "EXCHANGE_RATE_SOURCE_SAVED_CUSTOM",
Self::Manual => "EXCHANGE_RATE_SOURCE_MANUAL",
}
}
/// Creates an enum from field names used in the ProtoBuf definition.
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
match value {
"EXCHANGE_RATE_SOURCE_UNSPECIFIED" => Some(Self::Unspecified),
"EXCHANGE_RATE_SOURCE_OFFICIAL" => Some(Self::Official),
"EXCHANGE_RATE_SOURCE_SAVED_CUSTOM" => Some(Self::SavedCustom),
"EXCHANGE_RATE_SOURCE_MANUAL" => Some(Self::Manual),
_ => None,
}
}
#[prost(message, repeated, tag = "11")]
pub currency_coverage: ::prost::alloc::vec::Vec<EcbCurrencyCoverage>,
}
/// Generated client implementations.
pub mod ecb_service_client {
@@ -402,11 +80,8 @@ pub mod ecb_service_client {
)]
use tonic::codegen::*;
use tonic::codegen::http::Uri;
/// Read-only access to ECB conversion previews and audit evidence.
///
/// 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.
/// ECB-only operational details. Conversion selection and evidence live in the
/// provider-neutral exchange_rates contract.
#[derive(Debug, Clone)]
pub struct EcbServiceClient<T> {
inner: tonic::client::Grpc<T>,
@@ -487,67 +162,6 @@ 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,
request: impl tonic::IntoRequest<super::ListEcbConversionEvidenceRequest>,
) -> std::result::Result<
tonic::Response<super::ListEcbConversionEvidenceResponse>,
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/ListEcbConversionEvidence",
);
let mut req = request.into_request();
req.extensions_mut()
.insert(
GrpcMethod::new(
"komp_ac.ecb.EcbService",
"ListEcbConversionEvidence",
),
);
self.inner.unary(req, path, codec).await
}
/// Health of the reference-rate import pipeline: how far verified coverage
/// reaches, how far it should reach by now, and how the recent import
/// attempts went. Not profile-scoped -- one pipeline feeds every profile.
pub async fn get_ecb_pipeline_status(
&mut self,
request: impl tonic::IntoRequest<super::GetEcbPipelineStatusRequest>,
@@ -589,26 +203,6 @@ 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,
request: tonic::Request<super::ListEcbConversionEvidenceRequest>,
) -> std::result::Result<
tonic::Response<super::ListEcbConversionEvidenceResponse>,
tonic::Status,
>;
/// Health of the reference-rate import pipeline: how far verified coverage
/// reaches, how far it should reach by now, and how the recent import
/// attempts went. Not profile-scoped -- one pipeline feeds every profile.
async fn get_ecb_pipeline_status(
&self,
request: tonic::Request<super::GetEcbPipelineStatusRequest>,
@@ -617,11 +211,8 @@ pub mod ecb_service_server {
tonic::Status,
>;
}
/// Read-only access to ECB conversion previews and audit evidence.
///
/// 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.
/// ECB-only operational details. Conversion selection and evidence live in the
/// provider-neutral exchange_rates contract.
#[derive(Debug)]
pub struct EcbServiceServer<T> {
inner: Arc<T>,
@@ -698,104 +289,6 @@ 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>);
impl<
T: EcbService,
> tonic::server::UnaryService<
super::ListEcbConversionEvidenceRequest,
> for ListEcbConversionEvidenceSvc<T> {
type Response = super::ListEcbConversionEvidenceResponse;
type Future = BoxFuture<
tonic::Response<Self::Response>,
tonic::Status,
>;
fn call(
&mut self,
request: tonic::Request<
super::ListEcbConversionEvidenceRequest,
>,
) -> Self::Future {
let inner = Arc::clone(&self.0);
let fut = async move {
<T as EcbService>::list_ecb_conversion_evidence(
&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 = ListEcbConversionEvidenceSvc(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/GetEcbPipelineStatus" => {
#[allow(non_camel_case_types)]
struct GetEcbPipelineStatusSvc<T: EcbService>(pub Arc<T>);

View File

@@ -0,0 +1,846 @@
// This file is @generated by prost-build.
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct ExchangeRateSelection {
#[prost(enumeration = "ExchangeRateDateRule", tag = "1")]
pub date_rule: i32,
#[prost(enumeration = "ExchangeRateSource", tag = "2")]
pub source: i32,
#[prost(string, optional, tag = "3")]
pub specific_rate_date: ::core::option::Option<::prost::alloc::string::String>,
#[prost(string, optional, tag = "4")]
pub manual_foreign_units: ::core::option::Option<::prost::alloc::string::String>,
#[prost(string, tag = "5")]
pub reason: ::prost::alloc::string::String,
}
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct PreviewDirectConversionRequest {
#[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 conversion_basis_date: ::prost::alloc::string::String,
#[prost(string, tag = "4")]
pub profile_name: ::prost::alloc::string::String,
#[prost(message, optional, tag = "5")]
pub exchange_rate_selection: ::core::option::Option<ExchangeRateSelection>,
}
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct PreviewDirectConversionResponse {
#[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 converted_amount: ::prost::alloc::string::String,
#[prost(string, tag = "4")]
pub accounting_currency: ::prost::alloc::string::String,
#[prost(string, tag = "5")]
pub conversion_basis_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 accounting_units: ::core::option::Option<::prost::alloc::string::String>,
#[prost(string, optional, tag = "10")]
pub foreign_currency: ::core::option::Option<::prost::alloc::string::String>,
#[prost(string, optional, tag = "11")]
pub foreign_units: ::core::option::Option<::prost::alloc::string::String>,
#[prost(string, tag = "12")]
pub source_id: ::prost::alloc::string::String,
#[prost(int64, optional, tag = "13")]
pub rate_observation_id: ::core::option::Option<i64>,
#[prost(string, optional, tag = "14")]
pub observation_hash: ::core::option::Option<::prost::alloc::string::String>,
#[prost(string, optional, tag = "15")]
pub source_payload_hash: ::core::option::Option<::prost::alloc::string::String>,
#[prost(string, optional, tag = "16")]
pub rate_fetched_at: ::core::option::Option<::prost::alloc::string::String>,
#[prost(int64, optional, tag = "17")]
pub import_batch_id: ::core::option::Option<i64>,
#[prost(string, optional, tag = "18")]
pub source_endpoint: ::core::option::Option<::prost::alloc::string::String>,
#[prost(int64, optional, tag = "19")]
pub custom_rate_table_definition_id: ::core::option::Option<i64>,
#[prost(int64, optional, tag = "20")]
pub custom_rate_record_id: ::core::option::Option<i64>,
#[prost(int64, optional, tag = "21")]
pub custom_rate_row_revision: ::core::option::Option<i64>,
#[prost(string, optional, tag = "22")]
pub custom_rate_note: ::core::option::Option<::prost::alloc::string::String>,
#[prost(enumeration = "ExchangeRateDateRule", tag = "23")]
pub applied_date_rule: i32,
#[prost(enumeration = "ExchangeRateSource", tag = "24")]
pub applied_source: i32,
#[prost(string, tag = "25")]
pub selection_reason: ::prost::alloc::string::String,
}
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct ListConversionEvidenceRequest {
#[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 record_id: i64,
#[prost(string, tag = "4")]
pub column_name: ::prost::alloc::string::String,
#[prost(int32, tag = "5")]
pub limit: i32,
#[prost(int64, optional, tag = "6")]
pub before_evidence_id: ::core::option::Option<i64>,
}
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct ConversionEvidence {
#[prost(int64, tag = "1")]
pub evidence_id: i64,
#[prost(int64, tag = "2")]
pub row_revision: i64,
#[prost(string, tag = "3")]
pub original_amount: ::prost::alloc::string::String,
#[prost(string, tag = "4")]
pub original_currency: ::prost::alloc::string::String,
#[prost(string, tag = "5")]
pub converted_amount: ::prost::alloc::string::String,
#[prost(string, tag = "6")]
pub accounting_currency: ::prost::alloc::string::String,
#[prost(string, tag = "7")]
pub accounting_units: ::prost::alloc::string::String,
#[prost(string, tag = "8")]
pub foreign_currency: ::prost::alloc::string::String,
#[prost(string, tag = "9")]
pub foreign_units: ::prost::alloc::string::String,
#[prost(string, tag = "10")]
pub conversion_context: ::prost::alloc::string::String,
#[prost(string, tag = "11")]
pub conversion_basis_date: ::prost::alloc::string::String,
#[prost(string, tag = "12")]
pub determination_method: ::prost::alloc::string::String,
#[prost(string, tag = "13")]
pub rounding_method: ::prost::alloc::string::String,
#[prost(string, optional, tag = "14")]
pub rate_date: ::core::option::Option<::prost::alloc::string::String>,
#[prost(string, tag = "15")]
pub source_id: ::prost::alloc::string::String,
#[prost(int64, optional, tag = "16")]
pub rate_observation_id: ::core::option::Option<i64>,
#[prost(string, optional, tag = "17")]
pub observation_hash: ::core::option::Option<::prost::alloc::string::String>,
#[prost(string, optional, tag = "18")]
pub source_payload_hash: ::core::option::Option<::prost::alloc::string::String>,
#[prost(string, optional, tag = "19")]
pub rate_fetched_at: ::core::option::Option<::prost::alloc::string::String>,
#[prost(int64, optional, tag = "20")]
pub import_batch_id: ::core::option::Option<i64>,
#[prost(string, optional, tag = "21")]
pub source_endpoint: ::core::option::Option<::prost::alloc::string::String>,
#[prost(string, tag = "22")]
pub evidence_created_at: ::prost::alloc::string::String,
#[prost(int64, optional, tag = "23")]
pub custom_rate_table_definition_id: ::core::option::Option<i64>,
#[prost(int64, optional, tag = "24")]
pub custom_rate_record_id: ::core::option::Option<i64>,
#[prost(int64, optional, tag = "25")]
pub custom_rate_row_revision: ::core::option::Option<i64>,
#[prost(string, optional, tag = "26")]
pub custom_rate_note: ::core::option::Option<::prost::alloc::string::String>,
#[prost(string, tag = "27")]
pub selection_reason: ::prost::alloc::string::String,
#[prost(string, optional, tag = "28")]
pub selected_by_user_id: ::core::option::Option<::prost::alloc::string::String>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ListConversionEvidenceResponse {
#[prost(bool, tag = "1")]
pub has_exchange: bool,
#[prost(message, repeated, tag = "2")]
pub evidence: ::prost::alloc::vec::Vec<ConversionEvidence>,
#[prost(bool, tag = "3")]
pub has_more: bool,
}
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct GetProfileExchangeRateSettingsRequest {
#[prost(string, tag = "1")]
pub profile_name: ::prost::alloc::string::String,
}
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct ProfileForeignCurrency {
#[prost(string, tag = "1")]
pub currency: ::prost::alloc::string::String,
#[prost(bool, tag = "2")]
pub coverage_complete: bool,
#[prost(string, optional, tag = "3")]
pub verified_from_date: ::core::option::Option<::prost::alloc::string::String>,
#[prost(string, optional, tag = "4")]
pub verified_through_date: ::core::option::Option<::prost::alloc::string::String>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ProfileExchangeRateSettings {
#[prost(string, tag = "1")]
pub profile_name: ::prost::alloc::string::String,
#[prost(string, tag = "2")]
pub accounting_currency: ::prost::alloc::string::String,
#[prost(string, tag = "3")]
pub rate_source_id: ::prost::alloc::string::String,
#[prost(message, repeated, tag = "4")]
pub foreign_currencies: ::prost::alloc::vec::Vec<ProfileForeignCurrency>,
}
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct AddProfileForeignCurrencyRequest {
#[prost(string, tag = "1")]
pub profile_name: ::prost::alloc::string::String,
#[prost(string, tag = "2")]
pub currency: ::prost::alloc::string::String,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
#[repr(i32)]
pub enum ExchangeRateDateRule {
Unspecified = 0,
PreviousPublication = 1,
OnOrBeforeDate = 2,
SpecificPublicationDate = 3,
}
impl ExchangeRateDateRule {
/// 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 => "EXCHANGE_RATE_DATE_RULE_UNSPECIFIED",
Self::PreviousPublication => "EXCHANGE_RATE_DATE_RULE_PREVIOUS_PUBLICATION",
Self::OnOrBeforeDate => "EXCHANGE_RATE_DATE_RULE_ON_OR_BEFORE_DATE",
Self::SpecificPublicationDate => {
"EXCHANGE_RATE_DATE_RULE_SPECIFIC_PUBLICATION_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 {
"EXCHANGE_RATE_DATE_RULE_UNSPECIFIED" => Some(Self::Unspecified),
"EXCHANGE_RATE_DATE_RULE_PREVIOUS_PUBLICATION" => {
Some(Self::PreviousPublication)
}
"EXCHANGE_RATE_DATE_RULE_ON_OR_BEFORE_DATE" => Some(Self::OnOrBeforeDate),
"EXCHANGE_RATE_DATE_RULE_SPECIFIC_PUBLICATION_DATE" => {
Some(Self::SpecificPublicationDate)
}
_ => None,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
#[repr(i32)]
pub enum ExchangeRateSource {
Unspecified = 0,
Official = 1,
SavedCustom = 2,
Manual = 3,
}
impl ExchangeRateSource {
/// 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 => "EXCHANGE_RATE_SOURCE_UNSPECIFIED",
Self::Official => "EXCHANGE_RATE_SOURCE_OFFICIAL",
Self::SavedCustom => "EXCHANGE_RATE_SOURCE_SAVED_CUSTOM",
Self::Manual => "EXCHANGE_RATE_SOURCE_MANUAL",
}
}
/// Creates an enum from field names used in the ProtoBuf definition.
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
match value {
"EXCHANGE_RATE_SOURCE_UNSPECIFIED" => Some(Self::Unspecified),
"EXCHANGE_RATE_SOURCE_OFFICIAL" => Some(Self::Official),
"EXCHANGE_RATE_SOURCE_SAVED_CUSTOM" => Some(Self::SavedCustom),
"EXCHANGE_RATE_SOURCE_MANUAL" => Some(Self::Manual),
_ => None,
}
}
}
/// Generated client implementations.
pub mod exchange_rate_service_client {
#![allow(
unused_variables,
dead_code,
missing_docs,
clippy::wildcard_imports,
clippy::let_unit_value,
)]
use tonic::codegen::*;
use tonic::codegen::http::Uri;
#[derive(Debug, Clone)]
pub struct ExchangeRateServiceClient<T> {
inner: tonic::client::Grpc<T>,
}
impl ExchangeRateServiceClient<tonic::transport::Channel> {
/// Attempt to create a new client by connecting to a given endpoint.
pub async fn connect<D>(dst: D) -> Result<Self, tonic::transport::Error>
where
D: TryInto<tonic::transport::Endpoint>,
D::Error: Into<StdError>,
{
let conn = tonic::transport::Endpoint::new(dst)?.connect().await?;
Ok(Self::new(conn))
}
}
impl<T> ExchangeRateServiceClient<T>
where
T: tonic::client::GrpcService<tonic::body::Body>,
T::Error: Into<StdError>,
T::ResponseBody: Body<Data = Bytes> + std::marker::Send + 'static,
<T::ResponseBody as Body>::Error: Into<StdError> + std::marker::Send,
{
pub fn new(inner: T) -> Self {
let inner = tonic::client::Grpc::new(inner);
Self { inner }
}
pub fn with_origin(inner: T, origin: Uri) -> Self {
let inner = tonic::client::Grpc::with_origin(inner, origin);
Self { inner }
}
pub fn with_interceptor<F>(
inner: T,
interceptor: F,
) -> ExchangeRateServiceClient<InterceptedService<T, F>>
where
F: tonic::service::Interceptor,
T::ResponseBody: Default,
T: tonic::codegen::Service<
http::Request<tonic::body::Body>,
Response = http::Response<
<T as tonic::client::GrpcService<tonic::body::Body>>::ResponseBody,
>,
>,
<T as tonic::codegen::Service<
http::Request<tonic::body::Body>,
>>::Error: Into<StdError> + std::marker::Send + std::marker::Sync,
{
ExchangeRateServiceClient::new(InterceptedService::new(inner, interceptor))
}
/// Compress requests with the given encoding.
///
/// This requires the server to support it otherwise it might respond with an
/// error.
#[must_use]
pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self {
self.inner = self.inner.send_compressed(encoding);
self
}
/// Enable decompressing responses.
#[must_use]
pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self {
self.inner = self.inner.accept_compressed(encoding);
self
}
/// Limits the maximum size of a decoded message.
///
/// Default: `4MB`
#[must_use]
pub fn max_decoding_message_size(mut self, limit: usize) -> Self {
self.inner = self.inner.max_decoding_message_size(limit);
self
}
/// Limits the maximum size of an encoded message.
///
/// Default: `usize::MAX`
#[must_use]
pub fn max_encoding_message_size(mut self, limit: usize) -> Self {
self.inner = self.inner.max_encoding_message_size(limit);
self
}
pub async fn preview_direct_conversion(
&mut self,
request: impl tonic::IntoRequest<super::PreviewDirectConversionRequest>,
) -> std::result::Result<
tonic::Response<super::PreviewDirectConversionResponse>,
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.exchange_rates.ExchangeRateService/PreviewDirectConversion",
);
let mut req = request.into_request();
req.extensions_mut()
.insert(
GrpcMethod::new(
"komp_ac.exchange_rates.ExchangeRateService",
"PreviewDirectConversion",
),
);
self.inner.unary(req, path, codec).await
}
pub async fn list_conversion_evidence(
&mut self,
request: impl tonic::IntoRequest<super::ListConversionEvidenceRequest>,
) -> std::result::Result<
tonic::Response<super::ListConversionEvidenceResponse>,
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.exchange_rates.ExchangeRateService/ListConversionEvidence",
);
let mut req = request.into_request();
req.extensions_mut()
.insert(
GrpcMethod::new(
"komp_ac.exchange_rates.ExchangeRateService",
"ListConversionEvidence",
),
);
self.inner.unary(req, path, codec).await
}
pub async fn get_profile_exchange_rate_settings(
&mut self,
request: impl tonic::IntoRequest<
super::GetProfileExchangeRateSettingsRequest,
>,
) -> std::result::Result<
tonic::Response<super::ProfileExchangeRateSettings>,
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.exchange_rates.ExchangeRateService/GetProfileExchangeRateSettings",
);
let mut req = request.into_request();
req.extensions_mut()
.insert(
GrpcMethod::new(
"komp_ac.exchange_rates.ExchangeRateService",
"GetProfileExchangeRateSettings",
),
);
self.inner.unary(req, path, codec).await
}
pub async fn add_profile_foreign_currency(
&mut self,
request: impl tonic::IntoRequest<super::AddProfileForeignCurrencyRequest>,
) -> std::result::Result<
tonic::Response<super::ProfileForeignCurrency>,
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.exchange_rates.ExchangeRateService/AddProfileForeignCurrency",
);
let mut req = request.into_request();
req.extensions_mut()
.insert(
GrpcMethod::new(
"komp_ac.exchange_rates.ExchangeRateService",
"AddProfileForeignCurrency",
),
);
self.inner.unary(req, path, codec).await
}
}
}
/// Generated server implementations.
pub mod exchange_rate_service_server {
#![allow(
unused_variables,
dead_code,
missing_docs,
clippy::wildcard_imports,
clippy::let_unit_value,
)]
use tonic::codegen::*;
/// Generated trait containing gRPC methods that should be implemented for use with ExchangeRateServiceServer.
#[async_trait]
pub trait ExchangeRateService: std::marker::Send + std::marker::Sync + 'static {
async fn preview_direct_conversion(
&self,
request: tonic::Request<super::PreviewDirectConversionRequest>,
) -> std::result::Result<
tonic::Response<super::PreviewDirectConversionResponse>,
tonic::Status,
>;
async fn list_conversion_evidence(
&self,
request: tonic::Request<super::ListConversionEvidenceRequest>,
) -> std::result::Result<
tonic::Response<super::ListConversionEvidenceResponse>,
tonic::Status,
>;
async fn get_profile_exchange_rate_settings(
&self,
request: tonic::Request<super::GetProfileExchangeRateSettingsRequest>,
) -> std::result::Result<
tonic::Response<super::ProfileExchangeRateSettings>,
tonic::Status,
>;
async fn add_profile_foreign_currency(
&self,
request: tonic::Request<super::AddProfileForeignCurrencyRequest>,
) -> std::result::Result<
tonic::Response<super::ProfileForeignCurrency>,
tonic::Status,
>;
}
#[derive(Debug)]
pub struct ExchangeRateServiceServer<T> {
inner: Arc<T>,
accept_compression_encodings: EnabledCompressionEncodings,
send_compression_encodings: EnabledCompressionEncodings,
max_decoding_message_size: Option<usize>,
max_encoding_message_size: Option<usize>,
}
impl<T> ExchangeRateServiceServer<T> {
pub fn new(inner: T) -> Self {
Self::from_arc(Arc::new(inner))
}
pub fn from_arc(inner: Arc<T>) -> Self {
Self {
inner,
accept_compression_encodings: Default::default(),
send_compression_encodings: Default::default(),
max_decoding_message_size: None,
max_encoding_message_size: None,
}
}
pub fn with_interceptor<F>(
inner: T,
interceptor: F,
) -> InterceptedService<Self, F>
where
F: tonic::service::Interceptor,
{
InterceptedService::new(Self::new(inner), interceptor)
}
/// Enable decompressing requests with the given encoding.
#[must_use]
pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self {
self.accept_compression_encodings.enable(encoding);
self
}
/// Compress responses with the given encoding, if the client supports it.
#[must_use]
pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self {
self.send_compression_encodings.enable(encoding);
self
}
/// Limits the maximum size of a decoded message.
///
/// Default: `4MB`
#[must_use]
pub fn max_decoding_message_size(mut self, limit: usize) -> Self {
self.max_decoding_message_size = Some(limit);
self
}
/// Limits the maximum size of an encoded message.
///
/// Default: `usize::MAX`
#[must_use]
pub fn max_encoding_message_size(mut self, limit: usize) -> Self {
self.max_encoding_message_size = Some(limit);
self
}
}
impl<T, B> tonic::codegen::Service<http::Request<B>> for ExchangeRateServiceServer<T>
where
T: ExchangeRateService,
B: Body + std::marker::Send + 'static,
B::Error: Into<StdError> + std::marker::Send + 'static,
{
type Response = http::Response<tonic::body::Body>;
type Error = std::convert::Infallible;
type Future = BoxFuture<Self::Response, Self::Error>;
fn poll_ready(
&mut self,
_cx: &mut Context<'_>,
) -> Poll<std::result::Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: http::Request<B>) -> Self::Future {
match req.uri().path() {
"/komp_ac.exchange_rates.ExchangeRateService/PreviewDirectConversion" => {
#[allow(non_camel_case_types)]
struct PreviewDirectConversionSvc<T: ExchangeRateService>(
pub Arc<T>,
);
impl<
T: ExchangeRateService,
> tonic::server::UnaryService<super::PreviewDirectConversionRequest>
for PreviewDirectConversionSvc<T> {
type Response = super::PreviewDirectConversionResponse;
type Future = BoxFuture<
tonic::Response<Self::Response>,
tonic::Status,
>;
fn call(
&mut self,
request: tonic::Request<
super::PreviewDirectConversionRequest,
>,
) -> Self::Future {
let inner = Arc::clone(&self.0);
let fut = async move {
<T as ExchangeRateService>::preview_direct_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 = PreviewDirectConversionSvc(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.exchange_rates.ExchangeRateService/ListConversionEvidence" => {
#[allow(non_camel_case_types)]
struct ListConversionEvidenceSvc<T: ExchangeRateService>(pub Arc<T>);
impl<
T: ExchangeRateService,
> tonic::server::UnaryService<super::ListConversionEvidenceRequest>
for ListConversionEvidenceSvc<T> {
type Response = super::ListConversionEvidenceResponse;
type Future = BoxFuture<
tonic::Response<Self::Response>,
tonic::Status,
>;
fn call(
&mut self,
request: tonic::Request<super::ListConversionEvidenceRequest>,
) -> Self::Future {
let inner = Arc::clone(&self.0);
let fut = async move {
<T as ExchangeRateService>::list_conversion_evidence(
&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 = ListConversionEvidenceSvc(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.exchange_rates.ExchangeRateService/GetProfileExchangeRateSettings" => {
#[allow(non_camel_case_types)]
struct GetProfileExchangeRateSettingsSvc<T: ExchangeRateService>(
pub Arc<T>,
);
impl<
T: ExchangeRateService,
> tonic::server::UnaryService<
super::GetProfileExchangeRateSettingsRequest,
> for GetProfileExchangeRateSettingsSvc<T> {
type Response = super::ProfileExchangeRateSettings;
type Future = BoxFuture<
tonic::Response<Self::Response>,
tonic::Status,
>;
fn call(
&mut self,
request: tonic::Request<
super::GetProfileExchangeRateSettingsRequest,
>,
) -> Self::Future {
let inner = Arc::clone(&self.0);
let fut = async move {
<T as ExchangeRateService>::get_profile_exchange_rate_settings(
&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 = GetProfileExchangeRateSettingsSvc(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.exchange_rates.ExchangeRateService/AddProfileForeignCurrency" => {
#[allow(non_camel_case_types)]
struct AddProfileForeignCurrencySvc<T: ExchangeRateService>(
pub Arc<T>,
);
impl<
T: ExchangeRateService,
> tonic::server::UnaryService<
super::AddProfileForeignCurrencyRequest,
> for AddProfileForeignCurrencySvc<T> {
type Response = super::ProfileForeignCurrency;
type Future = BoxFuture<
tonic::Response<Self::Response>,
tonic::Status,
>;
fn call(
&mut self,
request: tonic::Request<
super::AddProfileForeignCurrencyRequest,
>,
) -> Self::Future {
let inner = Arc::clone(&self.0);
let fut = async move {
<T as ExchangeRateService>::add_profile_foreign_currency(
&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 = AddProfileForeignCurrencySvc(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)
}
_ => {
Box::pin(async move {
let mut response = http::Response::new(
tonic::body::Body::default(),
);
let headers = response.headers_mut();
headers
.insert(
tonic::Status::GRPC_STATUS,
(tonic::Code::Unimplemented as i32).into(),
);
headers
.insert(
http::header::CONTENT_TYPE,
tonic::metadata::GRPC_CONTENT_TYPE,
);
Ok(response)
})
}
}
}
}
impl<T> Clone for ExchangeRateServiceServer<T> {
fn clone(&self) -> Self {
let inner = self.inner.clone();
Self {
inner,
accept_compression_encodings: self.accept_compression_encodings,
send_compression_encodings: self.send_compression_encodings,
max_decoding_message_size: self.max_decoding_message_size,
max_encoding_message_size: self.max_encoding_message_size,
}
}
}
/// Generated gRPC service name
pub const SERVICE_NAME: &str = "komp_ac.exchange_rates.ExchangeRateService";
impl<T> tonic::server::NamedService for ExchangeRateServiceServer<T> {
const NAME: &'static str = SERVICE_NAME;
}
}

View File

@@ -51,6 +51,13 @@ pub struct PostTableDefinitionRequest {
/// to this one when they reach the ledger.
#[prost(string, tag = "8")]
pub accounting_currency: ::prost::alloc::string::String,
/// Compiled direct-rate provider selected when the profile is created.
/// Currently only "ecb" is available, and it requires EUR accounting.
#[prost(string, tag = "11")]
pub rate_source_id: ::prost::alloc::string::String,
/// Append-only foreign currencies initially enabled for this profile.
#[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.
#[prost(bool, tag = "9")]

View File

@@ -55,7 +55,7 @@ pub struct PostAccountingTableDataRequest {
pub row: ::core::option::Option<PostTableDataRequest>,
#[prost(message, optional, tag = "2")]
pub exchange_rate_selection: ::core::option::Option<
super::ecb::ExchangeRateSelection,
super::exchange_rates::ExchangeRateSelection,
>,
}
/// Insert response.
@@ -173,7 +173,7 @@ pub struct PutAccountingTableDataRequest {
pub update: ::core::option::Option<PutTableDataRequest>,
#[prost(message, optional, tag = "2")]
pub exchange_rate_selection: ::core::option::Option<
super::ecb::ExchangeRateSelection,
super::exchange_rates::ExchangeRateSelection,
>,
#[prost(bool, tag = "3")]
pub confirmed: bool,