use crate::kernel::Settings;
use crate::kernel::balance::Balance;
use crate::kernel::price_lookup::PriceLookupCtx;
use crate::kernel::report_item_selector::{BalanceSelector, RegisterSelector};
use crate::model::{RegisterEntry, RegisterPosting, Transaction, TxnAccount, TxnRefs};
use itertools::Itertools;
use rust_decimal::Decimal;
use std::collections::HashMap;
pub(crate) type TxnGroupByOp<'a> = Box<dyn Fn(&Transaction) -> String + 'a>;
pub(crate) fn balance_groups<T>(
txns: &TxnRefs<'_>,
group_by_op: &TxnGroupByOp<'_>,
price_lookup_ctx: &PriceLookupCtx<'_>,
ras: &T,
settings: &Settings,
) -> Vec<Balance>
where
T: BalanceSelector + ?Sized,
{
txns.iter()
.chunk_by(|txn| group_by_op(txn))
.into_iter()
.map(|(group_by_key, bal_grp_txns)| {
Balance::from_iter(
&group_by_key,
bal_grp_txns,
price_lookup_ctx,
ras,
settings,
settings.report.balance_group.bal_type.clone(),
)
.expect("Logic error with Balance Group: inner balance failed")
})
.filter(|bal| !bal.is_empty())
.sorted_by_key(|bal| bal.title.clone())
.collect()
}
pub(crate) fn register_engine<'a, T>(
txns: &'a TxnRefs<'_>,
price_lookup_ctx: &PriceLookupCtx<'_>,
ras: &T,
) -> Vec<RegisterEntry<'a>>
where
T: RegisterSelector<'a> + ?Sized,
{
let mut register_engine: HashMap<TxnAccount, Decimal> = HashMap::new();
txns.iter()
.map(|txn| {
let register_postings: Vec<_> = price_lookup_ctx
.convert_prices(txn)
.zip(&txn.posts)
.sorted_by(|a, b| Ord::cmp(&a.1.acctn, &b.1.acctn))
.map(|((conv_acctn, conv_amount, rate), orig_p)| {
let running_total = *register_engine
.entry(conv_acctn.clone())
.and_modify(|v| {
*v += conv_amount;
})
.or_insert(conv_amount);
RegisterPosting {
post: orig_p,
amount: running_total,
target_commodity: conv_acctn.comm,
rate,
}
})
.collect();
let mut filt_postings: Vec<_> = register_postings
.into_iter()
.filter(|p| ras.eval(p))
.collect();
filt_postings.sort();
RegisterEntry {
txn,
posts: filt_postings,
}
})
.collect()
}