summaryrefslogtreecommitdiff
path: root/rust
diff options
context:
space:
mode:
Diffstat (limited to 'rust')
-rw-r--r--rust/actualbudget_to_schist_transformer/src/transform_state/transform_date.rs14
-rw-r--r--rust/actualbudget_to_schist_transformer/src/transform_state/transform_transactions.rs184
2 files changed, 166 insertions, 32 deletions
diff --git a/rust/actualbudget_to_schist_transformer/src/transform_state/transform_date.rs b/rust/actualbudget_to_schist_transformer/src/transform_state/transform_date.rs
index 885ed84..57adae0 100644
--- a/rust/actualbudget_to_schist_transformer/src/transform_state/transform_date.rs
+++ b/rust/actualbudget_to_schist_transformer/src/transform_state/transform_date.rs
@@ -21,11 +21,11 @@ mod test {
assert_eq!(expected_date_utc, date_utc);
}
- #[test]
- fn when_utc_epoch_as_month_then_utc_epoch() {
- let actualbudget_date = ActualbudgetDate::from_i32(197001);
- let date_utc = transform_date(&actualbudget_date).unwrap();
- let expected_date_utc: DateUtc = "1970-01-01".parse().unwrap();
- assert_eq!(expected_date_utc, date_utc);
- }
+ #[test]
+ fn when_utc_epoch_as_month_then_utc_epoch() {
+ let actualbudget_date = ActualbudgetDate::from_i32(197001);
+ let date_utc = transform_date(&actualbudget_date).unwrap();
+ let expected_date_utc: DateUtc = "1970-01-01".parse().unwrap();
+ assert_eq!(expected_date_utc, date_utc);
+ }
}
diff --git a/rust/actualbudget_to_schist_transformer/src/transform_state/transform_transactions.rs b/rust/actualbudget_to_schist_transformer/src/transform_state/transform_transactions.rs
index 36f3d81..3e9b49a 100644
--- a/rust/actualbudget_to_schist_transformer/src/transform_state/transform_transactions.rs
+++ b/rust/actualbudget_to_schist_transformer/src/transform_state/transform_transactions.rs
@@ -8,27 +8,39 @@ pub fn transform_transactions(
actualbudget_transactions: &[ActualbudgetTransaction],
actualbudget_category_transfers_account: &Option<ActualbudgetAccount>,
) -> Result<(Vec<Transaction>, Vec<TransactionCategorisation>)> {
+ transform_transactions_without_context(actualbudget_transactions, actualbudget_category_transfers_account)
+ .with_context(|| "failed to transform actualbudget transactions to schist transactions")
+}
+
+fn transform_transactions_without_context(
+ actualbudget_transactions: &[ActualbudgetTransaction],
+ actualbudget_category_transfers_account: &Option<ActualbudgetAccount>,
+) -> Result<(Vec<Transaction>, Vec<TransactionCategorisation>)> {
let mut transactions = Vec::<Transaction>::new();
let mut transaction_categorisations = Vec::<TransactionCategorisation>::new();
- let mut actualbudget_transactions = actualbudget_transactions.to_vec();
- actualbudget_transactions.retain(|transaction|
- is_not_category_transfer(transaction, actualbudget_category_transfers_account)
- );
- let mut parent_actualbudget_transactions = actualbudget_transactions.to_vec();
- parent_actualbudget_transactions.retain(|t| t.is_parent);
- let mut child_actualbudget_transactions = actualbudget_transactions.to_vec();
- child_actualbudget_transactions.retain(|t| t.is_child);
- let mut atomic_actualbudget_transactions = actualbudget_transactions.to_vec();
- atomic_actualbudget_transactions.retain(|t| !t.is_parent && !t.is_child);
+ let actualbudget_transactions = actualbudget_transactions
+ .iter()
+ .filter(|t| is_not_category_transfer(t, actualbudget_category_transfers_account));
+ let parent_actualbudget_transactions: Vec<&ActualbudgetTransaction> = actualbudget_transactions
+ .clone()
+ .filter(|t| t.is_parent)
+ .collect();
+ let child_actualbudget_transactions: Vec<&ActualbudgetTransaction> = actualbudget_transactions
+ .clone()
+ .filter(|t| t.is_child)
+ .collect();
+ let atomic_actualbudget_transactions: Vec<&ActualbudgetTransaction> = actualbudget_transactions
+ .filter(|t| !t.is_parent && !t.is_child)
+ .collect();
for atomic_actualbudget_transaction in atomic_actualbudget_transactions {
let transaction_id = transform_id(&atomic_actualbudget_transaction.id);
transactions.push(Transaction {
id: transaction_id,
- description: atomic_actualbudget_transaction.notes.unwrap_or_else(String::new),
- payee: atomic_actualbudget_transaction.payee.unwrap_or_else(String::new),
+ description: atomic_actualbudget_transaction.notes.clone().unwrap_or_else(String::new),
+ payee: atomic_actualbudget_transaction.payee.clone().unwrap_or_else(String::new),
quantity: atomic_actualbudget_transaction.amount.try_into().unwrap(),
date: transform_date(&atomic_actualbudget_transaction.date)?,
account_id: transform_id(&atomic_actualbudget_transaction.account_id),
@@ -36,7 +48,11 @@ pub fn transform_transactions(
let actualbudget_category_id = atomic_actualbudget_transaction
.category_id
- .expect("actualbudget transaction should have been atomic, but in fact had no category ID");
+ .clone()
+ .with_context(|| format!(
+ "atomic actualbudget transaction {} did not have a category ID",
+ atomic_actualbudget_transaction.id,
+ ))?;
transaction_categorisations.push(TransactionCategorisation {
id: transaction_id,
@@ -48,19 +64,20 @@ pub fn transform_transactions(
}
for parent_actualbudget_transaction in parent_actualbudget_transactions {
- let mut child_actualbudget_transactions = child_actualbudget_transactions.to_vec();
- child_actualbudget_transactions.retain(|t| {
- t.parent_id
- .clone()
- .is_some_and(|parent_id| parent_id == parent_actualbudget_transaction.id.clone())
- });
+ let child_actualbudget_transactions = child_actualbudget_transactions
+ .iter()
+ .filter(|t| t
+ .parent_id
+ .clone()
+ .is_some_and(|parent_id| parent_id == parent_actualbudget_transaction.id.clone())
+ );
let transaction_id = transform_id(&parent_actualbudget_transaction.id);
transactions.push(Transaction {
id: transaction_id,
- description: parent_actualbudget_transaction.notes.unwrap_or_else(String::new),
- payee: parent_actualbudget_transaction.payee.unwrap_or_else(String::new),
+ description: parent_actualbudget_transaction.notes.clone().unwrap_or_else(String::new),
+ payee: parent_actualbudget_transaction.payee.clone().unwrap_or_else(String::new),
quantity: parent_actualbudget_transaction.amount.try_into().unwrap(),
date: transform_date(&parent_actualbudget_transaction.date)?,
account_id: transform_id(&parent_actualbudget_transaction.account_id),
@@ -69,11 +86,15 @@ pub fn transform_transactions(
for child_actualbudget_transaction in child_actualbudget_transactions {
let actualbudget_category_id = child_actualbudget_transaction
.category_id
- .expect("actualbudget transaction should have been a child transaction, but in fact had no category ID");
+ .clone()
+ .with_context(|| format!(
+ "actualbudget child transaction {} had no category ID",
+ child_actualbudget_transaction.id,
+ ))?;
transaction_categorisations.push(TransactionCategorisation {
id: transform_id(&child_actualbudget_transaction.id),
- description: child_actualbudget_transaction.notes.unwrap_or_else(String::new),
+ description: child_actualbudget_transaction.notes.clone().unwrap_or_else(String::new),
quantity: child_actualbudget_transaction.amount.try_into().unwrap(),
transaction_id,
category_id: transform_id(&actualbudget_category_id),
@@ -86,8 +107,121 @@ pub fn transform_transactions(
#[cfg(test)]
mod test {
+ use actualbudget_fakes::{actualbudget_account::make_fake_actualbudget_account, actualbudget_transaction::make_fake_actualbudget_transaction_builder};
+ use actualbudget_models::ActualbudgetDate;
+ use schist_models::date_utc::DateUtc;
+
+ use crate::transform_state::transform_id::transform_id;
+
+ use super::transform_transactions;
+
#[test]
- fn todo() {
- todo!()
+ fn when_one_category_transfer_transaction_then_returns_empty() {
+ let actualbudget_transactions = [
+ make_fake_actualbudget_transaction_builder("0")
+ .category_id(Some(String::from("0")))
+ .build()
+ .unwrap(),
+ ];
+ let actualbudget_category_transfers_account = make_fake_actualbudget_account("0");
+
+ let (transactions, transaction_categorisations) = transform_transactions(
+ &actualbudget_transactions,
+ &Some(actualbudget_category_transfers_account),
+ ).unwrap();
+
+ assert_eq!(0, transactions.len());
+ assert_eq!(0, transaction_categorisations.len());
+ }
+
+ #[test]
+ fn when_one_atomic_transaction_then_returns_transformed_transaction() {
+ let actualbudget_transactions = [
+ make_fake_actualbudget_transaction_builder("0")
+ .account_id(String::from("0"))
+ .amount(100)
+ .category_id(Some(String::from("0")))
+ .payee(Some(String::from("Gringotts")))
+ .notes(Some(String::from("Savings")))
+ .date(ActualbudgetDate::from_i32(19700101))
+ .build()
+ .unwrap(),
+ ];
+
+ let (transactions, transaction_categorisations) = transform_transactions(
+ &actualbudget_transactions,
+ &None,
+ ).unwrap();
+
+ assert_eq!(1, transactions.len());
+ let transaction = transactions.get(0).unwrap();
+ assert_eq!("Savings", transaction.description);
+ assert_eq!("Gringotts", transaction.payee);
+ assert_eq!(100, transaction.quantity);
+ assert_eq!(DateUtc::from_ymd(1970, 1, 1).unwrap(), transaction.date);
+ assert_eq!(transform_id("0"), transaction.account_id);
+
+ assert_eq!(1, transaction_categorisations.len());
+ let transaction_categorisation = transaction_categorisations.get(0).unwrap();
+ assert_eq!("", transaction_categorisation.description);
+ assert_eq!(100, transaction_categorisation.quantity);
+ assert_eq!(transaction.id, transaction_categorisation.transaction_id);
+ assert_eq!(transform_id("0"), transaction_categorisation.category_id);
+ }
+
+ #[test]
+ fn when_one_split_transaction_then_returns_transformed_transaction() {
+ let actualbudget_transactions = [
+ make_fake_actualbudget_transaction_builder("0")
+ .is_parent(true)
+ .amount(100)
+ .build()
+ .unwrap(),
+ make_fake_actualbudget_transaction_builder("1")
+ .amount(200)
+ .category_id(Some(String::from("0")))
+ .is_child(true)
+ .parent_id(Some(String::from("0")))
+ .notes(Some(String::from("notes A")))
+ .build()
+ .unwrap(),
+ make_fake_actualbudget_transaction_builder("2")
+ .amount(300)
+ .category_id(Some(String::from("0")))
+ .is_child(true)
+ .parent_id(Some(String::from("0")))
+ .notes(Some(String::from("notes B")))
+ .build()
+ .unwrap(),
+ ];
+
+ let (transactions, transaction_categorisations) = transform_transactions(
+ &actualbudget_transactions,
+ &None,
+ ).unwrap();
+
+ assert_eq!(1, transactions.len());
+ assert_eq!(2, transaction_categorisations.len());
+
+ let transaction = transactions.get(0).unwrap();
+ assert_eq!(100, transaction.quantity);
+
+ let transaction_categorisation_0 = transaction_categorisations
+ .iter()
+ .filter(|tc| tc.quantity == 200)
+ .last()
+ .unwrap();
+ assert_eq!(transaction.id, transaction_categorisation_0.transaction_id);
+ assert_eq!(transform_id("0"), transaction_categorisation_0.category_id);
+ assert_eq!("notes A", transaction_categorisation_0.description);
+
+ let transaction_categorisation_1 = transaction_categorisations
+ .iter()
+ .filter(|tc| tc.quantity == 300)
+ .last()
+ .unwrap();
+ assert_eq!(transaction.id, transaction_categorisation_1.transaction_id);
+ assert_eq!(transform_id("0"), transaction_categorisation_1.category_id);
+ assert_eq!("notes B", transaction_categorisation_1.description);
}
}