koprogo_api/infrastructure/web/handlers/
technical_inspection_handlers.rs1use 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#[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-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#[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#[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#[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#[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-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#[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("/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("/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#[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#[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#[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}