Skip to main content

koprogo_api/application/dto/
building_dto.rs

1use serde::{Deserialize, Serialize};
2use validator::Validate;
3
4#[derive(Debug, Serialize, Deserialize, Validate, Clone, utoipa::ToSchema)]
5pub struct CreateBuildingDto {
6    /// Story 1.2 — FK vers `acps.id` (anciennement `organization_id`).
7    /// La migration 040000 a DROP la colonne ; le scoping org se fait
8    /// désormais via `acps.organization_id`.
9    pub acp_id: String,
10
11    #[validate(length(min = 1, message = "Name cannot be empty"))]
12    pub name: String,
13
14    #[validate(length(min = 1))]
15    pub address: String,
16
17    #[validate(length(min = 1))]
18    pub city: String,
19
20    #[validate(length(min = 1))]
21    pub postal_code: String,
22
23    #[validate(length(min = 1))]
24    pub country: String,
25
26    #[validate(range(min = 1, message = "Total units must be greater than 0"))]
27    pub total_units: i32,
28
29    #[validate(range(min = 1, message = "Total tantiemes must be greater than 0"))]
30    pub total_tantiemes: Option<i32>,
31
32    pub construction_year: Option<i32>,
33}
34
35#[derive(Debug, Serialize, Deserialize, Validate, Clone, utoipa::ToSchema)]
36pub struct UpdateBuildingDto {
37    /// Story 1.2 — Réaffectation de l'ACP parente (SuperAdmin uniquement).
38    pub acp_id: Option<String>,
39
40    #[validate(length(min = 1))]
41    pub name: String,
42
43    #[validate(length(min = 1))]
44    pub address: String,
45
46    #[validate(length(min = 1))]
47    pub city: String,
48
49    #[validate(length(min = 1))]
50    pub postal_code: String,
51
52    #[validate(length(min = 1))]
53    pub country: String,
54
55    #[validate(range(min = 1, message = "Total units must be greater than 0"))]
56    pub total_units: i32,
57
58    #[validate(range(min = 1, message = "Total tantiemes must be greater than 0"))]
59    pub total_tantiemes: Option<i32>,
60
61    pub construction_year: Option<i32>,
62}
63
64#[derive(Debug, Serialize, utoipa::ToSchema)]
65pub struct BuildingResponseDto {
66    pub id: String,
67    /// Story 1.2 — FK vers `acps.id` (anciennement `organization_id`).
68    pub acp_id: String,
69    pub name: String,
70    pub address: String,
71    pub city: String,
72    pub postal_code: String,
73    pub country: String,
74    pub total_units: i32,
75    pub total_tantiemes: i32,
76    pub construction_year: Option<i32>,
77    pub created_at: String,
78    pub updated_at: String,
79
80    // Story 1.4 — FR11/FR12/FR23 : conformité immeuble exposée par défaut.
81    // `quota_sum` et `quota_delta` sont sérialisés en **string** Decimal-équivalent
82    // (cf. ADR-0007 + mémoire `no-f64-in-money`) — jamais f64/NaN côté API.
83    /// Nombre réel de `units` rattachées (COUNT(*) côté repo, JOIN units).
84    #[serde(default)]
85    pub units_count: i32,
86    /// Somme des quotas (Decimal-as-string, ex: "1000" / "999.5" / "0").
87    #[serde(default)]
88    pub quota_sum: String,
89    /// `units_count == total_units && quota_sum == 1000` (Decimal strict,
90    /// aucune tolérance d'arrondi).
91    #[serde(default)]
92    pub is_conformant: bool,
93    /// Delta vs 1000 (positif = surplus, négatif = manque) — pour message UX.
94    #[serde(default)]
95    pub quota_delta: String,
96}