Lines
20 %
Functions
7.04 %
Branches
100 %
use chrono::{DateTime, Utc};
use finance::error::{FinanceError, ReportError};
use sqlx::types::Uuid;
use supp_macro::command;
use crate::{config::ConfigError, user::User};
use super::{CmdError, CmdResult, PeriodData, ReportData, ReportFilter, ReportMeta};
mod fetch;
mod filter;
mod period;
mod tree;
#[cfg(test)]
mod command_tests;
mod unit_tests;
use fetch::{
fetch_accounts, fetch_balance_splits_filtered_no_conversion,
fetch_balance_splits_filtered_with_conversion, fetch_balance_splits_no_conversion,
fetch_balance_splits_with_conversion, fetch_date_range_splits_filtered_no_conversion,
fetch_date_range_splits_filtered_with_conversion, fetch_date_range_splits_no_conversion,
fetch_date_range_splits_with_conversion, fetch_target_symbol,
};
use period::{generate_month_boundaries, generate_quarter_boundaries, generate_year_boundaries};
use tree::build_tree;
command! {
BalanceReport {
#[required]
user_id: Uuid,
#[optional]
target_commodity_id: Uuid,
as_of: DateTime<Utc>,
report_filter: ReportFilter,
} => {
let user = User { id: user_id };
let mut conn = user.get_connection().await.map_err(|err| {
log::error!("{}", t!("Database error: %{err}", err = err : {:?}));
ConfigError::DB
})?;
let accounts = fetch_accounts(&mut conn).await?;
let amounts = match (&target_commodity_id, &report_filter) {
(Some(tid), Some(f)) => {
let sym = fetch_target_symbol(&mut conn, *tid).await?;
fetch_balance_splits_filtered_with_conversion(&mut conn, *tid, &sym, as_of, f).await?
}
(Some(tid), None) => {
fetch_balance_splits_with_conversion(&mut conn, *tid, &sym, as_of).await?
(None, Some(f)) => {
fetch_balance_splits_filtered_no_conversion(&mut conn, as_of, f).await?
(None, None) => {
fetch_balance_splits_no_conversion(&mut conn, as_of).await?
let roots = build_tree(&accounts, &amounts);
let period = PeriodData { label: None, roots };
Ok(Some(CmdResult::Report(ReportData {
meta: ReportMeta {
date_from: None,
date_to: as_of,
target_commodity_id,
},
periods: vec![period],
})))
IncomeExpenseReport {
date_from: DateTime<Utc>,
date_to: DateTime<Utc>,
period_grouping: String,
let period_boundaries = match &period_grouping {
Some(g) => match g.as_str() {
"month" => generate_month_boundaries(date_from, date_to),
"quarter" => generate_quarter_boundaries(date_from, date_to),
"year" => generate_year_boundaries(date_from, date_to),
other => return Err(CmdError::Finance(FinanceError::Report(
ReportError::InvalidPeriodGrouping(other.to_string()),
))),
None => vec![],
let periods = if period_boundaries.is_empty() {
fetch_date_range_splits_filtered_with_conversion(&mut conn, *tid, &sym, date_from, date_to, f).await?
fetch_date_range_splits_with_conversion(&mut conn, *tid, &sym, date_from, date_to).await?
fetch_date_range_splits_filtered_no_conversion(&mut conn, date_from, date_to, f).await?
fetch_date_range_splits_no_conversion(&mut conn, date_from, date_to).await?
vec![PeriodData { label: None, roots }]
} else {
let mut periods = Vec::new();
for (label, pfrom, pto) in &period_boundaries {
fetch_date_range_splits_filtered_with_conversion(&mut conn, *tid, &sym, *pfrom, *pto, f).await?
fetch_date_range_splits_with_conversion(&mut conn, *tid, &sym, *pfrom, *pto).await?
fetch_date_range_splits_filtered_no_conversion(&mut conn, *pfrom, *pto, f).await?
fetch_date_range_splits_no_conversion(&mut conn, *pfrom, *pto).await?
periods.push(PeriodData {
label: Some(label.clone()),
roots,
});
periods
date_from: Some(date_from),
date_to: Some(date_to),
periods,
TrialBalance {