backbone_banking/domain/entity/
bank_statement_import.rs1use chrono::{DateTime, Utc, NaiveDate};
2use serde::{Deserialize, Serialize};
3use sqlx::FromRow;
4use uuid::Uuid;
5use rust_decimal::Decimal;
6
7use super::SourceFormat;
8use super::ImportStatus;
9use super::AuditMetadata;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
13#[serde(transparent)]
14pub struct BankStatementImportId(pub Uuid);
15
16impl BankStatementImportId {
17 pub fn new(id: Uuid) -> Self { Self(id) }
18 pub fn generate() -> Self { Self(Uuid::new_v4()) }
19 pub fn into_inner(self) -> Uuid { self.0 }
20}
21
22impl std::fmt::Display for BankStatementImportId {
23 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24 write!(f, "{}", self.0)
25 }
26}
27
28impl std::str::FromStr for BankStatementImportId {
29 type Err = uuid::Error;
30 fn from_str(s: &str) -> Result<Self, Self::Err> {
31 Ok(Self(Uuid::parse_str(s)?))
32 }
33}
34
35impl From<Uuid> for BankStatementImportId {
36 fn from(id: Uuid) -> Self { Self(id) }
37}
38
39impl From<BankStatementImportId> for Uuid {
40 fn from(id: BankStatementImportId) -> Self { id.0 }
41}
42
43impl AsRef<Uuid> for BankStatementImportId {
44 fn as_ref(&self) -> &Uuid { &self.0 }
45}
46
47impl std::ops::Deref for BankStatementImportId {
48 type Target = Uuid;
49 fn deref(&self) -> &Self::Target { &self.0 }
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
53pub struct BankStatementImport {
54 pub id: Uuid,
55 pub bank_account_id: Uuid,
56 pub source_format: SourceFormat,
57 pub statement_period_start: NaiveDate,
58 pub statement_period_end: NaiveDate,
59 pub opening_balance: Decimal,
60 pub closing_balance: Decimal,
61 pub file_ref: Option<String>,
62 pub status: ImportStatus,
63 pub row_count: i32,
64 #[serde(default)]
65 #[sqlx(json)]
66 pub metadata: AuditMetadata,
67}
68
69impl BankStatementImport {
70 pub fn builder() -> BankStatementImportBuilder {
72 <BankStatementImportBuilder as Default>::default()
73 }
74
75 pub fn new(bank_account_id: Uuid, source_format: SourceFormat, statement_period_start: NaiveDate, statement_period_end: NaiveDate, opening_balance: Decimal, closing_balance: Decimal, status: ImportStatus, row_count: i32) -> Self {
77 Self {
78 id: Uuid::new_v4(),
79 bank_account_id,
80 source_format,
81 statement_period_start,
82 statement_period_end,
83 opening_balance,
84 closing_balance,
85 file_ref: None,
86 status,
87 row_count,
88 metadata: AuditMetadata::default(),
89 }
90 }
91
92 pub fn id(&self) -> &Uuid {
94 &self.id
95 }
96
97 pub fn typed_id(&self) -> BankStatementImportId {
99 BankStatementImportId(self.id)
100 }
101
102 pub fn created_at(&self) -> Option<&DateTime<Utc>> {
104 self.metadata.created_at.as_ref()
105 }
106
107 pub fn updated_at(&self) -> Option<&DateTime<Utc>> {
109 self.metadata.updated_at.as_ref()
110 }
111
112 pub fn is_deleted(&self) -> bool {
114 self.metadata.deleted_at.is_some()
115 }
116
117 pub fn is_active(&self) -> bool {
119 self.metadata.deleted_at.is_none()
120 }
121
122 pub fn deleted_at(&self) -> Option<&DateTime<Utc>> {
124 self.metadata.deleted_at.as_ref()
125 }
126
127 pub fn created_by(&self) -> Option<&Uuid> {
129 self.metadata.created_by.as_ref()
130 }
131
132 pub fn updated_by(&self) -> Option<&Uuid> {
134 self.metadata.updated_by.as_ref()
135 }
136
137 pub fn deleted_by(&self) -> Option<&Uuid> {
139 self.metadata.deleted_by.as_ref()
140 }
141
142 pub fn status(&self) -> &ImportStatus {
144 &self.status
145 }
146
147
148 pub fn with_file_ref(mut self, value: String) -> Self {
154 self.file_ref = Some(value);
155 self
156 }
157
158 pub fn apply_patch(&mut self, fields: std::collections::HashMap<String, serde_json::Value>) {
164 for (key, value) in fields {
165 match key.as_str() {
166 "bank_account_id" => {
167 if let Ok(v) = serde_json::from_value(value) { self.bank_account_id = v; }
168 }
169 "source_format" => {
170 if let Ok(v) = serde_json::from_value(value) { self.source_format = v; }
171 }
172 "statement_period_start" => {
173 if let Ok(v) = serde_json::from_value(value) { self.statement_period_start = v; }
174 }
175 "statement_period_end" => {
176 if let Ok(v) = serde_json::from_value(value) { self.statement_period_end = v; }
177 }
178 "opening_balance" => {
179 if let Ok(v) = serde_json::from_value(value) { self.opening_balance = v; }
180 }
181 "closing_balance" => {
182 if let Ok(v) = serde_json::from_value(value) { self.closing_balance = v; }
183 }
184 "file_ref" => {
185 if let Ok(v) = serde_json::from_value(value) { self.file_ref = v; }
186 }
187 "status" => {
188 if let Ok(v) = serde_json::from_value(value) { self.status = v; }
189 }
190 "row_count" => {
191 if let Ok(v) = serde_json::from_value(value) { self.row_count = v; }
192 }
193 _ => {} }
195 }
196 }
197
198}
199
200impl super::Entity for BankStatementImport {
201 type Id = Uuid;
202
203 fn entity_id(&self) -> &Self::Id {
204 &self.id
205 }
206
207 fn entity_type() -> &'static str {
208 "BankStatementImport"
209 }
210}
211
212impl backbone_core::PersistentEntity for BankStatementImport {
213 fn entity_id(&self) -> String {
214 self.id.to_string()
215 }
216 fn set_entity_id(&mut self, id: String) {
217 if let Ok(uuid) = uuid::Uuid::parse_str(&id) {
218 self.id = uuid;
219 }
220 }
221 fn created_at(&self) -> Option<chrono::DateTime<chrono::Utc>> {
222 self.metadata.created_at
223 }
224 fn set_created_at(&mut self, ts: chrono::DateTime<chrono::Utc>) {
225 self.metadata.created_at = Some(ts);
226 }
227 fn updated_at(&self) -> Option<chrono::DateTime<chrono::Utc>> {
228 self.metadata.updated_at
229 }
230 fn set_updated_at(&mut self, ts: chrono::DateTime<chrono::Utc>) {
231 self.metadata.updated_at = Some(ts);
232 }
233 fn deleted_at(&self) -> Option<chrono::DateTime<chrono::Utc>> {
234 self.metadata.deleted_at
235 }
236 fn set_deleted_at(&mut self, ts: Option<chrono::DateTime<chrono::Utc>>) {
237 self.metadata.deleted_at = ts;
238 }
239}
240
241impl backbone_orm::EntityRepoMeta for BankStatementImport {
242 fn column_types() -> std::collections::HashMap<String, String> {
243 let mut m = std::collections::HashMap::new();
244 m.insert("id".to_string(), "uuid".to_string());
245 m.insert("bank_account_id".to_string(), "uuid".to_string());
246 m.insert("source_format".to_string(), "source_format".to_string());
247 m.insert("status".to_string(), "import_status".to_string());
248 m.insert("statement_period_start".to_string(), "date".to_string());
249 m.insert("statement_period_end".to_string(), "date".to_string());
250 m
251 }
252 fn search_fields() -> &'static [&'static str] {
253 &[]
254 }
255 fn relations() -> &'static [(&'static str, &'static str, &'static str)] {
256 &[("bankAccount", "bank_accounts", "bankAccountId")]
257 }
258}
259
260#[derive(Debug, Clone, Default)]
265pub struct BankStatementImportBuilder {
266 bank_account_id: Option<Uuid>,
267 source_format: Option<SourceFormat>,
268 statement_period_start: Option<NaiveDate>,
269 statement_period_end: Option<NaiveDate>,
270 opening_balance: Option<Decimal>,
271 closing_balance: Option<Decimal>,
272 file_ref: Option<String>,
273 status: Option<ImportStatus>,
274 row_count: Option<i32>,
275}
276
277impl BankStatementImportBuilder {
278 pub fn bank_account_id(mut self, value: Uuid) -> Self {
280 self.bank_account_id = Some(value);
281 self
282 }
283
284 pub fn source_format(mut self, value: SourceFormat) -> Self {
286 self.source_format = Some(value);
287 self
288 }
289
290 pub fn statement_period_start(mut self, value: NaiveDate) -> Self {
292 self.statement_period_start = Some(value);
293 self
294 }
295
296 pub fn statement_period_end(mut self, value: NaiveDate) -> Self {
298 self.statement_period_end = Some(value);
299 self
300 }
301
302 pub fn opening_balance(mut self, value: Decimal) -> Self {
304 self.opening_balance = Some(value);
305 self
306 }
307
308 pub fn closing_balance(mut self, value: Decimal) -> Self {
310 self.closing_balance = Some(value);
311 self
312 }
313
314 pub fn file_ref(mut self, value: String) -> Self {
316 self.file_ref = Some(value);
317 self
318 }
319
320 pub fn status(mut self, value: ImportStatus) -> Self {
322 self.status = Some(value);
323 self
324 }
325
326 pub fn row_count(mut self, value: i32) -> Self {
328 self.row_count = Some(value);
329 self
330 }
331
332 pub fn build(self) -> Result<BankStatementImport, String> {
336 let bank_account_id = self.bank_account_id.ok_or_else(|| "bank_account_id is required".to_string())?;
337 let statement_period_start = self.statement_period_start.ok_or_else(|| "statement_period_start is required".to_string())?;
338 let statement_period_end = self.statement_period_end.ok_or_else(|| "statement_period_end is required".to_string())?;
339
340 Ok(BankStatementImport {
341 id: Uuid::new_v4(),
342 bank_account_id,
343 source_format: self.source_format.unwrap_or_default(),
344 statement_period_start,
345 statement_period_end,
346 opening_balance: self.opening_balance.unwrap_or(Decimal::from(0)),
347 closing_balance: self.closing_balance.unwrap_or(Decimal::from(0)),
348 file_ref: self.file_ref,
349 status: self.status.unwrap_or_default(),
350 row_count: self.row_count.unwrap_or(0),
351 metadata: AuditMetadata::default(),
352 })
353 }
354}