// TODO: put back in `src/model/user/mod.rs` once that is cleaned up use super::{AllowedToEditPaymentStatusUser, ManageUserUser, User}; use crate::model::{ activity::Activity, family::Family, log::Log, mail::valid_mails, notification::Notification, role::Role, }; use chrono::NaiveDate; use rocket::{fs::TempFile, tokio::io::AsyncReadExt}; use sqlx::SqlitePool; impl User { pub(crate) async fn add_note( &self, db: &SqlitePool, updated_by: &ManageUserUser, user: &User, note: &str, ) -> Result<(), String> { let note = note.trim(); Activity::create( db, &format!("({updated_by}) {note}"), &format!("user-{};", user.id), None, ) .await; Ok(()) } pub(crate) async fn update_mail( &self, db: &SqlitePool, updated_by: &ManageUserUser, new_mail: &str, ) -> Result<(), String> { let new_mail = new_mail.trim(); if !valid_mails(new_mail) { return Err(format!( "{new_mail} ist kein gültiges Format für eine Mailadresse" )); } sqlx::query!("UPDATE user SET mail = ? where id = ?", new_mail, self.id) .execute(db) .await .unwrap(); //Okay, because we can only create a User of a valid id let msg = match &self.mail { Some(old_mail) => format!( "{updated_by} has changed the mail address of {self} from {old_mail} to {new_mail}" ), None => format!("{updated_by} has added a mail address for {self}: {new_mail}"), }; Log::create(db, msg).await; Ok(()) } pub(crate) async fn update_phone( &self, db: &SqlitePool, updated_by: &ManageUserUser, new_phone: &str, ) { let new_phone = new_phone.trim(); let query = if new_phone.is_empty() { if self.phone.is_none() { return; // nothing to do } sqlx::query!("UPDATE user SET phone = NULL where id = ?", self.id) } else { if let Some(old_phone) = &self.phone { if old_phone == new_phone { return; //nothing to do } } sqlx::query!("UPDATE user SET phone = ? where id = ?", new_phone, self.id) }; 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} has removed the phone number of {self} (old number: {old_phone})"), Some(old_phone) => format!("{updated_by} has changed the phone number of {self} from {old_phone} to {new_phone}"), None => format!("{updated_by} has added a phone number for {self}: {new_phone}") }; Log::create(db, msg).await; } pub(crate) async fn update_address( &self, db: &SqlitePool, updated_by: &ManageUserUser, new_address: &str, ) { let new_address = new_address.trim(); let query = if new_address.is_empty() { if !self.address.is_none() { return; // nothing to do } sqlx::query!("UPDATE user SET address = NULL where id = ?", self.id) } else { if let Some(old_address) = &self.address { if old_address == new_address { return; //nothing to do } } sqlx::query!( "UPDATE user SET address = ? where id = ?", new_address, self.id ) }; 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} has removed the address of {self} (old address: {old_address})"), Some(old_address) => format!("{updated_by} has changed the address of {self} from {old_address} to {new_address}"), None => format!("{updated_by} has added an address for {self}: {new_address}") }; Log::create(db, msg).await; } pub(crate) async fn update_nickname( &self, db: &SqlitePool, updated_by: &ManageUserUser, new_nickname: &str, ) -> Result<(), String> { let new_nickname = new_nickname.trim(); let query = if new_nickname.is_empty() { sqlx::query!("UPDATE user SET nickname = NULL where id = ?", self.id) } else { sqlx::query!( "UPDATE user SET nickname = ? where id = ?", new_nickname, self.id ) }; 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} has removed the nickname of {self} (old nickname: {old_nickname})"), Some(old_nickname) => format!("{updated_by} has changed the nickname of {self} from {old_nickname} to {new_nickname}"), None => format!("{updated_by} has added a nickname for {self}: {new_nickname}") }; Log::create(db, msg).await; Ok(()) } pub(crate) async fn update_member_since( &self, db: &SqlitePool, updated_by: &ManageUserUser, new_member_since_date: &NaiveDate, ) { sqlx::query!( "UPDATE user SET member_since_date = ? where id = ?", new_member_since_date, self.id ) .execute(db) .await .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} has changed the member_since date of {self} from {old_member_since_date} to {new_member_since_date}"), None => format!("{updated_by} has added a member_since_date for {self}: {new_member_since_date}") }; Log::create(db, msg).await; } pub(crate) async fn update_birthdate( &self, db: &SqlitePool, updated_by: &ManageUserUser, new_birthdate: &NaiveDate, ) { sqlx::query!( "UPDATE user SET birthdate = ? where id = ?", new_birthdate, self.id ) .execute(db) .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} has changed the birthdate of {self} from {old_birthdate} to {new_birthdate}"), None => format!("{updated_by} has added a birthdate for {self}: {new_birthdate}") }; Log::create(db, msg).await; } pub(crate) async fn update_family( &self, db: &SqlitePool, updated_by: &ManageUserUser, family: Option, ) { if let Some(family) = family { let family_id = family.id; sqlx::query!( "UPDATE user SET family_id = ? where id = ?", family_id, self.id ) .execute(db) .await .unwrap(); } else { sqlx::query!("UPDATE user SET family_id = NULL where id = ?", self.id) .execute(db) .await .unwrap(); }; Family::clean_families_without_members(db).await; Log::create( db, format!("{updated_by} hat die Familie von {self} aktualisiert."), ) .await; } pub(crate) async fn change_skill( &self, db: &SqlitePool, updated_by: &ManageUserUser, skill: Option, ) -> Result<(), String> { let old_skill = self.skill(db).await; let member = Role::find_by_name(db, "Donau Linz").await.unwrap(); let cox = Role::find_by_name(db, "cox").await.unwrap(); let bootsfuehrer = Role::find_by_name(db, "Bootsführer").await.unwrap(); match (old_skill, skill) { (old, new) if old == None && new == Some(cox.clone()) => { self.add_role(db, updated_by, &cox).await?; Notification::create_for_role( db, &member, &format!( "Liebes Vereinsmitglied, {self} ist ab sofort Steuerperson 🎉 Hip hip ...!" ), "Neue Steuerperson", None, None, ) .await; } (old, new) if old == Some(cox.clone()) && new == Some(bootsfuehrer.clone()) => { self.remove_role(db, updated_by, &cox).await?; self.add_role(db, updated_by, &bootsfuehrer).await?; Notification::create_for_role( db, &member, &format!( "Liebes Vereinsmitglied, {self} ist ab sofort Bootsführer:in 🎉 Hip hip ...!" ), "Neue:r Bootsführer:in", None, None, ) .await; } (old, new) if new == None => { if let Some(old) = old { self.remove_role(db, updated_by, &old).await?; let vorstand = Role::find_by_name(db, "Vorstand").await.unwrap(); Notification::create_for_role( db, &vorstand, &format!("Lieber Vorstand, {self} ist ab kein {old} mehr."), "Steuerperson --", None, None, ) .await; } } (old, new) => return Err(format!("Not allowed to change from {old:?} to {new:?}")), }; Ok(()) } pub(crate) async fn change_financial( &self, db: &SqlitePool, updated_by: &ManageUserUser, financial: Option, ) -> Result<(), String> { let mut new = String::new(); let mut old = String::new(); if let Some(old_financial) = self.financial(db).await { self.remove_role(db, updated_by, &old_financial).await?; old.push_str(&old_financial.name); } else { old.push_str("Keine Ermäßigung"); } if let Some(new_financial) = financial { self.add_role(db, updated_by, &new_financial).await?; new.push_str(&new_financial.name); } else { new.push_str("Keine Ermäßigung"); } Activity::create( db, &format!("({updated_by}) Ermäßigung von {self} von {old} auf {new} geändert"), &format!("user-{};", self.id), None, ) .await; Ok(()) } pub(crate) async fn remove_role( &self, db: &SqlitePool, updated_by: &ManageUserUser, 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")); } sqlx::query!( "DELETE FROM user_role WHERE user_id = ? and role_id = ?", self.id, role.id ) .execute(db) .await .unwrap(); Log::create( db, format!("{updated_by} has removed role {role} from user {self}"), ) .await; Ok(()) } pub(crate) async fn has_not_paid( &self, db: &SqlitePool, updated_by: &AllowedToEditPaymentStatusUser, ) { let paid = Role::find_by_name(db, "paid").await.unwrap(); sqlx::query!( "DELETE FROM user_role WHERE user_id = ? and role_id = ?", self.id, paid.id ) .execute(db) .await .unwrap(); Log::create( db, format!("{updated_by} has set that user {self} has NOT paid the fee (yet)"), ) .await; } pub(crate) async fn has_paid( &self, db: &SqlitePool, updated_by: &AllowedToEditPaymentStatusUser, ) { let paid = Role::find_by_name(db, "paid").await.unwrap(); sqlx::query!( "INSERT INTO user_role(user_id, role_id) VALUES (?, ?)", self.id, paid.id ) .execute(db) .await .expect("paid role has no group"); Log::create( db, format!("{updated_by} has set that user {self} has paid the fee (yet)"), ) .await; } pub(crate) async fn add_role( &self, db: &SqlitePool, updated_by: &ManageUserUser, 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")); } sqlx::query!( "INSERT INTO user_role(user_id, role_id) VALUES (?, ?)", self.id, role.id ) .execute(db) .await .map_err(|_| { format!( "User already has a role in the cluster '{}'", role.cluster .clone() .expect("db trigger can't activate on empty string") ) })?; Log::create( db, format!("{updated_by} has added role {role} to user {self}"), ) .await; Ok(()) } pub(crate) async fn add_membership_pdf( &self, db: &SqlitePool, updated_by: &ManageUserUser, membership_pdf: &TempFile<'_>, ) -> Result<(), String> { if self.has_membership_pdf(db).await { return Err(format!("User {self} hat bereits eine Beitrittserklärung.")); } if membership_pdf.len() == 0 { return Err(format!("Keine Beitrittserklärung mitgeschickt.")); } let mut stream = membership_pdf.open().await.unwrap(); let mut buffer = Vec::new(); stream.read_to_end(&mut buffer).await.unwrap(); sqlx::query!( "UPDATE user SET membership_pdf = ? where id = ?", buffer, self.id ) .execute(db) .await .unwrap(); //Okay, because we can only create a User of a valid id Log::create( db, format!("{updated_by} has added the membership pdf for user {self}"), ) .await; Ok(()) } }