reservations
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m11s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped

This commit is contained in:
2024-03-30 01:36:37 +01:00
parent 163c97b2f5
commit 649169c192
14 changed files with 490 additions and 10 deletions

View File

@ -36,9 +36,10 @@ pub enum BoatDamage {
#[derive(Serialize, Deserialize, Debug)]
pub struct BoatWithDetails {
#[serde(flatten)]
boat: Boat,
pub(crate) boat: Boat,
damage: BoatDamage,
on_water: bool,
reserved_today: bool,
}
#[derive(FromForm)]
@ -135,6 +136,20 @@ impl Boat {
sqlx::query!("SELECT * FROM boat_damage WHERE boat_id=? AND lock_boat=false AND user_id_verified is null", self.id).fetch_optional(db).await.unwrap().is_some()
}
pub async fn reserved_today(&self, db: &SqlitePool) -> bool {
sqlx::query!(
"SELECT *
FROM boat_reservation
WHERE boat_id =?
AND date('now') BETWEEN start_date AND end_date;",
self.id
)
.fetch_optional(db)
.await
.unwrap()
.is_some()
}
pub async fn on_water(&self, db: &SqlitePool) -> bool {
sqlx::query!(
"SELECT * FROM logbook WHERE boat_id=? AND arrival is null",
@ -159,6 +174,7 @@ impl Boat {
res.push(BoatWithDetails {
damage,
on_water: boat.on_water(db).await,
reserved_today: boat.reserved_today(db).await,
boat,
});
}

View File

@ -0,0 +1,179 @@
use crate::model::{boat::Boat, user::User};
use chrono::NaiveDate;
use chrono::NaiveDateTime;
use rocket::serde::{Deserialize, Serialize};
use rocket::FromForm;
use sqlx::{FromRow, SqlitePool};
use super::log::Log;
use super::notification::Notification;
use super::role::Role;
#[derive(FromRow, Debug, Serialize, Deserialize)]
pub struct BoatReservation {
pub id: i64,
pub boat_id: i64,
pub start_date: NaiveDate,
pub end_date: NaiveDate,
pub time_desc: String,
pub usage: String,
pub user_id_applicant: i64,
pub user_id_confirmation: Option<i64>,
pub created_at: NaiveDateTime,
}
#[derive(FromRow, Debug, Serialize, Deserialize)]
pub struct BoatReservationWithDetails {
#[serde(flatten)]
boat_reservation: BoatReservation,
boat: Boat,
user_applicant: User,
user_confirmation: Option<User>,
}
#[derive(Debug)]
pub struct BoatReservationToAdd<'r> {
pub boat: &'r Boat,
pub start_date: NaiveDate,
pub end_date: NaiveDate,
pub time_desc: &'r str,
pub usage: &'r str,
pub user_applicant: &'r User,
}
impl BoatReservation {
pub async fn find_by_id(db: &SqlitePool, id: i32) -> Option<Self> {
sqlx::query_as!(
Self,
"SELECT id, boat_id, start_date, end_date, time_desc, usage, user_id_applicant, user_id_confirmation, created_at
FROM boat_reservation
WHERE id like ?",
id
)
.fetch_one(db)
.await
.ok()
}
pub async fn all_future(db: &SqlitePool) -> Vec<BoatReservationWithDetails> {
let boatreservations = sqlx::query_as!(
Self,
"
SELECT id, boat_id, start_date, end_date, time_desc, usage, user_id_applicant, user_id_confirmation, created_at
FROM boat_reservation
WHERE end_date >= CURRENT_DATE ORDER BY end_date
"
)
.fetch_all(db)
.await
.unwrap(); //TODO: fixme
let mut res = Vec::new();
for reservation in boatreservations {
let user_confirmation = match reservation.user_id_confirmation {
Some(id) => {
let user = User::find_by_id(db, id as i32).await;
Some(user.unwrap())
}
None => None,
};
let user_applicant = User::find_by_id(db, reservation.user_id_applicant as i32)
.await
.unwrap();
let boat = Boat::find_by_id(db, reservation.boat_id as i32)
.await
.unwrap();
res.push(BoatReservationWithDetails {
boat_reservation: reservation,
boat,
user_applicant,
user_confirmation,
});
}
res
}
pub async fn create(
db: &SqlitePool,
boatreservation: BoatReservationToAdd<'_>,
) -> Result<(), String> {
if Self::boat_reserved_between_dates(
db,
boatreservation.boat,
&boatreservation.start_date,
&boatreservation.end_date,
)
.await
{
return Err("Boot in diesem Zeitraum bereits reserviert.".into());
}
Log::create(db, format!("New boat reservation: {boatreservation:?}")).await;
sqlx::query!(
"INSERT INTO boat_reservation(boat_id, start_date, end_date, time_desc, usage, user_id_applicant) VALUES (?,?,?,?,?,?)",
boatreservation.boat.id,
boatreservation.start_date,
boatreservation.end_date,
boatreservation.time_desc,
boatreservation.usage,
boatreservation.user_applicant.id,
)
.execute(db)
.await
.map_err(|e| e.to_string())?;
let board =
User::all_with_role(db, &Role::find_by_name(db, "Vorstand").await.unwrap()).await;
for user in board {
Notification::create(
db,
&user,
&format!(
"{} hat eine neue Bootsreservierung für Boot '{}' angelegt: Von {} bis {} um {} wegen {}",
boatreservation.user_applicant.name,
boatreservation.boat.name,
boatreservation.start_date,
boatreservation.end_date,
boatreservation.time_desc,
boatreservation.usage
),
"Neue Bootsreservierung",
None,
)
.await;
}
Ok(())
}
pub async fn boat_reserved_between_dates(
db: &SqlitePool,
boat: &Boat,
start_date: &NaiveDate,
end_date: &NaiveDate,
) -> bool {
sqlx::query!(
"SELECT COUNT(*) AS reservation_count
FROM boat_reservation
WHERE boat_id = ?
AND start_date <= ? AND end_date >= ?;",
boat.id,
end_date,
start_date
)
.fetch_one(db)
.await
.unwrap()
.reservation_count
> 0
}
pub async fn delete(&self, db: &SqlitePool) {
sqlx::query!("DELETE FROM boat_reservation WHERE id=?", self.id)
.execute(db)
.await
.unwrap(); //Okay, because we can only create a Boat of a valid id
}
}

View File

@ -10,6 +10,7 @@ use self::{
pub mod boat;
pub mod boatdamage;
pub mod boathouse;
pub mod boatreservation;
pub mod family;
pub mod location;
pub mod log;

View File

@ -6,7 +6,7 @@ use ics::{
Event, ICalendar,
};
use serde::Serialize;
use sqlx::{FromRow, SqlitePool, Row};
use sqlx::{FromRow, Row, SqlitePool};
use super::{tripdetails::TripDetails, triptype::TripType, user::User};
@ -63,7 +63,7 @@ FROM user_trip WHERE trip_details_id = {}
.await
.unwrap()
.into_iter()
.map(|r|
.map(|r|
Registration {
name: r.get::<Option<String>, usize>(0).or(r.get::<Option<String>, usize>(1)).unwrap(), //Ok, either name or user_note needs to be set
registered_at: r.get::<String,usize>(3),