summaryrefslogtreecommitdiff
path: root/transformers
diff options
context:
space:
mode:
authorJoe Carstairs <me@joeac.net>2025-08-25 16:35:45 +0000
committerjoeac <me@joeac.net>2025-08-25 16:35:45 +0000
commitfe85652e4d3e8906824baddaafd05d19105579b8 (patch)
tree31b56e31cbf289a34ec1d6707becf3c60933b56f /transformers
parentd082a30de6a0cbd1fbe2ab5a4316db0891004129 (diff)
task-072
Co-authored-by: Joe Carstairs <jcarstairs@scottlogic.com> Reviewed-on: https://git.joeac.net/joeac/schist/pulls/3 Co-authored-by: Joe Carstairs <me@joeac.net> Co-committed-by: Joe Carstairs <me@joeac.net>
Diffstat (limited to 'transformers')
-rw-r--r--transformers/actualbudget_transformer/actualbudget_fakes/src/actualbudget_category.rs1
-rw-r--r--transformers/actualbudget_transformer/actualbudget_fakes/src/actualbudget_category_group.rs13
-rw-r--r--transformers/actualbudget_transformer/actualbudget_fakes/src/actualbudget_transaction.rs3
-rw-r--r--transformers/actualbudget_transformer/actualbudget_fakes/src/lib.rs1
-rw-r--r--transformers/actualbudget_transformer/actualbudget_models/src/actualbudget_category.rs1
-rw-r--r--transformers/actualbudget_transformer/actualbudget_models/src/actualbudget_category_group.rs9
-rw-r--r--transformers/actualbudget_transformer/actualbudget_models/src/actualbudget_schema.rs12
-rw-r--r--transformers/actualbudget_transformer/actualbudget_models/src/actualbudget_transaction.rs11
-rw-r--r--transformers/actualbudget_transformer/actualbudget_models/src/lib.rs3
-rw-r--r--transformers/actualbudget_transformer/actualbudget_queries/src/actualbudget_category_groups.rs11
-rw-r--r--transformers/actualbudget_transformer/actualbudget_queries/src/lib.rs1
-rw-r--r--transformers/actualbudget_transformer/actualbudget_queries/tests/resources/actualbudget_test_db.sqlitebin528384 -> 573440 bytes
-rw-r--r--transformers/actualbudget_transformer/actualbudget_queries/tests/resources/actualbudget_test_db_metadata.json10
-rw-r--r--transformers/actualbudget_transformer/actualbudget_to_schist_transformer/Cargo.toml1
-rw-r--r--transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/actualbudget_state.rs24
-rw-r--r--transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state.rs (renamed from transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/mod.rs)20
-rw-r--r--transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/is_bucket_transfer.rs46
-rw-r--r--transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/is_category_transfer.rs18
-rw-r--r--transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_account_transfers.rs18
-rw-r--r--transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_accounts.rs182
-rw-r--r--transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_categories.rs143
-rw-r--r--transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_categories_to_buckets.rs263
-rw-r--r--transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_state.rs49
-rw-r--r--transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_transactions.rs76
-rw-r--r--transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_transactions_to_bucket_transfers.rs (renamed from transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_category_transfers.rs)147
-rw-r--r--transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_zero_budgets.rs6
-rw-r--r--transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_zero_budgets_to_drips.rs (renamed from transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_budget_drips.rs)2
-rw-r--r--transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_zero_budgets_to_pipes.rs211
-rw-r--r--transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/common/account_matchers.rs8
-rw-r--r--transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/common/bucket_matchers.rs25
-rw-r--r--transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/common/bucket_names.rs22
-rw-r--r--transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/common/drip_matchers.rs (renamed from transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/common/budget_drip_matchers.rs)45
-rw-r--r--transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/common/mod.rs10
-rw-r--r--transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/common/pipe_matchers.rs36
-rw-r--r--transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/common/transaction_matchers.rs112
-rw-r--r--transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/resources/actualbudget_test_db.sqlitebin73 -> 573440 bytes
-rw-r--r--transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/run.rs26
37 files changed, 1057 insertions, 509 deletions
diff --git a/transformers/actualbudget_transformer/actualbudget_fakes/src/actualbudget_category.rs b/transformers/actualbudget_transformer/actualbudget_fakes/src/actualbudget_category.rs
index 84fb32a..a02e672 100644
--- a/transformers/actualbudget_transformer/actualbudget_fakes/src/actualbudget_category.rs
+++ b/transformers/actualbudget_transformer/actualbudget_fakes/src/actualbudget_category.rs
@@ -8,6 +8,7 @@ pub fn make_fake_actualbudget_category_builder(id: &str) -> ActualbudgetCategory
let mut builder = ActualbudgetCategoryBuilder::default();
builder
.id(String::from(id))
+ .group_id(None)
.name(String::new());
return builder
}
diff --git a/transformers/actualbudget_transformer/actualbudget_fakes/src/actualbudget_category_group.rs b/transformers/actualbudget_transformer/actualbudget_fakes/src/actualbudget_category_group.rs
new file mode 100644
index 0000000..8695e04
--- /dev/null
+++ b/transformers/actualbudget_transformer/actualbudget_fakes/src/actualbudget_category_group.rs
@@ -0,0 +1,13 @@
+use actualbudget_models::{ActualbudgetCategoryGroup, ActualbudgetCategoryGroupBuilder};
+
+pub fn make_fake_actualbudget_category_group(id: &str) -> ActualbudgetCategoryGroup {
+ make_fake_actualbudget_category_group_builder(id).build().unwrap()
+}
+
+pub fn make_fake_actualbudget_category_group_builder(id: &str) -> ActualbudgetCategoryGroupBuilder {
+ let mut builder = ActualbudgetCategoryGroupBuilder::default();
+ builder
+ .id(String::from(id))
+ .name(None);
+ return builder
+}
diff --git a/transformers/actualbudget_transformer/actualbudget_fakes/src/actualbudget_transaction.rs b/transformers/actualbudget_transformer/actualbudget_fakes/src/actualbudget_transaction.rs
index 548424d..f897444 100644
--- a/transformers/actualbudget_transformer/actualbudget_fakes/src/actualbudget_transaction.rs
+++ b/transformers/actualbudget_transformer/actualbudget_fakes/src/actualbudget_transaction.rs
@@ -17,6 +17,7 @@ pub fn make_fake_actualbudget_transaction_builder(id: &str) -> ActualbudgetTrans
.payee(None)
.notes(None)
.date(ActualbudgetDate::from_i32(19700101))
- .transfer_id(None);
+ .transfer_id(None)
+ .starting_balance_flag(0);
return builder
}
diff --git a/transformers/actualbudget_transformer/actualbudget_fakes/src/lib.rs b/transformers/actualbudget_transformer/actualbudget_fakes/src/lib.rs
index 815c962..dbd0849 100644
--- a/transformers/actualbudget_transformer/actualbudget_fakes/src/lib.rs
+++ b/transformers/actualbudget_transformer/actualbudget_fakes/src/lib.rs
@@ -1,5 +1,6 @@
pub mod actualbudget_account;
pub mod actualbudget_category;
+pub mod actualbudget_category_group;
pub mod actualbudget_payee;
pub mod actualbudget_transaction;
pub mod actualbudget_zero_budget;
diff --git a/transformers/actualbudget_transformer/actualbudget_models/src/actualbudget_category.rs b/transformers/actualbudget_transformer/actualbudget_models/src/actualbudget_category.rs
index bf35450..84ad4b0 100644
--- a/transformers/actualbudget_transformer/actualbudget_models/src/actualbudget_category.rs
+++ b/transformers/actualbudget_transformer/actualbudget_models/src/actualbudget_category.rs
@@ -5,5 +5,6 @@ use diesel::{prelude::Queryable, Selectable};
#[diesel(table_name = crate::actualbudget_schema::categories)]
pub struct ActualbudgetCategory {
pub id: String,
+ pub group_id: Option<String>,
pub name: String,
}
diff --git a/transformers/actualbudget_transformer/actualbudget_models/src/actualbudget_category_group.rs b/transformers/actualbudget_transformer/actualbudget_models/src/actualbudget_category_group.rs
new file mode 100644
index 0000000..8ec21f4
--- /dev/null
+++ b/transformers/actualbudget_transformer/actualbudget_models/src/actualbudget_category_group.rs
@@ -0,0 +1,9 @@
+use derive_builder::Builder;
+use diesel::prelude::*;
+
+#[derive(Builder, Clone, Queryable, Selectable)]
+#[diesel(table_name = crate::actualbudget_schema::category_groups)]
+pub struct ActualbudgetCategoryGroup {
+ pub id: String,
+ pub name: Option<String>,
+}
diff --git a/transformers/actualbudget_transformer/actualbudget_models/src/actualbudget_schema.rs b/transformers/actualbudget_transformer/actualbudget_models/src/actualbudget_schema.rs
index 4e1ba24..3a26344 100644
--- a/transformers/actualbudget_transformer/actualbudget_models/src/actualbudget_schema.rs
+++ b/transformers/actualbudget_transformer/actualbudget_models/src/actualbudget_schema.rs
@@ -8,11 +8,21 @@ diesel::table! {
diesel::table! {
categories (id) {
id -> Text,
+ #[sql_name = "cat_group"]
+ group_id -> Nullable<Text>,
name -> Text,
}
}
diesel::table! {
+ category_groups (id) {
+ id -> Text,
+ name -> Nullable<Text>,
+ }
+}
+
+
+diesel::table! {
v_transactions (id) {
id -> Text,
is_parent -> Bool,
@@ -27,10 +37,10 @@ diesel::table! {
notes -> Nullable<Text>,
date -> Integer,
transfer_id -> Nullable<Text>,
+ starting_balance_flag -> Integer,
// imported_id -> Text,
// error -> Text,
// imported_payee -> Text,
- // starting_balance_flag -> Integer,
// sort_order -> Real,
// cleared -> Integer,
// reconciled -> Integer,
diff --git a/transformers/actualbudget_transformer/actualbudget_models/src/actualbudget_transaction.rs b/transformers/actualbudget_transformer/actualbudget_models/src/actualbudget_transaction.rs
index c8ac522..d6270da 100644
--- a/transformers/actualbudget_transformer/actualbudget_models/src/actualbudget_transaction.rs
+++ b/transformers/actualbudget_transformer/actualbudget_models/src/actualbudget_transaction.rs
@@ -3,7 +3,9 @@ use diesel::prelude::{Identifiable, Queryable, QueryableByName, Selectable};
use crate::actualbudget_date::ActualbudgetDate;
-#[derive(Builder, Debug, PartialEq, Eq, Identifiable, QueryableByName, Queryable, Selectable, Clone)]
+#[derive(
+ Builder, Debug, PartialEq, Eq, Identifiable, QueryableByName, Queryable, Selectable, Clone,
+)]
#[diesel(table_name = crate::actualbudget_schema::v_transactions)]
pub struct ActualbudgetTransaction {
pub id: String,
@@ -17,4 +19,11 @@ pub struct ActualbudgetTransaction {
pub notes: Option<String>,
pub date: ActualbudgetDate,
pub transfer_id: Option<String>,
+ pub starting_balance_flag: i32,
+}
+
+impl ActualbudgetTransaction {
+ pub fn is_starting_balance(&self) -> bool {
+ self.starting_balance_flag != 0
+ }
}
diff --git a/transformers/actualbudget_transformer/actualbudget_models/src/lib.rs b/transformers/actualbudget_transformer/actualbudget_models/src/lib.rs
index 2d7d46c..b46b287 100644
--- a/transformers/actualbudget_transformer/actualbudget_models/src/lib.rs
+++ b/transformers/actualbudget_transformer/actualbudget_models/src/lib.rs
@@ -1,5 +1,6 @@
mod actualbudget_account;
mod actualbudget_category;
+mod actualbudget_category_group;
mod actualbudget_date;
mod actualbudget_payee;
mod actualbudget_transaction;
@@ -11,6 +12,8 @@ pub use actualbudget_account::ActualbudgetAccount;
pub use actualbudget_account::ActualbudgetAccountBuilder;
pub use actualbudget_category::ActualbudgetCategory;
pub use actualbudget_category::ActualbudgetCategoryBuilder;
+pub use actualbudget_category_group::ActualbudgetCategoryGroup;
+pub use actualbudget_category_group::ActualbudgetCategoryGroupBuilder;
pub use actualbudget_date::ActualbudgetDate;
pub use actualbudget_payee::ActualbudgetPayee;
pub use actualbudget_payee::ActualbudgetPayeeBuilder;
diff --git a/transformers/actualbudget_transformer/actualbudget_queries/src/actualbudget_category_groups.rs b/transformers/actualbudget_transformer/actualbudget_queries/src/actualbudget_category_groups.rs
new file mode 100644
index 0000000..8d90765
--- /dev/null
+++ b/transformers/actualbudget_transformer/actualbudget_queries/src/actualbudget_category_groups.rs
@@ -0,0 +1,11 @@
+use actualbudget_models::ActualbudgetCategoryGroup;
+use actualbudget_schema::actualbudget_schema::category_groups::dsl::category_groups;
+use diesel::{result::Error, QueryDsl, RunQueryDsl, SelectableHelper, SqliteConnection};
+
+pub fn get_all_actualbudget_category_groups(
+ connection: &mut SqliteConnection
+) -> Result<Vec<ActualbudgetCategoryGroup>, Error> {
+ category_groups
+ .select(ActualbudgetCategoryGroup::as_select())
+ .load(connection)
+}
diff --git a/transformers/actualbudget_transformer/actualbudget_queries/src/lib.rs b/transformers/actualbudget_transformer/actualbudget_queries/src/lib.rs
index ca3f6ea..0fffcbb 100644
--- a/transformers/actualbudget_transformer/actualbudget_queries/src/lib.rs
+++ b/transformers/actualbudget_transformer/actualbudget_queries/src/lib.rs
@@ -1,5 +1,6 @@
pub mod actualbudget_accounts;
pub mod actualbudget_categories;
+pub mod actualbudget_category_groups;
pub mod actualbudget_payees;
pub mod actualbudget_transactions;
pub mod actualbudget_zero_budgets;
diff --git a/transformers/actualbudget_transformer/actualbudget_queries/tests/resources/actualbudget_test_db.sqlite b/transformers/actualbudget_transformer/actualbudget_queries/tests/resources/actualbudget_test_db.sqlite
index 4146bb0..5f89924 100644
--- a/transformers/actualbudget_transformer/actualbudget_queries/tests/resources/actualbudget_test_db.sqlite
+++ b/transformers/actualbudget_transformer/actualbudget_queries/tests/resources/actualbudget_test_db.sqlite
Binary files differ
diff --git a/transformers/actualbudget_transformer/actualbudget_queries/tests/resources/actualbudget_test_db_metadata.json b/transformers/actualbudget_transformer/actualbudget_queries/tests/resources/actualbudget_test_db_metadata.json
index 006b23d..c88313a 100644
--- a/transformers/actualbudget_transformer/actualbudget_queries/tests/resources/actualbudget_test_db_metadata.json
+++ b/transformers/actualbudget_transformer/actualbudget_queries/tests/resources/actualbudget_test_db_metadata.json
@@ -1,7 +1,7 @@
{
- "id": "My-Finances-1-36ec11a",
- "budgetName": "My Finances 1",
- "userId": "fec9ba17-c260-4f55-8d44-676ef0ec5da9",
- "lastScheduleRun": "2024-11-21",
- "resetClock": true
+ "id": "My-Finances-1-36ec11a",
+ "budgetName": "My Finances 1",
+ "userId": "fec9ba17-c260-4f55-8d44-676ef0ec5da9",
+ "lastScheduleRun": "2025-08-16",
+ "resetClock": true
}
diff --git a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/Cargo.toml b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/Cargo.toml
index 51febe1..202d041 100644
--- a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/Cargo.toml
+++ b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/Cargo.toml
@@ -12,6 +12,7 @@ diesel = { workspace = true, features = ["sqlite"] }
diesel_migrations = { workspace = true }
itertools = { workspace = true }
libsqlite3-sys = { workspace = true, features = ["bundled"] }
+schist_fakes = { workspace = true }
schist_models = { workspace = true }
schist_queries = { workspace = true }
schist_traits = { workspace = true }
diff --git a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/actualbudget_state.rs b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/actualbudget_state.rs
index 5379b62..cda63b1 100644
--- a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/actualbudget_state.rs
+++ b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/actualbudget_state.rs
@@ -1,7 +1,13 @@
use std::cmp::min;
-use actualbudget_models::{ActualbudgetAccount, ActualbudgetCategory, ActualbudgetDate, ActualbudgetPayee, ActualbudgetTransaction, ActualbudgetZeroBudget};
-use actualbudget_queries::{actualbudget_accounts::{get_all_actualbudget_accounts, get_category_transfers_account}, actualbudget_categories::get_all_actualbudget_categories, actualbudget_payees::get_all_actualbudget_payees, actualbudget_transactions::{get_all_actualbudget_transactions, get_first_actualbudget_transaction_date}, actualbudget_zero_budgets::{get_all_actualbudget_zero_budgets, get_first_zero_budget_date}};
+use actualbudget_models::{
+ ActualbudgetAccount, ActualbudgetCategory, ActualbudgetCategoryGroup, ActualbudgetDate, ActualbudgetPayee, ActualbudgetTransaction, ActualbudgetZeroBudget
+};
+use actualbudget_queries::{
+ actualbudget_accounts::get_all_actualbudget_accounts, actualbudget_categories::get_all_actualbudget_categories, actualbudget_category_groups::get_all_actualbudget_category_groups, actualbudget_payees::get_all_actualbudget_payees, actualbudget_transactions::{
+ get_all_actualbudget_transactions, get_first_actualbudget_transaction_date,
+ }, actualbudget_zero_budgets::{get_all_actualbudget_zero_budgets, get_first_zero_budget_date}
+};
use anyhow::Result;
use diesel::SqliteConnection;
@@ -9,23 +15,25 @@ pub struct ActualbudgetState {
pub actualbudget_first_activity_date: ActualbudgetDate,
pub actualbudget_accounts: Vec<ActualbudgetAccount>,
pub actualbudget_categories: Vec<ActualbudgetCategory>,
+ pub actualbudget_category_groups: Vec<ActualbudgetCategoryGroup>,
pub actualbudget_payees: Vec<ActualbudgetPayee>,
pub actualbudget_zero_budgets: Vec<ActualbudgetZeroBudget>,
- pub actualbudget_category_transfers_account: Option<ActualbudgetAccount>,
pub actualbudget_transactions: Vec<ActualbudgetTransaction>,
}
-pub fn read_actualbudget_state(actualbudget_connection: &mut SqliteConnection) -> Result<ActualbudgetState> {
+pub fn read_actualbudget_state(
+ actualbudget_connection: &mut SqliteConnection,
+) -> Result<ActualbudgetState> {
Ok(ActualbudgetState {
actualbudget_first_activity_date: min(
- get_first_actualbudget_transaction_date(actualbudget_connection)?,
- get_first_zero_budget_date(actualbudget_connection)?,
- ),
+ get_first_actualbudget_transaction_date(actualbudget_connection)?,
+ get_first_zero_budget_date(actualbudget_connection)?,
+ ),
actualbudget_accounts: get_all_actualbudget_accounts(actualbudget_connection)?,
actualbudget_categories: get_all_actualbudget_categories(actualbudget_connection)?,
+ actualbudget_category_groups: get_all_actualbudget_category_groups(actualbudget_connection)?,
actualbudget_payees: get_all_actualbudget_payees(actualbudget_connection)?,
actualbudget_zero_budgets: get_all_actualbudget_zero_budgets(actualbudget_connection)?,
- actualbudget_category_transfers_account: get_category_transfers_account(actualbudget_connection)?,
actualbudget_transactions: get_all_actualbudget_transactions(actualbudget_connection)?,
})
}
diff --git a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/mod.rs b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state.rs
index 5fadc2c..a116649 100644
--- a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/mod.rs
+++ b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state.rs
@@ -1,27 +1,27 @@
mod is_account_transfer;
-mod is_category_transfer;
+mod is_bucket_transfer;
mod transform_account_transfers;
mod transform_accounts;
-mod transform_budget_drips;
-mod transform_categories;
-mod transform_category_transfers;
+mod transform_categories_to_buckets;
mod transform_date;
mod transform_id;
mod transform_state;
mod transform_transactions;
-mod transform_zero_budgets;
+mod transform_transactions_to_bucket_transfers;
+mod transform_zero_budgets_to_drips;
+mod transform_zero_budgets_to_pipes;
use is_account_transfer::is_account_transfer;
use is_account_transfer::is_not_account_transfer;
-use is_category_transfer::is_category_transfer;
-use is_category_transfer::is_not_category_transfer;
+use is_bucket_transfer::is_bucket_transfer;
+use is_bucket_transfer::is_not_bucket_transfer;
use transform_account_transfers::transform_account_transfers;
use transform_accounts::transform_accounts;
-use transform_categories::transform_categories;
-use transform_category_transfers::transform_category_transfers;
+use transform_categories_to_buckets::transform_categories_to_buckets;
use transform_date::transform_date;
use transform_id::transform_id;
use transform_transactions::transform_transactions;
-use transform_zero_budgets::transform_zero_budgets;
+use transform_transactions_to_bucket_transfers::transform_transactions_to_bucket_transfers;
+use transform_zero_budgets_to_pipes::transform_zero_budgets_to_pipes;
pub use transform_state::transform_state;
diff --git a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/is_bucket_transfer.rs b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/is_bucket_transfer.rs
new file mode 100644
index 0000000..188697e
--- /dev/null
+++ b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/is_bucket_transfer.rs
@@ -0,0 +1,46 @@
+use actualbudget_models::ActualbudgetTransaction;
+
+pub fn is_bucket_transfer(
+ transaction: &ActualbudgetTransaction,
+ transactions: &[ActualbudgetTransaction],
+) -> bool {
+ is_parent_bucket_transfer(transaction, transactions)
+ || is_child_bucket_transfer(transaction, transactions)
+}
+
+fn is_parent_bucket_transfer(
+ transaction: &ActualbudgetTransaction,
+ transactions: &[ActualbudgetTransaction],
+) -> bool {
+ if !transaction.is_parent {
+ return false;
+ }
+
+ let sum_of_children_amounts: i32 = transactions
+ .iter()
+ .filter(|t| is_parent_and_child(transaction, t))
+ .map(|t| t.amount)
+ .sum();
+ sum_of_children_amounts == 0
+}
+
+fn is_child_bucket_transfer(
+ transaction: &ActualbudgetTransaction,
+ transactions: &[ActualbudgetTransaction],
+) -> bool {
+ transaction.is_child
+ && transactions
+ .iter()
+ .any(|t| is_parent_and_child(t, transaction))
+}
+
+fn is_parent_and_child(parent: &ActualbudgetTransaction, child: &ActualbudgetTransaction) -> bool {
+ parent.is_parent && child.is_child && child.parent_id.clone().is_some_and(|id| id == parent.id)
+}
+
+pub fn is_not_bucket_transfer(
+ transaction: &ActualbudgetTransaction,
+ transactions: &[ActualbudgetTransaction],
+) -> bool {
+ !is_bucket_transfer(transaction, transactions)
+}
diff --git a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/is_category_transfer.rs b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/is_category_transfer.rs
deleted file mode 100644
index c82387c..0000000
--- a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/is_category_transfer.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-use actualbudget_models::{ActualbudgetAccount, ActualbudgetTransaction};
-
-pub fn is_category_transfer(
- transaction: &ActualbudgetTransaction,
- actualbudget_category_transfers_account: &Option<ActualbudgetAccount>
-) -> bool {
- match actualbudget_category_transfers_account {
- None => false,
- Some(acc) => transaction.account_id.clone() == acc.id.clone(),
- }
-}
-
-pub fn is_not_category_transfer(
- transaction: &ActualbudgetTransaction,
- actualbudget_category_transfers_account: &Option<ActualbudgetAccount>
-) -> bool {
- !is_category_transfer(transaction, actualbudget_category_transfers_account)
-}
diff --git a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_account_transfers.rs b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_account_transfers.rs
index c41b812..a116086 100644
--- a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_account_transfers.rs
+++ b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_account_transfers.rs
@@ -112,7 +112,7 @@ fn transform_account_transfer(
mod test {
use actualbudget_fakes::actualbudget_transaction::make_fake_actualbudget_transaction_builder;
use actualbudget_models::ActualbudgetDate;
- use schist_models::DateUtc;
+ use schist_models::{AccountTransfer, DateUtc};
use crate::transform_state::transform_id::transform_id;
@@ -147,14 +147,14 @@ mod test {
assert_eq!(0, remaining_transactions.len());
assert_eq!(1, account_transfers.len());
let account_transfer = account_transfers.get(0).unwrap();
- assert_eq!(
- "2024-08-01".parse::<DateUtc>().unwrap(),
- account_transfer.date
- );
- assert_eq!("My notes", account_transfer.description);
- assert_eq!(100, account_transfer.amount);
- assert_eq!(transform_id("0"), account_transfer.from_account_id);
- assert_eq!(transform_id("1"), account_transfer.to_account_id);
+ assert_eq!(AccountTransfer {
+ id: account_transfer.id,
+ amount: 100,
+ date: "2024-08-01".parse().unwrap(),
+ description: String::from("My notes"),
+ from_account_id: transform_id("0"),
+ to_account_id: transform_id("1"),
+ }, *account_transfer);
}
#[test]
diff --git a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_accounts.rs b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_accounts.rs
index c900da7..76eaf0c 100644
--- a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_accounts.rs
+++ b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_accounts.rs
@@ -1,32 +1,194 @@
-use actualbudget_models::ActualbudgetAccount;
+use actualbudget_models::{ActualbudgetAccount, ActualbudgetTransaction};
use schist_models::{Account, DateUtc};
-use super::transform_id;
+use super::{is_bucket_transfer, transform_id};
pub fn transform_accounts(
actualbudget_accounts: &[ActualbudgetAccount],
- actualbudget_category_transfers_account: &Option<ActualbudgetAccount>,
+ actualbudget_transactions: &[ActualbudgetTransaction],
first_activity_date: &DateUtc,
) -> Vec<Account> {
actualbudget_accounts
.iter()
- .filter(|a| {
- actualbudget_category_transfers_account
- .clone()
- .is_some_and(|cta| cta.id != a.id)
- })
- .map(|a| transform_account(a, first_activity_date))
+ .filter(|a| !is_empty_account(a, actualbudget_transactions))
+ .map(|a| transform_account(a, actualbudget_transactions, first_activity_date))
.collect()
}
+fn is_empty_account(
+ actualbudget_account: &ActualbudgetAccount,
+ actualbudget_transactions: &[ActualbudgetTransaction],
+) -> bool {
+ actualbudget_transactions.iter().all(|t| {
+ t.is_starting_balance() && t.amount == 0
+ || t.account_id != actualbudget_account.id
+ || is_bucket_transfer(t, actualbudget_transactions)
+ })
+}
+
fn transform_account(
actualbudget_account: &ActualbudgetAccount,
+ actualbudget_transactions: &[ActualbudgetTransaction],
opening_date: &DateUtc,
) -> Account {
+ let opening_balance = actualbudget_transactions
+ .iter()
+ .filter(|t| t.is_starting_balance())
+ .filter(|t| t.account_id == actualbudget_account.id)
+ .map(|t| t.amount)
+ .sum();
+
Account {
id: transform_id(&actualbudget_account.id.clone()),
name: actualbudget_account.name.clone(),
- opening_balance: 0,
+ opening_balance,
opening_date: opening_date.clone(),
}
}
+
+#[cfg(test)]
+mod test {
+ use actualbudget_fakes::{
+ actualbudget_account::make_fake_actualbudget_account_builder,
+ actualbudget_transaction::make_fake_actualbudget_transaction_builder,
+ };
+ use schist_models::{Account, DateUtc};
+
+ use super::{transform_accounts, transform_id};
+
+ #[test]
+ fn given_starting_balances_then_sets_opening_balances() {
+ let actualbudget_accounts = vec![
+ make_fake_actualbudget_account_builder("0")
+ .name(String::from("Cash account"))
+ .build()
+ .unwrap(),
+ make_fake_actualbudget_account_builder("1")
+ .name(String::from("Current account"))
+ .build()
+ .unwrap(),
+ ];
+
+ let actualbudget_transactions = vec![
+ make_fake_actualbudget_transaction_builder("0")
+ .account_id(String::from("0"))
+ .amount(100)
+ .starting_balance_flag(1)
+ .build()
+ .unwrap(),
+ make_fake_actualbudget_transaction_builder("1")
+ .account_id(String::from("1"))
+ .amount(200)
+ .starting_balance_flag(1)
+ .build()
+ .unwrap(),
+ make_fake_actualbudget_transaction_builder("2")
+ .account_id(String::from("1"))
+ .amount(50)
+ .starting_balance_flag(1)
+ .build()
+ .unwrap(),
+ make_fake_actualbudget_transaction_builder("3")
+ .account_id(String::from("0"))
+ .amount(300)
+ .starting_balance_flag(0)
+ .build()
+ .unwrap(),
+ make_fake_actualbudget_transaction_builder("4")
+ .account_id(String::from("1"))
+ .amount(600)
+ .starting_balance_flag(0)
+ .build()
+ .unwrap(),
+ ];
+ let first_activity_date: DateUtc = "2024-08-01".parse().unwrap();
+
+ let result = transform_accounts(
+ &actualbudget_accounts,
+ &actualbudget_transactions,
+ &first_activity_date,
+ );
+
+ assert_eq!(2, result.len());
+ assert_eq!(
+ Some(Account {
+ id: transform_id("0"),
+ name: String::from("Cash account"),
+ opening_balance: 100,
+ opening_date: "2024-08-01".parse().unwrap(),
+ }),
+ result.iter().find(|a| a.name.eq("Cash account")).cloned()
+ );
+ assert_eq!(
+ Some(Account {
+ id: transform_id("1"),
+ name: String::from("Current account"),
+ opening_balance: 250,
+ opening_date: "2024-08-01".parse().unwrap(),
+ }),
+ result
+ .iter()
+ .find(|a| a.name.eq("Current account"))
+ .cloned()
+ );
+ }
+
+ #[test]
+ fn filters_out_empty_account() {
+ let actualbudget_accounts = vec![
+ make_fake_actualbudget_account_builder("0")
+ .name(String::from("Cash account"))
+ .build()
+ .unwrap(),
+ make_fake_actualbudget_account_builder("1")
+ .name(String::from("Current account"))
+ .build()
+ .unwrap(),
+ ];
+
+ let actualbudget_transactions = vec![
+ make_fake_actualbudget_transaction_builder("0")
+ .account_id(String::from("0"))
+ .amount(0)
+ .starting_balance_flag(1)
+ .build()
+ .unwrap(),
+ make_fake_actualbudget_transaction_builder("1")
+ .account_id(String::from("1"))
+ .amount(200)
+ .starting_balance_flag(1)
+ .build()
+ .unwrap(),
+ make_fake_actualbudget_transaction_builder("2")
+ .account_id(String::from("1"))
+ .amount(50)
+ .starting_balance_flag(1)
+ .build()
+ .unwrap(),
+ make_fake_actualbudget_transaction_builder("3")
+ .account_id(String::from("1"))
+ .amount(300)
+ .starting_balance_flag(0)
+ .build()
+ .unwrap(),
+ ];
+ let first_activity_date: DateUtc = "2024-08-01".parse().unwrap();
+
+ let result = transform_accounts(
+ &actualbudget_accounts,
+ &actualbudget_transactions,
+ &first_activity_date,
+ );
+
+ assert_eq!(1, result.len());
+ assert_eq!(
+ Some(Account {
+ id: transform_id("1"),
+ name: String::from("Current account"),
+ opening_balance: 250,
+ opening_date: "2024-08-01".parse().unwrap(),
+ }),
+ result.first().cloned(),
+ );
+ }
+}
diff --git a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_categories.rs b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_categories.rs
deleted file mode 100644
index 5bea17c..0000000
--- a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_categories.rs
+++ /dev/null
@@ -1,143 +0,0 @@
-use actualbudget_models::{ActualbudgetCategory, ActualbudgetZeroBudget};
-use schist_models::Bucket;
-
-use super::transform_id;
-
-pub fn transform_categories(actualbudget_categories: &[ActualbudgetCategory]) -> Vec<Bucket> {
- actualbudget_categories
- .iter()
- .map(|actualbudget_category| Bucket {
- id: transform_id(&actualbudget_category.id.clone()),
- balance: None,
- balance_cache_key: None,
- group: String::from("un-named group"),
- name: actualbudget_category.name.clone(),
- })
- .collect()
-}
-
-fn get_budget_quantity(
- actualbudget_category: &ActualbudgetCategory,
- actualbudget_zero_budgets: &[ActualbudgetZeroBudget],
-) -> i32 {
- let latest_zero_budget_for_category = actualbudget_zero_budgets
- .iter()
- .filter(|zb| zb.category_id == actualbudget_category.id)
- .max_by_key(|zb| zb.month.clone());
-
- match latest_zero_budget_for_category {
- Some(latest_zero_budget_for_category) => latest_zero_budget_for_category.amount,
- None => 0,
- }
-}
-
-#[cfg(test)]
-mod test {
- use actualbudget_fakes::actualbudget_category::make_fake_actualbudget_category_builder;
-
- use crate::transform_state::transform_id::transform_id;
-
- use super::transform_categories;
-
- #[test]
- fn when_one_category_one_zero_budget_then_transforms_category() {
- let actualbudget_categories = [make_fake_actualbudget_category_builder("0")
- .name(String::from("Food"))
- .build()
- .unwrap()];
-
- let buckets = transform_categories(&actualbudget_categories);
-
- assert_eq!(1, buckets.len());
- let bucket = buckets.get(0).unwrap();
- assert_eq!(None, bucket.balance);
- assert_eq!(None, bucket.balance_cache_key);
- assert_eq!("Food", bucket.name);
- }
-
- #[test]
- fn when_one_category_no_zero_budget_then_transforms_category() {
- let actualbudget_categories = [make_fake_actualbudget_category_builder("0")
- .name(String::from("Food"))
- .build()
- .unwrap()];
-
- let buckets = transform_categories(&actualbudget_categories);
-
- assert_eq!(1, buckets.len());
- let bucket = buckets.get(0).unwrap();
- assert_eq!("Food", bucket.name);
- assert_eq!(None, bucket.balance);
- assert_eq!(None, bucket.balance_cache_key);
- assert_eq!("un-named group", bucket.group);
- }
-
- #[test]
- fn when_one_category_multiple_zero_budgets_then_transforms_category() {
- let actualbudget_categories = [make_fake_actualbudget_category_builder("0")
- .name(String::from("Food"))
- .build()
- .unwrap()];
-
- let buckets = transform_categories(&actualbudget_categories);
-
- assert_eq!(1, buckets.len());
- let bucket = buckets.get(0).unwrap();
- assert_eq!("Food", bucket.name);
- assert_eq!(None, bucket.balance);
- assert_eq!(None, bucket.balance_cache_key);
- assert_eq!("un-named group", bucket.group);
- }
-
- #[test]
- fn when_multiple_categories_multiple_zero_budgets_then_transforms_category() {
- let actualbudget_categories = [
- make_fake_actualbudget_category_builder("0")
- .name(String::from("Food"))
- .build()
- .unwrap(),
- make_fake_actualbudget_category_builder("1")
- .name(String::from("Clothes"))
- .build()
- .unwrap(),
- make_fake_actualbudget_category_builder("2")
- .name(String::from("Rent"))
- .build()
- .unwrap(),
- ];
-
- let buckets = transform_categories(&actualbudget_categories);
-
- assert_eq!(3, buckets.len());
-
- let bucket_0 = buckets
- .iter()
- .filter(|c| c.id == transform_id("0"))
- .last()
- .unwrap();
- assert_eq!("Food", bucket_0.name);
- assert_eq!(None, bucket_0.balance);
- assert_eq!(None, bucket_0.balance_cache_key);
- assert_eq!("un-named group", bucket_0.group);
-
- let bucket_1 = buckets
- .iter()
- .filter(|c| c.id == transform_id("1"))
- .last()
- .unwrap();
- assert_eq!("Clothes", bucket_1.name);
- assert_eq!(None, bucket_1.balance);
- assert_eq!(None, bucket_1.balance_cache_key);
- assert_eq!("un-named group", bucket_1.group);
-
- let bucket_2 = buckets
- .iter()
- .filter(|c| c.id == transform_id("2"))
- .last()
- .unwrap();
- assert_eq!("Rent", bucket_2.name);
- assert_eq!(None, bucket_2.balance);
- assert_eq!(None, bucket_2.balance_cache_key);
- assert_eq!("un-named group", bucket_2.group);
- }
-}
diff --git a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_categories_to_buckets.rs b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_categories_to_buckets.rs
new file mode 100644
index 0000000..5653c26
--- /dev/null
+++ b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_categories_to_buckets.rs
@@ -0,0 +1,263 @@
+use actualbudget_models::{ActualbudgetCategory, ActualbudgetCategoryGroup};
+use schist_models::{Bucket, Drip, Transaction};
+
+use super::transform_id;
+
+pub fn transform_categories_to_buckets(
+ actualbudget_categories: &[ActualbudgetCategory],
+ actualbudget_category_groups: &[ActualbudgetCategoryGroup],
+ drips: &[Drip],
+ transactions: &[Transaction],
+) -> Vec<Bucket> {
+ actualbudget_categories
+ .iter()
+ .map(|actualbudget_category| Bucket {
+ id: transform_id(&actualbudget_category.id.clone()),
+ balance: None,
+ balance_cache_key: None,
+ group: actualbudget_category.group_id.clone()
+ .map_or_else(
+ default_group,
+ |group_id| actualbudget_category_groups
+ .iter()
+ .find(|&group| group.id == group_id)
+ .map_or_else(default_group, |group| group.name.clone().unwrap_or_else(default_group)),
+ ),
+ name: actualbudget_category.name.clone(),
+ })
+ .filter(|bucket| !bucket.is_empty(drips, transactions))
+ .collect()
+}
+
+fn default_group() -> String {
+ String::from("un-named group")
+}
+
+#[cfg(test)]
+mod test {
+ use actualbudget_fakes::actualbudget_category::make_fake_actualbudget_category_builder;
+ use actualbudget_models::ActualbudgetCategoryGroup;
+ use schist_fakes::{drip::make_fake_drip_builder, transaction::make_fake_transaction_builder};
+ use schist_models::Bucket;
+
+ use crate::transform_state::transform_id::transform_id;
+
+ use super::transform_categories_to_buckets;
+
+ #[test]
+ fn when_one_category_one_drip_then_transforms_category() {
+ let actualbudget_categories = [make_fake_actualbudget_category_builder("0")
+ .name(String::from("Food"))
+ .build()
+ .unwrap()];
+ let actualbudget_category_groups = vec![];
+ let drips = [make_fake_drip_builder(0)
+ .bucket_id(transform_id("0"))
+ .build()
+ .unwrap()];
+ let transactions = [make_fake_transaction_builder(0)
+ .bucket_id(Some(transform_id("1")))
+ .build()
+ .unwrap()];
+
+ let buckets = transform_categories_to_buckets(
+ &actualbudget_categories,
+ &actualbudget_category_groups,
+ &drips,
+ &transactions);
+
+ assert_eq!(1, buckets.len());
+ let bucket = buckets.get(0).unwrap();
+ assert_eq!(None, bucket.balance);
+ assert_eq!(None, bucket.balance_cache_key);
+ assert_eq!("Food", bucket.name);
+ }
+
+ #[test]
+ fn when_one_category_one_transaction_then_transforms_category() {
+ let actualbudget_categories = [make_fake_actualbudget_category_builder("0")
+ .name(String::from("Food"))
+ .build()
+ .unwrap()];
+ let actualbudget_category_groups = vec![];
+ let drips = [make_fake_drip_builder(0)
+ .bucket_id(transform_id("2"))
+ .build()
+ .unwrap()];
+ let transactions = [make_fake_transaction_builder(0)
+ .bucket_id(Some(transform_id("0")))
+ .build()
+ .unwrap()];
+
+ let buckets = transform_categories_to_buckets(
+ &actualbudget_categories,
+ &actualbudget_category_groups,
+ &drips,
+ &transactions);
+
+ assert_eq!(1, buckets.len());
+ let bucket = buckets.get(0).unwrap();
+ assert_eq!("Food", bucket.name);
+ assert_eq!(None, bucket.balance);
+ assert_eq!(None, bucket.balance_cache_key);
+ assert_eq!("un-named group", bucket.group);
+ }
+
+ #[test]
+ fn when_one_category_no_drip_no_transaction_then_ignores_category() {
+ let actualbudget_categories = [make_fake_actualbudget_category_builder("0")
+ .name(String::from("Food"))
+ .build()
+ .unwrap()];
+ let actualbudget_category_groups = vec![];
+ let drips = [make_fake_drip_builder(0)
+ .bucket_id(transform_id("2"))
+ .build()
+ .unwrap()];
+ let transactions = [make_fake_transaction_builder(0)
+ .bucket_id(Some(transform_id("1")))
+ .build()
+ .unwrap()];
+
+ let buckets = transform_categories_to_buckets(
+ &actualbudget_categories,
+ &actualbudget_category_groups,
+ &drips,
+ &transactions);
+
+ assert_eq!(0, buckets.len());
+ }
+
+ #[test]
+ fn when_multiple_categories_then_transforms_categories() {
+ let actualbudget_categories = [
+ make_fake_actualbudget_category_builder("0")
+ .name(String::from("Food"))
+ .build()
+ .unwrap(),
+ make_fake_actualbudget_category_builder("1")
+ .name(String::from("Clothes"))
+ .build()
+ .unwrap(),
+ make_fake_actualbudget_category_builder("2")
+ .name(String::from("Rent"))
+ .build()
+ .unwrap(),
+ make_fake_actualbudget_category_builder("3")
+ .name(String::from("Fragrance"))
+ .build()
+ .unwrap(),
+ ];
+ let actualbudget_category_groups = vec![];
+ let drips = [
+ make_fake_drip_builder(0)
+ .bucket_id(transform_id("0"))
+ .build()
+ .unwrap(),
+ make_fake_drip_builder(0)
+ .bucket_id(transform_id("1"))
+ .build()
+ .unwrap(),
+ make_fake_drip_builder(0)
+ .bucket_id(transform_id("1"))
+ .build()
+ .unwrap(),
+ ];
+ let transactions = [
+ make_fake_transaction_builder(0)
+ .bucket_id(Some(transform_id("0")))
+ .build()
+ .unwrap(),
+ make_fake_transaction_builder(0)
+ .bucket_id(Some(transform_id("0")))
+ .build()
+ .unwrap(),
+ make_fake_transaction_builder(0)
+ .bucket_id(Some(transform_id("2")))
+ .build()
+ .unwrap(),
+ ];
+
+ let buckets = transform_categories_to_buckets(
+ &actualbudget_categories,
+ &actualbudget_category_groups,
+ &drips,
+ &transactions);
+
+ assert_eq!(3, buckets.len());
+
+ let bucket_0 = buckets
+ .iter()
+ .filter(|c| c.id == transform_id("0"))
+ .last()
+ .unwrap();
+ assert_eq!("Food", bucket_0.name);
+ assert_eq!(None, bucket_0.balance);
+ assert_eq!(None, bucket_0.balance_cache_key);
+ assert_eq!("un-named group", bucket_0.group);
+
+ let bucket_1 = buckets
+ .iter()
+ .filter(|c| c.id == transform_id("1"))
+ .last()
+ .unwrap();
+ assert_eq!("Clothes", bucket_1.name);
+ assert_eq!(None, bucket_1.balance);
+ assert_eq!(None, bucket_1.balance_cache_key);
+ assert_eq!("un-named group", bucket_1.group);
+
+ let bucket_2 = buckets
+ .iter()
+ .filter(|c| c.id == transform_id("2"))
+ .last()
+ .unwrap();
+ assert_eq!("Rent", bucket_2.name);
+ assert_eq!(None, bucket_2.balance);
+ assert_eq!(None, bucket_2.balance_cache_key);
+ assert_eq!("un-named group", bucket_2.group);
+ }
+
+ #[test]
+ fn when_groups_include_match_then_includes_group_name() {
+ let actualbudget_categories = [make_fake_actualbudget_category_builder("0")
+ .name(String::from("Food"))
+ .group_id(Some(String::from("group-1")))
+ .build()
+ .unwrap()];
+ let actualbudget_category_groups = vec![
+ ActualbudgetCategoryGroup {
+ id: String::from("group-0"),
+ name: Some(String::from("Income")),
+ },
+ ActualbudgetCategoryGroup {
+ id: String::from("group-1"),
+ name: Some(String::from("Essentials")),
+ }
+ ];
+ let drips = [make_fake_drip_builder(0)
+ .bucket_id(transform_id("0"))
+ .build()
+ .unwrap()];
+ let transactions = [make_fake_transaction_builder(0)
+ .bucket_id(Some(transform_id("1")))
+ .build()
+ .unwrap()];
+
+ let buckets = transform_categories_to_buckets(
+ &actualbudget_categories,
+ &actualbudget_category_groups,
+ &drips,
+ &transactions);
+
+ assert_eq!(1, buckets.len());
+ let bucket = buckets.get(0).unwrap();
+ assert_eq!(Bucket {
+ id: transform_id("0"),
+ balance: None,
+ balance_cache_key: None,
+ group: String::from("Essentials"),
+ name: String::from("Food"),
+ }, *bucket);
+
+ }
+}
diff --git a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_state.rs b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_state.rs
index 7816df6..835ceff 100644
--- a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_state.rs
+++ b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_state.rs
@@ -1,12 +1,12 @@
-use anyhow::{bail, Context, Result};
-use schist_models::{Account, Bucket, BucketTransfer, DateUtc, Pipe, Transaction};
+use anyhow::{Context, Result};
+use schist_models::{Account, Bucket, BucketTransfer, Pipe, Transaction};
use crate::{actualbudget_state::ActualbudgetState, schist_state::SchistState};
use super::{
- transform_account_transfers, transform_accounts,
- transform_budget_drips::transform_budget_drips, transform_categories,
- transform_category_transfers, transform_date, transform_transactions, transform_zero_budgets,
+ transform_account_transfers, transform_accounts, transform_categories_to_buckets,
+ transform_date, transform_transactions, transform_transactions_to_bucket_transfers,
+ transform_zero_budgets_to_drips::transform_budget_drips, transform_zero_budgets_to_pipes,
};
pub fn transform_state(actualbudget_state: ActualbudgetState) -> Result<SchistState> {
@@ -17,25 +17,27 @@ pub fn transform_state(actualbudget_state: ActualbudgetState) -> Result<SchistSt
fn transform_state_without_context(actualbudget_state: ActualbudgetState) -> Result<SchistState> {
let (remaining_transactions, account_transfers) =
transform_account_transfers(&actualbudget_state.actualbudget_transactions)?;
+ let transactions: Vec<Transaction> = transform_transactions(
+ &remaining_transactions,
+ &actualbudget_state.actualbudget_payees,
+ )?;
let first_activity_date = transform_date(&actualbudget_state.actualbudget_first_activity_date)?;
- let before_first_activity_date = find_date_before(&first_activity_date)?;
let accounts: Vec<Account> = transform_accounts(
&actualbudget_state.actualbudget_accounts,
- &actualbudget_state.actualbudget_category_transfers_account,
+ &actualbudget_state.actualbudget_transactions,
&first_activity_date,
);
let budget_drips = transform_budget_drips(&actualbudget_state.actualbudget_zero_budgets)?;
- let buckets: Vec<Bucket> = transform_categories(&actualbudget_state.actualbudget_categories);
- let pipes: Vec<Pipe> = transform_zero_budgets(&actualbudget_state.actualbudget_zero_budgets);
- let transactions: Vec<Transaction> = transform_transactions(
- &remaining_transactions,
- &actualbudget_state.actualbudget_category_transfers_account,
- &actualbudget_state.actualbudget_payees,
- )?;
- let bucket_transfers: Vec<BucketTransfer> = transform_category_transfers(
- &remaining_transactions,
- &actualbudget_state.actualbudget_category_transfers_account,
- )?;
+ let buckets: Vec<Bucket> = transform_categories_to_buckets(
+ &actualbudget_state.actualbudget_categories,
+ &actualbudget_state.actualbudget_category_groups,
+ &budget_drips,
+ &transactions,
+ );
+ let pipes: Vec<Pipe> =
+ transform_zero_budgets_to_pipes(&actualbudget_state.actualbudget_zero_budgets)?;
+ let bucket_transfers: Vec<BucketTransfer> =
+ transform_transactions_to_bucket_transfers(&remaining_transactions)?;
Ok(SchistState {
first_transaction_date: first_activity_date,
@@ -48,14 +50,3 @@ fn transform_state_without_context(actualbudget_state: ActualbudgetState) -> Res
bucket_transfers,
})
}
-
-fn find_date_before(date: &DateUtc) -> Result<DateUtc> {
- for days_before in 1..31 {
- let result = date.sub_days(days_before);
- if result.is_ok() {
- return result;
- }
- }
-
- bail!("failed to find a date before {}", date)
-}
diff --git a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_transactions.rs b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_transactions.rs
index 7c4ece5..11765be 100644
--- a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_transactions.rs
+++ b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_transactions.rs
@@ -1,32 +1,27 @@
-use actualbudget_models::{ActualbudgetAccount, ActualbudgetPayee, ActualbudgetTransaction};
+use actualbudget_models::{ActualbudgetPayee, ActualbudgetTransaction};
use anyhow::{Context, Result};
use schist_models::Transaction;
-use super::{is_not_category_transfer, transform_date, transform_id};
+use super::{is_not_bucket_transfer, transform_date, transform_id};
pub fn transform_transactions(
actualbudget_transactions: &[ActualbudgetTransaction],
- actualbudget_category_transfers_account: &Option<ActualbudgetAccount>,
actualbudget_payees: &[ActualbudgetPayee],
) -> Result<Vec<Transaction>> {
- transform_transactions_without_context(
- actualbudget_transactions,
- actualbudget_category_transfers_account,
- actualbudget_payees,
- )
- .with_context(|| "failed to transform actualbudget transactions to schist transactions")
+ transform_transactions_without_context(actualbudget_transactions, actualbudget_payees)
+ .with_context(|| "failed to transform actualbudget transactions to schist transactions")
}
fn transform_transactions_without_context(
actualbudget_transactions: &[ActualbudgetTransaction],
- actualbudget_category_transfers_account: &Option<ActualbudgetAccount>,
actualbudget_payees: &[ActualbudgetPayee],
) -> Result<Vec<Transaction>> {
let mut transactions = Vec::<Transaction>::new();
let actualbudget_transactions = actualbudget_transactions
.iter()
- .filter(|t| is_not_category_transfer(t, actualbudget_category_transfers_account));
+ .filter(|t| !t.is_starting_balance())
+ .filter(|t| is_not_bucket_transfer(t, actualbudget_transactions));
let parent_actualbudget_transactions: Vec<&ActualbudgetTransaction> = actualbudget_transactions
.clone()
.filter(|t| t.is_parent)
@@ -60,7 +55,10 @@ fn transform_transactions_without_context(
amount: atomic_actualbudget_transaction.amount.try_into().unwrap(),
date: transform_date(&atomic_actualbudget_transaction.date)?,
account_id: transform_id(&atomic_actualbudget_transaction.account_id),
- bucket_id: None,
+ bucket_id: atomic_actualbudget_transaction
+ .category_id
+ .clone()
+ .map(|id| transform_id(id.as_str())),
});
}
@@ -90,37 +88,17 @@ fn transform_transactions_without_context(
#[cfg(test)]
mod test {
use actualbudget_fakes::{
- actualbudget_account::make_fake_actualbudget_account,
- actualbudget_payee::{make_fake_actualbudget_payee, make_fake_actualbudget_payee_builder},
+ actualbudget_payee::make_fake_actualbudget_payee_builder,
actualbudget_transaction::make_fake_actualbudget_transaction_builder,
};
use actualbudget_models::ActualbudgetDate;
- use schist_models::DateUtc;
+ use schist_models::{DateUtc, Transaction};
use crate::transform_state::transform_id::transform_id;
use super::transform_transactions;
#[test]
- 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 actualbudget_payees = [make_fake_actualbudget_payee("0")];
-
- let transactions = transform_transactions(
- &actualbudget_transactions,
- &Some(actualbudget_category_transfers_account),
- &actualbudget_payees,
- )
- .unwrap();
-
- assert_eq!(0, transactions.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"))
@@ -137,8 +115,7 @@ mod test {
.unwrap()];
let transactions =
- transform_transactions(&actualbudget_transactions, &None, &actualbudget_payees)
- .unwrap();
+ transform_transactions(&actualbudget_transactions, &actualbudget_payees).unwrap();
assert_eq!(1, transactions.len());
let transaction = transactions.get(0).unwrap();
@@ -153,11 +130,14 @@ mod test {
fn when_one_split_transaction_then_returns_transformed_transaction() {
let actualbudget_transactions = [
make_fake_actualbudget_transaction_builder("0")
+ .account_id(String::from("0"))
+ .date(ActualbudgetDate::from_i32(20240801))
.is_parent(true)
- .amount(100)
+ .amount(300)
.build()
.unwrap(),
make_fake_actualbudget_transaction_builder("1")
+ .account_id(String::from("0"))
.amount(200)
.category_id(Some(String::from("0")))
.is_child(true)
@@ -166,8 +146,9 @@ mod test {
.build()
.unwrap(),
make_fake_actualbudget_transaction_builder("2")
- .amount(300)
- .category_id(Some(String::from("0")))
+ .account_id(String::from("0"))
+ .amount(100)
+ .category_id(Some(String::from("1")))
.is_child(true)
.parent_id(Some(String::from("0")))
.notes(Some(String::from("notes B")))
@@ -175,11 +156,20 @@ mod test {
.unwrap(),
];
- let transactions = transform_transactions(&actualbudget_transactions, &None, &[]).unwrap();
+ let transactions = transform_transactions(&actualbudget_transactions, &[]).unwrap();
assert_eq!(1, transactions.len());
-
- let transaction = transactions.get(0).unwrap();
- assert_eq!(100, transaction.amount);
+ assert_eq!(
+ Some(Transaction {
+ id: transform_id("0"),
+ account_id: transform_id("0"),
+ amount: 300,
+ bucket_id: None,
+ counterparty: String::from(""),
+ date: "2024-08-01".parse().unwrap(),
+ description: String::from(""),
+ }),
+ transactions.get(0).cloned(),
+ );
}
}
diff --git a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_category_transfers.rs b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_transactions_to_bucket_transfers.rs
index 78b466d..51545c3 100644
--- a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_category_transfers.rs
+++ b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_transactions_to_bucket_transfers.rs
@@ -1,32 +1,52 @@
-use actualbudget_models::{ActualbudgetAccount, ActualbudgetTransaction};
+use actualbudget_models::ActualbudgetTransaction;
use anyhow::{bail, Context, Result};
use itertools::Itertools;
use schist_models::BucketTransfer;
-use super::{is_category_transfer, transform_id};
+use super::transform_id;
-pub fn transform_category_transfers(
+pub fn transform_transactions_to_bucket_transfers(
actualbudget_transactions: &[ActualbudgetTransaction],
- actualbudget_category_transfers_account: &Option<ActualbudgetAccount>,
) -> Result<Vec<BucketTransfer>> {
- let actualbudget_category_transfers = actualbudget_transactions.iter().filter(|transaction| {
- is_category_transfer(transaction, actualbudget_category_transfers_account)
- });
- let actualbudget_parent_category_transfers: Vec<ActualbudgetTransaction> =
- actualbudget_category_transfers
- .clone()
+ let children: Vec<&ActualbudgetTransaction> = actualbudget_transactions
+ .into_iter()
+ .filter(|&t| t.is_child)
+ .collect();
+ let parent_children_pairs: Vec<(&ActualbudgetTransaction, Vec<&ActualbudgetTransaction>)> =
+ actualbudget_transactions
+ .iter()
.filter(|t| t.is_parent)
- .map(ActualbudgetTransaction::clone)
- .collect();
- let actualbudget_child_category_transfers: Vec<ActualbudgetTransaction> =
- actualbudget_category_transfers
- .filter(|t| t.is_child)
- .map(ActualbudgetTransaction::clone)
- .collect();
-
- Ok(actualbudget_parent_category_transfers
+ .map(|parent| {
+ (
+ parent,
+ children
+ .clone()
+ .into_iter()
+ .filter(|&c| c.parent_id.clone().is_some_and(|id| id == parent.id))
+ .collect::<Vec<&ActualbudgetTransaction>>(),
+ )
+ })
+ .map(|(parent, children)| {
+ let children_total_amount: i32 = children.iter().map(|child| child.amount).sum();
+ if parent.amount != children_total_amount {
+ bail!("parent transaction {} has amount: {}, but its {} children add up to a total amount of: {}",
+ parent.id,
+ parent.amount,
+ children.len(),
+ children_total_amount);
+ }
+ Ok((parent, children))
+ })
+ .filter(|result|
+ match result {
+ Ok((parent, _children)) => parent.amount == 0,
+ Err(_) => true,
+ })
+ .try_collect()?;
+
+ Ok(parent_children_pairs
.iter()
- .map(|pct| transform_category_transfer(pct, &actualbudget_child_category_transfers))
+ .map(|(parent, children)| transform_category_transfer(parent, children))
.filter_ok(Option::is_some)
.map_ok(Option::unwrap)
.collect::<Result<Vec<BucketTransfer>>>()
@@ -35,7 +55,7 @@ pub fn transform_category_transfers(
fn transform_category_transfer(
actualbudget_parent_category_transfer: &ActualbudgetTransaction,
- actualbudget_child_category_transfers: &[ActualbudgetTransaction],
+ actualbudget_child_category_transfers: &[&ActualbudgetTransaction],
) -> Result<Option<BucketTransfer>> {
transform_category_transfer_without_context(
actualbudget_parent_category_transfer,
@@ -46,20 +66,12 @@ fn transform_category_transfer(
fn transform_category_transfer_without_context(
actualbudget_parent_category_transfer: &ActualbudgetTransaction,
- actualbudget_child_category_transfers: &[ActualbudgetTransaction],
+ actualbudget_child_category_transfers: &[&ActualbudgetTransaction],
) -> Result<Option<BucketTransfer>> {
- let actualbudget_child_category_transfers = actualbudget_child_category_transfers
- .iter()
- .filter(|t| {
- t.parent_id
- .clone()
- .is_some_and(|parent_id| parent_id == actualbudget_parent_category_transfer.id)
- })
- .map(ActualbudgetTransaction::clone);
-
- let from_transfers: Vec<ActualbudgetTransaction> = actualbudget_child_category_transfers
- .clone()
- .filter(|t| t.amount < 0)
+ let from_transfers: Vec<&ActualbudgetTransaction> = actualbudget_child_category_transfers
+ .into_iter()
+ .cloned()
+ .filter(|&t| t.amount < 0)
.collect();
if from_transfers.len() != 1 {
bail!(
@@ -78,9 +90,10 @@ fn transform_category_transfer_without_context(
}
let from_category_id: i32 = transform_id(&from_transfer.category_id.clone().unwrap());
- let to_transfers: Vec<ActualbudgetTransaction> = actualbudget_child_category_transfers
- .clone()
- .filter(|t| t.amount > 0)
+ let to_transfers: Vec<&ActualbudgetTransaction> = actualbudget_child_category_transfers
+ .iter()
+ .filter(|&t| t.amount > 0)
+ .cloned()
.collect();
if to_transfers.len() != 1 {
bail!(
@@ -117,30 +130,23 @@ fn transform_category_transfer_without_context(
#[cfg(test)]
mod test {
- use actualbudget_fakes::{
- actualbudget_account::make_fake_actualbudget_account,
- actualbudget_transaction::make_fake_actualbudget_transaction_builder,
- };
+ use actualbudget_fakes::actualbudget_transaction::make_fake_actualbudget_transaction_builder;
use actualbudget_models::{ActualbudgetTransaction, ActualbudgetTransactionBuilder};
use schist_models::BucketTransfer;
use crate::transform_state::transform_id::transform_id;
- use super::transform_category_transfers;
+ use super::transform_transactions_to_bucket_transfers;
#[test]
fn when_one_category_transfer_then_transforms() {
- let actualbudget_category_transfers_account = make_fake_actualbudget_account("0");
let actualbudget_transactions = [
make_fake_parent_transaction("0"),
make_fake_from_child_transaction("1", "0"),
make_fake_to_child_transaction("2", "0"),
];
- let result = transform_category_transfers(
- &actualbudget_transactions,
- &Some(actualbudget_category_transfers_account),
- );
+ let result = transform_transactions_to_bucket_transfers(&actualbudget_transactions);
assert!(result.is_ok());
let bucket_transfers = result.unwrap();
@@ -154,7 +160,6 @@ mod test {
#[test]
fn when_many_transactions_then_transforms() {
- let actualbudget_category_transfers_account = make_fake_actualbudget_account("0");
let actualbudget_transactions = [
make_fake_parent_transaction("0"),
make_fake_from_child_transaction_builder("1", 100, "0")
@@ -184,10 +189,7 @@ mod test {
.unwrap(),
];
- let result = transform_category_transfers(
- &actualbudget_transactions,
- &Some(actualbudget_category_transfers_account),
- );
+ let result = transform_transactions_to_bucket_transfers(&actualbudget_transactions);
assert!(result.is_ok());
let bucket_transfers = result.unwrap();
@@ -214,7 +216,6 @@ mod test {
#[test]
fn when_multiple_child_from_transactions_then_err() {
- let actualbudget_category_transfers_account = make_fake_actualbudget_account("0");
let actualbudget_transactions = [
make_fake_parent_transaction("0"),
make_fake_from_child_transaction_builder("1", 50, "0")
@@ -228,17 +229,13 @@ mod test {
.unwrap(),
];
- let result = transform_category_transfers(
- &actualbudget_transactions,
- &Some(actualbudget_category_transfers_account),
- );
+ let result = transform_transactions_to_bucket_transfers(&actualbudget_transactions);
assert!(result.is_err());
}
#[test]
fn when_multiple_child_to_transactions_then_err() {
- let actualbudget_category_transfers_account = make_fake_actualbudget_account("0");
let actualbudget_transactions = [
make_fake_parent_transaction("0"),
make_fake_from_child_transaction_builder("1", 100, "0")
@@ -252,49 +249,37 @@ mod test {
.unwrap(),
];
- let result = transform_category_transfers(
- &actualbudget_transactions,
- &Some(actualbudget_category_transfers_account),
- );
+ let result = transform_transactions_to_bucket_transfers(&actualbudget_transactions);
assert!(result.is_err());
}
#[test]
fn when_no_child_from_transactions_then_err() {
- let actualbudget_category_transfers_account = make_fake_actualbudget_account("0");
let actualbudget_transactions = [
make_fake_parent_transaction("0"),
make_fake_to_child_transaction("1", "0"),
];
- let result = transform_category_transfers(
- &actualbudget_transactions,
- &Some(actualbudget_category_transfers_account),
- );
+ let result = transform_transactions_to_bucket_transfers(&actualbudget_transactions);
assert!(result.is_err());
}
#[test]
fn when_no_child_to_transactions_then_err() {
- let actualbudget_category_transfers_account = make_fake_actualbudget_account("0");
let actualbudget_transactions = [
make_fake_parent_transaction("0"),
make_fake_from_child_transaction("1", "0"),
];
- let result = transform_category_transfers(
- &actualbudget_transactions,
- &Some(actualbudget_category_transfers_account),
- );
+ let result = transform_transactions_to_bucket_transfers(&actualbudget_transactions);
assert!(result.is_err());
}
#[test]
fn when_child_amounts_dont_add_up_to_zero_then_err() {
- let actualbudget_category_transfers_account = make_fake_actualbudget_account("0");
let actualbudget_transactions = [
make_fake_parent_transaction("0"),
make_fake_from_child_transaction_builder("1", 50, "0")
@@ -305,17 +290,13 @@ mod test {
.unwrap(),
];
- let result = transform_category_transfers(
- &actualbudget_transactions,
- &Some(actualbudget_category_transfers_account),
- );
+ let result = transform_transactions_to_bucket_transfers(&actualbudget_transactions);
assert!(result.is_err());
}
#[test]
fn when_child_category_transfer_has_no_category_then_err() {
- let actualbudget_category_transfers_account = make_fake_actualbudget_account("0");
let actualbudget_transactions_0 = [
make_fake_parent_transaction("0"),
make_fake_from_child_transaction_builder("1", 100, "0")
@@ -337,14 +318,8 @@ mod test {
.unwrap(),
];
- let result_0 = transform_category_transfers(
- &actualbudget_transactions_0,
- &Some(actualbudget_category_transfers_account.clone()),
- );
- let result_1 = transform_category_transfers(
- &actualbudget_transactions_1,
- &Some(actualbudget_category_transfers_account),
- );
+ let result_0 = transform_transactions_to_bucket_transfers(&actualbudget_transactions_0);
+ let result_1 = transform_transactions_to_bucket_transfers(&actualbudget_transactions_1);
assert!(result_0.is_err());
assert!(result_1.is_err());
diff --git a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_zero_budgets.rs b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_zero_budgets.rs
deleted file mode 100644
index 999c3e8..0000000
--- a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_zero_budgets.rs
+++ /dev/null
@@ -1,6 +0,0 @@
-use actualbudget_models::ActualbudgetZeroBudget;
-use schist_models::Pipe;
-
-pub fn transform_zero_budgets(_actualbudget_zero_budgets: &[ActualbudgetZeroBudget]) -> Vec<Pipe> {
- todo!()
-}
diff --git a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_budget_drips.rs b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_zero_budgets_to_drips.rs
index 76509cd..bfaef31 100644
--- a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_budget_drips.rs
+++ b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_zero_budgets_to_drips.rs
@@ -141,7 +141,7 @@ mod test {
use schist_models::Drip;
use schist_traits::Dateable;
- use crate::transform_state::transform_budget_drips::map_actualbudget_zero_budget_to_budget_drips;
+ use crate::transform_state::transform_zero_budgets_to_drips::map_actualbudget_zero_budget_to_budget_drips;
#[test]
fn when_zero_budget_with_integer_budget_per_day_then_evenly_divided_budget_drips() {
diff --git a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_zero_budgets_to_pipes.rs b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_zero_budgets_to_pipes.rs
new file mode 100644
index 0000000..2469ba9
--- /dev/null
+++ b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/src/transform_state/transform_zero_budgets_to_pipes.rs
@@ -0,0 +1,211 @@
+use actualbudget_models::ActualbudgetZeroBudget;
+use anyhow::Context;
+use itertools::Itertools;
+use schist_models::{BudgetPeriodUnit, Pipe};
+
+use super::{transform_date, transform_id};
+
+pub fn transform_zero_budgets_to_pipes(
+ actualbudget_zero_budgets: &[ActualbudgetZeroBudget],
+) -> anyhow::Result<Vec<Pipe>> {
+ let pipes_with_consecutive_duplicates = actualbudget_zero_budgets
+ .iter()
+ .filter(|zb| zb.amount != 0)
+ .map(transform_zero_budget_to_pipe)
+ .collect::<anyhow::Result<Vec<Pipe>>>()
+ .context("Failed to transform Actualbudget zero budgets to Schist pipes")?;
+
+ Ok(pipes_with_consecutive_duplicates
+ .into_iter()
+ .sorted_by_key(|pipe| pipe.start_date)
+ .sorted_by_key(|pipe| pipe.bucket_id)
+ .fold(Vec::new(), fold_pipes))
+}
+
+fn transform_zero_budget_to_pipe(
+ actualbudget_zero_budget: &ActualbudgetZeroBudget,
+) -> anyhow::Result<Pipe> {
+ Ok(Pipe {
+ id: transform_id(&actualbudget_zero_budget.id),
+ amount: actualbudget_zero_budget.amount,
+ bucket_id: transform_id(&actualbudget_zero_budget.category_id),
+ period_length: 1,
+ period_unit: BudgetPeriodUnit::Month,
+ start_date: transform_date(&actualbudget_zero_budget.month)?,
+ })
+}
+
+fn fold_pipes(mut pipes: Vec<Pipe>, pipe: Pipe) -> Vec<Pipe> {
+ if let Some(index) = pipes.iter().position(|other_pipe| {
+ other_pipe.bucket_id == pipe.bucket_id && other_pipe.amount == pipe.amount
+ }) {
+ if pipe.start_date < pipes[index].start_date {
+ pipes.remove(index);
+ pipes.push(pipe);
+ }
+ } else {
+ pipes.push(pipe);
+ }
+ pipes
+}
+
+#[cfg(test)]
+mod test {
+ use actualbudget_models::{ActualbudgetDate, ActualbudgetZeroBudget};
+ use schist_models::{BudgetPeriodUnit, Pipe};
+
+ use crate::transform_state::transform_id::transform_id;
+
+ #[test]
+ fn when_zero_budget_is_zero_then_does_not_transform() {
+ let actualbudget_zero_budgets = vec![ActualbudgetZeroBudget {
+ id: String::from("zb-0"),
+ month: ActualbudgetDate::from_i32(202408),
+ category_id: String::from("category-0"),
+ amount: 0,
+ do_carry_over: true,
+ }];
+
+ let pipes = super::transform_zero_budgets_to_pipes(&actualbudget_zero_budgets);
+
+ assert!(pipes.is_ok());
+ assert_eq!(0, pipes.unwrap().len());
+ }
+
+ #[test]
+ fn when_zero_budget_is_not_zero_then_does_transform() {
+ let actualbudget_zero_budgets = vec![
+ ActualbudgetZeroBudget {
+ id: String::from("zb-0"),
+ month: ActualbudgetDate::from_i32(202408),
+ category_id: String::from("category-0"),
+ amount: 100,
+ do_carry_over: true,
+ },
+ ActualbudgetZeroBudget {
+ id: String::from("zb-1"),
+ month: ActualbudgetDate::from_i32(202408),
+ category_id: String::from("category-1"),
+ amount: -200,
+ do_carry_over: false,
+ },
+ ];
+
+ let pipes = super::transform_zero_budgets_to_pipes(&actualbudget_zero_budgets);
+
+ assert!(pipes.is_ok());
+ let mut pipes = pipes.unwrap();
+ pipes.sort_by_key(|pipe| -pipe.amount);
+ assert_eq!(
+ vec![
+ Pipe {
+ id: transform_id("zb-0"),
+ amount: 100,
+ bucket_id: transform_id("category-0"),
+ period_length: 1,
+ period_unit: BudgetPeriodUnit::Month,
+ start_date: "2024-08-01".parse().unwrap(),
+ },
+ Pipe {
+ id: transform_id("zb-1"),
+ amount: -200,
+ bucket_id: transform_id("category-1"),
+ period_length: 1,
+ period_unit: BudgetPeriodUnit::Month,
+ start_date: "2024-08-01".parse().unwrap()
+ },
+ ],
+ pipes,
+ );
+ }
+
+ #[test]
+ fn when_zero_budgets_contain_adjacent_duplicates_then_merge() {
+ let actualbudget_zero_budgets = vec![
+ ActualbudgetZeroBudget {
+ id: String::from("zb-0"),
+ month: ActualbudgetDate::from_i32(202408),
+ category_id: String::from("category-0"),
+ amount: 100,
+ do_carry_over: true,
+ },
+ ActualbudgetZeroBudget {
+ id: String::from("zb-1"),
+ month: ActualbudgetDate::from_i32(202409),
+ category_id: String::from("category-1"),
+ amount: -200,
+ do_carry_over: false,
+ },
+ ActualbudgetZeroBudget {
+ id: String::from("zb-0"),
+ month: ActualbudgetDate::from_i32(202410),
+ category_id: String::from("category-0"),
+ amount: 100,
+ do_carry_over: true,
+ },
+ ActualbudgetZeroBudget {
+ id: String::from("zb-0"),
+ month: ActualbudgetDate::from_i32(202411),
+ category_id: String::from("category-0"),
+ amount: 200,
+ do_carry_over: false,
+ },
+ ActualbudgetZeroBudget {
+ id: String::from("zb-1"),
+ month: ActualbudgetDate::from_i32(202410),
+ category_id: String::from("category-1"),
+ amount: -200,
+ do_carry_over: true,
+ },
+ ActualbudgetZeroBudget {
+ id: String::from("zb-0"),
+ month: ActualbudgetDate::from_i32(202409),
+ category_id: String::from("category-0"),
+ amount: 100,
+ do_carry_over: false,
+ },
+ ActualbudgetZeroBudget {
+ id: String::from("zb-1"),
+ month: ActualbudgetDate::from_i32(202408),
+ category_id: String::from("category-1"),
+ amount: -200,
+ do_carry_over: true,
+ },
+ ];
+
+ let pipes = super::transform_zero_budgets_to_pipes(&actualbudget_zero_budgets);
+
+ assert!(pipes.is_ok());
+ let mut pipes = pipes.unwrap();
+ pipes.sort_by_key(|pipe| -pipe.amount);
+ assert_eq!(
+ vec![
+ Pipe {
+ id: transform_id("zb-0"),
+ amount: 200,
+ bucket_id: transform_id("category-0"),
+ period_length: 1,
+ period_unit: BudgetPeriodUnit::Month,
+ start_date: "2024-11-01".parse().unwrap(),
+ },
+ Pipe {
+ id: transform_id("zb-0"),
+ amount: 100,
+ bucket_id: transform_id("category-0"),
+ period_length: 1,
+ period_unit: BudgetPeriodUnit::Month,
+ start_date: "2024-08-01".parse().unwrap(),
+ },
+ Pipe {
+ id: transform_id("zb-1"),
+ amount: -200,
+ bucket_id: transform_id("category-1"),
+ period_length: 1,
+ period_unit: BudgetPeriodUnit::Month,
+ start_date: "2024-08-01".parse().unwrap()
+ },
+ ],
+ pipes,
+ );
+ }
+}
diff --git a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/common/account_matchers.rs b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/common/account_matchers.rs
index eaa1805..358d894 100644
--- a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/common/account_matchers.rs
+++ b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/common/account_matchers.rs
@@ -10,10 +10,10 @@ pub fn get_account_matchers() -> Vec<Box<dyn Fn(&Account) -> bool>> {
fn matches_bank_account(account: &Account) -> bool {
match account {
Account {
+ id: _id,
name,
- opening_balance: 0,
+ opening_balance: 1200_00,
opening_date,
- ..
} => name == "My bank account" && *opening_date == "2024-08-01".parse().unwrap(),
_ => false,
}
@@ -22,10 +22,10 @@ fn matches_bank_account(account: &Account) -> bool {
fn matches_cash_account(account: &Account) -> bool {
match account {
Account {
+ id: _id,
name,
- opening_balance: 0,
+ opening_balance: 45_10,
opening_date,
- ..
} => name == "My cash account" && *opening_date == "2024-08-01".parse().unwrap(),
_ => false,
}
diff --git a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/common/bucket_matchers.rs b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/common/bucket_matchers.rs
index 3821806..5753847 100644
--- a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/common/bucket_matchers.rs
+++ b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/common/bucket_matchers.rs
@@ -1,16 +1,27 @@
use schist_models::Bucket;
-use super::bucket_names::BUCKET_NAMES;
+use super::bucket_names::BUCKET_NAMES_AND_GROUP_NAMES;
pub fn get_bucket_matchers() -> Vec<Box<dyn Fn(&Bucket) -> bool>> {
- BUCKET_NAMES
+ BUCKET_NAMES_AND_GROUP_NAMES
.iter()
- .map(|bucket_name| -> Box<dyn Fn(&Bucket) -> bool> {
- Box::new(|c| matches_bucket(c, bucket_name))
- })
+ .map(
+ |bucket_name_and_group_name| -> Box<dyn Fn(&Bucket) -> bool> {
+ Box::new(|c| matches_bucket(c, bucket_name_and_group_name))
+ },
+ )
.collect()
}
-fn matches_bucket(bucket: &Bucket, bucket_name: &str) -> bool {
- bucket.name == bucket_name && bucket.balance == None && bucket.balance_cache_key == None
+fn matches_bucket(bucket: &Bucket, (bucket_name, group_name): &(&str, &str)) -> bool {
+ match bucket {
+ Bucket {
+ id: _id,
+ balance: None,
+ balance_cache_key: None,
+ group,
+ name,
+ } => *name == *bucket_name && *group == *group_name,
+ _ => false,
+ }
}
diff --git a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/common/bucket_names.rs b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/common/bucket_names.rs
index 5f232a8..d8b5d3f 100644
--- a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/common/bucket_names.rs
+++ b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/common/bucket_names.rs
@@ -3,17 +3,19 @@ pub const BUCKET_NAME_GENERAL: &str = "General";
pub const BUCKET_NAME_BILLS: &str = "Bills";
pub const BUCKET_NAME_BILLS_FLEXIBLE: &str = "Bills (Flexible)";
pub const BUCKET_NAME_SAVINGS: &str = "Savings";
-pub const BUCKET_NAME_STARTING_BALANCES: &str = "Starting Balances";
pub const BUCKET_NAME_INCOME: &str = "Income";
-pub const BUCKET_NAMES: &[&'static str] = &[
- BUCKET_NAME_FOOD,
- BUCKET_NAME_GENERAL,
- BUCKET_NAME_BILLS,
- BUCKET_NAME_BILLS_FLEXIBLE,
- BUCKET_NAME_SAVINGS,
- BUCKET_NAME_STARTING_BALANCES,
- BUCKET_NAME_INCOME,
+pub const GROUP_NAME_USUAL_EXPENSES: &str = "Usual Expenses";
+pub const GROUP_NAME_SAVINGS: &str = "Investments and Savings";
+pub const GROUP_NAME_INCOME: &str = "Income";
+
+pub const BUCKET_NAMES_AND_GROUP_NAMES: &[(&'static str, &'static str)] = &[
+ (BUCKET_NAME_FOOD, GROUP_NAME_USUAL_EXPENSES),
+ (BUCKET_NAME_GENERAL, GROUP_NAME_USUAL_EXPENSES),
+ (BUCKET_NAME_BILLS, GROUP_NAME_USUAL_EXPENSES),
+ (BUCKET_NAME_BILLS_FLEXIBLE, GROUP_NAME_USUAL_EXPENSES),
+ (BUCKET_NAME_SAVINGS, GROUP_NAME_SAVINGS),
+ (BUCKET_NAME_INCOME, GROUP_NAME_INCOME),
];
pub struct BucketNamesDict {
@@ -22,7 +24,6 @@ pub struct BucketNamesDict {
pub bills: &'static str,
pub bills_flexible: &'static str,
pub savings: &'static str,
- pub starting_balances: &'static str,
pub income: &'static str,
}
@@ -32,6 +33,5 @@ pub const BUCKET_NAMES_DICT: BucketNamesDict = BucketNamesDict {
bills: BUCKET_NAME_BILLS,
bills_flexible: BUCKET_NAME_BILLS_FLEXIBLE,
savings: BUCKET_NAME_SAVINGS,
- starting_balances: BUCKET_NAME_STARTING_BALANCES,
income: BUCKET_NAME_INCOME,
};
diff --git a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/common/budget_drip_matchers.rs b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/common/drip_matchers.rs
index 85ad045..629a04b 100644
--- a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/common/budget_drip_matchers.rs
+++ b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/common/drip_matchers.rs
@@ -8,80 +8,71 @@ use crate::common::bucket_names::{
BUCKET_NAME_SAVINGS,
};
-pub fn get_num_expected_budget_drips() -> usize {
+pub fn get_num_expected_drips() -> usize {
let num_expected_dates = DATES.len();
let num_expected_buckets = 5;
num_expected_dates * num_expected_buckets
}
-pub fn assert_budget_drip_matches(budget_drip: &Drip, buckets: &[Bucket]) {
+pub fn assert_drip_matches(drip: &Drip, buckets: &[Bucket]) {
assert!(
- DATES
- .iter()
- .any(|date| *LazyLock::force(date) == budget_drip.date),
- "budget drip had unexpected date: {:?}",
- budget_drip,
+ DATES.iter().any(|date| *LazyLock::force(date) == drip.date),
+ "drip had unexpected date: {:?}",
+ drip,
);
let bucket_name = buckets
.iter()
- .find(|c| c.id == budget_drip.bucket_id)
+ .find(|c| c.id == drip.bucket_id)
.map(|c| c.name.clone())
.expect(
format!(
- "budget drip {:?} had unexpected bucket ID. buckets were: {:?}",
- budget_drip, buckets,
+ "drip {:?} had unexpected bucket ID. buckets were: {:?}",
+ drip, buckets,
)
.as_str(),
);
- let expected_quantity = match (
- bucket_name.as_str(),
- budget_drip.date.month(),
- budget_drip.date.day(),
- ) {
+ let expected_quantity = match (bucket_name.as_str(), drip.date.month(), drip.date.day()) {
(BUCKET_NAME_FOOD, 8 | 10, 1) => 6_50,
(BUCKET_NAME_FOOD, 8 | 10, _) => 6_45,
(BUCKET_NAME_FOOD, 9 | 11, 1) => 6_86,
(BUCKET_NAME_FOOD, 9 | 11, _) => 6_66,
- (BUCKET_NAME_FOOD, _, _) => panic!("budget drip {:?} had unexpected date", budget_drip),
+ (BUCKET_NAME_FOOD, _, _) => panic!("drip {:?} had unexpected date", drip),
(BUCKET_NAME_GENERAL, 8 | 10, 1) => 3_40,
(BUCKET_NAME_GENERAL, 8 | 10, _) => 3_22,
(BUCKET_NAME_GENERAL, 9 | 11, 1) => 3_43,
(BUCKET_NAME_GENERAL, 9 | 11, _) => 3_33,
- (BUCKET_NAME_GENERAL, _, _) => panic!("budget drip {:?} had unexpected date", budget_drip),
+ (BUCKET_NAME_GENERAL, _, _) => panic!("drip {:?} had unexpected date", drip),
(BUCKET_NAME_BILLS, 8 | 10, 1) => 4_10,
(BUCKET_NAME_BILLS, 8 | 10, _) => 4_03,
(BUCKET_NAME_BILLS, 9 | 11, 1) => 4_36,
(BUCKET_NAME_BILLS, 9 | 11, _) => 4_16,
- (BUCKET_NAME_BILLS, _, _) => panic!("budget drip {:?} had unexpected date", budget_drip),
+ (BUCKET_NAME_BILLS, _, _) => panic!("drip {:?} had unexpected date", drip),
(BUCKET_NAME_BILLS_FLEXIBLE, 8 | 10, 1) => 1_00,
(BUCKET_NAME_BILLS_FLEXIBLE, 8 | 10, _) => 80,
(BUCKET_NAME_BILLS_FLEXIBLE, 9 | 11, 1) => 93,
(BUCKET_NAME_BILLS_FLEXIBLE, 9 | 11, _) => 83,
(BUCKET_NAME_BILLS_FLEXIBLE, _, _) => {
- panic!("budget drip {:?} had unexpected date", budget_drip)
+ panic!("drip {:?} had unexpected date", drip)
}
(BUCKET_NAME_SAVINGS, 8 | 10, 1) => 13_00,
(BUCKET_NAME_SAVINGS, 8 | 10, _) => 12_90,
(BUCKET_NAME_SAVINGS, 9 | 11, 1) => 13_43,
(BUCKET_NAME_SAVINGS, 9 | 11, _) => 13_33,
- (BUCKET_NAME_SAVINGS, _, _) => panic!("budget drip {:?} had unexpected date", budget_drip),
+ (BUCKET_NAME_SAVINGS, _, _) => panic!("drip {:?} had unexpected date", drip),
- (_, _, _) => panic!(
- "budget drip {:?} had unexpected bucket name {}",
- budget_drip, bucket_name,
- ),
+ (_, _, _) => panic!("drip {:?} had unexpected bucket name {}", drip, bucket_name,),
};
assert_eq!(
- expected_quantity, budget_drip.amount,
- "budget drip {:?} had unexpected quantity",
- budget_drip,
+ expected_quantity, drip.amount,
+ "drip {:?} had unexpected quantity",
+ drip,
);
}
diff --git a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/common/mod.rs b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/common/mod.rs
index f067d0d..82d7870 100644
--- a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/common/mod.rs
+++ b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/common/mod.rs
@@ -4,8 +4,9 @@ mod account_transfer_matchers;
mod bucket_matchers;
mod bucket_names;
mod bucket_transfer_matchers;
-mod budget_drip_matchers;
mod db_url;
+mod drip_matchers;
+mod pipe_matchers;
mod test_context;
mod transaction_matchers;
@@ -15,13 +16,12 @@ pub use account_transfer_matchers::get_account_transfer_matchers;
pub use bucket_matchers::get_bucket_matchers;
pub use bucket_names::BUCKET_NAMES_DICT;
pub use bucket_transfer_matchers::get_bucket_transfer_matchers;
-pub use budget_drip_matchers::assert_budget_drip_matches;
-pub use budget_drip_matchers::get_num_expected_budget_drips;
pub use db_url::TEST_IN_DB_URL;
pub use db_url::TEST_OUT_DB_URL;
+pub use drip_matchers::assert_drip_matches;
+pub use drip_matchers::get_num_expected_drips;
+pub use pipe_matchers::get_pipe_matchers;
pub use test_context::TestContext;
pub use transaction_matchers::get_transaction_matchers;
-pub use transaction_matchers::matches_bank_account_initial_transaction;
-pub use transaction_matchers::matches_cash_account_initial_transaction;
pub use transaction_matchers::matches_food_transaction;
pub use transaction_matchers::matches_income_transaction;
diff --git a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/common/pipe_matchers.rs b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/common/pipe_matchers.rs
new file mode 100644
index 0000000..3ca92c8
--- /dev/null
+++ b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/common/pipe_matchers.rs
@@ -0,0 +1,36 @@
+use schist_models::{Bucket, BudgetPeriodUnit, Pipe};
+
+use super::bucket_names::BUCKET_NAMES_DICT;
+
+macro_rules! matches_pipe {
+ ($pipe:ident, $amount:expr, $start_date:expr) => {
+ Box::new(|pipe: &Pipe, buckets: &[Bucket]| -> bool {
+ let bucket = buckets
+ .iter()
+ .find(|c| c.name == BUCKET_NAMES_DICT.$pipe)
+ .expect("failed to find $pipe bucket");
+
+ match pipe {
+ Pipe {
+ id: _id,
+ amount: $amount,
+ bucket_id,
+ period_length: 1,
+ period_unit: BudgetPeriodUnit::Month,
+ start_date,
+ } => *bucket_id == bucket.id && *start_date == $start_date.parse().unwrap(),
+ _ => false,
+ }
+ })
+ };
+}
+
+pub fn get_pipe_matchers() -> Vec<Box<dyn Fn(&Pipe, &[Bucket]) -> bool>> {
+ vec![
+ matches_pipe!(food, 200_00, "2024-08-01"),
+ matches_pipe!(general, 100_00, "2024-08-01"),
+ matches_pipe!(bills, 125_00, "2024-08-01"),
+ matches_pipe!(bills_flexible, 25_00, "2024-08-01"),
+ matches_pipe!(savings, 400_00, "2024-08-01"),
+ ]
+}
diff --git a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/common/transaction_matchers.rs b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/common/transaction_matchers.rs
index e6c3f27..836a8a1 100644
--- a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/common/transaction_matchers.rs
+++ b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/common/transaction_matchers.rs
@@ -1,23 +1,28 @@
-use schist_models::{Account, Transaction};
+use schist_models::{Account, Bucket, Transaction};
+
+use crate::common::bucket_names::{BUCKET_NAME_FOOD, BUCKET_NAME_INCOME};
use super::ACCOUNT_NAMES_DICT;
-pub fn get_transaction_matchers() -> Vec<Box<dyn Fn(&Transaction, &[Account]) -> bool>> {
+pub fn get_transaction_matchers() -> Vec<Box<dyn Fn(&Transaction, &[Account], &[Bucket]) -> bool>> {
vec![
- Box::new(|t, a| matches_bank_account_initial_transaction(t, a)),
- Box::new(|t, a| matches_cash_account_initial_transaction(t, a)),
- Box::new(|t, a| matches_income_transaction(t, a, "08")),
- Box::new(|t, a| matches_income_transaction(t, a, "09")),
- Box::new(|t, a| matches_income_transaction(t, a, "10")),
- Box::new(|t, a| matches_income_transaction(t, a, "11")),
- Box::new(|t, a| matches_food_transaction(t, a, "08")),
- Box::new(|t, a| matches_food_transaction(t, a, "09")),
- Box::new(|t, a| matches_food_transaction(t, a, "10")),
- Box::new(|t, a| matches_food_transaction(t, a, "11")),
+ Box::new(|t, a, b| matches_income_transaction(t, a, b, "08")),
+ Box::new(|t, a, b| matches_income_transaction(t, a, b, "09")),
+ Box::new(|t, a, b| matches_income_transaction(t, a, b, "10")),
+ Box::new(|t, a, b| matches_income_transaction(t, a, b, "11")),
+ Box::new(|t, a, b| matches_food_transaction(t, a, b, "08")),
+ Box::new(|t, a, b| matches_food_transaction(t, a, b, "09")),
+ Box::new(|t, a, b| matches_food_transaction(t, a, b, "10")),
+ Box::new(|t, a, b| matches_food_transaction(t, a, b, "11")),
]
}
-pub fn matches_bank_account_initial_transaction(t: &Transaction, accounts: &[Account]) -> bool {
+pub fn matches_income_transaction(
+ t: &Transaction,
+ accounts: &[Account],
+ buckets: &[Bucket],
+ month: &str,
+) -> bool {
let bank_account_id = accounts
.iter()
.find(|a| a.name == ACCOUNT_NAMES_DICT.bank_account)
@@ -25,66 +30,15 @@ pub fn matches_bank_account_initial_transaction(t: &Transaction, accounts: &[Acc
.id
.clone();
- match t {
- Transaction {
- account_id,
- bucket_id,
- date,
- id: _id,
- description,
- counterparty: payee,
- amount: 1200_00,
- } => {
- *account_id == bank_account_id
- && *bucket_id == get_bucket_id()
- && *date == "2024-08-01".parse().unwrap()
- && description == ""
- && payee == "Starting Balance"
- }
- _ => false,
- }
-}
-
-pub fn matches_cash_account_initial_transaction(t: &Transaction, accounts: &[Account]) -> bool {
- let bank_account_id = accounts
+ let income_bucket = buckets
.iter()
- .find(|a| a.name == ACCOUNT_NAMES_DICT.cash_account)
- .expect("failed to find cash account")
- .id
- .clone();
+ .find(|c| c.name == BUCKET_NAME_INCOME)
+ .expect("failed to find income bucket");
match t {
Transaction {
account_id,
- bucket_id,
- date,
- id: _id,
- description,
- counterparty: payee,
- amount: 45_10,
- } => {
- *account_id == bank_account_id
- && *bucket_id == get_bucket_id()
- && *date == "2024-08-01".parse().unwrap()
- && description == ""
- && payee == "Starting Balance"
- }
- _ => false,
- }
-}
-
-pub fn matches_income_transaction(t: &Transaction, accounts: &[Account], month: &str) -> bool {
- let bank_account_id = accounts
- .iter()
- .find(|a| a.name == ACCOUNT_NAMES_DICT.bank_account)
- .expect("failed to find bank account")
- .id
- .clone();
-
- match t {
- Transaction {
- account_id,
- bucket_id,
+ bucket_id: Some(bucket_id),
date,
description,
id: _id,
@@ -92,7 +46,7 @@ pub fn matches_income_transaction(t: &Transaction, accounts: &[Account], month:
amount: 846_46,
} => {
*account_id == bank_account_id
- && *bucket_id == get_bucket_id()
+ && *bucket_id == income_bucket.id
&& *date == format!("2024-{}-21", month).parse().unwrap()
&& description == "Salary"
&& payee == "Employer Ltd"
@@ -101,7 +55,12 @@ pub fn matches_income_transaction(t: &Transaction, accounts: &[Account], month:
}
}
-pub fn matches_food_transaction(t: &Transaction, accounts: &[Account], month: &str) -> bool {
+pub fn matches_food_transaction(
+ t: &Transaction,
+ accounts: &[Account],
+ buckets: &[Bucket],
+ month: &str,
+) -> bool {
let bank_account_id = accounts
.iter()
.find(|a| a.name == ACCOUNT_NAMES_DICT.bank_account)
@@ -109,10 +68,15 @@ pub fn matches_food_transaction(t: &Transaction, accounts: &[Account], month: &s
.id
.clone();
+ let food_bucket = buckets
+ .iter()
+ .find(|c| c.name == BUCKET_NAME_FOOD)
+ .expect("failed to find food bucket");
+
match t {
Transaction {
account_id,
- bucket_id,
+ bucket_id: Some(bucket_id),
date,
description,
id: _id,
@@ -120,7 +84,7 @@ pub fn matches_food_transaction(t: &Transaction, accounts: &[Account], month: &s
amount: -200_00,
} => {
*account_id == bank_account_id
- && *bucket_id == get_bucket_id()
+ && *bucket_id == food_bucket.id
&& *date == format!("2024-{}-08", month).parse().unwrap()
&& description == ""
&& payee == "Tesco"
@@ -128,7 +92,3 @@ pub fn matches_food_transaction(t: &Transaction, accounts: &[Account], month: &s
_ => false,
}
}
-
-fn get_bucket_id() -> Option<i32> {
- todo!()
-}
diff --git a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/resources/actualbudget_test_db.sqlite b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/resources/actualbudget_test_db.sqlite
index f8d3f5d..5f89924 100644
--- a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/resources/actualbudget_test_db.sqlite
+++ b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/resources/actualbudget_test_db.sqlite
Binary files differ
diff --git a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/run.rs b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/run.rs
index aff034f..04c21ca 100644
--- a/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/run.rs
+++ b/transformers/actualbudget_transformer/actualbudget_to_schist_transformer/tests/run.rs
@@ -2,18 +2,20 @@ pub mod common;
use actualbudget_to_schist_transformer::run::run;
use common::{
- assert_budget_drip_matches, get_account_matchers, get_account_transfer_matchers,
- get_bucket_matchers, get_bucket_transfer_matchers, get_num_expected_budget_drips,
- get_transaction_matchers, TestContext, TEST_IN_DB_URL, TEST_OUT_DB_URL,
+ assert_drip_matches, get_account_matchers, get_account_transfer_matchers, get_bucket_matchers,
+ get_bucket_transfer_matchers, get_num_expected_drips, get_transaction_matchers, TestContext,
+ TEST_IN_DB_URL, TEST_OUT_DB_URL,
};
use diesel::{Connection, SqliteConnection};
-use schist_models::Pipe;
+
use schist_queries::{
account_transfers::get_all_account_transfers, accounts::get_all_accounts,
bucket_transfers::get_all_bucket_transfers, buckets::get_all_buckets, drips::get_all_drips,
pipes::get_all_pipes, transactions::get_all_transactions,
};
+use crate::common::get_pipe_matchers;
+
#[test]
fn given_test_database_when_run_then_exports_expected_schist_state() {
let _context = TestContext::new();
@@ -49,9 +51,9 @@ fn given_test_database_when_run_then_exports_expected_schist_state() {
);
}
- assert_eq!(get_num_expected_budget_drips(), drips.len());
+ assert_eq!(get_num_expected_drips(), drips.len());
for budget_drip in drips {
- assert_budget_drip_matches(&budget_drip, &buckets);
+ assert_drip_matches(&budget_drip, &buckets);
}
let bucket_matchers = get_bucket_matchers();
@@ -82,11 +84,19 @@ fn given_test_database_when_run_then_exports_expected_schist_state() {
assert!(
transaction_matchers
.iter()
- .any(|m| m(&transaction, &accounts)),
+ .any(|m| m(&transaction, &accounts, &buckets)),
"no match found for transaction {:?}",
transaction,
);
}
- assert_eq!(Vec::<Pipe>::new(), pipes);
+ let pipe_matchers = get_pipe_matchers();
+ assert_eq!(pipe_matchers.len(), pipes.len());
+ for pipe in pipes.clone() {
+ assert!(
+ pipe_matchers.iter().any(|p| p(&pipe, &buckets)),
+ "no match found for pipe: {:?}",
+ pipe
+ );
+ }
}