Fix move_money, get it working with real data
This commit is contained in:
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user