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 #[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#[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#[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 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#[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#[derive(Debug, Clone, Serialize)]
226#[cfg_attr(feature = "openapi", derive(ToSchema))]
227#[serde(rename_all = "camelCase")]
228pub struct TransferListResponseDto {
229 pub items: Vec<TransferResponseDto>,
231 pub total: u64,
233 pub page: u32,
235 pub per_page: u32,
237 pub total_pages: u32,
239 pub has_next: bool,
241 pub has_prev: bool,
243}
244
245impl TransferListResponseDto {
246 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#[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
277impl 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