koprogo_api/domain/entities/
unit.rs1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use uuid::Uuid;
4
5#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
7pub enum UnitType {
8 Apartment,
9 Parking,
10 Cellar,
11 Commercial,
12 Other,
13}
14
15#[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, pub quota: f64, 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 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}