koprogo_api/application/dto/
convocation_dto.rs

1use crate::domain::entities::{Convocation, ConvocationStatus, ConvocationType};
2use chrono::{DateTime, Utc};
3use serde::{Deserialize, Serialize};
4use uuid::Uuid;
5
6#[derive(Debug, Serialize, Deserialize, Clone)]
7pub struct ConvocationResponse {
8    pub id: Uuid,
9    pub organization_id: Uuid,
10    pub building_id: Uuid,
11    pub meeting_id: Uuid,
12    pub meeting_type: ConvocationType,
13    pub meeting_date: DateTime<Utc>,
14    pub status: ConvocationStatus,
15
16    // Legal deadline tracking
17    pub minimum_send_date: DateTime<Utc>,
18    pub actual_send_date: Option<DateTime<Utc>>,
19    pub scheduled_send_date: Option<DateTime<Utc>>,
20
21    // PDF generation
22    pub pdf_file_path: Option<String>,
23    pub language: String,
24
25    // Tracking
26    pub total_recipients: i32,
27    pub opened_count: i32,
28    pub will_attend_count: i32,
29    pub will_not_attend_count: i32,
30
31    // Computed fields
32    pub opening_rate: f64,             // opened / total * 100
33    pub attendance_rate: f64,          // will_attend / total * 100
34    pub days_until_meeting: i64,       // Computed from meeting_date
35    pub respects_legal_deadline: bool, // Computed: actual_send_date <= minimum_send_date
36
37    // Reminders
38    pub reminder_sent_at: Option<DateTime<Utc>>,
39
40    // Audit
41    pub created_at: DateTime<Utc>,
42    pub updated_at: DateTime<Utc>,
43    pub created_by: Uuid,
44}
45
46impl From<Convocation> for ConvocationResponse {
47    fn from(convocation: Convocation) -> Self {
48        let opening_rate = convocation.opening_rate();
49        let attendance_rate = convocation.attendance_rate();
50        let days_until_meeting = convocation.days_until_meeting();
51        let respects_legal_deadline = convocation.respects_legal_deadline();
52
53        Self {
54            id: convocation.id,
55            organization_id: convocation.organization_id,
56            building_id: convocation.building_id,
57            meeting_id: convocation.meeting_id,
58            meeting_type: convocation.meeting_type,
59            meeting_date: convocation.meeting_date,
60            status: convocation.status,
61            minimum_send_date: convocation.minimum_send_date,
62            actual_send_date: convocation.actual_send_date,
63            scheduled_send_date: convocation.scheduled_send_date,
64            pdf_file_path: convocation.pdf_file_path,
65            language: convocation.language,
66            total_recipients: convocation.total_recipients,
67            opened_count: convocation.opened_count,
68            will_attend_count: convocation.will_attend_count,
69            will_not_attend_count: convocation.will_not_attend_count,
70            opening_rate,
71            attendance_rate,
72            days_until_meeting,
73            respects_legal_deadline,
74            reminder_sent_at: convocation.reminder_sent_at,
75            created_at: convocation.created_at,
76            updated_at: convocation.updated_at,
77            created_by: convocation.created_by,
78        }
79    }
80}
81
82#[derive(Debug, Deserialize)]
83pub struct CreateConvocationRequest {
84    pub building_id: Uuid,
85    pub meeting_id: Uuid,
86    pub meeting_type: ConvocationType,
87    pub meeting_date: DateTime<Utc>,
88    pub language: String, // FR, NL, DE, EN
89}
90
91#[derive(Debug, Deserialize)]
92pub struct ScheduleConvocationRequest {
93    pub send_date: DateTime<Utc>,
94}
95
96#[derive(Debug, Deserialize)]
97pub struct SendConvocationRequest {
98    pub recipient_owner_ids: Vec<Uuid>, // List of owner IDs to send to
99}
100
101#[derive(Debug, Serialize)]
102pub struct ConvocationSummaryResponse {
103    pub id: Uuid,
104    pub meeting_id: Uuid,
105    pub meeting_date: DateTime<Utc>,
106    pub status: ConvocationStatus,
107    pub total_recipients: i32,
108    pub opened_count: i32,
109    pub will_attend_count: i32,
110    pub days_until_meeting: i64,
111}
112
113impl From<Convocation> for ConvocationSummaryResponse {
114    fn from(convocation: Convocation) -> Self {
115        Self {
116            id: convocation.id,
117            meeting_id: convocation.meeting_id,
118            meeting_date: convocation.meeting_date,
119            status: convocation.status.clone(),
120            total_recipients: convocation.total_recipients,
121            opened_count: convocation.opened_count,
122            will_attend_count: convocation.will_attend_count,
123            days_until_meeting: convocation.days_until_meeting(),
124        }
125    }
126}