Complete move money between departments

This commit is contained in:
Piv
2023-01-28 10:51:31 +10:30
parent 232ed9dd39
commit 37a7b333ac
4 changed files with 300 additions and 70 deletions

View File

@@ -6,6 +6,7 @@ use std::{collections::HashMap, error::Error, ops::Mul};
// TODO: Look into serde for serialisation, can also use it to serialise/deserialise
// records from a csv file using the csv crate
#[derive(Default)]
pub struct MovementRule {
// If the vectors are empty, then it means 'all'
pub from_units: Vec<String>,
@@ -16,16 +17,6 @@ pub struct MovementRule {
}
impl MovementRule {
pub fn new() -> MovementRule {
MovementRule {
from_units: vec![],
to_units: vec![],
amount: 0.0,
is_percent: false,
is_separator: false,
}
}
pub fn pass_break() -> MovementRule {
MovementRule {
from_units: vec![],
@@ -80,41 +71,50 @@ pub fn move_money_1() {}
// Traditinoal/naive, total for each department is stored in an initial map (department -> total amount).
// Another map is built up for each rule, and each rule is processed based on the amount in the current total
// map.
// Upon a pass break (divider), the temp map will assign the values into the total map.
// Once done, do a final assignment back to the total back, and return that. Probably want to make a copy or
// borrow the total map so it isn't mutated elsewhere.
// Advantage of this is the required code is tiny, and no third-party math library is required (my matrix math
// implementation probably won't be as good as one that's battle-tested)
// Upon a pass break (separator), the temp map will assign the values into the total map.
// Once done, do a final assignment back to the total, and return that.
// Advantage of this is the required code is tiny, and no third-party math library is required.
// Note that the movement happens on a line-by-line level. So we can stream the data from disk, and potentially apply this
// to every. It's also much more memory efficient than approach 1.
// TODO: Time both approaches to seee which is faster depending on the size of the input data/number of rules
// TODO: Right now this only supports movements between departments, we also need to support movements between accounts.
// This would require an expansion so that we also have from/to accounts, and the hashmap will use some struct
// that combines an account/department, which is also how the totals will be loaded. (so when loading from disk,
// we load the whole GL into memory sum the account/department totals, and move these into a map line by line)
pub fn move_money_2(
initial_totals: HashMap<String, f64>,
rules: Vec<MovementRule>,
) -> HashMap<String, f64> {
// TODO: Replace maps with generic objects, so we can sub in db access/load only some initially
// TODO: Should probably validate that all the rules have departments that actually exist in initial_totals.
// Note: It's potentially a bit more intensive to use cloned totals, but it's much simpler code and, and since we're only working line-by-line
// it isn't really that much memory. in practice
let mut running_total = HashMap::from(initial_totals);
let mut temp_total: HashMap<String, f64> = HashMap::new();
let mut temp_total: HashMap<String, f64> = running_total.clone();
for rule in rules {
if rule.is_separator {
temp_total.into_iter().for_each(|temp| {
running_total.insert(temp.0, temp.1).unwrap();
});
temp_total = HashMap::new();
} else if rule.is_percent {
let new_value: f64 = running_total
.iter()
.filter(|department| rule.from_units.contains(department.0))
.map(|department| department.1 * rule.amount)
.sum();
for department in rule.to_units {
let previous_temp = temp_total.entry(department).or_insert(0.0);
*previous_temp += new_value;
}
// TODO: Subtract values from the from departments
running_total = temp_total.clone();
} else {
// TODO: Simple addition to to departments/subtraction from from departments
let mut sum_from = 0.;
for department in rule.from_units {
let previous_temp = running_total.get(&department).expect(
"Failed to find department in temp totals, this should not be possible",
);
let added_amount = if rule.is_percent {
*previous_temp * rule.amount
} else {
rule.amount
};
sum_from += added_amount;
*temp_total.get_mut(&department).unwrap() -= added_amount;
}
let value_per_unit = sum_from / rule.to_units.len() as f64;
for department in rule.to_units {
*temp_total.get_mut(&department).unwrap() += value_per_unit;
}
}
}
running_total
temp_total
}
#[derive(Debug, PartialEq, Eq)]