Skip to main content

bo4e_core/bo/
location_assignment.rs

1//! Location assignment (Lokationszuordnung) business object.
2//!
3//! Represents the assignment/relationship between different location types.
4
5use serde::{Deserialize, Serialize};
6
7use crate::com::TimePeriod;
8use crate::enums::{ArithmeticOperation, LocationType};
9use crate::traits::{Bo4eMeta, Bo4eObject};
10
11/// An assignment between locations in the energy market.
12///
13/// German: Lokationszuordnung
14///
15/// This business object represents the relationship between different
16/// types of locations (market, metering, network).
17///
18/// # Example
19///
20/// ```rust
21/// use bo4e_core::bo::LocationAssignment;
22///
23/// let assignment = LocationAssignment {
24///     market_location_id: Some("12345678901".to_string()),
25///     metering_location_id: Some("DE00012345678901234567890123456789".to_string()),
26///     ..Default::default()
27/// };
28/// ```
29#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
30#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
31#[cfg_attr(feature = "json-schema", schemars(rename = "Lokationszuordnung"))]
32#[serde(rename_all = "camelCase")]
33pub struct LocationAssignment {
34    /// BO4E metadata
35    #[serde(flatten)]
36    pub meta: Bo4eMeta,
37
38    /// Market location ID (Marktlokations-ID)
39    #[serde(skip_serializing_if = "Option::is_none")]
40    #[cfg_attr(feature = "json-schema", schemars(rename = "marktlokationsId"))]
41    pub market_location_id: Option<String>,
42
43    /// Metering location ID (Messlokations-ID)
44    #[serde(skip_serializing_if = "Option::is_none")]
45    #[cfg_attr(feature = "json-schema", schemars(rename = "messlokationsId"))]
46    pub metering_location_id: Option<String>,
47
48    /// Network location ID (Netzlokations-ID)
49    #[serde(skip_serializing_if = "Option::is_none")]
50    #[cfg_attr(feature = "json-schema", schemars(rename = "netzlokationsId"))]
51    pub network_location_id: Option<String>,
52
53    /// Technical resource ID (Technische-Ressource-ID)
54    #[serde(skip_serializing_if = "Option::is_none")]
55    #[cfg_attr(feature = "json-schema", schemars(rename = "technischeRessourceId"))]
56    pub technical_resource_id: Option<String>,
57
58    /// Controllable resource ID (Steuerbare-Ressource-ID)
59    #[serde(skip_serializing_if = "Option::is_none")]
60    #[cfg_attr(feature = "json-schema", schemars(rename = "steuerbareRessourceId"))]
61    pub controllable_resource_id: Option<String>,
62
63    /// Location type (Lokationstyp)
64    #[serde(skip_serializing_if = "Option::is_none")]
65    #[cfg_attr(feature = "json-schema", schemars(rename = "lokationstyp"))]
66    pub location_type: Option<LocationType>,
67
68    /// Arithmetic operation for combination (Rechenoperation)
69    #[serde(skip_serializing_if = "Option::is_none")]
70    #[cfg_attr(feature = "json-schema", schemars(rename = "rechenoperation"))]
71    pub arithmetic_operation: Option<ArithmeticOperation>,
72
73    /// Validity period (Gueltigkeitszeitraum)
74    #[serde(skip_serializing_if = "Option::is_none")]
75    #[cfg_attr(feature = "json-schema", schemars(rename = "gueltigkeitszeitraum"))]
76    pub validity_period: Option<TimePeriod>,
77
78    /// Sequence/order number (Reihenfolge)
79    #[serde(skip_serializing_if = "Option::is_none")]
80    #[cfg_attr(feature = "json-schema", schemars(rename = "reihenfolge"))]
81    pub sequence: Option<i32>,
82}
83
84impl Bo4eObject for LocationAssignment {
85    fn type_name_german() -> &'static str {
86        "Lokationszuordnung"
87    }
88
89    fn type_name_english() -> &'static str {
90        "LocationAssignment"
91    }
92
93    fn meta(&self) -> &Bo4eMeta {
94        &self.meta
95    }
96
97    fn meta_mut(&mut self) -> &mut Bo4eMeta {
98        &mut self.meta
99    }
100}
101
102#[cfg(test)]
103mod tests {
104    use super::*;
105
106    #[test]
107    fn test_assignment_creation() {
108        let assignment = LocationAssignment {
109            market_location_id: Some("12345678901".to_string()),
110            metering_location_id: Some("DE00012345678901234567890123456789".to_string()),
111            ..Default::default()
112        };
113
114        assert!(assignment.market_location_id.is_some());
115        assert!(assignment.metering_location_id.is_some());
116    }
117
118    #[test]
119    fn test_serialize() {
120        let assignment = LocationAssignment {
121            meta: Bo4eMeta::with_type("Lokationszuordnung"),
122            market_location_id: Some("12345678901".to_string()),
123            ..Default::default()
124        };
125
126        let json = serde_json::to_string(&assignment).unwrap();
127        assert!(json.contains(r#""_typ":"Lokationszuordnung""#));
128    }
129
130    #[test]
131    fn test_roundtrip() {
132        let assignment = LocationAssignment {
133            meta: Bo4eMeta::with_type("Lokationszuordnung"),
134            market_location_id: Some("12345678901".to_string()),
135            metering_location_id: Some("DE00012345678901234567890123456789".to_string()),
136            arithmetic_operation: Some(ArithmeticOperation::Addition),
137            sequence: Some(1),
138            ..Default::default()
139        };
140
141        let json = serde_json::to_string(&assignment).unwrap();
142        let parsed: LocationAssignment = serde_json::from_str(&json).unwrap();
143        assert_eq!(assignment, parsed);
144    }
145
146    #[test]
147    fn test_bo4e_object_impl() {
148        assert_eq!(LocationAssignment::type_name_german(), "Lokationszuordnung");
149        assert_eq!(
150            LocationAssignment::type_name_english(),
151            "LocationAssignment"
152        );
153    }
154}