use super::User; use crate::{ model::family::Family, BOAT_STORAGE, EINSCHREIBGEBUEHR, FAMILY_THREE_OR_MORE, FAMILY_TWO, FOERDERND, REGULAR, RENNRUDERBEITRAG, STUDENT_OR_PUPIL, UNTERSTUETZEND, }; use chrono::{Datelike, Local, NaiveDate}; use serde::Serialize; use sqlx::SqlitePool; #[derive(Debug, Serialize)] pub struct Fee { pub sum_in_cents: i64, pub parts: Vec<(String, i64)>, pub name: String, pub user_ids: String, pub paid: bool, pub users: Vec, } impl Default for Fee { fn default() -> Self { Self::new() } } impl Fee { pub fn new() -> Self { Self { sum_in_cents: 0, name: "".into(), parts: Vec::new(), user_ids: "".into(), users: Vec::new(), paid: false, } } pub fn add(&mut self, desc: String, price_in_cents: i64) { self.sum_in_cents += price_in_cents; self.parts.push((desc, price_in_cents)); } pub fn add_person(&mut self, user: &User) { if !self.name.is_empty() { self.name.push_str(" + "); self.user_ids.push('&'); } self.name.push_str(&user.name); self.user_ids.push_str(&format!("user_ids[]={}", user.id)); self.users.push(user.clone()); } pub fn paid(&mut self) { self.paid = true; } pub fn merge(&mut self, fee: Fee) { for (desc, price_in_cents) in fee.parts { self.add(desc, price_in_cents); } } } impl User { pub async fn fee(&self, db: &SqlitePool) -> Option { if !self.has_role(db, "Donau Linz").await && !self.has_role(db, "Unterstützend").await && !self.has_role(db, "Förderndes Mitglied").await { return None; } if self.deleted { return None; } let mut fee = Fee::new(); if let Some(family) = Family::find_by_opt_id(db, self.family_id).await { for member in family.members(db).await { fee.add_person(&member); if member.has_role(db, "paid").await { fee.paid(); } fee.merge(member.fee_without_families(db).await); } if family.amount_family_members(db).await > 2 { fee.add("Familie 3+ Personen".into(), FAMILY_THREE_OR_MORE); } else { fee.add("Familie 2 Personen".into(), FAMILY_TWO); } } else { fee.add_person(self); if self.has_role(db, "paid").await { fee.paid(); } fee.merge(self.fee_without_families(db).await); } Some(fee) } async fn fee_without_families(&self, db: &SqlitePool) -> Fee { let mut fee = Fee::new(); if !self.has_role(db, "Donau Linz").await && !self.has_role(db, "Unterstützend").await && !self.has_role(db, "Förderndes Mitglied").await { return fee; } if self.has_role(db, "Rennrudern").await { if self.has_role(db, "half-rennrudern").await { fee.add("Rennruderbeitrag (1/2 Preis) ".into(), RENNRUDERBEITRAG / 2); } else if !self.has_role(db, "renntrainer").await { fee.add("Rennruderbeitrag".into(), RENNRUDERBEITRAG); } } let amount_boats = self.amount_boats(db).await; if amount_boats > 0 { fee.add( format!("{}x Bootsplatz", amount_boats), amount_boats * BOAT_STORAGE, ); } 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 { fee.add("Einschreibgebühr".into(), EINSCHREIBGEBUEHR); } } } let halfprice = if let Some(member_since_date) = &self.member_since_date { match NaiveDate::parse_from_str(member_since_date, "%Y-%m-%d") { Ok(member_since_date) => { let halfprice_startdate = NaiveDate::from_ymd_opt(Local::now().year(), 7, 1).unwrap(); member_since_date >= halfprice_startdate } Err(_) => false, } } else { false }; 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); } else if Family::find_by_opt_id(db, self.family_id).await.is_none() { if self.has_role(db, "Student").await || self.has_role(db, "Schüler").await { if halfprice { fee.add("Schüler/Student (Halbpreis)".into(), STUDENT_OR_PUPIL / 2); } else { fee.add("Schüler/Student".into(), STUDENT_OR_PUPIL); } } else if self.has_role(db, "Ehrenmitglied").await { fee.add("Ehrenmitglied".into(), 0); } else if halfprice { fee.add("Mitgliedsbeitrag (Halbpreis)".into(), REGULAR / 2); } else { fee.add("Mitgliedsbeitrag".into(), REGULAR); } } fee } }