koprogo_api/infrastructure/database/repositories/
owner_contribution_repository_impl.rs1use crate::application::ports::OwnerContributionRepository;
2use crate::domain::entities::{
3 ContributionPaymentMethod, ContributionPaymentStatus, ContributionType, OwnerContribution,
4};
5use async_trait::async_trait;
6use sqlx::PgPool;
7use uuid::Uuid;
8
9pub struct PostgresOwnerContributionRepository {
10 pool: PgPool,
11}
12
13impl PostgresOwnerContributionRepository {
14 pub fn new(pool: PgPool) -> Self {
15 Self { pool }
16 }
17
18 fn row_to_entity(row: sqlx::postgres::PgRow) -> Result<OwnerContribution, String> {
20 use sqlx::Row;
21
22 let contribution_type_str: String = row.get("contribution_type");
23 let contribution_type = match contribution_type_str.as_str() {
24 "regular" => ContributionType::Regular,
25 "extraordinary" => ContributionType::Extraordinary,
26 "advance" => ContributionType::Advance,
27 "adjustment" => ContributionType::Adjustment,
28 _ => {
29 return Err(format!(
30 "Unknown contribution type: {}",
31 contribution_type_str
32 ))
33 }
34 };
35
36 let payment_status_str: String = row.get("payment_status");
37 let payment_status = match payment_status_str.as_str() {
38 "pending" => ContributionPaymentStatus::Pending,
39 "paid" => ContributionPaymentStatus::Paid,
40 "partial" => ContributionPaymentStatus::Partial,
41 "cancelled" => ContributionPaymentStatus::Cancelled,
42 _ => return Err(format!("Unknown payment status: {}", payment_status_str)),
43 };
44
45 let payment_method: Option<String> = row.get("payment_method");
46 let payment_method = payment_method
47 .map(|pm| match pm.as_str() {
48 "bank_transfer" => Ok(ContributionPaymentMethod::BankTransfer),
49 "cash" => Ok(ContributionPaymentMethod::Cash),
50 "check" => Ok(ContributionPaymentMethod::Check),
51 "domiciliation" => Ok(ContributionPaymentMethod::Domiciliation),
52 _ => Err(format!("Unknown payment method: {}", pm)),
53 })
54 .transpose()?;
55
56 let amount: sqlx::types::Decimal = row.get("amount");
57 let amount = amount
58 .to_string()
59 .parse::<f64>()
60 .map_err(|e| format!("Failed to parse amount: {}", e))?;
61
62 Ok(OwnerContribution {
63 id: row.get("id"),
64 organization_id: row.get("organization_id"),
65 owner_id: row.get("owner_id"),
66 unit_id: row.get("unit_id"),
67 description: row.get("description"),
68 amount,
69 account_code: row.get("account_code"),
70 contribution_type,
71 contribution_date: row.get("contribution_date"),
72 payment_date: row.get("payment_date"),
73 payment_method,
74 payment_reference: row.get("payment_reference"),
75 payment_status,
76 call_for_funds_id: row.get("call_for_funds_id"),
77 notes: row.get("notes"),
78 created_at: row.get("created_at"),
79 updated_at: row.get("updated_at"),
80 created_by: row.get("created_by"),
81 })
82 }
83}
84
85#[async_trait]
86impl OwnerContributionRepository for PostgresOwnerContributionRepository {
87 async fn create(&self, contribution: &OwnerContribution) -> Result<OwnerContribution, String> {
88 let contribution_type = match contribution.contribution_type {
89 ContributionType::Regular => "regular",
90 ContributionType::Extraordinary => "extraordinary",
91 ContributionType::Advance => "advance",
92 ContributionType::Adjustment => "adjustment",
93 };
94
95 let payment_status = match contribution.payment_status {
96 ContributionPaymentStatus::Pending => "pending",
97 ContributionPaymentStatus::Paid => "paid",
98 ContributionPaymentStatus::Partial => "partial",
99 ContributionPaymentStatus::Cancelled => "cancelled",
100 };
101
102 let payment_method = contribution.payment_method.as_ref().map(|pm| match pm {
103 ContributionPaymentMethod::BankTransfer => "bank_transfer",
104 ContributionPaymentMethod::Cash => "cash",
105 ContributionPaymentMethod::Check => "check",
106 ContributionPaymentMethod::Domiciliation => "domiciliation",
107 });
108
109 let row = sqlx::query(
110 r#"
111 INSERT INTO owner_contributions (
112 id, organization_id, owner_id, unit_id,
113 description, amount, account_code,
114 contribution_type, contribution_date, payment_date,
115 payment_method, payment_reference, payment_status,
116 call_for_funds_id, notes, created_at, updated_at, created_by
117 ) VALUES (
118 $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18
119 )
120 RETURNING *
121 "#,
122 )
123 .bind(contribution.id)
124 .bind(contribution.organization_id)
125 .bind(contribution.owner_id)
126 .bind(contribution.unit_id)
127 .bind(&contribution.description)
128 .bind(contribution.amount)
129 .bind(&contribution.account_code)
130 .bind(contribution_type)
131 .bind(contribution.contribution_date)
132 .bind(contribution.payment_date)
133 .bind(payment_method)
134 .bind(&contribution.payment_reference)
135 .bind(payment_status)
136 .bind(contribution.call_for_funds_id)
137 .bind(&contribution.notes)
138 .bind(contribution.created_at)
139 .bind(contribution.updated_at)
140 .bind(contribution.created_by)
141 .fetch_one(&self.pool)
142 .await
143 .map_err(|e| format!("Failed to create owner contribution: {}", e))?;
144
145 Self::row_to_entity(row)
146 }
147
148 async fn find_by_id(&self, id: Uuid) -> Result<Option<OwnerContribution>, String> {
149 let row = sqlx::query("SELECT * FROM owner_contributions WHERE id = $1")
150 .bind(id)
151 .fetch_optional(&self.pool)
152 .await
153 .map_err(|e| format!("Failed to find owner contribution by id: {}", e))?;
154
155 match row {
156 Some(r) => Ok(Some(Self::row_to_entity(r)?)),
157 None => Ok(None),
158 }
159 }
160
161 async fn find_by_organization(
162 &self,
163 organization_id: Uuid,
164 ) -> Result<Vec<OwnerContribution>, String> {
165 let rows = sqlx::query(
166 "SELECT * FROM owner_contributions WHERE organization_id = $1 ORDER BY contribution_date DESC",
167 )
168 .bind(organization_id)
169 .fetch_all(&self.pool)
170 .await
171 .map_err(|e| format!("Failed to find owner contributions by organization: {}", e))?;
172
173 rows.into_iter().map(Self::row_to_entity).collect()
174 }
175
176 async fn find_by_owner(&self, owner_id: Uuid) -> Result<Vec<OwnerContribution>, String> {
177 let rows = sqlx::query(
178 "SELECT * FROM owner_contributions WHERE owner_id = $1 ORDER BY contribution_date DESC",
179 )
180 .bind(owner_id)
181 .fetch_all(&self.pool)
182 .await
183 .map_err(|e| format!("Failed to find owner contributions by owner: {}", e))?;
184
185 rows.into_iter().map(Self::row_to_entity).collect()
186 }
187
188 async fn update(&self, contribution: &OwnerContribution) -> Result<OwnerContribution, String> {
189 let contribution_type = match contribution.contribution_type {
190 ContributionType::Regular => "regular",
191 ContributionType::Extraordinary => "extraordinary",
192 ContributionType::Advance => "advance",
193 ContributionType::Adjustment => "adjustment",
194 };
195
196 let payment_status = match contribution.payment_status {
197 ContributionPaymentStatus::Pending => "pending",
198 ContributionPaymentStatus::Paid => "paid",
199 ContributionPaymentStatus::Partial => "partial",
200 ContributionPaymentStatus::Cancelled => "cancelled",
201 };
202
203 let payment_method = contribution.payment_method.as_ref().map(|pm| match pm {
204 ContributionPaymentMethod::BankTransfer => "bank_transfer",
205 ContributionPaymentMethod::Cash => "cash",
206 ContributionPaymentMethod::Check => "check",
207 ContributionPaymentMethod::Domiciliation => "domiciliation",
208 });
209
210 let row = sqlx::query(
211 r#"
212 UPDATE owner_contributions SET
213 organization_id = $2,
214 owner_id = $3,
215 unit_id = $4,
216 description = $5,
217 amount = $6,
218 account_code = $7,
219 contribution_type = $8,
220 contribution_date = $9,
221 payment_date = $10,
222 payment_method = $11,
223 payment_reference = $12,
224 payment_status = $13,
225 notes = $14,
226 updated_at = $15,
227 created_by = $16
228 WHERE id = $1
229 RETURNING *
230 "#,
231 )
232 .bind(contribution.id)
233 .bind(contribution.organization_id)
234 .bind(contribution.owner_id)
235 .bind(contribution.unit_id)
236 .bind(&contribution.description)
237 .bind(contribution.amount)
238 .bind(&contribution.account_code)
239 .bind(contribution_type)
240 .bind(contribution.contribution_date)
241 .bind(contribution.payment_date)
242 .bind(payment_method)
243 .bind(&contribution.payment_reference)
244 .bind(payment_status)
245 .bind(&contribution.notes)
246 .bind(contribution.updated_at)
247 .bind(contribution.created_by)
248 .fetch_one(&self.pool)
249 .await
250 .map_err(|e| format!("Failed to update owner contribution: {}", e))?;
251
252 Self::row_to_entity(row)
253 }
254}