1use 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#[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#[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#[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 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#[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#[derive(Debug, Clone, Serialize)]
227#[cfg_attr(feature = "openapi", derive(ToSchema))]
228#[serde(rename_all = "camelCase")]
229pub struct TransferListResponseDto {
230 pub items: Vec<TransferResponseDto>,
232 pub total: u64,
234 pub page: u32,
236 pub per_page: u32,
238 pub total_pages: u32,
240 pub has_next: bool,
242 pub has_prev: bool,
244}
245
246impl TransferListResponseDto {
247 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#[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
278impl 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