Compare commits
8 Commits
main
..
f6eac7b32f
| Author | SHA1 | Date | |
|---|---|---|---|
| f6eac7b32f | |||
| b2157a31c5 | |||
| bfb3ae4b6e | |||
| 88a3e5f2d0 | |||
| 32c250536d | |||
| b9d0e2a2dc | |||
| b597898bdf | |||
| e90555214a |
@@ -31,7 +31,6 @@ jobs:
|
|||||||
run: cd frontend && npx playwright install && npx playwright test --workers 1 --reporter html,line
|
run: cd frontend && npx playwright install && npx playwright test --workers 1 --reporter html,line
|
||||||
- uses: actions/upload-artifact@v3
|
- uses: actions/upload-artifact@v3
|
||||||
if: always()
|
if: always()
|
||||||
continue-on-error: true
|
|
||||||
with:
|
with:
|
||||||
name: playwright-report
|
name: playwright-report
|
||||||
path: frontend/playwright-report/
|
path: frontend/playwright-report/
|
||||||
|
|||||||
Generated
+862
-759
File diff suppressed because it is too large
Load Diff
+4
-4
@@ -23,11 +23,11 @@ tera = { version = "1.20", features = ["date-locale"], optional = true}
|
|||||||
ics = "0.5"
|
ics = "0.5"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
lettre = "0.11"
|
lettre = "0.11"
|
||||||
csv = "1.3"
|
csv = "1.4"
|
||||||
itertools = "0.14"
|
itertools = "0.14"
|
||||||
job_scheduler_ng = "2.2"
|
job_scheduler_ng = "2.4"
|
||||||
ureq = { version = "3.0", features = ["json"] }
|
ureq = { version = "3.2", features = ["json"] }
|
||||||
regex = "1.11"
|
regex = "1.12"
|
||||||
urlencoding = "2.1"
|
urlencoding = "2.1"
|
||||||
|
|
||||||
[target.'cfg(not(windows))'.dependencies]
|
[target.'cfg(not(windows))'.dependencies]
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import { defineConfig, devices } from '@playwright/test';
|
|||||||
*/
|
*/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
testDir: './tests',
|
testDir: './tests',
|
||||||
timeout: process.env.CI ? 120000 : 30000,
|
|
||||||
/* Run tests in files in parallel */
|
/* Run tests in files in parallel */
|
||||||
fullyParallel: true,
|
fullyParallel: true,
|
||||||
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||||
|
|||||||
@@ -9,10 +9,21 @@ export async function resetDatabase(): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function login(page: Page, username: string, password: string): Promise<void> {
|
export async function login(page: Page, username: string, password: string): Promise<void> {
|
||||||
|
// Clear cookies to ensure clean state
|
||||||
await page.context().clearCookies();
|
await page.context().clearCookies();
|
||||||
|
|
||||||
|
// Navigate to auth page and wait for it to fully load
|
||||||
await page.goto("/auth", { waitUntil: 'load' });
|
await page.goto("/auth", { waitUntil: 'load' });
|
||||||
|
await page.waitForLoadState('networkidle');
|
||||||
|
|
||||||
|
await page.getByPlaceholder("Name").click();
|
||||||
await page.getByPlaceholder("Name").fill(username);
|
await page.getByPlaceholder("Name").fill(username);
|
||||||
|
await page.getByPlaceholder("Passwort").click();
|
||||||
await page.getByPlaceholder("Passwort").fill(password);
|
await page.getByPlaceholder("Passwort").fill(password);
|
||||||
await page.getByPlaceholder("Passwort").press("Enter");
|
|
||||||
await page.waitForURL(/\/(planned|log|$)/);
|
// Wait for navigation after form submission
|
||||||
|
await Promise.all([
|
||||||
|
page.waitForURL(/\/(planned|log|$)/, { timeout: 10000 }),
|
||||||
|
page.getByPlaceholder("Passwort").press("Enter")
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -8,7 +8,7 @@ use rot::rest;
|
|||||||
use rot::tera;
|
use rot::tera;
|
||||||
use rot::{scheduled, tera::Config};
|
use rot::{scheduled, tera::Config};
|
||||||
|
|
||||||
use sqlx::{pool::PoolOptions, sqlite::SqliteConnectOptions, ConnectOptions};
|
use sqlx::{ConnectOptions, pool::PoolOptions, sqlite::SqliteConnectOptions};
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate rocket;
|
extern crate rocket;
|
||||||
|
|||||||
@@ -271,52 +271,6 @@ ORDER BY created_at DESC
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn unfix(&self, db: &SqlitePool, tech_user: &User) -> Result<(), String> {
|
|
||||||
if self.user_id_verified.is_some() {
|
|
||||||
return Err("Reparatur wurde bereits verifiziert und kann nicht mehr rückgängig gemacht werden.".into());
|
|
||||||
}
|
|
||||||
if self.user_id_fixed.is_none() {
|
|
||||||
return Err("Reparatur wurde noch nicht eingetragen.".into());
|
|
||||||
}
|
|
||||||
|
|
||||||
let boat = Boat::find_by_id(db, self.boat_id as i32)
|
|
||||||
.await
|
|
||||||
.ok_or("Boot gibt's ned")?;
|
|
||||||
|
|
||||||
Log::create(
|
|
||||||
db,
|
|
||||||
format!("Unfix boat damage id={} by user {:?}", self.id, tech_user),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
sqlx::query!(
|
|
||||||
"UPDATE boat_damage SET user_id_fixed=NULL, fixed_at=NULL WHERE id=?",
|
|
||||||
self.id
|
|
||||||
)
|
|
||||||
.execute(db)
|
|
||||||
.await
|
|
||||||
.map_err(|e| e.to_string())?;
|
|
||||||
|
|
||||||
let technicals =
|
|
||||||
User::all_with_role(db, &Role::find_by_name(db, "tech").await.unwrap()).await;
|
|
||||||
for technical in technicals {
|
|
||||||
Notification::create(
|
|
||||||
db,
|
|
||||||
&technical,
|
|
||||||
&format!(
|
|
||||||
"{} hat die Reparatur des Bootschadens '{}' beim Boot '{}' als fehlerhaft eingetragen zurückgesetzt.",
|
|
||||||
tech_user.name, self.desc, boat.name,
|
|
||||||
),
|
|
||||||
"Bootsschaden-Reparatur rückgängig gemacht",
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn verified(
|
pub async fn verified(
|
||||||
&self,
|
&self,
|
||||||
db: &SqlitePool,
|
db: &SqlitePool,
|
||||||
|
|||||||
@@ -95,13 +95,13 @@ WHERE end_date >= ? AND start_date <= ?
|
|||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn next_future(db: &SqlitePool) -> Vec<BoatReservationWithDetails> {
|
pub async fn all_future(db: &SqlitePool) -> Vec<BoatReservationWithDetails> {
|
||||||
let boatreservations = sqlx::query_as!(
|
let boatreservations = sqlx::query_as!(
|
||||||
Self,
|
Self,
|
||||||
"
|
"
|
||||||
SELECT id, boat_id, start_date, end_date, time_desc, usage, user_id_applicant, user_id_confirmation, created_at
|
SELECT id, boat_id, start_date, end_date, time_desc, usage, user_id_applicant, user_id_confirmation, created_at
|
||||||
FROM boat_reservation
|
FROM boat_reservation
|
||||||
WHERE end_date >= CURRENT_DATE AND end_date <= date(CURRENT_DATE, '+3 days') ORDER BY end_date
|
WHERE end_date >= CURRENT_DATE ORDER BY end_date
|
||||||
"
|
"
|
||||||
)
|
)
|
||||||
.fetch_all(db)
|
.fetch_all(db)
|
||||||
@@ -158,10 +158,10 @@ WHERE end_date >= CURRENT_DATE AND end_date <= date(CURRENT_DATE, '+3 days') ORD
|
|||||||
|
|
||||||
grouped_reservations
|
grouped_reservations
|
||||||
}
|
}
|
||||||
pub async fn next_future_with_groups(
|
pub async fn all_future_with_groups(
|
||||||
db: &SqlitePool,
|
db: &SqlitePool,
|
||||||
) -> HashMap<String, Vec<BoatReservationWithDetails>> {
|
) -> HashMap<String, Vec<BoatReservationWithDetails>> {
|
||||||
let reservations = Self::next_future(db).await;
|
let reservations = Self::all_future(db).await;
|
||||||
Self::with_groups(reservations)
|
Self::with_groups(reservations)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-15
@@ -93,24 +93,11 @@ GROUP BY family.id;"
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn clean_families_without_members(db: &SqlitePool) {
|
pub async fn clean_families_without_members(db: &SqlitePool) {
|
||||||
sqlx::query(
|
|
||||||
"UPDATE user SET family_id = NULL
|
|
||||||
WHERE family_id IN (
|
|
||||||
SELECT family_id FROM user
|
|
||||||
WHERE family_id IS NOT NULL
|
|
||||||
GROUP BY family_id
|
|
||||||
HAVING COUNT(*) = 1
|
|
||||||
);",
|
|
||||||
)
|
|
||||||
.execute(db)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
sqlx::query(
|
sqlx::query(
|
||||||
"DELETE FROM family
|
"DELETE FROM family
|
||||||
WHERE id NOT IN (
|
WHERE id NOT IN (
|
||||||
SELECT DISTINCT family_id
|
SELECT DISTINCT family_id
|
||||||
FROM user
|
FROM user
|
||||||
WHERE family_id IS NOT NULL
|
WHERE family_id IS NOT NULL
|
||||||
);",
|
);",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ pub(crate) enum Member {
|
|||||||
Regular(User),
|
Regular(User),
|
||||||
Foerdernd(User),
|
Foerdernd(User),
|
||||||
Unterstuetzend(User),
|
Unterstuetzend(User),
|
||||||
NoMembership(User),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Member {
|
impl Member {
|
||||||
@@ -32,7 +31,7 @@ impl Member {
|
|||||||
} else if user.has_role(db, "Unterstützend").await {
|
} else if user.has_role(db, "Unterstützend").await {
|
||||||
Self::Unterstuetzend(user)
|
Self::Unterstuetzend(user)
|
||||||
} else {
|
} else {
|
||||||
Self::NoMembership(user)
|
panic!("User {user} has no membership_type!!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ mod fee;
|
|||||||
pub(crate) mod foerdernd;
|
pub(crate) mod foerdernd;
|
||||||
pub(crate) mod member;
|
pub(crate) mod member;
|
||||||
pub mod merge;
|
pub mod merge;
|
||||||
pub(crate) mod nomembership;
|
|
||||||
pub(crate) mod regular;
|
pub(crate) mod regular;
|
||||||
pub(crate) mod scheckbuch;
|
pub(crate) mod scheckbuch;
|
||||||
pub(crate) mod schnupperant;
|
pub(crate) mod schnupperant;
|
||||||
|
|||||||
@@ -1,233 +0,0 @@
|
|||||||
use super::foerdernd::FoerderndUser;
|
|
||||||
use super::regular::RegularUser;
|
|
||||||
use super::scheckbuch::ScheckbuchUser;
|
|
||||||
use super::unterstuetzend::UnterstuetzendUser;
|
|
||||||
use super::{ManageUserUser, User};
|
|
||||||
use crate::NonEmptyString;
|
|
||||||
use crate::model::activity::ActivityBuilder;
|
|
||||||
use crate::model::role::Role;
|
|
||||||
use crate::model::notification::Notification;
|
|
||||||
use chrono::NaiveDate;
|
|
||||||
use rocket::fs::TempFile;
|
|
||||||
use sqlx::SqlitePool;
|
|
||||||
use std::fmt::Display;
|
|
||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
pub(crate) struct NoMembershipUser {
|
|
||||||
pub(crate) user: User,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for NoMembershipUser {
|
|
||||||
type Target = User;
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.user
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for NoMembershipUser {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(f, "{}", self.user.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NoMembershipUser {
|
|
||||||
pub(crate) async fn new(db: &SqlitePool, user: &User) -> Option<Self> {
|
|
||||||
if ScheckbuchUser::new(db, user).await.is_some() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
if user.has_role(db, "schnupper-interessierte").await {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
if user.has_role(db, "schnupperant").await {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
if user.has_role(db, "Donau Linz").await {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
if user.has_role(db, "Förderndes Mitglied").await {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
if user.has_role(db, "Unterstützend").await {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
Some(Self { user: user.clone() })
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn set_data_for_clubmember(
|
|
||||||
&self,
|
|
||||||
db: &SqlitePool,
|
|
||||||
changed_by: &ManageUserUser,
|
|
||||||
member_since: &NaiveDate,
|
|
||||||
birthdate: &NaiveDate,
|
|
||||||
phone: NonEmptyString,
|
|
||||||
address: NonEmptyString,
|
|
||||||
membership_pdf: &TempFile<'_>,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
self.user.update_birthdate(db, changed_by, birthdate).await;
|
|
||||||
self.user
|
|
||||||
.update_member_since(db, changed_by, member_since)
|
|
||||||
.await;
|
|
||||||
self.user.update_phone(db, changed_by, &phone).await;
|
|
||||||
self.user.update_address(db, changed_by, &address).await;
|
|
||||||
self.user
|
|
||||||
.add_membership_pdf(db, changed_by, membership_pdf)
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn convert_to_regular_user(
|
|
||||||
self,
|
|
||||||
db: &SqlitePool,
|
|
||||||
smtp_pw: &str,
|
|
||||||
changed_by: &ManageUserUser,
|
|
||||||
member_since: &NaiveDate,
|
|
||||||
birthdate: &NaiveDate,
|
|
||||||
phone: NonEmptyString,
|
|
||||||
address: NonEmptyString,
|
|
||||||
membership_pdf: &TempFile<'_>,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
self.set_data_for_clubmember(
|
|
||||||
db,
|
|
||||||
changed_by,
|
|
||||||
member_since,
|
|
||||||
birthdate,
|
|
||||||
phone,
|
|
||||||
address,
|
|
||||||
membership_pdf,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let regular = Role::find_by_name(db, "Donau Linz").await.unwrap();
|
|
||||||
self.user.add_role(db, changed_by, ®ular).await?;
|
|
||||||
|
|
||||||
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, {} hatte keinen Mitgliedsstatus und ist nun seit {} ein neues reguläres Mitglied. 🎉",
|
|
||||||
self.name,
|
|
||||||
member_since
|
|
||||||
),
|
|
||||||
"Neues Vereinsmitglied",
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
ActivityBuilder::new(&format!(
|
|
||||||
"{changed_by} hat den User ohne Mitgliedsstatus {self} auf ein reguläres Mitglied upgegraded! Die Steuerpersonen wurden via Notification informiert."
|
|
||||||
))
|
|
||||||
.user(&self)
|
|
||||||
.save(db)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn convert_to_unterstuetzend_user(
|
|
||||||
self,
|
|
||||||
db: &SqlitePool,
|
|
||||||
smtp_pw: &str,
|
|
||||||
changed_by: &ManageUserUser,
|
|
||||||
member_since: &NaiveDate,
|
|
||||||
birthdate: &NaiveDate,
|
|
||||||
phone: NonEmptyString,
|
|
||||||
address: NonEmptyString,
|
|
||||||
membership_pdf: &TempFile<'_>,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
self.set_data_for_clubmember(
|
|
||||||
db,
|
|
||||||
changed_by,
|
|
||||||
member_since,
|
|
||||||
birthdate,
|
|
||||||
phone,
|
|
||||||
address,
|
|
||||||
membership_pdf,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let unterstuetzend = Role::find_by_name(db, "Unterstützend").await.unwrap();
|
|
||||||
self.user.add_role(db, changed_by, &unterstuetzend).await?;
|
|
||||||
|
|
||||||
let unterstuetzend = UnterstuetzendUser::new(db, &self.user).await.unwrap();
|
|
||||||
unterstuetzend
|
|
||||||
.send_welcome_mail_to_user(db, smtp_pw)
|
|
||||||
.await?;
|
|
||||||
if let Some(vorstand) = Role::find_by_name(db, "vorstand").await {
|
|
||||||
Notification::create_for_role(
|
|
||||||
db,
|
|
||||||
&vorstand,
|
|
||||||
&format!(
|
|
||||||
"Lieber Vorstand, {} hatte keinen Mitgliedsstatus und ist nun seit {} ein neues unterstützendes Mitglied.",
|
|
||||||
self.name,
|
|
||||||
member_since
|
|
||||||
),
|
|
||||||
"Neues unterstützendes Vereinsmitglied",
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
ActivityBuilder::new(&format!(
|
|
||||||
"{changed_by} hat den User ohne Mitgliedsstatus {self} auf ein unterstützendes Mitglied upgegraded!"
|
|
||||||
))
|
|
||||||
.user(&self)
|
|
||||||
.save(db)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn convert_to_foerdernd_user(
|
|
||||||
self,
|
|
||||||
db: &SqlitePool,
|
|
||||||
smtp_pw: &str,
|
|
||||||
changed_by: &ManageUserUser,
|
|
||||||
member_since: &NaiveDate,
|
|
||||||
birthdate: &NaiveDate,
|
|
||||||
phone: NonEmptyString,
|
|
||||||
address: NonEmptyString,
|
|
||||||
membership_pdf: &TempFile<'_>,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
self.set_data_for_clubmember(
|
|
||||||
db,
|
|
||||||
changed_by,
|
|
||||||
member_since,
|
|
||||||
birthdate,
|
|
||||||
phone,
|
|
||||||
address,
|
|
||||||
membership_pdf,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let foerdernd = Role::find_by_name(db, "Förderndes Mitglied").await.unwrap();
|
|
||||||
self.user.add_role(db, changed_by, &foerdernd).await?;
|
|
||||||
|
|
||||||
let foerdernd = FoerderndUser::new(db, &self.user).await.unwrap();
|
|
||||||
foerdernd.send_welcome_mail_to_user(db, smtp_pw).await?;
|
|
||||||
if let Some(vorstand) = Role::find_by_name(db, "vorstand").await {
|
|
||||||
Notification::create_for_role(
|
|
||||||
db,
|
|
||||||
&vorstand,
|
|
||||||
&format!(
|
|
||||||
"Lieber Vorstand, {} hatte keinen Mitgliedsstatus und ist nun seit {} ein neues förderndes Mitglied.",
|
|
||||||
self.name,
|
|
||||||
member_since
|
|
||||||
),
|
|
||||||
"Neues förderndes Vereinsmitglied",
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
ActivityBuilder::new(&format!(
|
|
||||||
"{changed_by} hat den User ohne Mitgliedsstatus {self} auf ein förderndes Mitglied upgegraded!"
|
|
||||||
))
|
|
||||||
.user(&self)
|
|
||||||
.save(db)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+2
-113
@@ -8,9 +8,8 @@ use crate::{
|
|||||||
role::Role,
|
role::Role,
|
||||||
user::{
|
user::{
|
||||||
clubmember::ClubMemberUser, foerdernd::FoerderndUser, member::Member,
|
clubmember::ClubMemberUser, foerdernd::FoerderndUser, member::Member,
|
||||||
nomembership::NoMembershipUser, regular::RegularUser, scheckbuch::ScheckbuchUser,
|
regular::RegularUser, scheckbuch::ScheckbuchUser, schnupperant::SchnupperantUser,
|
||||||
schnupperant::SchnupperantUser, schnupperinterest::SchnupperInterestUser,
|
schnupperinterest::SchnupperInterestUser, unterstuetzend::UnterstuetzendUser,
|
||||||
unterstuetzend::UnterstuetzendUser,
|
|
||||||
AdminUser, AllowedToEditPaymentStatusUser, ManageUserUser, User, UserWithDetails,
|
AdminUser, AllowedToEditPaymentStatusUser, ManageUserUser, User, UserWithDetails,
|
||||||
UserWithMembershipPdf, UserWithRolesAndMembershipPdf, VorstandUser,
|
UserWithMembershipPdf, UserWithRolesAndMembershipPdf, VorstandUser,
|
||||||
},
|
},
|
||||||
@@ -1142,115 +1141,6 @@ async fn scheckbook_to_regular(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/user/<id>/nomembership-to-regular", data = "<data>")]
|
|
||||||
async fn nomembership_to_regular(
|
|
||||||
db: &State<SqlitePool>,
|
|
||||||
data: Form<ScheckToRegularForm<'_>>,
|
|
||||||
admin: ManageUserUser,
|
|
||||||
config: &State<Config>,
|
|
||||||
id: i32,
|
|
||||||
) -> Flash<Redirect> {
|
|
||||||
let Some(user) = User::find_by_id(db, id).await else {
|
|
||||||
return Flash::error(
|
|
||||||
Redirect::to("/admin/user"),
|
|
||||||
format!("User with ID {} does not exist!", id),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
let Ok(birthdate) = NaiveDate::parse_from_str(&data.birthdate, "%Y-%m-%d") else {
|
|
||||||
return Flash::error(
|
|
||||||
Redirect::to(format!("/admin/user/{id}")),
|
|
||||||
format!(
|
|
||||||
"Geburtsdatum {} ist nicht im YYYY-MM-DD Format",
|
|
||||||
&data.birthdate
|
|
||||||
),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
let Ok(member_since) = NaiveDate::parse_from_str(&data.member_since, "%Y-%m-%d") else {
|
|
||||||
return Flash::error(
|
|
||||||
Redirect::to(format!("/admin/user/{id}")),
|
|
||||||
format!(
|
|
||||||
"Beitrittsdatum {} ist nicht im YYYY-MM-DD Format",
|
|
||||||
&data.birthdate
|
|
||||||
),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
let Some(user) = NoMembershipUser::new(db, &user).await else {
|
|
||||||
return Flash::error(
|
|
||||||
Redirect::to(format!("/admin/user/{id}")),
|
|
||||||
"User hat keinen fehlenden Mitgliedsstatus",
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
let Ok(phone) = data.phone.clone().try_into() else {
|
|
||||||
return Flash::error(
|
|
||||||
Redirect::to(format!("/admin/user/{id}")),
|
|
||||||
"Vereinsmitglied braucht eine Telefonnummer",
|
|
||||||
);
|
|
||||||
};
|
|
||||||
let Ok(address) = data.address.clone().try_into() else {
|
|
||||||
return Flash::error(
|
|
||||||
Redirect::to(format!("/admin/user/{id}")),
|
|
||||||
"Vereinsmitglied braucht eine Adresse",
|
|
||||||
);
|
|
||||||
};
|
|
||||||
let response = match &*data.membertype {
|
|
||||||
"regular" => {
|
|
||||||
user.convert_to_regular_user(
|
|
||||||
db,
|
|
||||||
&config.smtp_pw,
|
|
||||||
&admin,
|
|
||||||
&member_since,
|
|
||||||
&birthdate,
|
|
||||||
phone,
|
|
||||||
address,
|
|
||||||
&data.membership_pdf,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
"unterstuetzend" => {
|
|
||||||
user.convert_to_unterstuetzend_user(
|
|
||||||
db,
|
|
||||||
&config.smtp_pw,
|
|
||||||
&admin,
|
|
||||||
&member_since,
|
|
||||||
&birthdate,
|
|
||||||
phone,
|
|
||||||
address,
|
|
||||||
&data.membership_pdf,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
"foerdernd" => {
|
|
||||||
user.convert_to_foerdernd_user(
|
|
||||||
db,
|
|
||||||
&config.smtp_pw,
|
|
||||||
&admin,
|
|
||||||
&member_since,
|
|
||||||
&birthdate,
|
|
||||||
phone,
|
|
||||||
address,
|
|
||||||
&data.membership_pdf,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
return Flash::error(
|
|
||||||
Redirect::to(format!("/admin/user/{id}")),
|
|
||||||
"Membertype gibts ned",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
match response {
|
|
||||||
Ok(_) => Flash::success(
|
|
||||||
Redirect::to(format!("/admin/user/{}", id)),
|
|
||||||
"Mitgliedstyp umgewandelt und Infos versendet",
|
|
||||||
),
|
|
||||||
Err(e) => Flash::error(Redirect::to(format!("/admin/user/{}", id)), e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(FromForm, Debug)]
|
#[derive(FromForm, Debug)]
|
||||||
pub struct ChangeMembertypeForm {
|
pub struct ChangeMembertypeForm {
|
||||||
membertype: String,
|
membertype: String,
|
||||||
@@ -1664,7 +1554,6 @@ pub fn routes() -> Vec<Route> {
|
|||||||
remove_role,
|
remove_role,
|
||||||
// Moves
|
// Moves
|
||||||
scheckbook_to_regular,
|
scheckbook_to_regular,
|
||||||
nomembership_to_regular,
|
|
||||||
schnupperant_to_regular,
|
schnupperant_to_regular,
|
||||||
schnupperant_to_scheckbook,
|
schnupperant_to_scheckbook,
|
||||||
schnupperinterest_to_schnupperant,
|
schnupperinterest_to_schnupperant,
|
||||||
|
|||||||
@@ -152,25 +152,6 @@ pub struct FormBoatDamageVerified<'r> {
|
|||||||
desc: &'r str,
|
desc: &'r str,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/<boatdamage_id>/unfix")]
|
|
||||||
async fn unfix(
|
|
||||||
db: &State<SqlitePool>,
|
|
||||||
boatdamage_id: i32,
|
|
||||||
techuser: TechUser,
|
|
||||||
) -> Flash<Redirect> {
|
|
||||||
let Some(boatdamage) = BoatDamage::find_by_id(db, boatdamage_id).await else {
|
|
||||||
return Flash::error(Redirect::to("/boatdamage"), "Bootsschaden nicht gefunden.");
|
|
||||||
};
|
|
||||||
let user: User = techuser.into_inner();
|
|
||||||
match boatdamage.unfix(db, &user).await {
|
|
||||||
Ok(_) => Flash::success(
|
|
||||||
Redirect::to("/boatdamage"),
|
|
||||||
"Reparatur wurde zurückgesetzt.",
|
|
||||||
),
|
|
||||||
Err(e) => Flash::error(Redirect::to("/boatdamage"), format!("Fehler: {e}")),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[post("/<boatdamage_id>/verified", data = "<data>")]
|
#[post("/<boatdamage_id>/verified", data = "<data>")]
|
||||||
async fn verified<'r>(
|
async fn verified<'r>(
|
||||||
db: &State<SqlitePool>,
|
db: &State<SqlitePool>,
|
||||||
@@ -195,7 +176,6 @@ pub fn routes() -> Vec<Route> {
|
|||||||
index_kiosk,
|
index_kiosk,
|
||||||
create,
|
create,
|
||||||
fixed,
|
fixed,
|
||||||
unfix,
|
|
||||||
verified,
|
verified,
|
||||||
create_from_kiosk
|
create_from_kiosk
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
use chrono::NaiveDate;
|
use chrono::NaiveDate;
|
||||||
use rocket::{
|
use rocket::{
|
||||||
|
FromForm, Route, State,
|
||||||
form::Form,
|
form::Form,
|
||||||
get, post,
|
get, post,
|
||||||
request::FlashMessage,
|
request::FlashMessage,
|
||||||
response::{Flash, Redirect},
|
response::{Flash, Redirect},
|
||||||
routes, FromForm, Route, State,
|
routes,
|
||||||
};
|
};
|
||||||
use rocket_dyn_templates::Template;
|
use rocket_dyn_templates::Template;
|
||||||
use sqlx::SqlitePool;
|
use sqlx::SqlitePool;
|
||||||
@@ -26,7 +27,7 @@ async fn index_kiosk(
|
|||||||
flash: Option<FlashMessage<'_>>,
|
flash: Option<FlashMessage<'_>>,
|
||||||
_kiosk: KioskCookie,
|
_kiosk: KioskCookie,
|
||||||
) -> Template {
|
) -> Template {
|
||||||
let boatreservations = BoatReservation::next_future(db).await;
|
let boatreservations = BoatReservation::all_future(db).await;
|
||||||
|
|
||||||
let mut context = Context::new();
|
let mut context = Context::new();
|
||||||
if let Some(msg) = flash {
|
if let Some(msg) = flash {
|
||||||
@@ -55,7 +56,7 @@ async fn index(
|
|||||||
flash: Option<FlashMessage<'_>>,
|
flash: Option<FlashMessage<'_>>,
|
||||||
user: DonauLinzUser,
|
user: DonauLinzUser,
|
||||||
) -> Template {
|
) -> Template {
|
||||||
let boatreservations = BoatReservation::next_future(db).await;
|
let boatreservations = BoatReservation::all_future(db).await;
|
||||||
|
|
||||||
let mut context = Context::new();
|
let mut context = Context::new();
|
||||||
if let Some(msg) = flash {
|
if let Some(msg) = flash {
|
||||||
|
|||||||
+1
-1
@@ -114,7 +114,7 @@ async fn index(db: &SqlitePool, flash: Option<FlashMessage<'_>>, mut context: Co
|
|||||||
context.insert("planned_trips", &Trip::get_for_today(db).await);
|
context.insert("planned_trips", &Trip::get_for_today(db).await);
|
||||||
context.insert(
|
context.insert(
|
||||||
"reservations",
|
"reservations",
|
||||||
&BoatReservation::next_future_with_groups(db).await,
|
&BoatReservation::all_future_with_groups(db).await,
|
||||||
);
|
);
|
||||||
context.insert("coxes", &coxes);
|
context.insert("coxes", &coxes);
|
||||||
context.insert("users", &users);
|
context.insert("users", &users);
|
||||||
|
|||||||
@@ -73,8 +73,6 @@
|
|||||||
Förderndes Vereinsmitglied
|
Förderndes Vereinsmitglied
|
||||||
{% elif "Unterstuetzend" in member %}
|
{% elif "Unterstuetzend" in member %}
|
||||||
Unterstützendes Vereinsmitglied
|
Unterstützendes Vereinsmitglied
|
||||||
{% elif "NoMembership" in member %}
|
|
||||||
⚠️ Kein Mitgliedsstatus!
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</small>
|
</small>
|
||||||
</h2>
|
</h2>
|
||||||
@@ -230,19 +228,8 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% elif "NoMembership" in member %}
|
|
||||||
{% if allowed_to_edit %}
|
|
||||||
<div class="grid pt-3">
|
|
||||||
<a href="/admin/user/{{ user.id }}/delete"
|
|
||||||
class="btn btn-alert"
|
|
||||||
onclick="return confirm('Willst du die Daten von {{ user.name }} wirklich löschen?');">
|
|
||||||
{% include "includes/delete-icon" %}
|
|
||||||
Daten löschen
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if "Scheckbuch" in member or "Schnupperant" in member or "NoMembership" in member %}
|
{% if "Scheckbuch" in member or "Schnupperant" in member %}
|
||||||
{% if allowed_to_edit %}
|
{% if allowed_to_edit %}
|
||||||
<div class="grid gap-3 pb-3 mt-3">
|
<div class="grid gap-3 pb-3 mt-3">
|
||||||
<button type="button"
|
<button type="button"
|
||||||
@@ -270,8 +257,6 @@
|
|||||||
{% set action = "scheckbook-to-regular" %}
|
{% set action = "scheckbook-to-regular" %}
|
||||||
{% elif "Schnupperant" in member %}
|
{% elif "Schnupperant" in member %}
|
||||||
{% set action = "schnupperant-to-regular" %}
|
{% set action = "schnupperant-to-regular" %}
|
||||||
{% elif "NoMembership" in member %}
|
|
||||||
{% set action = "nomembership-to-regular" %}
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<form action="/admin/user/{{ user.id }}/{{ action }}"
|
<form action="/admin/user/{{ user.id }}/{{ action }}"
|
||||||
method="post"
|
method="post"
|
||||||
|
|||||||
@@ -55,13 +55,6 @@
|
|||||||
</small>
|
</small>
|
||||||
{% if boatdamage.fixed_at %}
|
{% if boatdamage.fixed_at %}
|
||||||
<small class="block text-gray-600 dark:text-gray-100">Repariert von {{ boatdamage.user_fixed.name }} am/um {{ boatdamage.fixed_at | date(format='%d.%m.%Y (%H:%M)') }}</small>
|
<small class="block text-gray-600 dark:text-gray-100">Repariert von {{ boatdamage.user_fixed.name }} am/um {{ boatdamage.fixed_at | date(format='%d.%m.%Y (%H:%M)') }}</small>
|
||||||
{% if loggedin_user and "tech" in loggedin_user.roles and not boatdamage.verified_at %}
|
|
||||||
<form action="/boatdamage/{{ boatdamage.id }}/unfix" method="post" class="mt-1">
|
|
||||||
<input type="submit"
|
|
||||||
class="btn btn-dark text-sm"
|
|
||||||
value="Reparatur rückgängig" />
|
|
||||||
</form>
|
|
||||||
{% endif %}
|
|
||||||
{% else %}
|
{% else %}
|
||||||
{% if loggedin_user and loggedin_user.allowed_to_steer %}
|
{% if loggedin_user and loggedin_user.allowed_to_steer %}
|
||||||
<form action="/boatdamage/{{ boatdamage.id }}/fixed"
|
<form action="/boatdamage/{{ boatdamage.id }}/fixed"
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ function setChoiceByLabel(choicesInstance, label) {
|
|||||||
{% endmacro plannedtrips %}
|
{% endmacro plannedtrips %}
|
||||||
{% macro boatreservation() %}
|
{% macro boatreservation() %}
|
||||||
<div class="bg-white dark:bg-primary-900 rounded-md shadow pb-2 mt-3">
|
<div class="bg-white dark:bg-primary-900 rounded-md shadow pb-2 mt-3">
|
||||||
<h2 class="h2">Reservierungen<br /><small>in den nächsten 3 Tagen</small></h2>
|
<h2 class="h2">Reservierungen ({{ reservations | length }})</h2>
|
||||||
<div class="grid grid-cols-1 gap-3 mb-3 w-full">
|
<div class="grid grid-cols-1 gap-3 mb-3 w-full">
|
||||||
{% for _, reservations_for_event in reservations %}
|
{% for _, reservations_for_event in reservations %}
|
||||||
{% set reservation = reservations_for_event[0] %}
|
{% set reservation = reservations_for_event[0] %}
|
||||||
|
|||||||
Reference in New Issue
Block a user