Skip to main content

backbone_banking/domain/entity/
bank.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use sqlx::FromRow;
4use uuid::Uuid;
5
6use super::BankStatus;
7use super::AuditMetadata;
8
9/// Strongly-typed ID for Bank
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
11#[serde(transparent)]
12pub struct BankId(pub Uuid);
13
14impl BankId {
15    pub fn new(id: Uuid) -> Self { Self(id) }
16    pub fn generate() -> Self { Self(Uuid::new_v4()) }
17    pub fn into_inner(self) -> Uuid { self.0 }
18}
19
20impl std::fmt::Display for BankId {
21    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22        write!(f, "{}", self.0)
23    }
24}
25
26impl std::str::FromStr for BankId {
27    type Err = uuid::Error;
28    fn from_str(s: &str) -> Result<Self, Self::Err> {
29        Ok(Self(Uuid::parse_str(s)?))
30    }
31}
32
33impl From<Uuid> for BankId {
34    fn from(id: Uuid) -> Self { Self(id) }
35}
36
37impl From<BankId> for Uuid {
38    fn from(id: BankId) -> Self { id.0 }
39}
40
41impl AsRef<Uuid> for BankId {
42    fn as_ref(&self) -> &Uuid { &self.0 }
43}
44
45impl std::ops::Deref for BankId {
46    type Target = Uuid;
47    fn deref(&self) -> &Self::Target { &self.0 }
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
51pub struct Bank {
52    pub id: Uuid,
53    pub name: String,
54    pub swift_bic: Option<String>,
55    pub country: String,
56    pub status: BankStatus,
57    #[serde(default)]
58    #[sqlx(json)]
59    pub metadata: AuditMetadata,
60}
61
62impl Bank {
63    /// Create a builder for Bank
64    pub fn builder() -> BankBuilder {
65        <BankBuilder as Default>::default()
66    }
67
68    /// Create a new Bank with required fields
69    pub fn new(name: String, country: String, status: BankStatus) -> Self {
70        Self {
71            id: Uuid::new_v4(),
72            name,
73            swift_bic: None,
74            country,
75            status,
76            metadata: AuditMetadata::default(),
77        }
78    }
79
80    /// Get the entity's unique identifier
81    pub fn id(&self) -> &Uuid {
82        &self.id
83    }
84
85    /// Get a strongly-typed ID for this entity
86    pub fn typed_id(&self) -> BankId {
87        BankId(self.id)
88    }
89
90    /// Get when this entity was created
91    pub fn created_at(&self) -> Option<&DateTime<Utc>> {
92        self.metadata.created_at.as_ref()
93    }
94
95    /// Get when this entity was last updated
96    pub fn updated_at(&self) -> Option<&DateTime<Utc>> {
97        self.metadata.updated_at.as_ref()
98    }
99
100    /// Check if this entity is soft deleted
101    pub fn is_deleted(&self) -> bool {
102        self.metadata.deleted_at.is_some()
103    }
104
105    /// Check if this entity is active (not deleted)
106    pub fn is_active(&self) -> bool {
107        self.metadata.deleted_at.is_none()
108    }
109
110    /// Get when this entity was deleted
111    pub fn deleted_at(&self) -> Option<&DateTime<Utc>> {
112        self.metadata.deleted_at.as_ref()
113    }
114
115    /// Get who created this entity
116    pub fn created_by(&self) -> Option<&Uuid> {
117        self.metadata.created_by.as_ref()
118    }
119
120    /// Get who last updated this entity
121    pub fn updated_by(&self) -> Option<&Uuid> {
122        self.metadata.updated_by.as_ref()
123    }
124
125    /// Get who deleted this entity
126    pub fn deleted_by(&self) -> Option<&Uuid> {
127        self.metadata.deleted_by.as_ref()
128    }
129
130    /// Get the current status
131    pub fn status(&self) -> &BankStatus {
132        &self.status
133    }
134
135
136    // ==========================================================
137    // Fluent Setters (with_* for optional fields)
138    // ==========================================================
139
140    /// Set the swift_bic field (chainable)
141    pub fn with_swift_bic(mut self, value: String) -> Self {
142        self.swift_bic = Some(value);
143        self
144    }
145
146    // ==========================================================
147    // Partial Update
148    // ==========================================================
149
150    /// Apply partial updates from a map of field name to JSON value
151    pub fn apply_patch(&mut self, fields: std::collections::HashMap<String, serde_json::Value>) {
152        for (key, value) in fields {
153            match key.as_str() {
154                "name" => {
155                    if let Ok(v) = serde_json::from_value(value) { self.name = v; }
156                }
157                "swift_bic" => {
158                    if let Ok(v) = serde_json::from_value(value) { self.swift_bic = v; }
159                }
160                "country" => {
161                    if let Ok(v) = serde_json::from_value(value) { self.country = v; }
162                }
163                "status" => {
164                    if let Ok(v) = serde_json::from_value(value) { self.status = v; }
165                }
166                _ => {} // ignore unknown fields
167            }
168        }
169    }
170
171}
172
173impl super::Entity for Bank {
174    type Id = Uuid;
175
176    fn entity_id(&self) -> &Self::Id {
177        &self.id
178    }
179
180    fn entity_type() -> &'static str {
181        "Bank"
182    }
183}
184
185impl backbone_core::PersistentEntity for Bank {
186    fn entity_id(&self) -> String {
187        self.id.to_string()
188    }
189    fn set_entity_id(&mut self, id: String) {
190        if let Ok(uuid) = uuid::Uuid::parse_str(&id) {
191            self.id = uuid;
192        }
193    }
194    fn created_at(&self) -> Option<chrono::DateTime<chrono::Utc>> {
195        self.metadata.created_at
196    }
197    fn set_created_at(&mut self, ts: chrono::DateTime<chrono::Utc>) {
198        self.metadata.created_at = Some(ts);
199    }
200    fn updated_at(&self) -> Option<chrono::DateTime<chrono::Utc>> {
201        self.metadata.updated_at
202    }
203    fn set_updated_at(&mut self, ts: chrono::DateTime<chrono::Utc>) {
204        self.metadata.updated_at = Some(ts);
205    }
206    fn deleted_at(&self) -> Option<chrono::DateTime<chrono::Utc>> {
207        self.metadata.deleted_at
208    }
209    fn set_deleted_at(&mut self, ts: Option<chrono::DateTime<chrono::Utc>>) {
210        self.metadata.deleted_at = ts;
211    }
212}
213
214impl backbone_orm::EntityRepoMeta for Bank {
215    fn column_types() -> std::collections::HashMap<String, String> {
216        let mut m = std::collections::HashMap::new();
217        m.insert("id".to_string(), "uuid".to_string());
218        m.insert("status".to_string(), "bank_status".to_string());
219        m
220    }
221    fn search_fields() -> &'static [&'static str] {
222        &["name", "country"]
223    }
224}
225
226/// Builder for Bank entity
227///
228/// Provides a fluent API for constructing Bank instances.
229/// System fields (id, metadata, timestamps) are auto-initialized.
230#[derive(Debug, Clone, Default)]
231pub struct BankBuilder {
232    name: Option<String>,
233    swift_bic: Option<String>,
234    country: Option<String>,
235    status: Option<BankStatus>,
236}
237
238impl BankBuilder {
239    /// Set the name field (required)
240    pub fn name(mut self, value: String) -> Self {
241        self.name = Some(value);
242        self
243    }
244
245    /// Set the swift_bic field (optional)
246    pub fn swift_bic(mut self, value: String) -> Self {
247        self.swift_bic = Some(value);
248        self
249    }
250
251    /// Set the country field (default: `"ID".to_string()`)
252    pub fn country(mut self, value: String) -> Self {
253        self.country = Some(value);
254        self
255    }
256
257    /// Set the status field (default: `BankStatus::default()`)
258    pub fn status(mut self, value: BankStatus) -> Self {
259        self.status = Some(value);
260        self
261    }
262
263    /// Build the Bank entity
264    ///
265    /// Returns Err if any required field without a default is missing.
266    pub fn build(self) -> Result<Bank, String> {
267        let name = self.name.ok_or_else(|| "name is required".to_string())?;
268
269        Ok(Bank {
270            id: Uuid::new_v4(),
271            name,
272            swift_bic: self.swift_bic,
273            country: self.country.unwrap_or("ID".to_string()),
274            status: self.status.unwrap_or_default(),
275            metadata: AuditMetadata::default(),
276        })
277    }
278}