diff --git a/src/model/event.rs b/src/model/event.rs index 3605ea4..c0c8349 100644 --- a/src/model/event.rs +++ b/src/model/event.rs @@ -14,7 +14,7 @@ use super::{notification::Notification, tripdetails::TripDetails, triptype::Trip pub struct Event { pub id: i64, pub name: String, - planned_amount_cox: i64, + pub(crate) planned_amount_cox: i64, trip_details_id: i64, pub planned_starting_time: String, pub(crate) max_people: i64, @@ -22,8 +22,8 @@ pub struct Event { pub notes: Option, pub allow_guests: bool, trip_type_id: Option, - always_show: bool, - is_locked: bool, + pub(crate) always_show: bool, + pub(crate) is_locked: bool, } #[derive(Serialize, Debug)] @@ -73,7 +73,7 @@ FROM user_trip WHERE trip_details_id = {} .collect() } - pub async fn all_cox(db: &SqlitePool, trip_details_id: i64) -> Vec { + pub async fn all_cox(db: &SqlitePool, event_id: i64) -> Vec { //TODO: switch to join sqlx::query!( " @@ -82,7 +82,7 @@ SELECT (SELECT created_at FROM user WHERE cox_id = id) as registered_at FROM trip WHERE planned_event_id = ? ", - trip_details_id + event_id ) .fetch_all(db) .await @@ -159,7 +159,7 @@ WHERE day=?", cox_needed: event.planned_amount_cox > cox.len() as i64, cox, rower: Registration::all_rower(db, event.trip_details_id).await, - event: event, + event, trip_type, }); } @@ -195,11 +195,27 @@ INNER JOIN trip_details ON planned_event.trip_details_id = trip_details.id", is_rower.amount > 0 } + pub async fn find_by_trip_details(db: &SqlitePool, tripdetails_id: i64) -> Option { + sqlx::query_as!( + Self, + " +SELECT planned_event.id, planned_event.name, planned_amount_cox, trip_details_id, planned_starting_time, always_show, max_people, day, notes, allow_guests, trip_type_id, is_locked +FROM planned_event +INNER JOIN trip_details ON planned_event.trip_details_id = trip_details.id +WHERE trip_details.id=? + ", + tripdetails_id + ) + .fetch_one(db) + .await + .ok() + } + pub async fn create( db: &SqlitePool, name: &str, planned_amount_cox: i32, - trip_details: TripDetails, + trip_details: &TripDetails, ) { sqlx::query!( "INSERT INTO planned_event(name, planned_amount_cox, trip_details_id) VALUES(?, ?, ?)", @@ -257,7 +273,7 @@ INNER JOIN trip_details ON planned_event.trip_details_id = trip_details.id", ), "Absage Ausfahrt", None, - Some(&format!("remove_trip_by_planned_event:{}", self.id)), + Some(&format!("remove_trip_by_event:{}", self.id)), ) .await; } @@ -295,11 +311,7 @@ INNER JOIN trip_details ON planned_event.trip_details_id = trip_details.id", &format!("remove_user_trip_with_trip_details_id:{}", tripdetails.id), ) .await; - Notification::delete_by_action( - db, - &format!("remove_trip_by_planned_event:{}", self.id), - ) - .await; + Notification::delete_by_action(db, &format!("remove_trip_by_event:{}", self.id)).await; } } @@ -379,7 +391,7 @@ mod test { let trip_details = TripDetails::find_by_id(&pool, 1).await.unwrap(); - Event::create(&pool, "new-event".into(), 2, trip_details).await; + Event::create(&pool, "new-event".into(), 2, &trip_details).await; let res = Event::get_for_day(&pool, NaiveDate::from_ymd_opt(1970, 1, 1).unwrap()).await; assert_eq!(res.len(), 2); diff --git a/src/model/notification.rs b/src/model/notification.rs index 841ad3c..9bb242e 100644 --- a/src/model/notification.rs +++ b/src/model/notification.rs @@ -7,7 +7,7 @@ use sqlx::{FromRow, Sqlite, SqlitePool, Transaction}; use super::{role::Role, user::User}; -#[derive(FromRow, Debug, Serialize, Deserialize)] +#[derive(FromRow, Debug, Serialize, Deserialize, Clone)] pub struct Notification { pub id: i64, pub user_id: i64, @@ -153,7 +153,7 @@ ORDER BY read_at DESC, created_at DESC; } } // Cox read notification about cancelled event - let re = Regex::new(r"^remove_trip_by_planned_event:(\d+)$").unwrap(); + let re = Regex::new(r"^remove_trip_by_event:(\d+)$").unwrap(); if let Some(caps) = re.captures(action) { if let Some(matched) = caps.get(1) { if let Ok(number) = matched.as_str().parse::() { @@ -180,3 +180,114 @@ ORDER BY read_at DESC, created_at DESC; .unwrap(); } } + +#[cfg(test)] +mod test { + use crate::{ + model::{ + event::{Event, EventUpdate, Registration}, + notification::Notification, + trip::Trip, + tripdetails::{TripDetails, TripDetailsToAdd}, + user::{CoxUser, User}, + usertrip::UserTrip, + }, + testdb, + }; + + use sqlx::SqlitePool; + + #[sqlx::test] + fn event_canceled() { + let pool = testdb!(); + + // Create event + let add_tripdetails = TripDetailsToAdd { + planned_starting_time: "10:00", + max_people: 4, + day: "1970-02-01".into(), + notes: None, + trip_type: None, + allow_guests: false, + always_show: false, + }; + let tripdetails_id = TripDetails::create(&pool, add_tripdetails).await; + let trip_details = TripDetails::find_by_id(&pool, tripdetails_id) + .await + .unwrap(); + Event::create(&pool, "new-event".into(), 2, &trip_details).await; + let event = Event::find_by_trip_details(&pool, trip_details.id) + .await + .unwrap(); + + // Rower + Cox joins + let rower = User::find_by_name(&pool, "rower").await.unwrap(); + UserTrip::create(&pool, &rower, &trip_details, None) + .await + .unwrap(); + let cox = CoxUser::new(&pool, User::find_by_name(&pool, "cox").await.unwrap()) + .await + .unwrap(); + Trip::new_join(&pool, &cox, &event).await.unwrap(); + + // Cancel Event + let cancel_update = EventUpdate { + name: &event.name, + planned_amount_cox: event.planned_amount_cox as i32, + max_people: 0, + notes: event.notes.as_deref(), + always_show: event.always_show, + is_locked: event.is_locked, + }; + event.update(&pool, &cancel_update).await; + + // Rower received notification + let notifications = Notification::for_user(&pool, &rower).await; + let rower_notification = notifications[0].clone(); + assert_eq!(rower_notification.category, "Absage Ausfahrt"); + assert_eq!( + rower_notification.action_after_reading.as_deref(), + Some("remove_user_trip_with_trip_details_id:3") + ); + + // Cox received notification + let notifications = Notification::for_user(&pool, &cox.user).await; + let cox_notification = notifications[0].clone(); + assert_eq!(cox_notification.category, "Absage Ausfahrt"); + assert_eq!( + cox_notification.action_after_reading.as_deref(), + Some("remove_trip_by_event:2") + ); + + // Notification removed if cancellation is cancelled + let update = EventUpdate { + name: &event.name, + planned_amount_cox: event.planned_amount_cox as i32, + max_people: 3, + notes: event.notes.as_deref(), + always_show: event.always_show, + is_locked: event.is_locked, + }; + event.update(&pool, &update).await; + assert!(Notification::for_user(&pool, &rower).await.is_empty()); + assert!(Notification::for_user(&pool, &cox.user).await.is_empty()); + + // Cancel event again + event.update(&pool, &cancel_update).await; + + // Rower is removed if notification is accepted + assert!(event.is_rower_registered(&pool, &rower).await); + rower_notification.mark_read(&pool).await; + assert!(!event.is_rower_registered(&pool, &rower).await); + + // Cox is removed if notification is accepted + let registration = Registration::all_cox(&pool, event.id).await; + assert_eq!(registration.len(), 1); + assert_eq!(registration[0].name, "cox"); + + cox_notification.mark_read(&pool).await; + + let registration = Registration::all_cox(&pool, event.id).await; + assert!(registration.is_empty()); + } +} diff --git a/src/tera/admin/planned_event.rs b/src/tera/admin/planned_event.rs index 8e8ec67..7fa5303 100644 --- a/src/tera/admin/planned_event.rs +++ b/src/tera/admin/planned_event.rs @@ -34,7 +34,7 @@ async fn create( //just created //the object - Event::create(db, data.name, data.planned_amount_cox, trip_details).await; + Event::create(db, data.name, data.planned_amount_cox, &trip_details).await; Flash::success(Redirect::to("/planned"), "Event hinzugefĆ¼gt") }