koprogo_api/domain/entities/
unit.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use uuid::Uuid;
4
5/// Type de lot (appartement, cave, parking, etc.)
6#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
7pub enum UnitType {
8    Apartment,
9    Parking,
10    Cellar,
11    Commercial,
12    Other,
13}
14
15/// Représente un lot dans la copropriété
16#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
17pub struct Unit {
18    pub id: Uuid,
19    pub organization_id: Uuid,
20    pub building_id: Uuid,
21    pub unit_number: String,
22    pub unit_type: UnitType,
23    pub floor: Option<i32>,
24    pub surface_area: f64, // en m²
25    pub quota: f64,        // Quote-part en millièmes
26    pub owner_id: Option<Uuid>,
27    pub created_at: DateTime<Utc>,
28    pub updated_at: DateTime<Utc>,
29}
30
31impl Unit {
32    pub fn new(
33        organization_id: Uuid,
34        building_id: Uuid,
35        unit_number: String,
36        unit_type: UnitType,
37        floor: Option<i32>,
38        surface_area: f64,
39        quota: f64,
40    ) -> Result<Self, String> {
41        if unit_number.is_empty() {
42            return Err("Unit number cannot be empty".to_string());
43        }
44        if surface_area <= 0.0 {
45            return Err("Surface area must be greater than 0".to_string());
46        }
47        // Validate shares (tantièmes) according to Art. 577-2 §4 Code Civil belge
48        // Shares must be positive and typically don't exceed 1000 (building tantiemes)
49        // Individual unit shares must sum to building.total_shares (usually 1000)
50        if quota <= 0.0 || quota > 1000.0 {
51            return Err("Quota (shares) must be between 0 (exclusive) and 1000 (inclusive), representing building tantièmes".to_string());
52        }
53
54        let now = Utc::now();
55        Ok(Self {
56            id: Uuid::new_v4(),
57            organization_id,
58            building_id,
59            unit_number,
60            unit_type,
61            floor,
62            surface_area,
63            quota,
64            owner_id: None,
65            created_at: now,
66            updated_at: now,
67        })
68    }
69
70    pub fn validate_update(&self) -> Result<(), String> {
71        if self.unit_number.is_empty() {
72            return Err("Unit number cannot be empty".to_string());
73        }
74        if self.surface_area <= 0.0 {
75            return Err("Surface area must be greater than 0".to_string());
76        }
77        if self.quota <= 0.0 || self.quota > 1000.0 {
78            return Err("Quota must be between 0 and 1000".to_string());
79        }
80        Ok(())
81    }
82
83    pub fn assign_owner(&mut self, owner_id: Uuid) {
84        self.owner_id = Some(owner_id);
85        self.updated_at = Utc::now();
86    }
87
88    pub fn remove_owner(&mut self) {
89        self.owner_id = None;
90        self.updated_at = Utc::now();
91    }
92}
93
94#[cfg(test)]
95mod tests {
96    use super::*;
97
98    #[test]
99    fn test_create_unit_success() {
100        let org_id = Uuid::new_v4();
101        let building_id = Uuid::new_v4();
102        let unit = Unit::new(
103            org_id,
104            building_id,
105            "A101".to_string(),
106            UnitType::Apartment,
107            Some(1),
108            75.5,
109            50.0,
110        );
111
112        assert!(unit.is_ok());
113        let unit = unit.unwrap();
114        assert_eq!(unit.organization_id, org_id);
115        assert_eq!(unit.unit_number, "A101");
116        assert_eq!(unit.surface_area, 75.5);
117    }
118
119    #[test]
120    fn test_create_unit_invalid_surface_fails() {
121        let org_id = Uuid::new_v4();
122        let building_id = Uuid::new_v4();
123        let unit = Unit::new(
124            org_id,
125            building_id,
126            "A101".to_string(),
127            UnitType::Apartment,
128            Some(1),
129            0.0,
130            50.0,
131        );
132
133        assert!(unit.is_err());
134    }
135
136    #[test]
137    fn test_assign_owner() {
138        let org_id = Uuid::new_v4();
139        let building_id = Uuid::new_v4();
140        let mut unit = Unit::new(
141            org_id,
142            building_id,
143            "A101".to_string(),
144            UnitType::Apartment,
145            Some(1),
146            75.5,
147            50.0,
148        )
149        .unwrap();
150
151        let owner_id = Uuid::new_v4();
152        unit.assign_owner(owner_id);
153
154        assert_eq!(unit.owner_id, Some(owner_id));
155    }
156}