forked from Ruderverein-Donau-Linz/rowt
566 lines
18 KiB
Rust
566 lines
18 KiB
Rust
// TODO: put back in `src/model/user/mod.rs` once that is cleaned up
|
|
|
|
use super::{AllowedToEditPaymentStatusUser, ManageUserUser, User};
|
|
use crate::model::{
|
|
activity::{self, ActivityBuilder},
|
|
family::Family,
|
|
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,
|
|
note: &str,
|
|
) -> Result<(), String> {
|
|
let note = note.trim();
|
|
|
|
ActivityBuilder::from(activity::Reason::UserDataChange(
|
|
updated_by,
|
|
self,
|
|
note.to_string(),
|
|
))
|
|
.save(db)
|
|
.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!("Mail-Adresse von {old_mail} auf {new_mail} geändert."),
|
|
None => format!("Neue Mail-Adresse für: {new_mail}"),
|
|
};
|
|
|
|
ActivityBuilder::from(activity::Reason::UserDataChange(updated_by, self, msg))
|
|
.save(db)
|
|
.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!("Telefonnummer wurde entfernt (alte Nummer: {old_phone})")
|
|
}
|
|
Some(old_phone) => {
|
|
format!("Telefonnummer wurde von {old_phone} auf {new_phone} geändert.")
|
|
}
|
|
None => format!("Neue Telefonnummer hinzugefügt: {new_phone}"),
|
|
};
|
|
|
|
ActivityBuilder::from(activity::Reason::UserDataChange(updated_by, self, msg))
|
|
.save(db)
|
|
.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} 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)
|
|
.relevant_for_user(self)
|
|
.save(db)
|
|
.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} 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)
|
|
.save(db)
|
|
.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} 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)
|
|
.relevant_for_user(self)
|
|
.save(db)
|
|
.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} 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)
|
|
.relevant_for_user(self)
|
|
.save(db)
|
|
.await;
|
|
}
|
|
|
|
pub(crate) async fn update_family(
|
|
&self,
|
|
db: &SqlitePool,
|
|
updated_by: &ManageUserUser,
|
|
family: Option<Family>,
|
|
) {
|
|
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();
|
|
ActivityBuilder::new(&format!(
|
|
"{updated_by} hat {self} zu einer Familie hinzugefügt."
|
|
))
|
|
.relevant_for_user(self)
|
|
.save(db)
|
|
.await;
|
|
} else {
|
|
sqlx::query!("UPDATE user SET family_id = NULL where id = ?", self.id)
|
|
.execute(db)
|
|
.await
|
|
.unwrap();
|
|
ActivityBuilder::new(&format!(
|
|
"{updated_by} hat die Familienzugehörigkeit von {self} gelöscht."
|
|
))
|
|
.relevant_for_user(self)
|
|
.save(db)
|
|
.await;
|
|
};
|
|
|
|
Family::clean_families_without_members(db).await;
|
|
}
|
|
|
|
pub(crate) async fn change_skill(
|
|
&self,
|
|
db: &SqlitePool,
|
|
updated_by: &ManageUserUser,
|
|
skill: Option<Role>,
|
|
) -> 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) {
|
|
(None, new) if 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;
|
|
Notification::create(
|
|
db,
|
|
self,
|
|
&format!(
|
|
"Liebe neue Steuerperson, gratuliere zur geschafften Steuerprüfung 💪. Du kannst ab sofort selber Ausfahrten ausschreiben und der Steuerpersonen Signal-Gruppe beitreten: https://signal.group/#CjQKIHJInNb3zSVW7ipLo7_ygIqVxhxUaaNYx4sy2jdklLsIEhBHJNM2KZM1UnBdQxWy_Gdp"
|
|
),
|
|
"Gratulation",
|
|
None,
|
|
None,
|
|
)
|
|
.await;
|
|
ActivityBuilder::new(&format!("{updated_by} hat {self} zur Steuerperson gemacht"))
|
|
.relevant_for_user(self)
|
|
.save(db)
|
|
.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;
|
|
ActivityBuilder::new(&format!("{updated_by} hat {self} zum Bootsführer gemacht"))
|
|
.relevant_for_user(self)
|
|
.save(db)
|
|
.await;
|
|
}
|
|
(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();
|
|
Notification::create_for_role(
|
|
db,
|
|
&vorstand,
|
|
&format!("Lieber Vorstand, {self} ist ab sofort kein {old} mehr."),
|
|
"Steuerperson--;",
|
|
None,
|
|
None,
|
|
)
|
|
.await;
|
|
ActivityBuilder::new(&format!("{updated_by} hat {self} zum normalen Mitglied gemacht (keine Steuerperson/Schiffsführer mehr)"))
|
|
.relevant_for_user(self)
|
|
.save(db)
|
|
.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<Role>,
|
|
) -> 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.to_string());
|
|
} 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.to_string());
|
|
} else {
|
|
new.push_str("Keine Ermäßigung");
|
|
}
|
|
|
|
ActivityBuilder::new(&format!(
|
|
"{updated_by} hat die Ermäßigung von {self} von {old} auf {new} geändert"
|
|
))
|
|
.relevant_for_user(self)
|
|
.save(db)
|
|
.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();
|
|
|
|
if !role.hide_in_lists && role.cluster.is_none() {
|
|
ActivityBuilder::new(&format!(
|
|
"{updated_by} hat die Rolle {role} von {self} entfernt."
|
|
))
|
|
.relevant_for_user(self)
|
|
.save(db)
|
|
.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();
|
|
|
|
ActivityBuilder::new(&format!(
|
|
"{updated_by} hat den Bezahlstatus von {self} auf 'nicht bezahlt' gesetzt."
|
|
))
|
|
.relevant_for_user(self)
|
|
.save(db)
|
|
.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");
|
|
|
|
ActivityBuilder::new(&format!(
|
|
"{updated_by} hat den Bezahlstatus von {self} auf 'bezahlt' gesetzt."
|
|
))
|
|
.relevant_for_user(self)
|
|
.save(db)
|
|
.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")
|
|
)
|
|
})?;
|
|
|
|
if !role.hide_in_lists && role.cluster.is_none() {
|
|
ActivityBuilder::new(&format!(
|
|
"{updated_by} hat die Rolle '{role}' dem Benutzer {self} hinzugefügt."
|
|
))
|
|
.relevant_for_user(self)
|
|
.save(db)
|
|
.await;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub(crate) async fn remove_membership_pdf(&self, db: &SqlitePool, updated_by: &ManageUserUser) {
|
|
sqlx::query!(
|
|
"UPDATE user SET membership_pdf = null where id = ?",
|
|
self.id
|
|
)
|
|
.execute(db)
|
|
.await
|
|
.unwrap(); //Okay, because we can only create a User of a valid id
|
|
}
|
|
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("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();
|
|
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
|
|
|
|
ActivityBuilder::new(&format!(
|
|
"{updated_by} hat die Mitgliedserklärung (PDF) für user {self} hinzugefügt."
|
|
))
|
|
.relevant_for_user(self)
|
|
.save(db)
|
|
.await;
|
|
|
|
Ok(())
|
|
}
|
|
}
|