1use crate::application::dto::{OwnerFilters, PageRequest};
2use crate::application::ports::OwnerRepository;
3use crate::domain::entities::Owner;
4use crate::infrastructure::database::pool::DbPool;
5use async_trait::async_trait;
6use sqlx::Row;
7use uuid::Uuid;
8
9pub struct PostgresOwnerRepository {
10 pool: DbPool,
11}
12
13impl PostgresOwnerRepository {
14 pub fn new(pool: DbPool) -> Self {
15 Self { pool }
16 }
17}
18
19#[async_trait]
20impl OwnerRepository for PostgresOwnerRepository {
21 async fn create(&self, owner: &Owner) -> Result<Owner, String> {
22 sqlx::query(
23 r#"
24 INSERT INTO owners (id, organization_id, user_id, first_name, last_name, email, phone, address, city, postal_code, country, created_at, updated_at)
25 VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
26 "#,
27 )
28 .bind(owner.id)
29 .bind(owner.organization_id)
30 .bind(owner.user_id)
31 .bind(&owner.first_name)
32 .bind(&owner.last_name)
33 .bind(&owner.email)
34 .bind(&owner.phone)
35 .bind(&owner.address)
36 .bind(&owner.city)
37 .bind(&owner.postal_code)
38 .bind(&owner.country)
39 .bind(owner.created_at)
40 .bind(owner.updated_at)
41 .execute(&self.pool)
42 .await
43 .map_err(|e| format!("Database error: {}", e))?;
44
45 Ok(owner.clone())
46 }
47
48 async fn find_by_id(&self, id: Uuid) -> Result<Option<Owner>, String> {
49 let row = sqlx::query(
50 r#"
51 SELECT id, organization_id, user_id, first_name, last_name, email, phone, address, city, postal_code, country, created_at, updated_at
52 FROM owners
53 WHERE id = $1
54 "#,
55 )
56 .bind(id)
57 .fetch_optional(&self.pool)
58 .await
59 .map_err(|e| format!("Database error: {}", e))?;
60
61 Ok(row.map(|row| Owner {
62 id: row.get("id"),
63 organization_id: row.get("organization_id"),
64 user_id: row.get("user_id"),
65 first_name: row.get("first_name"),
66 last_name: row.get("last_name"),
67 email: row.get("email"),
68 phone: row.get("phone"),
69 address: row.get("address"),
70 city: row.get("city"),
71 postal_code: row.get("postal_code"),
72 country: row.get("country"),
73 created_at: row.get("created_at"),
74 updated_at: row.get("updated_at"),
75 }))
76 }
77
78 async fn find_by_user_id(&self, user_id: Uuid) -> Result<Option<Owner>, String> {
79 let row = sqlx::query(
80 r#"
81 SELECT id, organization_id, user_id, first_name, last_name, email, phone, address, city, postal_code, country, created_at, updated_at
82 FROM owners
83 WHERE user_id = $1
84 "#,
85 )
86 .bind(user_id)
87 .fetch_optional(&self.pool)
88 .await
89 .map_err(|e| format!("Database error: {}", e))?;
90
91 Ok(row.map(|row| Owner {
92 id: row.get("id"),
93 organization_id: row.get("organization_id"),
94 user_id: row.get("user_id"),
95 first_name: row.get("first_name"),
96 last_name: row.get("last_name"),
97 email: row.get("email"),
98 phone: row.get("phone"),
99 address: row.get("address"),
100 city: row.get("city"),
101 postal_code: row.get("postal_code"),
102 country: row.get("country"),
103 created_at: row.get("created_at"),
104 updated_at: row.get("updated_at"),
105 }))
106 }
107
108 async fn find_by_user_id_and_organization(
109 &self,
110 user_id: Uuid,
111 organization_id: Uuid,
112 ) -> Result<Option<Owner>, String> {
113 let row = sqlx::query(
114 r#"
115 SELECT id, organization_id, user_id, first_name, last_name, email, phone, address, city, postal_code, country, created_at, updated_at
116 FROM owners
117 WHERE user_id = $1 AND organization_id = $2
118 "#,
119 )
120 .bind(user_id)
121 .bind(organization_id)
122 .fetch_optional(&self.pool)
123 .await
124 .map_err(|e| format!("Database error: {}", e))?;
125
126 Ok(row.map(|row| Owner {
127 id: row.get("id"),
128 organization_id: row.get("organization_id"),
129 user_id: row.get("user_id"),
130 first_name: row.get("first_name"),
131 last_name: row.get("last_name"),
132 email: row.get("email"),
133 phone: row.get("phone"),
134 address: row.get("address"),
135 city: row.get("city"),
136 postal_code: row.get("postal_code"),
137 country: row.get("country"),
138 created_at: row.get("created_at"),
139 updated_at: row.get("updated_at"),
140 }))
141 }
142
143 async fn find_by_email(&self, email: &str) -> Result<Option<Owner>, String> {
144 let row = sqlx::query(
145 r#"
146 SELECT id, organization_id, user_id, first_name, last_name, email, phone, address, city, postal_code, country, created_at, updated_at
147 FROM owners
148 WHERE email = $1
149 "#,
150 )
151 .bind(email)
152 .fetch_optional(&self.pool)
153 .await
154 .map_err(|e| format!("Database error: {}", e))?;
155
156 Ok(row.map(|row| Owner {
157 id: row.get("id"),
158 organization_id: row.get("organization_id"),
159 user_id: row.get("user_id"),
160 first_name: row.get("first_name"),
161 last_name: row.get("last_name"),
162 email: row.get("email"),
163 phone: row.get("phone"),
164 address: row.get("address"),
165 city: row.get("city"),
166 postal_code: row.get("postal_code"),
167 country: row.get("country"),
168 created_at: row.get("created_at"),
169 updated_at: row.get("updated_at"),
170 }))
171 }
172
173 async fn find_all(&self) -> Result<Vec<Owner>, String> {
174 let rows = sqlx::query(
175 r#"
176 SELECT id, organization_id, user_id, first_name, last_name, email, phone, address, city, postal_code, country, created_at, updated_at
177 FROM owners
178 ORDER BY last_name, first_name
179 "#,
180 )
181 .fetch_all(&self.pool)
182 .await
183 .map_err(|e| format!("Database error: {}", e))?;
184
185 Ok(rows
186 .iter()
187 .map(|row| Owner {
188 id: row.get("id"),
189 organization_id: row.get("organization_id"),
190 user_id: row.get("user_id"),
191 first_name: row.get("first_name"),
192 last_name: row.get("last_name"),
193 email: row.get("email"),
194 phone: row.get("phone"),
195 address: row.get("address"),
196 city: row.get("city"),
197 postal_code: row.get("postal_code"),
198 country: row.get("country"),
199 created_at: row.get("created_at"),
200 updated_at: row.get("updated_at"),
201 })
202 .collect())
203 }
204
205 async fn find_all_paginated(
206 &self,
207 page_request: &PageRequest,
208 filters: &OwnerFilters,
209 ) -> Result<(Vec<Owner>, i64), String> {
210 page_request.validate()?;
212
213 let mut where_clauses = Vec::new();
215 let mut param_count = 0;
216
217 if filters.organization_id.is_some() {
241 param_count += 1;
242 where_clauses.push(format!(
243 "(EXISTS (SELECT 1 FROM unit_owners uo \
244 JOIN units u ON u.id = uo.unit_id \
245 JOIN buildings b ON b.id = u.building_id \
246 JOIN acps a ON a.id = b.acp_id \
247 WHERE uo.owner_id = owners.id AND a.organization_id = ${p}) \
248 OR (NOT EXISTS (SELECT 1 FROM unit_owners uo WHERE uo.owner_id = owners.id) \
249 AND owners.organization_id = ${p}))",
250 p = param_count
251 ));
252 }
253
254 if filters.email.is_some() {
255 param_count += 1;
256 where_clauses.push(format!("email ILIKE ${}", param_count));
257 }
258
259 if filters.phone.is_some() {
260 param_count += 1;
261 where_clauses.push(format!("phone ILIKE ${}", param_count));
262 }
263
264 if filters.last_name.is_some() {
265 param_count += 1;
266 where_clauses.push(format!("last_name ILIKE ${}", param_count));
267 }
268
269 if filters.first_name.is_some() {
270 param_count += 1;
271 where_clauses.push(format!("first_name ILIKE ${}", param_count));
272 }
273
274 let where_clause = if where_clauses.is_empty() {
275 String::new()
276 } else {
277 format!("WHERE {}", where_clauses.join(" AND "))
278 };
279
280 let allowed_columns = ["last_name", "first_name", "email", "created_at"];
282 let sort_column = page_request.sort_by.as_deref().unwrap_or("last_name");
283
284 if !allowed_columns.contains(&sort_column) {
285 return Err(format!("Invalid sort column: {}", sort_column));
286 }
287
288 let count_query = format!("SELECT COUNT(*) FROM owners {}", where_clause);
290 let mut count_query = sqlx::query_scalar::<_, i64>(&count_query);
291
292 if let Some(organization_id) = &filters.organization_id {
293 count_query = count_query.bind(organization_id);
294 }
295 if let Some(email) = &filters.email {
296 count_query = count_query.bind(format!("%{}%", email));
297 }
298 if let Some(phone) = &filters.phone {
299 count_query = count_query.bind(format!("%{}%", phone));
300 }
301 if let Some(last_name) = &filters.last_name {
302 count_query = count_query.bind(format!("%{}%", last_name));
303 }
304 if let Some(first_name) = &filters.first_name {
305 count_query = count_query.bind(format!("%{}%", first_name));
306 }
307
308 let total_items = count_query
309 .fetch_one(&self.pool)
310 .await
311 .map_err(|e| format!("Database error: {}", e))?;
312
313 param_count += 1;
315 let limit_param = param_count;
316 param_count += 1;
317 let offset_param = param_count;
318
319 let data_query = format!(
320 "SELECT id, organization_id, user_id, first_name, last_name, email, phone, address, city, postal_code, country, created_at, updated_at \
321 FROM owners {} ORDER BY {} {} LIMIT ${} OFFSET ${}",
322 where_clause,
323 sort_column,
324 page_request.order.to_sql(),
325 limit_param,
326 offset_param
327 );
328
329 let mut data_query = sqlx::query(&data_query);
330
331 if let Some(organization_id) = &filters.organization_id {
332 data_query = data_query.bind(organization_id);
333 }
334 if let Some(email) = &filters.email {
335 data_query = data_query.bind(format!("%{}%", email));
336 }
337 if let Some(phone) = &filters.phone {
338 data_query = data_query.bind(format!("%{}%", phone));
339 }
340 if let Some(last_name) = &filters.last_name {
341 data_query = data_query.bind(format!("%{}%", last_name));
342 }
343 if let Some(first_name) = &filters.first_name {
344 data_query = data_query.bind(format!("%{}%", first_name));
345 }
346
347 data_query = data_query
348 .bind(page_request.limit())
349 .bind(page_request.offset());
350
351 let rows = data_query
352 .fetch_all(&self.pool)
353 .await
354 .map_err(|e| format!("Database error: {}", e))?;
355
356 let owners: Vec<Owner> = rows
357 .iter()
358 .map(|row| Owner {
359 id: row.get("id"),
360 organization_id: row.get("organization_id"),
361 user_id: row.get("user_id"),
362 first_name: row.get("first_name"),
363 last_name: row.get("last_name"),
364 email: row.get("email"),
365 phone: row.get("phone"),
366 address: row.get("address"),
367 city: row.get("city"),
368 postal_code: row.get("postal_code"),
369 country: row.get("country"),
370 created_at: row.get("created_at"),
371 updated_at: row.get("updated_at"),
372 })
373 .collect();
374
375 Ok((owners, total_items))
376 }
377
378 async fn update(&self, owner: &Owner) -> Result<Owner, String> {
379 sqlx::query(
380 r#"
381 UPDATE owners
382 SET first_name = $2, last_name = $3, email = $4, phone = $5, address = $6, city = $7, postal_code = $8, country = $9, updated_at = $10
383 WHERE id = $1
384 "#,
385 )
386 .bind(owner.id)
387 .bind(&owner.first_name)
388 .bind(&owner.last_name)
389 .bind(&owner.email)
390 .bind(&owner.phone)
391 .bind(&owner.address)
392 .bind(&owner.city)
393 .bind(&owner.postal_code)
394 .bind(&owner.country)
395 .bind(owner.updated_at)
396 .execute(&self.pool)
397 .await
398 .map_err(|e| format!("Database error: {}", e))?;
399
400 Ok(owner.clone())
401 }
402
403 async fn delete(&self, id: Uuid) -> Result<bool, String> {
404 let result = sqlx::query("DELETE FROM owners WHERE id = $1")
405 .bind(id)
406 .execute(&self.pool)
407 .await
408 .map_err(|e| format!("Database error: {}", e))?;
409
410 Ok(result.rows_affected() > 0)
411 }
412
413 async fn set_user_link(&self, owner_id: Uuid, user_id: Option<Uuid>) -> Result<bool, String> {
414 let result =
415 sqlx::query("UPDATE owners SET user_id = $1, updated_at = NOW() WHERE id = $2")
416 .bind(user_id)
417 .bind(owner_id)
418 .execute(&self.pool)
419 .await
420 .map_err(|e| format!("Database error: {}", e))?;
421
422 Ok(result.rows_affected() > 0)
423 }
424}