backbone_pos/presentation/dto/
pos_table_dto.rs1use serde::{Deserialize, Serialize};
9use uuid::Uuid;
10use chrono::{DateTime, Utc};
11
12#[cfg(feature = "openapi")]
13#[cfg(feature = "openapi")]
14use utoipa::ToSchema;
15
16#[cfg(feature = "validation")]
17use validator::Validate;
18
19use crate::domain::entity::PosTable;
20use crate::domain::entity::AuditMetadata;
21
22#[derive(Debug, Clone, Deserialize)]
31#[cfg_attr(feature = "openapi", derive(ToSchema))]
32#[cfg_attr(feature = "validation", derive(Validate))]
33#[serde(rename_all = "camelCase")]
34pub struct CreatePosTableDto {
35 #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
36 #[serde(alias = "pos_floor_plan_id")]
37 pub pos_floor_plan_id: Uuid,
38 #[cfg_attr(feature = "validation", validate(length(max = 140)))]
39 #[serde(default, skip_serializing_if = "Option::is_none")]
40 pub name: Option<String>,
41 #[serde(default, skip_serializing_if = "Option::is_none")]
42 pub seats: Option<i32>,
43 #[cfg_attr(feature = "validation", validate(length(max = 30)))]
44 #[serde(default, skip_serializing_if = "Option::is_none")]
45 pub shape: Option<String>,
46 #[serde(default, skip_serializing_if = "Option::is_none")]
47 pub position: Option<serde_json::Value>,
48}
49
50#[derive(Debug, Clone, Deserialize)]
59#[cfg_attr(feature = "openapi", derive(ToSchema))]
60#[cfg_attr(feature = "validation", derive(Validate))]
61#[serde(rename_all = "camelCase")]
62pub struct UpdatePosTableDto {
63 #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
64 #[serde(alias = "pos_floor_plan_id")]
65 pub pos_floor_plan_id: Uuid,
66 #[cfg_attr(feature = "validation", validate(length(max = 140)))]
67 #[serde(default, skip_serializing_if = "Option::is_none")]
68 pub name: Option<String>,
69 #[serde(default, skip_serializing_if = "Option::is_none")]
70 pub seats: Option<i32>,
71 #[cfg_attr(feature = "validation", validate(length(max = 30)))]
72 #[serde(default, skip_serializing_if = "Option::is_none")]
73 pub shape: Option<String>,
74 #[serde(default, skip_serializing_if = "Option::is_none")]
75 pub position: Option<serde_json::Value>,
76}
77
78#[derive(Debug, Clone, Default, Deserialize)]
87#[cfg_attr(feature = "openapi", derive(ToSchema))]
88#[cfg_attr(feature = "validation", derive(Validate))]
89#[serde(rename_all = "camelCase")]
90pub struct PatchPosTableDto {
91 #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
92 #[serde(skip_serializing_if = "Option::is_none", alias = "pos_floor_plan_id")]
93 pub pos_floor_plan_id: Option<Uuid>,
94 #[cfg_attr(feature = "validation", validate(length(max = 140)))]
95 #[serde(skip_serializing_if = "Option::is_none")]
96 pub name: Option<String>,
97 #[serde(skip_serializing_if = "Option::is_none")]
98 pub seats: Option<i32>,
99 #[cfg_attr(feature = "validation", validate(length(max = 30)))]
100 #[serde(skip_serializing_if = "Option::is_none")]
101 pub shape: Option<String>,
102 #[serde(skip_serializing_if = "Option::is_none")]
103 pub position: Option<serde_json::Value>,
104}
105
106impl PatchPosTableDto {
107 pub fn has_changes(&self) -> bool {
109 self.pos_floor_plan_id.is_some() || self.name.is_some() || self.seats.is_some() || self.shape.is_some() || self.position.is_some()
110 }
111}
112
113#[derive(Debug, Clone, Serialize)]
122#[cfg_attr(feature = "openapi", derive(ToSchema))]
123#[serde(rename_all = "camelCase")]
124pub struct PosTableResponseDto {
125 #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
126 pub id: Uuid,
127 #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
128 pub pos_floor_plan_id: Uuid,
129 pub name: Option<String>,
130 pub seats: Option<i32>,
131 pub shape: Option<String>,
132 pub position: Option<serde_json::Value>,
133 pub metadata: AuditMetadata,
134}
135
136#[derive(Debug, Clone, Serialize)]
145#[cfg_attr(feature = "openapi", derive(ToSchema))]
146#[serde(rename_all = "camelCase")]
147pub struct PosTableListResponseDto {
148 pub items: Vec<PosTableResponseDto>,
150 pub total: u64,
152 pub page: u32,
154 pub per_page: u32,
156 pub total_pages: u32,
158 pub has_next: bool,
160 pub has_prev: bool,
162}
163
164impl PosTableListResponseDto {
165 pub fn new(items: Vec<PosTableResponseDto>, total: u64, page: u32, per_page: u32) -> Self {
167 let total_pages = if per_page > 0 {
168 ((total as f64) / (per_page as f64)).ceil() as u32
169 } else {
170 0
171 };
172 Self {
173 items,
174 total,
175 page,
176 per_page,
177 total_pages,
178 has_next: page < total_pages,
179 has_prev: page > 1,
180 }
181 }
182}
183
184#[derive(Debug, Clone, Serialize)]
186#[cfg_attr(feature = "openapi", derive(ToSchema))]
187#[serde(rename_all = "camelCase")]
188pub struct PosTableSummaryDto {
189 pub id: Uuid,
190 pub pos_floor_plan_id: Uuid,
191 pub name: Option<String>,
192 pub seats: Option<i32>,
193 pub created_at: Option<DateTime<Utc>>,
194}
195
196impl From<PosTable> for PosTableResponseDto {
201 fn from(entity: PosTable) -> Self {
202 Self {
203 id: entity.id,
204 pos_floor_plan_id: entity.pos_floor_plan_id,
205 name: entity.name,
206 seats: entity.seats,
207 shape: entity.shape,
208 position: entity.position,
209 metadata: entity.metadata,
210 }
211 }
212}
213
214impl From<PosTable> for PosTableSummaryDto {
215 fn from(entity: PosTable) -> Self {
216 let created_at = backbone_core::PersistentEntity::created_at(&entity);
217 Self {
218 id: entity.id,
219 pos_floor_plan_id: entity.pos_floor_plan_id,
220 name: entity.name,
221 seats: entity.seats,
222 created_at,
223 }
224 }
225}
226
227impl From<CreatePosTableDto> for PosTable {
228 fn from(dto: CreatePosTableDto) -> Self {
229 Self {
230 id: Uuid::new_v4(),
231 pos_floor_plan_id: dto.pos_floor_plan_id,
232 name: dto.name,
233 seats: dto.seats,
234 shape: dto.shape,
235 position: dto.position,
236 metadata: AuditMetadata::default(),
237 }
238 }
239}
240
241impl From<&PosTable> for PosTableResponseDto {
242 fn from(entity: &PosTable) -> Self {
243 Self {
244 id: entity.id.clone(),
245 pos_floor_plan_id: entity.pos_floor_plan_id.clone(),
246 name: entity.name.clone(),
247 seats: entity.seats.clone(),
248 shape: entity.shape.clone(),
249 position: entity.position.clone(),
250 metadata: entity.metadata.clone(),
251 }
252 }
253}
254
255impl backbone_core::FromCreateDto<CreatePosTableDto> for PosTable {
256 fn from_create_dto(dto: CreatePosTableDto) -> backbone_core::ServiceResult<Self> {
257 Ok(PosTable::from(dto))
258 }
259}
260
261impl backbone_core::ApplyUpdateDto<UpdatePosTableDto> for PosTable {
262 fn apply_update(mut self, dto: UpdatePosTableDto) -> backbone_core::ServiceResult<Self> {
263 self.pos_floor_plan_id = dto.pos_floor_plan_id;
264 self.name = dto.name;
265 self.seats = dto.seats;
266 self.shape = dto.shape;
267 self.position = dto.position;
268 Ok(self)
269 }
270}
271
272