new newbie flag

This commit is contained in:
2026-06-04 19:49:16 +02:00
parent 0e1973fbac
commit c940ce0fdc
17 changed files with 114 additions and 28 deletions
+5 -3
View File
@@ -301,8 +301,9 @@ mod test {
always_show: event.always_show,
is_locked: event.is_locked,
trip_type_id: None,
allow_guests: event.allow_guests,
};
event.update(&pool, &user, &cancel_update).await;
event.update(&pool, &user, &cancel_update).await.unwrap();
// Rower received notification
let notifications = Notification::for_user(&pool, &rower).await;
@@ -331,13 +332,14 @@ mod test {
always_show: event.always_show,
is_locked: event.is_locked,
trip_type_id: None,
allow_guests: event.allow_guests,
};
event.update(&pool, &user, &update).await;
event.update(&pool, &user, &update).await.unwrap();
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, &user, &cancel_update).await;
event.update(&pool, &user, &cancel_update).await.unwrap();
// Rower is removed if notification is accepted
assert!(event.is_rower_registered(&pool, &rower).await);
+23 -8
View File
@@ -48,6 +48,7 @@ pub struct Registration {
pub name: String,
pub registered_at: String,
pub is_guest: bool,
pub is_newbie: bool,
pub is_real_guest: bool,
}
@@ -57,12 +58,13 @@ impl Registration {
&format!(
r#"
SELECT
(SELECT name FROM user WHERE user_trip.user_id = user.id) as "name?",
(SELECT name FROM user WHERE user_trip.user_id = user.id) as "name?",
user_note,
user_id,
(SELECT created_at FROM user WHERE user_trip.user_id = user.id) as registered_at,
(SELECT EXISTS (SELECT 1 FROM user_role WHERE user_role.user_id = user_trip.user_id AND user_role.role_id = (SELECT id FROM role WHERE name = 'scheckbuch'))) as is_guest
FROM user_trip WHERE trip_details_id = {}
(SELECT EXISTS (SELECT 1 FROM user_role WHERE user_role.user_id = user_trip.user_id AND user_role.role_id = (SELECT id FROM role WHERE name = 'scheckbuch'))) as is_guest,
(SELECT EXISTS (SELECT 1 FROM user_role WHERE user_role.user_id = user_trip.user_id AND user_role.role_id = (SELECT id FROM role WHERE name = 'Vereinsneuling'))) as is_newbie
FROM user_trip WHERE trip_details_id = {}
"#,trip_details_id),
)
.fetch_all(db)
@@ -74,6 +76,7 @@ FROM user_trip WHERE trip_details_id = {}
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),
is_guest: r.get::<bool, usize>(4),
is_newbie: r.get::<bool, usize>(5),
is_real_guest: r.get::<Option<i64>, usize>(2).is_none(),
})
.collect()
@@ -98,6 +101,7 @@ FROM trip WHERE planned_event_id = ?
name: r.name.unwrap(),
registered_at: r.registered_at.unwrap(),
is_guest: false,
is_newbie: false,
is_real_guest: false,
})
.collect() //Okay, as Event can only be created with proper DB backing
@@ -113,6 +117,7 @@ pub struct EventUpdate<'a> {
pub always_show: bool,
pub is_locked: bool,
pub trip_type_id: Option<i64>,
pub allow_guests: bool,
}
impl EventUpdate<'_> {
@@ -318,7 +323,17 @@ WHERE trip_details.id=?
}
//TODO: create unit test
pub async fn update(&self, db: &SqlitePool, user: &EventUser, update: &EventUpdate<'_>) {
pub async fn update(&self, db: &SqlitePool, user: &EventUser, update: &EventUpdate<'_>) -> Result<(), String> {
let tripdetails = self.trip_details(db).await;
let was_already_cancelled = tripdetails.cancelled();
if tripdetails.allow_guests && !update.allow_guests {
let rowers = Registration::all_rower(db, self.trip_details_id).await;
if rowers.iter().any(|r| r.is_newbie) {
return Err("Es sind bereits Neulinge angemeldet — 'Neulinge willkommen' kann nicht deaktiviert werden.".into());
}
}
sqlx::query!(
"UPDATE planned_event SET name = ?, planned_amount_cox = ? WHERE id = ?",
update.name,
@@ -329,16 +344,14 @@ WHERE trip_details.id=?
.await
.unwrap(); //Okay, as planned_event can only be created with proper DB backing
let tripdetails = self.trip_details(db).await;
let was_already_cancelled = tripdetails.cancelled();
sqlx::query!(
"UPDATE trip_details SET max_people = ?, notes = ?, always_show = ?, is_locked = ?, trip_type_id = ? WHERE id = ?",
"UPDATE trip_details SET max_people = ?, notes = ?, always_show = ?, is_locked = ?, trip_type_id = ?, allow_guests = ? WHERE id = ?",
update.max_people,
update.notes,
update.always_show,
update.is_locked,
update.trip_type_id,
update.allow_guests,
self.trip_details_id
)
.execute(db)
@@ -426,6 +439,8 @@ WHERE trip_details.id=?
.await;
Notification::delete_by_action(db, &format!("remove_trip_by_event:{}", self.id)).await;
}
Ok(())
}
pub async fn delete(&self, db: &SqlitePool) -> Result<(), String> {
+14 -1
View File
@@ -48,6 +48,7 @@ pub struct TripUpdate<'a> {
pub notes: Option<&'a str>,
pub trip_type: Option<i64>, //TODO: Move to `TripType`
pub is_locked: bool,
pub allow_guests: bool,
}
impl TripUpdate<'_> {
@@ -228,6 +229,13 @@ WHERE day=?
let tripdetails = TripDetails::find_by_id(db, trip_details_id).await.unwrap();
let was_already_cancelled = tripdetails.cancelled();
if tripdetails.allow_guests && !update.allow_guests {
let rowers = Registration::all_rower(db, trip_details_id).await;
if rowers.iter().any(|r| r.is_newbie) {
return Err(TripUpdateError::NeulingAlreadyRegistered);
}
}
let is_locked = if update.cancelled() {
false
} else {
@@ -235,11 +243,12 @@ WHERE day=?
};
sqlx::query!(
"UPDATE trip_details SET max_people = ?, notes = ?, trip_type_id = ?, is_locked = ? WHERE id = ?",
"UPDATE trip_details SET max_people = ?, notes = ?, trip_type_id = ?, is_locked = ?, allow_guests = ? WHERE id = ?",
update.max_people,
update.notes,
update.trip_type,
is_locked,
update.allow_guests,
trip_details_id
)
.execute(db)
@@ -407,6 +416,7 @@ pub enum TripUpdateError {
NotYourTrip,
TripDetailsDoesNotExist,
TripTypeNotAllowed,
NeulingAlreadyRegistered,
}
#[cfg(test)]
@@ -540,6 +550,7 @@ mod test {
notes: None,
trip_type: None,
is_locked: false,
allow_guests: false,
};
assert!(Trip::update_own(&pool, &update).await.is_ok());
@@ -568,6 +579,7 @@ mod test {
notes: None,
trip_type: Some(1),
is_locked: false,
allow_guests: false,
};
assert!(Trip::update_own(&pool, &update).await.is_ok());
@@ -596,6 +608,7 @@ mod test {
notes: None,
trip_type: None,
is_locked: false,
allow_guests: false,
};
assert!(Trip::update_own(&pool, &update).await.is_err());
assert_eq!(trip.max_people, 1);
+11
View File
@@ -528,6 +528,17 @@ impl User {
Ok(())
}
pub(crate) async fn add_vereinsneuling(
&self,
db: &SqlitePool,
updated_by: &ManageUserUser,
) -> Result<(), String> {
if let Some(vereinsneuling) = Role::find_by_name(db, "Vereinsneuling").await {
self.add_role(db, updated_by, &vereinsneuling).await?;
}
Ok(())
}
pub(crate) async fn remove_membership_pdf(&self, db: &SqlitePool, updated_by: &ManageUserUser) {
ActivityBuilder::new(&format!(
"{updated_by} hat die Beitrittserklärung vom Beutzer gelöscht."
+10 -5
View File
@@ -112,6 +112,7 @@ impl UserWithDetails {
self.roles.contains(&"Donau Linz".into())
|| self.roles.contains(&"Förderndes Mitglied".into())
|| self.roles.contains(&"scheckbuch".into())
|| self.roles.contains(&"Vereinsneuling".into())
|| self.user.name == "Externe Steuerperson"
}
}
@@ -598,18 +599,22 @@ ASKÖ Ruderverein Donau Linz", self.name),
pub async fn get_days(&self, db: &SqlitePool) -> Vec<Day> {
let mut days = Vec::new();
for i in 0..self.amount_days_to_show(db).await {
let roles = self.roles(db).await;
let is_beginner = roles.contains(&"scheckbuch".to_string())
|| roles.contains(&"Vereinsneuling".to_string());
let days_to_show = self.amount_days_to_show(db).await;
for i in 0..days_to_show {
let date = (Local::now() + chrono::Duration::days(i)).date_naive();
if self.has_role(db, "scheckbuch").await {
if is_beginner {
days.push(Day::new_guest(db, date, false).await);
} else {
days.push(Day::new(db, date, false).await);
}
}
for date in TripDetails::pinned_days(db, self.amount_days_to_show(db).await - 1).await {
if self.has_role(db, "scheckbuch").await {
for date in TripDetails::pinned_days(db, days_to_show - 1).await {
if is_beginner {
let day = Day::new_guest(db, date, true).await;
if !day.events.is_empty() {
days.push(day);
@@ -868,7 +873,7 @@ special_user!(TechUser, +"tech");
special_user!(ErgoUser, +"ergo");
special_user!(SteeringUser, +"cox", +"Bootsführer");
special_user!(AdminUser, +"admin");
special_user!(AllowedForPlannedTripsUser, +"Donau Linz", +"scheckbuch", +"Förderndes Mitglied");
special_user!(AllowedForPlannedTripsUser, +"Donau Linz", +"scheckbuch", +"Förderndes Mitglied", +"Vereinsneuling");
special_user!(DonauLinzUser, +"Donau Linz", +"Förderndes Mitglied", -"Unterstützend"); // TODO:
// remove ->
// RegularUser
+3
View File
@@ -99,6 +99,7 @@ impl NoMembershipUser {
let regular = Role::find_by_name(db, "Donau Linz").await.unwrap();
self.user.add_role(db, changed_by, &regular).await?;
self.user.add_vereinsneuling(db, changed_by).await?;
let regular = RegularUser::new(db, &self.user).await.unwrap();
regular.send_welcome_mail_to_user(db, smtp_pw).await?;
@@ -149,6 +150,7 @@ impl NoMembershipUser {
let unterstuetzend = Role::find_by_name(db, "Unterstützend").await.unwrap();
self.user.add_role(db, changed_by, &unterstuetzend).await?;
self.user.add_vereinsneuling(db, changed_by).await?;
let unterstuetzend = UnterstuetzendUser::new(db, &self.user).await.unwrap();
unterstuetzend
@@ -203,6 +205,7 @@ impl NoMembershipUser {
let foerdernd = Role::find_by_name(db, "Förderndes Mitglied").await.unwrap();
self.user.add_role(db, changed_by, &foerdernd).await?;
self.user.add_vereinsneuling(db, changed_by).await?;
let foerdernd = FoerderndUser::new(db, &self.user).await.unwrap();
foerdernd.send_welcome_mail_to_user(db, smtp_pw).await?;
+1
View File
@@ -47,6 +47,7 @@ pub trait ClubMember {
let user = User::find_by_name(db, name).await.unwrap();
user.change_financial(db, created_by, financial).await?;
user.add_role(db, created_by, role).await?;
user.add_vereinsneuling(db, created_by).await?;
ActivityBuilder::new(&format!(
"{created_by} hat Mitglied {user} mit der Rolle {role} angelegt."
+3
View File
@@ -68,6 +68,7 @@ impl ScheckbuchUser {
let scheckbook = Role::find_by_name(db, "scheckbuch").await.unwrap();
self.user.remove_role(db, changed_by, &scheckbook).await?;
self.user.add_role(db, changed_by, &regular).await?;
self.user.add_vereinsneuling(db, changed_by).await?;
// Notify
let regular = RegularUser::new(db, &self.user).await.unwrap();
@@ -123,6 +124,7 @@ impl ScheckbuchUser {
let scheckbook = Role::find_by_name(db, "scheckbuch").await.unwrap();
self.user.remove_role(db, changed_by, &scheckbook).await?;
self.user.add_role(db, changed_by, &unterstuetzend).await?;
self.user.add_vereinsneuling(db, changed_by).await?;
let unterstuetzend = UnterstuetzendUser::new(db, &self.user).await.unwrap();
unterstuetzend
@@ -179,6 +181,7 @@ impl ScheckbuchUser {
let scheckbook = Role::find_by_name(db, "scheckbuch").await.unwrap();
self.user.remove_role(db, changed_by, &scheckbook).await?;
self.user.add_role(db, changed_by, &unterstuetzend).await?;
self.user.add_vereinsneuling(db, changed_by).await?;
let foerdernd = FoerderndUser::new(db, &self.user).await.unwrap();
foerdernd.send_welcome_mail_to_user(db, smtp_pw).await?;
+3
View File
@@ -75,6 +75,7 @@ impl SchnupperantUser {
let regular = Role::find_by_name(db, "Donau Linz").await.unwrap();
self.user.add_role(db, changed_by, &regular).await?;
self.user.add_vereinsneuling(db, changed_by).await?;
let participated_schnupperkurs = Role::find_by_name(db, "participated_schnupperkurs")
.await
@@ -224,6 +225,7 @@ impl SchnupperantUser {
let scheckbook = Role::find_by_name(db, "schnupperant").await.unwrap();
self.user.remove_role(db, changed_by, &scheckbook).await?;
self.user.add_role(db, changed_by, &unterstuetzend).await?;
self.user.add_vereinsneuling(db, changed_by).await?;
if let Some(no_einschreibgebuehr) = Role::find_by_name(db, "no-einschreibgebuehr").await {
self.add_role(db, changed_by, &no_einschreibgebuehr)
.await
@@ -293,6 +295,7 @@ impl SchnupperantUser {
let scheckbook = Role::find_by_name(db, "schnupperant").await.unwrap();
self.user.remove_role(db, changed_by, &scheckbook).await?;
self.user.add_role(db, changed_by, &unterstuetzend).await?;
self.user.add_vereinsneuling(db, changed_by).await?;
if let Some(no_einschreibgebuehr) = Role::find_by_name(db, "no-einschreibgebuehr").await {
self.add_role(db, changed_by, &no_einschreibgebuehr)
.await
+6 -4
View File
@@ -62,6 +62,7 @@ struct UpdateEventForm<'r> {
always_show: bool,
is_locked: bool,
trip_type: Option<i64>,
allow_guests: bool,
}
#[put("/planned-event", data = "<data>")]
@@ -78,12 +79,13 @@ async fn update(
always_show: data.always_show,
is_locked: data.is_locked,
trip_type_id: data.trip_type,
allow_guests: data.allow_guests,
};
match Event::find_by_id(db, data.id).await {
Some(planned_event) => {
planned_event.update(db, &user, &update).await;
Flash::success(Redirect::to("/planned"), "Event erfolgreich bearbeitet")
}
Some(planned_event) => match planned_event.update(db, &user, &update).await {
Ok(_) => Flash::success(Redirect::to("/planned"), "Event erfolgreich bearbeitet"),
Err(e) => Flash::error(Redirect::to("/planned"), e),
},
None => Flash::error(Redirect::to("/planned"), "Planned event id not found"),
}
}
+6
View File
@@ -52,6 +52,7 @@ struct EditTripForm<'r> {
notes: Option<&'r str>,
trip_type: Option<i64>,
is_locked: bool,
allow_guests: bool,
}
#[post("/trip/<trip_id>", data = "<data>")]
@@ -69,6 +70,7 @@ async fn update(
notes: data.notes,
trip_type: data.trip_type,
is_locked: data.is_locked,
allow_guests: data.allow_guests,
};
match Trip::update_own(db, &update).await {
Ok(_) => Flash::success(
@@ -85,6 +87,10 @@ async fn update(
Err(TripUpdateError::TripDetailsDoesNotExist) => {
Flash::error(Redirect::to("/planned"), "Ausfahrt gibt's nicht")
}
Err(TripUpdateError::NeulingAlreadyRegistered) => Flash::error(
Redirect::to("/planned"),
"Es sind bereits Neulinge angemeldet — 'Neulinge willkommen' kann nicht deaktiviert werden.",
),
}
} else {
Flash::error(Redirect::to("/planned"), "Ausfahrt gibt's nicht")