bo4e_core/bo/
balancing.rs

1//! Balancing (Bilanzierung) business object.
2
3use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5
6use crate::com::TimePeriod;
7use crate::enums::Division;
8use crate::traits::{Bo4eMeta, Bo4eObject};
9
10/// Balance group data for energy market balancing.
11///
12/// German: Bilanzierung
13///
14/// # Example
15///
16/// ```rust
17/// use bo4e_core::bo::Balancing;
18/// use bo4e_core::enums::Division;
19///
20/// let balancing = Balancing {
21///     balance_group_id: Some("11XBALANCEGROUP".to_string()),
22///     division: Some(Division::Electricity),
23///     ..Default::default()
24/// };
25/// ```
26#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
27#[serde(rename_all = "camelCase")]
28pub struct Balancing {
29    /// BO4E metadata
30    #[serde(flatten)]
31    pub meta: Bo4eMeta,
32
33    /// Balance group identifier (Bilanzkreis-ID)
34    #[serde(skip_serializing_if = "Option::is_none")]
35    pub balance_group_id: Option<String>,
36
37    /// Balance group name (Bilanzkreisname)
38    #[serde(skip_serializing_if = "Option::is_none")]
39    pub balance_group_name: Option<String>,
40
41    /// Description (Beschreibung)
42    #[serde(skip_serializing_if = "Option::is_none")]
43    pub description: Option<String>,
44
45    /// Energy division (Sparte)
46    #[serde(skip_serializing_if = "Option::is_none")]
47    pub division: Option<Division>,
48
49    /// Market area (Marktgebiet)
50    #[serde(skip_serializing_if = "Option::is_none")]
51    pub market_area: Option<String>,
52
53    /// Balance responsible party (Bilanzkreisverantwortlicher)
54    #[serde(skip_serializing_if = "Option::is_none")]
55    pub balance_responsible_party: Option<Box<super::MarketParticipant>>,
56
57    /// Validity period (Gueltigkeitszeitraum)
58    #[serde(skip_serializing_if = "Option::is_none")]
59    pub validity_period: Option<TimePeriod>,
60
61    /// Start date of balancing (Startdatum)
62    #[serde(skip_serializing_if = "Option::is_none")]
63    pub start_date: Option<DateTime<Utc>>,
64
65    /// End date of balancing (Enddatum)
66    #[serde(skip_serializing_if = "Option::is_none")]
67    pub end_date: Option<DateTime<Utc>>,
68}
69
70impl Bo4eObject for Balancing {
71    fn type_name_german() -> &'static str {
72        "Bilanzierung"
73    }
74
75    fn type_name_english() -> &'static str {
76        "Balancing"
77    }
78
79    fn meta(&self) -> &Bo4eMeta {
80        &self.meta
81    }
82
83    fn meta_mut(&mut self) -> &mut Bo4eMeta {
84        &mut self.meta
85    }
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91
92    #[test]
93    fn test_balancing_creation() {
94        let balancing = Balancing {
95            balance_group_id: Some("11XBALANCEGROUP".to_string()),
96            division: Some(Division::Electricity),
97            ..Default::default()
98        };
99
100        assert_eq!(
101            balancing.balance_group_id,
102            Some("11XBALANCEGROUP".to_string())
103        );
104        assert_eq!(balancing.division, Some(Division::Electricity));
105    }
106
107    #[test]
108    fn test_gas_balancing() {
109        let balancing = Balancing {
110            balance_group_id: Some("GASBALANCE001".to_string()),
111            balance_group_name: Some("Gas Balance Group 1".to_string()),
112            division: Some(Division::Gas),
113            market_area: Some("NCG".to_string()),
114            ..Default::default()
115        };
116
117        assert_eq!(balancing.division, Some(Division::Gas));
118        assert_eq!(balancing.market_area, Some("NCG".to_string()));
119    }
120
121    #[test]
122    fn test_serialize() {
123        let balancing = Balancing {
124            meta: Bo4eMeta::with_type("Bilanzierung"),
125            balance_group_id: Some("11XTEST".to_string()),
126            division: Some(Division::Electricity),
127            ..Default::default()
128        };
129
130        let json = serde_json::to_string(&balancing).unwrap();
131        assert!(json.contains(r#""balanceGroupId":"11XTEST""#));
132    }
133
134    #[test]
135    fn test_roundtrip() {
136        let balancing = Balancing {
137            meta: Bo4eMeta::with_type("Bilanzierung"),
138            balance_group_id: Some("11XTEST".to_string()),
139            balance_group_name: Some("Test Balance Group".to_string()),
140            description: Some("Test description".to_string()),
141            division: Some(Division::Electricity),
142            market_area: Some("DE".to_string()),
143            ..Default::default()
144        };
145
146        let json = serde_json::to_string(&balancing).unwrap();
147        let parsed: Balancing = serde_json::from_str(&json).unwrap();
148        assert_eq!(balancing, parsed);
149    }
150
151    #[test]
152    fn test_bo4e_object_impl() {
153        assert_eq!(Balancing::type_name_german(), "Bilanzierung");
154        assert_eq!(Balancing::type_name_english(), "Balancing");
155    }
156}