koprogo_api/infrastructure/web/handlers/
financial_report_handlers_building.rs

1// Web Handlers: Financial Reports for Buildings
2use super::financial_report_handlers::IncomeStatementQuery;
3use crate::infrastructure::audit::{AuditEventType, AuditLogEntry};
4use crate::infrastructure::web::{AppState, AuthenticatedUser};
5use actix_web::{get, web, HttpResponse, Responder};
6
7/// Generate balance sheet report for a specific building
8#[get("/buildings/{building_id}/reports/balance-sheet")]
9pub async fn generate_balance_sheet_for_building(
10    state: web::Data<AppState>,
11    user: AuthenticatedUser,
12    building_id: web::Path<uuid::Uuid>,
13) -> impl Responder {
14    if !matches!(user.role.as_str(), "accountant" | "superadmin" | "syndic") {
15        return HttpResponse::Forbidden().json(serde_json::json!({
16            "error": "Only accountants, syndics and superadmins can generate financial reports"
17        }));
18    }
19
20    let organization_id = match user.require_organization() {
21        Ok(org_id) => org_id,
22        Err(e) => {
23            return HttpResponse::Unauthorized().json(serde_json::json!({
24                "error": e.to_string()
25            }))
26        }
27    };
28
29    match state
30        .financial_report_use_cases
31        .generate_balance_sheet_for_building(organization_id, *building_id)
32        .await
33    {
34        Ok(report) => {
35            AuditLogEntry::new(
36                AuditEventType::ReportGenerated,
37                Some(user.user_id),
38                Some(organization_id),
39            )
40            .with_metadata(serde_json::json!({
41                "report_type": "balance_sheet",
42                "building_id": building_id.to_string()
43            }))
44            .log();
45
46            HttpResponse::Ok().json(report)
47        }
48        Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({
49            "error": err
50        })),
51    }
52}
53
54/// Generate income statement for a specific building
55#[get("/buildings/{building_id}/reports/income-statement")]
56pub async fn generate_income_statement_for_building(
57    state: web::Data<AppState>,
58    user: AuthenticatedUser,
59    building_id: web::Path<uuid::Uuid>,
60    query: web::Query<IncomeStatementQuery>,
61) -> impl Responder {
62    if !matches!(user.role.as_str(), "accountant" | "superadmin" | "syndic") {
63        return HttpResponse::Forbidden().json(serde_json::json!({
64            "error": "Only accountants, syndics and superadmins can generate financial reports"
65        }));
66    }
67
68    let organization_id = match user.require_organization() {
69        Ok(org_id) => org_id,
70        Err(e) => {
71            return HttpResponse::Unauthorized().json(serde_json::json!({
72                "error": e.to_string()
73            }))
74        }
75    };
76
77    let period_start = match chrono::DateTime::parse_from_rfc3339(&query.period_start) {
78        Ok(dt) => dt.with_timezone(&chrono::Utc),
79        Err(_) => {
80            return HttpResponse::BadRequest().json(serde_json::json!({
81                "error": "Invalid period_start format"
82            }))
83        }
84    };
85
86    let period_end = match chrono::DateTime::parse_from_rfc3339(&query.period_end) {
87        Ok(dt) => dt.with_timezone(&chrono::Utc),
88        Err(_) => {
89            return HttpResponse::BadRequest().json(serde_json::json!({
90                "error": "Invalid period_end format"
91            }))
92        }
93    };
94
95    if period_start >= period_end {
96        return HttpResponse::BadRequest().json(serde_json::json!({
97            "error": "period_start must be before period_end"
98        }));
99    }
100
101    match state
102        .financial_report_use_cases
103        .generate_income_statement_for_building(
104            organization_id,
105            *building_id,
106            period_start,
107            period_end,
108        )
109        .await
110    {
111        Ok(report) => {
112            AuditLogEntry::new(
113                AuditEventType::ReportGenerated,
114                Some(user.user_id),
115                Some(organization_id),
116            )
117            .with_metadata(serde_json::json!({
118                "report_type": "income_statement",
119                "building_id": building_id.to_string()
120            }))
121            .log();
122
123            HttpResponse::Ok().json(report)
124        }
125        Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({
126            "error": err
127        })),
128    }
129}