backbone_inventory/domain/entity/
quant.rs1use chrono::{DateTime, Utc, NaiveDate};
2use serde::{Deserialize, Serialize};
3use sqlx::FromRow;
4use uuid::Uuid;
5use rust_decimal::Decimal;
6use super::AuditMetadata;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
10#[serde(transparent)]
11pub struct QuantId(pub Uuid);
12
13impl QuantId {
14 pub fn new(id: Uuid) -> Self { Self(id) }
15 pub fn generate() -> Self { Self(Uuid::new_v4()) }
16 pub fn into_inner(self) -> Uuid { self.0 }
17}
18
19impl std::fmt::Display for QuantId {
20 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21 write!(f, "{}", self.0)
22 }
23}
24
25impl std::str::FromStr for QuantId {
26 type Err = uuid::Error;
27 fn from_str(s: &str) -> Result<Self, Self::Err> {
28 Ok(Self(Uuid::parse_str(s)?))
29 }
30}
31
32impl From<Uuid> for QuantId {
33 fn from(id: Uuid) -> Self { Self(id) }
34}
35
36impl From<QuantId> for Uuid {
37 fn from(id: QuantId) -> Self { id.0 }
38}
39
40impl AsRef<Uuid> for QuantId {
41 fn as_ref(&self) -> &Uuid { &self.0 }
42}
43
44impl std::ops::Deref for QuantId {
45 type Target = Uuid;
46 fn deref(&self) -> &Self::Target { &self.0 }
47}
48
49#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
50pub struct Quant {
51 pub id: Uuid,
52 pub item_id: Uuid,
53 pub location_id: Uuid,
54 pub lot_id: Option<Uuid>,
55 pub package_id: Option<Uuid>,
56 pub owner_id: Option<Uuid>,
57 pub quantity: Decimal,
58 pub reserved_quantity: Decimal,
59 pub available_quantity: Decimal,
60 pub in_date: Option<DateTime<Utc>>,
61 pub inventory_quantity: Option<Decimal>,
62 pub inventory_diff_quantity: Option<Decimal>,
63 pub inventory_quantity_set: bool,
64 pub inventory_date: Option<NaiveDate>,
65 pub sn_duplicated: bool,
66 #[serde(default)]
67 #[sqlx(json)]
68 pub metadata: AuditMetadata,
69}
70
71impl Quant {
72 pub fn builder() -> QuantBuilder {
74 <QuantBuilder as Default>::default()
75 }
76
77 pub fn new(item_id: Uuid, location_id: Uuid, quantity: Decimal, reserved_quantity: Decimal, available_quantity: Decimal, inventory_quantity_set: bool, sn_duplicated: bool) -> Self {
79 Self {
80 id: Uuid::new_v4(),
81 item_id,
82 location_id,
83 lot_id: None,
84 package_id: None,
85 owner_id: None,
86 quantity,
87 reserved_quantity,
88 available_quantity,
89 in_date: None,
90 inventory_quantity: None,
91 inventory_diff_quantity: None,
92 inventory_quantity_set,
93 inventory_date: None,
94 sn_duplicated,
95 metadata: AuditMetadata::default(),
96 }
97 }
98
99 pub fn id(&self) -> &Uuid {
101 &self.id
102 }
103
104 pub fn typed_id(&self) -> QuantId {
106 QuantId(self.id)
107 }
108
109 pub fn created_at(&self) -> Option<&DateTime<Utc>> {
111 self.metadata.created_at.as_ref()
112 }
113
114 pub fn updated_at(&self) -> Option<&DateTime<Utc>> {
116 self.metadata.updated_at.as_ref()
117 }
118
119 pub fn is_deleted(&self) -> bool {
121 self.metadata.deleted_at.is_some()
122 }
123
124 pub fn is_active(&self) -> bool {
126 self.metadata.deleted_at.is_none()
127 }
128
129 pub fn deleted_at(&self) -> Option<&DateTime<Utc>> {
131 self.metadata.deleted_at.as_ref()
132 }
133
134 pub fn created_by(&self) -> Option<&Uuid> {
136 self.metadata.created_by.as_ref()
137 }
138
139 pub fn updated_by(&self) -> Option<&Uuid> {
141 self.metadata.updated_by.as_ref()
142 }
143
144 pub fn deleted_by(&self) -> Option<&Uuid> {
146 self.metadata.deleted_by.as_ref()
147 }
148
149
150 pub fn with_lot_id(mut self, value: Uuid) -> Self {
156 self.lot_id = Some(value);
157 self
158 }
159
160 pub fn with_package_id(mut self, value: Uuid) -> Self {
162 self.package_id = Some(value);
163 self
164 }
165
166 pub fn with_owner_id(mut self, value: Uuid) -> Self {
168 self.owner_id = Some(value);
169 self
170 }
171
172 pub fn with_in_date(mut self, value: DateTime<Utc>) -> Self {
174 self.in_date = Some(value);
175 self
176 }
177
178 pub fn with_inventory_quantity(mut self, value: Decimal) -> Self {
180 self.inventory_quantity = Some(value);
181 self
182 }
183
184 pub fn with_inventory_diff_quantity(mut self, value: Decimal) -> Self {
186 self.inventory_diff_quantity = Some(value);
187 self
188 }
189
190 pub fn with_inventory_date(mut self, value: NaiveDate) -> Self {
192 self.inventory_date = Some(value);
193 self
194 }
195
196 pub fn apply_patch(&mut self, fields: std::collections::HashMap<String, serde_json::Value>) {
202 for (key, value) in fields {
203 match key.as_str() {
204 "item_id" => {
205 if let Ok(v) = serde_json::from_value(value) { self.item_id = v; }
206 }
207 "location_id" => {
208 if let Ok(v) = serde_json::from_value(value) { self.location_id = v; }
209 }
210 "lot_id" => {
211 if let Ok(v) = serde_json::from_value(value) { self.lot_id = v; }
212 }
213 "package_id" => {
214 if let Ok(v) = serde_json::from_value(value) { self.package_id = v; }
215 }
216 "owner_id" => {
217 if let Ok(v) = serde_json::from_value(value) { self.owner_id = v; }
218 }
219 "quantity" => {
220 if let Ok(v) = serde_json::from_value(value) { self.quantity = v; }
221 }
222 "reserved_quantity" => {
223 if let Ok(v) = serde_json::from_value(value) { self.reserved_quantity = v; }
224 }
225 "available_quantity" => {
226 if let Ok(v) = serde_json::from_value(value) { self.available_quantity = v; }
227 }
228 "in_date" => {
229 if let Ok(v) = serde_json::from_value(value) { self.in_date = v; }
230 }
231 "inventory_quantity" => {
232 if let Ok(v) = serde_json::from_value(value) { self.inventory_quantity = v; }
233 }
234 "inventory_diff_quantity" => {
235 if let Ok(v) = serde_json::from_value(value) { self.inventory_diff_quantity = v; }
236 }
237 "inventory_quantity_set" => {
238 if let Ok(v) = serde_json::from_value(value) { self.inventory_quantity_set = v; }
239 }
240 "inventory_date" => {
241 if let Ok(v) = serde_json::from_value(value) { self.inventory_date = v; }
242 }
243 "sn_duplicated" => {
244 if let Ok(v) = serde_json::from_value(value) { self.sn_duplicated = v; }
245 }
246 _ => {} }
248 }
249 }
250
251}
252
253impl super::Entity for Quant {
254 type Id = Uuid;
255
256 fn entity_id(&self) -> &Self::Id {
257 &self.id
258 }
259
260 fn entity_type() -> &'static str {
261 "Quant"
262 }
263}
264
265impl backbone_core::PersistentEntity for Quant {
266 fn references() -> &'static [backbone_core::Reference] {
267 const REFERENCES: &[backbone_core::Reference] = &[
268 backbone_core::Reference::new("location_id", "inventory.locations", "id"),
269 backbone_core::Reference::new("lot_id", "inventory.lots", "id"),
270 backbone_core::Reference::new("package_id", "inventory.packages", "id"),
271 ];
272 REFERENCES
273 }
274 fn entity_id(&self) -> String {
275 self.id.to_string()
276 }
277 fn set_entity_id(&mut self, id: String) {
278 if let Ok(uuid) = uuid::Uuid::parse_str(&id) {
279 self.id = uuid;
280 }
281 }
282 fn created_at(&self) -> Option<chrono::DateTime<chrono::Utc>> {
283 self.metadata.created_at
284 }
285 fn set_created_at(&mut self, ts: chrono::DateTime<chrono::Utc>) {
286 self.metadata.created_at = Some(ts);
287 }
288 fn updated_at(&self) -> Option<chrono::DateTime<chrono::Utc>> {
289 self.metadata.updated_at
290 }
291 fn set_updated_at(&mut self, ts: chrono::DateTime<chrono::Utc>) {
292 self.metadata.updated_at = Some(ts);
293 }
294 fn deleted_at(&self) -> Option<chrono::DateTime<chrono::Utc>> {
295 self.metadata.deleted_at
296 }
297 fn set_deleted_at(&mut self, ts: Option<chrono::DateTime<chrono::Utc>>) {
298 self.metadata.deleted_at = ts;
299 }
300}
301
302impl backbone_orm::EntityRepoMeta for Quant {
303 fn column_types() -> std::collections::HashMap<String, String> {
304 let mut m = std::collections::HashMap::new();
305 m.insert("id".to_string(), "uuid".to_string());
306 m.insert("item_id".to_string(), "uuid".to_string());
307 m.insert("location_id".to_string(), "uuid".to_string());
308 m.insert("lot_id".to_string(), "uuid".to_string());
309 m.insert("package_id".to_string(), "uuid".to_string());
310 m.insert("owner_id".to_string(), "uuid".to_string());
311 m.insert("in_date".to_string(), "timestamptz".to_string());
312 m.insert("inventory_date".to_string(), "date".to_string());
313 m
314 }
315 fn search_fields() -> &'static [&'static str] {
316 &[]
317 }
318 fn relations() -> &'static [(&'static str, &'static str, &'static str)] {
319 &[("location", "locations", "locationId"), ("lot", "lots", "lotId"), ("package", "packages", "packageId")]
320 }
321}
322
323#[derive(Debug, Clone, Default)]
328pub struct QuantBuilder {
329 item_id: Option<Uuid>,
330 location_id: Option<Uuid>,
331 lot_id: Option<Uuid>,
332 package_id: Option<Uuid>,
333 owner_id: Option<Uuid>,
334 quantity: Option<Decimal>,
335 reserved_quantity: Option<Decimal>,
336 available_quantity: Option<Decimal>,
337 in_date: Option<DateTime<Utc>>,
338 inventory_quantity: Option<Decimal>,
339 inventory_diff_quantity: Option<Decimal>,
340 inventory_quantity_set: Option<bool>,
341 inventory_date: Option<NaiveDate>,
342 sn_duplicated: Option<bool>,
343}
344
345impl QuantBuilder {
346 pub fn item_id(mut self, value: Uuid) -> Self {
348 self.item_id = Some(value);
349 self
350 }
351
352 pub fn location_id(mut self, value: Uuid) -> Self {
354 self.location_id = Some(value);
355 self
356 }
357
358 pub fn lot_id(mut self, value: Uuid) -> Self {
360 self.lot_id = Some(value);
361 self
362 }
363
364 pub fn package_id(mut self, value: Uuid) -> Self {
366 self.package_id = Some(value);
367 self
368 }
369
370 pub fn owner_id(mut self, value: Uuid) -> Self {
372 self.owner_id = Some(value);
373 self
374 }
375
376 pub fn quantity(mut self, value: Decimal) -> Self {
378 self.quantity = Some(value);
379 self
380 }
381
382 pub fn reserved_quantity(mut self, value: Decimal) -> Self {
384 self.reserved_quantity = Some(value);
385 self
386 }
387
388 pub fn available_quantity(mut self, value: Decimal) -> Self {
390 self.available_quantity = Some(value);
391 self
392 }
393
394 pub fn in_date(mut self, value: DateTime<Utc>) -> Self {
396 self.in_date = Some(value);
397 self
398 }
399
400 pub fn inventory_quantity(mut self, value: Decimal) -> Self {
402 self.inventory_quantity = Some(value);
403 self
404 }
405
406 pub fn inventory_diff_quantity(mut self, value: Decimal) -> Self {
408 self.inventory_diff_quantity = Some(value);
409 self
410 }
411
412 pub fn inventory_quantity_set(mut self, value: bool) -> Self {
414 self.inventory_quantity_set = Some(value);
415 self
416 }
417
418 pub fn inventory_date(mut self, value: NaiveDate) -> Self {
420 self.inventory_date = Some(value);
421 self
422 }
423
424 pub fn sn_duplicated(mut self, value: bool) -> Self {
426 self.sn_duplicated = Some(value);
427 self
428 }
429
430 pub fn build(self) -> Result<Quant, String> {
434 let item_id = self.item_id.ok_or_else(|| "item_id is required".to_string())?;
435 let location_id = self.location_id.ok_or_else(|| "location_id is required".to_string())?;
436
437 Ok(Quant {
438 id: Uuid::new_v4(),
439 item_id,
440 location_id,
441 lot_id: self.lot_id,
442 package_id: self.package_id,
443 owner_id: self.owner_id,
444 quantity: self.quantity.unwrap_or(Decimal::from(0)),
445 reserved_quantity: self.reserved_quantity.unwrap_or(Decimal::from(0)),
446 available_quantity: self.available_quantity.unwrap_or(Decimal::from(0)),
447 in_date: self.in_date,
448 inventory_quantity: self.inventory_quantity,
449 inventory_diff_quantity: self.inventory_diff_quantity,
450 inventory_quantity_set: self.inventory_quantity_set.unwrap_or(false),
451 inventory_date: self.inventory_date,
452 sn_duplicated: self.sn_duplicated.unwrap_or(false),
453 metadata: AuditMetadata::default(),
454 })
455 }
456}