Lines
89.61 %
Functions
61.54 %
Branches
100 %
//! `create-transaction-logical` and `update-transaction-logical` host fns.
//!
//! Both resolve account/commodity names via `logical_common` and delegate
//! physical lowering to `server::logical`.
use chrono::Utc;
use server::command::transaction::{CreateTransaction, UpdateTransaction};
use server::command::{CmdError, CmdResult, FinanceEntity};
use server::logical::lower_logical_transaction;
use uuid::Uuid;
use super::logical_common::{
physical_splits_to_entities, price_rows_to_entities, resolve_logical_split,
};
use super::parse::{
parse_create_transaction_logical_payload, parse_update_transaction_logical_payload,
pub(super) async fn run_create_transaction_logical(
user_id: Uuid,
payload_arg: Option<String>,
) -> wasmtime::Result<String> {
const CTX: &str = "create-transaction-logical";
let payload = payload_arg
.filter(|s| !s.is_empty())
.ok_or_else(|| wasmtime::Error::msg(format!("{CTX}: missing or empty payload")))?;
let input = parse_create_transaction_logical_payload(&payload)
.map_err(|err| wasmtime::Error::msg(format!("{CTX}: {err}")))?;
let mut logical = Vec::with_capacity(input.splits.len());
for s in input.splits {
logical.push(resolve_logical_split(CTX, user_id, s).await?);
}
let (physical_splits, price_rows) = lower_logical_transaction(&logical)
let tx_id = Uuid::new_v4();
let post_date = input.post_date.unwrap_or_else(Utc::now);
let enter_date = Utc::now();
let splits = physical_splits_to_entities(physical_splits, tx_id);
let mut runner = CreateTransaction::new()
.user_id(user_id)
.id(tx_id)
.post_date(post_date)
.enter_date(enter_date)
.splits(splits);
if let Some(note) = input.note {
runner = runner.note(note);
if !price_rows.is_empty() {
runner = runner.prices(price_rows_to_entities(price_rows, post_date));
dispatch_tx_result(CTX, runner.run().await)
pub(super) async fn run_update_transaction_logical(
const CTX: &str = "update-transaction-logical";
let input = parse_update_transaction_logical_payload(&payload)
// A split-only edit must leave the stored dates intact: forward post_date
// only when :date was explicitly provided. The price rows still need a
// fallback observation date when none is given.
let price_date = input.post_date.unwrap_or_else(Utc::now);
let splits = physical_splits_to_entities(physical_splits, input.transaction_id);
let mut runner = UpdateTransaction::new()
.transaction_id(input.transaction_id)
if let Some(post_date) = input.post_date {
runner = runner.post_date(post_date);
runner = runner.prices(price_rows_to_entities(price_rows, price_date));
fn dispatch_tx_result(
ctx: &str,
result: Result<Option<CmdResult>, CmdError>,
match result {
Ok(Some(CmdResult::Entity(FinanceEntity::Transaction(tx)))) => Ok(tx.id.to_string()),
Ok(Some(other)) => Err(wasmtime::Error::msg(format!(
"{ctx}: expected Transaction entity, got {other:?}"
))),
Ok(None) => Err(wasmtime::Error::msg(format!(
"{ctx}: command returned no entity"
Err(err) => Err(wasmtime::Error::msg(format!("{ctx}: {err}"))),