Skip to main content

backbone_inventory/presentation/dto/
package_dto.rs

1//! Package 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 validation and OpenAPI documentation support.
7
8use serde::{Deserialize, Serialize};
9use uuid::Uuid;
10use chrono::{DateTime, Utc, NaiveDate};
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::Package;
20use crate::domain::entity::AuditMetadata;
21
22// =============================================================================
23// Create DTO
24// =============================================================================
25
26/// Request DTO for creating a new Package
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#[cfg_attr(feature = "validation", derive(Validate))]
33#[serde(rename_all = "camelCase")]
34pub struct CreatePackageDto {
35    #[cfg_attr(feature = "validation", validate(length(max = 150)))]
36    #[cfg_attr(feature = "openapi", schema(example = "example"))]
37    pub name: String,
38    #[cfg_attr(feature = "validation", validate(length(max = 1000)))]
39    #[cfg_attr(feature = "openapi", schema(example = "example"))]
40    #[serde(alias = "complete_name")]
41    pub complete_name: String,
42    #[serde(default, skip_serializing_if = "Option::is_none", alias = "location_id")]
43    pub location_id: Option<Uuid>,
44    #[serde(default, skip_serializing_if = "Option::is_none", alias = "parent_package_id")]
45    pub parent_package_id: Option<Uuid>,
46    #[cfg_attr(feature = "validation", validate(length(max = 1000)))]
47    #[cfg_attr(feature = "openapi", schema(example = "example"))]
48    #[serde(alias = "parent_path")]
49    pub parent_path: String,
50    #[serde(default, skip_serializing_if = "Option::is_none", alias = "pack_date")]
51    pub pack_date: Option<NaiveDate>,
52    #[serde(default, skip_serializing_if = "Option::is_none", alias = "package_type_id")]
53    pub package_type_id: Option<Uuid>,
54}
55
56// =============================================================================
57// Update DTO
58// =============================================================================
59
60/// Request DTO for full update of a Package
61///
62/// Used in PUT requests for full entity replacement.
63/// All fields are required (except auto-generated ones).
64#[derive(Debug, Clone, Deserialize)]
65#[cfg_attr(feature = "openapi", derive(ToSchema))]
66#[cfg_attr(feature = "validation", derive(Validate))]
67#[serde(rename_all = "camelCase")]
68pub struct UpdatePackageDto {
69    #[cfg_attr(feature = "validation", validate(length(max = 150)))]
70    #[cfg_attr(feature = "openapi", schema(example = "example"))]
71    pub name: String,
72    #[cfg_attr(feature = "validation", validate(length(max = 1000)))]
73    #[cfg_attr(feature = "openapi", schema(example = "example"))]
74    #[serde(alias = "complete_name")]
75    pub complete_name: String,
76    #[serde(default, skip_serializing_if = "Option::is_none", alias = "location_id")]
77    pub location_id: Option<Uuid>,
78    #[serde(default, skip_serializing_if = "Option::is_none", alias = "parent_package_id")]
79    pub parent_package_id: Option<Uuid>,
80    #[cfg_attr(feature = "validation", validate(length(max = 1000)))]
81    #[cfg_attr(feature = "openapi", schema(example = "example"))]
82    #[serde(alias = "parent_path")]
83    pub parent_path: String,
84    #[serde(default, skip_serializing_if = "Option::is_none", alias = "pack_date")]
85    pub pack_date: Option<NaiveDate>,
86    #[serde(default, skip_serializing_if = "Option::is_none", alias = "package_type_id")]
87    pub package_type_id: Option<Uuid>,
88}
89
90// =============================================================================
91// Patch DTO
92// =============================================================================
93
94/// Request DTO for partial update of a Package
95///
96/// Used in PATCH requests for partial updates.
97/// All fields are optional - only provided fields will be updated.
98#[derive(Debug, Clone, Default, Deserialize)]
99#[cfg_attr(feature = "openapi", derive(ToSchema))]
100#[cfg_attr(feature = "validation", derive(Validate))]
101#[serde(rename_all = "camelCase")]
102pub struct PatchPackageDto {
103    #[cfg_attr(feature = "validation", validate(length(max = 150)))]
104    #[cfg_attr(feature = "openapi", schema(example = "example"))]
105    #[serde(skip_serializing_if = "Option::is_none")]
106    pub name: Option<String>,
107    #[cfg_attr(feature = "validation", validate(length(max = 1000)))]
108    #[cfg_attr(feature = "openapi", schema(example = "example"))]
109    #[serde(skip_serializing_if = "Option::is_none", alias = "complete_name")]
110    pub complete_name: Option<String>,
111    #[serde(skip_serializing_if = "Option::is_none", alias = "location_id")]
112    pub location_id: Option<Uuid>,
113    #[serde(skip_serializing_if = "Option::is_none", alias = "parent_package_id")]
114    pub parent_package_id: Option<Uuid>,
115    #[cfg_attr(feature = "validation", validate(length(max = 1000)))]
116    #[cfg_attr(feature = "openapi", schema(example = "example"))]
117    #[serde(skip_serializing_if = "Option::is_none", alias = "parent_path")]
118    pub parent_path: Option<String>,
119    #[serde(skip_serializing_if = "Option::is_none", alias = "pack_date")]
120    pub pack_date: Option<NaiveDate>,
121    #[serde(skip_serializing_if = "Option::is_none", alias = "package_type_id")]
122    pub package_type_id: Option<Uuid>,
123}
124
125impl PatchPackageDto {
126    /// Check if any field is set
127    pub fn has_changes(&self) -> bool {
128        self.name.is_some() || self.complete_name.is_some() || self.location_id.is_some() || self.parent_package_id.is_some() || self.parent_path.is_some() || self.pack_date.is_some() || self.package_type_id.is_some()
129    }
130}
131
132// =============================================================================
133// Response DTO
134// =============================================================================
135
136/// Response DTO for Package entity
137///
138/// Used in API responses for single entity.
139/// Includes all fields including metadata.
140#[derive(Debug, Clone, Serialize)]
141#[cfg_attr(feature = "openapi", derive(ToSchema))]
142#[serde(rename_all = "camelCase")]
143pub struct PackageResponseDto {
144    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
145    pub id: Uuid,
146    #[cfg_attr(feature = "openapi", schema(example = "example"))]
147    pub name: String,
148    #[cfg_attr(feature = "openapi", schema(example = "example"))]
149    pub complete_name: String,
150    pub location_id: Option<Uuid>,
151    pub parent_package_id: Option<Uuid>,
152    #[cfg_attr(feature = "openapi", schema(example = "example"))]
153    pub parent_path: String,
154    pub pack_date: Option<NaiveDate>,
155    pub package_type_id: Option<Uuid>,
156    pub metadata: AuditMetadata,
157}
158
159// =============================================================================
160// List Response DTO
161// =============================================================================
162
163/// Paginated list response for Package entities
164///
165/// Used in API responses for list endpoints.
166/// Includes pagination metadata.
167#[derive(Debug, Clone, Serialize)]
168#[cfg_attr(feature = "openapi", derive(ToSchema))]
169#[serde(rename_all = "camelCase")]
170pub struct PackageListResponseDto {
171    /// List of items
172    pub items: Vec<PackageResponseDto>,
173    /// Total number of items (across all pages)
174    pub total: u64,
175    /// Current page number (1-indexed)
176    pub page: u32,
177    /// Number of items per page
178    pub per_page: u32,
179    /// Total number of pages
180    pub total_pages: u32,
181    /// Whether there is a next page
182    pub has_next: bool,
183    /// Whether there is a previous page
184    pub has_prev: bool,
185}
186
187impl PackageListResponseDto {
188    /// Create a new list response from items and pagination info
189    pub fn new(items: Vec<PackageResponseDto>, total: u64, page: u32, per_page: u32) -> Self {
190        let total_pages = if per_page > 0 {
191            ((total as f64) / (per_page as f64)).ceil() as u32
192        } else {
193            0
194        };
195        Self {
196            items,
197            total,
198            page,
199            per_page,
200            total_pages,
201            has_next: page < total_pages,
202            has_prev: page > 1,
203        }
204    }
205}
206
207/// Summary DTO for list views (compact version)
208#[derive(Debug, Clone, Serialize)]
209#[cfg_attr(feature = "openapi", derive(ToSchema))]
210#[serde(rename_all = "camelCase")]
211pub struct PackageSummaryDto {
212    pub id: Uuid,
213    pub name: String,
214    pub complete_name: String,
215    pub location_id: Option<Uuid>,
216    pub created_at: Option<DateTime<Utc>>,
217}
218
219// =============================================================================
220// Conversions
221// =============================================================================
222
223impl From<Package> for PackageResponseDto {
224    fn from(entity: Package) -> Self {
225        Self {
226            id: entity.id,
227            name: entity.name,
228            complete_name: entity.complete_name,
229            location_id: entity.location_id,
230            parent_package_id: entity.parent_package_id,
231            parent_path: entity.parent_path,
232            pack_date: entity.pack_date,
233            package_type_id: entity.package_type_id,
234            metadata: entity.metadata,
235        }
236    }
237}
238
239impl From<Package> for PackageSummaryDto {
240    fn from(entity: Package) -> Self {
241        let created_at = backbone_core::PersistentEntity::created_at(&entity);
242        Self {
243            id: entity.id,
244            name: entity.name,
245            complete_name: entity.complete_name,
246            location_id: entity.location_id,
247            created_at,
248        }
249    }
250}
251
252impl From<CreatePackageDto> for Package {
253    fn from(dto: CreatePackageDto) -> Self {
254        Self {
255            id: Uuid::new_v4(),
256            name: dto.name,
257            complete_name: dto.complete_name,
258            location_id: dto.location_id,
259            parent_package_id: dto.parent_package_id,
260            parent_path: dto.parent_path,
261            pack_date: dto.pack_date,
262            package_type_id: dto.package_type_id,
263            metadata: AuditMetadata::default(),
264        }
265    }
266}
267
268impl From<&Package> for PackageResponseDto {
269    fn from(entity: &Package) -> Self {
270        Self {
271            id: entity.id.clone(),
272            name: entity.name.clone(),
273            complete_name: entity.complete_name.clone(),
274            location_id: entity.location_id.clone(),
275            parent_package_id: entity.parent_package_id.clone(),
276            parent_path: entity.parent_path.clone(),
277            pack_date: entity.pack_date.clone(),
278            package_type_id: entity.package_type_id.clone(),
279            metadata: entity.metadata.clone(),
280        }
281    }
282}
283
284impl backbone_core::FromCreateDto<CreatePackageDto> for Package {
285    fn from_create_dto(dto: CreatePackageDto) -> backbone_core::ServiceResult<Self> {
286        Ok(Package::from(dto))
287    }
288}
289
290impl backbone_core::ApplyUpdateDto<UpdatePackageDto> for Package {
291    fn apply_update(mut self, dto: UpdatePackageDto) -> backbone_core::ServiceResult<Self> {
292        self.name = dto.name;
293        self.complete_name = dto.complete_name;
294        self.location_id = dto.location_id;
295        self.parent_package_id = dto.parent_package_id;
296        self.parent_path = dto.parent_path;
297        self.pack_date = dto.pack_date;
298        self.package_type_id = dto.package_type_id;
299        Ok(self)
300    }
301}
302
303// =============================================================================
304// Custom DTOs
305// =============================================================================
306
307// Add custom DTOs specific to Package here.
308// This section will be preserved during regeneration.
309// >>> END CUSTOM DTOs
310