Skip to main content

bo4e_core/bo/
metering_price_sheet.rs

1//! Metering price sheet (PreisblattMessung) business object.
2
3use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5
6use crate::com::{Price, PricePosition, TimePeriod};
7use crate::enums::{Division, MeterType};
8use crate::traits::{Bo4eMeta, Bo4eObject};
9
10/// A price sheet for metering services.
11///
12/// German: PreisblattMessung
13///
14/// # Example
15///
16/// ```rust
17/// use bo4e_core::bo::MeteringPriceSheet;
18/// use bo4e_core::enums::Division;
19///
20/// let price_sheet = MeteringPriceSheet {
21///     designation: Some("Messpreisblatt 2024".to_string()),
22///     division: Some(Division::Electricity),
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 = "PreisblattMessung"))]
29#[serde(rename_all = "camelCase")]
30pub struct MeteringPriceSheet {
31    /// BO4E metadata
32    #[serde(flatten)]
33    pub meta: Bo4eMeta,
34
35    /// Name/designation of the price sheet (Bezeichnung)
36    #[serde(skip_serializing_if = "Option::is_none")]
37    #[cfg_attr(feature = "json-schema", schemars(rename = "bezeichnung"))]
38    pub designation: Option<String>,
39
40    /// Description (Beschreibung)
41    #[serde(skip_serializing_if = "Option::is_none")]
42    #[cfg_attr(feature = "json-schema", schemars(rename = "beschreibung"))]
43    pub description: Option<String>,
44
45    /// Energy division (Sparte)
46    #[serde(skip_serializing_if = "Option::is_none")]
47    #[cfg_attr(feature = "json-schema", schemars(rename = "sparte"))]
48    pub division: Option<Division>,
49
50    /// Type of meter this applies to (Zaehlerart)
51    #[serde(skip_serializing_if = "Option::is_none")]
52    #[cfg_attr(feature = "json-schema", schemars(rename = "zaehlerart"))]
53    pub meter_type: Option<MeterType>,
54
55    /// Price sheet number/identifier (Preisblattnummer)
56    #[serde(skip_serializing_if = "Option::is_none")]
57    #[cfg_attr(feature = "json-schema", schemars(rename = "preisblattnummer"))]
58    pub price_sheet_number: Option<String>,
59
60    /// Validity period (Gueltigkeitszeitraum)
61    #[serde(skip_serializing_if = "Option::is_none")]
62    #[cfg_attr(feature = "json-schema", schemars(rename = "gueltigkeitszeitraum"))]
63    pub validity_period: Option<TimePeriod>,
64
65    /// Valid from date (Gueltig ab)
66    #[serde(skip_serializing_if = "Option::is_none")]
67    #[cfg_attr(feature = "json-schema", schemars(rename = "gueltigAb"))]
68    pub valid_from: Option<DateTime<Utc>>,
69
70    /// Valid until date (Gueltig bis)
71    #[serde(skip_serializing_if = "Option::is_none")]
72    #[cfg_attr(feature = "json-schema", schemars(rename = "gueltigBis"))]
73    pub valid_until: Option<DateTime<Utc>>,
74
75    /// Metering point operation price (Messstellenbetrieb)
76    #[serde(skip_serializing_if = "Option::is_none")]
77    #[cfg_attr(feature = "json-schema", schemars(rename = "messstellenbetrieb"))]
78    pub metering_point_operation_price: Option<Price>,
79
80    /// Meter reading price (Ablesepreis)
81    #[serde(skip_serializing_if = "Option::is_none")]
82    #[cfg_attr(feature = "json-schema", schemars(rename = "ablesepreis"))]
83    pub reading_price: Option<Price>,
84
85    /// Price positions (Preispositionen)
86    #[serde(default, skip_serializing_if = "Vec::is_empty")]
87    #[cfg_attr(feature = "json-schema", schemars(rename = "preispositionen"))]
88    pub positions: Vec<PricePosition>,
89
90    /// Metering operator
91    #[serde(skip_serializing_if = "Option::is_none")]
92    #[cfg_attr(feature = "json-schema", schemars(rename = "messstellenbetreiber"))]
93    pub operator: Option<Box<super::BusinessPartner>>,
94}
95
96impl Bo4eObject for MeteringPriceSheet {
97    fn type_name_german() -> &'static str {
98        "PreisblattMessung"
99    }
100
101    fn type_name_english() -> &'static str {
102        "MeteringPriceSheet"
103    }
104
105    fn meta(&self) -> &Bo4eMeta {
106        &self.meta
107    }
108
109    fn meta_mut(&mut self) -> &mut Bo4eMeta {
110        &mut self.meta
111    }
112}
113
114#[cfg(test)]
115mod tests {
116    use super::*;
117
118    #[test]
119    fn test_metering_price_sheet_creation() {
120        let price_sheet = MeteringPriceSheet {
121            designation: Some("Messpreisblatt 2024".to_string()),
122            division: Some(Division::Electricity),
123            meter_type: Some(MeterType::ElectronicMeter),
124            price_sheet_number: Some("MP-2024-001".to_string()),
125            ..Default::default()
126        };
127
128        assert_eq!(price_sheet.meter_type, Some(MeterType::ElectronicMeter));
129    }
130
131    #[test]
132    fn test_serialize() {
133        let price_sheet = MeteringPriceSheet {
134            meta: Bo4eMeta::with_type("PreisblattMessung"),
135            designation: Some("Metering Prices".to_string()),
136            ..Default::default()
137        };
138
139        let json = serde_json::to_string(&price_sheet).unwrap();
140        assert!(json.contains(r#""designation":"Metering Prices""#));
141        assert!(json.contains(r#""_typ":"PreisblattMessung""#));
142    }
143
144    #[test]
145    fn test_roundtrip() {
146        let price_sheet = MeteringPriceSheet {
147            meta: Bo4eMeta::with_type("PreisblattMessung"),
148            designation: Some("Gas Metering".to_string()),
149            description: Some("Gas metering service prices".to_string()),
150            division: Some(Division::Gas),
151            price_sheet_number: Some("GM-2024".to_string()),
152            ..Default::default()
153        };
154
155        let json = serde_json::to_string(&price_sheet).unwrap();
156        let parsed: MeteringPriceSheet = serde_json::from_str(&json).unwrap();
157        assert_eq!(price_sheet, parsed);
158    }
159
160    #[test]
161    fn test_bo4e_object_impl() {
162        assert_eq!(MeteringPriceSheet::type_name_german(), "PreisblattMessung");
163        assert_eq!(
164            MeteringPriceSheet::type_name_english(),
165            "MeteringPriceSheet"
166        );
167    }
168}