From 6a581aac645a3121dc705396887208abc3890ebd Mon Sep 17 00:00:00 2001 From: philipp Date: Mon, 19 Aug 2024 15:05:36 +0200 Subject: [PATCH] verify, that boat is not on water on adding log entry; Fixes #625 --- src/model/boat.rs | 34 ++++++++++++++++++++++++++++++++++ src/model/logbook.rs | 8 ++++++++ src/tera/log.rs | 1 + 3 files changed, 43 insertions(+) diff --git a/src/model/boat.rs b/src/model/boat.rs index fe87f65..a851180 100644 --- a/src/model/boat.rs +++ b/src/model/boat.rs @@ -1,5 +1,6 @@ use std::ops::DerefMut; +use chrono::NaiveDateTime; use itertools::Itertools; use rocket::serde::{Deserialize, Serialize}; use rocket::FromForm; @@ -391,6 +392,39 @@ ORDER BY amount_seats DESC .await .ok() } + + pub async fn on_water_between( + &self, + db: &mut Transaction<'_, Sqlite>, + dep: NaiveDateTime, + arr: NaiveDateTime, + ) -> bool { + let dep = dep.format("%Y-%m-%dT%H:%M").to_string(); + let arr = arr.format("%Y-%m-%dT%H:%M").to_string(); + + sqlx::query!( + "SELECT COUNT(*) AS overlap_count +FROM logbook +WHERE boat_id = ? + AND ( + (departure <= ? AND arrival >= ?) -- Existing entry covers the entire new period + OR (departure >= ? AND departure < ?) -- Existing entry starts during the new period + OR (arrival > ? AND arrival <= ?) -- Existing entry ends during the new period + );", + self.id, + arr, + arr, + dep, + dep, + dep, + arr + ) + .fetch_one(db.deref_mut()) + .await + .unwrap() + .overlap_count + > 0 + } } #[cfg(test)] diff --git a/src/model/logbook.rs b/src/model/logbook.rs index cac3fdc..a45a3cc 100644 --- a/src/model/logbook.rs +++ b/src/model/logbook.rs @@ -142,6 +142,7 @@ pub enum LogbookUpdateError { TooFast(i64, i64), AlreadyFinalized, ExternalSteeringPersonMustSteerOrShipmaster, + BoatAlreadyOnWater, } #[derive(Debug, PartialEq)] @@ -196,6 +197,7 @@ impl From for LogbookCreateError { LogbookUpdateError::ExternalSteeringPersonMustSteerOrShipmaster => { LogbookCreateError::ExternalSteeringPersonMustSteerOrShipmaster } + LogbookUpdateError::BoatAlreadyOnWater => LogbookCreateError::BoatAlreadyOnWater, } } } @@ -573,6 +575,12 @@ ORDER BY departure DESC return Err(LogbookUpdateError::ArrivalNotAfterDeparture); } + if !boat.external { + if boat.on_water_between(db, dep, arr).await { + return Err(LogbookUpdateError::BoatAlreadyOnWater); + }; + } + let duration_in_mins = (arr.and_utc().timestamp() - dep.and_utc().timestamp()) / 60; // Not possible to row < 1 min / 500 m = < 2 min / km let possible_distance_km = duration_in_mins / 2; diff --git a/src/tera/log.rs b/src/tera/log.rs index 98e49d5..b21b78a 100644 --- a/src/tera/log.rs +++ b/src/tera/log.rs @@ -347,6 +347,7 @@ async fn home_logbook( Err(LogbookUpdateError::TooFast(km, min)) => Flash::error(Redirect::to("/log"), format!("KM zu groß für die eingegebene Dauer ({km} km in {min} Minuten). Bitte überprüfe deine Start- und Endzeit und versuche es erneut.")), Err(LogbookUpdateError::AlreadyFinalized) => Flash::error(Redirect::to("/log"), "Logbucheintrag wurde bereits abgeschlossen."), Err(LogbookUpdateError::ExternalSteeringPersonMustSteerOrShipmaster) => Flash::error(Redirect::to("/log"), "Wenn du eine 'Externe Steuerperson' hinzufügst, muss diese steuern oder Schiffsführer sein!"), + Err(LogbookUpdateError::BoatAlreadyOnWater) => Flash::error(Redirect::to("/log"), "Das Boot war in diesem Zeitraum schon am Wasser. Bitte überprüfe das Datum und die Zeit."), Err(e) => Flash::error( Redirect::to("/log"), format!("Eintrag {logbook_id} konnte nicht abgesendet werden (Fehler: {e:?})!"),