214 lines
6.3 KiB
Rust
214 lines
6.3 KiB
Rust
use std::collections::HashMap;
|
|
|
|
use crate::model::{boat::Boat, user::User};
|
|
use chrono::NaiveDate;
|
|
use chrono::NaiveDateTime;
|
|
use rocket::serde::{Deserialize, Serialize};
|
|
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)]
|
|
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 {
|
|
reservation,
|
|
boat,
|
|
user_applicant,
|
|
user_confirmation,
|
|
});
|
|
}
|
|
res
|
|
}
|
|
pub async fn all_future_with_groups(
|
|
db: &SqlitePool,
|
|
) -> HashMap<String, Vec<BoatReservationWithDetails>> {
|
|
let mut grouped_reservations: HashMap<String, Vec<BoatReservationWithDetails>> =
|
|
HashMap::new();
|
|
|
|
let reservations = Self::all_future(db).await;
|
|
for reservation in reservations {
|
|
let key = format!(
|
|
"{}-{}-{}-{}-{}",
|
|
reservation.reservation.start_date,
|
|
reservation.reservation.end_date,
|
|
reservation.reservation.time_desc,
|
|
reservation.reservation.usage,
|
|
reservation.user_applicant.name
|
|
);
|
|
|
|
grouped_reservations
|
|
.entry(key)
|
|
.or_insert_with(Vec::new)
|
|
.push(reservation);
|
|
}
|
|
|
|
grouped_reservations
|
|
}
|
|
|
|
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 {
|
|
let date = if boatreservation.start_date == boatreservation.end_date {
|
|
format!("am {}", boatreservation.start_date)
|
|
} else {
|
|
format!(
|
|
"von {} bis {}",
|
|
boatreservation.start_date, boatreservation.end_date
|
|
)
|
|
};
|
|
|
|
Notification::create(
|
|
db,
|
|
&user,
|
|
&format!(
|
|
"{} hat eine neue Bootsreservierung für Boot '{}' {} angelegt. Zeit: {}; Zweck: {}",
|
|
boatreservation.user_applicant.name,
|
|
boatreservation.boat.name,
|
|
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
|
|
}
|
|
}
|