Skip to main content

bo4e_core/bo/
tariff_costs.rs

1//! Tariff costs (Tarifkosten) business object.
2
3use serde::{Deserialize, Serialize};
4
5use crate::com::{Amount, CostBlock, Price, TimePeriod};
6use crate::enums::Division;
7use crate::traits::{Bo4eMeta, Bo4eObject};
8
9/// Tariff-related costs.
10///
11/// Represents the costs associated with a specific tariff.
12///
13/// German: Tarifkosten
14///
15/// # Example
16///
17/// ```rust
18/// use bo4e_core::bo::TariffCosts;
19/// use bo4e_core::com::{Amount, Price};
20/// use bo4e_core::enums::Division;
21///
22/// let tariff_costs = TariffCosts {
23///     designation: Some("Haushaltstarif Kosten".to_string()),
24///     division: Some(Division::Electricity),
25///     base_price_cost: Some(Amount::eur(119.40)),
26///     working_price_cost: Some(Amount::eur(960.00)),
27///     ..Default::default()
28/// };
29/// ```
30#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
31#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
32#[cfg_attr(feature = "json-schema", schemars(rename = "Tarifkosten"))]
33#[serde(rename_all = "camelCase")]
34pub struct TariffCosts {
35    /// BO4E metadata
36    #[serde(flatten)]
37    pub meta: Bo4eMeta,
38
39    /// Name/designation of the tariff costs (Bezeichnung)
40    #[serde(skip_serializing_if = "Option::is_none")]
41    #[cfg_attr(feature = "json-schema", schemars(rename = "bezeichnung"))]
42    pub designation: Option<String>,
43
44    /// Description (Beschreibung)
45    #[serde(skip_serializing_if = "Option::is_none")]
46    #[cfg_attr(feature = "json-schema", schemars(rename = "beschreibung"))]
47    pub description: Option<String>,
48
49    /// Energy division (Sparte)
50    #[serde(skip_serializing_if = "Option::is_none")]
51    #[cfg_attr(feature = "json-schema", schemars(rename = "sparte"))]
52    pub division: Option<Division>,
53
54    /// Period the costs apply to (Abrechnungszeitraum)
55    #[serde(skip_serializing_if = "Option::is_none")]
56    #[cfg_attr(feature = "json-schema", schemars(rename = "abrechnungszeitraum"))]
57    pub period: Option<TimePeriod>,
58
59    /// Total amount (Gesamtbetrag)
60    #[serde(skip_serializing_if = "Option::is_none")]
61    #[cfg_attr(feature = "json-schema", schemars(rename = "gesamtbetrag"))]
62    pub total_amount: Option<Amount>,
63
64    /// Base price applied (Grundpreis)
65    #[serde(skip_serializing_if = "Option::is_none")]
66    #[cfg_attr(feature = "json-schema", schemars(rename = "grundpreis"))]
67    pub base_price: Option<Price>,
68
69    /// Base price cost (Grundpreiskosten)
70    #[serde(skip_serializing_if = "Option::is_none")]
71    #[cfg_attr(feature = "json-schema", schemars(rename = "grundpreiskosten"))]
72    pub base_price_cost: Option<Amount>,
73
74    /// Working price applied (Arbeitspreis)
75    #[serde(skip_serializing_if = "Option::is_none")]
76    #[cfg_attr(feature = "json-schema", schemars(rename = "arbeitspreis"))]
77    pub working_price: Option<Price>,
78
79    /// Working price cost (Arbeitspreiskosten)
80    #[serde(skip_serializing_if = "Option::is_none")]
81    #[cfg_attr(feature = "json-schema", schemars(rename = "arbeitspreiskosten"))]
82    pub working_price_cost: Option<Amount>,
83
84    /// Consumption quantity (Verbrauchsmenge)
85    #[serde(skip_serializing_if = "Option::is_none")]
86    #[cfg_attr(feature = "json-schema", schemars(rename = "verbrauchsmenge"))]
87    pub consumption: Option<f64>,
88
89    /// Cost blocks (Kostenbloecke)
90    #[serde(default, skip_serializing_if = "Vec::is_empty")]
91    #[cfg_attr(feature = "json-schema", schemars(rename = "kostenbloecke"))]
92    pub cost_blocks: Vec<CostBlock>,
93
94    /// Reference to the tariff
95    #[serde(skip_serializing_if = "Option::is_none")]
96    #[cfg_attr(feature = "json-schema", schemars(rename = "tarif"))]
97    pub tariff: Option<Box<super::Tariff>>,
98}
99
100impl Bo4eObject for TariffCosts {
101    fn type_name_german() -> &'static str {
102        "Tarifkosten"
103    }
104
105    fn type_name_english() -> &'static str {
106        "TariffCosts"
107    }
108
109    fn meta(&self) -> &Bo4eMeta {
110        &self.meta
111    }
112
113    fn meta_mut(&mut self) -> &mut Bo4eMeta {
114        &mut self.meta
115    }
116}
117
118#[cfg(test)]
119mod tests {
120    use super::*;
121
122    #[test]
123    fn test_tariff_costs_creation() {
124        let tariff_costs = TariffCosts {
125            designation: Some("Haushaltstarif Kosten".to_string()),
126            division: Some(Division::Electricity),
127            base_price: Some(Price::eur_per_month(9.95)),
128            base_price_cost: Some(Amount::eur(119.40)),
129            working_price: Some(Price::eur_per_kwh(0.32)),
130            working_price_cost: Some(Amount::eur(960.00)),
131            consumption: Some(3000.0),
132            total_amount: Some(Amount::eur(1079.40)),
133            ..Default::default()
134        };
135
136        assert_eq!(tariff_costs.consumption, Some(3000.0));
137    }
138
139    #[test]
140    fn test_serialize() {
141        let tariff_costs = TariffCosts {
142            meta: Bo4eMeta::with_type("Tarifkosten"),
143            designation: Some("Test Tariff Costs".to_string()),
144            total_amount: Some(Amount::eur(500.0)),
145            ..Default::default()
146        };
147
148        let json = serde_json::to_string(&tariff_costs).unwrap();
149        assert!(json.contains(r#""designation":"Test Tariff Costs""#));
150        assert!(json.contains(r#""_typ":"Tarifkosten""#));
151    }
152
153    #[test]
154    fn test_roundtrip() {
155        let tariff_costs = TariffCosts {
156            meta: Bo4eMeta::with_type("Tarifkosten"),
157            designation: Some("Gas Tariff Costs".to_string()),
158            description: Some("Annual gas tariff costs".to_string()),
159            division: Some(Division::Gas),
160            consumption: Some(15000.0),
161            total_amount: Some(Amount::eur(1500.0)),
162            ..Default::default()
163        };
164
165        let json = serde_json::to_string(&tariff_costs).unwrap();
166        let parsed: TariffCosts = serde_json::from_str(&json).unwrap();
167        assert_eq!(tariff_costs, parsed);
168    }
169
170    #[test]
171    fn test_bo4e_object_impl() {
172        assert_eq!(TariffCosts::type_name_german(), "Tarifkosten");
173        assert_eq!(TariffCosts::type_name_english(), "TariffCosts");
174    }
175}