koprogo_api/infrastructure/web/handlers/
technical_inspection_handlers.rs

1use crate::application::dto::{
2    AddCertificateDto, AddInspectionPhotoDto, AddReportDto, CreateTechnicalInspectionDto,
3    PageRequest, TechnicalInspectionFilters, UpdateTechnicalInspectionDto,
4};
5use crate::infrastructure::audit::{AuditEventType, AuditLogEntry};
6use crate::infrastructure::web::{AppState, AuthenticatedUser};
7use actix_web::{delete, get, post, put, web, HttpResponse, Responder};
8use uuid::Uuid;
9
10// ==================== Technical Inspection CRUD Endpoints ====================
11
12/// Create a new technical inspection
13#[post("/technical-inspections")]
14pub async fn create_technical_inspection(
15    state: web::Data<AppState>,
16    user: AuthenticatedUser,
17    request: web::Json<CreateTechnicalInspectionDto>,
18) -> impl Responder {
19    let organization_id = match user.require_organization() {
20        Ok(org_id) => org_id,
21        Err(e) => {
22            return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
23        }
24    };
25
26    match state
27        .technical_inspection_use_cases
28        .create_technical_inspection(request.into_inner())
29        .await
30    {
31        Ok(inspection) => {
32            AuditLogEntry::new(
33                AuditEventType::TechnicalInspectionCreated,
34                Some(user.user_id),
35                Some(organization_id),
36            )
37            .with_resource("TechnicalInspection", inspection.id.parse().unwrap())
38            .log();
39
40            HttpResponse::Created().json(inspection)
41        }
42        Err(err) => HttpResponse::BadRequest().json(serde_json::json!({"error": err})),
43    }
44}
45
46/// Get technical inspection by ID
47#[get("/technical-inspections/{id}")]
48pub async fn get_technical_inspection(
49    state: web::Data<AppState>,
50    id: web::Path<Uuid>,
51) -> impl Responder {
52    match state
53        .technical_inspection_use_cases
54        .get_technical_inspection(*id)
55        .await
56    {
57        Ok(Some(inspection)) => HttpResponse::Ok().json(inspection),
58        Ok(None) => HttpResponse::NotFound().json(serde_json::json!({
59            "error": "Technical inspection not found"
60        })),
61        Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
62    }
63}
64
65/// List technical inspections by building
66#[get("/buildings/{building_id}/technical-inspections")]
67pub async fn list_building_technical_inspections(
68    state: web::Data<AppState>,
69    building_id: web::Path<Uuid>,
70) -> impl Responder {
71    match state
72        .technical_inspection_use_cases
73        .list_technical_inspections_by_building(*building_id)
74        .await
75    {
76        Ok(inspections) => HttpResponse::Ok().json(inspections),
77        Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
78    }
79}
80
81/// List technical inspections by organization
82#[get("/organizations/{organization_id}/technical-inspections")]
83pub async fn list_organization_technical_inspections(
84    state: web::Data<AppState>,
85    organization_id: web::Path<Uuid>,
86) -> impl Responder {
87    match state
88        .technical_inspection_use_cases
89        .list_technical_inspections_by_organization(*organization_id)
90        .await
91    {
92        Ok(inspections) => HttpResponse::Ok().json(inspections),
93        Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
94    }
95}
96
97/// List technical inspections with pagination and filters
98#[get("/technical-inspections")]
99pub async fn list_technical_inspections_paginated(
100    state: web::Data<AppState>,
101    page_request: web::Query<PageRequest>,
102    filters: web::Query<TechnicalInspectionFilters>,
103) -> impl Responder {
104    match state
105        .technical_inspection_use_cases
106        .list_technical_inspections_paginated(&page_request.into_inner(), &filters.into_inner())
107        .await
108    {
109        Ok(response) => HttpResponse::Ok().json(response),
110        Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
111    }
112}
113
114/// Update technical inspection
115#[put("/technical-inspections/{id}")]
116pub async fn update_technical_inspection(
117    state: web::Data<AppState>,
118    user: AuthenticatedUser,
119    id: web::Path<Uuid>,
120    request: web::Json<UpdateTechnicalInspectionDto>,
121) -> impl Responder {
122    let organization_id = match user.require_organization() {
123        Ok(org_id) => org_id,
124        Err(e) => {
125            return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
126        }
127    };
128
129    match state
130        .technical_inspection_use_cases
131        .update_technical_inspection(*id, request.into_inner())
132        .await
133    {
134        Ok(inspection) => {
135            AuditLogEntry::new(
136                AuditEventType::TechnicalInspectionUpdated,
137                Some(user.user_id),
138                Some(organization_id),
139            )
140            .with_resource("TechnicalInspection", *id)
141            .log();
142
143            HttpResponse::Ok().json(inspection)
144        }
145        Err(err) => HttpResponse::BadRequest().json(serde_json::json!({"error": err})),
146    }
147}
148
149/// Delete technical inspection
150#[delete("/technical-inspections/{id}")]
151pub async fn delete_technical_inspection(
152    state: web::Data<AppState>,
153    user: AuthenticatedUser,
154    id: web::Path<Uuid>,
155) -> impl Responder {
156    let organization_id = match user.require_organization() {
157        Ok(org_id) => org_id,
158        Err(e) => {
159            return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
160        }
161    };
162
163    match state
164        .technical_inspection_use_cases
165        .delete_technical_inspection(*id)
166        .await
167    {
168        Ok(deleted) => {
169            if deleted {
170                AuditLogEntry::new(
171                    AuditEventType::TechnicalInspectionDeleted,
172                    Some(user.user_id),
173                    Some(organization_id),
174                )
175                .with_resource("TechnicalInspection", *id)
176                .log();
177
178                HttpResponse::NoContent().finish()
179            } else {
180                HttpResponse::NotFound().json(serde_json::json!({
181                    "error": "Technical inspection not found"
182                }))
183            }
184        }
185        Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
186    }
187}
188
189// ==================== Inspection Tracking Endpoints ====================
190
191/// Get overdue inspections for a building
192#[get("/buildings/{building_id}/technical-inspections/overdue")]
193pub async fn get_overdue_inspections(
194    state: web::Data<AppState>,
195    building_id: web::Path<Uuid>,
196) -> impl Responder {
197    match state
198        .technical_inspection_use_cases
199        .get_overdue_inspections(*building_id)
200        .await
201    {
202        Ok(inspections) => HttpResponse::Ok().json(inspections),
203        Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
204    }
205}
206
207/// Get upcoming inspections for a building (within X days)
208#[get("/buildings/{building_id}/technical-inspections/upcoming")]
209pub async fn get_upcoming_inspections(
210    state: web::Data<AppState>,
211    path: web::Path<Uuid>,
212    query: web::Query<serde_json::Value>,
213) -> impl Responder {
214    let building_id = path.into_inner();
215    let days = query.get("days").and_then(|v| v.as_i64()).unwrap_or(90) as i32;
216
217    match state
218        .technical_inspection_use_cases
219        .get_upcoming_inspections(building_id, days)
220        .await
221    {
222        Ok(inspections) => HttpResponse::Ok().json(inspections),
223        Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
224    }
225}
226
227/// Get inspections by type for a building
228#[get("/buildings/{building_id}/technical-inspections/type/{inspection_type}")]
229pub async fn get_inspections_by_type(
230    state: web::Data<AppState>,
231    path: web::Path<(Uuid, String)>,
232) -> impl Responder {
233    let (building_id, inspection_type) = path.into_inner();
234
235    match state
236        .technical_inspection_use_cases
237        .get_inspections_by_type(building_id, &inspection_type)
238        .await
239    {
240        Ok(inspections) => HttpResponse::Ok().json(inspections),
241        Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
242    }
243}
244
245// ==================== Document Management ====================
246
247/// Add report to technical inspection
248#[post("/technical-inspections/{id}/reports")]
249pub async fn add_report(
250    state: web::Data<AppState>,
251    user: AuthenticatedUser,
252    id: web::Path<Uuid>,
253    request: web::Json<AddReportDto>,
254) -> impl Responder {
255    let organization_id = match user.require_organization() {
256        Ok(org_id) => org_id,
257        Err(e) => {
258            return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
259        }
260    };
261
262    match state
263        .technical_inspection_use_cases
264        .add_report(*id, request.into_inner())
265        .await
266    {
267        Ok(inspection) => {
268            AuditLogEntry::new(
269                AuditEventType::TechnicalInspectionReportAdded,
270                Some(user.user_id),
271                Some(organization_id),
272            )
273            .with_resource("TechnicalInspection", *id)
274            .log();
275
276            HttpResponse::Ok().json(inspection)
277        }
278        Err(err) => HttpResponse::BadRequest().json(serde_json::json!({"error": err})),
279    }
280}
281
282/// Add photo to technical inspection
283#[post("/technical-inspections/{id}/photos")]
284pub async fn add_inspection_photo(
285    state: web::Data<AppState>,
286    user: AuthenticatedUser,
287    id: web::Path<Uuid>,
288    request: web::Json<AddInspectionPhotoDto>,
289) -> impl Responder {
290    let organization_id = match user.require_organization() {
291        Ok(org_id) => org_id,
292        Err(e) => {
293            return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
294        }
295    };
296
297    match state
298        .technical_inspection_use_cases
299        .add_photo(*id, request.into_inner())
300        .await
301    {
302        Ok(inspection) => {
303            AuditLogEntry::new(
304                AuditEventType::TechnicalInspectionPhotoAdded,
305                Some(user.user_id),
306                Some(organization_id),
307            )
308            .with_resource("TechnicalInspection", *id)
309            .log();
310
311            HttpResponse::Ok().json(inspection)
312        }
313        Err(err) => HttpResponse::BadRequest().json(serde_json::json!({"error": err})),
314    }
315}
316
317/// Add certificate to technical inspection
318#[post("/technical-inspections/{id}/certificates")]
319pub async fn add_certificate(
320    state: web::Data<AppState>,
321    user: AuthenticatedUser,
322    id: web::Path<Uuid>,
323    request: web::Json<AddCertificateDto>,
324) -> impl Responder {
325    let organization_id = match user.require_organization() {
326        Ok(org_id) => org_id,
327        Err(e) => {
328            return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
329        }
330    };
331
332    match state
333        .technical_inspection_use_cases
334        .add_certificate(*id, request.into_inner())
335        .await
336    {
337        Ok(inspection) => {
338            AuditLogEntry::new(
339                AuditEventType::TechnicalInspectionCertificateAdded,
340                Some(user.user_id),
341                Some(organization_id),
342            )
343            .with_resource("TechnicalInspection", *id)
344            .log();
345
346            HttpResponse::Ok().json(inspection)
347        }
348        Err(err) => HttpResponse::BadRequest().json(serde_json::json!({"error": err})),
349    }
350}