Fix move_money, get it working with real data

This commit is contained in:
Piv
2023-01-31 21:15:36 +10:30
parent 10723efb57
commit caeed72ad1

View File

@@ -3,22 +3,39 @@ use std::collections::HashMap;
use itertools::Itertools; use itertools::Itertools;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
// TODO: Fix up these names, check if all is actually integrated into the strings
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
struct CsvMovementRule { struct CsvMovementRule {
#[serde(rename = "FromCC")] #[serde(rename = "CostCentreSourceFrom", default)]
// Need strings to further split later source_from_department: String,
from_departments: String, #[serde(rename = "CostCentreSourceTo", default)]
to_departments: String, 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, all_from_departments: bool,
#[serde(default)]
all_to_departments: bool, all_to_departments: bool,
from_accounts: String, #[serde(default)]
to_accounts: String,
all_from_accounts: bool, all_from_accounts: bool,
#[serde(default)]
all_to_accounts: bool, all_to_accounts: bool,
amount: f64, #[serde(rename = "AmountValue")]
is_percent: Option<bool>, amount: Option<f64>,
is_separator: Option<bool>, #[serde(rename = "AmountType", default)]
is_percent: String,
#[serde(rename = "Apply", default)]
apply: String,
} }
#[derive(Default)] #[derive(Default)]
@@ -65,7 +82,9 @@ pub struct Unit {
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct CsvCost { pub struct CsvCost {
#[serde(rename = "ACCOUNT")]
account: String, account: String,
#[serde(rename = "COSTCENTRE")]
department: String, department: String,
value: f64, value: f64,
} }
@@ -85,11 +104,14 @@ where
let headers = lines_reader.headers()?; let headers = lines_reader.headers()?;
let mut account_index = 0; let mut account_index = 0;
let mut department_index = 0; let mut department_index = 0;
let mut account_type_index = 0;
for (index, field) in headers.iter().enumerate() { for (index, field) in headers.iter().enumerate() {
if field.eq_ignore_ascii_case("account") { if field.eq_ignore_ascii_case("account") {
account_index = index; account_index = index;
} else if field.eq_ignore_ascii_case("department") { } else if field.eq_ignore_ascii_case("costcentre") {
department_index = index; department_index = index;
} else if field.eq_ignore_ascii_case("accounttype") {
account_type_index = index;
} }
} }
@@ -102,8 +124,10 @@ where
let sum = record let sum = record
.iter() .iter()
.enumerate() .enumerate()
.filter(|(i, _)| *i != account_index && *i != department_index) .filter(|(i, _)| {
.map(|(_, f)| f.parse::<f64>().unwrap()) *i != account_index && *i != department_index && *i != account_type_index
})
.map(|(_, f)| f.parse::<f64>().unwrap_or(0.))
.sum(); .sum();
( (
Unit { Unit {
@@ -119,6 +143,7 @@ where
lines lines
.keys() .keys()
.map(|key| key.account.clone().parse::<i32>().unwrap()) .map(|key| key.account.clone().parse::<i32>().unwrap())
.unique()
.sorted() .sorted()
.map(|account| account.to_string()) .map(|account| account.to_string())
.collect() .collect()
@@ -126,50 +151,56 @@ where
lines lines
.keys() .keys()
.map(|key| key.account.clone()) .map(|key| key.account.clone())
.unique()
.sorted() .sorted()
.collect() .collect()
}; };
let all_departments_sorted = lines let all_departments_sorted = lines
.keys() .keys()
.map(|key| key.department.clone()) .map(|key| key.department.clone())
.unique()
.sorted() .sorted()
.collect(); .collect();
let mut rules_reader = rules_reader; let mut rules_reader = rules_reader;
let mut rules: Vec<MovementRule> = vec![]; let mut rules: Vec<MovementRule> = vec![];
for result in rules_reader.deserialize() { for movement_rule in rules_reader.deserialize() {
let movement_rule: CsvMovementRule = result?; let movement_rule: CsvMovementRule = movement_rule?;
let from_accounts = extract_range( let from_accounts = extract_range(
movement_rule.from_accounts, movement_rule.source_from_account,
movement_rule.all_from_accounts, movement_rule.source_to_account,
false,
&all_accounts_sorted, &all_accounts_sorted,
); );
let to_accounts = extract_range( let to_accounts = extract_range(
movement_rule.to_accounts, movement_rule.dest_from_account,
movement_rule.all_to_accounts, movement_rule.dest_to_account,
false,
&all_accounts_sorted, &all_accounts_sorted,
); );
let from_departments = extract_range( let from_departments = extract_range(
movement_rule.from_departments, movement_rule.source_from_department,
movement_rule.all_from_departments, movement_rule.source_to_department,
false,
&all_departments_sorted, &all_departments_sorted,
); );
let to_departments = extract_range( let to_departments = extract_range(
movement_rule.to_departments, movement_rule.dest_from_department,
movement_rule.all_to_departments, movement_rule.dest_to_department,
false,
&all_departments_sorted, &all_departments_sorted,
); );
rules.push(MovementRule { rules.push(MovementRule {
from_departments, from_departments,
to_departments, to_departments,
all_from_departments: movement_rule.all_from_departments, all_from_departments: false,
all_to_departments: movement_rule.all_to_departments, all_to_departments: false,
from_accounts, from_accounts,
to_accounts, to_accounts,
all_from_accounts: movement_rule.all_from_accounts, all_from_accounts: false,
all_to_accounts: movement_rule.all_to_accounts, all_to_accounts: false,
amount: movement_rule.amount, amount: movement_rule.amount.unwrap_or(0.),
is_percent: movement_rule.is_percent.unwrap_or(false), is_percent: movement_rule.is_percent == "%",
is_separator: movement_rule.is_separator.unwrap_or(false), is_separator: movement_rule.apply == "-DIVIDER-",
}) })
} }
@@ -189,23 +220,19 @@ where
Ok(()) Ok(())
} }
fn extract_range(range: String, all: bool, options: &Vec<String>) -> Vec<String> { fn extract_range(from: String, to: String, all: bool, options: &Vec<String>) -> Vec<String> {
if all { if all {
return vec![]; return vec![];
} }
let split_range: Vec<&str> = range.split("-").collect();
if split_range.len() == 1 {
return vec![range];
}
let start_index = options let start_index = options
.iter() .iter()
.enumerate() .enumerate()
.find(|option| option.1 == split_range[0]) .find(|option| option.1 == &from)
.map(|start| start.0); .map(|start| start.0);
let end_index = options let end_index = options
.iter() .iter()
.enumerate() .enumerate()
.find(|option| option.1 == split_range[1]) .find(|option| option.1 == &to)
.map(|end| end.0); .map(|end| end.0);
if let Some(start) = start_index { if let Some(start) = start_index {
if let Some(end) = end_index { if let Some(end) = end_index {
@@ -269,15 +296,42 @@ pub fn move_money_2(
let num_to_units = running_total let num_to_units = running_total
.keys() .keys()
.filter(|key| { .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)) && (rule.all_to_accounts || rule.to_accounts.contains(&key.account))
}) })
.count(); .count();
let value_per_unit = sum_from / num_to_units as f64; let value_per_unit = sum_from / num_to_units as f64;
for unit in running_total.keys() { for unit in running_total.keys() {
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.get_mut(&unit).unwrap() += value_per_unit;
} }
} }
} }
}
temp_total 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);
}
}
}