koprogo_api/application/use_cases/
unit_owner_use_cases.rs1use crate::application::ports::{OwnerRepository, UnitOwnerRepository, UnitRepository};
2use crate::domain::entities::UnitOwner;
3use chrono::Utc;
4use std::sync::Arc;
5use uuid::Uuid;
6
7#[cfg(test)]
8#[path = "unit_owner_use_cases_test.rs"]
9mod unit_owner_use_cases_test;
10
11pub struct UnitOwnerUseCases {
12 unit_owner_repository: Arc<dyn UnitOwnerRepository>,
13 unit_repository: Arc<dyn UnitRepository>,
14 owner_repository: Arc<dyn OwnerRepository>,
15}
16
17impl UnitOwnerUseCases {
18 pub fn new(
19 unit_owner_repository: Arc<dyn UnitOwnerRepository>,
20 unit_repository: Arc<dyn UnitRepository>,
21 owner_repository: Arc<dyn OwnerRepository>,
22 ) -> Self {
23 Self {
24 unit_owner_repository,
25 unit_repository,
26 owner_repository,
27 }
28 }
29
30 pub async fn add_owner_to_unit(
32 &self,
33 unit_id: Uuid,
34 owner_id: Uuid,
35 ownership_percentage: f64,
36 is_primary_contact: bool,
37 ) -> Result<UnitOwner, String> {
38 self.unit_repository
40 .find_by_id(unit_id)
41 .await?
42 .ok_or("Unit not found")?;
43
44 self.owner_repository
46 .find_by_id(owner_id)
47 .await?
48 .ok_or("Owner not found")?;
49
50 if let Some(_existing) = self
52 .unit_owner_repository
53 .find_active_by_unit_and_owner(unit_id, owner_id)
54 .await?
55 {
56 return Err("Owner is already active on this unit".to_string());
57 }
58
59 let current_total = self
61 .unit_owner_repository
62 .get_total_ownership_percentage(unit_id)
63 .await?;
64
65 if current_total + ownership_percentage > 1.0 {
66 return Err(format!(
67 "Total ownership would exceed 100% (current: {:.2}%, adding: {:.2}%)",
68 current_total * 100.0,
69 ownership_percentage * 100.0
70 ));
71 }
72
73 if is_primary_contact {
75 self.unset_all_primary_contacts(unit_id).await?;
76 }
77
78 let unit_owner =
80 UnitOwner::new(unit_id, owner_id, ownership_percentage, is_primary_contact)?;
81
82 self.unit_owner_repository.create(&unit_owner).await
83 }
84
85 pub async fn remove_owner_from_unit(
87 &self,
88 unit_id: Uuid,
89 owner_id: Uuid,
90 ) -> Result<UnitOwner, String> {
91 let mut unit_owner = self
93 .unit_owner_repository
94 .find_active_by_unit_and_owner(unit_id, owner_id)
95 .await?
96 .ok_or("Active unit-owner relationship not found")?;
97
98 unit_owner.end_ownership(Utc::now())?;
100
101 self.unit_owner_repository.update(&unit_owner).await
102 }
103
104 pub async fn update_ownership_percentage(
106 &self,
107 unit_owner_id: Uuid,
108 new_percentage: f64,
109 ) -> Result<UnitOwner, String> {
110 let mut unit_owner = self
112 .unit_owner_repository
113 .find_by_id(unit_owner_id)
114 .await?
115 .ok_or("Unit-owner relationship not found")?;
116
117 if !unit_owner.is_active() {
119 return Err("Cannot update percentage of ended ownership".to_string());
120 }
121
122 let current_total = self
124 .unit_owner_repository
125 .get_total_ownership_percentage(unit_owner.unit_id)
126 .await?;
127 let old_percentage = unit_owner.ownership_percentage;
128 let new_total = current_total - old_percentage + new_percentage;
129
130 if new_total > 1.0 {
131 return Err(format!(
132 "Total ownership would exceed 100% (current: {:.2}%, new total: {:.2}%)",
133 current_total * 100.0,
134 new_total * 100.0
135 ));
136 }
137
138 unit_owner.update_percentage(new_percentage)?;
140
141 self.unit_owner_repository.update(&unit_owner).await
142 }
143
144 pub async fn transfer_ownership(
146 &self,
147 from_owner_id: Uuid,
148 to_owner_id: Uuid,
149 unit_id: Uuid,
150 ) -> Result<(UnitOwner, UnitOwner), String> {
151 self.owner_repository
153 .find_by_id(from_owner_id)
154 .await?
155 .ok_or("Source owner not found")?;
156
157 self.owner_repository
158 .find_by_id(to_owner_id)
159 .await?
160 .ok_or("Target owner not found")?;
161
162 let mut from_relationship = self
164 .unit_owner_repository
165 .find_active_by_unit_and_owner(unit_id, from_owner_id)
166 .await?
167 .ok_or("Source owner does not own this unit")?;
168
169 if let Some(_existing) = self
171 .unit_owner_repository
172 .find_active_by_unit_and_owner(unit_id, to_owner_id)
173 .await?
174 {
175 return Err("Target owner already owns this unit".to_string());
176 }
177
178 let transfer_date = Utc::now();
180 from_relationship.end_ownership(transfer_date)?;
181
182 let to_relationship = UnitOwner::new(
184 unit_id,
185 to_owner_id,
186 from_relationship.ownership_percentage,
187 from_relationship.is_primary_contact,
188 )?;
189
190 let ended_relationship = self
192 .unit_owner_repository
193 .update(&from_relationship)
194 .await?;
195 let new_relationship = self.unit_owner_repository.create(&to_relationship).await?;
196
197 Ok((ended_relationship, new_relationship))
198 }
199
200 pub async fn get_unit_owners(&self, unit_id: Uuid) -> Result<Vec<UnitOwner>, String> {
202 self.unit_repository
204 .find_by_id(unit_id)
205 .await?
206 .ok_or("Unit not found")?;
207
208 self.unit_owner_repository
209 .find_current_owners_by_unit(unit_id)
210 .await
211 }
212
213 pub async fn get_owner_units(&self, owner_id: Uuid) -> Result<Vec<UnitOwner>, String> {
215 self.owner_repository
217 .find_by_id(owner_id)
218 .await?
219 .ok_or("Owner not found")?;
220
221 self.unit_owner_repository
222 .find_current_units_by_owner(owner_id)
223 .await
224 }
225
226 pub async fn get_unit_ownership_history(
228 &self,
229 unit_id: Uuid,
230 ) -> Result<Vec<UnitOwner>, String> {
231 self.unit_repository
233 .find_by_id(unit_id)
234 .await?
235 .ok_or("Unit not found")?;
236
237 self.unit_owner_repository
238 .find_all_owners_by_unit(unit_id)
239 .await
240 }
241
242 pub async fn get_owner_ownership_history(
244 &self,
245 owner_id: Uuid,
246 ) -> Result<Vec<UnitOwner>, String> {
247 self.owner_repository
249 .find_by_id(owner_id)
250 .await?
251 .ok_or("Owner not found")?;
252
253 self.unit_owner_repository
254 .find_all_units_by_owner(owner_id)
255 .await
256 }
257
258 pub async fn set_primary_contact(&self, unit_owner_id: Uuid) -> Result<UnitOwner, String> {
260 let mut unit_owner = self
262 .unit_owner_repository
263 .find_by_id(unit_owner_id)
264 .await?
265 .ok_or("Unit-owner relationship not found")?;
266
267 if !unit_owner.is_active() {
269 return Err("Cannot set primary contact for ended ownership".to_string());
270 }
271
272 self.unset_all_primary_contacts(unit_owner.unit_id).await?;
274
275 unit_owner.set_primary_contact(true);
277
278 self.unit_owner_repository.update(&unit_owner).await
279 }
280
281 pub async fn get_unit_owner(&self, id: Uuid) -> Result<Option<UnitOwner>, String> {
283 self.unit_owner_repository.find_by_id(id).await
284 }
285
286 pub async fn has_active_owners(&self, unit_id: Uuid) -> Result<bool, String> {
288 self.unit_owner_repository.has_active_owners(unit_id).await
289 }
290
291 pub async fn get_total_ownership_percentage(&self, unit_id: Uuid) -> Result<f64, String> {
293 self.unit_owner_repository
294 .get_total_ownership_percentage(unit_id)
295 .await
296 }
297
298 async fn unset_all_primary_contacts(&self, unit_id: Uuid) -> Result<(), String> {
300 let current_owners = self
301 .unit_owner_repository
302 .find_current_owners_by_unit(unit_id)
303 .await?;
304
305 for mut owner in current_owners {
306 if owner.is_primary_contact {
307 owner.set_primary_contact(false);
308 self.unit_owner_repository.update(&owner).await?;
309 }
310 }
311
312 Ok(())
313 }
314}