Skip to main content

backbone_inventory/presentation/dto/
transfer_dto.rs

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