koprogo_api/infrastructure/web/handlers/
etat_date_handlers.rs1use crate::application::dto::{
2 CreateEtatDateRequest, PageRequest, PageResponse, UpdateEtatDateAdditionalDataRequest,
3 UpdateEtatDateFinancialRequest,
4};
5use crate::domain::entities::EtatDateStatus;
6use crate::infrastructure::audit::{AuditEventType, AuditLogEntry};
7use crate::infrastructure::web::{AppState, AuthenticatedUser};
8use actix_web::{delete, get, post, put, web, HttpResponse, Responder};
9use serde::Deserialize;
10use uuid::Uuid;
11
12#[derive(Debug, Deserialize)]
13pub struct EtatDateListQuery {
14 #[serde(default = "default_page")]
15 pub page: i64,
16 #[serde(default = "default_per_page")]
17 pub per_page: i64,
18 pub status: Option<String>,
19}
20
21fn default_page() -> i64 {
22 1
23}
24fn default_per_page() -> i64 {
25 10
26}
27
28#[post("/etats-dates")]
30pub async fn create_etat_date(
31 state: web::Data<AppState>,
32 user: AuthenticatedUser,
33 mut request: web::Json<CreateEtatDateRequest>,
34) -> impl Responder {
35 let organization_id = match user.require_organization() {
37 Ok(org_id) => org_id,
38 Err(e) => {
39 return HttpResponse::Unauthorized().json(serde_json::json!({
40 "error": e.to_string()
41 }))
42 }
43 };
44 request.organization_id = organization_id;
45
46 match state
47 .etat_date_use_cases
48 .create_etat_date(request.into_inner())
49 .await
50 {
51 Ok(etat_date) => {
52 AuditLogEntry::new(
53 AuditEventType::EtatDateCreated,
54 Some(user.user_id),
55 Some(organization_id),
56 )
57 .with_resource("EtatDate", etat_date.id)
58 .log();
59
60 HttpResponse::Created().json(etat_date)
61 }
62 Err(err) => {
63 AuditLogEntry::new(
64 AuditEventType::EtatDateCreated,
65 Some(user.user_id),
66 Some(organization_id),
67 )
68 .with_error(err.clone())
69 .log();
70
71 HttpResponse::BadRequest().json(serde_json::json!({
72 "error": err
73 }))
74 }
75 }
76}
77
78#[get("/etats-dates/{id}")]
80pub async fn get_etat_date(
81 state: web::Data<AppState>,
82 user: AuthenticatedUser,
83 id: web::Path<Uuid>,
84) -> impl Responder {
85 match state.etat_date_use_cases.get_etat_date(*id).await {
86 Ok(Some(etat_date)) => {
87 if let Err(err) = user.verify_org_access(etat_date.organization_id) {
89 return HttpResponse::Forbidden().json(serde_json::json!({"error": err}));
90 }
91 HttpResponse::Ok().json(etat_date)
92 }
93 Ok(None) => HttpResponse::NotFound().json(serde_json::json!({
94 "error": "État daté not found"
95 })),
96 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({
97 "error": err
98 })),
99 }
100}
101
102#[get("/etats-dates/reference/{reference_number}")]
104pub async fn get_by_reference_number(
105 state: web::Data<AppState>,
106 reference_number: web::Path<String>,
107) -> impl Responder {
108 match state
109 .etat_date_use_cases
110 .get_by_reference_number(&reference_number)
111 .await
112 {
113 Ok(Some(etat_date)) => HttpResponse::Ok().json(etat_date),
114 Ok(None) => HttpResponse::NotFound().json(serde_json::json!({
115 "error": "État daté not found"
116 })),
117 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({
118 "error": err
119 })),
120 }
121}
122
123#[get("/etats-dates")]
125pub async fn list_etats_dates(
126 state: web::Data<AppState>,
127 user: AuthenticatedUser,
128 query: web::Query<EtatDateListQuery>,
129) -> impl Responder {
130 let organization_id = user.organization_id;
131
132 let status = query.status.as_ref().and_then(|s| match s.as_str() {
134 "requested" => Some(EtatDateStatus::Requested),
135 "in_progress" => Some(EtatDateStatus::InProgress),
136 "generated" => Some(EtatDateStatus::Generated),
137 "delivered" => Some(EtatDateStatus::Delivered),
138 "expired" => Some(EtatDateStatus::Expired),
139 _ => None,
140 });
141
142 let page_request = PageRequest {
143 page: query.page,
144 per_page: query.per_page,
145 sort_by: None,
146 order: Default::default(),
147 };
148
149 match state
150 .etat_date_use_cases
151 .list_paginated(&page_request, organization_id, status)
152 .await
153 {
154 Ok((etats, total)) => {
155 let response =
156 PageResponse::new(etats, page_request.page, page_request.per_page, total);
157 HttpResponse::Ok().json(response)
158 }
159 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({
160 "error": err
161 })),
162 }
163}
164
165#[get("/units/{unit_id}/etats-dates")]
167pub async fn list_etats_dates_by_unit(
168 state: web::Data<AppState>,
169 unit_id: web::Path<Uuid>,
170) -> impl Responder {
171 match state.etat_date_use_cases.list_by_unit(*unit_id).await {
172 Ok(etats) => HttpResponse::Ok().json(etats),
173 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({
174 "error": err
175 })),
176 }
177}
178
179#[get("/buildings/{building_id}/etats-dates")]
181pub async fn list_etats_dates_by_building(
182 state: web::Data<AppState>,
183 building_id: web::Path<Uuid>,
184) -> impl Responder {
185 match state
186 .etat_date_use_cases
187 .list_by_building(*building_id)
188 .await
189 {
190 Ok(etats) => HttpResponse::Ok().json(etats),
191 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({
192 "error": err
193 })),
194 }
195}
196
197#[put("/etats-dates/{id}/mark-in-progress")]
199pub async fn mark_in_progress(
200 state: web::Data<AppState>,
201 user: AuthenticatedUser,
202 id: web::Path<Uuid>,
203) -> impl Responder {
204 match state.etat_date_use_cases.mark_in_progress(*id).await {
205 Ok(etat_date) => {
206 AuditLogEntry::new(
207 AuditEventType::EtatDateInProgress,
208 Some(user.user_id),
209 user.organization_id,
210 )
211 .with_resource("EtatDate", etat_date.id)
212 .log();
213
214 HttpResponse::Ok().json(etat_date)
215 }
216 Err(err) => HttpResponse::BadRequest().json(serde_json::json!({
217 "error": err
218 })),
219 }
220}
221
222#[put("/etats-dates/{id}/mark-generated")]
224pub async fn mark_generated(
225 state: web::Data<AppState>,
226 user: AuthenticatedUser,
227 id: web::Path<Uuid>,
228 pdf_path: web::Json<serde_json::Value>,
229) -> impl Responder {
230 let pdf_file_path = match pdf_path.get("pdf_file_path") {
231 Some(serde_json::Value::String(path)) => path.clone(),
232 _ => {
233 return HttpResponse::BadRequest().json(serde_json::json!({
234 "error": "pdf_file_path is required as a string"
235 }))
236 }
237 };
238
239 match state
240 .etat_date_use_cases
241 .mark_generated(*id, pdf_file_path)
242 .await
243 {
244 Ok(etat_date) => {
245 AuditLogEntry::new(
246 AuditEventType::EtatDateGenerated,
247 Some(user.user_id),
248 user.organization_id,
249 )
250 .with_resource("EtatDate", etat_date.id)
251 .log();
252
253 HttpResponse::Ok().json(etat_date)
254 }
255 Err(err) => HttpResponse::BadRequest().json(serde_json::json!({
256 "error": err
257 })),
258 }
259}
260
261#[put("/etats-dates/{id}/mark-delivered")]
263pub async fn mark_delivered(
264 state: web::Data<AppState>,
265 user: AuthenticatedUser,
266 id: web::Path<Uuid>,
267) -> impl Responder {
268 match state.etat_date_use_cases.mark_delivered(*id).await {
269 Ok(etat_date) => {
270 AuditLogEntry::new(
271 AuditEventType::EtatDateDelivered,
272 Some(user.user_id),
273 user.organization_id,
274 )
275 .with_resource("EtatDate", etat_date.id)
276 .log();
277
278 HttpResponse::Ok().json(etat_date)
279 }
280 Err(err) => HttpResponse::BadRequest().json(serde_json::json!({
281 "error": err
282 })),
283 }
284}
285
286#[put("/etats-dates/{id}/financial")]
288pub async fn update_financial_data(
289 state: web::Data<AppState>,
290 user: AuthenticatedUser,
291 id: web::Path<Uuid>,
292 request: web::Json<UpdateEtatDateFinancialRequest>,
293) -> impl Responder {
294 match state
295 .etat_date_use_cases
296 .update_financial_data(*id, request.into_inner())
297 .await
298 {
299 Ok(etat_date) => {
300 AuditLogEntry::new(
301 AuditEventType::EtatDateFinancialUpdate,
302 Some(user.user_id),
303 user.organization_id,
304 )
305 .with_resource("EtatDate", etat_date.id)
306 .log();
307
308 HttpResponse::Ok().json(etat_date)
309 }
310 Err(err) => HttpResponse::BadRequest().json(serde_json::json!({
311 "error": err
312 })),
313 }
314}
315
316#[put("/etats-dates/{id}/additional-data")]
318pub async fn update_additional_data(
319 state: web::Data<AppState>,
320 user: AuthenticatedUser,
321 id: web::Path<Uuid>,
322 request: web::Json<UpdateEtatDateAdditionalDataRequest>,
323) -> impl Responder {
324 match state
325 .etat_date_use_cases
326 .update_additional_data(*id, request.into_inner())
327 .await
328 {
329 Ok(etat_date) => {
330 AuditLogEntry::new(
331 AuditEventType::EtatDateAdditionalDataUpdate,
332 Some(user.user_id),
333 user.organization_id,
334 )
335 .with_resource("EtatDate", etat_date.id)
336 .log();
337
338 HttpResponse::Ok().json(etat_date)
339 }
340 Err(err) => HttpResponse::BadRequest().json(serde_json::json!({
341 "error": err
342 })),
343 }
344}
345
346#[get("/etats-dates/overdue")]
348pub async fn list_overdue(state: web::Data<AppState>, user: AuthenticatedUser) -> impl Responder {
349 let organization_id = match user.require_organization() {
350 Ok(org_id) => org_id,
351 Err(e) => {
352 return HttpResponse::Unauthorized().json(serde_json::json!({
353 "error": e.to_string()
354 }))
355 }
356 };
357
358 match state
359 .etat_date_use_cases
360 .list_overdue(organization_id)
361 .await
362 {
363 Ok(etats) => HttpResponse::Ok().json(etats),
364 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({
365 "error": err
366 })),
367 }
368}
369
370#[get("/etats-dates/expired")]
372pub async fn list_expired(state: web::Data<AppState>, user: AuthenticatedUser) -> impl Responder {
373 let organization_id = match user.require_organization() {
374 Ok(org_id) => org_id,
375 Err(e) => {
376 return HttpResponse::Unauthorized().json(serde_json::json!({
377 "error": e.to_string()
378 }))
379 }
380 };
381
382 match state
383 .etat_date_use_cases
384 .list_expired(organization_id)
385 .await
386 {
387 Ok(etats) => HttpResponse::Ok().json(etats),
388 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({
389 "error": err
390 })),
391 }
392}
393
394#[get("/etats-dates/stats")]
396pub async fn get_stats(state: web::Data<AppState>, user: AuthenticatedUser) -> impl Responder {
397 let organization_id = match user.require_organization() {
398 Ok(org_id) => org_id,
399 Err(e) => {
400 return HttpResponse::Unauthorized().json(serde_json::json!({
401 "error": e.to_string()
402 }))
403 }
404 };
405
406 match state.etat_date_use_cases.get_stats(organization_id).await {
407 Ok(stats) => HttpResponse::Ok().json(stats),
408 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({
409 "error": err
410 })),
411 }
412}
413
414#[delete("/etats-dates/{id}")]
416pub async fn delete_etat_date(
417 state: web::Data<AppState>,
418 user: AuthenticatedUser,
419 id: web::Path<Uuid>,
420) -> impl Responder {
421 match state.etat_date_use_cases.delete_etat_date(*id).await {
422 Ok(true) => {
423 AuditLogEntry::new(
424 AuditEventType::EtatDateDeleted,
425 Some(user.user_id),
426 user.organization_id,
427 )
428 .with_resource("EtatDate", *id)
429 .log();
430
431 HttpResponse::NoContent().finish()
432 }
433 Ok(false) => HttpResponse::NotFound().json(serde_json::json!({
434 "error": "État daté not found"
435 })),
436 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({
437 "error": err
438 })),
439 }
440}