From dc75e0145ae7dff1533d66a0f386870f99290377 Mon Sep 17 00:00:00 2001 From: Philipp Hofer Date: Fri, 16 May 2025 23:02:40 +0200 Subject: [PATCH] reflect new fee structure --- src/lib.rs | 3 ++ src/model/mail.rs | 14 +++++++-- src/model/user/basic.rs | 9 ++++++ src/model/user/fee.rs | 55 ++++++++++++++++++++++++++++------ src/model/user/regular.rs | 5 ++-- src/model/user/schnupperant.rs | 52 +++++++++++++++++++++++++------- staging-diff.sql | 43 ++------------------------ 7 files changed, 116 insertions(+), 65 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b21dcb4..ab20008 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,6 +23,9 @@ pub(crate) const UNTERSTUETZEND: i64 = 2500; pub(crate) const FOERDERND: i64 = 8500; pub(crate) const SCHECKBUCH: i64 = 3000; pub(crate) const EINSCHREIBGEBUEHR: i64 = 3000; +pub(crate) const DUAL_MEMBERSHIP: i64 = 18000; +pub(crate) const TRIAL_ROWING: i64 = 12000; +pub(crate) const TRIAL_ROWING_REDUCED: i64 = 6000; #[derive(Debug, Clone, PartialEq, Eq)] pub struct NonEmptyString(String); diff --git a/src/model/mail.rs b/src/model/mail.rs index 35328a9..ecabddd 100644 --- a/src/model/mail.rs +++ b/src/model/mail.rs @@ -1,9 +1,9 @@ use std::{error::Error, fs}; use lettre::{ - Address, Message, SmtpTransport, Transport, - message::{Attachment, MultiPart, SinglePart, header::ContentType}, + message::{header::ContentType, Attachment, MultiPart, SinglePart}, transport::smtp::authentication::Credentials, + Address, Message, SmtpTransport, Transport, }; use sqlx::{Sqlite, SqlitePool, Transaction}; @@ -161,6 +161,11 @@ impl Mail { continue; } } + + if user.has_role(db, "schnupperant").await { + continue; + } + if !user.has_role(db, "paid").await || test.is_some() { let mut is_family = false; let mut send_to = String::new(); @@ -273,6 +278,11 @@ Der Vorstand"); continue; } } + + if user.has_role(db, "schnupperant").await { + continue; + } + if let Some(fee) = user.fee(db).await { if !fee.paid || test.is_some() { let mut is_family = false; diff --git a/src/model/user/basic.rs b/src/model/user/basic.rs index 2e879e7..1308355 100644 --- a/src/model/user/basic.rs +++ b/src/model/user/basic.rs @@ -519,6 +519,15 @@ impl User { 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, diff --git a/src/model/user/fee.rs b/src/model/user/fee.rs index 55317bd..044b750 100644 --- a/src/model/user/fee.rs +++ b/src/model/user/fee.rs @@ -1,7 +1,8 @@ use super::User; use crate::{ - BOAT_STORAGE, EINSCHREIBGEBUEHR, FAMILY_THREE_OR_MORE, FAMILY_TWO, FOERDERND, REGULAR, - RENNRUDERBEITRAG, STUDENT_OR_PUPIL, UNTERSTUETZEND, model::family::Family, + model::family::Family, BOAT_STORAGE, DUAL_MEMBERSHIP, EINSCHREIBGEBUEHR, FAMILY_THREE_OR_MORE, + FAMILY_TWO, FOERDERND, REGULAR, RENNRUDERBEITRAG, STUDENT_OR_PUPIL, TRIAL_ROWING, + TRIAL_ROWING_REDUCED, UNTERSTUETZEND, }; use chrono::{Datelike, Local, NaiveDate}; use serde::Serialize; @@ -68,6 +69,7 @@ impl User { if !self.has_role(db, "Donau Linz").await && !self.has_role(db, "Unterstützend").await && !self.has_role(db, "Förderndes Mitglied").await + && !self.has_role(db, "schnupperant").await { return None; } @@ -107,6 +109,7 @@ impl User { if !self.has_role(db, "Donau Linz").await && !self.has_role(db, "Unterstützend").await && !self.has_role(db, "Förderndes Mitglied").await + && !self.has_role(db, "schnupperant").await { return fee; } @@ -126,13 +129,16 @@ impl User { ); } - if let Some(member_since_date) = &self.member_since_date { - if let Ok(member_since_date) = NaiveDate::parse_from_str(member_since_date, "%Y-%m-%d") - { - if member_since_date.year() == Local::now().year() - && !self.has_role(db, "no-einschreibgebuehr").await + if !self.has_role(db, "schnupperant").await { + if let Some(member_since_date) = &self.member_since_date { + if let Ok(member_since_date) = + NaiveDate::parse_from_str(member_since_date, "%Y-%m-%d") { - fee.add("Einschreibgebühr".into(), EINSCHREIBGEBUEHR); + if member_since_date.year() == Local::now().year() + && !self.has_role(db, "no-einschreibgebuehr").await + { + fee.add("Einschreibgebühr".into(), EINSCHREIBGEBUEHR); + } } } } @@ -150,7 +156,13 @@ impl User { false }; - if self.has_role(db, "Unterstützend").await { + if self.has_role(db, "schnupperant").await { + if self.has_role(db, "Student").await || self.has_role(db, "Schüler").await { + fee.add("Schnupperkurs (reduziert)".into(), TRIAL_ROWING_REDUCED); + } else { + fee.add("Schnupperkurs".into(), TRIAL_ROWING); + } + } else if self.has_role(db, "Unterstützend").await { fee.add("Unterstützendes Mitglied".into(), UNTERSTUETZEND); } else if self.has_role(db, "Förderndes Mitglied").await { fee.add("Förderndes Mitglied".into(), FOERDERND); @@ -163,6 +175,18 @@ impl User { } } else if self.has_role(db, "Ehrenmitglied").await { fee.add("Ehrenmitglied".into(), 0); + } else if self.has_role(db, "dual_membership").await { + if halfprice { + fee.add( + "Doppelmitgliedschaft mit anderem österr. Ruderverein (Halbpreis)".into(), + DUAL_MEMBERSHIP / 2, + ); + } else { + fee.add( + "Doppelmitgliedschaft mit anderem österr. Ruderverein".into(), + DUAL_MEMBERSHIP, + ); + } } else if halfprice { fee.add("Mitgliedsbeitrag (Halbpreis)".into(), REGULAR / 2); } else { @@ -170,6 +194,19 @@ impl User { } } + if !self.has_role(db, "schnupperant").await + && self.has_role(db, "participated_schnupperkurs").await + { + if self.has_role(db, "Student").await || self.has_role(db, "Schüler").await { + fee.add( + "Anrechnung reduzierter Schnupperkurs".into(), + -TRIAL_ROWING_REDUCED, + ); + } else { + fee.add("Anrechnung Schnupperkurs".into(), -TRIAL_ROWING); + } + } + fee } } diff --git a/src/model/user/regular.rs b/src/model/user/regular.rs index 438a8a6..f8b5ee4 100644 --- a/src/model/user/regular.rs +++ b/src/model/user/regular.rs @@ -1,8 +1,7 @@ use super::{ManageUserUser, User}; use crate::{ - NonEmptyString, model::{activity::ActivityBuilder, mail::Mail, notification::Notification, role::Role}, - special_user, + special_user, NonEmptyString, }; use chrono::NaiveDate; use rocket::{async_trait, fs::TempFile, tokio::io::AsyncReadExt}; @@ -93,6 +92,8 @@ Beim nächsten Treffen im Verein, erinnere jemand vom Vorstand (https://rudernli Damit du dich noch mehr verbunden fühlst (:-)), haben wir im Bootshaus ein WLAN für Vereinsmitglieder 'ASKÖ Ruderverein Donau Linz' eingerichtet. Das Passwort dafür lautet 'donau1921' (ohne Anführungszeichen). Bitte gib das Passwort an keine vereinsfremden Personen weiter. +Falls du deinen Mitgliedsbeitrag noch nicht bezahlt hast, erledige dies bitte demnächst. Den genauen Betrag und einen QR Code, den du mit deiner Bankapp scannen kannst findest du unter https://app.rudernlinz.at/planned + Wir freuen uns darauf, dich bald am Wasser zu sehen und gemeinsam tolle Erfahrungen zu sammeln! Riemen- & Dollenbruch diff --git a/src/model/user/schnupperant.rs b/src/model/user/schnupperant.rs index b5addb5..098ece8 100644 --- a/src/model/user/schnupperant.rs +++ b/src/model/user/schnupperant.rs @@ -4,9 +4,9 @@ use super::scheckbuch::ScheckbuchUser; use super::schnupperinterest::SchnupperInterestUser; use super::unterstuetzend::UnterstuetzendUser; use super::{ManageUserUser, User}; -use crate::NonEmptyString; use crate::model::activity::ActivityBuilder; use crate::model::role::Role; +use crate::NonEmptyString; use crate::{ model::{mail::Mail, notification::Notification}, special_user, @@ -65,20 +65,32 @@ impl SchnupperantUser { .await?; // Change roles - let regular = Role::find_by_name(db, "Donau Linz").await.unwrap(); + let paid = Role::find_by_name(db, "paid").await.unwrap(); + if self.user.remove_role(db, changed_by, &paid).await.is_err() { + self.remove_membership_pdf(db, changed_by).await; + return Err("Kann noch kein normales Mitglied werden, da die Schnupperkurs-Gebühr noch nicht bezahlt wurde.".into()); + } let scheckbook = Role::find_by_name(db, "schnupperant").await.unwrap(); self.user.remove_role(db, changed_by, &scheckbook).await?; + + let regular = Role::find_by_name(db, "Donau Linz").await.unwrap(); self.user.add_role(db, changed_by, ®ular).await?; + let participated_schnupperkurs = Role::find_by_name(db, "participated_schnupperkurs") + .await + .unwrap(); + self.user + .add_role(db, changed_by, &participated_schnupperkurs) + .await?; + // Notify let regular = RegularUser::new(db, &self.user).await.unwrap(); regular.send_welcome_mail_to_user(db, smtp_pw).await?; Notification::create_for_steering_people( db, &format!( - "Liebe Steuerberechtigte, {} nahm an unserem Schnupperkurs teil und ist nun seit {} ein neues reguläres Mitglied. 🎉", - self.name, - self.member_since_date.clone().unwrap() + "Liebe Steuerberechtigte, {} nahm an unserem Schnupperkurs teil und ist nun seit {member_since} ein neues reguläres Mitglied. 🎉", + self.name ), "Neues Vereinsmitglied", None, @@ -203,6 +215,11 @@ impl SchnupperantUser { .await?; // Change roles + let paid = Role::find_by_name(db, "paid").await.unwrap(); + if self.user.remove_role(db, changed_by, &paid).await.is_err() { + self.remove_membership_pdf(db, changed_by).await; + return Err("Kann noch kein normales Mitglied werden, da die Schnupperkurs-Gebühr noch nicht bezahlt wurde.".into()); + } let unterstuetzend = Role::find_by_name(db, "Unterstützend").await.unwrap(); let scheckbook = Role::find_by_name(db, "schnupperant").await.unwrap(); self.user.remove_role(db, changed_by, &scheckbook).await?; @@ -267,6 +284,11 @@ impl SchnupperantUser { .await?; // Change roles + let paid = Role::find_by_name(db, "paid").await.unwrap(); + if self.user.remove_role(db, changed_by, &paid).await.is_err() { + self.remove_membership_pdf(db, changed_by).await; + return Err("Kann noch kein normales Mitglied werden, da die Schnupperkurs-Gebühr noch nicht bezahlt wurde.".into()); + } let unterstuetzend = Role::find_by_name(db, "Förderndes Mitglied").await.unwrap(); let scheckbook = Role::find_by_name(db, "schnupperant").await.unwrap(); self.user.remove_role(db, changed_by, &scheckbook).await?; @@ -307,7 +329,7 @@ impl SchnupperantUser { // TODO: make private pub(crate) async fn notify(&self, db: &SqlitePool, smtp_pw: &str) -> Result<(), String> { - self.notify_coxes_about_new_scheckbuch(db).await; + self.notify_coxes_about_new_schnupperant(db).await; self.send_welcome_mail_to_user(db, smtp_pw).await?; ActivityBuilder::new(&format!( @@ -335,19 +357,27 @@ impl SchnupperantUser { mail, "ASKÖ Ruderverein Donau Linz | Anmeldung Schnupperkurs", format!( -"Hallo {0}, + "Hallo {0}, -es freut uns sehr, dich bei unserem Schnupperkurs willkommen heißen zu dürfen. Detaillierte Informationen folgen noch, ich werde sie dir ein paar Tage vor dem Termin zusenden. +es freut uns sehr, dich bei unserem Schnupperkurs willkommen heißen zu dürfen. + +Bitte überweise die {1} € auf unser Bankkonto (IBAN: AT58 2032 0321 0072 9256) und gib beim Verwendungszweck 'Schnupperkurs {0}' an. + +Detaillierte Informationen folgen noch, du wirst sie ein paar Tage vor dem Termin bekommen (wenn das Wetter/Wasserstand/... abschätzbar ist). Riemen- & Dollenbruch, -ASKÖ Ruderverein Donau Linz", self.name), +ASKÖ Ruderverein Donau Linz", + self.name, + self.fee(db).await.unwrap().sum_in_cents/100 + ), smtp_pw, - ).await?; + ) + .await?; Ok(()) } - async fn notify_coxes_about_new_scheckbuch(&self, db: &SqlitePool) { + async fn notify_coxes_about_new_schnupperant(&self, db: &SqlitePool) { if let Some(role) = Role::find_by_name(db, "schnupper-betreuer").await { Notification::create_for_role( db, diff --git a/staging-diff.sql b/staging-diff.sql index c48e107..da2a13e 100644 --- a/staging-diff.sql +++ b/staging-diff.sql @@ -5,44 +5,5 @@ INSERT INTO user(name) VALUES('Philipp'); INSERT INTO "user_role" (user_id, role_id) VALUES((SELECT id from user where name = 'Philipp'),(SELECT id FROM role where name = 'Donau Linz')); --- Step 1: Create a new table without the 'notes' column -CREATE TABLE "user_new" ( - "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, - "name" text NOT NULL UNIQUE, - "pw" text, - "deleted" boolean NOT NULL DEFAULT FALSE, - "last_access" DATETIME, - "dob" text, - "weight" text, - "sex" text, - "dirty_thirty" text, - "dirty_dozen" text, - "member_since_date" text, - "birthdate" text, - "mail" text, - "nickname" text, - "phone" text, - "address" text, - "family_id" INTEGER REFERENCES family(id), - "membership_pdf" BLOB, - "user_token" TEXT NOT NULL DEFAULT (lower(hex(randomblob(16)))) -); - --- Step 2: Copy data from the old table to the new one (excluding 'notes') -INSERT INTO user_new ( - id, name, pw, deleted, last_access, dob, weight, sex, - dirty_thirty, dirty_dozen, member_since_date, birthdate, - mail, nickname, phone, address, family_id, membership_pdf, user_token -) -SELECT - id, name, pw, deleted, last_access, dob, weight, sex, - dirty_thirty, dirty_dozen, member_since_date, birthdate, - mail, nickname, phone, address, family_id, membership_pdf, user_token -FROM user; - --- Step 3: Drop the old table -DROP TABLE user; - --- Step 4: Rename the new table to the original name -ALTER TABLE user_new RENAME TO user; - +insert into role(name, cluster, formatted_name) values('dual_membership', 'financial', 'Doppelmitgliedschaft mit anderem österr. Ruderverein'); +insert into role(name, hide_in_lists) values('participated_schnupperkurs', true); -- 2.47.2