use crate::controllers::HttpResult; use crate::extractors::accommodation_extractor::FamilyAndAccommodationInPath; use crate::extractors::accommodation_reservation_extractor::FamilyAndAccommodationReservationInPath; use crate::extractors::family_extractor::FamilyInPath; use crate::models::{Accommodation, AccommodationReservationID, NewAccommodationReservation}; use crate::services::accommodations_reservations_service; use crate::utils::time_utils::time; use actix_web::{web, HttpResponse}; #[derive(serde::Deserialize)] pub struct UpdateReservationQuery { start: usize, end: usize, } impl UpdateReservationQuery { /// Check whether a reservation request is valid or not async fn validate( &self, a: &Accommodation, resa_id: Option, ) -> anyhow::Result> { if !a.open_to_reservations { return Ok(Some( "The accommodation is not open to reservations creation / update!", )); } if (self.start as i64) < (time() as i64 - 3600 * 24 * 30) { return Ok(Some("Start time is too far in the past!")); } if self.start == self.end { return Ok(Some("Start and end time must be different!")); } if self.start > self.end { return Ok(Some("End time happens before start time!")); } let existing = accommodations_reservations_service::get_reservations_for_time_interval( a.id(), self.start, self.end, ) .await?; if existing .iter() .any(|r| r.validated != Some(false) && resa_id != Some(r.id())) { return Ok(Some("This reservation is in conflict with another one!")); } Ok(None) } } /// Create a reservation pub async fn create_reservation( a: FamilyAndAccommodationInPath, req: web::Json, ) -> HttpResult { if let Some(err) = req.validate(&a, None).await? { return Ok(HttpResponse::BadRequest().json(err)); } let mut reservation = accommodations_reservations_service::create(&NewAccommodationReservation { family_id: a.family_id().0, accommodation_id: a.id().0, user_id: a.membership().user_id().0, time_create: time() as i64, time_update: time() as i64, reservation_start: req.start as i64, reservation_end: req.end as i64, }) .await?; // Auto validate reservation if requested if !a.need_validation { reservation.validated = Some(true); accommodations_reservations_service::update(&mut reservation).await?; } Ok(HttpResponse::Ok().json(reservation)) } /// Get the reservations for a given accommodation pub async fn get_accommodation_reservations(a: FamilyAndAccommodationInPath) -> HttpResult { Ok(HttpResponse::Ok() .json(accommodations_reservations_service::get_all_of_accommodation(a.id()).await?)) } #[derive(serde::Deserialize)] pub struct CheckAvailabilityQuery { start: usize, end: usize, } /// Check reservation availability pub async fn get_accommodation_reservations_for_interval( a: FamilyAndAccommodationInPath, req: web::Query, ) -> HttpResult { if req.start > req.end { return Ok(HttpResponse::BadRequest().json("start should be smaller than end!")); } let res = accommodations_reservations_service::get_reservations_for_time_interval( a.id(), req.start, req.end, ) .await?; Ok(HttpResponse::Ok().json(res)) } /// Get the full list of accommodations reservations for a family pub async fn full_list(m: FamilyInPath) -> HttpResult { Ok(HttpResponse::Ok() .json(accommodations_reservations_service::get_all_of_family(m.family_id()).await?)) } /// Get a single accommodation reservation pub async fn get_single(m: FamilyAndAccommodationReservationInPath) -> HttpResult { Ok(HttpResponse::Ok().json(m.to_reservation())) } /// Update a reservation pub async fn update_single( m: FamilyAndAccommodationReservationInPath, req: web::Json, ) -> HttpResult { if let Some(err) = req.validate(m.as_accommodation(), Some(m.id())).await? { return Ok(HttpResponse::BadRequest().json(err)); } if m.membership().user_id() != m.user_id() { return Ok( HttpResponse::BadRequest().json("Only the owner of a reservation can change it!") ); } let need_validation = m.as_accommodation().need_validation; let mut reservation = m.to_reservation(); reservation.reservation_start = req.start as i64; reservation.reservation_end = req.end as i64; if need_validation { reservation.validated = None; } accommodations_reservations_service::update(&mut reservation).await?; Ok(HttpResponse::Accepted().finish()) } /// Delete a reservation pub async fn delete(m: FamilyAndAccommodationReservationInPath) -> HttpResult { if m.membership().user_id() != m.user_id() { return Ok( HttpResponse::BadRequest().json("Only the owner of a reservation can delete it!") ); } accommodations_reservations_service::delete(m.to_reservation()).await?; Ok(HttpResponse::Accepted().finish()) } #[derive(serde::Deserialize)] pub struct ValidateQuery { validate: bool, } /// Validate or reject a reservation pub async fn validate_or_reject( m: FamilyAndAccommodationReservationInPath, q: web::Json, ) -> HttpResult { if !m.membership().is_admin { return Ok( HttpResponse::BadRequest().json("Only a family admin can validate a reservation!") ); } if m.validated == Some(q.validate) { return Ok( HttpResponse::AlreadyReported().json("This reservation has already been processed!") ); } // In case of re-validation, check that the time is still available if m.validated == Some(false) && q.validate { let potential_conflicts = accommodations_reservations_service::get_reservations_for_time_interval( m.accommodation_id(), m.reservation_start as usize, m.reservation_end as usize, ) .await?; if potential_conflicts .iter() .any(|a| a.validated != Some(false)) { return Ok(HttpResponse::Conflict().json( "This cannot be accepted as it would create a conflict with another reservation!", )); } } // Update reservation validation status let mut reservation = m.to_reservation(); reservation.validated = Some(q.validate); accommodations_reservations_service::update(&mut reservation).await?; Ok(HttpResponse::Accepted().finish()) }