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    /// Use when the operator states that they use spot price variable pricing, but don't specify the actual multipliers
80    pub(crate) const fn spot_price_variable_placeholder() -> TransferFee {
81        Self::spot_price_variable(2.0, 0.06, true)
82    }
83}
84
85/// Like TransferFee, but with costs being simple Money objects
86#[derive(Debug, Clone, Serialize)]
87#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
88pub enum TransferFeeSimplified {
89    /// Price was not listed on their website
90    Unlisted,
91    /// Transfer fee has not been verified by us
92    Unverified,
93    /// Fee does not change except possibly by fuse size
94    Simple(Money),
95    /// Transfer fee that varies by the current spot price
96    /// We have currently only observed that Växjö Energi uses this variant
97    SpotPriceVariable {
98        base_cost: Money,
99        spot_price_multiplier: f64,
100        approximated: bool,
101        info: String,
102    },
103    Periods(CostPeriodsSimple),
104}
105
106impl TransferFeeSimplified {
107    fn new(fee: &TransferFee, fuse_size: u16, yearly_consumption: u32, language: Language) -> Self {
108        match *fee {
109            TransferFee::Unlisted => TransferFeeSimplified::Unlisted,
110            TransferFee::Unverified => TransferFeeSimplified::Unverified,
111            TransferFee::Simple(cost) => TransferFeeSimplified::Simple(
112                cost.cost_for(fuse_size, yearly_consumption)
113                    .unwrap_or(Money::ZERO),
114            ),
115            TransferFee::SpotPriceVariable {
116                base_cost,
117                spot_price_multiplier,
118                approximated,
119            } => TransferFeeSimplified::SpotPriceVariable {
120                base_cost,
121                spot_price_multiplier,
122                approximated,
123                info: Default::default(),
124            },
125            TransferFee::Periods(periods) => TransferFeeSimplified::Periods(
126                CostPeriodsSimple::new(periods, fuse_size, yearly_consumption, language),
127            ),
128        }
129        .add_info(language)
130    }
131
132    fn add_info(mut self, language: Language) -> Self {
133        match self {
134            TransferFeeSimplified::Unlisted => self,
135            TransferFeeSimplified::Unverified => self,
136            TransferFeeSimplified::Simple(_) => self,
137            TransferFeeSimplified::SpotPriceVariable {
138                base_cost,
139                spot_price_multiplier,
140                approximated,
141                info,
142            } => {
143                let percentage = spot_price_multiplier * 100.;
144                let mut info = match language {
145                    Language::En => format!(
146                        "The grid operator bases its transfer fee on a fixed part of {} and {}% of the current spot price.",
147                        base_cost.display(Currency::SEK),
148                        percentage
149                    ),
150                    Language::Sv => format!(
151                        "Nätbolaget baserar sin överföringsavgift på en fast del om {} samt {}% av spotpriset.",
152                        base_cost.display(Currency::SEK),
153                        percentage
154                    ),
155                };
156                if approximated {
157                    info.push_str(match language {
158                        Language::En => " The base fee and percentage are estimated, as the grid operator doesn't list them on their website.",
159                        Language::Sv => " Basavgift och procentsats är uppskattade, eftersom nätbolaget inte skriver ut exakt vad de är på sin webbplats.",
160                    })
161                }
162                TransferFeeSimplified::SpotPriceVariable {
163                    base_cost,
164                    spot_price_multiplier,
165                    approximated,
166                    info,
167                }
168            }
169            TransferFeeSimplified::Periods(_) => self,
170        }
171    }
172}