1use chrono::{DateTime, TimeZone};
2use serde::Serialize;
3
4use crate::{Cost, CostPeriods, CostPeriodsSimple, Country, Language, Money, currency::Currency};
5
6#[derive(Debug, Clone, Copy, Serialize)]
9#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
10pub enum FeedInRevenue {
11 Simple(Cost),
12 Unverified,
14 Unlisted,
16 SpotPriceVariable {
18 base_cost: Money,
19 spot_price_multiplier: f64,
20 approximated: bool,
22 },
23 Periods(CostPeriods),
24}
25
26impl FeedInRevenue {
27 pub const fn is_unverified(&self) -> bool {
28 matches!(self, Self::Unverified)
29 }
30
31 pub(crate) const fn new_periods(periods: CostPeriods) -> Self {
32 Self::Periods(periods)
33 }
34
35 pub(crate) const fn fixed_subunit(subunit: f64) -> Self {
36 Self::Simple(Cost::fixed_subunit(subunit))
37 }
38
39 pub(crate) const fn fixed_subunit_plus_vat(subunit: f64, country: Country) -> Self {
40 Self::Simple(Cost::fixed_subunit(subunit).add_vat(country))
41 }
42
43 pub const fn spot_price_variable(
44 base_cost_subunit: f64,
45 spot_price_multiplier: f64,
46 approximated: bool,
47 ) -> Self {
48 if spot_price_multiplier < 0.0 || spot_price_multiplier > 1.0 {
49 panic!("`spot_price_multiplier` too low/high");
50 }
51 Self::SpotPriceVariable {
52 base_cost: Money::new_subunit(base_cost_subunit),
53 spot_price_multiplier,
54 approximated,
55 }
56 }
57
58 pub fn simplified(
59 &self,
60 fuse_size: u16,
61 yearly_consumption: u32,
62 language: Language,
63 ) -> FeedInRevenueSimplified {
64 FeedInRevenueSimplified::new(self, fuse_size, yearly_consumption, language)
65 }
66
67 pub fn kwh_revenue<Tz: TimeZone>(
68 &self,
69 timestamp: DateTime<Tz>,
70 spotprice: Money,
71 fuse_size: u16,
72 yearly_consumption: u32,
73 ) -> Money
74 where
75 DateTime<Tz>: Copy,
76 {
77 match *self {
78 FeedInRevenue::Unverified | FeedInRevenue::Unlisted => Money::ZERO,
79 FeedInRevenue::Simple(cost) => cost
80 .cost_for(fuse_size, yearly_consumption)
81 .unwrap_or_default(),
82 FeedInRevenue::SpotPriceVariable {
83 base_cost,
84 spot_price_multiplier,
85 approximated: _,
86 } => base_cost + (spotprice * spot_price_multiplier),
87 FeedInRevenue::Periods(cost_periods) => cost_periods
88 .matching_periods(timestamp)
89 .into_iter()
90 .flat_map(|period| period.cost().cost_for(fuse_size, yearly_consumption))
91 .sum(),
92 }
93 }
94}
95
96#[derive(Debug, Clone, Serialize)]
99#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
100pub enum FeedInRevenueSimplified {
101 Simple(Option<Money>),
102 Unverified,
104 Unlisted,
106 SpotPriceVariable {
108 base_cost: Money,
109 spot_price_multiplier: f64,
110 approximated: bool,
112 info: String,
113 },
114 Periods(CostPeriodsSimple),
115}
116
117impl FeedInRevenueSimplified {
118 fn new(
119 revenue: &FeedInRevenue,
120 fuse_size: u16,
121 yearly_consumption: u32,
122 language: Language,
123 ) -> Self {
124 match *revenue {
125 FeedInRevenue::Unlisted => Self::Unlisted,
126 FeedInRevenue::Unverified => Self::Unverified,
127 FeedInRevenue::Simple(cost) => {
128 Self::Simple(cost.cost_for(fuse_size, yearly_consumption))
129 }
130 FeedInRevenue::SpotPriceVariable {
131 base_cost,
132 spot_price_multiplier,
133 approximated,
134 } => Self::SpotPriceVariable {
135 base_cost,
136 spot_price_multiplier,
137 approximated,
138 info: Default::default(),
139 },
140 FeedInRevenue::Periods(periods) => Self::Periods(CostPeriodsSimple::new(
141 periods,
142 fuse_size,
143 yearly_consumption,
144 language,
145 )),
146 }
147 .add_info(language)
148 }
149
150 fn add_info(self, language: Language) -> Self {
151 match self {
152 FeedInRevenueSimplified::SpotPriceVariable {
153 base_cost,
154 spot_price_multiplier,
155 approximated,
156 info: _,
157 } => {
158 let percentage = spot_price_multiplier * 100.;
159 let mut info = match language {
160 Language::En => format!(
161 "The grid operator bases its feed-in revenue on a fixed part of {} and {:.2}% of the current spot price.",
162 base_cost.display(Currency::SEK).subunit_display(),
163 percentage
164 ),
165 Language::Sv => format!(
166 "Nätbolaget baserar sin nätnytta på en fast del om {} samt {:.2}% av spotpriset.",
167 base_cost.display(Currency::SEK).subunit_display(),
168 percentage
169 ),
170 };
171 if approximated {
172 info.push_str(match language {
173 Language::En => " The base fee and percentage are estimated, as the grid operator doesn't list them on their website.",
174 Language::Sv => " Basavgift och procentsats är uppskattade, eftersom nätbolaget inte skriver ut exakt vad de är på sin webbplats.",
175 })
176 }
177 FeedInRevenueSimplified::SpotPriceVariable {
178 base_cost,
179 spot_price_multiplier,
180 approximated,
181 info,
182 }
183 }
184 _ => self,
185 }
186 }
187}