1use serde::{Deserialize, Serialize};
4
5use crate::com::{EnergyMix, Price, PriceTier, TariffCalculationParameter, TimePeriod};
6use crate::enums::{CustomerType, Division};
7use crate::traits::{Bo4eMeta, Bo4eObject};
8
9#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
30#[serde(rename_all = "camelCase")]
31pub struct Tariff {
32 #[serde(flatten)]
34 pub meta: Bo4eMeta,
35
36 #[serde(skip_serializing_if = "Option::is_none")]
38 pub tariff_name: Option<String>,
39
40 #[serde(skip_serializing_if = "Option::is_none")]
42 pub description: Option<String>,
43
44 #[serde(skip_serializing_if = "Option::is_none")]
46 pub division: Option<Division>,
47
48 #[serde(skip_serializing_if = "Option::is_none")]
50 pub customer_type: Option<CustomerType>,
51
52 #[serde(skip_serializing_if = "Option::is_none")]
54 pub validity_period: Option<TimePeriod>,
55
56 #[serde(skip_serializing_if = "Option::is_none")]
58 pub base_price: Option<Price>,
59
60 #[serde(skip_serializing_if = "Option::is_none")]
62 pub working_price: Option<Price>,
63
64 #[serde(default, skip_serializing_if = "Vec::is_empty")]
66 pub price_tiers: Vec<PriceTier>,
67
68 #[serde(skip_serializing_if = "Option::is_none")]
70 pub calculation_parameters: Option<TariffCalculationParameter>,
71
72 #[serde(skip_serializing_if = "Option::is_none")]
74 pub energy_mix: Option<EnergyMix>,
75
76 #[serde(skip_serializing_if = "Option::is_none")]
78 pub supplier: Option<Box<super::BusinessPartner>>,
79}
80
81impl Bo4eObject for Tariff {
82 fn type_name_german() -> &'static str {
83 "Tarif"
84 }
85
86 fn type_name_english() -> &'static str {
87 "Tariff"
88 }
89
90 fn meta(&self) -> &Bo4eMeta {
91 &self.meta
92 }
93
94 fn meta_mut(&mut self) -> &mut Bo4eMeta {
95 &mut self.meta
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102
103 #[test]
104 fn test_household_tariff() {
105 let tariff = Tariff {
106 tariff_name: Some("Haushaltstarif 2024".to_string()),
107 division: Some(Division::Electricity),
108 customer_type: Some(CustomerType::Private),
109 base_price: Some(Price::eur_per_month(9.95)),
110 working_price: Some(Price::eur_per_kwh(0.32)),
111 ..Default::default()
112 };
113
114 assert_eq!(tariff.customer_type, Some(CustomerType::Private));
115 assert_eq!(tariff.division, Some(Division::Electricity));
116 }
117
118 #[test]
119 fn test_tiered_tariff() {
120 let tariff = Tariff {
121 tariff_name: Some("Staffeltarif".to_string()),
122 price_tiers: vec![
123 PriceTier {
124 lower_limit: Some(0.0),
125 upper_limit: Some(1000.0),
126 unit_price: Some(0.35),
127 tier_number: Some(1),
128 ..Default::default()
129 },
130 PriceTier {
131 lower_limit: Some(1000.0),
132 upper_limit: Some(5000.0),
133 unit_price: Some(0.30),
134 tier_number: Some(2),
135 ..Default::default()
136 },
137 ],
138 ..Default::default()
139 };
140
141 assert_eq!(tariff.price_tiers.len(), 2);
142 }
143
144 #[test]
145 fn test_serialize() {
146 let tariff = Tariff {
147 meta: Bo4eMeta::with_type("Tarif"),
148 tariff_name: Some("Basic Tariff".to_string()),
149 division: Some(Division::Electricity),
150 ..Default::default()
151 };
152
153 let json = serde_json::to_string(&tariff).unwrap();
154 assert!(json.contains(r#""tariffName":"Basic Tariff""#));
155 assert!(json.contains(r#""_typ":"Tarif""#));
156 }
157
158 #[test]
159 fn test_roundtrip() {
160 let tariff = Tariff {
161 meta: Bo4eMeta::with_type("Tarif"),
162 tariff_name: Some("Test Tariff".to_string()),
163 description: Some("A test tariff".to_string()),
164 division: Some(Division::Gas),
165 customer_type: Some(CustomerType::Commercial),
166 base_price: Some(Price::eur_per_month(15.0)),
167 ..Default::default()
168 };
169
170 let json = serde_json::to_string(&tariff).unwrap();
171 let parsed: Tariff = serde_json::from_str(&json).unwrap();
172 assert_eq!(tariff, parsed);
173 }
174
175 #[test]
176 fn test_bo4e_object_impl() {
177 assert_eq!(Tariff::type_name_german(), "Tarif");
178 assert_eq!(Tariff::type_name_english(), "Tariff");
179 }
180}