Skip to main content

koprogo_api/infrastructure/web/handlers/
pcn_handlers.rs

1use crate::application::dto::PcnReportRequest;
2use crate::infrastructure::web::{AppState, AuthenticatedUser};
3use actix_web::{get, post, web, HttpResponse, Responder, ResponseError};
4use uuid::Uuid;
5
6/// Generate PCN report for a building (JSON)
7/// POST /api/v1/pcn/report/:building_id
8#[post("/pcn/report/{building_id}")]
9pub async fn generate_pcn_report(
10    app_state: web::Data<AppState>,
11    user: AuthenticatedUser,
12    building_id: web::Path<Uuid>,
13) -> impl Responder {
14    // Aucune identité n'était exigée ici : ni `AuthenticatedUser`, ni jeton
15    // lu à la main. N'importe qui pouvait exporter la comptabilité complète
16    // de n'importe quel immeuble.
17    //
18    // Le cliquet d'identité de #772 ne pouvait pas le voir : il compte les
19    // routes qui PRENNENT `AuthenticatedUser` sans s'en servir. Une route qui
20    // ne le prend pas du tout lui échappait entièrement. Cf. #845.
21    if let Err(err) =
22        crate::infrastructure::web::middleware::scope_guard::verify_building_org_access(
23            &user,
24            *building_id,
25            &app_state.building_use_cases,
26            &app_state.acp_use_cases,
27        )
28        .await
29    {
30        return err.error_response();
31    }
32
33    let request = PcnReportRequest {
34        building_id: *building_id,
35        start_date: None,
36        end_date: None,
37    };
38
39    match app_state.pcn_use_cases.generate_report(request).await {
40        Ok(report) => HttpResponse::Ok().json(report),
41        Err(err) => HttpResponse::InternalServerError().body(err),
42    }
43}
44
45/// Export PCN report as PDF
46/// GET /api/v1/pcn/export/pdf/:building_id?name=MyBuilding
47#[get("/pcn/export/pdf/{building_id}")]
48pub async fn export_pcn_pdf(
49    app_state: web::Data<AppState>,
50    user: AuthenticatedUser,
51    building_id: web::Path<Uuid>,
52    query: web::Query<std::collections::HashMap<String, String>>,
53) -> impl Responder {
54    // Aucune identité n'était exigée ici : ni `AuthenticatedUser`, ni jeton
55    // lu à la main. N'importe qui pouvait exporter la comptabilité complète
56    // de n'importe quel immeuble.
57    //
58    // Le cliquet d'identité de #772 ne pouvait pas le voir : il compte les
59    // routes qui PRENNENT `AuthenticatedUser` sans s'en servir. Une route qui
60    // ne le prend pas du tout lui échappait entièrement. Cf. #845.
61    if let Err(err) =
62        crate::infrastructure::web::middleware::scope_guard::verify_building_org_access(
63            &user,
64            *building_id,
65            &app_state.building_use_cases,
66            &app_state.acp_use_cases,
67        )
68        .await
69    {
70        return err.error_response();
71    }
72
73    let building_name = query
74        .get("name")
75        .cloned()
76        .unwrap_or_else(|| "Building".to_string());
77
78    let request = PcnReportRequest {
79        building_id: *building_id,
80        start_date: None,
81        end_date: None,
82    };
83
84    match app_state
85        .pcn_use_cases
86        .export_pdf(&building_name, request)
87        .await
88    {
89        Ok(pdf_bytes) => HttpResponse::Ok()
90            .content_type("application/pdf")
91            .append_header((
92                "Content-Disposition",
93                "attachment; filename=\"rapport-pcn.pdf\"",
94            ))
95            .body(pdf_bytes),
96        Err(err) => HttpResponse::InternalServerError().body(err),
97    }
98}
99
100/// Export PCN report as Excel
101/// GET /api/v1/pcn/export/excel/:building_id?name=MyBuilding
102#[get("/pcn/export/excel/{building_id}")]
103pub async fn export_pcn_excel(
104    app_state: web::Data<AppState>,
105    user: AuthenticatedUser,
106    building_id: web::Path<Uuid>,
107    query: web::Query<std::collections::HashMap<String, String>>,
108) -> impl Responder {
109    // Aucune identité n'était exigée ici : ni `AuthenticatedUser`, ni jeton
110    // lu à la main. N'importe qui pouvait exporter la comptabilité complète
111    // de n'importe quel immeuble.
112    //
113    // Le cliquet d'identité de #772 ne pouvait pas le voir : il compte les
114    // routes qui PRENNENT `AuthenticatedUser` sans s'en servir. Une route qui
115    // ne le prend pas du tout lui échappait entièrement. Cf. #845.
116    if let Err(err) =
117        crate::infrastructure::web::middleware::scope_guard::verify_building_org_access(
118            &user,
119            *building_id,
120            &app_state.building_use_cases,
121            &app_state.acp_use_cases,
122        )
123        .await
124    {
125        return err.error_response();
126    }
127
128    let building_name = query
129        .get("name")
130        .cloned()
131        .unwrap_or_else(|| "Building".to_string());
132
133    let request = PcnReportRequest {
134        building_id: *building_id,
135        start_date: None,
136        end_date: None,
137    };
138
139    match app_state
140        .pcn_use_cases
141        .export_excel(&building_name, request)
142        .await
143    {
144        Ok(excel_bytes) => HttpResponse::Ok()
145            .content_type("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
146            .append_header((
147                "Content-Disposition",
148                "attachment; filename=\"rapport-pcn.xlsx\"",
149            ))
150            .body(excel_bytes),
151        Err(err) => HttpResponse::InternalServerError().body(err),
152    }
153}