koprogo_api/application/use_cases/
technical_inspection_use_cases.rs1use crate::application::dto::{
2 AddCertificateDto, AddInspectionPhotoDto, AddReportDto, CreateTechnicalInspectionDto,
3 InspectionStatusDto, PageRequest, TechnicalInspectionFilters,
4 TechnicalInspectionListResponseDto, TechnicalInspectionResponseDto,
5 UpdateTechnicalInspectionDto,
6};
7use crate::application::ports::TechnicalInspectionRepository;
8use crate::domain::entities::{InspectionStatus, TechnicalInspection};
9use chrono::DateTime;
10use std::sync::Arc;
11use uuid::Uuid;
12
13pub struct TechnicalInspectionUseCases {
14 repository: Arc<dyn TechnicalInspectionRepository>,
15}
16
17impl TechnicalInspectionUseCases {
18 pub fn new(repository: Arc<dyn TechnicalInspectionRepository>) -> Self {
19 Self { repository }
20 }
21
22 pub async fn create_technical_inspection(
23 &self,
24 dto: CreateTechnicalInspectionDto,
25 ) -> Result<TechnicalInspectionResponseDto, String> {
26 let organization_id = Uuid::parse_str(&dto.organization_id)
27 .map_err(|_| "Invalid organization_id format".to_string())?;
28 let building_id = Uuid::parse_str(&dto.building_id)
29 .map_err(|_| "Invalid building_id format".to_string())?;
30
31 let inspection_date = DateTime::parse_from_rfc3339(&dto.inspection_date)
32 .map_err(|_| "Invalid inspection_date format".to_string())?
33 .with_timezone(&chrono::Utc);
34
35 let compliance_valid_until = if let Some(ref date_str) = dto.compliance_valid_until {
36 Some(
37 DateTime::parse_from_rfc3339(date_str)
38 .map_err(|_| "Invalid compliance_valid_until format".to_string())?
39 .with_timezone(&chrono::Utc),
40 )
41 } else {
42 None
43 };
44
45 let inspection = TechnicalInspection::new(
46 organization_id,
47 building_id,
48 dto.title,
49 dto.description,
50 dto.inspection_type.clone(),
51 dto.inspector_name,
52 inspection_date,
53 );
54
55 let mut inspection = inspection;
56 inspection.inspector_company = dto.inspector_company;
57 inspection.inspector_certification = dto.inspector_certification;
58 inspection.result_summary = dto.result_summary;
59 inspection.defects_found = dto.defects_found;
60 inspection.recommendations = dto.recommendations;
61 inspection.compliant = dto.compliant;
62 inspection.compliance_certificate_number = dto.compliance_certificate_number;
63 inspection.compliance_valid_until = compliance_valid_until;
64 inspection.cost = dto.cost;
65 inspection.invoice_number = dto.invoice_number;
66 inspection.notes = dto.notes;
67
68 let created = self.repository.create(&inspection).await?;
69 Ok(self.to_response_dto(&created))
70 }
71
72 pub async fn get_technical_inspection(
73 &self,
74 id: Uuid,
75 ) -> Result<Option<TechnicalInspectionResponseDto>, String> {
76 let inspection = self.repository.find_by_id(id).await?;
77 Ok(inspection.map(|i| self.to_response_dto(&i)))
78 }
79
80 pub async fn list_technical_inspections_by_building(
81 &self,
82 building_id: Uuid,
83 ) -> Result<Vec<TechnicalInspectionResponseDto>, String> {
84 let inspections = self.repository.find_by_building(building_id).await?;
85 Ok(inspections
86 .iter()
87 .map(|i| self.to_response_dto(i))
88 .collect())
89 }
90
91 pub async fn list_technical_inspections_by_organization(
92 &self,
93 organization_id: Uuid,
94 ) -> Result<Vec<TechnicalInspectionResponseDto>, String> {
95 let inspections = self
96 .repository
97 .find_by_organization(organization_id)
98 .await?;
99 Ok(inspections
100 .iter()
101 .map(|i| self.to_response_dto(i))
102 .collect())
103 }
104
105 pub async fn list_technical_inspections_paginated(
106 &self,
107 page_request: &PageRequest,
108 filters: &TechnicalInspectionFilters,
109 ) -> Result<TechnicalInspectionListResponseDto, String> {
110 let (inspections, total) = self
111 .repository
112 .find_all_paginated(page_request, filters)
113 .await?;
114
115 let dtos = inspections
116 .iter()
117 .map(|i| self.to_response_dto(i))
118 .collect();
119
120 Ok(TechnicalInspectionListResponseDto {
121 inspections: dtos,
122 total,
123 page: page_request.page,
124 page_size: page_request.per_page,
125 })
126 }
127
128 pub async fn get_overdue_inspections(
129 &self,
130 building_id: Uuid,
131 ) -> Result<Vec<InspectionStatusDto>, String> {
132 let inspections = self.repository.find_overdue(building_id).await?;
133
134 Ok(inspections
135 .iter()
136 .map(|i| InspectionStatusDto {
137 inspection_id: i.id.to_string(),
138 title: i.title.clone(),
139 inspection_type: i.inspection_type.clone(),
140 next_due_date: i.next_due_date.to_rfc3339(),
141 status: i.status.clone(),
142 is_overdue: i.is_overdue(),
143 days_until_due: i.days_until_due(),
144 })
145 .collect())
146 }
147
148 pub async fn get_upcoming_inspections(
149 &self,
150 building_id: Uuid,
151 days: i32,
152 ) -> Result<Vec<InspectionStatusDto>, String> {
153 let inspections = self.repository.find_upcoming(building_id, days).await?;
154
155 Ok(inspections
156 .iter()
157 .map(|i| InspectionStatusDto {
158 inspection_id: i.id.to_string(),
159 title: i.title.clone(),
160 inspection_type: i.inspection_type.clone(),
161 next_due_date: i.next_due_date.to_rfc3339(),
162 status: i.status.clone(),
163 is_overdue: i.is_overdue(),
164 days_until_due: i.days_until_due(),
165 })
166 .collect())
167 }
168
169 pub async fn get_inspections_by_type(
170 &self,
171 building_id: Uuid,
172 inspection_type: &str,
173 ) -> Result<Vec<TechnicalInspectionResponseDto>, String> {
174 let inspections = self
175 .repository
176 .find_by_type(building_id, inspection_type)
177 .await?;
178
179 Ok(inspections
180 .iter()
181 .map(|i| self.to_response_dto(i))
182 .collect())
183 }
184
185 pub async fn update_technical_inspection(
186 &self,
187 id: Uuid,
188 dto: UpdateTechnicalInspectionDto,
189 ) -> Result<TechnicalInspectionResponseDto, String> {
190 let mut inspection = self
191 .repository
192 .find_by_id(id)
193 .await?
194 .ok_or_else(|| "Technical inspection not found".to_string())?;
195
196 if let Some(title) = dto.title {
197 inspection.title = title;
198 }
199 if let Some(description) = dto.description {
200 inspection.description = Some(description);
201 }
202 if let Some(inspection_type) = dto.inspection_type {
203 inspection.inspection_type = inspection_type;
204 inspection.next_due_date = inspection.calculate_next_due_date();
206 }
207 if let Some(inspector_name) = dto.inspector_name {
208 inspection.inspector_name = inspector_name;
209 }
210 if let Some(inspector_company) = dto.inspector_company {
211 inspection.inspector_company = Some(inspector_company);
212 }
213 if let Some(inspector_certification) = dto.inspector_certification {
214 inspection.inspector_certification = Some(inspector_certification);
215 }
216 if let Some(inspection_date_str) = dto.inspection_date {
217 let inspection_date = DateTime::parse_from_rfc3339(&inspection_date_str)
218 .map_err(|_| "Invalid inspection_date format".to_string())?
219 .with_timezone(&chrono::Utc);
220 inspection.inspection_date = inspection_date;
221 inspection.next_due_date = inspection.calculate_next_due_date();
223 }
224 if let Some(status) = dto.status {
225 inspection.status = status;
226 }
227 if let Some(result_summary) = dto.result_summary {
228 inspection.result_summary = Some(result_summary);
229 }
230 if let Some(defects_found) = dto.defects_found {
231 inspection.defects_found = Some(defects_found);
232 }
233 if let Some(recommendations) = dto.recommendations {
234 inspection.recommendations = Some(recommendations);
235 }
236 if let Some(compliant) = dto.compliant {
237 inspection.compliant = Some(compliant);
238 }
239 if let Some(compliance_certificate_number) = dto.compliance_certificate_number {
240 inspection.compliance_certificate_number = Some(compliance_certificate_number);
241 }
242 if let Some(compliance_valid_until_str) = dto.compliance_valid_until {
243 let compliance_valid_until = DateTime::parse_from_rfc3339(&compliance_valid_until_str)
244 .map_err(|_| "Invalid compliance_valid_until format".to_string())?
245 .with_timezone(&chrono::Utc);
246 inspection.compliance_valid_until = Some(compliance_valid_until);
247 }
248 if let Some(cost) = dto.cost {
249 inspection.cost = Some(cost);
250 }
251 if let Some(invoice_number) = dto.invoice_number {
252 inspection.invoice_number = Some(invoice_number);
253 }
254 if let Some(notes) = dto.notes {
255 inspection.notes = Some(notes);
256 }
257
258 inspection.updated_at = chrono::Utc::now();
259
260 let updated = self.repository.update(&inspection).await?;
261 Ok(self.to_response_dto(&updated))
262 }
263
264 pub async fn mark_as_completed(
265 &self,
266 id: Uuid,
267 ) -> Result<TechnicalInspectionResponseDto, String> {
268 let mut inspection = self
269 .repository
270 .find_by_id(id)
271 .await?
272 .ok_or_else(|| "Technical inspection not found".to_string())?;
273
274 inspection.status = InspectionStatus::Completed;
275 inspection.updated_at = chrono::Utc::now();
276
277 let updated = self.repository.update(&inspection).await?;
278 Ok(self.to_response_dto(&updated))
279 }
280
281 pub async fn add_report(
282 &self,
283 id: Uuid,
284 dto: AddReportDto,
285 ) -> Result<TechnicalInspectionResponseDto, String> {
286 let mut inspection = self
287 .repository
288 .find_by_id(id)
289 .await?
290 .ok_or_else(|| "Technical inspection not found".to_string())?;
291
292 inspection.add_report(dto.report_path);
293
294 let updated = self.repository.update(&inspection).await?;
295 Ok(self.to_response_dto(&updated))
296 }
297
298 pub async fn add_photo(
299 &self,
300 id: Uuid,
301 dto: AddInspectionPhotoDto,
302 ) -> Result<TechnicalInspectionResponseDto, String> {
303 let mut inspection = self
304 .repository
305 .find_by_id(id)
306 .await?
307 .ok_or_else(|| "Technical inspection not found".to_string())?;
308
309 inspection.add_photo(dto.photo_path);
310
311 let updated = self.repository.update(&inspection).await?;
312 Ok(self.to_response_dto(&updated))
313 }
314
315 pub async fn add_certificate(
316 &self,
317 id: Uuid,
318 dto: AddCertificateDto,
319 ) -> Result<TechnicalInspectionResponseDto, String> {
320 let mut inspection = self
321 .repository
322 .find_by_id(id)
323 .await?
324 .ok_or_else(|| "Technical inspection not found".to_string())?;
325
326 inspection.add_certificate(dto.certificate_path);
327
328 let updated = self.repository.update(&inspection).await?;
329 Ok(self.to_response_dto(&updated))
330 }
331
332 pub async fn delete_technical_inspection(&self, id: Uuid) -> Result<bool, String> {
333 self.repository.delete(id).await
334 }
335
336 fn to_response_dto(&self, inspection: &TechnicalInspection) -> TechnicalInspectionResponseDto {
337 TechnicalInspectionResponseDto {
338 id: inspection.id.to_string(),
339 organization_id: inspection.organization_id.to_string(),
340 building_id: inspection.building_id.to_string(),
341 title: inspection.title.clone(),
342 description: inspection.description.clone(),
343 inspection_type: inspection.inspection_type.clone(),
344 inspector_name: inspection.inspector_name.clone(),
345 inspector_company: inspection.inspector_company.clone(),
346 inspector_certification: inspection.inspector_certification.clone(),
347 inspection_date: inspection.inspection_date.to_rfc3339(),
348 next_due_date: inspection.next_due_date.to_rfc3339(),
349 status: inspection.status.clone(),
350 result_summary: inspection.result_summary.clone(),
351 defects_found: inspection.defects_found.clone(),
352 recommendations: inspection.recommendations.clone(),
353 compliant: inspection.compliant,
354 compliance_certificate_number: inspection.compliance_certificate_number.clone(),
355 compliance_valid_until: inspection
356 .compliance_valid_until
357 .as_ref()
358 .map(|d| d.to_rfc3339()),
359 cost: inspection.cost,
360 invoice_number: inspection.invoice_number.clone(),
361 reports: inspection.reports.clone(),
362 photos: inspection.photos.clone(),
363 certificates: inspection.certificates.clone(),
364 notes: inspection.notes.clone(),
365 is_overdue: inspection.is_overdue(),
366 days_until_due: inspection.days_until_due(),
367 created_at: inspection.created_at.to_rfc3339(),
368 updated_at: inspection.updated_at.to_rfc3339(),
369 }
370 }
371}