Skip to main content

backbone_maintenance/presentation/dto/
maintenance_request_dto.rs

1//! MaintenanceRequest 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};
11use rust_decimal::Decimal;
12
13#[cfg(feature = "openapi")]
14#[cfg(feature = "openapi")]
15use utoipa::ToSchema;
16
17#[cfg(feature = "validation")]
18use validator::Validate;
19
20use crate::domain::entity::MaintenanceRequest;
21use crate::domain::entity::AuditMetadata;
22use crate::domain::entity::MaintenanceType;
23use crate::domain::entity::RepeatType;
24use crate::domain::entity::RepeatUnit;
25use crate::domain::entity::RequestKanbanState;
26use crate::domain::entity::RequestPriority;
27
28// =============================================================================
29// Create DTO
30// =============================================================================
31
32/// Request DTO for creating a new MaintenanceRequest
33///
34/// Used in POST requests to create new entities.
35/// Excludes auto-generated fields (id, created_at, updated_at, deleted_at).
36#[derive(Debug, Clone, Deserialize)]
37#[cfg_attr(feature = "openapi", derive(ToSchema))]
38#[cfg_attr(feature = "validation", derive(Validate))]
39#[serde(rename_all = "camelCase")]
40pub struct CreateMaintenanceRequestDto {
41    #[cfg_attr(feature = "openapi", schema(example = "example"))]
42    pub name: String,
43    #[serde(default, skip_serializing_if = "Option::is_none")]
44    pub description: Option<String>,
45    #[cfg_attr(feature = "openapi", schema(example = "2024-01-01"))]
46    #[serde(alias = "request_date")]
47    pub request_date: NaiveDate,
48    #[serde(default, skip_serializing_if = "Option::is_none", alias = "schedule_date")]
49    pub schedule_date: Option<DateTime<Utc>>,
50    #[serde(default, skip_serializing_if = "Option::is_none", alias = "schedule_end")]
51    pub schedule_end: Option<DateTime<Utc>>,
52    #[serde(default, skip_serializing_if = "Option::is_none", alias = "close_date")]
53    pub close_date: Option<NaiveDate>,
54    pub duration: Decimal,
55    #[serde(default, skip_serializing_if = "Option::is_none", alias = "owner_user_id")]
56    pub owner_user_id: Option<Uuid>,
57    #[serde(default, skip_serializing_if = "Option::is_none", alias = "user_id")]
58    pub user_id: Option<Uuid>,
59    #[serde(default, skip_serializing_if = "Option::is_none", alias = "asset_id")]
60    pub asset_id: Option<Uuid>,
61    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
62    #[serde(alias = "stage_id")]
63    pub stage_id: Uuid,
64    #[serde(alias = "kanban_state")]
65    pub kanban_state: RequestKanbanState,
66    pub priority: RequestPriority,
67    #[serde(alias = "maintenance_type")]
68    pub maintenance_type: MaintenanceType,
69    #[cfg_attr(feature = "openapi", schema(example = true))]
70    pub recurring: bool,
71    #[cfg_attr(feature = "openapi", schema(example = 42))]
72    #[serde(alias = "repeat_interval")]
73    pub repeat_interval: i32,
74    #[serde(alias = "repeat_unit")]
75    pub repeat_unit: RepeatUnit,
76    #[serde(alias = "repeat_type")]
77    pub repeat_type: RepeatType,
78    #[serde(default, skip_serializing_if = "Option::is_none", alias = "repeat_until")]
79    pub repeat_until: Option<NaiveDate>,
80    #[serde(default, skip_serializing_if = "Option::is_none", alias = "successor_request_id")]
81    pub successor_request_id: Option<Uuid>,
82    #[serde(default, skip_serializing_if = "Option::is_none", alias = "successor_of_request_id")]
83    pub successor_of_request_id: Option<Uuid>,
84}
85
86// =============================================================================
87// Update DTO
88// =============================================================================
89
90/// Request DTO for full update of a MaintenanceRequest
91///
92/// Used in PUT requests for full entity replacement.
93/// All fields are required (except auto-generated ones).
94#[derive(Debug, Clone, Deserialize)]
95#[cfg_attr(feature = "openapi", derive(ToSchema))]
96#[cfg_attr(feature = "validation", derive(Validate))]
97#[serde(rename_all = "camelCase")]
98pub struct UpdateMaintenanceRequestDto {
99    #[cfg_attr(feature = "openapi", schema(example = "example"))]
100    pub name: String,
101    #[serde(default, skip_serializing_if = "Option::is_none")]
102    pub description: Option<String>,
103    #[cfg_attr(feature = "openapi", schema(example = "2024-01-01"))]
104    #[serde(alias = "request_date")]
105    pub request_date: NaiveDate,
106    #[serde(default, skip_serializing_if = "Option::is_none", alias = "schedule_date")]
107    pub schedule_date: Option<DateTime<Utc>>,
108    #[serde(default, skip_serializing_if = "Option::is_none", alias = "schedule_end")]
109    pub schedule_end: Option<DateTime<Utc>>,
110    #[serde(default, skip_serializing_if = "Option::is_none", alias = "close_date")]
111    pub close_date: Option<NaiveDate>,
112    pub duration: Decimal,
113    #[serde(default, skip_serializing_if = "Option::is_none", alias = "owner_user_id")]
114    pub owner_user_id: Option<Uuid>,
115    #[serde(default, skip_serializing_if = "Option::is_none", alias = "user_id")]
116    pub user_id: Option<Uuid>,
117    #[serde(default, skip_serializing_if = "Option::is_none", alias = "asset_id")]
118    pub asset_id: Option<Uuid>,
119    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
120    #[serde(alias = "stage_id")]
121    pub stage_id: Uuid,
122    #[serde(alias = "kanban_state")]
123    pub kanban_state: RequestKanbanState,
124    pub priority: RequestPriority,
125    #[serde(alias = "maintenance_type")]
126    pub maintenance_type: MaintenanceType,
127    #[cfg_attr(feature = "openapi", schema(example = true))]
128    pub recurring: bool,
129    #[cfg_attr(feature = "openapi", schema(example = 42))]
130    #[serde(alias = "repeat_interval")]
131    pub repeat_interval: i32,
132    #[serde(alias = "repeat_unit")]
133    pub repeat_unit: RepeatUnit,
134    #[serde(alias = "repeat_type")]
135    pub repeat_type: RepeatType,
136    #[serde(default, skip_serializing_if = "Option::is_none", alias = "repeat_until")]
137    pub repeat_until: Option<NaiveDate>,
138    #[serde(default, skip_serializing_if = "Option::is_none", alias = "successor_request_id")]
139    pub successor_request_id: Option<Uuid>,
140    #[serde(default, skip_serializing_if = "Option::is_none", alias = "successor_of_request_id")]
141    pub successor_of_request_id: Option<Uuid>,
142}
143
144// =============================================================================
145// Patch DTO
146// =============================================================================
147
148/// Request DTO for partial update of a MaintenanceRequest
149///
150/// Used in PATCH requests for partial updates.
151/// All fields are optional - only provided fields will be updated.
152#[derive(Debug, Clone, Default, Deserialize)]
153#[cfg_attr(feature = "openapi", derive(ToSchema))]
154#[cfg_attr(feature = "validation", derive(Validate))]
155#[serde(rename_all = "camelCase")]
156pub struct PatchMaintenanceRequestDto {
157    #[cfg_attr(feature = "openapi", schema(example = "example"))]
158    #[serde(skip_serializing_if = "Option::is_none")]
159    pub name: Option<String>,
160    #[serde(skip_serializing_if = "Option::is_none")]
161    pub description: Option<String>,
162    #[cfg_attr(feature = "openapi", schema(example = "2024-01-01"))]
163    #[serde(skip_serializing_if = "Option::is_none", alias = "request_date")]
164    pub request_date: Option<NaiveDate>,
165    #[serde(skip_serializing_if = "Option::is_none", alias = "schedule_date")]
166    pub schedule_date: Option<DateTime<Utc>>,
167    #[serde(skip_serializing_if = "Option::is_none", alias = "schedule_end")]
168    pub schedule_end: Option<DateTime<Utc>>,
169    #[serde(skip_serializing_if = "Option::is_none", alias = "close_date")]
170    pub close_date: Option<NaiveDate>,
171    #[serde(skip_serializing_if = "Option::is_none")]
172    pub duration: Option<Decimal>,
173    #[serde(skip_serializing_if = "Option::is_none", alias = "owner_user_id")]
174    pub owner_user_id: Option<Uuid>,
175    #[serde(skip_serializing_if = "Option::is_none", alias = "user_id")]
176    pub user_id: Option<Uuid>,
177    #[serde(skip_serializing_if = "Option::is_none", alias = "asset_id")]
178    pub asset_id: Option<Uuid>,
179    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
180    #[serde(skip_serializing_if = "Option::is_none", alias = "stage_id")]
181    pub stage_id: Option<Uuid>,
182    #[serde(skip_serializing_if = "Option::is_none", alias = "kanban_state")]
183    pub kanban_state: Option<RequestKanbanState>,
184    #[serde(skip_serializing_if = "Option::is_none")]
185    pub priority: Option<RequestPriority>,
186    #[serde(skip_serializing_if = "Option::is_none", alias = "maintenance_type")]
187    pub maintenance_type: Option<MaintenanceType>,
188    #[cfg_attr(feature = "openapi", schema(example = true))]
189    #[serde(skip_serializing_if = "Option::is_none")]
190    pub recurring: Option<bool>,
191    #[cfg_attr(feature = "openapi", schema(example = 42))]
192    #[serde(skip_serializing_if = "Option::is_none", alias = "repeat_interval")]
193    pub repeat_interval: Option<i32>,
194    #[serde(skip_serializing_if = "Option::is_none", alias = "repeat_unit")]
195    pub repeat_unit: Option<RepeatUnit>,
196    #[serde(skip_serializing_if = "Option::is_none", alias = "repeat_type")]
197    pub repeat_type: Option<RepeatType>,
198    #[serde(skip_serializing_if = "Option::is_none", alias = "repeat_until")]
199    pub repeat_until: Option<NaiveDate>,
200    #[serde(skip_serializing_if = "Option::is_none", alias = "successor_request_id")]
201    pub successor_request_id: Option<Uuid>,
202    #[serde(skip_serializing_if = "Option::is_none", alias = "successor_of_request_id")]
203    pub successor_of_request_id: Option<Uuid>,
204}
205
206impl PatchMaintenanceRequestDto {
207    /// Check if any field is set
208    pub fn has_changes(&self) -> bool {
209        self.name.is_some() || self.description.is_some() || self.request_date.is_some() || self.schedule_date.is_some() || self.schedule_end.is_some() || self.close_date.is_some() || self.duration.is_some() || self.owner_user_id.is_some() || self.user_id.is_some() || self.asset_id.is_some() || self.stage_id.is_some() || self.kanban_state.is_some() || self.priority.is_some() || self.maintenance_type.is_some() || self.recurring.is_some() || self.repeat_interval.is_some() || self.repeat_unit.is_some() || self.repeat_type.is_some() || self.repeat_until.is_some() || self.successor_request_id.is_some() || self.successor_of_request_id.is_some()
210    }
211}
212
213// =============================================================================
214// Response DTO
215// =============================================================================
216
217/// Response DTO for MaintenanceRequest entity
218///
219/// Used in API responses for single entity.
220/// Includes all fields including metadata.
221#[derive(Debug, Clone, Serialize)]
222#[cfg_attr(feature = "openapi", derive(ToSchema))]
223#[serde(rename_all = "camelCase")]
224pub struct MaintenanceRequestResponseDto {
225    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
226    pub id: Uuid,
227    #[cfg_attr(feature = "openapi", schema(example = "example"))]
228    pub name: String,
229    pub description: Option<String>,
230    #[cfg_attr(feature = "openapi", schema(example = "2024-01-01"))]
231    pub request_date: NaiveDate,
232    pub schedule_date: Option<DateTime<Utc>>,
233    pub schedule_end: Option<DateTime<Utc>>,
234    pub close_date: Option<NaiveDate>,
235    pub duration: Decimal,
236    pub owner_user_id: Option<Uuid>,
237    pub user_id: Option<Uuid>,
238    pub asset_id: Option<Uuid>,
239    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
240    pub stage_id: Uuid,
241    pub kanban_state: RequestKanbanState,
242    pub priority: RequestPriority,
243    pub maintenance_type: MaintenanceType,
244    #[cfg_attr(feature = "openapi", schema(example = true))]
245    pub recurring: bool,
246    #[cfg_attr(feature = "openapi", schema(example = 42))]
247    pub repeat_interval: i32,
248    pub repeat_unit: RepeatUnit,
249    pub repeat_type: RepeatType,
250    pub repeat_until: Option<NaiveDate>,
251    pub successor_request_id: Option<Uuid>,
252    pub successor_of_request_id: Option<Uuid>,
253    pub metadata: AuditMetadata,
254}
255
256// =============================================================================
257// List Response DTO
258// =============================================================================
259
260/// Paginated list response for MaintenanceRequest entities
261///
262/// Used in API responses for list endpoints.
263/// Includes pagination metadata.
264#[derive(Debug, Clone, Serialize)]
265#[cfg_attr(feature = "openapi", derive(ToSchema))]
266#[serde(rename_all = "camelCase")]
267pub struct MaintenanceRequestListResponseDto {
268    /// List of items
269    pub items: Vec<MaintenanceRequestResponseDto>,
270    /// Total number of items (across all pages)
271    pub total: u64,
272    /// Current page number (1-indexed)
273    pub page: u32,
274    /// Number of items per page
275    pub per_page: u32,
276    /// Total number of pages
277    pub total_pages: u32,
278    /// Whether there is a next page
279    pub has_next: bool,
280    /// Whether there is a previous page
281    pub has_prev: bool,
282}
283
284impl MaintenanceRequestListResponseDto {
285    /// Create a new list response from items and pagination info
286    pub fn new(items: Vec<MaintenanceRequestResponseDto>, total: u64, page: u32, per_page: u32) -> Self {
287        let total_pages = if per_page > 0 {
288            ((total as f64) / (per_page as f64)).ceil() as u32
289        } else {
290            0
291        };
292        Self {
293            items,
294            total,
295            page,
296            per_page,
297            total_pages,
298            has_next: page < total_pages,
299            has_prev: page > 1,
300        }
301    }
302}
303
304/// Summary DTO for list views (compact version)
305#[derive(Debug, Clone, Serialize)]
306#[cfg_attr(feature = "openapi", derive(ToSchema))]
307#[serde(rename_all = "camelCase")]
308pub struct MaintenanceRequestSummaryDto {
309    pub id: Uuid,
310    pub name: String,
311    pub description: Option<String>,
312    pub request_date: NaiveDate,
313    pub created_at: Option<DateTime<Utc>>,
314}
315
316// =============================================================================
317// Conversions
318// =============================================================================
319
320impl From<MaintenanceRequest> for MaintenanceRequestResponseDto {
321    fn from(entity: MaintenanceRequest) -> Self {
322        Self {
323            id: entity.id,
324            name: entity.name,
325            description: entity.description,
326            request_date: entity.request_date,
327            schedule_date: entity.schedule_date,
328            schedule_end: entity.schedule_end,
329            close_date: entity.close_date,
330            duration: entity.duration,
331            owner_user_id: entity.owner_user_id,
332            user_id: entity.user_id,
333            asset_id: entity.asset_id,
334            stage_id: entity.stage_id,
335            kanban_state: entity.kanban_state,
336            priority: entity.priority,
337            maintenance_type: entity.maintenance_type,
338            recurring: entity.recurring,
339            repeat_interval: entity.repeat_interval,
340            repeat_unit: entity.repeat_unit,
341            repeat_type: entity.repeat_type,
342            repeat_until: entity.repeat_until,
343            successor_request_id: entity.successor_request_id,
344            successor_of_request_id: entity.successor_of_request_id,
345            metadata: entity.metadata,
346        }
347    }
348}
349
350impl From<MaintenanceRequest> for MaintenanceRequestSummaryDto {
351    fn from(entity: MaintenanceRequest) -> Self {
352        let created_at = backbone_core::PersistentEntity::created_at(&entity);
353        Self {
354            id: entity.id,
355            name: entity.name,
356            description: entity.description,
357            request_date: entity.request_date,
358            created_at,
359        }
360    }
361}
362
363impl From<CreateMaintenanceRequestDto> for MaintenanceRequest {
364    fn from(dto: CreateMaintenanceRequestDto) -> Self {
365        Self {
366            id: Uuid::new_v4(),
367            name: dto.name,
368            description: dto.description,
369            request_date: dto.request_date,
370            schedule_date: dto.schedule_date,
371            schedule_end: dto.schedule_end,
372            close_date: dto.close_date,
373            duration: dto.duration,
374            owner_user_id: dto.owner_user_id,
375            user_id: dto.user_id,
376            asset_id: dto.asset_id,
377            stage_id: dto.stage_id,
378            kanban_state: dto.kanban_state,
379            priority: dto.priority,
380            maintenance_type: dto.maintenance_type,
381            recurring: dto.recurring,
382            repeat_interval: dto.repeat_interval,
383            repeat_unit: dto.repeat_unit,
384            repeat_type: dto.repeat_type,
385            repeat_until: dto.repeat_until,
386            successor_request_id: dto.successor_request_id,
387            successor_of_request_id: dto.successor_of_request_id,
388            metadata: AuditMetadata::default(),
389        }
390    }
391}
392
393impl From<&MaintenanceRequest> for MaintenanceRequestResponseDto {
394    fn from(entity: &MaintenanceRequest) -> Self {
395        Self {
396            id: entity.id.clone(),
397            name: entity.name.clone(),
398            description: entity.description.clone(),
399            request_date: entity.request_date.clone(),
400            schedule_date: entity.schedule_date.clone(),
401            schedule_end: entity.schedule_end.clone(),
402            close_date: entity.close_date.clone(),
403            duration: entity.duration.clone(),
404            owner_user_id: entity.owner_user_id.clone(),
405            user_id: entity.user_id.clone(),
406            asset_id: entity.asset_id.clone(),
407            stage_id: entity.stage_id.clone(),
408            kanban_state: entity.kanban_state.clone(),
409            priority: entity.priority.clone(),
410            maintenance_type: entity.maintenance_type.clone(),
411            recurring: entity.recurring.clone(),
412            repeat_interval: entity.repeat_interval.clone(),
413            repeat_unit: entity.repeat_unit.clone(),
414            repeat_type: entity.repeat_type.clone(),
415            repeat_until: entity.repeat_until.clone(),
416            successor_request_id: entity.successor_request_id.clone(),
417            successor_of_request_id: entity.successor_of_request_id.clone(),
418            metadata: entity.metadata.clone(),
419        }
420    }
421}
422
423impl backbone_core::FromCreateDto<CreateMaintenanceRequestDto> for MaintenanceRequest {
424    fn from_create_dto(dto: CreateMaintenanceRequestDto) -> backbone_core::ServiceResult<Self> {
425        Ok(MaintenanceRequest::from(dto))
426    }
427}
428
429impl backbone_core::ApplyUpdateDto<UpdateMaintenanceRequestDto> for MaintenanceRequest {
430    fn apply_update(mut self, dto: UpdateMaintenanceRequestDto) -> backbone_core::ServiceResult<Self> {
431        self.name = dto.name;
432        self.description = dto.description;
433        self.request_date = dto.request_date;
434        self.schedule_date = dto.schedule_date;
435        self.schedule_end = dto.schedule_end;
436        self.close_date = dto.close_date;
437        self.duration = dto.duration;
438        self.owner_user_id = dto.owner_user_id;
439        self.user_id = dto.user_id;
440        self.asset_id = dto.asset_id;
441        self.stage_id = dto.stage_id;
442        self.kanban_state = dto.kanban_state;
443        self.priority = dto.priority;
444        self.maintenance_type = dto.maintenance_type;
445        self.recurring = dto.recurring;
446        self.repeat_interval = dto.repeat_interval;
447        self.repeat_unit = dto.repeat_unit;
448        self.repeat_type = dto.repeat_type;
449        self.repeat_until = dto.repeat_until;
450        self.successor_request_id = dto.successor_request_id;
451        self.successor_of_request_id = dto.successor_of_request_id;
452        Ok(self)
453    }
454}
455
456// =============================================================================
457// Custom DTOs
458// =============================================================================
459
460// <<< CUSTOM DTOs
461// Add custom DTOs specific to MaintenanceRequest here.
462// This section will be preserved during regeneration.
463// >>> END CUSTOM DTOs