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::RouteRule;
17use crate::domain::entity::AuditMetadata;
18use crate::domain::entity::ProcureMethod;
19use crate::domain::entity::RuleAction;
20use crate::domain::entity::RuleAuto;
21
22#[derive(Debug, Clone, Deserialize)]
31#[cfg_attr(feature = "openapi", derive(ToSchema))]
32#[serde(rename_all = "camelCase")]
33pub struct CreateRouteRuleDto {
34 #[cfg_attr(feature = "openapi", schema(example = "example"))]
35 pub name: String,
36 #[cfg_attr(feature = "openapi", schema(example = true))]
37 pub active: bool,
38 #[cfg_attr(feature = "openapi", schema(example = 42))]
39 pub sequence: i32,
40 pub action: RuleAction,
41 pub auto: RuleAuto,
42 #[serde(alias = "procure_method")]
43 pub procure_method: ProcureMethod,
44 #[cfg_attr(feature = "openapi", schema(example = 42))]
45 pub delay: i32,
46 #[serde(default, skip_serializing_if = "Option::is_none", alias = "location_src_id")]
47 pub location_src_id: Option<Uuid>,
48 #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
49 #[serde(alias = "location_dest_id")]
50 pub location_dest_id: Uuid,
51 #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
52 #[serde(alias = "picking_type_id")]
53 pub picking_type_id: Uuid,
54 #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
55 #[serde(alias = "route_id")]
56 pub route_id: Uuid,
57 #[serde(default, skip_serializing_if = "Option::is_none", alias = "warehouse_id")]
58 pub warehouse_id: Option<Uuid>,
59 #[cfg_attr(feature = "openapi", schema(example = true))]
60 #[serde(alias = "propagate_cancel")]
61 pub propagate_cancel: bool,
62}
63
64#[derive(Debug, Clone, Deserialize)]
73#[cfg_attr(feature = "openapi", derive(ToSchema))]
74#[serde(rename_all = "camelCase")]
75pub struct UpdateRouteRuleDto {
76 #[cfg_attr(feature = "openapi", schema(example = "example"))]
77 pub name: String,
78 #[cfg_attr(feature = "openapi", schema(example = true))]
79 pub active: bool,
80 #[cfg_attr(feature = "openapi", schema(example = 42))]
81 pub sequence: i32,
82 pub action: RuleAction,
83 pub auto: RuleAuto,
84 #[serde(alias = "procure_method")]
85 pub procure_method: ProcureMethod,
86 #[cfg_attr(feature = "openapi", schema(example = 42))]
87 pub delay: i32,
88 #[serde(default, skip_serializing_if = "Option::is_none", alias = "location_src_id")]
89 pub location_src_id: Option<Uuid>,
90 #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
91 #[serde(alias = "location_dest_id")]
92 pub location_dest_id: Uuid,
93 #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
94 #[serde(alias = "picking_type_id")]
95 pub picking_type_id: Uuid,
96 #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
97 #[serde(alias = "route_id")]
98 pub route_id: Uuid,
99 #[serde(default, skip_serializing_if = "Option::is_none", alias = "warehouse_id")]
100 pub warehouse_id: Option<Uuid>,
101 #[cfg_attr(feature = "openapi", schema(example = true))]
102 #[serde(alias = "propagate_cancel")]
103 pub propagate_cancel: bool,
104}
105
106#[derive(Debug, Clone, Default, Deserialize)]
115#[cfg_attr(feature = "openapi", derive(ToSchema))]
116#[serde(rename_all = "camelCase")]
117pub struct PatchRouteRuleDto {
118 #[cfg_attr(feature = "openapi", schema(example = "example"))]
119 #[serde(skip_serializing_if = "Option::is_none")]
120 pub name: Option<String>,
121 #[cfg_attr(feature = "openapi", schema(example = true))]
122 #[serde(skip_serializing_if = "Option::is_none")]
123 pub active: Option<bool>,
124 #[cfg_attr(feature = "openapi", schema(example = 42))]
125 #[serde(skip_serializing_if = "Option::is_none")]
126 pub sequence: Option<i32>,
127 #[serde(skip_serializing_if = "Option::is_none")]
128 pub action: Option<RuleAction>,
129 #[serde(skip_serializing_if = "Option::is_none")]
130 pub auto: Option<RuleAuto>,
131 #[serde(skip_serializing_if = "Option::is_none", alias = "procure_method")]
132 pub procure_method: Option<ProcureMethod>,
133 #[cfg_attr(feature = "openapi", schema(example = 42))]
134 #[serde(skip_serializing_if = "Option::is_none")]
135 pub delay: Option<i32>,
136 #[serde(skip_serializing_if = "Option::is_none", alias = "location_src_id")]
137 pub location_src_id: Option<Uuid>,
138 #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
139 #[serde(skip_serializing_if = "Option::is_none", alias = "location_dest_id")]
140 pub location_dest_id: Option<Uuid>,
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 = "route_id")]
146 pub route_id: Option<Uuid>,
147 #[serde(skip_serializing_if = "Option::is_none", alias = "warehouse_id")]
148 pub warehouse_id: Option<Uuid>,
149 #[cfg_attr(feature = "openapi", schema(example = true))]
150 #[serde(skip_serializing_if = "Option::is_none", alias = "propagate_cancel")]
151 pub propagate_cancel: Option<bool>,
152}
153
154impl PatchRouteRuleDto {
155 pub fn has_changes(&self) -> bool {
157 self.name.is_some() || self.active.is_some() || self.sequence.is_some() || self.action.is_some() || self.auto.is_some() || self.procure_method.is_some() || self.delay.is_some() || self.location_src_id.is_some() || self.location_dest_id.is_some() || self.picking_type_id.is_some() || self.route_id.is_some() || self.warehouse_id.is_some() || self.propagate_cancel.is_some()
158 }
159}
160
161#[derive(Debug, Clone, Serialize)]
170#[cfg_attr(feature = "openapi", derive(ToSchema))]
171#[serde(rename_all = "camelCase")]
172pub struct RouteRuleResponseDto {
173 #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
174 pub id: Uuid,
175 #[cfg_attr(feature = "openapi", schema(example = "example"))]
176 pub name: String,
177 #[cfg_attr(feature = "openapi", schema(example = true))]
178 pub active: bool,
179 #[cfg_attr(feature = "openapi", schema(example = 42))]
180 pub sequence: i32,
181 pub action: RuleAction,
182 pub auto: RuleAuto,
183 pub procure_method: ProcureMethod,
184 #[cfg_attr(feature = "openapi", schema(example = 42))]
185 pub delay: i32,
186 pub location_src_id: Option<Uuid>,
187 #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
188 pub location_dest_id: Uuid,
189 #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
190 pub picking_type_id: Uuid,
191 #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
192 pub route_id: Uuid,
193 pub warehouse_id: Option<Uuid>,
194 #[cfg_attr(feature = "openapi", schema(example = true))]
195 pub propagate_cancel: bool,
196 pub metadata: AuditMetadata,
197}
198
199#[derive(Debug, Clone, Serialize)]
208#[cfg_attr(feature = "openapi", derive(ToSchema))]
209#[serde(rename_all = "camelCase")]
210pub struct RouteRuleListResponseDto {
211 pub items: Vec<RouteRuleResponseDto>,
213 pub total: u64,
215 pub page: u32,
217 pub per_page: u32,
219 pub total_pages: u32,
221 pub has_next: bool,
223 pub has_prev: bool,
225}
226
227impl RouteRuleListResponseDto {
228 pub fn new(items: Vec<RouteRuleResponseDto>, total: u64, page: u32, per_page: u32) -> Self {
230 let total_pages = if per_page > 0 {
231 ((total as f64) / (per_page as f64)).ceil() as u32
232 } else {
233 0
234 };
235 Self {
236 items,
237 total,
238 page,
239 per_page,
240 total_pages,
241 has_next: page < total_pages,
242 has_prev: page > 1,
243 }
244 }
245}
246
247#[derive(Debug, Clone, Serialize)]
249#[cfg_attr(feature = "openapi", derive(ToSchema))]
250#[serde(rename_all = "camelCase")]
251pub struct RouteRuleSummaryDto {
252 pub id: Uuid,
253 pub name: String,
254 pub active: bool,
255 pub sequence: i32,
256 pub created_at: Option<DateTime<Utc>>,
257}
258
259impl From<RouteRule> for RouteRuleResponseDto {
264 fn from(entity: RouteRule) -> Self {
265 Self {
266 id: entity.id,
267 name: entity.name,
268 active: entity.active,
269 sequence: entity.sequence,
270 action: entity.action,
271 auto: entity.auto,
272 procure_method: entity.procure_method,
273 delay: entity.delay,
274 location_src_id: entity.location_src_id,
275 location_dest_id: entity.location_dest_id,
276 picking_type_id: entity.picking_type_id,
277 route_id: entity.route_id,
278 warehouse_id: entity.warehouse_id,
279 propagate_cancel: entity.propagate_cancel,
280 metadata: entity.metadata,
281 }
282 }
283}
284
285impl From<RouteRule> for RouteRuleSummaryDto {
286 fn from(entity: RouteRule) -> Self {
287 let created_at = backbone_core::PersistentEntity::created_at(&entity);
288 Self {
289 id: entity.id,
290 name: entity.name,
291 active: entity.active,
292 sequence: entity.sequence,
293 created_at,
294 }
295 }
296}
297
298impl From<CreateRouteRuleDto> for RouteRule {
299 fn from(dto: CreateRouteRuleDto) -> Self {
300 Self {
301 id: Uuid::new_v4(),
302 name: dto.name,
303 active: dto.active,
304 sequence: dto.sequence,
305 action: dto.action,
306 auto: dto.auto,
307 procure_method: dto.procure_method,
308 delay: dto.delay,
309 location_src_id: dto.location_src_id,
310 location_dest_id: dto.location_dest_id,
311 picking_type_id: dto.picking_type_id,
312 route_id: dto.route_id,
313 warehouse_id: dto.warehouse_id,
314 propagate_cancel: dto.propagate_cancel,
315 metadata: AuditMetadata::default(),
316 }
317 }
318}
319
320impl From<&RouteRule> for RouteRuleResponseDto {
321 fn from(entity: &RouteRule) -> Self {
322 Self {
323 id: entity.id.clone(),
324 name: entity.name.clone(),
325 active: entity.active.clone(),
326 sequence: entity.sequence.clone(),
327 action: entity.action.clone(),
328 auto: entity.auto.clone(),
329 procure_method: entity.procure_method.clone(),
330 delay: entity.delay.clone(),
331 location_src_id: entity.location_src_id.clone(),
332 location_dest_id: entity.location_dest_id.clone(),
333 picking_type_id: entity.picking_type_id.clone(),
334 route_id: entity.route_id.clone(),
335 warehouse_id: entity.warehouse_id.clone(),
336 propagate_cancel: entity.propagate_cancel.clone(),
337 metadata: entity.metadata.clone(),
338 }
339 }
340}
341
342impl backbone_core::FromCreateDto<CreateRouteRuleDto> for RouteRule {
343 fn from_create_dto(dto: CreateRouteRuleDto) -> backbone_core::ServiceResult<Self> {
344 Ok(RouteRule::from(dto))
345 }
346}
347
348impl backbone_core::ApplyUpdateDto<UpdateRouteRuleDto> for RouteRule {
349 fn apply_update(mut self, dto: UpdateRouteRuleDto) -> backbone_core::ServiceResult<Self> {
350 self.name = dto.name;
351 self.active = dto.active;
352 self.sequence = dto.sequence;
353 self.action = dto.action;
354 self.auto = dto.auto;
355 self.procure_method = dto.procure_method;
356 self.delay = dto.delay;
357 self.location_src_id = dto.location_src_id;
358 self.location_dest_id = dto.location_dest_id;
359 self.picking_type_id = dto.picking_type_id;
360 self.route_id = dto.route_id;
361 self.warehouse_id = dto.warehouse_id;
362 self.propagate_cancel = dto.propagate_cancel;
363 Ok(self)
364 }
365}
366
367