1use serde::{Deserialize, Serialize};
4
5use crate::com::{Amount, CostBlock, TimePeriod};
6use crate::enums::Division;
7use crate::traits::{Bo4eMeta, Bo4eObject};
8
9#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
28#[serde(rename_all = "camelCase")]
29pub struct Costs {
30 #[serde(flatten)]
32 pub meta: Bo4eMeta,
33
34 #[serde(skip_serializing_if = "Option::is_none")]
36 pub designation: Option<String>,
37
38 #[serde(skip_serializing_if = "Option::is_none")]
40 pub description: Option<String>,
41
42 #[serde(skip_serializing_if = "Option::is_none")]
44 pub division: Option<Division>,
45
46 #[serde(skip_serializing_if = "Option::is_none")]
48 pub period: Option<TimePeriod>,
49
50 #[serde(skip_serializing_if = "Option::is_none")]
52 pub total_amount: Option<Amount>,
53
54 #[serde(default, skip_serializing_if = "Vec::is_empty")]
56 pub cost_blocks: Vec<CostBlock>,
57
58 #[serde(skip_serializing_if = "Option::is_none")]
60 pub market_location: Option<Box<super::MarketLocation>>,
61}
62
63impl Bo4eObject for Costs {
64 fn type_name_german() -> &'static str {
65 "Kosten"
66 }
67
68 fn type_name_english() -> &'static str {
69 "Costs"
70 }
71
72 fn meta(&self) -> &Bo4eMeta {
73 &self.meta
74 }
75
76 fn meta_mut(&mut self) -> &mut Bo4eMeta {
77 &mut self.meta
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84 use crate::com::CostBlock;
85
86 #[test]
87 fn test_costs_creation() {
88 let costs = Costs {
89 designation: Some("Jahreskosten 2024".to_string()),
90 division: Some(Division::Electricity),
91 total_amount: Some(Amount::eur(2500.00)),
92 ..Default::default()
93 };
94
95 assert_eq!(costs.total_amount, Some(Amount::eur(2500.00)));
96 }
97
98 #[test]
99 fn test_costs_with_blocks() {
100 let costs = Costs {
101 designation: Some("Annual Costs".to_string()),
102 cost_blocks: vec![
103 CostBlock {
104 designation: Some("Energy".to_string()),
105 total_amount: Some(Amount::eur(1500.0)),
106 ..Default::default()
107 },
108 CostBlock {
109 designation: Some("Network".to_string()),
110 total_amount: Some(Amount::eur(800.0)),
111 ..Default::default()
112 },
113 ],
114 total_amount: Some(Amount::eur(2300.0)),
115 ..Default::default()
116 };
117
118 assert_eq!(costs.cost_blocks.len(), 2);
119 }
120
121 #[test]
122 fn test_serialize() {
123 let costs = Costs {
124 meta: Bo4eMeta::with_type("Kosten"),
125 designation: Some("Test Costs".to_string()),
126 total_amount: Some(Amount::eur(1000.0)),
127 ..Default::default()
128 };
129
130 let json = serde_json::to_string(&costs).unwrap();
131 assert!(json.contains(r#""designation":"Test Costs""#));
132 assert!(json.contains(r#""_typ":"Kosten""#));
133 }
134
135 #[test]
136 fn test_roundtrip() {
137 let costs = Costs {
138 meta: Bo4eMeta::with_type("Kosten"),
139 designation: Some("Gas Costs".to_string()),
140 description: Some("Annual gas costs".to_string()),
141 division: Some(Division::Gas),
142 total_amount: Some(Amount::eur(1234.56)),
143 ..Default::default()
144 };
145
146 let json = serde_json::to_string(&costs).unwrap();
147 let parsed: Costs = serde_json::from_str(&json).unwrap();
148 assert_eq!(costs, parsed);
149 }
150
151 #[test]
152 fn test_bo4e_object_impl() {
153 assert_eq!(Costs::type_name_german(), "Kosten");
154 assert_eq!(Costs::type_name_english(), "Costs");
155 }
156}