Skip to main content

backbone_pos/domain/entity/
pos_table.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use sqlx::FromRow;
4use uuid::Uuid;
5use super::AuditMetadata;
6
7/// Strongly-typed ID for PosTable
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
9#[serde(transparent)]
10pub struct PosTableId(pub Uuid);
11
12impl PosTableId {
13    pub fn new(id: Uuid) -> Self { Self(id) }
14    pub fn generate() -> Self { Self(Uuid::new_v4()) }
15    pub fn into_inner(self) -> Uuid { self.0 }
16}
17
18impl std::fmt::Display for PosTableId {
19    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20        write!(f, "{}", self.0)
21    }
22}
23
24impl std::str::FromStr for PosTableId {
25    type Err = uuid::Error;
26    fn from_str(s: &str) -> Result<Self, Self::Err> {
27        Ok(Self(Uuid::parse_str(s)?))
28    }
29}
30
31impl From<Uuid> for PosTableId {
32    fn from(id: Uuid) -> Self { Self(id) }
33}
34
35impl From<PosTableId> for Uuid {
36    fn from(id: PosTableId) -> Self { id.0 }
37}
38
39impl AsRef<Uuid> for PosTableId {
40    fn as_ref(&self) -> &Uuid { &self.0 }
41}
42
43impl std::ops::Deref for PosTableId {
44    type Target = Uuid;
45    fn deref(&self) -> &Self::Target { &self.0 }
46}
47
48#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
49pub struct PosTable {
50    pub id: Uuid,
51    pub pos_floor_plan_id: Uuid,
52    pub name: Option<String>,
53    pub seats: Option<i32>,
54    pub shape: Option<String>,
55    pub position: Option<serde_json::Value>,
56    #[serde(default)]
57    #[sqlx(json)]
58    pub metadata: AuditMetadata,
59}
60
61impl PosTable {
62    /// Create a builder for PosTable
63    pub fn builder() -> PosTableBuilder {
64        <PosTableBuilder as Default>::default()
65    }
66
67    /// Create a new PosTable with required fields
68    pub fn new(pos_floor_plan_id: Uuid) -> Self {
69        Self {
70            id: Uuid::new_v4(),
71            pos_floor_plan_id,
72            name: None,
73            seats: None,
74            shape: None,
75            position: None,
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) -> PosTableId {
87        PosTableId(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
131    // ==========================================================
132    // Fluent Setters (with_* for optional fields)
133    // ==========================================================
134
135    /// Set the name field (chainable)
136    pub fn with_name(mut self, value: String) -> Self {
137        self.name = Some(value);
138        self
139    }
140
141    /// Set the seats field (chainable)
142    pub fn with_seats(mut self, value: i32) -> Self {
143        self.seats = Some(value);
144        self
145    }
146
147    /// Set the shape field (chainable)
148    pub fn with_shape(mut self, value: String) -> Self {
149        self.shape = Some(value);
150        self
151    }
152
153    /// Set the position field (chainable)
154    pub fn with_position(mut self, value: serde_json::Value) -> Self {
155        self.position = Some(value);
156        self
157    }
158
159    // ==========================================================
160    // Partial Update
161    // ==========================================================
162
163    /// Apply partial updates from a map of field name to JSON value
164    pub fn apply_patch(&mut self, fields: std::collections::HashMap<String, serde_json::Value>) {
165        for (key, value) in fields {
166            match key.as_str() {
167                "pos_floor_plan_id" => {
168                    if let Ok(v) = serde_json::from_value(value) { self.pos_floor_plan_id = v; }
169                }
170                "name" => {
171                    if let Ok(v) = serde_json::from_value(value) { self.name = v; }
172                }
173                "seats" => {
174                    if let Ok(v) = serde_json::from_value(value) { self.seats = v; }
175                }
176                "shape" => {
177                    if let Ok(v) = serde_json::from_value(value) { self.shape = v; }
178                }
179                "position" => {
180                    if let Ok(v) = serde_json::from_value(value) { self.position = v; }
181                }
182                _ => {} // ignore unknown fields
183            }
184        }
185    }
186
187}
188
189impl super::Entity for PosTable {
190    type Id = Uuid;
191
192    fn entity_id(&self) -> &Self::Id {
193        &self.id
194    }
195
196    fn entity_type() -> &'static str {
197        "PosTable"
198    }
199}
200
201impl backbone_core::PersistentEntity for PosTable {
202    fn entity_id(&self) -> String {
203        self.id.to_string()
204    }
205    fn set_entity_id(&mut self, id: String) {
206        if let Ok(uuid) = uuid::Uuid::parse_str(&id) {
207            self.id = uuid;
208        }
209    }
210    fn created_at(&self) -> Option<chrono::DateTime<chrono::Utc>> {
211        self.metadata.created_at
212    }
213    fn set_created_at(&mut self, ts: chrono::DateTime<chrono::Utc>) {
214        self.metadata.created_at = Some(ts);
215    }
216    fn updated_at(&self) -> Option<chrono::DateTime<chrono::Utc>> {
217        self.metadata.updated_at
218    }
219    fn set_updated_at(&mut self, ts: chrono::DateTime<chrono::Utc>) {
220        self.metadata.updated_at = Some(ts);
221    }
222    fn deleted_at(&self) -> Option<chrono::DateTime<chrono::Utc>> {
223        self.metadata.deleted_at
224    }
225    fn set_deleted_at(&mut self, ts: Option<chrono::DateTime<chrono::Utc>>) {
226        self.metadata.deleted_at = ts;
227    }
228}
229
230impl backbone_orm::EntityRepoMeta for PosTable {
231    fn column_types() -> std::collections::HashMap<String, String> {
232        let mut m = std::collections::HashMap::new();
233        m.insert("id".to_string(), "uuid".to_string());
234        m.insert("pos_floor_plan_id".to_string(), "uuid".to_string());
235        m
236    }
237    fn search_fields() -> &'static [&'static str] {
238        &[]
239    }
240    fn relations() -> &'static [(&'static str, &'static str, &'static str)] {
241        &[("floorPlan", "pos_floor_plans", "posFloorPlanId")]
242    }
243}
244
245/// Builder for PosTable entity
246///
247/// Provides a fluent API for constructing PosTable instances.
248/// System fields (id, metadata, timestamps) are auto-initialized.
249#[derive(Debug, Clone, Default)]
250pub struct PosTableBuilder {
251    pos_floor_plan_id: Option<Uuid>,
252    name: Option<String>,
253    seats: Option<i32>,
254    shape: Option<String>,
255    position: Option<serde_json::Value>,
256}
257
258impl PosTableBuilder {
259    /// Set the pos_floor_plan_id field (required)
260    pub fn pos_floor_plan_id(mut self, value: Uuid) -> Self {
261        self.pos_floor_plan_id = Some(value);
262        self
263    }
264
265    /// Set the name field (optional)
266    pub fn name(mut self, value: String) -> Self {
267        self.name = Some(value);
268        self
269    }
270
271    /// Set the seats field (optional)
272    pub fn seats(mut self, value: i32) -> Self {
273        self.seats = Some(value);
274        self
275    }
276
277    /// Set the shape field (optional)
278    pub fn shape(mut self, value: String) -> Self {
279        self.shape = Some(value);
280        self
281    }
282
283    /// Set the position field (optional)
284    pub fn position(mut self, value: serde_json::Value) -> Self {
285        self.position = Some(value);
286        self
287    }
288
289    /// Build the PosTable entity
290    ///
291    /// Returns Err if any required field without a default is missing.
292    pub fn build(self) -> Result<PosTable, String> {
293        let pos_floor_plan_id = self.pos_floor_plan_id.ok_or_else(|| "pos_floor_plan_id is required".to_string())?;
294
295        Ok(PosTable {
296            id: Uuid::new_v4(),
297            pos_floor_plan_id,
298            name: self.name,
299            seats: self.seats,
300            shape: self.shape,
301            position: self.position,
302            metadata: AuditMetadata::default(),
303        })
304    }
305}