bo4e_core/com/
external_cost_position.rs1use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5
6use crate::enums::Unit;
7use crate::traits::{Bo4eMeta, Bo4eObject};
8
9use super::{Amount, Price};
10
11#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
27#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
28#[cfg_attr(feature = "json-schema", schemars(rename = "Fremdkostenposition"))]
29#[serde(rename_all = "camelCase")]
30pub struct ExternalCostPosition {
31 #[serde(flatten)]
33 pub meta: Bo4eMeta,
34
35 #[serde(skip_serializing_if = "Option::is_none")]
37 #[cfg_attr(feature = "json-schema", schemars(rename = "positionstitel"))]
38 pub title: Option<String>,
39
40 #[serde(skip_serializing_if = "Option::is_none")]
42 #[cfg_attr(feature = "json-schema", schemars(rename = "betrag"))]
43 pub amount: Option<Amount>,
44
45 #[serde(skip_serializing_if = "Option::is_none")]
47 #[cfg_attr(feature = "json-schema", schemars(rename = "artikelbezeichnung"))]
48 pub article_description: Option<String>,
49
50 #[serde(skip_serializing_if = "Option::is_none")]
52 #[cfg_attr(feature = "json-schema", schemars(rename = "einzelpreis"))]
53 pub unit_price: Option<Price>,
54
55 #[serde(skip_serializing_if = "Option::is_none")]
57 #[cfg_attr(feature = "json-schema", schemars(rename = "von"))]
58 pub start_date: Option<DateTime<Utc>>,
59
60 #[serde(skip_serializing_if = "Option::is_none")]
62 #[cfg_attr(feature = "json-schema", schemars(rename = "bis"))]
63 pub end_date: Option<DateTime<Utc>>,
64
65 #[serde(skip_serializing_if = "Option::is_none")]
67 #[cfg_attr(feature = "json-schema", schemars(rename = "mengeWert"))]
68 pub quantity_value: Option<f64>,
69
70 #[serde(skip_serializing_if = "Option::is_none")]
72 #[cfg_attr(feature = "json-schema", schemars(rename = "mengeEinheit"))]
73 pub quantity_unit: Option<Unit>,
74
75 #[serde(skip_serializing_if = "Option::is_none")]
77 #[cfg_attr(feature = "json-schema", schemars(rename = "link"))]
78 pub external_reference: Option<String>,
79}
80
81impl Bo4eObject for ExternalCostPosition {
82 fn type_name_german() -> &'static str {
83 "Fremdkostenposition"
84 }
85
86 fn type_name_english() -> &'static str {
87 "ExternalCostPosition"
88 }
89
90 fn meta(&self) -> &Bo4eMeta {
91 &self.meta
92 }
93
94 fn meta_mut(&mut self) -> &mut Bo4eMeta {
95 &mut self.meta
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102
103 #[test]
104 fn test_external_cost_position() {
105 let position = ExternalCostPosition {
106 title: Some("Netzbetreiber ABC".to_string()),
107 article_description: Some("Netznutzungsentgelt".to_string()),
108 amount: Some(Amount::eur(120.0)),
109 quantity_value: Some(1500.0),
110 quantity_unit: Some(Unit::KilowattHour),
111 ..Default::default()
112 };
113
114 assert_eq!(position.title, Some("Netzbetreiber ABC".to_string()));
115 assert_eq!(position.quantity_value, Some(1500.0));
116 }
117
118 #[test]
119 fn test_default() {
120 let position = ExternalCostPosition::default();
121 assert!(position.title.is_none());
122 assert!(position.amount.is_none());
123 }
124
125 #[test]
126 fn test_roundtrip() {
127 let position = ExternalCostPosition {
128 title: Some("Messstellenbetreiber".to_string()),
129 amount: Some(Amount::eur(80.0)),
130 external_reference: Some("MSB-2024-001".to_string()),
131 ..Default::default()
132 };
133
134 let json = serde_json::to_string(&position).unwrap();
135 let parsed: ExternalCostPosition = serde_json::from_str(&json).unwrap();
136 assert_eq!(position, parsed);
137 }
138
139 #[test]
140 fn test_bo4e_object_impl() {
141 assert_eq!(
142 ExternalCostPosition::type_name_german(),
143 "Fremdkostenposition"
144 );
145 assert_eq!(
146 ExternalCostPosition::type_name_english(),
147 "ExternalCostPosition"
148 );
149 }
150}