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#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
31#[cfg_attr(feature = "json-schema", schemars(rename = "Tarif"))]
32#[serde(rename_all = "camelCase")]
33pub struct Tariff {
34 #[serde(flatten)]
36 pub meta: Bo4eMeta,
37
38 #[serde(skip_serializing_if = "Option::is_none")]
40 #[cfg_attr(feature = "json-schema", schemars(rename = "tarifname"))]
41 pub tariff_name: Option<String>,
42
43 #[serde(skip_serializing_if = "Option::is_none")]
45 #[cfg_attr(feature = "json-schema", schemars(rename = "tarifbeschreibung"))]
46 pub description: Option<String>,
47
48 #[serde(skip_serializing_if = "Option::is_none")]
50 #[cfg_attr(feature = "json-schema", schemars(rename = "sparte"))]
51 pub division: Option<Division>,
52
53 #[serde(skip_serializing_if = "Option::is_none")]
55 #[cfg_attr(feature = "json-schema", schemars(rename = "kundentyp"))]
56 pub customer_type: Option<CustomerType>,
57
58 #[serde(skip_serializing_if = "Option::is_none")]
60 #[cfg_attr(feature = "json-schema", schemars(rename = "gueltigkeitszeitraum"))]
61 pub validity_period: Option<TimePeriod>,
62
63 #[serde(skip_serializing_if = "Option::is_none")]
65 #[cfg_attr(feature = "json-schema", schemars(rename = "grundpreis"))]
66 pub base_price: Option<Price>,
67
68 #[serde(skip_serializing_if = "Option::is_none")]
70 #[cfg_attr(feature = "json-schema", schemars(rename = "arbeitspreis"))]
71 pub working_price: Option<Price>,
72
73 #[serde(default, skip_serializing_if = "Vec::is_empty")]
75 #[cfg_attr(feature = "json-schema", schemars(rename = "preisstaffeln"))]
76 pub price_tiers: Vec<PriceTier>,
77
78 #[serde(skip_serializing_if = "Option::is_none")]
80 #[cfg_attr(
81 feature = "json-schema",
82 schemars(rename = "tarifberechnungsparameter")
83 )]
84 pub calculation_parameters: Option<TariffCalculationParameter>,
85
86 #[serde(skip_serializing_if = "Option::is_none")]
88 #[cfg_attr(feature = "json-schema", schemars(rename = "energiemix"))]
89 pub energy_mix: Option<EnergyMix>,
90
91 #[serde(skip_serializing_if = "Option::is_none")]
93 #[cfg_attr(feature = "json-schema", schemars(rename = "anbieter"))]
94 pub supplier: Option<Box<super::BusinessPartner>>,
95}
96
97impl Bo4eObject for Tariff {
98 fn type_name_german() -> &'static str {
99 "Tarif"
100 }
101
102 fn type_name_english() -> &'static str {
103 "Tariff"
104 }
105
106 fn meta(&self) -> &Bo4eMeta {
107 &self.meta
108 }
109
110 fn meta_mut(&mut self) -> &mut Bo4eMeta {
111 &mut self.meta
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118
119 #[test]
120 fn test_household_tariff() {
121 let tariff = Tariff {
122 tariff_name: Some("Haushaltstarif 2024".to_string()),
123 division: Some(Division::Electricity),
124 customer_type: Some(CustomerType::Private),
125 base_price: Some(Price::eur_per_month(9.95)),
126 working_price: Some(Price::eur_per_kwh(0.32)),
127 ..Default::default()
128 };
129
130 assert_eq!(tariff.customer_type, Some(CustomerType::Private));
131 assert_eq!(tariff.division, Some(Division::Electricity));
132 }
133
134 #[test]
135 fn test_tiered_tariff() {
136 let tariff = Tariff {
137 tariff_name: Some("Staffeltarif".to_string()),
138 price_tiers: vec![
139 PriceTier {
140 lower_limit: Some(0.0),
141 upper_limit: Some(1000.0),
142 unit_price: Some(0.35),
143 tier_number: Some(1),
144 ..Default::default()
145 },
146 PriceTier {
147 lower_limit: Some(1000.0),
148 upper_limit: Some(5000.0),
149 unit_price: Some(0.30),
150 tier_number: Some(2),
151 ..Default::default()
152 },
153 ],
154 ..Default::default()
155 };
156
157 assert_eq!(tariff.price_tiers.len(), 2);
158 }
159
160 #[test]
161 fn test_serialize() {
162 let tariff = Tariff {
163 meta: Bo4eMeta::with_type("Tarif"),
164 tariff_name: Some("Basic Tariff".to_string()),
165 division: Some(Division::Electricity),
166 ..Default::default()
167 };
168
169 let json = serde_json::to_string(&tariff).unwrap();
170 assert!(json.contains(r#""tariffName":"Basic Tariff""#));
171 assert!(json.contains(r#""_typ":"Tarif""#));
172 }
173
174 #[test]
175 fn test_roundtrip() {
176 let tariff = Tariff {
177 meta: Bo4eMeta::with_type("Tarif"),
178 tariff_name: Some("Test Tariff".to_string()),
179 description: Some("A test tariff".to_string()),
180 division: Some(Division::Gas),
181 customer_type: Some(CustomerType::Commercial),
182 base_price: Some(Price::eur_per_month(15.0)),
183 ..Default::default()
184 };
185
186 let json = serde_json::to_string(&tariff).unwrap();
187 let parsed: Tariff = serde_json::from_str(&json).unwrap();
188 assert_eq!(tariff, parsed);
189 }
190
191 #[test]
192 fn test_bo4e_object_impl() {
193 assert_eq!(Tariff::type_name_german(), "Tarif");
194 assert_eq!(Tariff::type_name_english(), "Tariff");
195 }
196}