bo4e_core/com/
cost_block.rs1use serde::{Deserialize, Serialize};
4
5use crate::enums::CostClass;
6use crate::traits::{Bo4eMeta, Bo4eObject};
7
8use super::{Amount, CostPosition};
9
10#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
26#[serde(rename_all = "camelCase")]
27pub struct CostBlock {
28 #[serde(flatten)]
30 pub meta: Bo4eMeta,
31
32 #[serde(skip_serializing_if = "Option::is_none")]
34 pub designation: Option<String>,
35
36 #[serde(skip_serializing_if = "Option::is_none")]
38 pub cost_class: Option<CostClass>,
39
40 #[serde(skip_serializing_if = "Option::is_none")]
42 pub total_amount: Option<Amount>,
43
44 #[serde(default, skip_serializing_if = "Vec::is_empty")]
46 pub positions: Vec<CostPosition>,
47}
48
49impl Bo4eObject for CostBlock {
50 fn type_name_german() -> &'static str {
51 "Kostenblock"
52 }
53
54 fn type_name_english() -> &'static str {
55 "CostBlock"
56 }
57
58 fn meta(&self) -> &Bo4eMeta {
59 &self.meta
60 }
61
62 fn meta_mut(&mut self) -> &mut Bo4eMeta {
63 &mut self.meta
64 }
65}
66
67#[cfg(test)]
68mod tests {
69 use super::*;
70 use crate::com::Price;
71
72 #[test]
73 fn test_cost_block() {
74 let cost_block = CostBlock {
75 designation: Some("Netzkosten".to_string()),
76 cost_class: Some(CostClass::ExternalCosts),
77 total_amount: Some(Amount::eur(500.0)),
78 positions: vec![
79 CostPosition {
80 title: Some("Arbeitspreis Netz".to_string()),
81 amount: Some(Amount::eur(300.0)),
82 ..Default::default()
83 },
84 CostPosition {
85 title: Some("Leistungspreis Netz".to_string()),
86 amount: Some(Amount::eur(200.0)),
87 ..Default::default()
88 },
89 ],
90 ..Default::default()
91 };
92
93 assert_eq!(cost_block.designation, Some("Netzkosten".to_string()));
94 assert_eq!(cost_block.positions.len(), 2);
95 }
96
97 #[test]
98 fn test_default() {
99 let cost_block = CostBlock::default();
100 assert!(cost_block.designation.is_none());
101 assert!(cost_block.total_amount.is_none());
102 assert!(cost_block.positions.is_empty());
103 }
104
105 #[test]
106 fn test_serialize() {
107 let cost_block = CostBlock {
108 designation: Some("Messkosten".to_string()),
109 total_amount: Some(Amount::eur(100.0)),
110 ..Default::default()
111 };
112
113 let json = serde_json::to_string(&cost_block).unwrap();
114 assert!(json.contains(r#""designation":"Messkosten""#));
115 }
116
117 #[test]
118 fn test_roundtrip() {
119 let cost_block = CostBlock {
120 designation: Some("Energiekosten".to_string()),
121 cost_class: Some(CostClass::Procurement),
122 total_amount: Some(Amount::eur(1234.56)),
123 positions: vec![CostPosition {
124 title: Some("Arbeitspreis Energie".to_string()),
125 unit_price: Some(Price::eur_per_kwh(0.25)),
126 ..Default::default()
127 }],
128 ..Default::default()
129 };
130
131 let json = serde_json::to_string(&cost_block).unwrap();
132 let parsed: CostBlock = serde_json::from_str(&json).unwrap();
133 assert_eq!(cost_block, parsed);
134 }
135
136 #[test]
137 fn test_bo4e_object_impl() {
138 assert_eq!(CostBlock::type_name_german(), "Kostenblock");
139 assert_eq!(CostBlock::type_name_english(), "CostBlock");
140 }
141}