1use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5
6use crate::com::{ContractConditions, ContractPart, TimePeriod};
7use crate::enums::{ContractStatus, ContractType, Division};
8use crate::traits::{Bo4eMeta, Bo4eObject};
9
10#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
29#[serde(rename_all = "camelCase")]
30pub struct Contract {
31 #[serde(flatten)]
33 pub meta: Bo4eMeta,
34
35 #[serde(skip_serializing_if = "Option::is_none")]
37 pub contract_number: Option<String>,
38
39 #[serde(skip_serializing_if = "Option::is_none")]
41 pub description: Option<String>,
42
43 #[serde(skip_serializing_if = "Option::is_none")]
45 pub contract_type: Option<ContractType>,
46
47 #[serde(skip_serializing_if = "Option::is_none")]
49 pub status: Option<ContractStatus>,
50
51 #[serde(skip_serializing_if = "Option::is_none")]
53 pub division: Option<Division>,
54
55 #[serde(skip_serializing_if = "Option::is_none")]
57 pub contract_start: Option<DateTime<Utc>>,
58
59 #[serde(skip_serializing_if = "Option::is_none")]
61 pub contract_end: Option<DateTime<Utc>>,
62
63 #[serde(skip_serializing_if = "Option::is_none")]
65 pub signing_date: Option<DateTime<Utc>>,
66
67 #[serde(skip_serializing_if = "Option::is_none")]
69 pub validity_period: Option<TimePeriod>,
70
71 #[serde(skip_serializing_if = "Option::is_none")]
73 pub conditions: Option<ContractConditions>,
74
75 #[serde(default, skip_serializing_if = "Vec::is_empty")]
77 pub parts: Vec<ContractPart>,
78
79 #[serde(skip_serializing_if = "Option::is_none")]
81 pub contract_partner: Option<Box<super::BusinessPartner>>,
82}
83
84impl Bo4eObject for Contract {
85 fn type_name_german() -> &'static str {
86 "Vertrag"
87 }
88
89 fn type_name_english() -> &'static str {
90 "Contract"
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_supply_contract() {
108 let contract = Contract {
109 contract_number: Some("V-2024-001".to_string()),
110 contract_type: Some(ContractType::EnergySupplyContract),
111 status: Some(ContractStatus::Active),
112 division: Some(Division::Electricity),
113 ..Default::default()
114 };
115
116 assert_eq!(contract.status, Some(ContractStatus::Active));
117 }
118
119 #[test]
120 fn test_serialize() {
121 let contract = Contract {
122 meta: Bo4eMeta::with_type("Vertrag"),
123 contract_number: Some("V-123".to_string()),
124 ..Default::default()
125 };
126
127 let json = serde_json::to_string(&contract).unwrap();
128 assert!(json.contains(r#""contractNumber":"V-123""#));
129 }
130
131 #[test]
132 fn test_roundtrip() {
133 let contract = Contract {
134 meta: Bo4eMeta::with_type("Vertrag"),
135 contract_number: Some("V-123".to_string()),
136 description: Some("Test contract".to_string()),
137 contract_type: Some(ContractType::EnergySupplyContract),
138 status: Some(ContractStatus::Active),
139 division: Some(Division::Electricity),
140 ..Default::default()
141 };
142
143 let json = serde_json::to_string(&contract).unwrap();
144 let parsed: Contract = serde_json::from_str(&json).unwrap();
145 assert_eq!(contract, parsed);
146 }
147
148 #[test]
149 fn test_bo4e_object_impl() {
150 assert_eq!(Contract::type_name_german(), "Vertrag");
151 assert_eq!(Contract::type_name_english(), "Contract");
152 }
153}