1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
use std::collections::{HashMap, HashSet};
use std::default::Default;
use chrono::Datelike;
use lazy_static::lazy_static;
use crate::core::EmptyResult;
use crate::currency;
use crate::formatting::format_date;
use crate::localities::Country;
use crate::types::{Date, Decimal};
use crate::util;
#[derive(Debug, Clone, Copy)]
pub enum TaxPaymentDay {
Day {month: u32, day: u32},
OnClose,
}
impl Default for TaxPaymentDay {
fn default() -> TaxPaymentDay {
TaxPaymentDay::Day {
month: 3,
day: 15,
}
}
}
impl TaxPaymentDay {
pub fn get(&self, income_date: Date) -> Date {
lazy_static! {
static ref ACCOUNT_CLOSE_DATE: Date = Date::from_ymd(util::today().year() + 10, 1, 1);
}
match *self {
TaxPaymentDay::Day {month, day} => Date::from_ymd(income_date.year() + 1, month, day),
TaxPaymentDay::OnClose => *ACCOUNT_CLOSE_DATE,
}
}
}
pub struct TaxRemapping {
remapping: HashMap<(Date, String), (Date, bool)>
}
impl TaxRemapping {
pub fn new() -> TaxRemapping {
TaxRemapping {
remapping: HashMap::new(),
}
}
pub fn add(&mut self, date: Date, description: &str, to_date: Date) -> EmptyResult {
if self.remapping.insert((date, description.to_owned()), (to_date, false)).is_some() {
return Err!(
"Invalid tax remapping configuration: Duplicated match: {} - {:?}",
format_date(date), description);
}
Ok(())
}
pub fn map(&mut self, date: Date, description: &str) -> Date {
if let Some((to_date, mapped)) = self.remapping.get_mut(&(date, description.to_owned())) {
*mapped = true;
*to_date
} else {
date
}
}
pub fn ensure_all_mapped(&self) -> EmptyResult {
for ((date, description), (_, mapped)) in self.remapping.iter() {
if !mapped {
return Err!(
"The following tax remapping rule hasn't been mapped to any tax: {} - {:?}",
format_date(*date), description)
}
}
Ok(())
}
}
pub struct NetTaxCalculator {
country: Country,
tax_payment_day: TaxPaymentDay,
profit: HashMap<Date, Decimal>,
}
impl NetTaxCalculator {
pub fn new(country: Country, tax_payment_day: TaxPaymentDay) -> NetTaxCalculator {
NetTaxCalculator {
country,
tax_payment_day,
profit: HashMap::new(),
}
}
pub fn add_profit(&mut self, date: Date, amount: Decimal) {
let amount = currency::round(amount);
self.profit.entry(self.tax_payment_day.get(date))
.and_modify(|profit| *profit += amount)
.or_insert(amount);
}
pub fn get_taxes(&self) -> HashMap<Date, Decimal> {
let mut taxes = HashMap::new();
let mut years = HashSet::new();
for (&tax_payment_date, &profit) in self.profit.iter() {
let year = tax_payment_date.year();
assert!(years.insert(year));
let tax_to_pay = self.country.tax_to_pay(profit, None);
assert_eq!(taxes.insert(tax_payment_date, tax_to_pay), None);
}
taxes
}
}