Skip to main content

server/command/report/
balance.rs

1use chrono::{DateTime, Utc};
2use sqlx::types::Uuid;
3use supp_macro::command;
4
5use crate::{config::ConfigError, user::User};
6
7use super::super::{CmdError, CmdResult, PeriodData, ReportData, ReportFilter, ReportMeta};
8use super::fetch::{
9    fetch_accounts, fetch_balance_splits_filtered_no_conversion,
10    fetch_balance_splits_filtered_with_conversion, fetch_balance_splits_no_conversion,
11    fetch_balance_splits_with_conversion, fetch_date_range_splits_filtered_no_conversion,
12    fetch_date_range_splits_filtered_with_conversion, fetch_date_range_splits_no_conversion,
13    fetch_date_range_splits_with_conversion, fetch_target_symbol,
14};
15use super::tree::{AccountAmounts, build_tree};
16
17async fn fetch_snapshot(
18    conn: &mut sqlx::PgConnection,
19    target_commodity_id: Option<Uuid>,
20    as_of: Option<DateTime<Utc>>,
21    report_filter: Option<&ReportFilter>,
22) -> Result<AccountAmounts, CmdError> {
23    match (target_commodity_id, report_filter) {
24        (Some(tid), Some(f)) => {
25            let sym = fetch_target_symbol(conn, tid).await?;
26            fetch_balance_splits_filtered_with_conversion(conn, tid, &sym, as_of, f).await
27        }
28        (Some(tid), None) => {
29            let sym = fetch_target_symbol(conn, tid).await?;
30            fetch_balance_splits_with_conversion(conn, tid, &sym, as_of).await
31        }
32        (None, Some(f)) => fetch_balance_splits_filtered_no_conversion(conn, as_of, f).await,
33        (None, None) => fetch_balance_splits_no_conversion(conn, as_of).await,
34    }
35}
36
37async fn fetch_range(
38    conn: &mut sqlx::PgConnection,
39    target_commodity_id: Option<Uuid>,
40    from: DateTime<Utc>,
41    to: DateTime<Utc>,
42    report_filter: Option<&ReportFilter>,
43) -> Result<AccountAmounts, CmdError> {
44    match (target_commodity_id, report_filter) {
45        (Some(tid), Some(f)) => {
46            let sym = fetch_target_symbol(conn, tid).await?;
47            fetch_date_range_splits_filtered_with_conversion(conn, tid, &sym, from, to, f).await
48        }
49        (Some(tid), None) => {
50            let sym = fetch_target_symbol(conn, tid).await?;
51            fetch_date_range_splits_with_conversion(conn, tid, &sym, from, to).await
52        }
53        (None, Some(f)) => fetch_date_range_splits_filtered_no_conversion(conn, from, to, f).await,
54        (None, None) => fetch_date_range_splits_no_conversion(conn, from, to).await,
55    }
56}
57
58command! {
59    BalanceReport {
60        #[required]
61        user_id: Uuid,
62        #[optional]
63        target_commodity_id: Uuid,
64        #[optional]
65        date_from: DateTime<Utc>,
66        #[optional]
67        as_of: DateTime<Utc>,
68        #[optional]
69        report_filter: ReportFilter,
70    } => {
71        let user = User { id: user_id };
72        let mut conn = user.get_connection().await.map_err(|err| {
73            log::error!("{}", t!("Database error: %{err}", err = err : {:?}));
74            ConfigError::DB
75        })?;
76
77        let accounts = fetch_accounts(&mut conn).await?;
78
79        let amounts = match date_from {
80            Some(from) => {
81                let to = as_of.unwrap_or_else(Utc::now);
82                fetch_range(&mut conn, target_commodity_id, from, to, report_filter.as_ref()).await?
83            }
84            None => fetch_snapshot(&mut conn, target_commodity_id, as_of, report_filter.as_ref()).await?,
85        };
86
87        let roots = build_tree(&accounts, &amounts);
88        let period = PeriodData { label: None, roots };
89
90        Ok(Some(CmdResult::Report(ReportData {
91            meta: ReportMeta {
92                date_from,
93                date_to: as_of,
94                target_commodity_id,
95            },
96            periods: vec![period],
97        })))
98    }
99}