allow others to send fee reminder thus reducing my bus factor #915

Merged
philipp merged 2 commits from send-fee-reminder into staging 2025-04-17 20:44:53 +02:00
6 changed files with 88 additions and 23 deletions

View File

@ -2,7 +2,7 @@
secret_key = "/NtVGizglEoyoxBLzsRDWTy4oAG1qDw4J4O+CWJSv+fypD7W9sam8hUY4j90EZsbZk8wEradS5zBoWtWKi3k8w==" secret_key = "/NtVGizglEoyoxBLzsRDWTy4oAG1qDw4J4O+CWJSv+fypD7W9sam8hUY4j90EZsbZk8wEradS5zBoWtWKi3k8w=="
rss_key = "rss-key-for-ci" rss_key = "rss-key-for-ci"
limits = { file = "10 MiB", data-form = "10 MiB"} limits = { file = "10 MiB", data-form = "10 MiB"}
smtp_pw = "8kIjlLH79Ky6D3j" smtp_pw = "my-smtp-password"
usage_log_path = "./usage.txt" usage_log_path = "./usage.txt"
openweathermap_key = "c8dab8f91b5b815d76e9879cbaecd8d5" openweathermap_key = "openweather-key"
wordpress_key = "pw-to-allow-sending-notifications" wordpress_key = "pw-to-allow-sending-notifications"

View File

@ -151,10 +151,15 @@ impl Mail {
false false
} }
pub async fn fees(db: &SqlitePool, smtp_pw: String) { pub async fn fees(db: &SqlitePool, smtp_pw: String, test: Option<User>) {
let users = User::all_payer_groups(db).await; let users = User::all_payer_groups(db).await;
for user in users { for user in users {
if !user.has_role(db, "paid").await { if let Some(test) = &test {
if user.id != test.id {
continue;
}
}
if !user.has_role(db, "paid").await || test.is_some() {
let mut is_family = false; let mut is_family = false;
let mut send_to = String::new(); let mut send_to = String::new();
match Family::find_by_opt_id(db, user.family_id).await { match Family::find_by_opt_id(db, user.family_id).await {
@ -196,11 +201,10 @@ dein Vereinsbeitrag für das aktuelle Jahr beträgt {}€",
)) ))
} }
content.push_str("\nBitte überweise diesen auf folgendes Konto: IBAN: AT58 2032 0321 0072 9256. Auf https://app.rudernlinz.at/planned findest du einen QR Code, den du mit deiner Bankapp scannen kannst um alle Eingaben bereits ausgefüllt zu haben.\n\n\ content.push_str("\nBitte überweise diesen auf folgendes Konto: IBAN: AT58 2032 0321 0072 9256. Auf https://app.rudernlinz.at/planned findest du einen QR Code, den du mit deiner Bankapp scannen kannst um alle Eingaben bereits ausgefüllt zu haben.\n\n\
Falls die Berechnung nicht stimmt (korrekte Preise findest du unter https://rudernlinz.at/unser-verein/gebuhren/) melde dich bitte bei it@rudernlinz.at. @Studenten: Bitte die aktuelle Studienbestätigung an it@rudernlinz.at schicken.\n\n\ Falls die Berechnung nicht stimmt (korrekte Preise findest du unter https://rudernlinz.at/unser-verein/gebuhren/) melde dich bitte bei kassier@rudernlinz.at. @Studenten: Bitte die aktuelle Studienbestätigung an kassier@rudernlinz.at schicken.\n\n\
Wenn du die Vereinsgebühren schon bezahlt hast, kannst du diese Mail einfach ignorieren.\n\n Wenn du die Vereinsgebühren schon bezahlt hast, kannst du diese Mail einfach ignorieren.\n\n
Beste Grüße\n\ Beste Grüße\n\
Der Vorstand Der Vorstand");
");
let mut email = Message::builder() let mut email = Message::builder()
.from( .from(
"ASKÖ Ruderverein Donau Linz <no-reply@rudernlinz.at>" "ASKÖ Ruderverein Donau Linz <no-reply@rudernlinz.at>"
@ -208,7 +212,7 @@ Der Vorstand
.unwrap(), .unwrap(),
) )
.reply_to( .reply_to(
"ASKÖ Ruderverein Donau Linz <it@rudernlinz.at>" "ASKÖ Ruderverein Donau Linz <kassier@rudernlinz.at>"
.parse() .parse()
.unwrap(), .unwrap(),
) )
@ -253,11 +257,16 @@ Der Vorstand
} }
} }
pub async fn fees_final(db: &SqlitePool, smtp_pw: String) { pub async fn fees_final(db: &SqlitePool, smtp_pw: String, test: Option<User>) {
let users = User::all_payer_groups(db).await; let users = User::all_payer_groups(db).await;
for user in users { for user in users {
if let Some(test) = &test {
if user.id != test.id {
continue;
}
}
if let Some(fee) = user.fee(db).await { if let Some(fee) = user.fee(db).await {
if !fee.paid { if !fee.paid || test.is_some() {
let mut is_family = false; let mut is_family = false;
let mut send_to = String::new(); let mut send_to = String::new();
match Family::find_by_opt_id(db, user.family_id).await { match Family::find_by_opt_id(db, user.family_id).await {
@ -282,7 +291,7 @@ Der Vorstand
"Liebes Vereinsmitglied, \n\n\ "Liebes Vereinsmitglied, \n\n\
wir möchten darauf hinweisen, dass wir deinen Mitgliedsbeitrag für das laufende Jahr bislang nicht verbuchen konnten. Es besteht die Möglichkeit, dass es sich hierbei um ein Versehen unsererseits handelt. Solltest du den Betrag bereits überwiesen haben, bitte kurz auf diese E-Mail antworten, damit wir es richtigstellen können. wir möchten darauf hinweisen, dass wir deinen Mitgliedsbeitrag für das laufende Jahr bislang nicht verbuchen konnten. Es besteht die Möglichkeit, dass es sich hierbei um ein Versehen unsererseits handelt. Solltest du den Betrag bereits überwiesen haben, bitte kurz auf diese E-Mail antworten, damit wir es richtigstellen können.
Falls die Zahlung noch nicht erfolgt ist, bitten wir um umgehende Überweisung des ausstehenden Betrags, spätestens jedoch bis zum 31. März, auf unser Bankkonto.\n\n\ Falls die Zahlung noch nicht erfolgt ist, bitten wir um umgehende Überweisung des ausstehenden Betrags, spätestens jedoch binnen 14 Tagen, auf unser Bankkonto.\n\n\
Dein Vereinsbeitrag für das aktuelle Jahr beträgt {}", Dein Vereinsbeitrag für das aktuelle Jahr beträgt {}",
fees.sum_in_cents / 100, fees.sum_in_cents / 100,
); );
@ -317,7 +326,7 @@ Der Vorstand");
.unwrap(), .unwrap(),
) )
.reply_to( .reply_to(
"ASKÖ Ruderverein Donau Linz <it@rudernlinz.at>" "ASKÖ Ruderverein Donau Linz <kassier@rudernlinz.at>"
.parse() .parse()
.unwrap(), .unwrap(),
) )

View File

@ -1182,6 +1182,7 @@ special_user!(VorstandUser, +"admin", +"Vorstand");
special_user!(EventUser, +"manage_events"); special_user!(EventUser, +"manage_events");
special_user!(AllowedToEditPaymentStatusUser, +"kassier", +"admin"); special_user!(AllowedToEditPaymentStatusUser, +"kassier", +"admin");
special_user!(ManageUserUser, +"admin", +"schriftfuehrer"); special_user!(ManageUserUser, +"admin", +"schriftfuehrer");
special_user!(AllowedToSendFeeReminderUser, +"admin", +"schriftfuehrer", +"kassier");
special_user!(AllowedToUpdateTripToAlwaysBeShownUser, +"admin"); special_user!(AllowedToUpdateTripToAlwaysBeShownUser, +"admin");
#[derive(FromRow, Serialize, Deserialize, Clone, Debug)] #[derive(FromRow, Serialize, Deserialize, Clone, Debug)]

View File

@ -9,8 +9,8 @@ use sqlx::SqlitePool;
use crate::model::log::Log; use crate::model::log::Log;
use crate::model::mail::Mail; use crate::model::mail::Mail;
use crate::model::role::Role; use crate::model::role::Role;
use crate::model::user::UserWithDetails; use crate::model::user::VorstandUser;
use crate::model::user::{AdminUser, VorstandUser}; use crate::model::user::{AllowedToSendFeeReminderUser, UserWithDetails};
use crate::tera::Config; use crate::tera::Config;
#[get("/mail")] #[get("/mail")]
@ -35,21 +35,51 @@ async fn index(
} }
#[get("/mail/fee")] #[get("/mail/fee")]
async fn fee(db: &State<SqlitePool>, admin: AdminUser, config: &State<Config>) -> &'static str { async fn fee(
db: &State<SqlitePool>,
admin: AllowedToSendFeeReminderUser,
config: &State<Config>,
) -> Flash<Redirect> {
Log::create(db, format!("{admin:?} trying to send fee")).await; Log::create(db, format!("{admin:?} trying to send fee")).await;
Mail::fees(db, config.smtp_pw.clone()).await; Mail::fees(db, config.smtp_pw.clone(), None).await;
"SUCC" Log::create(db, "Mail successfully sent".into()).await;
Flash::success(Redirect::to("/admin/mail"), "Mail versendet")
}
#[get("/mail/fee/test")]
async fn fee_test(
db: &State<SqlitePool>,
admin: AllowedToSendFeeReminderUser,
config: &State<Config>,
) -> Flash<Redirect> {
Log::create(db, format!("{admin:?} trying to send test fee")).await;
Mail::fees(db, config.smtp_pw.clone(), Some(admin.user)).await;
Log::create(db, "Mail successfully sent".into()).await;
Flash::success(Redirect::to("/admin/mail"), "Mail versendet")
} }
#[get("/mail/fee-final")] #[get("/mail/fee-final")]
async fn fee_final( async fn fee_final(
db: &State<SqlitePool>, db: &State<SqlitePool>,
admin: AdminUser, admin: AllowedToSendFeeReminderUser,
config: &State<Config>, config: &State<Config>,
) -> &'static str { ) -> Flash<Redirect> {
Log::create(db, format!("{admin:?} trying to send fee_final")).await; Log::create(db, format!("{admin:?} trying to send fee_final")).await;
Mail::fees_final(db, config.smtp_pw.clone()).await; Mail::fees_final(db, config.smtp_pw.clone(), None).await;
"SUCC" Log::create(db, "Mail successfully sent".into()).await;
Flash::success(Redirect::to("/admin/mail"), "Mail versendet")
}
#[get("/mail/fee-final/test")]
async fn fee_final_test(
db: &State<SqlitePool>,
admin: AllowedToSendFeeReminderUser,
config: &State<Config>,
) -> Flash<Redirect> {
Log::create(db, format!("{admin:?} trying to send test fee_final")).await;
Mail::fees_final(db, config.smtp_pw.clone(), Some(admin.user)).await;
Log::create(db, "Mail successfully sent".into()).await;
Flash::success(Redirect::to("/admin/mail"), "Mail versendet")
} }
#[derive(FromForm, Debug)] #[derive(FromForm, Debug)]
@ -79,7 +109,7 @@ async fn update(
} }
pub fn routes() -> Vec<Route> { pub fn routes() -> Vec<Route> {
routes![index, update, fee, fee_final] routes![index, update, fee, fee_test, fee_final, fee_final_test]
} }
#[cfg(test)] #[cfg(test)]

View File

@ -127,7 +127,6 @@ async fn wikiauth(db: &State<SqlitePool>, login: Form<LoginForm<'_>>) -> String
fn unauthorized_error(req: &Request) -> Redirect { fn unauthorized_error(req: &Request) -> Redirect {
// Save the URL the user tried to access, to be able to go there once logged in // Save the URL the user tried to access, to be able to go there once logged in
let mut redirect_cookie = Cookie::new("redirect_url", format!("{}", req.uri())); let mut redirect_cookie = Cookie::new("redirect_url", format!("{}", req.uri()));
println!("{}", req.uri());
redirect_cookie.set_expires(OffsetDateTime::now_utc() + Duration::hours(1)); redirect_cookie.set_expires(OffsetDateTime::now_utc() + Duration::hours(1));
req.cookies().add_private(redirect_cookie); req.cookies().add_private(redirect_cookie);

View File

@ -22,6 +22,32 @@
<input type="submit" class="btn btn-primary" value="Abschicken" /> <input type="submit" class="btn btn-primary" value="Abschicken" />
</form> </form>
</div> </div>
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5"
role="alert">
<h2 class="h2">Mitglieds-Beitrags-Info</h2>
<div class="p-3 grid gap-3">
<a class="btn btn-primary" href="/admin/mail/fee/test">
Test-Mail an mich versenden
</a>
<a class="btn btn-alert" href="/admin/mail/fee"
onclick="return confirm('Hast du die Gebührenauflistung geprüft und willst du die Mail an alle ausschicken?');">
An ALLE Mitglieder versenden
</a>
</div>
</div>
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5"
role="alert">
<h2 class="h2">Unfreundliche Zahlungsaufforderung</h2>
<div class="p-3 grid gap-3">
<a class="btn btn-primary" href="/admin/mail/fee-final/test">
Test-Mail an mich versenden
</a>
<a class="btn btn-alert" href="/admin/mail/fee-final"
onclick="return confirm('Hast du die Gebührenauflistung geprüft, gecheckt ob alle die bereits bezahlt haben auch eingetragen wurden und willst du die Mail an alle, die noch nicht bezahlt haben ausschicken?');">
An ALLE Mitglieder versenden
</a>
</div>
</div>
</div> </div>
</div> </div>
{% endblock content %} {% endblock content %}