From d9e86bf43bcec3fb91a53961ab1a1f7304bab354 Mon Sep 17 00:00:00 2001 From: Philipp Hofer Date: Mon, 5 May 2025 11:35:38 +0200 Subject: [PATCH] allow to create users --- src/main.rs | 2 +- src/model/boat.rs | 2 +- src/model/boatdamage.rs | 2 +- src/model/event.rs | 9 +- src/model/family.rs | 2 +- src/model/logbook.rs | 8 +- src/model/mail.rs | 4 +- src/model/personal/cal.rs | 2 +- src/model/trip.rs | 8 +- src/model/user/basic.rs | 86 ++++++--- src/model/user/fee.rs | 4 +- src/model/user/foerdernd.rs | 60 +++++- src/model/user/member.rs | 22 +-- src/model/user/mod.rs | 20 +- src/model/user/regular.rs | 107 ++++++++++- src/model/user/scheckbuch.rs | 40 +++- src/model/user/schnupperant.rs | 46 ++++- src/model/user/schnupperinterest.rs | 40 +++- src/model/user/unterstuetzend.rs | 60 +++++- src/rest/mod.rs | 2 +- src/scheduled/weather.rs | 4 +- src/tera/admin/boat.rs | 13 +- src/tera/admin/event.rs | 7 +- src/tera/admin/mail.rs | 6 +- src/tera/admin/mod.rs | 4 +- src/tera/admin/notification.rs | 5 +- src/tera/admin/schnupper.rs | 4 +- src/tera/admin/user.rs | 267 +++++++++++++++++++++++++-- src/tera/auth.rs | 9 +- src/tera/board/achievement.rs | 4 +- src/tera/board/boathouse.rs | 5 +- src/tera/boatdamage.rs | 3 +- src/tera/boatreservation.rs | 3 +- src/tera/cox.rs | 28 +-- src/tera/ergo.rs | 5 +- src/tera/log.rs | 146 +++++++++++---- src/tera/misc.rs | 2 +- src/tera/mod.rs | 22 ++- src/tera/notification.rs | 4 +- src/tera/planned.rs | 33 ++-- src/tera/stat.rs | 4 +- src/tera/trailerreservation.rs | 3 +- templates/admin/user/index.html.tera | 72 ++++++-- templates/admin/user/view.html.tera | 26 +-- 44 files changed, 964 insertions(+), 241 deletions(-) diff --git a/src/main.rs b/src/main.rs index 6cc87a7..0a57574 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,7 +8,7 @@ use rot::rest; use rot::tera; use rot::{scheduled, tera::Config}; -use sqlx::{pool::PoolOptions, sqlite::SqliteConnectOptions, ConnectOptions}; +use sqlx::{ConnectOptions, pool::PoolOptions, sqlite::SqliteConnectOptions}; #[macro_use] extern crate rocket; diff --git a/src/model/boat.rs b/src/model/boat.rs index 4776cf9..22818f4 100644 --- a/src/model/boat.rs +++ b/src/model/boat.rs @@ -2,8 +2,8 @@ use std::ops::DerefMut; use chrono::NaiveDateTime; use itertools::Itertools; -use rocket::serde::{Deserialize, Serialize}; use rocket::FromForm; +use rocket::serde::{Deserialize, Serialize}; use sqlx::{FromRow, Sqlite, SqlitePool, Transaction}; use crate::model::boathouse::Boathouse; diff --git a/src/model/boatdamage.rs b/src/model/boatdamage.rs index bcdb377..3f6c742 100644 --- a/src/model/boatdamage.rs +++ b/src/model/boatdamage.rs @@ -1,7 +1,7 @@ use crate::model::{boat::Boat, user::User}; use chrono::NaiveDateTime; -use rocket::serde::{Deserialize, Serialize}; use rocket::FromForm; +use rocket::serde::{Deserialize, Serialize}; use sqlx::{FromRow, SqlitePool}; use super::log::Log; diff --git a/src/model/event.rs b/src/model/event.rs index 699f5e7..4af2276 100644 --- a/src/model/event.rs +++ b/src/model/event.rs @@ -2,8 +2,8 @@ use std::io::Write; use chrono::{Duration, NaiveDate, NaiveTime}; use ics::{ - properties::{DtEnd, DtStart, Summary}, ICalendar, + properties::{DtEnd, DtStart, Summary}, }; use serde::Serialize; use sqlx::{FromRow, Row, SqlitePool}; @@ -578,6 +578,11 @@ mod test { let today = Local::now().date_naive().format("%Y%m%d").to_string(); let actual = Event::get_ics_feed(&pool).await; - assert_eq!(format!("BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:ics-rs\r\nBEGIN:VEVENT\r\nUID:event-1@rudernlinz.at\r\nDTSTAMP:19900101T180000\r\nDTSTART:{today}T100000\r\nDTEND:{today}T130000\r\nSUMMARY:test-planned-event \r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"), actual); + assert_eq!( + format!( + "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:ics-rs\r\nBEGIN:VEVENT\r\nUID:event-1@rudernlinz.at\r\nDTSTAMP:19900101T180000\r\nDTSTART:{today}T100000\r\nDTEND:{today}T130000\r\nSUMMARY:test-planned-event \r\nEND:VEVENT\r\nEND:VCALENDAR\r\n" + ), + actual + ); } } diff --git a/src/model/family.rs b/src/model/family.rs index 46bc95e..8794779 100644 --- a/src/model/family.rs +++ b/src/model/family.rs @@ -1,7 +1,7 @@ use std::ops::DerefMut; use serde::Serialize; -use sqlx::{sqlite::SqliteQueryResult, FromRow, Sqlite, SqlitePool, Transaction}; +use sqlx::{FromRow, Sqlite, SqlitePool, Transaction, sqlite::SqliteQueryResult}; use super::user::User; diff --git a/src/model/logbook.rs b/src/model/logbook.rs index 8fd9f8d..e670128 100644 --- a/src/model/logbook.rs +++ b/src/model/logbook.rs @@ -823,7 +823,13 @@ ORDER BY departure DESC 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")); + 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 { diff --git a/src/model/mail.rs b/src/model/mail.rs index bbb6253..35328a9 100644 --- a/src/model/mail.rs +++ b/src/model/mail.rs @@ -1,9 +1,9 @@ use std::{error::Error, fs}; use lettre::{ - message::{header::ContentType, Attachment, MultiPart, SinglePart}, - transport::smtp::authentication::Credentials, Address, Message, SmtpTransport, Transport, + message::{Attachment, MultiPart, SinglePart, header::ContentType}, + transport::smtp::authentication::Credentials, }; use sqlx::{Sqlite, SqlitePool, Transaction}; diff --git a/src/model/personal/cal.rs b/src/model/personal/cal.rs index f0fc1ef..2f5d68d 100644 --- a/src/model/personal/cal.rs +++ b/src/model/personal/cal.rs @@ -1,6 +1,6 @@ use std::io::Write; -use ics::{components::Property, ICalendar}; +use ics::{ICalendar, components::Property}; use sqlx::SqlitePool; use crate::model::{event::Event, trip::Trip, user::User}; diff --git a/src/model/trip.rs b/src/model/trip.rs index e94614a..b654721 100644 --- a/src/model/trip.rs +++ b/src/model/trip.rs @@ -567,9 +567,11 @@ mod test { let last_notification = &Notification::for_user(&pool, &cox).await[0]; - assert!(last_notification - .message - .starts_with("cox2 hat eine Ausfahrt zur selben Zeit")); + assert!( + last_notification + .message + .starts_with("cox2 hat eine Ausfahrt zur selben Zeit") + ); } #[sqlx::test] diff --git a/src/model/user/basic.rs b/src/model/user/basic.rs index 6c25c08..dfcb739 100644 --- a/src/model/user/basic.rs +++ b/src/model/user/basic.rs @@ -20,7 +20,7 @@ impl User { let note = note.trim(); ActivityBuilder::new(&format!("({updated_by}) {note}")) - .relevant_for_user(&user) + .relevant_for_user(user) .save(db) .await; @@ -48,7 +48,9 @@ impl User { let msg = match &self.mail { Some(old_mail) => { - format!("{updated_by} hat die Mail-Adresse von {self} von {old_mail} auf {new_mail} geändert.") + format!( + "{updated_by} hat die Mail-Adresse von {self} von {old_mail} auf {new_mail} geändert." + ) } None => { format!("{updated_by} eine neue Mail-Adresse für {self} hinzugefügt: {new_mail}") @@ -87,9 +89,15 @@ impl User { query.execute(db).await.unwrap(); //Okay, because we can only create a User of a valid id let msg = match &self.phone { - Some(old_phone) if new_phone.is_empty() => format!("{updated_by} hat die Telefonnummer von {self} entfernt (alte Nummer: {old_phone})"), - Some(old_phone) => format!("{updated_by} hat die Telefonnummer von {self} von {old_phone} auf {new_phone} geändert."), - None => format!("{updated_by} hat eine neue Telefonnummer für {self} hinzugefügt: {new_phone}") + Some(old_phone) if new_phone.is_empty() => format!( + "{updated_by} hat die Telefonnummer von {self} entfernt (alte Nummer: {old_phone})" + ), + Some(old_phone) => format!( + "{updated_by} hat die Telefonnummer von {self} von {old_phone} auf {new_phone} geändert." + ), + None => format!( + "{updated_by} hat eine neue Telefonnummer für {self} hinzugefügt: {new_phone}" + ), }; ActivityBuilder::new(&msg) @@ -107,7 +115,7 @@ impl User { let new_address = new_address.trim(); let query = if new_address.is_empty() { - if !self.address.is_none() { + if self.address.is_none() { return; // nothing to do } sqlx::query!("UPDATE user SET address = NULL where id = ?", self.id) @@ -126,9 +134,13 @@ impl User { query.execute(db).await.unwrap(); //Okay, because we can only create a User of a valid id let msg = match &self.address { - Some(old_address) if new_address.is_empty() => format!("{updated_by} hat die Adresse von {self} entfernt (alte Adresse: {old_address})"), - Some(old_address) => format!("{updated_by} hat die Adresse von {self} von {old_address} auf {new_address} geändert."), - None => format!("{updated_by} hat eine Adresse für {self} hinzugefügt: {new_address}") + Some(old_address) if new_address.is_empty() => format!( + "{updated_by} hat die Adresse von {self} entfernt (alte Adresse: {old_address})" + ), + Some(old_address) => format!( + "{updated_by} hat die Adresse von {self} von {old_address} auf {new_address} geändert." + ), + None => format!("{updated_by} hat eine Adresse für {self} hinzugefügt: {new_address}"), }; ActivityBuilder::new(&msg) @@ -157,9 +169,15 @@ impl User { query.execute(db).await.unwrap(); //Okay, because we can only create a User of a valid id let msg = match &self.nickname { - Some(old_nickname) if new_nickname.is_empty() => format!("{updated_by} hat den Sitznamen von {self} entfernt (alter Spitzname: {old_nickname})"), - Some(old_nickname) => format!("{updated_by} hat den Spitznamen von {self} von {old_nickname} auf {new_nickname} geändert."), - None => format!("{updated_by} hat einen neuen Spitznamen für {self} hinzugefügt: {new_nickname}") + Some(old_nickname) if new_nickname.is_empty() => format!( + "{updated_by} hat den Sitznamen von {self} entfernt (alter Spitzname: {old_nickname})" + ), + Some(old_nickname) => format!( + "{updated_by} hat den Spitznamen von {self} von {old_nickname} auf {new_nickname} geändert." + ), + None => format!( + "{updated_by} hat einen neuen Spitznamen für {self} hinzugefügt: {new_nickname}" + ), }; ActivityBuilder::new(&msg) .relevant_for_user(self) @@ -185,8 +203,12 @@ impl User { .unwrap(); //Okay, because we can only create a User of a valid id let msg = match &self.member_since_date { - Some(old_member_since_date) => format!("{updated_by} hat das Beitrittsdatum von {self} von {old_member_since_date} auf {new_member_since_date} geändert."), - None => format!("{updated_by} hat ein neues Beitrittsdatum für {self} hinzugefügt: {new_member_since_date}") + Some(old_member_since_date) => format!( + "{updated_by} hat das Beitrittsdatum von {self} von {old_member_since_date} auf {new_member_since_date} geändert." + ), + None => format!( + "{updated_by} hat ein neues Beitrittsdatum für {self} hinzugefügt: {new_member_since_date}" + ), }; ActivityBuilder::new(&msg) @@ -210,9 +232,13 @@ impl User { .await .unwrap(); //Okay, because we can only create a User of a valid id - let msg = match &self.birthdate{ - Some(old_birthdate) => format!("{updated_by} hat das Geburtsdatum von {self} von {old_birthdate} auf {new_birthdate} geändert."), - None => format!("{updated_by} hat ein Geburtsdatum für {self} hinzugefügt: {new_birthdate}") + let msg = match &self.birthdate { + Some(old_birthdate) => format!( + "{updated_by} hat das Geburtsdatum von {self} von {old_birthdate} auf {new_birthdate} geändert." + ), + None => { + format!("{updated_by} hat ein Geburtsdatum für {self} hinzugefügt: {new_birthdate}") + } }; ActivityBuilder::new(&msg) @@ -272,7 +298,7 @@ impl User { let bootsfuehrer = Role::find_by_name(db, "Bootsführer").await.unwrap(); match (old_skill, skill) { - (old, new) if old == None && new == Some(cox.clone()) => { + (None, new) if new == Some(cox.clone()) => { self.add_role(db, updated_by, &cox).await?; Notification::create_for_role( db, @@ -309,7 +335,7 @@ impl User { .save(db) .await; } - (old, new) if new == None => { + (old, None) => { if let Some(old) = old { self.remove_role(db, updated_by, &old).await?; let vorstand = Role::find_by_name(db, "Vorstand").await.unwrap(); @@ -360,7 +386,7 @@ impl User { ActivityBuilder::new(&format!( "{updated_by} hat die Ermäßigung von {self} von {old} auf {new} geändert" )) - .relevant_for_user(&self) + .relevant_for_user(self) .save(db) .await; @@ -374,7 +400,9 @@ impl User { role: &Role, ) -> Result<(), String> { if !self.has_role(db, &role.name).await { - return Err(format!("Kann Rolle {role} von User {self} nicht entfernen, da der User die Rolle gar nicht hat")); + return Err(format!( + "Kann Rolle {role} von User {self} nicht entfernen, da der User die Rolle gar nicht hat" + )); } sqlx::query!( @@ -389,7 +417,7 @@ impl User { ActivityBuilder::new(&format!( "{updated_by} hat die Rolle {role} von {self} entfernt." )) - .relevant_for_user(&self) + .relevant_for_user(self) .save(db) .await; @@ -415,7 +443,7 @@ impl User { ActivityBuilder::new(&format!( "{updated_by} hat den Bezahlstatus von {self} auf 'nicht bezahlt' gesetzt." )) - .relevant_for_user(&self) + .relevant_for_user(self) .save(db) .await; } @@ -438,7 +466,7 @@ impl User { ActivityBuilder::new(&format!( "{updated_by} hat den Bezahlstatus von {self} auf 'bezahlt' gesetzt." )) - .relevant_for_user(&self) + .relevant_for_user(self) .save(db) .await; } @@ -450,7 +478,9 @@ impl User { role: &Role, ) -> Result<(), String> { if self.has_role(db, &role.name).await { - return Err(format!("Kann Rolle {role} von User {self} nicht hinzufügen, da der User die Rolle schon hat")); + return Err(format!( + "Kann Rolle {role} von User {self} nicht hinzufügen, da der User die Rolle schon hat" + )); } sqlx::query!( @@ -473,7 +503,7 @@ impl User { ActivityBuilder::new(&format!( "{updated_by} hat die Rolle '{role}' dem Benutzer {self} hinzugefügt." )) - .relevant_for_user(&self) + .relevant_for_user(self) .save(db) .await; } @@ -491,7 +521,7 @@ impl User { return Err(format!("User {self} hat bereits eine Beitrittserklärung.")); } if membership_pdf.len() == 0 { - return Err(format!("Keine Beitrittserklärung mitgeschickt.")); + return Err("Keine Beitrittserklärung mitgeschickt.".to_string()); } let mut stream = membership_pdf.open().await.unwrap(); @@ -509,7 +539,7 @@ impl User { ActivityBuilder::new(&format!( "{updated_by} hat die Mitgliedserklärung (PDF) für user {self} hinzugefügt." )) - .relevant_for_user(&self) + .relevant_for_user(self) .save(db) .await; diff --git a/src/model/user/fee.rs b/src/model/user/fee.rs index 4f3febf..55317bd 100644 --- a/src/model/user/fee.rs +++ b/src/model/user/fee.rs @@ -1,7 +1,7 @@ use super::User; use crate::{ - model::family::Family, BOAT_STORAGE, EINSCHREIBGEBUEHR, FAMILY_THREE_OR_MORE, FAMILY_TWO, - FOERDERND, REGULAR, RENNRUDERBEITRAG, STUDENT_OR_PUPIL, UNTERSTUETZEND, + BOAT_STORAGE, EINSCHREIBGEBUEHR, FAMILY_THREE_OR_MORE, FAMILY_TWO, FOERDERND, REGULAR, + RENNRUDERBEITRAG, STUDENT_OR_PUPIL, UNTERSTUETZEND, model::family::Family, }; use chrono::{Datelike, Local, NaiveDate}; use serde::Serialize; diff --git a/src/model/user/foerdernd.rs b/src/model/user/foerdernd.rs index 6c257c5..c51cc7f 100644 --- a/src/model/user/foerdernd.rs +++ b/src/model/user/foerdernd.rs @@ -1,13 +1,16 @@ -use super::User; +use super::{regular::ClubMember, ManageUserUser, User}; use crate::{ - model::{activity::ActivityBuilder, mail::Mail}, - special_user, + model::{activity::ActivityBuilder, mail::Mail, notification::Notification, role::Role}, + special_user, NonEmptyString, }; -use rocket::async_trait; +use chrono::NaiveDate; +use rocket::{async_trait, fs::TempFile}; use sqlx::SqlitePool; special_user!(FoerderndUser, +"Förderndes Mitglied"); +impl ClubMember for FoerderndUser {} + impl FoerderndUser { pub(crate) async fn send_welcome_mail_to_user( &self, @@ -41,10 +44,57 @@ ASKÖ Ruderverein Donau Linz", self.name), ActivityBuilder::new(&format!( "User {self} hat die Info-Mail bzgl. neues förderndes Mitglied (Handbuch und WLAN Infos) an {mail} gesendet bekommen" )) - .relevant_for_user(&self) + .relevant_for_user(self) .save(db) .await; Ok(()) } + + pub(crate) async fn create( + db: &SqlitePool, + created_by: &ManageUserUser, + smtp_pw: &str, + name: NonEmptyString, + mail: &str, + financial: Option, + birthdate: &NaiveDate, + member_since: &NaiveDate, + phone: NonEmptyString, + address: NonEmptyString, + membership_pdf: &TempFile<'_>, + ) -> Result<(), String> { + let role = Role::find_by_name(db, "Förderndes Mitglied").await.unwrap(); + let user = Self::create_member( + db, + created_by, + &role, + name, + mail, + financial, + birthdate, + member_since, + phone, + address, + membership_pdf, + ) + .await?; + + let user = Self::new(db, &user).await.unwrap(); + user.send_welcome_mail_to_user(db, smtp_pw).await?; + + if let Some(vorstand) = Role::find_by_name(db, "Vorstand").await { + Notification::create_for_role( + db, + &vorstand, + &format!("Lieber Vorstand, es gibt ein neues förderndes Mitglied: {user}"), + "Neues unterstützendes Vereinsmitglied", + None, + None, + ) + .await; + } + + Ok(()) + } } diff --git a/src/model/user/member.rs b/src/model/user/member.rs index 9161ac6..9762fda 100644 --- a/src/model/user/member.rs +++ b/src/model/user/member.rs @@ -36,19 +36,19 @@ impl Member { } pub(crate) fn is_club_member(&self) -> bool { - match self { - Member::Regular(_) | Member::Foerdernd(_) | Member::Unterstuetzend(_) => true, - _ => false, - } + matches!( + self, + Member::Regular(_) | Member::Foerdernd(_) | Member::Unterstuetzend(_) + ) } pub(crate) fn supposed_to_pay(&self) -> bool { - match self { + matches!( + self, Member::Schnupperant(_) - | Member::Scheckbuch(_) - | Member::Regular(_) - | Member::Foerdernd(_) - | Member::Unterstuetzend(_) => true, - _ => false, - } + | Member::Scheckbuch(_) + | Member::Regular(_) + | Member::Foerdernd(_) + | Member::Unterstuetzend(_) + ) } } diff --git a/src/model/user/mod.rs b/src/model/user/mod.rs index 8d5f54d..0539e08 100644 --- a/src/model/user/mod.rs +++ b/src/model/user/mod.rs @@ -458,7 +458,7 @@ ASKÖ Ruderverein Donau Linz", self.name), smtp_pw, ).await?; - ActivityBuilder::new(&format!("User {self} hat eine Mail bekommen, dass seine 5 Ausfahrten mit der heutigen Ausfahrt aufgebraucht sind, und dass der nächste Schritt eine Vereinsmitgliedschaft wäre (inkl. Links zu Beitrittserklärung + Info, dass sie an info@ geschickt werden soll.")).relevant_for_user(&self).save_tx(db).await; + ActivityBuilder::new(&format!("User {self} hat eine Mail bekommen, dass seine 5 Ausfahrten mit der heutigen Ausfahrt aufgebraucht sind, und dass der nächste Schritt eine Vereinsmitgliedschaft wäre (inkl. Links zu Beitrittserklärung + Info, dass sie an info@ geschickt werden soll.")).relevant_for_user(self).save_tx(db).await; Ok(()) } @@ -541,7 +541,7 @@ ASKÖ Ruderverein Donau Linz", self.name), // TODO: add responsible person ActivityBuilder::new(&format!("Passwort von User {self} wurde zurückgesetzt.")) - .relevant_for_user(&self) + .relevant_for_user(self) .save(db) .await; } @@ -555,7 +555,7 @@ ASKÖ Ruderverein Donau Linz", self.name), ActivityBuilder::new(&format!( "Passwort von User {self} wurde erfolgreich geändert." )) - .relevant_for_user(&self) + .relevant_for_user(self) .save(db) .await; } @@ -578,7 +578,7 @@ ASKÖ Ruderverein Donau Linz", self.name), .await .unwrap(); //Okay, because we can only create a User of a valid id ActivityBuilder::new(&format!("User {self} hat sich eingeloggt.")) - .relevant_for_user(&self) + .relevant_for_user(self) .save(db) .await; } @@ -589,7 +589,7 @@ ASKÖ Ruderverein Donau Linz", self.name), .await .unwrap(); //Okay, because we can only create a User of a valid id ActivityBuilder::new(&format!("User {self} wurde gelöscht.")) - .relevant_for_user(&self) + .relevant_for_user(self) .save(db) .await; } @@ -684,7 +684,7 @@ ASKÖ Ruderverein Donau Linz", self.name), ) .await; ActivityBuilder::new(&format!("5 Scheckbuchausfahrten von {self} wurden mit der heutigen Ausfahrt aufgebraucht. Info-Mail wurde an {self} geschickt + alle Steuerberechtigten informiert, dass wir pot. ein neues Mitglied haben")) - .relevant_for_user(&self) + .relevant_for_user(self) .save_tx(db) .await; } @@ -702,7 +702,7 @@ ASKÖ Ruderverein Donau Linz", self.name), ) .await; ActivityBuilder::new(&format!("{self} hat nun bereits die {amount_trips}. seiner 5 Scheckbuchausfahrten absolviert. Vorstand wurde via Notification informiert.")) - .relevant_for_user(&self) + .relevant_for_user(self) .save_tx(db) .await; } @@ -727,7 +727,7 @@ ASKÖ Ruderverein Donau Linz", self.name), ActivityBuilder::new(&format!( "{self} hat das heurige Fahrtenabzeichen geschafft! Der Vorstand + {self} wurde via Notification informiert." )) - .relevant_for_user(&self) + .relevant_for_user(self) .save_tx(db) .await; @@ -749,7 +749,7 @@ ASKÖ Ruderverein Donau Linz", self.name), ) .await; ActivityBuilder::new(&format!("{self} hat den Äquatorpreis in {level} geschafft! Der Vorstand + {self} wurde via Notification informiert.")) - .relevant_for_user(&self) + .relevant_for_user(self) .save_tx(db) .await; @@ -812,7 +812,7 @@ macro_rules! special_user { #[async_trait] impl<'r> rocket::request::FromRequest<'r> for $name { - type Error = crate::model::user::LoginError; + type Error = $crate::model::user::LoginError; async fn from_request(req: &'r rocket::request::Request<'_>) -> rocket::request::Outcome { let db = req.rocket().state::().unwrap(); match User::from_request(req).await { diff --git a/src/model/user/regular.rs b/src/model/user/regular.rs index 2611434..a49b377 100644 --- a/src/model/user/regular.rs +++ b/src/model/user/regular.rs @@ -1,13 +1,66 @@ -use super::User; +use super::{ManageUserUser, User}; use crate::{ - model::{activity::ActivityBuilder, mail::Mail}, - special_user, + model::{activity::ActivityBuilder, mail::Mail, notification::Notification, role::Role}, + special_user, NonEmptyString, }; -use rocket::async_trait; +use chrono::NaiveDate; +use rocket::{async_trait, fs::TempFile, tokio::io::AsyncReadExt}; use sqlx::SqlitePool; special_user!(RegularUser, +"Donau Linz"); +pub trait ClubMember { + async fn create_member( + db: &SqlitePool, + created_by: &ManageUserUser, + role: &Role, + name: NonEmptyString, + mail: &str, + financial: Option, + birthdate: &NaiveDate, + member_since: &NaiveDate, + phone: NonEmptyString, + address: NonEmptyString, + membership_pdf: &TempFile<'_>, + ) -> Result { + if membership_pdf.len() == 0 { + return Err("Keine Beitrittserklärung mitgeschickt.".to_string()); + } + + let mut stream = membership_pdf.open().await.unwrap(); + let mut buffer = Vec::new(); + stream.read_to_end(&mut buffer).await.unwrap(); + + let name = name.as_str(); + let phone = phone.as_str(); + let address = address.as_str(); + + sqlx::query!( + "INSERT INTO user(name, member_since_date, birthdate, mail, phone, address, membership_pdf) + VALUES (?,?,?,?,?,?,?)", + name, member_since, birthdate, mail, phone, address,buffer + ) + .execute(db) + .await + .map_err(|e| e.to_string())?; + + 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?; + + ActivityBuilder::new(&format!( + "{created_by} hat Mitglied {user} mit der Rolle {role} angelegt." + )) + .relevant_for_user(&user) + .save(db) + .await; + + Ok(user) + } +} + +impl ClubMember for RegularUser {} + impl RegularUser { pub(crate) async fn send_welcome_mail_to_user( &self, @@ -47,10 +100,54 @@ ASKÖ Ruderverein Donau Linz", self.name), ).await?; ActivityBuilder::new(&format!("Willkommensmail für {self} wurde an {mail} verschickt (Handbuch, Signal-Gruppe, App-Info, Fingerprint, WLAN).")) - .relevant_for_user(&self) + .relevant_for_user(self) .save(db) .await; Ok(()) } + + pub(crate) async fn create( + db: &SqlitePool, + created_by: &ManageUserUser, + smtp_pw: &str, + name: NonEmptyString, + mail: &str, + financial: Option, + birthdate: &NaiveDate, + member_since: &NaiveDate, + phone: NonEmptyString, + address: NonEmptyString, + membership_pdf: &TempFile<'_>, + ) -> Result<(), String> { + let role = Role::find_by_name(db, "Donau Linz").await.unwrap(); + let user = Self::create_member( + db, + created_by, + &role, + name, + mail, + financial, + birthdate, + member_since, + phone, + address, + membership_pdf, + ) + .await?; + + let user = Self::new(db, &user).await.unwrap(); + user.send_welcome_mail_to_user(db, smtp_pw).await?; + + Notification::create_for_steering_people( + db, + &format!("Liebe Steuerberechtigte, es gibt ein neues Mitglied: {user} 🎉"), + "Neues Vereinsmitglied", + None, + None, + ) + .await; + + Ok(()) + } } diff --git a/src/model/user/scheckbuch.rs b/src/model/user/scheckbuch.rs index 5006383..4a58f04 100644 --- a/src/model/user/scheckbuch.rs +++ b/src/model/user/scheckbuch.rs @@ -85,7 +85,7 @@ impl ScheckbuchUser { .await; ActivityBuilder::new(&format!( - "{changed_by} hat den Scheckbuch-User {self} auf ein reguläres Mitglied upgegraded!" + "{changed_by} hat den Scheckbuch-User {self} auf ein reguläres Mitglied upgegraded! Die Steuerpersonen wurden via Notification informiert." )) .relevant_for_user(&self) .save(db) @@ -214,7 +214,7 @@ impl ScheckbuchUser { ActivityBuilder::new(&format!( "{self} hat eine Info-Mail bekommen (Erklärung Scheckbuch, Ruderapp) und alle Steuerberechtigten wurden informiert." )) - .relevant_for_user(&self) + .relevant_for_user(self) .save(db) .await; @@ -233,7 +233,7 @@ impl ScheckbuchUser { }; Mail::send_single( db, - &mail, + mail, "ASKÖ Ruderverein Donau Linz | Dein Scheckbuch wartet auf Dich", format!( "Hallo {0}, @@ -266,4 +266,38 @@ ASKÖ Ruderverein Donau Linz", self.name, SCHECKBUCH/100), ) .await; } + + pub(crate) async fn create( + db: &SqlitePool, + created_by: &ManageUserUser, + smtp_pw: &str, + name: NonEmptyString, + mail: &str, + ) -> Result<(), String> { + let role = Role::find_by_name(db, "scheckbuch").await.unwrap(); + + let name = name.as_str(); + sqlx::query!( + "INSERT INTO user(name, mail) + VALUES (?,?)", + name, + mail + ) + .execute(db) + .await + .map_err(|e| e.to_string())?; + + let user = User::find_by_name(db, name).await.unwrap(); + user.add_role(db, created_by, &role).await?; + + let user = Self::new(db, &user).await.unwrap(); + user.notify(db, smtp_pw).await?; + + ActivityBuilder::new(&format!("{created_by} hat Scheckbuch {user} angelegt.")) + .relevant_for_user(&user) + .save(db) + .await; + + Ok(()) + } } diff --git a/src/model/user/schnupperant.rs b/src/model/user/schnupperant.rs index f486a66..5e00bed 100644 --- a/src/model/user/schnupperant.rs +++ b/src/model/user/schnupperant.rs @@ -108,7 +108,7 @@ impl SchnupperantUser { self.user.add_role(db, changed_by, &scheckbook).await?; if let Some(no_einschreibgebuehr) = Role::find_by_name(db, "no-einschreibgebuehr").await { - self.add_role(db, &changed_by, &no_einschreibgebuehr) + self.add_role(db, changed_by, &no_einschreibgebuehr) .await .expect("role doesn't have a group"); } @@ -208,7 +208,7 @@ impl SchnupperantUser { self.user.remove_role(db, changed_by, &scheckbook).await?; self.user.add_role(db, changed_by, &unterstuetzend).await?; if let Some(no_einschreibgebuehr) = Role::find_by_name(db, "no-einschreibgebuehr").await { - self.add_role(db, &changed_by, &no_einschreibgebuehr) + self.add_role(db, changed_by, &no_einschreibgebuehr) .await .expect("role doesn't have a group"); } @@ -272,7 +272,7 @@ impl SchnupperantUser { self.user.remove_role(db, changed_by, &scheckbook).await?; self.user.add_role(db, changed_by, &unterstuetzend).await?; if let Some(no_einschreibgebuehr) = Role::find_by_name(db, "no-einschreibgebuehr").await { - self.add_role(db, &changed_by, &no_einschreibgebuehr) + self.add_role(db, changed_by, &no_einschreibgebuehr) .await .expect("role doesn't have a group"); } @@ -313,7 +313,7 @@ impl SchnupperantUser { ActivityBuilder::new(&format!( "{self} hat eine Mail bekommen (Inhalt: wir freuen uns auf ihn + senden detailliertere Infos später zu) und die Schnupperbetreuer wurden via Notification informiert." )) - .relevant_for_user(&self) + .relevant_for_user(self) .save(db) .await; @@ -332,7 +332,7 @@ impl SchnupperantUser { }; Mail::send_single( db, - &mail, + mail, "ASKÖ Ruderverein Donau Linz | Anmeldung Schnupperkurs", format!( "Hallo {0}, @@ -363,4 +363,40 @@ ASKÖ Ruderverein Donau Linz", self.name), .await; } } + + pub(crate) async fn create( + db: &SqlitePool, + created_by: &ManageUserUser, + smtp_pw: &str, + name: NonEmptyString, + mail: &str, + ) -> Result<(), String> { + let role = Role::find_by_name(db, "schnupperant").await.unwrap(); + + let name = name.as_str(); + sqlx::query!( + "INSERT INTO user(name, mail) + VALUES (?,?)", + name, + mail + ) + .execute(db) + .await + .map_err(|e| e.to_string())?; + + let user = User::find_by_name(db, name).await.unwrap(); + user.add_role(db, created_by, &role).await?; + + let user = Self::new(db, &user).await.unwrap(); + user.notify(db, smtp_pw).await?; + + ActivityBuilder::new(&format!( + "{created_by} hat {user} zur fixen Schnupperkurs-Anmeldung hinzugefügt." + )) + .relevant_for_user(&user) + .save(db) + .await; + + Ok(()) + } } diff --git a/src/model/user/schnupperinterest.rs b/src/model/user/schnupperinterest.rs index cfafe91..b6aed52 100644 --- a/src/model/user/schnupperinterest.rs +++ b/src/model/user/schnupperinterest.rs @@ -3,6 +3,7 @@ use super::schnupperant::SchnupperantUser; use super::{ManageUserUser, User}; use crate::model::activity::ActivityBuilder; use crate::model::role::Role; +use crate::NonEmptyString; use crate::{model::notification::Notification, special_user}; use rocket::async_trait; use sqlx::SqlitePool; @@ -98,7 +99,7 @@ impl SchnupperInterestUser { ActivityBuilder::new(&format!( "Der Schnupperbetreuer hat eine Info via Notification bekommen, dass {self} Interesse an einen Schnupperkurs hat." )) - .relevant_for_user(&self) + .relevant_for_user(self) .save(db) .await; @@ -121,4 +122,41 @@ impl SchnupperInterestUser { .await; } } + + pub(crate) async fn create( + db: &SqlitePool, + created_by: &ManageUserUser, + name: NonEmptyString, + mail: &str, + ) -> Result<(), String> { + let role = Role::find_by_name(db, "schnupper-interessierte") + .await + .unwrap(); + + let name = name.as_str(); + sqlx::query!( + "INSERT INTO user(name, mail) + VALUES (?,?)", + name, + mail + ) + .execute(db) + .await + .map_err(|e| e.to_string())?; + + let user = User::find_by_name(db, name).await.unwrap(); + user.add_role(db, created_by, &role).await?; + + let user = Self::new(db, &user).await.unwrap(); + user.notify(db).await?; + + ActivityBuilder::new(&format!( + "{created_by} hat Schnupper-Interessierten {user} angelegt." + )) + .relevant_for_user(&user) + .save(db) + .await; + + Ok(()) + } } diff --git a/src/model/user/unterstuetzend.rs b/src/model/user/unterstuetzend.rs index 552d9aa..716b207 100644 --- a/src/model/user/unterstuetzend.rs +++ b/src/model/user/unterstuetzend.rs @@ -1,13 +1,16 @@ -use super::User; +use super::{regular::ClubMember, ManageUserUser, User}; use crate::{ - model::{activity::ActivityBuilder, mail::Mail}, - special_user, + model::{activity::ActivityBuilder, mail::Mail, notification::Notification, role::Role}, + special_user, NonEmptyString, }; -use rocket::async_trait; +use chrono::NaiveDate; +use rocket::{async_trait, fs::TempFile}; use sqlx::SqlitePool; special_user!(UnterstuetzendUser, +"Unterstützend"); +impl ClubMember for UnterstuetzendUser {} + impl UnterstuetzendUser { pub(crate) async fn send_welcome_mail_to_user( &self, @@ -41,10 +44,57 @@ ASKÖ Ruderverein Donau Linz", self.name), ActivityBuilder::new(&format!( "{self} hat eine Mail an {mail} bekommen, mit Infos dass er/sie nun ein unterstützendes Mitglied ist (Handbuch, WLAN)." )) - .relevant_for_user(&self) + .relevant_for_user(self) .save(db) .await; Ok(()) } + + pub(crate) async fn create( + db: &SqlitePool, + created_by: &ManageUserUser, + smtp_pw: &str, + name: NonEmptyString, + mail: &str, + financial: Option, + birthdate: &NaiveDate, + member_since: &NaiveDate, + phone: NonEmptyString, + address: NonEmptyString, + membership_pdf: &TempFile<'_>, + ) -> Result<(), String> { + let role = Role::find_by_name(db, "Unterstützend").await.unwrap(); + let user = Self::create_member( + db, + created_by, + &role, + name, + mail, + financial, + birthdate, + member_since, + phone, + address, + membership_pdf, + ) + .await?; + + let user = Self::new(db, &user).await.unwrap(); + user.send_welcome_mail_to_user(db, smtp_pw).await?; + + if let Some(vorstand) = Role::find_by_name(db, "Vorstand").await { + Notification::create_for_role( + db, + &vorstand, + &format!("Lieber Vorstand, es gibt ein neues unterstützendes Mitglied: {user}"), + "Neues unterstützendes Vereinsmitglied", + None, + None, + ) + .await; + } + + Ok(()) + } } diff --git a/src/rest/mod.rs b/src/rest/mod.rs index bf08819..113d560 100644 --- a/src/rest/mod.rs +++ b/src/rest/mod.rs @@ -1,4 +1,4 @@ -use rocket::{form::Form, post, routes, Build, FromForm, Rocket, State}; +use rocket::{Build, FromForm, Rocket, State, form::Form, post, routes}; use serde_json::json; use sqlx::SqlitePool; diff --git a/src/scheduled/weather.rs b/src/scheduled/weather.rs index 990764e..3da3f20 100644 --- a/src/scheduled/weather.rs +++ b/src/scheduled/weather.rs @@ -96,7 +96,9 @@ struct DailyWeather { } fn fetch(api_key: &str) -> Result { - let url = format!("https://api.openweathermap.org/data/3.0/onecall?lat=48.31970&lon=14.29451&units=metric&exclude=current,minutely,hourly,alert&appid={api_key}"); + let url = format!( + "https://api.openweathermap.org/data/3.0/onecall?lat=48.31970&lon=14.29451&units=metric&exclude=current,minutely,hourly,alert&appid={api_key}" + ); match ureq::get(&url).call() { Ok(mut response) => { diff --git a/src/tera/admin/boat.rs b/src/tera/admin/boat.rs index 56bf5fd..8defa13 100644 --- a/src/tera/admin/boat.rs +++ b/src/tera/admin/boat.rs @@ -5,13 +5,14 @@ use crate::model::{ user::{User, UserWithDetails, VorstandUser}, }; use rocket::{ + Route, State, form::Form, get, post, request::FlashMessage, response::{Flash, Redirect}, - routes, Route, State, + routes, }; -use rocket_dyn_templates::{tera::Context, Template}; +use rocket_dyn_templates::{Template, tera::Context}; use sqlx::SqlitePool; #[get("/boat")] @@ -245,9 +246,11 @@ mod test { let rocket = rocket::build().manage(db.clone()); let rocket = crate::tera::config(rocket); - assert!(Boat::find_by_name(&db, "completely-new-boat".into()) - .await - .is_none()); + assert!( + Boat::find_by_name(&db, "completely-new-boat".into()) + .await + .is_none() + ); let client = Client::tracked(rocket).await.unwrap(); let login = client diff --git a/src/tera/admin/event.rs b/src/tera/admin/event.rs index 2bccdcf..26e7112 100644 --- a/src/tera/admin/event.rs +++ b/src/tera/admin/event.rs @@ -1,8 +1,9 @@ use rocket::{ + FromForm, Route, State, form::Form, get, post, put, response::{Flash, Redirect}, - routes, FromForm, Route, State, + routes, }; use serde::Serialize; use sqlx::SqlitePool; @@ -32,8 +33,8 @@ async fn create( let trip_details_id = TripDetails::create(db, data.tripdetails).await; let trip_details = TripDetails::find_by_id(db, trip_details_id).await.unwrap(); //Okay, bc. we - //just created - //the object + //just created + //the object Event::create( db, diff --git a/src/tera/admin/mail.rs b/src/tera/admin/mail.rs index 953bf23..e886a2d 100644 --- a/src/tera/admin/mail.rs +++ b/src/tera/admin/mail.rs @@ -1,9 +1,9 @@ use rocket::form::Form; use rocket::fs::TempFile; use rocket::response::{Flash, Redirect}; -use rocket::{get, request::FlashMessage, routes, Route, State}; -use rocket::{post, FromForm}; -use rocket_dyn_templates::{tera::Context, Template}; +use rocket::{FromForm, post}; +use rocket::{Route, State, get, request::FlashMessage, routes}; +use rocket_dyn_templates::{Template, tera::Context}; use sqlx::SqlitePool; use crate::model::log::Log; diff --git a/src/tera/admin/mod.rs b/src/tera/admin/mod.rs index 1690f45..2c68b4a 100644 --- a/src/tera/admin/mod.rs +++ b/src/tera/admin/mod.rs @@ -1,6 +1,6 @@ use csv::ReaderBuilder; -use rocket::{form::Form, get, post, routes, FromForm, Route, State}; -use rocket_dyn_templates::{context, Template}; +use rocket::{FromForm, Route, State, form::Form, get, post, routes}; +use rocket_dyn_templates::{Template, context}; use sqlx::SqlitePool; use crate::{ diff --git a/src/tera/admin/notification.rs b/src/tera/admin/notification.rs index fe6578b..098e171 100644 --- a/src/tera/admin/notification.rs +++ b/src/tera/admin/notification.rs @@ -6,13 +6,14 @@ use crate::model::{ }; use itertools::Itertools; use rocket::{ + FromForm, Route, State, form::Form, get, post, request::FlashMessage, response::{Flash, Redirect}, - routes, FromForm, Route, State, + routes, }; -use rocket_dyn_templates::{tera::Context, Template}; +use rocket_dyn_templates::{Template, tera::Context}; use sqlx::SqlitePool; #[get("/notification")] diff --git a/src/tera/admin/schnupper.rs b/src/tera/admin/schnupper.rs index 9a751e1..a53c155 100644 --- a/src/tera/admin/schnupper.rs +++ b/src/tera/admin/schnupper.rs @@ -3,8 +3,8 @@ use crate::model::{ user::{SchnupperBetreuerUser, User, UserWithDetails}, }; use futures::future::join_all; -use rocket::{get, request::FlashMessage, routes, Route, State}; -use rocket_dyn_templates::{tera::Context, Template}; +use rocket::{Route, State, get, request::FlashMessage, routes}; +use rocket_dyn_templates::{Template, tera::Context}; use sqlx::SqlitePool; #[get("/schnupper")] diff --git a/src/tera/admin/user.rs b/src/tera/admin/user.rs index d093b59..7978cf9 100644 --- a/src/tera/admin/user.rs +++ b/src/tera/admin/user.rs @@ -4,11 +4,13 @@ use crate::{ family::Family, log::Log, logbook::Logbook, + mail::valid_mails, role::Role, user::{ - clubmember::ClubMemberUser, member::Member, scheckbuch::ScheckbuchUser, - schnupperant::SchnupperantUser, schnupperinterest::SchnupperInterestUser, AdminUser, - AllowedToEditPaymentStatusUser, ManageUserUser, User, UserWithDetails, + clubmember::ClubMemberUser, foerdernd::FoerderndUser, member::Member, + regular::RegularUser, scheckbuch::ScheckbuchUser, schnupperant::SchnupperantUser, + schnupperinterest::SchnupperInterestUser, unterstuetzend::UnterstuetzendUser, + AdminUser, AllowedToEditPaymentStatusUser, ManageUserUser, User, UserWithDetails, UserWithMembershipPdf, UserWithRolesAndMembershipPdf, VorstandUser, }, }, @@ -64,6 +66,7 @@ async fn index( let allowed_to_edit = ManageUserUser::new(db, &user).await.is_some(); let users: Vec = join_all(user_futures).await; + let financial = Role::all_cluster(db, "financial").await; let roles = Role::all(db).await; let families = Family::all_with_members(db).await; @@ -75,6 +78,7 @@ async fn index( context.insert("allowed_to_edit", &allowed_to_edit); context.insert("users", &users); context.insert("roles", &roles); + context.insert("financial", &financial); context.insert("families", &families); context.insert("loggedin_user", &UserWithDetails::from_user(user, db).await); @@ -95,6 +99,7 @@ async fn index_admin( let users: Vec = join_all(user_futures).await; let user: User = user.user; + let financial = Role::all_cluster(db, "financial").await; let allowed_to_edit = ManageUserUser::new(db, &user).await.is_some(); let roles = Role::all(db).await; @@ -107,6 +112,7 @@ async fn index_admin( context.insert("allowed_to_edit", &allowed_to_edit); context.insert("users", &users); context.insert("roles", &roles); + context.insert("financial", &financial); context.insert("families", &families); context.insert("loggedin_user", &UserWithDetails::from_user(user, db).await); @@ -463,13 +469,13 @@ async fn change_skill( ); }; - let skill = if &data.skill_id == "" { + let skill = if data.skill_id.is_empty() { None } else { let Ok(skill_id) = data.skill_id.parse() else { return Flash::error( Redirect::to(format!("/admin/user/{id}")), - format!("Skill_id is not a number"), + "Skill_id is not a number", ); }; Role::find_by_id(db, skill_id).await @@ -503,13 +509,13 @@ async fn change_financial( ); }; - let financial = if &data.financial_id == "" { + let financial = if data.financial_id.is_empty() { None } else { let Ok(financial_id) = data.financial_id.parse() else { return Flash::error( Redirect::to(format!("/admin/user/{id}")), - format!("Finacial_id is not a number"), + "Finacial_id is not a number", ); }; Role::find_by_id(db, financial_id).await @@ -899,7 +905,7 @@ async fn schnupperant_to_regular( return Flash::error( Redirect::to(format!("/admin/user/{id}")), "Membertype gibts ned", - ) + ); } }; @@ -1018,7 +1024,7 @@ async fn scheckbook_to_regular( return Flash::error( Redirect::to(format!("/admin/user/{id}")), "Membertype gibts ned", - ) + ); } }; @@ -1050,7 +1056,7 @@ async fn change_membertype( ); }; - let Some(user) = ClubMemberUser::new(&db, &user).await else { + let Some(user) = ClubMemberUser::new(db, &user).await else { return Flash::error( Redirect::to("/admin/user"), format!("User {user} ist kein Vereinsmitglied"), @@ -1063,9 +1069,9 @@ async fn change_membertype( "foerdernd" => user.move_to_foerdernd(db, &admin).await, _ => { return Flash::error( - Redirect::to(format!("/admin/user/{{ id }}")), - format!("Membertype gibt's ned"), - ) + Redirect::to(format!("/admin/user/{id}")), + "Membertype gibt's ned", + ); } }; @@ -1092,7 +1098,7 @@ async fn schnupperant_to_scheckbook( ); }; - let Some(user) = SchnupperantUser::new(&db, &user).await else { + let Some(user) = SchnupperantUser::new(db, &user).await else { return Flash::error( Redirect::to(format!("/admin/user/{id}")), format!("User {user} ist kein Schnupperant"), @@ -1122,7 +1128,7 @@ async fn schnupperinterest_to_schnupperant( ); }; - let Some(user) = SchnupperInterestUser::new(&db, &user).await else { + let Some(user) = SchnupperInterestUser::new(db, &user).await else { return Flash::error( Redirect::to(format!("/admin/user/{id}")), format!("User {user} ist kein Schnupperinteressierter"), @@ -1151,7 +1157,7 @@ async fn schnupperant_to_schnupperinterest( ); }; - let Some(user) = SchnupperantUser::new(&db, &user).await else { + let Some(user) = SchnupperantUser::new(db, &user).await else { return Flash::error( Redirect::to(format!("/admin/user/{id}")), format!("User {user} ist kein Schnupperant"), @@ -1180,7 +1186,7 @@ async fn schnupperinterest_to_scheckbuch( ); }; - let Some(user) = SchnupperInterestUser::new(&db, &user).await else { + let Some(user) = SchnupperInterestUser::new(db, &user).await else { return Flash::error( Redirect::to(format!("/admin/user/{id}")), format!("User {user} ist kein Schnupperinteressierter"), @@ -1196,19 +1202,236 @@ async fn schnupperinterest_to_scheckbuch( } } +#[derive(FromForm, Debug)] +pub struct AddClubMemberForm<'a> { + name: String, + mail: String, + financial_id: String, + membertype: String, + member_since: String, + birthdate: String, + phone: String, + address: String, + membership_pdf: TempFile<'a>, +} + +#[post("/user/new/clubmember", data = "")] +async fn add_club_member( + db: &State, + data: Form>, + admin: ManageUserUser, + config: &State, +) -> Flash { + if !valid_mails(&data.mail) { + return Flash::error( + Redirect::to("/admin/user"), + format!( + "{} ist kein gültiges Format für eine Mailadresse", + &data.mail + ), + ); + } + + let financial = if data.financial_id.is_empty() { + None + } else { + let Ok(financial_id) = data.financial_id.parse() else { + return Flash::error(Redirect::to("/admin/user"), "Finacial_id is not a number"); + }; + Role::find_by_id(db, financial_id).await + }; + + let Ok(birthdate) = NaiveDate::parse_from_str(&data.birthdate, "%Y-%m-%d") else { + return Flash::error( + Redirect::to("/admin/user/"), + format!( + "Geburtsdatum {} ist nicht im YYYY-MM-DD Format", + &data.birthdate + ), + ); + }; + let Ok(member_since) = NaiveDate::parse_from_str(&data.member_since, "%Y-%m-%d") else { + return Flash::error( + Redirect::to("/admin/user"), + format!( + "Beitrittsdatum {} ist nicht im YYYY-MM-DD Format", + &data.birthdate + ), + ); + }; + + let Ok(phone) = data.phone.clone().try_into() else { + return Flash::error( + Redirect::to("/admin/user"), + "Vereinsmitglied braucht eine Telefonnummer", + ); + }; + let Ok(address) = data.address.clone().try_into() else { + return Flash::error( + Redirect::to("/admin/user"), + "Vereinsmitglied braucht eine Adresse", + ); + }; + let Ok(name) = data.name.clone().try_into() else { + return Flash::error( + Redirect::to("/admin/user"), + "Vereinsmitglied braucht einen Namen", + ); + }; + let response = match &*data.membertype { + "regular" => { + RegularUser::create( + db, + &admin, + &config.smtp_pw, + name, + &data.mail, + financial, + &birthdate, + &member_since, + phone, + address, + &data.membership_pdf, + ) + .await + } + "unterstuetzend" => { + UnterstuetzendUser::create( + db, + &admin, + &config.smtp_pw, + name, + &data.mail, + financial, + &birthdate, + &member_since, + phone, + address, + &data.membership_pdf, + ) + .await + } + "foerdernd" => { + FoerderndUser::create( + db, + &admin, + &config.smtp_pw, + name, + &data.mail, + financial, + &birthdate, + &member_since, + phone, + address, + &data.membership_pdf, + ) + .await + } + _ => return Flash::error(Redirect::to("/admin/user"), "Membertype gibts ned"), + }; + + match response { + Ok(_) => Flash::success(Redirect::to("/admin/user"), "Mitglied erfolgreich erstellt"), + Err(e) => Flash::error(Redirect::to("/admin/user"), e), + } +} + +#[derive(FromForm, Debug)] +pub struct AddScheckbuchForm { + name: String, + mail: String, +} + +#[post("/user/new/scheckbuch", data = "")] +async fn add_scheckbuch( + db: &State, + data: Form, + admin: ManageUserUser, + config: &State, +) -> Flash { + if !valid_mails(&data.mail) { + return Flash::error( + Redirect::to("/admin/user"), + format!( + "{} ist kein gültiges Format für eine Mailadresse", + &data.mail + ), + ); + } + + let Ok(name) = data.name.clone().try_into() else { + return Flash::error( + Redirect::to("/admin/user"), + "Scheckbuch braucht einen Namen", + ); + }; + match ScheckbuchUser::create(db, &admin, &config.smtp_pw, name, &data.mail).await { + Ok(_) => Flash::success( + Redirect::to("/admin/user"), + "Scheckbuch erfolgreich erstellt", + ), + Err(e) => Flash::error(Redirect::to("/admin/user"), e), + } +} + +#[derive(FromForm, Debug)] +pub struct AddSchnupperForm { + name: String, + mail: String, + schnupper_type: String, +} + +#[post("/user/new/schnupper", data = "")] +async fn add_schnupper( + db: &State, + data: Form, + admin: ManageUserUser, + config: &State, +) -> Flash { + if !valid_mails(&data.mail) { + return Flash::error( + Redirect::to("/admin/user"), + format!( + "{} ist kein gültiges Format für eine Mailadresse", + &data.mail + ), + ); + } + + let Ok(name) = data.name.clone().try_into() else { + return Flash::error( + Redirect::to("/admin/user"), + "Schnupperer braucht einen Namen", + ); + }; + let response = match &*data.schnupper_type { + "schnupperInterested" => SchnupperInterestUser::create(db, &admin, name, &data.mail).await, + "schnupperant" => { + SchnupperantUser::create(db, &admin, &config.smtp_pw, name, &data.mail).await + } + _ => return Flash::error(Redirect::to("/admin/user"), "Schnuppertyp gibts ned"), + }; + match response { + Ok(_) => Flash::success( + Redirect::to("/admin/user"), + "Schnupperer erfolgreich erstellt", + ), + Err(e) => Flash::error(Redirect::to("/admin/user"), e), + } +} + pub fn routes() -> Vec { routes![ index, index_admin, view, resetpw, - //create_scheckbuch, delete, fees, fees_paid, scheckbuch, download_membership_pdf, - // + // Updates update_mail, update_phone, update_nickname, @@ -1222,7 +1445,7 @@ pub fn routes() -> Vec { add_role, add_note, remove_role, - // + // Moves scheckbook_to_regular, schnupperant_to_regular, schnupperant_to_scheckbook, @@ -1230,5 +1453,9 @@ pub fn routes() -> Vec { schnupperant_to_schnupperinterest, schnupperinterest_to_scheckbuch, change_membertype, + // Add + add_club_member, + add_scheckbuch, + add_schnupper, ] } diff --git a/src/tera/auth.rs b/src/tera/auth.rs index 511c6b9..d9f3317 100644 --- a/src/tera/auth.rs +++ b/src/tera/auth.rs @@ -1,4 +1,5 @@ use rocket::{ + FromForm, Request, Route, State, form::Form, get, http::{Cookie, CookieJar}, @@ -8,9 +9,8 @@ use rocket::{ response::{Flash, Redirect}, routes, time::{Duration, OffsetDateTime}, - FromForm, Request, Route, State, }; -use rocket_dyn_templates::{context, tera, Template}; +use rocket_dyn_templates::{Template, context, tera}; use sqlx::SqlitePool; use crate::model::{ @@ -73,7 +73,10 @@ async fn login( ); } Err(_) => { - return Flash::error(Redirect::to("/auth"), "Falscher Benutzername/Passwort. Du bist Vereinsmitglied und der Login klappt nicht? Kontaktiere unseren Schriftführer oder schreibe eine Mail an info@rudernlinz.at!"); + return Flash::error( + Redirect::to("/auth"), + "Falscher Benutzername/Passwort. Du bist Vereinsmitglied und der Login klappt nicht? Kontaktiere unseren Schriftführer oder schreibe eine Mail an info@rudernlinz.at!", + ); } }; diff --git a/src/tera/board/achievement.rs b/src/tera/board/achievement.rs index 750ab25..9aa3f83 100644 --- a/src/tera/board/achievement.rs +++ b/src/tera/board/achievement.rs @@ -3,8 +3,8 @@ use crate::model::{ role::Role, user::{User, UserWithDetails, VorstandUser}, }; -use rocket::{get, request::FlashMessage, routes, Route, State}; -use rocket_dyn_templates::{tera::Context, Template}; +use rocket::{Route, State, get, request::FlashMessage, routes}; +use rocket_dyn_templates::{Template, tera::Context}; use sqlx::SqlitePool; #[get("/achievement")] diff --git a/src/tera/board/boathouse.rs b/src/tera/board/boathouse.rs index 1c10408..7d31f95 100644 --- a/src/tera/board/boathouse.rs +++ b/src/tera/board/boathouse.rs @@ -4,13 +4,14 @@ use crate::model::{ user::{AdminUser, UserWithDetails, VorstandUser}, }; use rocket::{ + FromForm, Route, State, form::Form, get, post, request::FlashMessage, response::{Flash, Redirect}, - routes, FromForm, Route, State, + routes, }; -use rocket_dyn_templates::{tera::Context, Template}; +use rocket_dyn_templates::{Template, tera::Context}; use sqlx::SqlitePool; #[get("/boathouse")] diff --git a/src/tera/boatdamage.rs b/src/tera/boatdamage.rs index 2e345ac..2fcc368 100644 --- a/src/tera/boatdamage.rs +++ b/src/tera/boatdamage.rs @@ -1,9 +1,10 @@ use rocket::{ + FromForm, Route, State, form::Form, get, post, request::FlashMessage, response::{Flash, Redirect}, - routes, FromForm, Route, State, + routes, }; use rocket_dyn_templates::Template; use sqlx::SqlitePool; diff --git a/src/tera/boatreservation.rs b/src/tera/boatreservation.rs index a739dfc..b8a8fe7 100644 --- a/src/tera/boatreservation.rs +++ b/src/tera/boatreservation.rs @@ -1,10 +1,11 @@ use chrono::NaiveDate; use rocket::{ + FromForm, Route, State, form::Form, get, post, request::FlashMessage, response::{Flash, Redirect}, - routes, FromForm, Route, State, + routes, }; use rocket_dyn_templates::Template; use sqlx::SqlitePool; diff --git a/src/tera/cox.rs b/src/tera/cox.rs index 31c791c..84ca58a 100644 --- a/src/tera/cox.rs +++ b/src/tera/cox.rs @@ -1,8 +1,9 @@ use rocket::{ + FromForm, Route, State, form::Form, get, post, response::{Flash, Redirect}, - routes, FromForm, Route, State, + routes, }; use sqlx::SqlitePool; @@ -22,7 +23,7 @@ async fn create_ergo( ) -> Flash { let trip_details_id = TripDetails::create(db, data.into_inner()).await; let trip_details = TripDetails::find_by_id(db, trip_details_id).await.unwrap(); //Okay, bc just - //created + //created Trip::new_own_ergo(db, &cox, trip_details).await; //TODO: fix //Log::create( @@ -45,7 +46,7 @@ async fn create( ) -> Flash { let trip_details_id = TripDetails::create(db, data.into_inner()).await; let trip_details = TripDetails::find_by_id(db, trip_details_id).await.unwrap(); //Okay, bc just - //created + //created Trip::new_own(db, &cox, trip_details).await; //TODO: fix //Log::create( @@ -137,9 +138,10 @@ async fn join(db: &State, planned_event_id: i64, cox: SteeringUser) .await; Flash::success(Redirect::to("/planned"), "Danke für's helfen!") } - Err(CoxHelpError::CanceledEvent) => { - Flash::error(Redirect::to("/planned"), "Die Ausfahrt wurde leider abgesagt...") - } + Err(CoxHelpError::CanceledEvent) => Flash::error( + Redirect::to("/planned"), + "Die Ausfahrt wurde leider abgesagt...", + ), Err(CoxHelpError::AlreadyRegisteredAsCox) => { Flash::error(Redirect::to("/planned"), "Du hilfst bereits aus!") } @@ -147,9 +149,10 @@ async fn join(db: &State, planned_event_id: i64, cox: SteeringUser) Redirect::to("/planned"), "Du hast dich bereits als Ruderer angemeldet!", ), - Err(CoxHelpError::DetailsLocked) => { - Flash::error(Redirect::to("/planned"), "Die Bootseinteilung wurde bereits gemacht. Wenn du noch steuern möchtest, frag bitte bei einer bereits angemeldeten Steuerperson nach, ob das noch möglich ist.") - } + Err(CoxHelpError::DetailsLocked) => Flash::error( + Redirect::to("/planned"), + "Die Bootseinteilung wurde bereits gemacht. Wenn du noch steuern möchtest, frag bitte bei einer bereits angemeldeten Steuerperson nach, ob das noch möglich ist.", + ), } } else { Flash::error(Redirect::to("/planned"), "Event gibt's nicht") @@ -197,9 +200,10 @@ async fn remove( Flash::success(Redirect::to("/planned"), "Erfolgreich abgemeldet!") } - Err(TripHelpDeleteError::DetailsLocked) => { - Flash::error(Redirect::to("/planned"), "Die Bootseinteilung wurde bereits gemacht. Wenn du doch nicht steuern kannst, melde dich bitte unbedingt schnellstmöglich bei einer anderen Steuerperson!") - } + Err(TripHelpDeleteError::DetailsLocked) => Flash::error( + Redirect::to("/planned"), + "Die Bootseinteilung wurde bereits gemacht. Wenn du doch nicht steuern kannst, melde dich bitte unbedingt schnellstmöglich bei einer anderen Steuerperson!", + ), Err(TripHelpDeleteError::CoxNotHelping) => { Flash::error(Redirect::to("/planned"), "Steuermann hilft nicht aus...") } diff --git a/src/tera/ergo.rs b/src/tera/ergo.rs index 68f7588..5d007d1 100644 --- a/src/tera/ergo.rs +++ b/src/tera/ergo.rs @@ -2,6 +2,7 @@ use std::env; use chrono::Utc; use rocket::{ + FromForm, Route, State, form::Form, fs::TempFile, get, @@ -9,9 +10,9 @@ use rocket::{ post, request::FlashMessage, response::{Flash, Redirect}, - routes, FromForm, Route, State, + routes, }; -use rocket_dyn_templates::{context, Template}; +use rocket_dyn_templates::{Template, context}; use serde::Serialize; use sqlx::SqlitePool; use tera::Context; diff --git a/src/tera/log.rs b/src/tera/log.rs index 1f33e3a..390e285 100644 --- a/src/tera/log.rs +++ b/src/tera/log.rs @@ -1,6 +1,7 @@ use std::net::IpAddr; use rocket::{ + Request, Route, State, form::Form, get, http::{Cookie, CookieJar}, @@ -9,9 +10,8 @@ use rocket::{ response::{Flash, Redirect}, routes, time::{Duration, OffsetDateTime}, - Request, Route, State, }; -use rocket_dyn_templates::{context, Template}; +use rocket_dyn_templates::{Template, context}; use sqlx::SqlitePool; use tera::Context; @@ -215,31 +215,77 @@ async fn create_logbook( user: &DonauLinzUser, smtp_pw: &str, ) -> Flash { - match Logbook::create( - db, - data.into_inner(), - user, smtp_pw - ) - .await - { - Ok(msg) => Flash::success(Redirect::to("/log"), format!("Ausfahrt erfolgreich hinzugefügt{msg}")), - Err(LogbookCreateError::BoatAlreadyOnWater) => Flash::error(Redirect::to("/log"), "Boot schon am Wasser"), - Err(LogbookCreateError::RowerAlreadyOnWater(rower)) => Flash::error(Redirect::to("/log"), format!("Ruderer {} schon am Wasser", rower.name)), - Err(LogbookCreateError::BoatLocked) => Flash::error(Redirect::to("/log"),"Boot gesperrt"), - Err(LogbookCreateError::BoatNotFound) => Flash::error(Redirect::to("/log"), "Boot gibt's ned"), - Err(LogbookCreateError::TooManyRowers(expected, actual)) => Flash::error(Redirect::to("/log"), format!("Zu viele Ruderer (Boot fasst maximal {expected}, es wurden jedoch {actual} Ruderer ausgewählt)")), - Err(LogbookCreateError::RowerCreateError(rower, e)) => Flash::error(Redirect::to("/log"), format!("Fehler bei Ruderer {rower}: {e}")), - Err(LogbookCreateError::ArrivalNotAfterDeparture) => Flash::error(Redirect::to("/log"), "Ankunftszeit kann nicht vor der Abfahrtszeit sein"), - Err(LogbookCreateError::UserNotAllowedToUseBoat) => Flash::error(Redirect::to("/log"), "Schiffsführer darf dieses Boot nicht verwenden"), - Err(LogbookCreateError::SteeringPersonNotInRowers) => Flash::error(Redirect::to("/log"), "Steuerperson nicht in Liste der Ruderer!"), - Err(LogbookCreateError::ShipmasterNotInRowers) => Flash::error(Redirect::to("/log"), "Schiffsführer nicht in Liste der Ruderer!"), - Err(LogbookCreateError::NotYourEntry) => Flash::error(Redirect::to("/log"), "Nicht deine Ausfahrt!"), - Err(LogbookCreateError::ArrivalSetButNotRemainingTwo) => Flash::error(Redirect::to("/log"), "Ankunftszeit gesetzt aber nicht Distanz + Strecke"), - Err(LogbookCreateError::OnlyAllowedToEndTripsEndingToday) => Flash::error(Redirect::to("/log"), "Nur Ausfahrten, die in der letzten Woche enden dürfen eingetragen werden. Für einen Nachtrag schreibe alle Daten an den Vorstand (info@rudernlinz.at)."), - Err(LogbookCreateError::CantChangeHandoperatableStatusForThisBoat) => Flash::error(Redirect::to("/log"), "Handsteuer-Status dieses Boots kann nicht verändert werden."), - Err(LogbookCreateError::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(LogbookCreateError::AlreadyFinalized) => Flash::error(Redirect::to("/log"), "Logbucheintrag wurde bereits abgeschlossen."), - Err(LogbookCreateError::ExternalSteeringPersonMustSteerOrShipmaster) => Flash::error(Redirect::to("/log"), "Wenn du eine 'Externe Steuerperson' hinzufügst, muss diese steuern oder Schiffsführer sein!"), + match Logbook::create(db, data.into_inner(), user, smtp_pw).await { + Ok(msg) => Flash::success( + Redirect::to("/log"), + format!("Ausfahrt erfolgreich hinzugefügt{msg}"), + ), + Err(LogbookCreateError::BoatAlreadyOnWater) => { + Flash::error(Redirect::to("/log"), "Boot schon am Wasser") + } + Err(LogbookCreateError::RowerAlreadyOnWater(rower)) => Flash::error( + Redirect::to("/log"), + format!("Ruderer {} schon am Wasser", rower.name), + ), + Err(LogbookCreateError::BoatLocked) => Flash::error(Redirect::to("/log"), "Boot gesperrt"), + Err(LogbookCreateError::BoatNotFound) => { + Flash::error(Redirect::to("/log"), "Boot gibt's ned") + } + Err(LogbookCreateError::TooManyRowers(expected, actual)) => Flash::error( + Redirect::to("/log"), + format!( + "Zu viele Ruderer (Boot fasst maximal {expected}, es wurden jedoch {actual} Ruderer ausgewählt)" + ), + ), + Err(LogbookCreateError::RowerCreateError(rower, e)) => Flash::error( + Redirect::to("/log"), + format!("Fehler bei Ruderer {rower}: {e}"), + ), + Err(LogbookCreateError::ArrivalNotAfterDeparture) => Flash::error( + Redirect::to("/log"), + "Ankunftszeit kann nicht vor der Abfahrtszeit sein", + ), + Err(LogbookCreateError::UserNotAllowedToUseBoat) => Flash::error( + Redirect::to("/log"), + "Schiffsführer darf dieses Boot nicht verwenden", + ), + Err(LogbookCreateError::SteeringPersonNotInRowers) => Flash::error( + Redirect::to("/log"), + "Steuerperson nicht in Liste der Ruderer!", + ), + Err(LogbookCreateError::ShipmasterNotInRowers) => Flash::error( + Redirect::to("/log"), + "Schiffsführer nicht in Liste der Ruderer!", + ), + Err(LogbookCreateError::NotYourEntry) => { + Flash::error(Redirect::to("/log"), "Nicht deine Ausfahrt!") + } + Err(LogbookCreateError::ArrivalSetButNotRemainingTwo) => Flash::error( + Redirect::to("/log"), + "Ankunftszeit gesetzt aber nicht Distanz + Strecke", + ), + Err(LogbookCreateError::OnlyAllowedToEndTripsEndingToday) => Flash::error( + Redirect::to("/log"), + "Nur Ausfahrten, die in der letzten Woche enden dürfen eingetragen werden. Für einen Nachtrag schreibe alle Daten an den Vorstand (info@rudernlinz.at).", + ), + Err(LogbookCreateError::CantChangeHandoperatableStatusForThisBoat) => Flash::error( + Redirect::to("/log"), + "Handsteuer-Status dieses Boots kann nicht verändert werden.", + ), + Err(LogbookCreateError::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(LogbookCreateError::AlreadyFinalized) => Flash::error( + Redirect::to("/log"), + "Logbucheintrag wurde bereits abgeschlossen.", + ), + Err(LogbookCreateError::ExternalSteeringPersonMustSteerOrShipmaster) => Flash::error( + Redirect::to("/log"), + "Wenn du eine 'Externe Steuerperson' hinzufügst, muss diese steuern oder Schiffsführer sein!", + ), } } @@ -312,7 +358,13 @@ async fn update( let data = data.into_inner(); let Some(logbook) = Logbook::find_by_id(db, data.id).await else { - return Flash::error(Redirect::to("/log"), format!("Logbucheintrag kann nicht bearbeitet werden, da es einen Logbuch-Eintrag mit ID={} nicht gibt", data.id)); + return Flash::error( + Redirect::to("/log"), + format!( + "Logbucheintrag kann nicht bearbeitet werden, da es einen Logbuch-Eintrag mit ID={} nicht gibt", + data.id + ), + ); }; match logbook.update(db, data.clone(), &user.user).await { @@ -353,14 +405,36 @@ async fn home_logbook( ); }; - match logbook.home(db,user, data.into_inner(), smtp_pw).await { + match logbook.home(db, user, data.into_inner(), smtp_pw).await { Ok(_) => Flash::success(Redirect::to("/log"), "Ausfahrt korrekt eingetragen"), - Err(LogbookUpdateError::TooManyRowers(expected, actual)) => Flash::error(Redirect::to("/log"), format!("Zu viele Ruderer (Boot fasst maximal {expected}, es wurden jedoch {actual} Ruderer ausgewählt)")), - Err(LogbookUpdateError::OnlyAllowedToEndTripsEndingToday) => Flash::error(Redirect::to("/log"), "Nur Ausfahrten, die heute enden dürfen eingetragen werden. Für einen Nachtrag schreibe alle Daten dem Vorstand an info@rudernlinz.at."), - 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(LogbookUpdateError::TooManyRowers(expected, actual)) => Flash::error( + Redirect::to("/log"), + format!( + "Zu viele Ruderer (Boot fasst maximal {expected}, es wurden jedoch {actual} Ruderer ausgewählt)" + ), + ), + Err(LogbookUpdateError::OnlyAllowedToEndTripsEndingToday) => Flash::error( + Redirect::to("/log"), + "Nur Ausfahrten, die heute enden dürfen eingetragen werden. Für einen Nachtrag schreibe alle Daten dem Vorstand an info@rudernlinz.at.", + ), + 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:?})!"), @@ -508,7 +582,7 @@ mod test { use sqlx::SqlitePool; use crate::model::logbook::Logbook; - use crate::tera::{log::Boat, User}; + use crate::tera::{User, log::Boat}; use crate::testdb; #[sqlx::test] diff --git a/src/tera/misc.rs b/src/tera/misc.rs index 929694e..52a2b5e 100644 --- a/src/tera/misc.rs +++ b/src/tera/misc.rs @@ -1,4 +1,4 @@ -use rocket::{get, http::ContentType, routes, Route, State}; +use rocket::{Route, State, get, http::ContentType, routes}; use sqlx::SqlitePool; use crate::model::{event::Event, personal::cal::get_personal_cal, user::User}; diff --git a/src/tera/mod.rs b/src/tera/mod.rs index 91f6ecb..11d68b5 100644 --- a/src/tera/mod.rs +++ b/src/tera/mod.rs @@ -2,7 +2,7 @@ use std::{fs::OpenOptions, io::Write}; use chrono::{Datelike, Local}; use rocket::{ - catch, catchers, + Build, Data, FromForm, Request, Rocket, State, catch, catchers, fairing::{AdHoc, Fairing, Info, Kind}, form::Form, fs::FileServer, @@ -13,7 +13,6 @@ use rocket::{ response::{Flash, Redirect}, routes, time::{Duration, OffsetDateTime}, - Build, Data, FromForm, Request, Rocket, State, }; use rocket_dyn_templates::Template; use serde::Deserialize; @@ -21,6 +20,7 @@ use sqlx::SqlitePool; use tera::Context; use crate::{ + SCHECKBUCH, model::{ logbook::Logbook, notification::Notification, @@ -28,7 +28,6 @@ use crate::{ role::Role, user::{User, UserWithDetails}, }, - SCHECKBUCH, }; pub(crate) mod admin; @@ -202,7 +201,10 @@ async fn blogpost_unpublished( #[catch(403)] //forbidden fn forbidden_error() -> Flash { - Flash::error(Redirect::to("/"), "Keine Berechtigung für diese Aktion. Wenn du der Meinung bist, dass du das machen darfst, melde dich bitte bei it@rudernlinz.at.") + Flash::error( + Redirect::to("/"), + "Keine Berechtigung für diese Aktion. Wenn du der Meinung bist, dass du das machen darfst, melde dich bitte bei it@rudernlinz.at.", + ) } struct Usage {} @@ -328,11 +330,13 @@ mod test { assert_eq!(response.status(), Status::Ok); - assert!(response - .into_string() - .await - .unwrap() - .contains("Ruderassistent")); + assert!( + response + .into_string() + .await + .unwrap() + .contains("Ruderassistent") + ); } #[sqlx::test] diff --git a/src/tera/notification.rs b/src/tera/notification.rs index e7c0069..e677586 100644 --- a/src/tera/notification.rs +++ b/src/tera/notification.rs @@ -1,7 +1,7 @@ use rocket::{ - get, + Route, State, get, response::{Flash, Redirect}, - routes, Route, State, + routes, }; use sqlx::SqlitePool; diff --git a/src/tera/planned.rs b/src/tera/planned.rs index 3102818..54d6aad 100644 --- a/src/tera/planned.rs +++ b/src/tera/planned.rs @@ -1,14 +1,15 @@ use rocket::{ - get, + Route, State, get, request::FlashMessage, response::{Flash, Redirect}, - routes, Route, State, + routes, }; use rocket_dyn_templates::Template; use sqlx::SqlitePool; use tera::Context; use crate::{ + AMOUNT_DAYS_TO_SHOW_TRIPS_AHEAD, model::{ log::Log, tripdetails::TripDetails, @@ -16,7 +17,6 @@ use crate::{ user::{AllowedForPlannedTripsUser, User, UserWithDetails}, usertrip::{UserTrip, UserTripDeleteError, UserTripError}, }, - AMOUNT_DAYS_TO_SHOW_TRIPS_AHEAD, }; #[get("/")] @@ -81,14 +81,15 @@ async fn join( ), ) .await; - }else{ + } else { Log::create( db, format!( "User {} registered the guest '{}' for trip_details.id={}", user.name, registered_user, trip_details_id ), - ).await; + ) + .await; } Flash::success(Redirect::to("/planned"), "Erfolgreich angemeldet!") } @@ -98,9 +99,10 @@ async fn join( Err(UserTripError::AlreadyRegistered) => { Flash::error(Redirect::to("/planned"), "Du nimmst bereits teil!") } - Err(UserTripError::AlreadyRegisteredAsCox) => { - Flash::error(Redirect::to("/planned"), "Du hilfst bereits als Steuerperson aus!") - } + Err(UserTripError::AlreadyRegisteredAsCox) => Flash::error( + Redirect::to("/planned"), + "Du hilfst bereits als Steuerperson aus!", + ), Err(UserTripError::CantRegisterAtOwnEvent) => Flash::error( Redirect::to("/planned"), "Du kannst bei einer selbst ausgeschriebenen Fahrt nicht mitrudern ;)", @@ -160,7 +162,10 @@ async fn remove_guest( ) .await; - Flash::error(Redirect::to("/planned"), "Die Bootseinteilung wurde bereits gemacht. Wenn du doch nicht mitrudern kannst, melde dich bitte unbedingt schnellstmöglich bei einer angemeldeten Steuerperson!") + Flash::error( + Redirect::to("/planned"), + "Die Bootseinteilung wurde bereits gemacht. Wenn du doch nicht mitrudern kannst, melde dich bitte unbedingt schnellstmöglich bei einer angemeldeten Steuerperson!", + ) } Err(UserTripDeleteError::GuestNotParticipating) => { Flash::error(Redirect::to("/planned"), "Gast nicht angemeldet.") @@ -211,7 +216,10 @@ async fn remove( ) .await; - Flash::error(Redirect::to("/planned"), "Das Boot ist bereits eingeteilt. Bitte kontaktiere den Schiffsführer (Nummern siehe Signalgruppe) falls du dich doch abmelden willst.") + Flash::error( + Redirect::to("/planned"), + "Das Boot ist bereits eingeteilt. Bitte kontaktiere den Schiffsführer (Nummern siehe Signalgruppe) falls du dich doch abmelden willst.", + ) } Err(UserTripDeleteError::NotVisibleToUser) => { Log::create( @@ -223,7 +231,10 @@ async fn remove( ) .await; - Flash::error(Redirect::to("/planned"), "Abmeldung nicht möglich, da du dieses Event eigentlich gar nicht sehen solltest...") + Flash::error( + Redirect::to("/planned"), + "Abmeldung nicht möglich, da du dieses Event eigentlich gar nicht sehen solltest...", + ) } Err(_) => { panic!("Not possible to be here"); diff --git a/src/tera/stat.rs b/src/tera/stat.rs index 3665b4c..11d224b 100644 --- a/src/tera/stat.rs +++ b/src/tera/stat.rs @@ -1,5 +1,5 @@ -use rocket::{get, routes, Route, State}; -use rocket_dyn_templates::{context, Template}; +use rocket::{Route, State, get, routes}; +use rocket_dyn_templates::{Template, context}; use sqlx::SqlitePool; use crate::model::{ diff --git a/src/tera/trailerreservation.rs b/src/tera/trailerreservation.rs index 4746ec9..42317d1 100644 --- a/src/tera/trailerreservation.rs +++ b/src/tera/trailerreservation.rs @@ -1,10 +1,11 @@ use chrono::NaiveDate; use rocket::{ + FromForm, Route, State, form::Form, get, post, request::FlashMessage, response::{Flash, Redirect}, - routes, FromForm, Route, State, + routes, }; use rocket_dyn_templates::Template; use sqlx::SqlitePool; diff --git a/templates/admin/user/index.html.tera b/templates/admin/user/index.html.tera index 7d66067..2006d9b 100644 --- a/templates/admin/user/index.html.tera +++ b/templates/admin/user/index.html.tera @@ -8,25 +8,65 @@ Neue Person hinzufügen -
-
+
+ Vereinsmitglied +
- - + +
-
-
- -
-
+ class="btn btn-primary" /> + + +
+ Scheckbuch +
+ {{ macros::input(label='Name', name='name', type="text", required=true) }} + {{ macros::input(label='Mailadresse', name='mail', type="email", required=true) }} + +
+
+
+ Schnupperkurs +
+
+ + +
+ {{ macros::input(label='Name', name='name', type="text", required=true) }} + {{ macros::input(label='Mailadresse', name='mail', type="email", required=true) }} + {{ macros::select(label="Finanzielles", data=financial, name='financial_id', display=['name'], default="Keine Ermäßigung") }} + +
+
{% endif %} diff --git a/templates/admin/user/view.html.tera b/templates/admin/user/view.html.tera index 5da609b..642a2f2 100644 --- a/templates/admin/user/view.html.tera +++ b/templates/admin/user/view.html.tera @@ -175,19 +175,27 @@ {% endfor %} + {% endif %} {% elif "SchnupperInterest" in member %} {% if allowed_to_edit %} - + - {% endif %} {% endif %}