Skip to main content

bo4e_core/com/
tariff_price_position.rs

1//! Tariff price position (Tarifpreisposition) component.
2
3use serde::{Deserialize, Serialize};
4
5use crate::enums::{CalculationMethod, PriceType, Unit};
6use crate::traits::{Bo4eMeta, Bo4eObject};
7
8use super::TariffPrice;
9
10/// A tariff price position with its associated tariff prices.
11///
12/// German: Tarifpreisposition
13///
14/// # Example
15///
16/// ```rust
17/// use bo4e_core::com::{TariffPricePosition, TariffPrice};
18/// use bo4e_core::enums::PriceType;
19///
20/// let position = TariffPricePosition {
21///     description: Some("Arbeitspreis".to_string()),
22///     price_type: Some(PriceType::WorkingPriceSingleTariff),
23///     ..Default::default()
24/// };
25/// ```
26#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
27#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
28#[cfg_attr(feature = "json-schema", schemars(rename = "Tarifpreisposition"))]
29#[serde(rename_all = "camelCase")]
30pub struct TariffPricePosition {
31    /// BO4E metadata
32    #[serde(flatten)]
33    pub meta: Bo4eMeta,
34
35    /// Description of the position (Bezeichnung)
36    #[serde(skip_serializing_if = "Option::is_none")]
37    #[cfg_attr(feature = "json-schema", schemars(rename = "bezeichnung"))]
38    pub description: Option<String>,
39
40    /// Type of price (Preistyp)
41    #[serde(skip_serializing_if = "Option::is_none")]
42    #[cfg_attr(feature = "json-schema", schemars(rename = "preistyp"))]
43    pub price_type: Option<PriceType>,
44
45    /// Reference unit (Bezugseinheit)
46    #[serde(skip_serializing_if = "Option::is_none")]
47    #[cfg_attr(feature = "json-schema", schemars(rename = "bezugseinheit"))]
48    pub reference_unit: Option<Unit>,
49
50    /// Calculation method (Berechnungsmethode)
51    #[serde(skip_serializing_if = "Option::is_none")]
52    #[cfg_attr(feature = "json-schema", schemars(rename = "berechnungsmethode"))]
53    pub calculation_method: Option<CalculationMethod>,
54
55    /// Tariff prices (Tarifpreise)
56    #[serde(default, skip_serializing_if = "Vec::is_empty")]
57    #[cfg_attr(feature = "json-schema", schemars(rename = "tarifpreise"))]
58    pub prices: Vec<TariffPrice>,
59
60    /// Article ID (Artikel-ID)
61    #[serde(skip_serializing_if = "Option::is_none")]
62    #[cfg_attr(feature = "json-schema", schemars(rename = "artikelId"))]
63    pub article_id: Option<String>,
64}
65
66impl Bo4eObject for TariffPricePosition {
67    fn type_name_german() -> &'static str {
68        "Tarifpreisposition"
69    }
70
71    fn type_name_english() -> &'static str {
72        "TariffPricePosition"
73    }
74
75    fn meta(&self) -> &Bo4eMeta {
76        &self.meta
77    }
78
79    fn meta_mut(&mut self) -> &mut Bo4eMeta {
80        &mut self.meta
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87    use crate::enums::Currency;
88
89    #[test]
90    fn test_tariff_price_position() {
91        let position = TariffPricePosition {
92            description: Some("Arbeitspreis HT/NT".to_string()),
93            price_type: Some(PriceType::WorkingPriceSingleTariff),
94            reference_unit: Some(Unit::KilowattHour),
95            prices: vec![
96                TariffPrice {
97                    description: Some("HT".to_string()),
98                    value: Some(30.5),
99                    currency: Some(Currency::Eur),
100                    ..Default::default()
101                },
102                TariffPrice {
103                    description: Some("NT".to_string()),
104                    value: Some(22.0),
105                    currency: Some(Currency::Eur),
106                    ..Default::default()
107                },
108            ],
109            ..Default::default()
110        };
111
112        assert_eq!(position.prices.len(), 2);
113    }
114
115    #[test]
116    fn test_default() {
117        let position = TariffPricePosition::default();
118        assert!(position.description.is_none());
119        assert!(position.prices.is_empty());
120    }
121
122    #[test]
123    fn test_roundtrip() {
124        let position = TariffPricePosition {
125            description: Some("Grundpreis".to_string()),
126            price_type: Some(PriceType::BasePrice),
127            article_id: Some("GP-001".to_string()),
128            ..Default::default()
129        };
130
131        let json = serde_json::to_string(&position).unwrap();
132        let parsed: TariffPricePosition = serde_json::from_str(&json).unwrap();
133        assert_eq!(position, parsed);
134    }
135
136    #[test]
137    fn test_bo4e_object_impl() {
138        assert_eq!(
139            TariffPricePosition::type_name_german(),
140            "Tarifpreisposition"
141        );
142        assert_eq!(
143            TariffPricePosition::type_name_english(),
144            "TariffPricePosition"
145        );
146    }
147}