grid_tariffs/
tax_reductions.rs

1use chrono::{Local, NaiveDate};
2use serde::Serialize;
3
4use crate::{Money, minivec::MiniVec};
5
6#[derive(Debug, Clone, Copy, Serialize)]
7#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
8pub enum TaxReductionAppliedBy {
9    KwhFeedIn,
10}
11
12// NOTE: Just increase N when we need it
13type TaxReductionsInner = MiniVec<&'static TaxReduction, 2>;
14
15pub(crate) struct TaxReductionsBuilder {
16    t: TaxReductionsInner,
17}
18
19impl TaxReductionsBuilder {
20    pub(crate) const fn new() -> Self {
21        Self {
22            t: TaxReductionsInner::new(),
23        }
24    }
25
26    pub(crate) const fn add(mut self, t: &'static TaxReduction) -> Self {
27        self.t.push(t);
28        self
29    }
30
31    pub(crate) const fn build(self) -> TaxReductions {
32        TaxReductions(self.t)
33    }
34}
35
36#[derive(Debug, Clone, Copy, Serialize)]
37#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
38pub struct TaxReductions(TaxReductionsInner);
39
40impl TaxReductions {
41    /// Get tax reductions that are current for the given date
42    pub fn for_date(&self, today: NaiveDate) -> Vec<TaxReduction> {
43        self.0
44            .iter()
45            .filter(|tr| tr.valid_for(today))
46            .copied()
47            .collect()
48    }
49
50    /// Tax reduction total for the given date
51    pub fn kwh_total(&self, today: NaiveDate) -> Money {
52        self.for_date(today)
53            .into_iter()
54            .filter(|tr| tr.is_kwh_based())
55            .map(|tr| tr.amount())
56            .sum()
57    }
58
59    pub(crate) fn with_current_only(mut self) -> TaxReductions {
60        let today = Local::now().date_naive();
61        self.0 = self.0.iter().filter(|tr| tr.valid_for(today)).collect();
62        self
63    }
64}
65
66#[derive(Debug, Clone, Copy, Serialize)]
67#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
68pub struct TaxReduction {
69    description: &'static str,
70    amount: Money,
71    applied_by: TaxReductionAppliedBy,
72    from_date: NaiveDate,
73    /// Last date when this tax reduction applies
74    to_date: Option<NaiveDate>,
75}
76
77impl TaxReduction {
78    pub(crate) const fn new(
79        description: &'static str,
80        amount: Money,
81        applied_by: TaxReductionAppliedBy,
82        from_date: NaiveDate,
83        to_date: Option<NaiveDate>,
84    ) -> Self {
85        Self {
86            description,
87            amount,
88            applied_by,
89            from_date,
90            to_date,
91        }
92    }
93
94    pub fn amount(&self) -> Money {
95        self.amount
96    }
97
98    pub(crate) fn valid_for(&self, today: NaiveDate) -> bool {
99        today >= self.from_date && self.to_date.is_none_or(|to_date| today <= to_date)
100    }
101
102    pub(crate) fn is_kwh_based(&self) -> bool {
103        matches!(self.applied_by, TaxReductionAppliedBy::KwhFeedIn)
104    }
105}