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    // 2e convocation — Art. 3.87 §5 CC
41    pub first_meeting_id: Option<Uuid>,
42    pub no_quorum_required: bool, // TRUE for second convocations (quorum not required per Art. 3.87 §5)
43
44    // Audit
45    pub created_at: DateTime<Utc>,
46    pub updated_at: DateTime<Utc>,
47    pub created_by: Uuid,
48}
49
50impl From<Convocation> for ConvocationResponse {
51    fn from(convocation: Convocation) -> Self {
52        let opening_rate = convocation.opening_rate();
53        let attendance_rate = convocation.attendance_rate();
54        let days_until_meeting = convocation.days_until_meeting();
55        let respects_legal_deadline = convocation.respects_legal_deadline();
56
57        Self {
58            id: convocation.id,
59            organization_id: convocation.organization_id,
60            building_id: convocation.building_id,
61            meeting_id: convocation.meeting_id,
62            meeting_type: convocation.meeting_type,
63            meeting_date: convocation.meeting_date,
64            status: convocation.status,
65            minimum_send_date: convocation.minimum_send_date,
66            actual_send_date: convocation.actual_send_date,
67            scheduled_send_date: convocation.scheduled_send_date,
68            pdf_file_path: convocation.pdf_file_path,
69            language: convocation.language,
70            total_recipients: convocation.total_recipients,
71            opened_count: convocation.opened_count,
72            will_attend_count: convocation.will_attend_count,
73            will_not_attend_count: convocation.will_not_attend_count,
74            opening_rate,
75            attendance_rate,
76            days_until_meeting,
77            respects_legal_deadline,
78            reminder_sent_at: convocation.reminder_sent_at,
79            first_meeting_id: convocation.first_meeting_id,
80            no_quorum_required: convocation.no_quorum_required,
81            created_at: convocation.created_at,
82            updated_at: convocation.updated_at,
83            created_by: convocation.created_by,
84        }
85    }
86}
87
88#[derive(Debug, Deserialize)]
89pub struct CreateConvocationRequest {
90    pub building_id: Uuid,
91    pub meeting_id: Uuid,
92    pub meeting_type: ConvocationType,
93    pub meeting_date: DateTime<Utc>,
94    pub language: String, // FR, NL, DE, EN
95}
96
97#[derive(Debug, Deserialize)]
98pub struct ScheduleConvocationRequest {
99    pub send_date: DateTime<Utc>,
100}
101
102#[derive(Debug, Deserialize)]
103pub struct SendConvocationRequest {
104    pub recipient_owner_ids: Vec<Uuid>, // List of owner IDs to send to
105}
106
107#[derive(Debug, Serialize)]
108pub struct ConvocationSummaryResponse {
109    pub id: Uuid,
110    pub meeting_id: Uuid,
111    pub meeting_date: DateTime<Utc>,
112    pub status: ConvocationStatus,
113    pub total_recipients: i32,
114    pub opened_count: i32,
115    pub will_attend_count: i32,
116    pub days_until_meeting: i64,
117}
118
119impl From<Convocation> for ConvocationSummaryResponse {
120    fn from(convocation: Convocation) -> Self {
121        Self {
122            id: convocation.id,
123            meeting_id: convocation.meeting_id,
124            meeting_date: convocation.meeting_date,
125            status: convocation.status.clone(),
126            total_recipients: convocation.total_recipients,
127            opened_count: convocation.opened_count,
128            will_attend_count: convocation.will_attend_count,
129            days_until_meeting: convocation.days_until_meeting(),
130        }
131    }
132}