grid_tariffs/
transfer_fee.rs

1use serde::Serialize;
2
3use crate::{Cost, CostPeriods, CostPeriodsSimple, Language, Money, currency::Currency};
4
5#[derive(Debug, Clone, Copy, Serialize)]
6#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
7pub enum TransferFee {
8    /// Price was not listed on their website
9    Unlisted,
10    /// Transfer fee has not been verified by us
11    Unverified,
12    /// Fee does not change except possibly by fuse size
13    Simple(Cost),
14    /// Transfer fee that varies by the current spot price
15    /// We have currently only observed that Växjö Energi uses this variant
16    SpotPriceVariable {
17        base_cost: Money,
18        spot_price_multiplier: f64,
19        approximated: bool,
20    },
21    Periods(CostPeriods),
22}
23
24impl TransferFee {
25    pub const fn is_unverified(&self) -> bool {
26        matches!(self, Self::Unverified)
27    }
28
29    pub const fn simple_cost(&self) -> Option<Cost> {
30        match self {
31            Self::Simple(cost) => Some(*cost),
32            _ => None,
33        }
34    }
35
36    pub const fn spot_price_variable(
37        base_cost_subunit: f64,
38        spot_price_multiplier: f64,
39        approximated: bool,
40    ) -> Self {
41        Self::SpotPriceVariable {
42            base_cost: Money::new_subunit(base_cost_subunit),
43            spot_price_multiplier,
44            approximated,
45        }
46    }
47
48    pub fn simplified(
49        &self,
50        fuse_size: u16,
51        yearly_consumption: u32,
52        language: Language,
53    ) -> TransferFeeSimplified {
54        TransferFeeSimplified::new(self, fuse_size, yearly_consumption, language)
55    }
56
57    pub(super) const fn new_periods(periods: CostPeriods) -> Self {
58        Self::Periods(periods)
59    }
60
61    pub(super) const fn fixed(int: i64, fract: u8) -> Self {
62        Self::Simple(Cost::fixed(int, fract))
63    }
64
65    pub(super) const fn fixed_subunit(subunit: f64) -> Self {
66        Self::Simple(Cost::fixed_subunit(subunit))
67    }
68
69    pub(super) fn is_yearly_consumption_based(&self, fuse_size: u16) -> bool {
70        match self {
71            TransferFee::Unlisted
72            | TransferFee::Unverified
73            | TransferFee::SpotPriceVariable { .. } => false,
74            TransferFee::Simple(cost) => cost.is_yearly_consumption_based(fuse_size),
75            TransferFee::Periods(periods) => periods.is_yearly_consumption_based(fuse_size),
76        }
77    }
78}
79
80/// Like TransferFee, but with costs being simple Money objects
81#[derive(Debug, Clone, Serialize)]
82#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
83pub enum TransferFeeSimplified {
84    /// Price was not listed on their website
85    Unlisted,
86    /// Transfer fee has not been verified by us
87    Unverified,
88    /// Fee does not change except possibly by fuse size
89    Simple(Option<Money>),
90    /// Transfer fee that varies by the current spot price
91    /// We have currently only observed that Växjö Energi uses this variant
92    SpotPriceVariable {
93        base_cost: Money,
94        spot_price_multiplier: f64,
95        approximated: bool,
96        info: String,
97    },
98    Periods(CostPeriodsSimple),
99}
100
101impl TransferFeeSimplified {
102    fn new(fee: &TransferFee, fuse_size: u16, yearly_consumption: u32, language: Language) -> Self {
103        match *fee {
104            TransferFee::Unlisted => TransferFeeSimplified::Unlisted,
105            TransferFee::Unverified => TransferFeeSimplified::Unverified,
106            TransferFee::Simple(cost) => {
107                TransferFeeSimplified::Simple(cost.cost_for(fuse_size, yearly_consumption))
108            }
109            TransferFee::SpotPriceVariable {
110                base_cost,
111                spot_price_multiplier,
112                approximated,
113            } => TransferFeeSimplified::SpotPriceVariable {
114                base_cost,
115                spot_price_multiplier,
116                approximated,
117                info: Default::default(),
118            },
119            TransferFee::Periods(periods) => TransferFeeSimplified::Periods(
120                CostPeriodsSimple::new(periods, fuse_size, yearly_consumption, language),
121            ),
122        }
123        .add_info(language)
124    }
125
126    fn add_info(mut self, language: Language) -> Self {
127        match self {
128            TransferFeeSimplified::Unlisted => self,
129            TransferFeeSimplified::Unverified => self,
130            TransferFeeSimplified::Simple(_) => self,
131            TransferFeeSimplified::SpotPriceVariable {
132                base_cost,
133                spot_price_multiplier,
134                approximated,
135                info,
136            } => {
137                let percentage = spot_price_multiplier * 100.;
138                let mut info = match language {
139                    Language::En => format!(
140                        "The grid operator bases its transfer fee on a fixed part of {} and {}% of the current spot price.",
141                        base_cost.display(Currency::SEK),
142                        percentage
143                    ),
144                    Language::Sv => format!(
145                        "Nätbolaget baserar sin överföringsavgift på en fast del om {} samt {}% av spotpriset.",
146                        base_cost.display(Currency::SEK),
147                        percentage
148                    ),
149                };
150                if approximated {
151                    info.push_str(&match language {
152                        Language::En => format!(
153                            " The percentage is estimated as the grid operator doesn't list it on their website."
154                        ),
155                        Language::Sv => format!(
156                            " Procentsatsen är uppskattad eftersom nätbolaget inte skriver ut exakt vad den är på sin webbplats."
157                        ),
158                    })
159                }
160                TransferFeeSimplified::SpotPriceVariable {
161                    base_cost,
162                    spot_price_multiplier,
163                    approximated,
164                    info,
165                }
166            }
167            TransferFeeSimplified::Periods(_) => self,
168        }
169    }
170}