Skip to main content

backbone_activity/presentation/dto/
activity_dto.rs

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