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