From caeed72ad1dd46c715d1328a7cc6c22069cf3add Mon Sep 17 00:00:00 2001 From: Piv <18462828+Piv200@users.noreply.github.com> Date: Tue, 31 Jan 2023 21:15:36 +1030 Subject: [PATCH] Fix move_money, get it working with real data --- src/move_money.rs | 132 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 93 insertions(+), 39 deletions(-) diff --git a/src/move_money.rs b/src/move_money.rs index f89fb3a..a07ec33 100644 --- a/src/move_money.rs +++ b/src/move_money.rs @@ -3,22 +3,39 @@ use std::collections::HashMap; use itertools::Itertools; use serde::{Deserialize, Serialize}; -// TODO: Fix up these names, check if all is actually integrated into the strings #[derive(Debug, Deserialize)] struct CsvMovementRule { - #[serde(rename = "FromCC")] - // Need strings to further split later - from_departments: String, - to_departments: String, + #[serde(rename = "CostCentreSourceFrom", default)] + source_from_department: String, + #[serde(rename = "CostCentreSourceTo", default)] + source_to_department: String, + #[serde(rename = "CostCentreDestFrom", default)] + dest_from_department: String, + #[serde(rename = "CostCentreDestTo", default)] + dest_to_department: String, + #[serde(rename = "AccountSourceFrom", default)] + source_from_account: String, + #[serde(rename = "AccountSourceTo", default)] + source_to_account: String, + #[serde(rename = "AccountDestFrom", default)] + dest_from_account: String, + #[serde(rename = "AccountDestTo", default)] + dest_to_account: String, + // TODO: Check how these actually work, they're somehow integrated into other columns + #[serde(default)] all_from_departments: bool, + #[serde(default)] all_to_departments: bool, - from_accounts: String, - to_accounts: String, + #[serde(default)] all_from_accounts: bool, + #[serde(default)] all_to_accounts: bool, - amount: f64, - is_percent: Option, - is_separator: Option, + #[serde(rename = "AmountValue")] + amount: Option, + #[serde(rename = "AmountType", default)] + is_percent: String, + #[serde(rename = "Apply", default)] + apply: String, } #[derive(Default)] @@ -65,7 +82,9 @@ pub struct Unit { #[derive(Debug, Serialize, Deserialize)] pub struct CsvCost { + #[serde(rename = "ACCOUNT")] account: String, + #[serde(rename = "COSTCENTRE")] department: String, value: f64, } @@ -85,11 +104,14 @@ where let headers = lines_reader.headers()?; let mut account_index = 0; let mut department_index = 0; + let mut account_type_index = 0; for (index, field) in headers.iter().enumerate() { if field.eq_ignore_ascii_case("account") { account_index = index; - } else if field.eq_ignore_ascii_case("department") { + } else if field.eq_ignore_ascii_case("costcentre") { department_index = index; + } else if field.eq_ignore_ascii_case("accounttype") { + account_type_index = index; } } @@ -102,8 +124,10 @@ where let sum = record .iter() .enumerate() - .filter(|(i, _)| *i != account_index && *i != department_index) - .map(|(_, f)| f.parse::().unwrap()) + .filter(|(i, _)| { + *i != account_index && *i != department_index && *i != account_type_index + }) + .map(|(_, f)| f.parse::().unwrap_or(0.)) .sum(); ( Unit { @@ -119,6 +143,7 @@ where lines .keys() .map(|key| key.account.clone().parse::().unwrap()) + .unique() .sorted() .map(|account| account.to_string()) .collect() @@ -126,50 +151,56 @@ where lines .keys() .map(|key| key.account.clone()) + .unique() .sorted() .collect() }; let all_departments_sorted = lines .keys() .map(|key| key.department.clone()) + .unique() .sorted() .collect(); let mut rules_reader = rules_reader; let mut rules: Vec = vec![]; - for result in rules_reader.deserialize() { - let movement_rule: CsvMovementRule = result?; + for movement_rule in rules_reader.deserialize() { + let movement_rule: CsvMovementRule = movement_rule?; let from_accounts = extract_range( - movement_rule.from_accounts, - movement_rule.all_from_accounts, + movement_rule.source_from_account, + movement_rule.source_to_account, + false, &all_accounts_sorted, ); let to_accounts = extract_range( - movement_rule.to_accounts, - movement_rule.all_to_accounts, + movement_rule.dest_from_account, + movement_rule.dest_to_account, + false, &all_accounts_sorted, ); let from_departments = extract_range( - movement_rule.from_departments, - movement_rule.all_from_departments, + movement_rule.source_from_department, + movement_rule.source_to_department, + false, &all_departments_sorted, ); let to_departments = extract_range( - movement_rule.to_departments, - movement_rule.all_to_departments, + movement_rule.dest_from_department, + movement_rule.dest_to_department, + false, &all_departments_sorted, ); rules.push(MovementRule { from_departments, to_departments, - all_from_departments: movement_rule.all_from_departments, - all_to_departments: movement_rule.all_to_departments, + all_from_departments: false, + all_to_departments: false, from_accounts, to_accounts, - all_from_accounts: movement_rule.all_from_accounts, - all_to_accounts: movement_rule.all_to_accounts, - amount: movement_rule.amount, - is_percent: movement_rule.is_percent.unwrap_or(false), - is_separator: movement_rule.is_separator.unwrap_or(false), + all_from_accounts: false, + all_to_accounts: false, + amount: movement_rule.amount.unwrap_or(0.), + is_percent: movement_rule.is_percent == "%", + is_separator: movement_rule.apply == "-DIVIDER-", }) } @@ -189,23 +220,19 @@ where Ok(()) } -fn extract_range(range: String, all: bool, options: &Vec) -> Vec { +fn extract_range(from: String, to: String, all: bool, options: &Vec) -> Vec { if all { return vec![]; } - let split_range: Vec<&str> = range.split("-").collect(); - if split_range.len() == 1 { - return vec![range]; - } let start_index = options .iter() .enumerate() - .find(|option| option.1 == split_range[0]) + .find(|option| option.1 == &from) .map(|start| start.0); let end_index = options .iter() .enumerate() - .find(|option| option.1 == split_range[1]) + .find(|option| option.1 == &to) .map(|end| end.0); if let Some(start) = start_index { if let Some(end) = end_index { @@ -269,15 +296,42 @@ pub fn move_money_2( let num_to_units = running_total .keys() .filter(|key| { - (rule.all_to_accounts || rule.to_departments.contains(&key.department)) + (rule.all_to_departments || rule.to_departments.contains(&key.department)) && (rule.all_to_accounts || rule.to_accounts.contains(&key.account)) }) .count(); let value_per_unit = sum_from / num_to_units as f64; for unit in running_total.keys() { - *temp_total.get_mut(&unit).unwrap() += value_per_unit; + if (rule.all_to_departments || rule.to_departments.contains(&unit.department)) + && (rule.all_to_accounts || rule.to_accounts.contains(&unit.account)) + { + *temp_total.get_mut(&unit).unwrap() += value_per_unit; + } } } } temp_total } + +#[cfg(test)] +mod tests { + + #[test] + fn it_works() { + let result = 2 + 2; + assert_eq!(result, 4); + } + + #[test] + fn move_money() { + let result = super::move_money( + csv::Reader::from_path("reclassrule.csv").unwrap(), + csv::Reader::from_path("line.csv").unwrap(), + csv::Writer::from_path("output.csv").unwrap(), + false, + ); + if let Err(err) = result { + print!("{}", err); + } + } +}