backbone_inventory/domain/entity/
warehouse.rs1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use sqlx::FromRow;
4use uuid::Uuid;
5
6use super::WarehouseType;
7use super::AuditMetadata;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
11#[serde(transparent)]
12pub struct WarehouseId(pub Uuid);
13
14impl WarehouseId {
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 WarehouseId {
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 WarehouseId {
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 WarehouseId {
34 fn from(id: Uuid) -> Self { Self(id) }
35}
36
37impl From<WarehouseId> for Uuid {
38 fn from(id: WarehouseId) -> Self { id.0 }
39}
40
41impl AsRef<Uuid> for WarehouseId {
42 fn as_ref(&self) -> &Uuid { &self.0 }
43}
44
45impl std::ops::Deref for WarehouseId {
46 type Target = Uuid;
47 fn deref(&self) -> &Self::Target { &self.0 }
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
51pub struct Warehouse {
52 pub id: Uuid,
53 pub code: String,
54 pub name: String,
55 pub warehouse_type: WarehouseType,
56 pub parent_warehouse_id: Option<Uuid>,
57 pub is_group: bool,
58 #[serde(default)]
59 #[sqlx(json)]
60 pub metadata: AuditMetadata,
61}
62
63impl Warehouse {
64 pub fn builder() -> WarehouseBuilder {
66 <WarehouseBuilder as Default>::default()
67 }
68
69 pub fn new(code: String, name: String, warehouse_type: WarehouseType, is_group: bool) -> Self {
71 Self {
72 id: Uuid::new_v4(),
73 code,
74 name,
75 warehouse_type,
76 parent_warehouse_id: None,
77 is_group,
78 metadata: AuditMetadata::default(),
79 }
80 }
81
82 pub fn id(&self) -> &Uuid {
84 &self.id
85 }
86
87 pub fn typed_id(&self) -> WarehouseId {
89 WarehouseId(self.id)
90 }
91
92 pub fn created_at(&self) -> Option<&DateTime<Utc>> {
94 self.metadata.created_at.as_ref()
95 }
96
97 pub fn updated_at(&self) -> Option<&DateTime<Utc>> {
99 self.metadata.updated_at.as_ref()
100 }
101
102 pub fn is_deleted(&self) -> bool {
104 self.metadata.deleted_at.is_some()
105 }
106
107 pub fn is_active(&self) -> bool {
109 self.metadata.deleted_at.is_none()
110 }
111
112 pub fn deleted_at(&self) -> Option<&DateTime<Utc>> {
114 self.metadata.deleted_at.as_ref()
115 }
116
117 pub fn created_by(&self) -> Option<&Uuid> {
119 self.metadata.created_by.as_ref()
120 }
121
122 pub fn updated_by(&self) -> Option<&Uuid> {
124 self.metadata.updated_by.as_ref()
125 }
126
127 pub fn deleted_by(&self) -> Option<&Uuid> {
129 self.metadata.deleted_by.as_ref()
130 }
131
132
133 pub fn with_parent_warehouse_id(mut self, value: Uuid) -> Self {
139 self.parent_warehouse_id = Some(value);
140 self
141 }
142
143 pub fn apply_patch(&mut self, fields: std::collections::HashMap<String, serde_json::Value>) {
149 for (key, value) in fields {
150 match key.as_str() {
151 "code" => {
152 if let Ok(v) = serde_json::from_value(value) { self.code = v; }
153 }
154 "name" => {
155 if let Ok(v) = serde_json::from_value(value) { self.name = v; }
156 }
157 "warehouse_type" => {
158 if let Ok(v) = serde_json::from_value(value) { self.warehouse_type = v; }
159 }
160 "parent_warehouse_id" => {
161 if let Ok(v) = serde_json::from_value(value) { self.parent_warehouse_id = v; }
162 }
163 "is_group" => {
164 if let Ok(v) = serde_json::from_value(value) { self.is_group = v; }
165 }
166 _ => {} }
168 }
169 }
170
171}
172
173impl super::Entity for Warehouse {
174 type Id = Uuid;
175
176 fn entity_id(&self) -> &Self::Id {
177 &self.id
178 }
179
180 fn entity_type() -> &'static str {
181 "Warehouse"
182 }
183}
184
185impl backbone_core::PersistentEntity for Warehouse {
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 Warehouse {
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("parent_warehouse_id".to_string(), "uuid".to_string());
219 m.insert("warehouse_type".to_string(), "warehouse_type".to_string());
220 m
221 }
222 fn search_fields() -> &'static [&'static str] {
223 &["code", "name"]
224 }
225 fn relations() -> &'static [(&'static str, &'static str, &'static str)] {
226 &[("parentWarehouse", "warehouses", "parentWarehouseId")]
227 }
228}
229
230#[derive(Debug, Clone, Default)]
235pub struct WarehouseBuilder {
236 code: Option<String>,
237 name: Option<String>,
238 warehouse_type: Option<WarehouseType>,
239 parent_warehouse_id: Option<Uuid>,
240 is_group: Option<bool>,
241}
242
243impl WarehouseBuilder {
244 pub fn code(mut self, value: String) -> Self {
246 self.code = Some(value);
247 self
248 }
249
250 pub fn name(mut self, value: String) -> Self {
252 self.name = Some(value);
253 self
254 }
255
256 pub fn warehouse_type(mut self, value: WarehouseType) -> Self {
258 self.warehouse_type = Some(value);
259 self
260 }
261
262 pub fn parent_warehouse_id(mut self, value: Uuid) -> Self {
264 self.parent_warehouse_id = Some(value);
265 self
266 }
267
268 pub fn is_group(mut self, value: bool) -> Self {
270 self.is_group = Some(value);
271 self
272 }
273
274 pub fn build(self) -> Result<Warehouse, String> {
278 let code = self.code.ok_or_else(|| "code is required".to_string())?;
279 let name = self.name.ok_or_else(|| "name is required".to_string())?;
280
281 Ok(Warehouse {
282 id: Uuid::new_v4(),
283 code,
284 name,
285 warehouse_type: self.warehouse_type.unwrap_or_default(),
286 parent_warehouse_id: self.parent_warehouse_id,
287 is_group: self.is_group.unwrap_or(false),
288 metadata: AuditMetadata::default(),
289 })
290 }
291}