koprogo_api/domain/services/
expense_calculator.rs1use crate::domain::entities::{Expense, Unit};
2
3pub struct ExpenseCalculator;
5
6impl ExpenseCalculator {
7 pub fn calculate_unit_share(expense: &Expense, unit: &Unit) -> f64 {
9 expense.amount * (unit.quota / 1000.0)
10 }
11
12 pub fn calculate_total_expenses(expenses: &[Expense]) -> f64 {
14 expenses.iter().map(|e| e.amount).sum()
15 }
16
17 pub fn calculate_paid_expenses(expenses: &[Expense]) -> f64 {
19 expenses
20 .iter()
21 .filter(|e| e.is_paid())
22 .map(|e| e.amount)
23 .sum()
24 }
25
26 pub fn calculate_unpaid_expenses(expenses: &[Expense]) -> f64 {
28 expenses
29 .iter()
30 .filter(|e| !e.is_paid())
31 .map(|e| e.amount)
32 .sum()
33 }
34}
35
36#[cfg(test)]
37mod tests {
38 use super::*;
39 use crate::domain::entities::{ExpenseCategory, UnitType};
40 use chrono::Utc;
41 use uuid::Uuid;
42
43 #[test]
44 fn test_calculate_unit_share() {
45 let org_id = Uuid::new_v4();
46 let building_id = Uuid::new_v4();
47
48 let expense = Expense::new(
49 org_id,
50 building_id,
51 ExpenseCategory::Maintenance,
52 "Test".to_string(),
53 1000.0,
54 Utc::now(),
55 None,
56 None,
57 None, )
59 .unwrap();
60
61 let unit = Unit::new(
62 org_id,
63 building_id,
64 "A101".to_string(),
65 UnitType::Apartment,
66 Some(1),
67 75.0,
68 50.0, )
70 .unwrap();
71
72 let share = ExpenseCalculator::calculate_unit_share(&expense, &unit);
73 assert_eq!(share, 50.0); }
75
76 #[test]
77 fn test_calculate_total_expenses() {
78 let org_id = Uuid::new_v4();
79 let building_id = Uuid::new_v4();
80
81 let expenses = vec![
82 Expense::new(
83 org_id,
84 building_id,
85 ExpenseCategory::Maintenance,
86 "Test 1".to_string(),
87 100.0,
88 Utc::now(),
89 None,
90 None,
91 None, )
93 .unwrap(),
94 Expense::new(
95 org_id,
96 building_id,
97 ExpenseCategory::Repairs,
98 "Test 2".to_string(),
99 200.0,
100 Utc::now(),
101 None,
102 None,
103 None, )
105 .unwrap(),
106 ];
107
108 let total = ExpenseCalculator::calculate_total_expenses(&expenses);
109 assert_eq!(total, 300.0);
110 }
111
112 #[test]
113 fn test_calculate_paid_and_unpaid() {
114 let org_id = Uuid::new_v4();
115 let building_id = Uuid::new_v4();
116 let syndic_id = Uuid::new_v4();
117
118 let mut expense1 = Expense::new(
119 org_id,
120 building_id,
121 ExpenseCategory::Maintenance,
122 "Test 1".to_string(),
123 100.0,
124 Utc::now(),
125 None,
126 None,
127 None, )
129 .unwrap();
130 expense1.submit_for_approval().unwrap();
132 expense1.approve(syndic_id).unwrap();
133 let _ = expense1.mark_as_paid();
134
135 let expense2 = Expense::new(
136 org_id,
137 building_id,
138 ExpenseCategory::Repairs,
139 "Test 2".to_string(),
140 200.0,
141 Utc::now(),
142 None,
143 None,
144 None, )
146 .unwrap();
147
148 let expenses = vec![expense1, expense2];
149
150 let paid = ExpenseCalculator::calculate_paid_expenses(&expenses);
151 let unpaid = ExpenseCalculator::calculate_unpaid_expenses(&expenses);
152
153 assert_eq!(paid, 100.0);
154 assert_eq!(unpaid, 200.0);
155 }
156}