170 lines
4.8 KiB
Rust
170 lines
4.8 KiB
Rust
use std::{collections::HashMap, time::Instant};
|
|
|
|
use crate::core::{Amount, Ledger};
|
|
use chrono::NaiveDate;
|
|
use rust_decimal_macros::dec;
|
|
|
|
use super::{
|
|
base::{self, DataValue, Function},
|
|
functions::ComparisonFunction,
|
|
transaction::{PostingData, PostingField, TransactionField},
|
|
};
|
|
|
|
pub enum Query {
|
|
StartDate(NaiveDate),
|
|
EndDate(NaiveDate),
|
|
}
|
|
|
|
pub fn balance2(
|
|
ledger: &Ledger,
|
|
end_date: NaiveDate,
|
|
convert_to_unit: Option<&str>,
|
|
) -> HashMap<u32, Vec<Amount>> {
|
|
let q = ComparisonFunction::new(
|
|
"<=",
|
|
base::Query::from_field(PostingField::Transaction(TransactionField::Date)),
|
|
base::Query::from(DataValue::from(end_date)),
|
|
)
|
|
.unwrap();
|
|
|
|
let convert_to_unit = convert_to_unit.map(|u| ledger.get_unit_by_symbol(u).unwrap());
|
|
|
|
let postings = ledger
|
|
.get_transactions()
|
|
.iter()
|
|
.map(|t| {
|
|
t.get_postings().iter().map(|p| PostingData {
|
|
ledger,
|
|
posting: p,
|
|
parent_transaction: t,
|
|
})
|
|
})
|
|
.flatten();
|
|
|
|
let filtered_postings =
|
|
postings.filter(|data| q.evaluate(data).map(|v| bool::from(v)).unwrap_or(false));
|
|
|
|
let mut accounts = HashMap::new();
|
|
for posting_data in filtered_postings {
|
|
let posting = posting_data.posting;
|
|
let mut amount = *posting.get_amount();
|
|
if let Some(new_unit) = convert_to_unit {
|
|
if amount.unit_id != new_unit.get_id() {
|
|
let price = ledger.get_price_on_date(end_date, amount.unit_id, new_unit.get_id());
|
|
if let Some(price) = price {
|
|
amount = Amount {
|
|
value: amount.value * price,
|
|
unit_id: new_unit.get_id(),
|
|
};
|
|
}
|
|
}
|
|
}
|
|
let account_vals = accounts
|
|
.entry(posting.get_account_id())
|
|
.or_insert(HashMap::new());
|
|
let a = account_vals.entry(amount.unit_id).or_insert(dec!(0));
|
|
*a += amount.value;
|
|
}
|
|
|
|
accounts
|
|
.iter()
|
|
.map(|(&k, v)| {
|
|
(
|
|
k,
|
|
v.into_iter()
|
|
.map(|(&unit_id, &value)| Amount { value, unit_id })
|
|
.collect(),
|
|
)
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
pub fn balance3(ledger: &Ledger, query: &base::Query<PostingField>) -> HashMap<u32, Vec<Amount>> {
|
|
// let q = ComparisonFunction::new(
|
|
// "<=",
|
|
// base::Query::from_field(PostingField::Transaction(TransactionField::Date)),
|
|
// base::Query::from(DataValue::from(end_date)),
|
|
// )
|
|
// .unwrap();
|
|
|
|
let t0 = Instant::now();
|
|
|
|
let postings = ledger
|
|
.get_transactions()
|
|
.iter()
|
|
.map(|t| {
|
|
t.get_postings().iter().map(|p| PostingData {
|
|
ledger,
|
|
posting: p,
|
|
parent_transaction: t,
|
|
})
|
|
})
|
|
.flatten();
|
|
|
|
let t1 = Instant::now();
|
|
|
|
let filtered_postings =
|
|
postings.filter(|data| query.evaluate(data).map(|v| bool::from(v)).unwrap_or(false));
|
|
|
|
let t2 = Instant::now();
|
|
|
|
// println!("{:?} {:?}", t1-t0, t2-t1);
|
|
|
|
let mut accounts = HashMap::new();
|
|
for posting_data in filtered_postings {
|
|
let posting = posting_data.posting;
|
|
let amount = posting.get_amount();
|
|
let account_vals = accounts
|
|
.entry(posting.get_account_id())
|
|
.or_insert(HashMap::new());
|
|
let a = account_vals.entry(amount.unit_id).or_insert(dec!(0));
|
|
*a += amount.value;
|
|
}
|
|
|
|
accounts
|
|
.iter()
|
|
.map(|(&k, v)| {
|
|
(
|
|
k,
|
|
v.into_iter()
|
|
.map(|(&unit_id, &value)| Amount { value, unit_id })
|
|
.collect(),
|
|
)
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
pub fn balance(ledger: &Ledger, query: &[Query]) -> HashMap<u32, Vec<Amount>> {
|
|
let relevant_transactions = ledger.get_transactions().iter().filter(|txn| {
|
|
query.iter().all(|q| match q {
|
|
Query::StartDate(date) => txn.get_date() >= *date,
|
|
Query::EndDate(date) => txn.get_date() <= *date,
|
|
})
|
|
});
|
|
|
|
let mut accounts = HashMap::new();
|
|
|
|
for txn in relevant_transactions.clone() {
|
|
for posting in txn.get_postings() {
|
|
let amount = posting.get_amount();
|
|
let account_vals = accounts
|
|
.entry(posting.get_account_id())
|
|
.or_insert(HashMap::new());
|
|
let a = account_vals.entry(amount.unit_id).or_insert(dec!(0));
|
|
*a += amount.value;
|
|
}
|
|
}
|
|
|
|
accounts
|
|
.iter()
|
|
.map(|(&k, v)| {
|
|
(
|
|
k,
|
|
v.into_iter()
|
|
.map(|(&unit_id, &value)| Amount { value, unit_id })
|
|
.collect(),
|
|
)
|
|
})
|
|
.collect()
|
|
}
|