conversions web3

This commit is contained in:
Filipriec
2026-08-24 10:25:00 +02:00
parent 77e409dce6
commit fbe05feed2
9 changed files with 9 additions and 390 deletions

View File

@@ -298,6 +298,7 @@ td-root = kořen
td-template-create-heading = Vytvořit tabulky z fakturové šablony td-template-create-heading = Vytvořit tabulky z fakturové šablony
td-template-hint = Načte kontrakt <code>#let komp_ac_fields = (…)</code> z Typst šablony. Cesta, která odpovídá existující tabulce a sloupci, se stane odkazem na ni; vše ostatní se stane sloupcem TEXT, který později upravíte, a každé <code>[]</code> se stane podřízenou tabulkou. td-template-hint = Načte kontrakt <code>#let komp_ac_fields = (…)</code> z Typst šablony. Cesta, která odpovídá existující tabulce a sloupci, se stane odkazem na ni; vše ostatní se stane sloupcem TEXT, který později upravíte, a každé <code>[]</code> se stane podřízenou tabulkou.
td-table-name = Název tabulky td-table-name = Název tabulky
td-table-name-hint = Zde přejmenujte tabulku. Odkazy a její definice budou nadále odkazovat na stejnou tabulku.
td-row-display-columns = Sloupce identifikující řádek td-row-display-columns = Sloupce identifikující řádek
td-row-display-hint = Oddělte čárkami, platí jen pro kořenovou tabulku. td-row-display-hint = Oddělte čárkami, platí jen pro kořenovou tabulku.
td-template-source = Zdroj šablony td-template-source = Zdroj šablony
@@ -925,14 +926,12 @@ validation-err-permission = Vyžaduje se oprávnění ke správě validací.
# Pracoviště správy konverzních kurzů # Pracoviště správy konverzních kurzů
rates-title = Konverzní kurzy rates-title = Konverzní kurzy
rates-eyebrow = Pracoviště správce rates-eyebrow = Pracoviště správce
rates-description = Nastavte měny profilů, ověřte konverze, zkontrolujte auditní důkazy a sledujte poskytovatele. rates-description = Nastavte měny profilů, zkontrolujte auditní důkazy a sledujte poskytovatele.
rates-updated = Konverzní kurzy byly aktualizovány rates-updated = Konverzní kurzy byly aktualizovány
rates-could-not-continue = Operaci se nepodařilo dokončit rates-could-not-continue = Operaci se nepodařilo dokončit
rates-sections = Části pracoviště konverzních kurzů rates-sections = Části pracoviště konverzních kurzů
rates-tab-configuration = Konfigurace rates-tab-configuration = Konfigurace
rates-tab-configuration-sub = Měny a poskytovatelé rates-tab-configuration-sub = Měny a poskytovatelé
rates-tab-preview = Náhled
rates-tab-preview-sub = Zkouška konverze
rates-tab-evidence = Důkazy rates-tab-evidence = Důkazy
rates-tab-evidence-sub = Kontrola uloženého výsledku rates-tab-evidence-sub = Kontrola uloženého výsledku
rates-tab-providers = Poskytovatelé rates-tab-providers = Poskytovatelé
@@ -957,35 +956,8 @@ rates-append-only-hint = Konfigurace se pouze rozšiřuje: přidejte dalšího p
rates-provider = Poskytovatel rates-provider = Poskytovatel
rates-choose-provider = Vyberte poskytovatele rates-choose-provider = Vyberte poskytovatele
rates-make-default-now = Použít jako výchozího poskytovatele této měny rates-make-default-now = Použít jako výchozího poskytovatele této měny
rates-conversion-preview = Náhled konverze
rates-preview-hint = Ověří stejná pravidla výběru jako účtování bez zápisu účetních údajů.
rates-amount = Původní částka
rates-currency = Původní měna
rates-basis-date = Rozhodné datum konverze
rates-rate-method = Metoda kurzu
rates-method-automatic = Automatický oficiální kurz
rates-method-specific = Oficiální kurz z konkrétního dne zveřejnění
rates-method-saved = Uložený vlastní kurz
rates-method-manual = Jednorázový ruční kurz
rates-date-context = Kontext data
rates-context-ordinary = Běžná transakce — předchozí zveřejnění
rates-context-statement = Výpis — v den nebo před datem
rates-specific-date = Datum zveřejnění
rates-manual-foreign-units = Jednotky cizí měny na účetní jednotku
rates-provider-override = Jiný poskytovatel
rates-use-default-provider = Použít nastaveného výchozího
rates-reason = Důvod rates-reason = Důvod
rates-reason-hint = Backend jej vyžaduje, když výjimečný výběr potřebuje odůvodnění.
rates-preview-action = Zobrazit náhled konverze
rates-preview-result = Výsledek náhledu
rates-date-rule = Pravidlo data
rates-rate-date = Použité datum kurzu rates-rate-date = Použité datum kurzu
rates-audit-details = Auditní podrobnosti
rates-exact-quote = Přesná kotace
rates-determination = Způsob určení
rates-rounding = Způsob zaokrouhlení
rates-observation = ID oficiálního pozorování
rates-import-batch = ID dávky importu
rates-evidence-lookup = Vyhledání důkazů rates-evidence-lookup = Vyhledání důkazů
rates-evidence-hint = Vyhledejte přesnou historii konverzí jedné buňky tabulky a jejích revizí. rates-evidence-hint = Vyhledejte přesnou historii konverzí jedné buňky tabulky a jejích revizí.
rates-table = Tabulka rates-table = Tabulka
@@ -1003,13 +975,5 @@ rates-provider-health = Stav poskytovatele
rates-provider-health-hint = Ověřené pokrytí, hranice účtování a nedávné pokusy o import oficiálního poskytovatele. rates-provider-health-hint = Ověřené pokrytí, hranice účtování a nedávné pokusy o import oficiálního poskytovatele.
rates-unavailable-title = Konverzní kurzy nejsou dostupné rates-unavailable-title = Konverzní kurzy nejsou dostupné
rates-err-profile-missing = Vybraný profil již neexistuje. Zobrazuje se první dostupný profil. rates-err-profile-missing = Vybraný profil již neexistuje. Zobrazuje se první dostupný profil.
rates-err-preview-method = Vyberte podporovanou metodu kurzu.
rates-err-record-id = ID záznamu musí být kladné celé číslo. rates-err-record-id = ID záznamu musí být kladné celé číslo.
rates-configuration-added = Poskytovatel { $source } byl přidán pro měnu { $currency }. rates-configuration-added = Poskytovatel { $source } byl přidán pro měnu { $currency }.
rates-source-official = Oficiální
rates-source-saved = Uložený vlastní
rates-source-manual = Jednorázový ruční
rates-rule-previous = Předchozí zveřejnění
rates-rule-on-or-before = V den nebo před datem
rates-rule-specific = Konkrétní datum zveřejnění
rates-unknown = Neznámé

View File

@@ -299,7 +299,6 @@ td-col-parent = Parent
td-root = root td-root = root
td-template-create-heading = Create tables from an invoice template td-template-create-heading = Create tables from an invoice template
td-template-hint = Reads the <code>#let komp_ac_fields = (…)</code> contract out of a Typst template. A path that matches an existing table and column becomes a reference to it; anything else becomes a TEXT column to refine later, and every <code>[]</code> becomes a child table. td-template-hint = Reads the <code>#let komp_ac_fields = (…)</code> contract out of a Typst template. A path that matches an existing table and column becomes a reference to it; anything else becomes a TEXT column to refine later, and every <code>[]</code> becomes a child table.
td-table-name = Table name
td-row-display-columns = Row display columns td-row-display-columns = Row display columns
td-row-display-hint = Comma-separated, and only for the root table. td-row-display-hint = Comma-separated, and only for the root table.
td-template-source = Template source td-template-source = Template source
@@ -911,14 +910,12 @@ validation-err-permission = Validation-management permission is required.
# Exchange-rate administration workspace # Exchange-rate administration workspace
rates-title = Conversion rates rates-title = Conversion rates
rates-eyebrow = Admin workspace rates-eyebrow = Admin workspace
rates-description = Configure profile currencies, verify conversions, inspect audit evidence, and monitor providers. rates-description = Configure profile currencies, inspect audit evidence, and monitor providers.
rates-updated = Conversion rates updated rates-updated = Conversion rates updated
rates-could-not-continue = The operation could not be completed rates-could-not-continue = The operation could not be completed
rates-sections = Conversion-rate workspace sections rates-sections = Conversion-rate workspace sections
rates-tab-configuration = Configuration rates-tab-configuration = Configuration
rates-tab-configuration-sub = Currencies and providers rates-tab-configuration-sub = Currencies and providers
rates-tab-preview = Preview
rates-tab-preview-sub = Test a conversion
rates-tab-evidence = Evidence rates-tab-evidence = Evidence
rates-tab-evidence-sub = Inspect a stored result rates-tab-evidence-sub = Inspect a stored result
rates-tab-providers = Providers rates-tab-providers = Providers
@@ -943,35 +940,8 @@ rates-append-only-hint = Configuration is append-only: add another provider or m
rates-provider = Provider rates-provider = Provider
rates-choose-provider = Choose a provider rates-choose-provider = Choose a provider
rates-make-default-now = Use as the default provider for this currency rates-make-default-now = Use as the default provider for this currency
rates-conversion-preview = Conversion preview
rates-preview-hint = Exercise the same selection rules as posting without writing accounting data.
rates-amount = Original amount
rates-currency = Original currency
rates-basis-date = Conversion basis date
rates-rate-method = Rate method
rates-method-automatic = Automatic official rate
rates-method-specific = Official rate from a specific publication date
rates-method-saved = Saved custom rate
rates-method-manual = One-off manual rate
rates-date-context = Date context
rates-context-ordinary = Ordinary transaction — previous publication
rates-context-statement = Statement — on or before date
rates-specific-date = Publication date
rates-manual-foreign-units = Foreign units per accounting unit
rates-provider-override = Provider override
rates-use-default-provider = Use the configured default
rates-reason = Reason rates-reason = Reason
rates-reason-hint = Required by the backend when an exceptional selection needs justification.
rates-preview-action = Preview conversion
rates-preview-result = Preview result
rates-date-rule = Date rule
rates-rate-date = Applied rate date rates-rate-date = Applied rate date
rates-audit-details = Audit details
rates-exact-quote = Exact quote
rates-determination = Determination method
rates-rounding = Rounding method
rates-observation = Official observation ID
rates-import-batch = Import batch ID
rates-evidence-lookup = Evidence lookup rates-evidence-lookup = Evidence lookup
rates-evidence-hint = Look up the exact conversion history for one table cell and its revisions. rates-evidence-hint = Look up the exact conversion history for one table cell and its revisions.
rates-table = Table rates-table = Table
@@ -989,13 +959,5 @@ rates-provider-health = Provider health
rates-provider-health-hint = Verified coverage, posting horizons, and recent import attempts for the official provider. rates-provider-health-hint = Verified coverage, posting horizons, and recent import attempts for the official provider.
rates-unavailable-title = Conversion rates are unavailable rates-unavailable-title = Conversion rates are unavailable
rates-err-profile-missing = The selected profile no longer exists. The first available profile is shown instead. rates-err-profile-missing = The selected profile no longer exists. The first available profile is shown instead.
rates-err-preview-method = Choose a supported rate method.
rates-err-record-id = Record ID must be a positive whole number. rates-err-record-id = Record ID must be a positive whole number.
rates-configuration-added = Added provider { $source } for { $currency }. rates-configuration-added = Added provider { $source } for { $currency }.
rates-source-official = Official
rates-source-saved = Saved custom
rates-source-manual = One-off manual
rates-rule-previous = Previous publication
rates-rule-on-or-before = On or before date
rates-rule-specific = Specific publication date
rates-unknown = Unknown

View File

@@ -298,6 +298,7 @@ td-root = koreň
td-template-create-heading = Vytvoriť tabuľky z faktúrovej šablóny td-template-create-heading = Vytvoriť tabuľky z faktúrovej šablóny
td-template-hint = Načíta kontrakt <code>#let komp_ac_fields = (…)</code> z Typst šablóny. Cesta, ktorá zodpovedá existujúcej tabuľke a stĺpcu, sa stane odkazom na ňu; všetko ostatné sa stane stĺpcom TEXT, ktorý neskôr upravíte, a každé <code>[]</code> sa stane podradenou tabuľkou. td-template-hint = Načíta kontrakt <code>#let komp_ac_fields = (…)</code> z Typst šablóny. Cesta, ktorá zodpovedá existujúcej tabuľke a stĺpcu, sa stane odkazom na ňu; všetko ostatné sa stane stĺpcom TEXT, ktorý neskôr upravíte, a každé <code>[]</code> sa stane podradenou tabuľkou.
td-table-name = Názov tabuľky td-table-name = Názov tabuľky
td-table-name-hint = Tu premenujte tabuľku. Odkazy a jej definícia budú naďalej odkazovať na tú istú tabuľku.
td-row-display-columns = Stĺpce identifikujúce riadok td-row-display-columns = Stĺpce identifikujúce riadok
td-row-display-hint = Oddeľte čiarkami, platí len pre koreňovú tabuľku. td-row-display-hint = Oddeľte čiarkami, platí len pre koreňovú tabuľku.
td-template-source = Zdroj šablóny td-template-source = Zdroj šablóny
@@ -923,14 +924,12 @@ validation-err-permission = Vyžaduje sa oprávnenie na správu validácií.
# Pracovisko správy konverzných kurzov # Pracovisko správy konverzných kurzov
rates-title = Konverzné kurzy rates-title = Konverzné kurzy
rates-eyebrow = Pracovisko správcu rates-eyebrow = Pracovisko správcu
rates-description = Nastavte meny profilov, overte konverzie, skontrolujte auditné dôkazy a sledujte poskytovateľov. rates-description = Nastavte meny profilov, skontrolujte auditné dôkazy a sledujte poskytovateľov.
rates-updated = Konverzné kurzy boli aktualizované rates-updated = Konverzné kurzy boli aktualizované
rates-could-not-continue = Operáciu sa nepodarilo dokončiť rates-could-not-continue = Operáciu sa nepodarilo dokončiť
rates-sections = Časti pracoviska konverzných kurzov rates-sections = Časti pracoviska konverzných kurzov
rates-tab-configuration = Konfigurácia rates-tab-configuration = Konfigurácia
rates-tab-configuration-sub = Meny a poskytovatelia rates-tab-configuration-sub = Meny a poskytovatelia
rates-tab-preview = Náhľad
rates-tab-preview-sub = Skúška konverzie
rates-tab-evidence = Dôkazy rates-tab-evidence = Dôkazy
rates-tab-evidence-sub = Kontrola uloženého výsledku rates-tab-evidence-sub = Kontrola uloženého výsledku
rates-tab-providers = Poskytovatelia rates-tab-providers = Poskytovatelia
@@ -955,35 +954,8 @@ rates-append-only-hint = Konfigurácia sa iba rozširuje: pridajte ďalšieho po
rates-provider = Poskytovateľ rates-provider = Poskytovateľ
rates-choose-provider = Vyberte poskytovateľa rates-choose-provider = Vyberte poskytovateľa
rates-make-default-now = Použiť ako predvoleného poskytovateľa tejto meny rates-make-default-now = Použiť ako predvoleného poskytovateľa tejto meny
rates-conversion-preview = Náhľad konverzie
rates-preview-hint = Overí rovnaké pravidlá výberu ako účtovanie bez zápisu účtovných údajov.
rates-amount = Pôvodná suma
rates-currency = Pôvodná mena
rates-basis-date = Rozhodujúci dátum konverzie
rates-rate-method = Metóda kurzu
rates-method-automatic = Automatický oficiálny kurz
rates-method-specific = Oficiálny kurz z konkrétneho dňa zverejnenia
rates-method-saved = Uložený vlastný kurz
rates-method-manual = Jednorazový manuálny kurz
rates-date-context = Kontext dátumu
rates-context-ordinary = Bežná transakcia — predchádzajúce zverejnenie
rates-context-statement = Výpis — v deň alebo pred dátumom
rates-specific-date = Dátum zverejnenia
rates-manual-foreign-units = Jednotky cudzej meny na účtovnú jednotku
rates-provider-override = Iný poskytovateľ
rates-use-default-provider = Použiť nastaveného predvoleného
rates-reason = Dôvod rates-reason = Dôvod
rates-reason-hint = Backend ho vyžaduje, keď výnimočný výber potrebuje odôvodnenie.
rates-preview-action = Zobraziť náhľad konverzie
rates-preview-result = Výsledok náhľadu
rates-date-rule = Pravidlo dátumu
rates-rate-date = Použitý dátum kurzu rates-rate-date = Použitý dátum kurzu
rates-audit-details = Auditné podrobnosti
rates-exact-quote = Presná kotácia
rates-determination = Spôsob určenia
rates-rounding = Spôsob zaokrúhlenia
rates-observation = ID oficiálneho pozorovania
rates-import-batch = ID dávky importu
rates-evidence-lookup = Vyhľadanie dôkazov rates-evidence-lookup = Vyhľadanie dôkazov
rates-evidence-hint = Vyhľadajte presnú históriu konverzií jednej bunky tabuľky a jej revízií. rates-evidence-hint = Vyhľadajte presnú históriu konverzií jednej bunky tabuľky a jej revízií.
rates-table = Tabuľka rates-table = Tabuľka
@@ -1001,13 +973,5 @@ rates-provider-health = Stav poskytovateľa
rates-provider-health-hint = Overené pokrytie, hranice účtovania a nedávne pokusy o import oficiálneho poskytovateľa. rates-provider-health-hint = Overené pokrytie, hranice účtovania a nedávne pokusy o import oficiálneho poskytovateľa.
rates-unavailable-title = Konverzné kurzy nie sú dostupné rates-unavailable-title = Konverzné kurzy nie sú dostupné
rates-err-profile-missing = Vybraný profil už neexistuje. Zobrazuje sa prvý dostupný profil. rates-err-profile-missing = Vybraný profil už neexistuje. Zobrazuje sa prvý dostupný profil.
rates-err-preview-method = Vyberte podporovanú metódu kurzu.
rates-err-record-id = ID záznamu musí byť kladné celé číslo. rates-err-record-id = ID záznamu musí byť kladné celé číslo.
rates-configuration-added = Poskytovateľ { $source } bol pridaný pre menu { $currency }. rates-configuration-added = Poskytovateľ { $source } bol pridaný pre menu { $currency }.
rates-source-official = Oficiálny
rates-source-saved = Uložený vlastný
rates-source-manual = Jednorazový manuálny
rates-rule-previous = Predchádzajúce zverejnenie
rates-rule-on-or-before = V deň alebo pred dátumom
rates-rule-specific = Konkrétny dátum zverejnenia
rates-unknown = Neznáme

View File

@@ -24,10 +24,6 @@ pub(crate) fn router() -> Router<AppState> {
"/admin/exchange-rates/configuration", "/admin/exchange-rates/configuration",
post(workspace_logic::add_configuration), post(workspace_logic::add_configuration),
) )
.route(
"/admin/exchange-rates/preview",
post(workspace_logic::preview),
)
.route( .route(
"/admin/exchange-rates/evidence", "/admin/exchange-rates/evidence",
post(workspace_logic::evidence), post(workspace_logic::evidence),

View File

@@ -128,8 +128,6 @@ pub(crate) async fn load_workspace(
settings, settings,
can_configure: crate::authz::can_manage(&authorization, crate::authz::STRUCT_TABLE), can_configure: crate::authz::can_manage(&authorization, crate::authz::STRUCT_TABLE),
configuration: inputs.configuration, configuration: inputs.configuration,
preview_form: inputs.preview_form,
preview: inputs.preview,
evidence_form: inputs.evidence_form, evidence_form: inputs.evidence_form,
evidence: inputs.evidence, evidence: inputs.evidence,
evidence_has_exchange: inputs.evidence_has_exchange, evidence_has_exchange: inputs.evidence_has_exchange,

View File

@@ -9,10 +9,7 @@ use axum_extra::extract::Form;
use crate::{ use crate::{
AppState, AppState,
definitions::exchange_rates::{ definitions::exchange_rates::{AddProfileCurrencySourceRequest, ListConversionEvidenceRequest},
AddProfileCurrencySourceRequest, ExchangeRateDateRule, ExchangeRateSource,
ListConversionEvidenceRequest,
},
services::{authenticated_request, reject_cross_site}, services::{authenticated_request, reject_cross_site},
}; };
@@ -20,8 +17,7 @@ use super::{
state::LoadError, state::LoadError,
workspace_loader::load_workspace, workspace_loader::load_workspace,
workspace_state::{ workspace_state::{
ConfigurationForm, EvidenceForm, EvidenceView, PreviewForm, PreviewView, WorkspaceInputs, ConfigurationForm, EvidenceForm, EvidenceView, WorkspaceInputs, WorkspaceQuery,
WorkspaceQuery,
}, },
workspace_ui, workspace_ui,
}; };
@@ -90,57 +86,6 @@ pub(crate) async fn add_configuration(
} }
} }
pub(crate) async fn preview(
State(state): State<AppState>,
headers: HeaderMap,
Form(form): Form<PreviewForm>,
) -> Response {
if let Some(rejection) = reject_cross_site(&headers) {
return rejection;
}
let mut inputs = WorkspaceInputs::for_profile(form.profile.clone());
inputs.preview_form = form.clone();
let request = match form.request() {
Ok(request) => request,
Err(key) => {
inputs.error = Some(crate::tr!(crate::i18n::Locale::from_headers(&headers), key));
return render_loaded(
&headers,
load_workspace(state, &headers, inputs).await,
StatusCode::UNPROCESSABLE_ENTITY,
);
}
};
let Ok(request) = authenticated_request(&headers, request) else {
return Redirect::to("/login").into_response();
};
let mut exchange_rates = state.exchange_rates.clone();
match exchange_rates.preview_direct_conversion(request).await {
Ok(response) => {
let response = response.into_inner();
let locale = crate::i18n::Locale::from_headers(&headers);
inputs.preview = Some(PreviewView {
applied_method: crate::tr!(locale, rate_method_key(response.applied_source)),
applied_date_rule: crate::tr!(locale, date_rule_key(response.applied_date_rule)),
response,
});
render_loaded(
&headers,
load_workspace(state, &headers, inputs).await,
StatusCode::OK,
)
}
Err(error) => {
inputs.error = Some(error.message().to_string());
render_loaded(
&headers,
load_workspace(state, &headers, inputs).await,
StatusCode::UNPROCESSABLE_ENTITY,
)
}
}
}
pub(crate) async fn evidence( pub(crate) async fn evidence(
State(state): State<AppState>, State(state): State<AppState>,
headers: HeaderMap, headers: HeaderMap,
@@ -204,24 +149,6 @@ pub(crate) async fn evidence(
} }
} }
fn rate_method_key(value: i32) -> &'static str {
match ExchangeRateSource::try_from(value).ok() {
Some(ExchangeRateSource::Official) => "rates-source-official",
Some(ExchangeRateSource::SavedCustom) => "rates-source-saved",
Some(ExchangeRateSource::Manual) => "rates-source-manual",
_ => "rates-unknown",
}
}
fn date_rule_key(value: i32) -> &'static str {
match ExchangeRateDateRule::try_from(value).ok() {
Some(ExchangeRateDateRule::PreviousPublication) => "rates-rule-previous",
Some(ExchangeRateDateRule::OnOrBeforeDate) => "rates-rule-on-or-before",
Some(ExchangeRateDateRule::SpecificPublicationDate) => "rates-rule-specific",
_ => "rates-unknown",
}
}
fn render_loaded( fn render_loaded(
headers: &HeaderMap, headers: &HeaderMap,
result: Result<super::workspace_state::ExchangeRateWorkspaceState, LoadError>, result: Result<super::workspace_state::ExchangeRateWorkspaceState, LoadError>,

View File

@@ -1,9 +1,6 @@
//! State and form wire formats for the provider-neutral exchange-rate workspace. //! State and form wire formats for the provider-neutral exchange-rate workspace.
use crate::definitions::exchange_rates::{ use crate::definitions::exchange_rates::ConversionEvidence;
ConversionEvidence, ExchangeRateDateRule, ExchangeRateSelection, ExchangeRateSource,
PreviewDirectConversionRequest, PreviewDirectConversionResponse,
};
use super::state::EcbPageState; use super::state::EcbPageState;
@@ -25,92 +22,6 @@ pub(crate) struct ConfigurationForm {
pub make_default: bool, pub make_default: bool,
} }
#[derive(Clone, Debug, serde::Deserialize)]
pub(crate) struct PreviewForm {
#[serde(default)]
pub profile: String,
#[serde(default)]
pub original_amount: String,
#[serde(default)]
pub original_currency: String,
#[serde(default)]
pub conversion_basis_date: String,
#[serde(default = "automatic_official")]
pub method: String,
#[serde(default = "ordinary_context")]
pub context: String,
#[serde(default)]
pub specific_rate_date: String,
#[serde(default)]
pub manual_foreign_units: String,
#[serde(default)]
pub rate_source_id: String,
#[serde(default)]
pub reason: String,
}
fn automatic_official() -> String {
"automatic-official".to_string()
}
fn ordinary_context() -> String {
"ordinary".to_string()
}
impl Default for PreviewForm {
fn default() -> Self {
Self {
profile: String::new(),
original_amount: String::new(),
original_currency: String::new(),
conversion_basis_date: String::new(),
method: automatic_official(),
context: ordinary_context(),
specific_rate_date: String::new(),
manual_foreign_units: String::new(),
rate_source_id: String::new(),
reason: String::new(),
}
}
}
impl PreviewForm {
pub(crate) fn request(&self) -> Result<PreviewDirectConversionRequest, &'static str> {
let date_rule = match self.method.as_str() {
"official-specific" | "manual" => ExchangeRateDateRule::SpecificPublicationDate,
_ if self.context == "statement" => ExchangeRateDateRule::OnOrBeforeDate,
_ => ExchangeRateDateRule::PreviousPublication,
};
let source = match self.method.as_str() {
"automatic-official" | "official-specific" => ExchangeRateSource::Official,
"saved-custom" => ExchangeRateSource::SavedCustom,
"manual" => ExchangeRateSource::Manual,
_ => return Err("rates-err-preview-method"),
};
let use_default_selection = self.method == "automatic-official"
&& self.context != "statement"
&& self.rate_source_id.trim().is_empty();
let selection = (!use_default_selection).then(|| ExchangeRateSelection {
date_rule: date_rule as i32,
source: source as i32,
specific_rate_date: (!self.specific_rate_date.trim().is_empty())
.then(|| self.specific_rate_date.trim().to_string()),
manual_foreign_units: (!self.manual_foreign_units.trim().is_empty())
.then(|| self.manual_foreign_units.trim().to_string()),
reason: self.reason.trim().to_string(),
rate_source_id: (!self.rate_source_id.trim().is_empty())
.then(|| self.rate_source_id.trim().to_string()),
});
Ok(PreviewDirectConversionRequest {
original_amount: self.original_amount.trim().to_string(),
original_currency: self.original_currency.trim().to_ascii_uppercase(),
conversion_basis_date: self.conversion_basis_date.trim().to_string(),
profile_name: self.profile.clone(),
exchange_rate_selection: selection,
})
}
}
#[derive(Clone, Debug, Default, serde::Deserialize)] #[derive(Clone, Debug, Default, serde::Deserialize)]
pub(crate) struct EvidenceForm { pub(crate) struct EvidenceForm {
#[serde(default)] #[serde(default)]
@@ -154,44 +65,6 @@ pub(crate) struct ProfileSettingsView {
pub foreign_currencies: Vec<CurrencySettingsView>, pub foreign_currencies: Vec<CurrencySettingsView>,
} }
#[derive(Clone, Debug)]
pub(crate) struct PreviewView {
pub response: PreviewDirectConversionResponse,
pub applied_method: String,
pub applied_date_rule: String,
}
impl PreviewView {
pub(crate) fn quote(&self) -> String {
match (
self.response.accounting_units.as_deref(),
self.response.foreign_units.as_deref(),
self.response.foreign_currency.as_deref(),
) {
(Some(accounting_units), Some(foreign_units), Some(foreign_currency)) => format!(
"{} {} = {} {}",
accounting_units,
self.response.accounting_currency,
foreign_units,
foreign_currency,
),
_ => "".to_string(),
}
}
pub(crate) fn observation_id(&self) -> String {
self.response
.official_observation_id
.map_or_else(|| "".to_string(), |id| id.to_string())
}
pub(crate) fn import_batch_id(&self) -> String {
self.response
.import_batch_id
.map_or_else(|| "".to_string(), |id| id.to_string())
}
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub(crate) struct EvidenceView { pub(crate) struct EvidenceView {
pub evidence: ConversionEvidence, pub evidence: ConversionEvidence,
@@ -200,8 +73,6 @@ pub(crate) struct EvidenceView {
pub(crate) struct WorkspaceInputs { pub(crate) struct WorkspaceInputs {
pub selected_profile: String, pub selected_profile: String,
pub configuration: ConfigurationForm, pub configuration: ConfigurationForm,
pub preview_form: PreviewForm,
pub preview: Option<PreviewView>,
pub evidence_form: EvidenceForm, pub evidence_form: EvidenceForm,
pub evidence: Vec<EvidenceView>, pub evidence: Vec<EvidenceView>,
pub evidence_has_exchange: Option<bool>, pub evidence_has_exchange: Option<bool>,
@@ -215,8 +86,6 @@ impl WorkspaceInputs {
Self { Self {
selected_profile: profile, selected_profile: profile,
configuration: ConfigurationForm::default(), configuration: ConfigurationForm::default(),
preview_form: PreviewForm::default(),
preview: None,
evidence_form: EvidenceForm::default(), evidence_form: EvidenceForm::default(),
evidence: Vec::new(), evidence: Vec::new(),
evidence_has_exchange: None, evidence_has_exchange: None,
@@ -235,8 +104,6 @@ pub(crate) struct ExchangeRateWorkspaceState {
pub settings: Option<ProfileSettingsView>, pub settings: Option<ProfileSettingsView>,
pub can_configure: bool, pub can_configure: bool,
pub configuration: ConfigurationForm, pub configuration: ConfigurationForm,
pub preview_form: PreviewForm,
pub preview: Option<PreviewView>,
pub evidence_form: EvidenceForm, pub evidence_form: EvidenceForm,
pub evidence: Vec<EvidenceView>, pub evidence: Vec<EvidenceView>,
pub evidence_has_exchange: Option<bool>, pub evidence_has_exchange: Option<bool>,

View File

@@ -45,7 +45,7 @@ mod tests {
state::{EcbPageState, ImportBatchView}, state::{EcbPageState, ImportBatchView},
workspace_state::{ workspace_state::{
ConfigurationForm, CurrencySettingsView, CurrencySourceView, EvidenceForm, ConfigurationForm, CurrencySettingsView, CurrencySourceView, EvidenceForm,
ExchangeRateWorkspaceState, PreviewForm, ProfileSettingsView, ExchangeRateWorkspaceState, ProfileSettingsView,
}, },
}; };
@@ -98,8 +98,6 @@ mod tests {
}), }),
can_configure: true, can_configure: true,
configuration: ConfigurationForm::default(), configuration: ConfigurationForm::default(),
preview_form: PreviewForm::default(),
preview: None,
evidence_form: EvidenceForm::default(), evidence_form: EvidenceForm::default(),
evidence: Vec::new(), evidence: Vec::new(),
evidence_has_exchange: None, evidence_has_exchange: None,
@@ -116,7 +114,6 @@ mod tests {
assert!(!html.contains("Template error"), "{html}"); assert!(!html.contains("Template error"), "{html}");
assert!(html.contains("Profile configuration"), "{html}"); assert!(html.contains("Profile configuration"), "{html}");
assert!(html.contains("Conversion preview"), "{html}");
assert!(html.contains("Evidence lookup"), "{html}"); assert!(html.contains("Evidence lookup"), "{html}");
assert!(html.contains("Provider health"), "{html}"); assert!(html.contains("Provider health"), "{html}");
assert!(html.contains("CZK"), "{html}"); assert!(html.contains("CZK"), "{html}");

View File

@@ -20,7 +20,6 @@
<nav class="tabs" aria-label="{{ nav.tr("rates-sections") }}"> <nav class="tabs" aria-label="{{ nav.tr("rates-sections") }}">
<a class="tab" href="#configuration"><span>{{ nav.tr("rates-tab-configuration") }}</span><small>{{ nav.tr("rates-tab-configuration-sub") }}</small></a> <a class="tab" href="#configuration"><span>{{ nav.tr("rates-tab-configuration") }}</span><small>{{ nav.tr("rates-tab-configuration-sub") }}</small></a>
<a class="tab" href="#preview"><span>{{ nav.tr("rates-tab-preview") }}</span><small>{{ nav.tr("rates-tab-preview-sub") }}</small></a>
<a class="tab" href="#evidence"><span>{{ nav.tr("rates-tab-evidence") }}</span><small>{{ nav.tr("rates-tab-evidence-sub") }}</small></a> <a class="tab" href="#evidence"><span>{{ nav.tr("rates-tab-evidence") }}</span><small>{{ nav.tr("rates-tab-evidence-sub") }}</small></a>
<a class="tab" href="#providers"><span>{{ nav.tr("rates-tab-providers") }}</span><small>{{ nav.tr("rates-tab-providers-sub") }}</small></a> <a class="tab" href="#providers"><span>{{ nav.tr("rates-tab-providers") }}</span><small>{{ nav.tr("rates-tab-providers-sub") }}</small></a>
</nav> </nav>
@@ -113,61 +112,6 @@
{% endif %} {% endif %}
</section> </section>
<section class="panel" id="preview" x-data="{ method: '{{ page.preview_form.method }}' }">
<h2>{{ nav.tr("rates-conversion-preview") }}</h2>
<p class="hint">{{ nav.tr("rates-preview-hint") }}</p>
<form method="post" action="/admin/exchange-rates/preview">
<input type="hidden" name="profile" value="{{ page.selected_profile }}">
<div class="form-grid">
<label>{{ nav.tr("rates-amount") }}<input name="original_amount" value="{{ page.preview_form.original_amount }}" inputmode="decimal" placeholder="2415.30" required></label>
<label>{{ nav.tr("rates-currency") }}<input name="original_currency" value="{{ page.preview_form.original_currency }}" list="currency-codes" maxlength="3" placeholder="CZK" required></label>
<label>{{ nav.tr("rates-basis-date") }}<input type="date" name="conversion_basis_date" value="{{ page.preview_form.conversion_basis_date }}" required></label>
<label>{{ nav.tr("rates-rate-method") }}
<select name="method" x-model="method">
<option value="automatic-official" {% if page.preview_form.method == "automatic-official" %}selected{% endif %}>{{ nav.tr("rates-method-automatic") }}</option>
<option value="official-specific" {% if page.preview_form.method == "official-specific" %}selected{% endif %}>{{ nav.tr("rates-method-specific") }}</option>
<option value="saved-custom" {% if page.preview_form.method == "saved-custom" %}selected{% endif %}>{{ nav.tr("rates-method-saved") }}</option>
<option value="manual" {% if page.preview_form.method == "manual" %}selected{% endif %}>{{ nav.tr("rates-method-manual") }}</option>
</select>
</label>
<label x-show="method !== 'official-specific' && method !== 'manual'">{{ nav.tr("rates-date-context") }}
<select name="context">
<option value="ordinary" {% if page.preview_form.context == "ordinary" %}selected{% endif %}>{{ nav.tr("rates-context-ordinary") }}</option>
<option value="statement" {% if page.preview_form.context == "statement" %}selected{% endif %}>{{ nav.tr("rates-context-statement") }}</option>
</select>
</label>
<label x-show="method === 'official-specific' || method === 'manual'">{{ nav.tr("rates-specific-date") }}<input type="date" name="specific_rate_date" value="{{ page.preview_form.specific_rate_date }}"></label>
<label x-show="method === 'manual'">{{ nav.tr("rates-manual-foreign-units") }}<input name="manual_foreign_units" value="{{ page.preview_form.manual_foreign_units }}" inputmode="decimal" placeholder="24.153"></label>
<label>{{ nav.tr("rates-provider-override") }}
<select name="rate_source_id"><option value="">{{ nav.tr("rates-use-default-provider") }}</option>{% for provider in page.providers %}<option value="{{ provider }}" {% if page.preview_form.rate_source_id == *provider %}selected{% endif %}>{{ provider }}</option>{% endfor %}</select>
</label>
<label>{{ nav.tr("rates-reason") }}<input name="reason" value="{{ page.preview_form.reason }}" maxlength="1000"><small>{{ nav.tr("rates-reason-hint") }}</small></label>
</div>
<div class="form-actions"><button type="submit">{{ nav.tr("rates-preview-action") }}</button></div>
</form>
{% if let Some(preview) = page.preview %}
<h3>{{ nav.tr("rates-preview-result") }}</h3>
<p><strong>{{ preview.response.original_amount }} {{ preview.response.original_currency }} = {{ preview.response.converted_amount }} {{ preview.response.accounting_currency }}</strong></p>
<dl class="status-grid">
<div><dt>{{ nav.tr("rates-provider") }}</dt><dd>{{ preview.response.source_id }}</dd></div>
<div><dt>{{ nav.tr("rates-rate-method") }}</dt><dd>{{ preview.applied_method }}</dd></div>
<div><dt>{{ nav.tr("rates-date-rule") }}</dt><dd>{{ preview.applied_date_rule }}</dd></div>
<div><dt>{{ nav.tr("rates-rate-date") }}</dt><dd>{{ preview.response.rate_date.as_deref().unwrap_or("—") }}</dd></div>
</dl>
<details><summary>{{ nav.tr("rates-audit-details") }}</summary>
<dl class="status-grid">
<div><dt>{{ nav.tr("rates-exact-quote") }}</dt><dd>{{ preview.quote() }}</dd></div>
<div><dt>{{ nav.tr("rates-determination") }}</dt><dd>{{ preview.response.determination_method }}</dd></div>
<div><dt>{{ nav.tr("rates-rounding") }}</dt><dd>{{ preview.response.rounding_method }}</dd></div>
<div><dt>{{ nav.tr("rates-reason") }}</dt><dd>{{ preview.response.selection_reason }}</dd></div>
<div><dt>{{ nav.tr("rates-observation") }}</dt><dd>{{ preview.observation_id() }}</dd></div>
<div><dt>{{ nav.tr("rates-import-batch") }}</dt><dd>{{ preview.import_batch_id() }}</dd></div>
</dl>
</details>
{% endif %}
</section>
<section class="panel" id="evidence"> <section class="panel" id="evidence">
<h2>{{ nav.tr("rates-evidence-lookup") }}</h2> <h2>{{ nav.tr("rates-evidence-lookup") }}</h2>
<p class="hint">{{ nav.tr("rates-evidence-hint") }}</p> <p class="hint">{{ nav.tr("rates-evidence-hint") }}</p>