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