GeneIT/geneit_backend/src/controllers/accommodations_reservations_controller.rs

224 lines
6.8 KiB
Rust
Raw Permalink Normal View History

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<AccommodationReservationID>,
) -> anyhow::Result<Option<&str>> {
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<UpdateReservationQuery>,
) -> 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<CheckAvailabilityQuery>,
) -> 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<UpdateReservationQuery>,
) -> 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<ValidateQuery>,
) -> 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())
}