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}