board members can delete trips, proper notification + succ message is created
All checks were successful
CI/CD Pipeline / test (push) Successful in 24m26s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped

This commit is contained in:
2025-05-21 09:56:45 +02:00
parent 129c90f1aa
commit 4e04b2b082
15 changed files with 269 additions and 156 deletions

View File

@ -1,4 +1,4 @@
use std::ops::DerefMut;
use std::{fmt::Display, ops::DerefMut};
use chrono::{Datelike, Duration, Local, NaiveDateTime};
use rocket::FromForm;
@ -6,8 +6,15 @@ use serde::{Deserialize, Serialize};
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
use super::{
boat::Boat, log::Log, notification::Notification, role::Role, rower::Rower, user::User,
activity::{ActivityBuilder, ReasonLogbook},
boat::Boat,
log::Log,
notification::Notification,
role::Role,
rower::Rower,
user::User,
};
use crate::model::user::VecUser;
#[derive(FromRow, Serialize, Deserialize, Clone, Debug)]
pub struct Logbook {
@ -115,6 +122,54 @@ pub struct LogbookWithBoatAndRowers {
pub rowers: Vec<User>,
}
impl Display for LogbookWithBoatAndRowers {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(arrival) = self.logbook.arrival {
let departure_date = format!("{}", self.logbook.departure.format("%Y-%m-%d"));
let arrival_date = format!("{}", arrival.format("%Y-%m-%d"));
if departure_date == arrival_date {
write!(
f,
"Datum: {}: Start: {}, Ende: {}; ",
&self.logbook.departure.format("%d. %m. %Y"),
&self.logbook.departure.format("%H:%M"),
&arrival.format("%H:%M")
)?;
} else {
write!(
f,
"{} - {}; ",
&self.logbook.departure.format("%d. %m. %Y"),
&arrival.format("%d. %m. %Y"),
)?;
}
} else {
write!(
f,
"Start: {}",
&self.logbook.departure.format("%d. %m. %Y %H:%M")
)?;
}
if let Some(destination) = &self.logbook.destination {
write!(f, "Ziel: {destination}; ")?;
}
write!(f, "Boot: {}; ", self.boat)?;
if let Some(distance) = self.logbook.distance_in_km {
write!(f, "Distanz: {distance} km; ")?;
}
write!(f, "Schiffsführer: {}; ", self.shipmaster_user)?;
write!(f, "Steuerperson: {}; ", self.steering_user)?;
write!(f, "Rudernde: {}; ", VecUser(&self.rowers))?;
if let Some(comments) = &self.logbook.comments {
if !comments.trim().is_empty() {
write!(f, "Kommentar: {comments}; ")?;
}
}
Ok(())
}
}
impl LogbookWithBoatAndRowers {
pub(crate) async fn from(db: &SqlitePool, log: Logbook) -> Self {
let mut tx = db.begin().await.unwrap();
@ -811,43 +866,22 @@ ORDER BY departure DESC
}
pub async fn delete(&self, db: &SqlitePool, user: &User) -> Result<(), LogbookDeleteError> {
Log::create(db, format!("{} deleted trip: {self:?}", user.name)).await;
if self.arrival.is_none() {
if user.has_role(db, "admin").await
|| user.has_role(db, "Vorstand").await
|| user.id == self.shipmaster
{
Log::create(db, format!("{} deleted trip: {self:?}", user.name)).await;
let now = Local::now().naive_local();
let difference = now - self.departure;
if difference > Duration::hours(1) {
let vorstand = Role::find_by_name(db, "Vorstand").await.unwrap();
let logbook = LogbookWithBoatAndRowers::from(db, self.clone()).await;
let mut msg = format!(
"{} hat folgenden Logbuch-Eintrag jetzt gelöscht, welcher bereits vor über einer Stunde begonnen wurde: Schiffsführer: {}, Steuerperson: {}, Abfahrt: {}",
user.name,
logbook.steering_user.name,
logbook.steering_user.name,
logbook.logbook.departure.format("%Y-%m-%d %H:%M")
);
if let Some(destination) = logbook.logbook.destination {
msg.push_str(&format!(", Ziel: {}", destination));
} else {
msg.push_str(", kein Ziel eingegeben");
}
msg.push_str(", Ruderer: ");
let mut it = logbook.rowers.clone().into_iter().peekable();
while let Some(rower) = it.next() {
msg.push_str(&rower.name);
if it.peek().is_some() {
msg.push_str(" + ");
}
}
Notification::create_for_role(
db,
&vorstand,
&msg,
&format!("{user} hat folgenden Logbuch-Eintrag jetzt gelöscht, welcher bereits vor über einer Stunde begonnen wurde: {logbook}"),
"Ungewöhnliches Verhalten",
None,
None,
@ -862,8 +896,24 @@ ORDER BY departure DESC
return Ok(());
}
} else {
// Only admins can delete completed logbook entries
if user.has_role(db, "admin").await {
// Only admins+Vorstand can delete completed logbook entries
if user.has_role(db, "admin").await || user.has_role(db, "Vorstand").await {
let logbookdetails = LogbookWithBoatAndRowers::from(db, self.clone()).await;
ActivityBuilder::from(ReasonLogbook::BoardOrAdminDeleted(user, &logbookdetails))
.save(db)
.await;
let vorstand = Role::find_by_name(db, "Vorstand").await.unwrap();
Notification::create_for_role(
db,
&vorstand,
&format!("{user} hat den Logbuch-Eintrag gelöscht: {logbookdetails}"),
"Logbuch gelöscht",
None,
None,
)
.await;
sqlx::query!("DELETE FROM logbook WHERE id=?", self.id)
.execute(db)
.await