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