koprogo_api/domain/entities/
work_report.rs1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use uuid::Uuid;
4
5#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
10pub struct WorkReport {
11 pub id: Uuid,
12 pub organization_id: Uuid,
13 pub building_id: Uuid,
14
15 pub title: String,
17 pub description: String,
18 pub work_type: WorkType,
19 pub contractor_name: String,
20 pub contractor_contact: Option<String>,
21
22 pub work_date: DateTime<Utc>, pub completion_date: Option<DateTime<Utc>>, pub cost: f64,
28 pub invoice_number: Option<String>,
29
30 pub photos: Vec<String>, pub documents: Vec<String>, pub notes: Option<String>,
34
35 pub warranty_type: WarrantyType,
37 pub warranty_expiry: DateTime<Utc>,
38
39 pub created_at: DateTime<Utc>,
41 pub updated_at: DateTime<Utc>,
42}
43
44#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
45#[serde(rename_all = "snake_case")]
46pub enum WorkType {
47 Maintenance, Repair, Renovation, Emergency, Inspection, Installation, Other,
54}
55
56#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
57#[serde(rename_all = "snake_case")]
58pub enum WarrantyType {
59 None, Standard, Decennial, Extended, Custom { years: i32 }, }
65
66impl WorkReport {
67 #[allow(clippy::too_many_arguments)]
68 pub fn new(
69 organization_id: Uuid,
70 building_id: Uuid,
71 title: String,
72 description: String,
73 work_type: WorkType,
74 contractor_name: String,
75 work_date: DateTime<Utc>,
76 cost: f64,
77 warranty_type: WarrantyType,
78 ) -> Self {
79 let now = Utc::now();
80
81 let warranty_expiry = match warranty_type {
83 WarrantyType::None => now, WarrantyType::Standard => work_date + chrono::Duration::days(2 * 365), WarrantyType::Decennial => work_date + chrono::Duration::days(10 * 365), WarrantyType::Extended => work_date + chrono::Duration::days(3 * 365), WarrantyType::Custom { years } => {
88 work_date + chrono::Duration::days(years as i64 * 365)
89 }
90 };
91
92 Self {
93 id: Uuid::new_v4(),
94 organization_id,
95 building_id,
96 title,
97 description,
98 work_type,
99 contractor_name,
100 contractor_contact: None,
101 work_date,
102 completion_date: None,
103 cost,
104 invoice_number: None,
105 photos: Vec::new(),
106 documents: Vec::new(),
107 notes: None,
108 warranty_type,
109 warranty_expiry,
110 created_at: now,
111 updated_at: now,
112 }
113 }
114
115 pub fn is_warranty_valid(&self) -> bool {
117 Utc::now() < self.warranty_expiry
118 }
119
120 pub fn warranty_days_remaining(&self) -> i64 {
122 let now = Utc::now();
123 if now >= self.warranty_expiry {
124 0
125 } else {
126 (self.warranty_expiry - now).num_days()
127 }
128 }
129
130 pub fn add_photo(&mut self, photo_path: String) {
132 self.photos.push(photo_path);
133 self.updated_at = Utc::now();
134 }
135
136 pub fn add_document(&mut self, document_path: String) {
138 self.documents.push(document_path);
139 self.updated_at = Utc::now();
140 }
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146
147 #[test]
148 fn test_work_report_creation() {
149 let report = WorkReport::new(
150 Uuid::new_v4(),
151 Uuid::new_v4(),
152 "Réparation ascenseur".to_string(),
153 "Remplacement câble principal".to_string(),
154 WorkType::Repair,
155 "Schindler Belgium".to_string(),
156 Utc::now(),
157 1500.0,
158 WarrantyType::Standard,
159 );
160
161 assert_eq!(report.title, "Réparation ascenseur");
162 assert_eq!(report.cost, 1500.0);
163 assert!(report.is_warranty_valid());
164 assert!(report.warranty_days_remaining() > 700); }
166
167 #[test]
168 fn test_decennial_warranty() {
169 let report = WorkReport::new(
170 Uuid::new_v4(),
171 Uuid::new_v4(),
172 "Rénovation façade".to_string(),
173 "Réfection complète façade".to_string(),
174 WorkType::Renovation,
175 "BatiPro SPRL".to_string(),
176 Utc::now(),
177 50000.0,
178 WarrantyType::Decennial,
179 );
180
181 assert!(report.warranty_days_remaining() > 3600); }
183
184 #[test]
185 fn test_add_photos() {
186 let mut report = WorkReport::new(
187 Uuid::new_v4(),
188 Uuid::new_v4(),
189 "Test".to_string(),
190 "Test".to_string(),
191 WorkType::Maintenance,
192 "Test".to_string(),
193 Utc::now(),
194 100.0,
195 WarrantyType::None,
196 );
197
198 report.add_photo("/uploads/photo1.jpg".to_string());
199 report.add_photo("/uploads/photo2.jpg".to_string());
200
201 assert_eq!(report.photos.len(), 2);
202 }
203}