koprogo_api/infrastructure/web/handlers/
work_report_handlers.rs1use crate::application::dto::{
2 AddDocumentDto, AddPhotoDto, CreateWorkReportDto, PageRequest, UpdateWorkReportDto,
3 WorkReportFilters,
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("/work-reports")]
14pub async fn create_work_report(
15 state: web::Data<AppState>,
16 user: AuthenticatedUser,
17 request: web::Json<CreateWorkReportDto>,
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 .work_report_use_cases
28 .create_work_report(request.into_inner())
29 .await
30 {
31 Ok(work_report) => {
32 AuditLogEntry::new(
33 AuditEventType::WorkReportCreated,
34 Some(user.user_id),
35 Some(organization_id),
36 )
37 .with_resource("WorkReport", work_report.id.parse().unwrap())
38 .log();
39
40 HttpResponse::Created().json(work_report)
41 }
42 Err(err) => HttpResponse::BadRequest().json(serde_json::json!({"error": err})),
43 }
44}
45
46#[get("/work-reports/{id}")]
48pub async fn get_work_report(state: web::Data<AppState>, id: web::Path<Uuid>) -> impl Responder {
49 match state.work_report_use_cases.get_work_report(*id).await {
50 Ok(Some(work_report)) => HttpResponse::Ok().json(work_report),
51 Ok(None) => HttpResponse::NotFound().json(serde_json::json!({
52 "error": "Work report not found"
53 })),
54 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
55 }
56}
57
58#[get("/buildings/{building_id}/work-reports")]
60pub async fn list_building_work_reports(
61 state: web::Data<AppState>,
62 building_id: web::Path<Uuid>,
63) -> impl Responder {
64 match state
65 .work_report_use_cases
66 .list_work_reports_by_building(*building_id)
67 .await
68 {
69 Ok(work_reports) => HttpResponse::Ok().json(work_reports),
70 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
71 }
72}
73
74#[get("/organizations/{organization_id}/work-reports")]
76pub async fn list_organization_work_reports(
77 state: web::Data<AppState>,
78 organization_id: web::Path<Uuid>,
79) -> impl Responder {
80 match state
81 .work_report_use_cases
82 .list_work_reports_by_organization(*organization_id)
83 .await
84 {
85 Ok(work_reports) => HttpResponse::Ok().json(work_reports),
86 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
87 }
88}
89
90#[get("/work-reports")]
92pub async fn list_work_reports_paginated(
93 state: web::Data<AppState>,
94 page_request: web::Query<PageRequest>,
95 filters: web::Query<WorkReportFilters>,
96) -> impl Responder {
97 match state
98 .work_report_use_cases
99 .list_work_reports_paginated(&page_request.into_inner(), &filters.into_inner())
100 .await
101 {
102 Ok(response) => HttpResponse::Ok().json(response),
103 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
104 }
105}
106
107#[put("/work-reports/{id}")]
109pub async fn update_work_report(
110 state: web::Data<AppState>,
111 user: AuthenticatedUser,
112 id: web::Path<Uuid>,
113 request: web::Json<UpdateWorkReportDto>,
114) -> impl Responder {
115 let organization_id = match user.require_organization() {
116 Ok(org_id) => org_id,
117 Err(e) => {
118 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
119 }
120 };
121
122 match state
123 .work_report_use_cases
124 .update_work_report(*id, request.into_inner())
125 .await
126 {
127 Ok(work_report) => {
128 AuditLogEntry::new(
129 AuditEventType::WorkReportUpdated,
130 Some(user.user_id),
131 Some(organization_id),
132 )
133 .with_resource("WorkReport", *id)
134 .log();
135
136 HttpResponse::Ok().json(work_report)
137 }
138 Err(err) => HttpResponse::BadRequest().json(serde_json::json!({"error": err})),
139 }
140}
141
142#[delete("/work-reports/{id}")]
144pub async fn delete_work_report(
145 state: web::Data<AppState>,
146 user: AuthenticatedUser,
147 id: web::Path<Uuid>,
148) -> impl Responder {
149 let organization_id = match user.require_organization() {
150 Ok(org_id) => org_id,
151 Err(e) => {
152 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
153 }
154 };
155
156 match state.work_report_use_cases.delete_work_report(*id).await {
157 Ok(deleted) => {
158 if deleted {
159 AuditLogEntry::new(
160 AuditEventType::WorkReportDeleted,
161 Some(user.user_id),
162 Some(organization_id),
163 )
164 .with_resource("WorkReport", *id)
165 .log();
166
167 HttpResponse::NoContent().finish()
168 } else {
169 HttpResponse::NotFound().json(serde_json::json!({
170 "error": "Work report not found"
171 }))
172 }
173 }
174 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
175 }
176}
177
178#[get("/buildings/{building_id}/work-reports/warranties/active")]
182pub async fn get_active_warranties(
183 state: web::Data<AppState>,
184 building_id: web::Path<Uuid>,
185) -> impl Responder {
186 match state
187 .work_report_use_cases
188 .get_active_warranties(*building_id)
189 .await
190 {
191 Ok(warranties) => HttpResponse::Ok().json(warranties),
192 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
193 }
194}
195
196#[get("/buildings/{building_id}/work-reports/warranties/expiring")]
198pub async fn get_expiring_warranties(
199 state: web::Data<AppState>,
200 path: web::Path<Uuid>,
201 query: web::Query<serde_json::Value>,
202) -> impl Responder {
203 let building_id = path.into_inner();
204 let days = query.get("days").and_then(|v| v.as_i64()).unwrap_or(90) as i32;
205
206 match state
207 .work_report_use_cases
208 .get_expiring_warranties(building_id, days)
209 .await
210 {
211 Ok(warranties) => HttpResponse::Ok().json(warranties),
212 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
213 }
214}
215
216#[post("/work-reports/{id}/photos")]
220pub async fn add_photo(
221 state: web::Data<AppState>,
222 user: AuthenticatedUser,
223 id: web::Path<Uuid>,
224 request: web::Json<AddPhotoDto>,
225) -> impl Responder {
226 let organization_id = match user.require_organization() {
227 Ok(org_id) => org_id,
228 Err(e) => {
229 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
230 }
231 };
232
233 match state
234 .work_report_use_cases
235 .add_photo(*id, request.into_inner())
236 .await
237 {
238 Ok(work_report) => {
239 AuditLogEntry::new(
240 AuditEventType::WorkReportPhotoAdded,
241 Some(user.user_id),
242 Some(organization_id),
243 )
244 .with_resource("WorkReport", *id)
245 .log();
246
247 HttpResponse::Ok().json(work_report)
248 }
249 Err(err) => HttpResponse::BadRequest().json(serde_json::json!({"error": err})),
250 }
251}
252
253#[post("/work-reports/{id}/documents")]
255pub async fn add_document(
256 state: web::Data<AppState>,
257 user: AuthenticatedUser,
258 id: web::Path<Uuid>,
259 request: web::Json<AddDocumentDto>,
260) -> impl Responder {
261 let organization_id = match user.require_organization() {
262 Ok(org_id) => org_id,
263 Err(e) => {
264 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
265 }
266 };
267
268 match state
269 .work_report_use_cases
270 .add_document(*id, request.into_inner())
271 .await
272 {
273 Ok(work_report) => {
274 AuditLogEntry::new(
275 AuditEventType::WorkReportDocumentAdded,
276 Some(user.user_id),
277 Some(organization_id),
278 )
279 .with_resource("WorkReport", *id)
280 .log();
281
282 HttpResponse::Ok().json(work_report)
283 }
284 Err(err) => HttpResponse::BadRequest().json(serde_json::json!({"error": err})),
285 }
286}