rex_app/modifier/
shared.rs

1use anyhow::{Result, anyhow};
2use chrono::{Days, Local, Months, NaiveDate, NaiveTime};
3use rex_db::ConnCache;
4use rex_db::models::{Balance, DateNature, FetchNature, NewSearch, NewTx, Tx, TxType};
5use rex_shared::models::{Dollar, LAST_POSSIBLE_TIME};
6
7use crate::utils::parse_amount_nature_cent;
8
9pub(crate) fn tidy_balances(date: NaiveDate, db_conn: &mut impl ConnCache) -> Result<()> {
10    let nature = FetchNature::Monthly;
11
12    let txs = Tx::get_txs(date, nature, db_conn)?;
13
14    let current_balance = Balance::get_balance(date, nature, db_conn)?;
15
16    let mut last_balance = Balance::get_last_balance(date, nature, db_conn)?;
17
18    for tx in txs {
19        match tx.tx_type.as_str().into() {
20            TxType::Income | TxType::Borrow | TxType::LendRepay => {
21                let method_id = tx.from_method;
22                *last_balance.get_mut(&method_id).unwrap() += tx.amount;
23            }
24            TxType::Expense | TxType::Lend | TxType::BorrowRepay => {
25                let method_id = tx.from_method;
26                *last_balance.get_mut(&method_id).unwrap() -= tx.amount;
27            }
28
29            TxType::Transfer => {
30                let from_method_id = tx.from_method;
31                let to_method_id = tx.to_method.as_ref().unwrap();
32
33                *last_balance.get_mut(&from_method_id).unwrap() -= tx.amount;
34                *last_balance.get_mut(to_method_id).unwrap() += tx.amount;
35            }
36        }
37    }
38
39    let mut to_insert_balance = Vec::new();
40
41    for mut balance in current_balance {
42        let method_id = balance.method_id;
43        let last_balance = *last_balance.get(&method_id).unwrap();
44
45        if balance.balance != last_balance {
46            balance.balance = last_balance.value();
47            to_insert_balance.push(balance);
48        }
49    }
50
51    for to_insert in to_insert_balance {
52        to_insert.insert(db_conn)?;
53    }
54
55    Ok(())
56}
57
58pub fn parse_tx_fields<'a>(
59    date: &'a str,
60    details: &'a str,
61    from_method: &'a str,
62    to_method: &'a str,
63    amount: &'a str,
64    tx_type: &'a str,
65    db_conn: &impl ConnCache,
66) -> Result<NewTx<'a>> {
67    let date = date.parse::<NaiveDate>()?;
68
69    let local_now = Local::now().naive_local();
70
71    let new_date = if date == local_now.date() {
72        local_now
73    } else {
74        date.and_time(NaiveTime::MIN)
75    };
76
77    let details = if details.is_empty() {
78        None
79    } else {
80        Some(details)
81    };
82
83    let amount = Dollar::new(amount.parse()?).cent().value();
84
85    let from_method = db_conn.cache().get_method_id(from_method)?;
86    let to_method = if to_method.is_empty() {
87        None
88    } else {
89        Some(db_conn.cache().get_method_id(to_method)?)
90    };
91
92    let new_tx = NewTx::new(new_date, details, from_method, to_method, amount, tx_type);
93    Ok(new_tx)
94}
95
96pub fn parse_search_fields<'a>(
97    date: &'a str,
98    details: &'a str,
99    from_method: &'a str,
100    to_method: &'a str,
101    amount: &'a str,
102    tx_type: &'a str,
103    tags: &'a str,
104    db_conn: &impl ConnCache,
105) -> Result<NewSearch<'a>> {
106    let date_nature = if date.is_empty() {
107        None
108    } else {
109        let split_date = date.trim().split('-').collect::<Vec<&str>>();
110
111        match split_date.len() {
112            1 => {
113                let year = split_date[0].parse::<i32>()?;
114
115                let start_date = NaiveDate::from_ymd_opt(year, 1, 1)
116                    .ok_or_else(|| anyhow!("{year} is an invalid year"))?
117                    .and_time(NaiveTime::MIN);
118
119                let end_date = NaiveDate::from_ymd_opt(year + 1, 1, 1)
120                    .ok_or_else(|| anyhow!("{year} is an invalid year"))?
121                    .and_time(LAST_POSSIBLE_TIME);
122
123                Some(DateNature::ByYear {
124                    start_date,
125                    end_date,
126                })
127            }
128            2 => {
129                let year = split_date[0].parse::<i32>()?;
130                let month = split_date[1].parse::<u32>()?;
131
132                let start_date = NaiveDate::from_ymd_opt(year, month, 1)
133                    .ok_or_else(|| anyhow!("{year} or {month} value is invalid"))?;
134
135                let end_date = start_date + Months::new(1) - Days::new(1);
136
137                let start_date = start_date.and_time(NaiveTime::MIN);
138                let end_date = end_date.and_time(LAST_POSSIBLE_TIME);
139
140                Some(DateNature::ByMonth {
141                    start_date,
142                    end_date,
143                })
144            }
145            3 => {
146                let date = date.parse::<NaiveDate>()?.and_time(NaiveTime::MIN);
147                Some(DateNature::Exact(date))
148            }
149            _ => None,
150        }
151    };
152
153    let details = if details.is_empty() {
154        None
155    } else {
156        Some(details)
157    };
158
159    let from_method = if from_method.is_empty() {
160        None
161    } else {
162        Some(db_conn.cache().get_method_id(from_method)?)
163    };
164
165    let to_method = if to_method.is_empty() {
166        None
167    } else {
168        Some(db_conn.cache().get_method_id(to_method)?)
169    };
170
171    let amount = if amount.is_empty() {
172        None
173    } else {
174        parse_amount_nature_cent(amount)?
175    };
176
177    let tx_type = if tx_type.is_empty() {
178        None
179    } else {
180        Some(tx_type)
181    };
182
183    let tags = if tags.is_empty() {
184        None
185    } else {
186        let tags = tags.split(',').map(str::trim).collect::<Vec<&str>>();
187        let tags = tags
188            .iter()
189            .map(|t| db_conn.cache().get_tag_id(t))
190            .filter_map(Result::ok)
191            .collect::<Vec<i32>>();
192
193        Some(tags)
194    };
195
196    let search_tx = NewSearch::new(
197        date_nature,
198        details,
199        tx_type,
200        from_method,
201        to_method,
202        amount,
203        tags,
204    );
205
206    Ok(search_tx)
207}