koprogo_api/application/dto/
resource_booking_dto.rs1use crate::domain::entities::{BookingStatus, RecurringPattern, ResourceBooking, ResourceType};
2use chrono::{DateTime, Utc};
3use serde::{Deserialize, Serialize};
4use uuid::Uuid;
5
6#[derive(Debug, Serialize, Deserialize, Clone)]
8pub struct CreateResourceBookingDto {
9 pub building_id: Uuid,
10 pub resource_type: ResourceType,
11 pub resource_name: String, pub start_time: DateTime<Utc>,
13 pub end_time: DateTime<Utc>,
14 #[serde(skip_serializing_if = "Option::is_none")]
15 pub notes: Option<String>,
16 #[serde(default)]
17 pub recurring_pattern: RecurringPattern, #[serde(skip_serializing_if = "Option::is_none")]
19 pub recurrence_end_date: Option<DateTime<Utc>>,
20 #[serde(skip_serializing_if = "Option::is_none")]
22 pub max_duration_hours: Option<i64>,
23 #[serde(skip_serializing_if = "Option::is_none")]
25 pub max_advance_days: Option<i64>,
26 #[serde(default)]
30 pub on_behalf_of_acp: bool,
31 #[serde(skip_serializing_if = "Option::is_none")]
33 pub motif: Option<String>,
34}
35
36#[derive(Debug, Serialize, Deserialize, Clone)]
40pub struct UpdateResourceBookingDto {
41 #[serde(skip_serializing_if = "Option::is_none")]
42 pub resource_name: Option<String>,
43 #[serde(skip_serializing_if = "Option::is_none")]
44 pub notes: Option<String>,
45}
46
47#[derive(Debug, Serialize, Deserialize, Clone)]
49pub struct ResourceBookingResponseDto {
50 pub id: Uuid,
51 pub building_id: Uuid,
52 pub resource_type: ResourceType,
53 pub resource_name: String,
54 #[serde(skip_serializing_if = "Option::is_none")]
55 pub booked_by: Option<Uuid>,
56 pub booked_by_name: String, #[serde(skip_serializing_if = "Option::is_none")]
59 pub booked_by_user_id: Option<Uuid>,
60 #[serde(default)]
61 pub on_behalf_of_acp: bool,
62 #[serde(skip_serializing_if = "Option::is_none")]
63 pub motif: Option<String>,
64 pub start_time: DateTime<Utc>,
65 pub end_time: DateTime<Utc>,
66 pub status: BookingStatus,
67 #[serde(skip_serializing_if = "Option::is_none")]
68 pub notes: Option<String>,
69 pub recurring_pattern: RecurringPattern,
70 #[serde(skip_serializing_if = "Option::is_none")]
71 pub recurrence_end_date: Option<DateTime<Utc>>,
72 pub created_at: DateTime<Utc>,
73 pub updated_at: DateTime<Utc>,
74 pub duration_hours: f64,
76 pub is_active: bool,
77 pub is_past: bool,
78 pub is_future: bool,
79 pub is_modifiable: bool,
80 pub is_recurring: bool,
81}
82
83impl ResourceBookingResponseDto {
84 pub fn from_entity(booking: ResourceBooking, booked_by_name: String) -> Self {
86 Self {
87 id: booking.id,
88 building_id: booking.building_id,
89 resource_type: booking.resource_type.clone(),
90 resource_name: booking.resource_name.clone(),
91 booked_by: booking.booked_by,
92 booked_by_name,
93 booked_by_user_id: booking.booked_by_user_id,
94 on_behalf_of_acp: booking.on_behalf_of_acp,
95 motif: booking.motif.clone(),
96 start_time: booking.start_time,
97 end_time: booking.end_time,
98 status: booking.status.clone(),
99 notes: booking.notes.clone(),
100 recurring_pattern: booking.recurring_pattern.clone(),
101 recurrence_end_date: booking.recurrence_end_date,
102 created_at: booking.created_at,
103 updated_at: booking.updated_at,
104 duration_hours: booking.duration_hours(),
106 is_active: booking.is_active(),
107 is_past: booking.is_past(),
108 is_future: booking.is_future(),
109 is_modifiable: booking.is_modifiable(),
110 is_recurring: booking.is_recurring(),
111 }
112 }
113}
114
115#[derive(Debug, Serialize, Deserialize, Clone)]
117pub struct BookingStatisticsDto {
118 pub building_id: Uuid,
119 pub total_bookings: i64,
120 pub confirmed_bookings: i64,
121 pub pending_bookings: i64,
122 pub completed_bookings: i64,
123 pub cancelled_bookings: i64,
124 pub no_show_bookings: i64,
125 pub active_bookings: i64, pub upcoming_bookings: i64, pub total_hours_booked: f64, pub most_popular_resource: Option<String>, }
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134
135 #[test]
136 fn test_create_dto_serialization() {
137 let dto = CreateResourceBookingDto {
138 building_id: Uuid::new_v4(),
139 resource_type: ResourceType::MeetingRoom,
140 resource_name: "Meeting Room A".to_string(),
141 start_time: Utc::now() + chrono::Duration::hours(2),
142 end_time: Utc::now() + chrono::Duration::hours(4),
143 notes: Some("Team meeting".to_string()),
144 recurring_pattern: RecurringPattern::None,
145 recurrence_end_date: None,
146 max_duration_hours: None,
147 max_advance_days: None,
148 on_behalf_of_acp: false,
149 motif: None,
150 };
151
152 let json = serde_json::to_string(&dto).unwrap();
153 assert!(json.contains("Meeting Room A"));
154 }
155
156 #[test]
157 fn test_response_dto_from_entity() {
158 let building_id = Uuid::new_v4();
159 let booked_by = Uuid::new_v4();
160 let start_time = Utc::now() + chrono::Duration::hours(2);
161 let end_time = start_time + chrono::Duration::hours(2);
162
163 let booking = ResourceBooking::new(
164 building_id,
165 ResourceType::MeetingRoom,
166 "Meeting Room A".to_string(),
167 booked_by,
168 start_time,
169 end_time,
170 Some("Team meeting".to_string()),
171 RecurringPattern::None,
172 None,
173 None,
174 None,
175 )
176 .unwrap();
177
178 let dto = ResourceBookingResponseDto::from_entity(booking, "John Doe".to_string());
179
180 assert_eq!(dto.resource_name, "Meeting Room A");
181 assert_eq!(dto.booked_by_name, "John Doe");
182 assert_eq!(dto.duration_hours, 2.0);
183 assert!(dto.is_future);
184 assert!(!dto.is_past);
185 assert!(dto.is_modifiable);
186 }
187}