Skip to main content

backbone_banking/domain/entity/
bank_clearance.rs

1use chrono::{DateTime, Utc, NaiveDate};
2use serde::{Deserialize, Serialize};
3use sqlx::FromRow;
4use uuid::Uuid;
5use rust_decimal::Decimal;
6
7use super::MatchedSourceType;
8use super::MatchMethod;
9use super::AuditMetadata;
10
11/// Strongly-typed ID for BankClearance
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
13#[serde(transparent)]
14pub struct BankClearanceId(pub Uuid);
15
16impl BankClearanceId {
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 BankClearanceId {
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 BankClearanceId {
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 BankClearanceId {
36    fn from(id: Uuid) -> Self { Self(id) }
37}
38
39impl From<BankClearanceId> for Uuid {
40    fn from(id: BankClearanceId) -> Self { id.0 }
41}
42
43impl AsRef<Uuid> for BankClearanceId {
44    fn as_ref(&self) -> &Uuid { &self.0 }
45}
46
47impl std::ops::Deref for BankClearanceId {
48    type Target = Uuid;
49    fn deref(&self) -> &Self::Target { &self.0 }
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
53pub struct BankClearance {
54    pub id: Uuid,
55    pub bank_transaction_id: Uuid,
56    pub matched_source_type: MatchedSourceType,
57    pub matched_source_id: Uuid,
58    pub matched_amount: Decimal,
59    pub match_method: MatchMethod,
60    pub clearance_date: NaiveDate,
61    pub accounting_post_id: Option<Uuid>,
62    pub journal_id: Option<Uuid>,
63    #[serde(default)]
64    #[sqlx(json)]
65    pub metadata: AuditMetadata,
66}
67
68impl BankClearance {
69    /// Create a builder for BankClearance
70    pub fn builder() -> BankClearanceBuilder {
71        <BankClearanceBuilder as Default>::default()
72    }
73
74    /// Create a new BankClearance with required fields
75    pub fn new(bank_transaction_id: Uuid, matched_source_type: MatchedSourceType, matched_source_id: Uuid, matched_amount: Decimal, match_method: MatchMethod, clearance_date: NaiveDate) -> Self {
76        Self {
77            id: Uuid::new_v4(),
78            bank_transaction_id,
79            matched_source_type,
80            matched_source_id,
81            matched_amount,
82            match_method,
83            clearance_date,
84            accounting_post_id: None,
85            journal_id: None,
86            metadata: AuditMetadata::default(),
87        }
88    }
89
90    /// Get the entity's unique identifier
91    pub fn id(&self) -> &Uuid {
92        &self.id
93    }
94
95    /// Get a strongly-typed ID for this entity
96    pub fn typed_id(&self) -> BankClearanceId {
97        BankClearanceId(self.id)
98    }
99
100    /// Get when this entity was created
101    pub fn created_at(&self) -> Option<&DateTime<Utc>> {
102        self.metadata.created_at.as_ref()
103    }
104
105    /// Get when this entity was last updated
106    pub fn updated_at(&self) -> Option<&DateTime<Utc>> {
107        self.metadata.updated_at.as_ref()
108    }
109
110    /// Check if this entity is soft deleted
111    pub fn is_deleted(&self) -> bool {
112        self.metadata.deleted_at.is_some()
113    }
114
115    /// Check if this entity is active (not deleted)
116    pub fn is_active(&self) -> bool {
117        self.metadata.deleted_at.is_none()
118    }
119
120    /// Get when this entity was deleted
121    pub fn deleted_at(&self) -> Option<&DateTime<Utc>> {
122        self.metadata.deleted_at.as_ref()
123    }
124
125    /// Get who created this entity
126    pub fn created_by(&self) -> Option<&Uuid> {
127        self.metadata.created_by.as_ref()
128    }
129
130    /// Get who last updated this entity
131    pub fn updated_by(&self) -> Option<&Uuid> {
132        self.metadata.updated_by.as_ref()
133    }
134
135    /// Get who deleted this entity
136    pub fn deleted_by(&self) -> Option<&Uuid> {
137        self.metadata.deleted_by.as_ref()
138    }
139
140
141    // ==========================================================
142    // Fluent Setters (with_* for optional fields)
143    // ==========================================================
144
145    /// Set the accounting_post_id field (chainable)
146    pub fn with_accounting_post_id(mut self, value: Uuid) -> Self {
147        self.accounting_post_id = Some(value);
148        self
149    }
150
151    /// Set the journal_id field (chainable)
152    pub fn with_journal_id(mut self, value: Uuid) -> Self {
153        self.journal_id = Some(value);
154        self
155    }
156
157    // ==========================================================
158    // Partial Update
159    // ==========================================================
160
161    /// Apply partial updates from a map of field name to JSON value
162    pub fn apply_patch(&mut self, fields: std::collections::HashMap<String, serde_json::Value>) {
163        for (key, value) in fields {
164            match key.as_str() {
165                "bank_transaction_id" => {
166                    if let Ok(v) = serde_json::from_value(value) { self.bank_transaction_id = v; }
167                }
168                "matched_source_type" => {
169                    if let Ok(v) = serde_json::from_value(value) { self.matched_source_type = v; }
170                }
171                "matched_source_id" => {
172                    if let Ok(v) = serde_json::from_value(value) { self.matched_source_id = v; }
173                }
174                "matched_amount" => {
175                    if let Ok(v) = serde_json::from_value(value) { self.matched_amount = v; }
176                }
177                "match_method" => {
178                    if let Ok(v) = serde_json::from_value(value) { self.match_method = v; }
179                }
180                "clearance_date" => {
181                    if let Ok(v) = serde_json::from_value(value) { self.clearance_date = v; }
182                }
183                "accounting_post_id" => {
184                    if let Ok(v) = serde_json::from_value(value) { self.accounting_post_id = v; }
185                }
186                "journal_id" => {
187                    if let Ok(v) = serde_json::from_value(value) { self.journal_id = v; }
188                }
189                _ => {} // ignore unknown fields
190            }
191        }
192    }
193
194}
195
196impl super::Entity for BankClearance {
197    type Id = Uuid;
198
199    fn entity_id(&self) -> &Self::Id {
200        &self.id
201    }
202
203    fn entity_type() -> &'static str {
204        "BankClearance"
205    }
206}
207
208impl backbone_core::PersistentEntity for BankClearance {
209    fn entity_id(&self) -> String {
210        self.id.to_string()
211    }
212    fn set_entity_id(&mut self, id: String) {
213        if let Ok(uuid) = uuid::Uuid::parse_str(&id) {
214            self.id = uuid;
215        }
216    }
217    fn created_at(&self) -> Option<chrono::DateTime<chrono::Utc>> {
218        self.metadata.created_at
219    }
220    fn set_created_at(&mut self, ts: chrono::DateTime<chrono::Utc>) {
221        self.metadata.created_at = Some(ts);
222    }
223    fn updated_at(&self) -> Option<chrono::DateTime<chrono::Utc>> {
224        self.metadata.updated_at
225    }
226    fn set_updated_at(&mut self, ts: chrono::DateTime<chrono::Utc>) {
227        self.metadata.updated_at = Some(ts);
228    }
229    fn deleted_at(&self) -> Option<chrono::DateTime<chrono::Utc>> {
230        self.metadata.deleted_at
231    }
232    fn set_deleted_at(&mut self, ts: Option<chrono::DateTime<chrono::Utc>>) {
233        self.metadata.deleted_at = ts;
234    }
235}
236
237impl backbone_orm::EntityRepoMeta for BankClearance {
238    fn column_types() -> std::collections::HashMap<String, String> {
239        let mut m = std::collections::HashMap::new();
240        m.insert("id".to_string(), "uuid".to_string());
241        m.insert("bank_transaction_id".to_string(), "uuid".to_string());
242        m.insert("matched_source_id".to_string(), "uuid".to_string());
243        m.insert("accounting_post_id".to_string(), "uuid".to_string());
244        m.insert("journal_id".to_string(), "uuid".to_string());
245        m.insert("matched_source_type".to_string(), "matched_source_type".to_string());
246        m.insert("match_method".to_string(), "match_method".to_string());
247        m.insert("clearance_date".to_string(), "date".to_string());
248        m
249    }
250    fn search_fields() -> &'static [&'static str] {
251        &[]
252    }
253    fn relations() -> &'static [(&'static str, &'static str, &'static str)] {
254        &[("bankTransaction", "bank_transactions", "bankTransactionId")]
255    }
256}
257
258/// Builder for BankClearance entity
259///
260/// Provides a fluent API for constructing BankClearance instances.
261/// System fields (id, metadata, timestamps) are auto-initialized.
262#[derive(Debug, Clone, Default)]
263pub struct BankClearanceBuilder {
264    bank_transaction_id: Option<Uuid>,
265    matched_source_type: Option<MatchedSourceType>,
266    matched_source_id: Option<Uuid>,
267    matched_amount: Option<Decimal>,
268    match_method: Option<MatchMethod>,
269    clearance_date: Option<NaiveDate>,
270    accounting_post_id: Option<Uuid>,
271    journal_id: Option<Uuid>,
272}
273
274impl BankClearanceBuilder {
275    /// Set the bank_transaction_id field (required)
276    pub fn bank_transaction_id(mut self, value: Uuid) -> Self {
277        self.bank_transaction_id = Some(value);
278        self
279    }
280
281    /// Set the matched_source_type field (required)
282    pub fn matched_source_type(mut self, value: MatchedSourceType) -> Self {
283        self.matched_source_type = Some(value);
284        self
285    }
286
287    /// Set the matched_source_id field (required)
288    pub fn matched_source_id(mut self, value: Uuid) -> Self {
289        self.matched_source_id = Some(value);
290        self
291    }
292
293    /// Set the matched_amount field (required)
294    pub fn matched_amount(mut self, value: Decimal) -> Self {
295        self.matched_amount = Some(value);
296        self
297    }
298
299    /// Set the match_method field (default: `MatchMethod::default()`)
300    pub fn match_method(mut self, value: MatchMethod) -> Self {
301        self.match_method = Some(value);
302        self
303    }
304
305    /// Set the clearance_date field (required)
306    pub fn clearance_date(mut self, value: NaiveDate) -> Self {
307        self.clearance_date = Some(value);
308        self
309    }
310
311    /// Set the accounting_post_id field (optional)
312    pub fn accounting_post_id(mut self, value: Uuid) -> Self {
313        self.accounting_post_id = Some(value);
314        self
315    }
316
317    /// Set the journal_id field (optional)
318    pub fn journal_id(mut self, value: Uuid) -> Self {
319        self.journal_id = Some(value);
320        self
321    }
322
323    /// Build the BankClearance entity
324    ///
325    /// Returns Err if any required field without a default is missing.
326    pub fn build(self) -> Result<BankClearance, String> {
327        let bank_transaction_id = self.bank_transaction_id.ok_or_else(|| "bank_transaction_id is required".to_string())?;
328        let matched_source_type = self.matched_source_type.ok_or_else(|| "matched_source_type is required".to_string())?;
329        let matched_source_id = self.matched_source_id.ok_or_else(|| "matched_source_id is required".to_string())?;
330        let matched_amount = self.matched_amount.ok_or_else(|| "matched_amount is required".to_string())?;
331        let clearance_date = self.clearance_date.ok_or_else(|| "clearance_date is required".to_string())?;
332
333        Ok(BankClearance {
334            id: Uuid::new_v4(),
335            bank_transaction_id,
336            matched_source_type,
337            matched_source_id,
338            matched_amount,
339            match_method: self.match_method.unwrap_or_default(),
340            clearance_date,
341            accounting_post_id: self.accounting_post_id,
342            journal_id: self.journal_id,
343            metadata: AuditMetadata::default(),
344        })
345    }
346}