From c1d4b0a43046b8aa4bad6ebf6ca3f74aba1bb54f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yaroslav=20de=20la=20Pe=C3=B1a=20Smirnov?= Date: Fri, 9 Oct 2020 23:51:37 +0300 Subject: take into account shared expenses --- budget/src/lib.rs | 24 ++++++++++++++++++++---- budget/tests/budget.rs | 14 ++++++++++++-- budget/tests/test.toml | 3 ++- src/main.rs | 16 ++++++++++++++++ 4 files changed, 50 insertions(+), 7 deletions(-) diff --git a/budget/src/lib.rs b/budget/src/lib.rs index 7d7c390..7d194c0 100644 --- a/budget/src/lib.rs +++ b/budget/src/lib.rs @@ -49,6 +49,7 @@ pub struct Calculated { pub categories_subtotal: HashMap, pub total: f64, pub balance: f64, + pub total_owed: HashMap, pub days_left: f64, pub days_left_essential: f64, pub last_day: NaiveDate, @@ -69,9 +70,6 @@ fn recurring_default() -> bool { } // Parse the dates from toml's Datetime to Chrono's NaiveDate -// Probably unnecessary for now, but since I am planning on using the dates in -// the future to more easily count the days, it would be better to have them in -// a proper format fn deserialize_date<'de, D>(deserializer: D) -> Result where D: Deserializer<'de> { toml::value::Datetime::deserialize(deserializer) @@ -98,7 +96,7 @@ pub fn parse_account(path: &str) -> Result { } pub fn calculate(account: &Account) -> Option { - if account.days.len() < 1 { + if account.days.is_empty() { return None; } @@ -110,6 +108,7 @@ pub fn calculate(account: &Account) -> Option { categories_subtotal: HashMap::::new(), total: 0.0, balance: 0.0, + total_owed: HashMap::::new(), days_left: 0.0, days_left_essential: 0.0, last_day: account.days.last().unwrap().date, @@ -137,6 +136,23 @@ pub fn calculate(account: &Account) -> Option { if account.essential_categories.contains(category) { calculated.essential_subtotal += expense.price; } + + if expense.shared > 1 { + let owed = + expense.price * + (expense.shared as f64 - 1.0) / + expense.shared as f64; + + if let Some(total_owed_by) = + calculated.total_owed.get_mut(&expense.shared) { + *total_owed_by += owed; + } else { + calculated.total_owed.insert( + expense.shared, + owed, + ); + } + } } } } diff --git a/budget/tests/budget.rs b/budget/tests/budget.rs index feae240..6a9214d 100644 --- a/budget/tests/budget.rs +++ b/budget/tests/budget.rs @@ -31,7 +31,7 @@ fn can_parse_account() -> Result<(), ParseError>{ name: String::from("Bacon"), price: 3.33, qty: 1, - shared: 2, + shared: 3, recurring: false, category: Some(String::from("products")), }, @@ -39,7 +39,7 @@ fn can_parse_account() -> Result<(), ParseError>{ name: String::from("Yoghurt"), price: 1.24, qty: 2, - shared: 1, + shared: 2, recurring: false, category: Some(String::from("products")), }, @@ -106,6 +106,7 @@ fn can_calculate() -> Result<(), ParseError> { categories_subtotal: HashMap::::new(), total: 22.71, balance: 397.29, + total_owed: HashMap::::new(), days_left: 69.9762219286658, days_left_essential: 84.08253968253969, last_day: NaiveDate::from_ymd(2020, 10, 04), @@ -145,6 +146,15 @@ fn can_calculate() -> Result<(), ParseError> { 5.0, ); + should_be.total_owed.insert( + 2, + 1.7599999999999998, + ); + should_be.total_owed.insert( + 3, + 2.22, + ); + let account = budget::parse_account("tests/test.toml")?; let actually_is = budget::calculate(&account).unwrap(); diff --git a/budget/tests/test.toml b/budget/tests/test.toml index a29467e..ca2c1e5 100644 --- a/budget/tests/test.toml +++ b/budget/tests/test.toml @@ -19,13 +19,14 @@ date = 2020-10-01 name = "Bacon" price = 3.33 category = "products" - shared = 2 + shared = 3 [[days.expenses]] name = "Yoghurt" price = 1.24 category = "products" qty = 2 + shared = 2 [[days.expenses]] name = "Onion" diff --git a/src/main.rs b/src/main.rs index 5c64959..cd401e2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -181,6 +181,20 @@ fn output(account: Account, maybe_calculated: Option) { println!(); + for (n, owed) in calculated.total_owed.iter() { + println!( + "{} person(s) owe you in shared expenses: {:.2}", + n - 1, + owed, + ); + + if *n > 2 { + println!("Each owes you: {}", *owed / (*n as f64 - 1.0)); + } + + println!(); + } + println!("Days until balance runs out:"); let days_left_output = format!( @@ -192,6 +206,8 @@ fn output(account: Account, maybe_calculated: Option) { calculated.days_left_essential, ); + // TODO: also show much money would be left by the end of the period + let mut all_are_healthy = true; let mut essential_are_healthy = true; -- cgit v1.2.3