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