Skip to main content

backbone_inventory/presentation/dto/
quant_dto.rs

1//! Quant DTOs (Data Transfer Objects)
2//!
3//! Generated by metaphor-schema. Do not edit manually.
4//!
5//! DTOs provide a clean separation between domain entities and API
6//! representations, with validation and OpenAPI documentation support.
7
8use serde::{Deserialize, Serialize};
9use uuid::Uuid;
10use chrono::{DateTime, Utc, NaiveDate};
11use rust_decimal::Decimal;
12
13#[cfg(feature = "openapi")]
14#[cfg(feature = "openapi")]
15use utoipa::ToSchema;
16
17#[cfg(feature = "validation")]
18use validator::Validate;
19
20use crate::domain::entity::Quant;
21use crate::domain::entity::AuditMetadata;
22
23// =============================================================================
24// Create DTO
25// =============================================================================
26
27/// Request DTO for creating a new Quant
28///
29/// Used in POST requests to create new entities.
30/// Excludes auto-generated fields (id, created_at, updated_at, deleted_at).
31#[derive(Debug, Clone, Deserialize)]
32#[cfg_attr(feature = "openapi", derive(ToSchema))]
33#[cfg_attr(feature = "validation", derive(Validate))]
34#[serde(rename_all = "camelCase")]
35pub struct CreateQuantDto {
36    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
37    #[serde(alias = "item_id")]
38    pub item_id: Uuid,
39    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
40    #[serde(alias = "location_id")]
41    pub location_id: Uuid,
42    #[serde(default, skip_serializing_if = "Option::is_none", alias = "lot_id")]
43    pub lot_id: Option<Uuid>,
44    #[serde(default, skip_serializing_if = "Option::is_none", alias = "package_id")]
45    pub package_id: Option<Uuid>,
46    #[serde(default, skip_serializing_if = "Option::is_none", alias = "owner_id")]
47    pub owner_id: Option<Uuid>,
48    pub quantity: Decimal,
49    #[serde(alias = "reserved_quantity")]
50    pub reserved_quantity: Decimal,
51    #[serde(alias = "available_quantity")]
52    pub available_quantity: Decimal,
53    #[serde(default, skip_serializing_if = "Option::is_none", alias = "in_date")]
54    pub in_date: Option<DateTime<Utc>>,
55    #[serde(default, skip_serializing_if = "Option::is_none", alias = "inventory_quantity")]
56    pub inventory_quantity: Option<Decimal>,
57    #[serde(default, skip_serializing_if = "Option::is_none", alias = "inventory_diff_quantity")]
58    pub inventory_diff_quantity: Option<Decimal>,
59    #[cfg_attr(feature = "openapi", schema(example = true))]
60    #[serde(alias = "inventory_quantity_set")]
61    pub inventory_quantity_set: bool,
62    #[serde(default, skip_serializing_if = "Option::is_none", alias = "inventory_date")]
63    pub inventory_date: Option<NaiveDate>,
64    #[cfg_attr(feature = "openapi", schema(example = true))]
65    #[serde(alias = "sn_duplicated")]
66    pub sn_duplicated: bool,
67}
68
69// =============================================================================
70// Update DTO
71// =============================================================================
72
73/// Request DTO for full update of a Quant
74///
75/// Used in PUT requests for full entity replacement.
76/// All fields are required (except auto-generated ones).
77#[derive(Debug, Clone, Deserialize)]
78#[cfg_attr(feature = "openapi", derive(ToSchema))]
79#[cfg_attr(feature = "validation", derive(Validate))]
80#[serde(rename_all = "camelCase")]
81pub struct UpdateQuantDto {
82    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
83    #[serde(alias = "item_id")]
84    pub item_id: Uuid,
85    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
86    #[serde(alias = "location_id")]
87    pub location_id: Uuid,
88    #[serde(default, skip_serializing_if = "Option::is_none", alias = "lot_id")]
89    pub lot_id: Option<Uuid>,
90    #[serde(default, skip_serializing_if = "Option::is_none", alias = "package_id")]
91    pub package_id: Option<Uuid>,
92    #[serde(default, skip_serializing_if = "Option::is_none", alias = "owner_id")]
93    pub owner_id: Option<Uuid>,
94    pub quantity: Decimal,
95    #[serde(alias = "reserved_quantity")]
96    pub reserved_quantity: Decimal,
97    #[serde(alias = "available_quantity")]
98    pub available_quantity: Decimal,
99    #[serde(default, skip_serializing_if = "Option::is_none", alias = "in_date")]
100    pub in_date: Option<DateTime<Utc>>,
101    #[serde(default, skip_serializing_if = "Option::is_none", alias = "inventory_quantity")]
102    pub inventory_quantity: Option<Decimal>,
103    #[serde(default, skip_serializing_if = "Option::is_none", alias = "inventory_diff_quantity")]
104    pub inventory_diff_quantity: Option<Decimal>,
105    #[cfg_attr(feature = "openapi", schema(example = true))]
106    #[serde(alias = "inventory_quantity_set")]
107    pub inventory_quantity_set: bool,
108    #[serde(default, skip_serializing_if = "Option::is_none", alias = "inventory_date")]
109    pub inventory_date: Option<NaiveDate>,
110    #[cfg_attr(feature = "openapi", schema(example = true))]
111    #[serde(alias = "sn_duplicated")]
112    pub sn_duplicated: bool,
113}
114
115// =============================================================================
116// Patch DTO
117// =============================================================================
118
119/// Request DTO for partial update of a Quant
120///
121/// Used in PATCH requests for partial updates.
122/// All fields are optional - only provided fields will be updated.
123#[derive(Debug, Clone, Default, Deserialize)]
124#[cfg_attr(feature = "openapi", derive(ToSchema))]
125#[cfg_attr(feature = "validation", derive(Validate))]
126#[serde(rename_all = "camelCase")]
127pub struct PatchQuantDto {
128    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
129    #[serde(skip_serializing_if = "Option::is_none", alias = "item_id")]
130    pub item_id: Option<Uuid>,
131    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
132    #[serde(skip_serializing_if = "Option::is_none", alias = "location_id")]
133    pub location_id: Option<Uuid>,
134    #[serde(skip_serializing_if = "Option::is_none", alias = "lot_id")]
135    pub lot_id: Option<Uuid>,
136    #[serde(skip_serializing_if = "Option::is_none", alias = "package_id")]
137    pub package_id: Option<Uuid>,
138    #[serde(skip_serializing_if = "Option::is_none", alias = "owner_id")]
139    pub owner_id: Option<Uuid>,
140    #[serde(skip_serializing_if = "Option::is_none")]
141    pub quantity: Option<Decimal>,
142    #[serde(skip_serializing_if = "Option::is_none", alias = "reserved_quantity")]
143    pub reserved_quantity: Option<Decimal>,
144    #[serde(skip_serializing_if = "Option::is_none", alias = "available_quantity")]
145    pub available_quantity: Option<Decimal>,
146    #[serde(skip_serializing_if = "Option::is_none", alias = "in_date")]
147    pub in_date: Option<DateTime<Utc>>,
148    #[serde(skip_serializing_if = "Option::is_none", alias = "inventory_quantity")]
149    pub inventory_quantity: Option<Decimal>,
150    #[serde(skip_serializing_if = "Option::is_none", alias = "inventory_diff_quantity")]
151    pub inventory_diff_quantity: Option<Decimal>,
152    #[cfg_attr(feature = "openapi", schema(example = true))]
153    #[serde(skip_serializing_if = "Option::is_none", alias = "inventory_quantity_set")]
154    pub inventory_quantity_set: Option<bool>,
155    #[serde(skip_serializing_if = "Option::is_none", alias = "inventory_date")]
156    pub inventory_date: Option<NaiveDate>,
157    #[cfg_attr(feature = "openapi", schema(example = true))]
158    #[serde(skip_serializing_if = "Option::is_none", alias = "sn_duplicated")]
159    pub sn_duplicated: Option<bool>,
160}
161
162impl PatchQuantDto {
163    /// Check if any field is set
164    pub fn has_changes(&self) -> bool {
165        self.item_id.is_some() || self.location_id.is_some() || self.lot_id.is_some() || self.package_id.is_some() || self.owner_id.is_some() || self.quantity.is_some() || self.reserved_quantity.is_some() || self.available_quantity.is_some() || self.in_date.is_some() || self.inventory_quantity.is_some() || self.inventory_diff_quantity.is_some() || self.inventory_quantity_set.is_some() || self.inventory_date.is_some() || self.sn_duplicated.is_some()
166    }
167}
168
169// =============================================================================
170// Response DTO
171// =============================================================================
172
173/// Response DTO for Quant entity
174///
175/// Used in API responses for single entity.
176/// Includes all fields including metadata.
177#[derive(Debug, Clone, Serialize)]
178#[cfg_attr(feature = "openapi", derive(ToSchema))]
179#[serde(rename_all = "camelCase")]
180pub struct QuantResponseDto {
181    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
182    pub id: Uuid,
183    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
184    pub item_id: Uuid,
185    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
186    pub location_id: Uuid,
187    pub lot_id: Option<Uuid>,
188    pub package_id: Option<Uuid>,
189    pub owner_id: Option<Uuid>,
190    pub quantity: Decimal,
191    pub reserved_quantity: Decimal,
192    pub available_quantity: Decimal,
193    pub in_date: Option<DateTime<Utc>>,
194    pub inventory_quantity: Option<Decimal>,
195    pub inventory_diff_quantity: Option<Decimal>,
196    #[cfg_attr(feature = "openapi", schema(example = true))]
197    pub inventory_quantity_set: bool,
198    pub inventory_date: Option<NaiveDate>,
199    #[cfg_attr(feature = "openapi", schema(example = true))]
200    pub sn_duplicated: bool,
201    pub metadata: AuditMetadata,
202}
203
204// =============================================================================
205// List Response DTO
206// =============================================================================
207
208/// Paginated list response for Quant entities
209///
210/// Used in API responses for list endpoints.
211/// Includes pagination metadata.
212#[derive(Debug, Clone, Serialize)]
213#[cfg_attr(feature = "openapi", derive(ToSchema))]
214#[serde(rename_all = "camelCase")]
215pub struct QuantListResponseDto {
216    /// List of items
217    pub items: Vec<QuantResponseDto>,
218    /// Total number of items (across all pages)
219    pub total: u64,
220    /// Current page number (1-indexed)
221    pub page: u32,
222    /// Number of items per page
223    pub per_page: u32,
224    /// Total number of pages
225    pub total_pages: u32,
226    /// Whether there is a next page
227    pub has_next: bool,
228    /// Whether there is a previous page
229    pub has_prev: bool,
230}
231
232impl QuantListResponseDto {
233    /// Create a new list response from items and pagination info
234    pub fn new(items: Vec<QuantResponseDto>, total: u64, page: u32, per_page: u32) -> Self {
235        let total_pages = if per_page > 0 {
236            ((total as f64) / (per_page as f64)).ceil() as u32
237        } else {
238            0
239        };
240        Self {
241            items,
242            total,
243            page,
244            per_page,
245            total_pages,
246            has_next: page < total_pages,
247            has_prev: page > 1,
248        }
249    }
250}
251
252/// Summary DTO for list views (compact version)
253#[derive(Debug, Clone, Serialize)]
254#[cfg_attr(feature = "openapi", derive(ToSchema))]
255#[serde(rename_all = "camelCase")]
256pub struct QuantSummaryDto {
257    pub id: Uuid,
258    pub item_id: Uuid,
259    pub location_id: Uuid,
260    pub lot_id: Option<Uuid>,
261    pub created_at: Option<DateTime<Utc>>,
262}
263
264// =============================================================================
265// Conversions
266// =============================================================================
267
268impl From<Quant> for QuantResponseDto {
269    fn from(entity: Quant) -> Self {
270        Self {
271            id: entity.id,
272            item_id: entity.item_id,
273            location_id: entity.location_id,
274            lot_id: entity.lot_id,
275            package_id: entity.package_id,
276            owner_id: entity.owner_id,
277            quantity: entity.quantity,
278            reserved_quantity: entity.reserved_quantity,
279            available_quantity: entity.available_quantity,
280            in_date: entity.in_date,
281            inventory_quantity: entity.inventory_quantity,
282            inventory_diff_quantity: entity.inventory_diff_quantity,
283            inventory_quantity_set: entity.inventory_quantity_set,
284            inventory_date: entity.inventory_date,
285            sn_duplicated: entity.sn_duplicated,
286            metadata: entity.metadata,
287        }
288    }
289}
290
291impl From<Quant> for QuantSummaryDto {
292    fn from(entity: Quant) -> Self {
293        let created_at = backbone_core::PersistentEntity::created_at(&entity);
294        Self {
295            id: entity.id,
296            item_id: entity.item_id,
297            location_id: entity.location_id,
298            lot_id: entity.lot_id,
299            created_at,
300        }
301    }
302}
303
304impl From<CreateQuantDto> for Quant {
305    fn from(dto: CreateQuantDto) -> Self {
306        Self {
307            id: Uuid::new_v4(),
308            item_id: dto.item_id,
309            location_id: dto.location_id,
310            lot_id: dto.lot_id,
311            package_id: dto.package_id,
312            owner_id: dto.owner_id,
313            quantity: dto.quantity,
314            reserved_quantity: dto.reserved_quantity,
315            available_quantity: dto.available_quantity,
316            in_date: dto.in_date,
317            inventory_quantity: dto.inventory_quantity,
318            inventory_diff_quantity: dto.inventory_diff_quantity,
319            inventory_quantity_set: dto.inventory_quantity_set,
320            inventory_date: dto.inventory_date,
321            sn_duplicated: dto.sn_duplicated,
322            metadata: AuditMetadata::default(),
323        }
324    }
325}
326
327impl From<&Quant> for QuantResponseDto {
328    fn from(entity: &Quant) -> Self {
329        Self {
330            id: entity.id.clone(),
331            item_id: entity.item_id.clone(),
332            location_id: entity.location_id.clone(),
333            lot_id: entity.lot_id.clone(),
334            package_id: entity.package_id.clone(),
335            owner_id: entity.owner_id.clone(),
336            quantity: entity.quantity.clone(),
337            reserved_quantity: entity.reserved_quantity.clone(),
338            available_quantity: entity.available_quantity.clone(),
339            in_date: entity.in_date.clone(),
340            inventory_quantity: entity.inventory_quantity.clone(),
341            inventory_diff_quantity: entity.inventory_diff_quantity.clone(),
342            inventory_quantity_set: entity.inventory_quantity_set.clone(),
343            inventory_date: entity.inventory_date.clone(),
344            sn_duplicated: entity.sn_duplicated.clone(),
345            metadata: entity.metadata.clone(),
346        }
347    }
348}
349
350impl backbone_core::FromCreateDto<CreateQuantDto> for Quant {
351    fn from_create_dto(dto: CreateQuantDto) -> backbone_core::ServiceResult<Self> {
352        Ok(Quant::from(dto))
353    }
354}
355
356impl backbone_core::ApplyUpdateDto<UpdateQuantDto> for Quant {
357    fn apply_update(mut self, dto: UpdateQuantDto) -> backbone_core::ServiceResult<Self> {
358        self.item_id = dto.item_id;
359        self.location_id = dto.location_id;
360        self.lot_id = dto.lot_id;
361        self.package_id = dto.package_id;
362        self.owner_id = dto.owner_id;
363        self.quantity = dto.quantity;
364        self.reserved_quantity = dto.reserved_quantity;
365        self.available_quantity = dto.available_quantity;
366        self.in_date = dto.in_date;
367        self.inventory_quantity = dto.inventory_quantity;
368        self.inventory_diff_quantity = dto.inventory_diff_quantity;
369        self.inventory_quantity_set = dto.inventory_quantity_set;
370        self.inventory_date = dto.inventory_date;
371        self.sn_duplicated = dto.sn_duplicated;
372        Ok(self)
373    }
374}
375
376// =============================================================================
377// Custom DTOs
378// =============================================================================
379
380// Add custom DTOs specific to Quant here.
381// This section will be preserved during regeneration.
382// >>> END CUSTOM DTOs
383