Add an accommodations reservations module #188
@ -2,50 +2,67 @@ use crate::controllers::HttpResult;
|
|||||||
use crate::extractors::accommodation_extractor::FamilyAndAccommodationInPath;
|
use crate::extractors::accommodation_extractor::FamilyAndAccommodationInPath;
|
||||||
use crate::extractors::accommodation_reservation_extractor::FamilyAndAccommodationReservationInPath;
|
use crate::extractors::accommodation_reservation_extractor::FamilyAndAccommodationReservationInPath;
|
||||||
use crate::extractors::family_extractor::FamilyInPath;
|
use crate::extractors::family_extractor::FamilyInPath;
|
||||||
use crate::models::NewAccommodationReservation;
|
use crate::models::{Accommodation, AccommodationReservationID, NewAccommodationReservation};
|
||||||
use crate::services::accommodations_reservations_service;
|
use crate::services::accommodations_reservations_service;
|
||||||
use crate::utils::time_utils::time;
|
use crate::utils::time_utils::time;
|
||||||
use actix_web::{web, HttpResponse};
|
use actix_web::{web, HttpResponse};
|
||||||
|
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize)]
|
||||||
pub struct CreateReservationQuery {
|
pub struct UpdateReservationQuery {
|
||||||
start: usize,
|
start: usize,
|
||||||
end: 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 create / 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
|
/// Create a reservation
|
||||||
pub async fn create_reservation(
|
pub async fn create_reservation(
|
||||||
a: FamilyAndAccommodationInPath,
|
a: FamilyAndAccommodationInPath,
|
||||||
req: web::Json<CreateReservationQuery>,
|
req: web::Json<UpdateReservationQuery>,
|
||||||
) -> HttpResult {
|
) -> HttpResult {
|
||||||
if !a.open_to_reservations {
|
if let Some(err) = req.validate(&a, None).await? {
|
||||||
return Ok(HttpResponse::ExpectationFailed()
|
return Ok(HttpResponse::BadRequest().json(err));
|
||||||
.json("The accommodation is not open to reservations!"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (req.start as i64) < (time() as i64 - 3600 * 24 * 30) {
|
|
||||||
return Ok(HttpResponse::BadRequest().json("Start time is too far in the past!"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if req.start == req.end {
|
|
||||||
return Ok(HttpResponse::BadRequest().json("Start and end time must be different!"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if req.start > req.end {
|
|
||||||
return Ok(HttpResponse::BadRequest().json("End time happens before start time!"));
|
|
||||||
}
|
|
||||||
|
|
||||||
let existing = accommodations_reservations_service::get_reservations_for_time_interval(
|
|
||||||
a.id(),
|
|
||||||
req.start,
|
|
||||||
req.end,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
if existing.iter().any(|r| r.validated != Some(false)) {
|
|
||||||
return Ok(
|
|
||||||
HttpResponse::Conflict().json("This reservation is in conflict with another one!")
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut reservation =
|
let mut reservation =
|
||||||
@ -87,6 +104,36 @@ pub async fn get_single(m: FamilyAndAccommodationReservationInPath) -> HttpResul
|
|||||||
Ok(HttpResponse::Ok().json(m.to_reservation()))
|
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
|
/// Delete a reservation
|
||||||
pub async fn delete(m: FamilyAndAccommodationReservationInPath) -> HttpResult {
|
pub async fn delete(m: FamilyAndAccommodationReservationInPath) -> HttpResult {
|
||||||
if m.membership().user_id() != m.user_id() {
|
if m.membership().user_id() != m.user_id() {
|
||||||
|
@ -60,6 +60,10 @@ impl FamilyAndAccommodationReservationInPath {
|
|||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn as_accommodation(&self) -> &Accommodation {
|
||||||
|
&self.1
|
||||||
|
}
|
||||||
|
|
||||||
pub fn to_accommodation(self) -> Accommodation {
|
pub fn to_accommodation(self) -> Accommodation {
|
||||||
self.1
|
self.1
|
||||||
}
|
}
|
||||||
|
@ -244,7 +244,10 @@ async fn main() -> std::io::Result<()> {
|
|||||||
"/family/{id}/accommodations/reservation/{reservation_id}",
|
"/family/{id}/accommodations/reservation/{reservation_id}",
|
||||||
web::get().to(accommodations_reservations_controller::get_single),
|
web::get().to(accommodations_reservations_controller::get_single),
|
||||||
)
|
)
|
||||||
// TODO : update
|
.route(
|
||||||
|
"/family/{id}/accommodations/reservation/{reservation_id}",
|
||||||
|
web::patch().to(accommodations_reservations_controller::update_single),
|
||||||
|
)
|
||||||
.route(
|
.route(
|
||||||
"/family/{id}/accommodations/reservation/{reservation_id}",
|
"/family/{id}/accommodations/reservation/{reservation_id}",
|
||||||
web::delete().to(accommodations_reservations_controller::delete),
|
web::delete().to(accommodations_reservations_controller::delete),
|
||||||
|
@ -30,6 +30,8 @@ pub async fn update(r: &mut AccommodationReservation) -> anyhow::Result<()> {
|
|||||||
.set((
|
.set((
|
||||||
accommodations_reservations::dsl::time_update.eq(r.time_update),
|
accommodations_reservations::dsl::time_update.eq(r.time_update),
|
||||||
accommodations_reservations::dsl::validated.eq(r.validated),
|
accommodations_reservations::dsl::validated.eq(r.validated),
|
||||||
|
accommodations_reservations::dsl::reservation_start.eq(r.reservation_start),
|
||||||
|
accommodations_reservations::dsl::reservation_end.eq(r.reservation_end),
|
||||||
))
|
))
|
||||||
.execute(conn)
|
.execute(conn)
|
||||||
})?;
|
})?;
|
||||||
|
Loading…
Reference in New Issue
Block a user