kassier-role #685

Merged
philipp merged 3 commits from kassier-role into main 2024-08-19 11:24:10 +02:00
15 changed files with 104 additions and 296 deletions

View File

@ -9,6 +9,7 @@ INSERT INTO "role" (name) VALUES ('paid');
INSERT INTO "role" (name) VALUES ('Vorstand'); INSERT INTO "role" (name) VALUES ('Vorstand');
INSERT INTO "role" (name) VALUES ('Bootsführer'); INSERT INTO "role" (name) VALUES ('Bootsführer');
INSERT INTO "role" (name) VALUES ('schnupperant'); INSERT INTO "role" (name) VALUES ('schnupperant');
INSERT INTO "role" (name) VALUES ('kassier');
INSERT INTO "user" (name, pw) VALUES('admin', '$argon2id$v=19$m=19456,t=2,p=1$dS/X5/sPEKTj4Rzs/CuvzQ$4P4NCw4Ukhv80/eQYTsarHhnw61JuL1KMx/L9dm82YM'); INSERT INTO "user" (name, pw) VALUES('admin', '$argon2id$v=19$m=19456,t=2,p=1$dS/X5/sPEKTj4Rzs/CuvzQ$4P4NCw4Ukhv80/eQYTsarHhnw61JuL1KMx/L9dm82YM');
INSERT INTO "user_role" (user_id, role_id) VALUES(1,1); INSERT INTO "user_role" (user_id, role_id) VALUES(1,1);
INSERT INTO "user_role" (user_id, role_id) VALUES(1,2); INSERT INTO "user_role" (user_id, role_id) VALUES(1,2);

View File

@ -918,266 +918,82 @@ impl<'r> FromRequest<'r> for User {
} }
} }
pub struct TechUser { /// Creates a struct named $name. Allows to be created from a user, if one of the specified $roles are active for the user.
pub(crate) user: User, macro_rules! special_user {
} ($name:ident, $($role:tt)*) => {
#[derive(Debug)]
pub struct $name {
pub(crate) user: User,
}
impl Deref for TechUser { impl Deref for $name {
type Target = User; type Target = User;
fn deref(&self) -> &Self::Target {
&self.user
}
}
fn deref(&self) -> &Self::Target { impl $name {
&self.user pub fn into_inner(self) -> User {
} self.user
} }
}
#[async_trait] #[async_trait]
impl<'r> FromRequest<'r> for TechUser { impl<'r> FromRequest<'r> for $name {
type Error = LoginError; type Error = LoginError;
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> { let db = req.rocket().state::<SqlitePool>().unwrap();
let db = req.rocket().state::<SqlitePool>().unwrap(); match User::from_request(req).await {
Outcome::Success(user) => {
match User::from_request(req).await { if special_user!(@check_roles user, db, $($role)*) {
Outcome::Success(user) => { Outcome::Success($name { user })
if user.has_role(db, "tech").await { } else {
Outcome::Success(TechUser { user }) Outcome::Forward(Status::Forbidden)
} else { }
Outcome::Error((Status::Forbidden, LoginError::NotACox)) }
Outcome::Error(f) => Outcome::Error(f),
Outcome::Forward(f) => Outcome::Forward(f),
} }
} }
Outcome::Error(f) => Outcome::Error(f),
Outcome::Forward(f) => Outcome::Forward(f),
} }
}
}
pub struct CoxUser { impl $name {
pub(crate) user: User, pub async fn new(db: &SqlitePool, user: User) -> Option<Self> {
} if special_user!(@check_roles user, db, $($role)*) {
Some($name { user })
impl Deref for CoxUser {
type Target = User;
fn deref(&self) -> &Self::Target {
&self.user
}
}
impl CoxUser {
pub async fn new(db: &SqlitePool, user: User) -> Option<Self> {
if user.has_role(db, "cox").await {
Some(CoxUser { user })
} else {
None
}
}
}
#[async_trait]
impl<'r> FromRequest<'r> for CoxUser {
type Error = LoginError;
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
let db = req.rocket().state::<SqlitePool>().unwrap();
match User::from_request(req).await {
Outcome::Success(user) => {
if user.has_role(db, "cox").await {
Outcome::Success(CoxUser { user })
} else { } else {
Outcome::Error((Status::Forbidden, LoginError::NotACox)) None
} }
} }
Outcome::Error(f) => Outcome::Error(f),
Outcome::Forward(f) => Outcome::Forward(f),
} }
} };
} (@check_roles $user:ident, $db:ident, $(+$role:expr),* $(,-$neg_role:expr)*) => {
{
#[derive(Debug, Serialize, Deserialize)] let mut has_positive_role = false;
pub struct AdminUser { $(
pub(crate) user: User, if $user.has_role($db, $role).await {
} has_positive_role = true;
#[async_trait]
impl<'r> FromRequest<'r> for AdminUser {
type Error = LoginError;
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
let db = req.rocket().state::<SqlitePool>().unwrap();
match User::from_request(req).await {
Outcome::Success(user) => {
if user.has_role(db, "admin").await {
Outcome::Success(AdminUser { user })
} else {
Outcome::Forward(Status::Forbidden)
} }
} )*
Outcome::Error(f) => Outcome::Error(f), has_positive_role
Outcome::Forward(f) => Outcome::Forward(f), $(
&& !$user.has_role($db, $neg_role).await
)*
} }
} };
} }
#[derive(Debug, Serialize, Deserialize)] special_user!(TechUser, +"tech");
pub struct AllowedForPlannedTripsUser(pub(crate) User); special_user!(CoxUser, +"cox");
special_user!(AdminUser, +"admin");
special_user!(AllowedForPlannedTripsUser, +"Donau Linz", +"scheckbuch");
special_user!(DonauLinzUser, +"Donau Linz", -"Unterstützend", -"Förderndes Mitglied");
special_user!(SchnupperBetreuerUser, +"schnupper-betreuer");
special_user!(VorstandUser, +"Vorstand");
special_user!(EventUser, +"manage_events");
special_user!(AllowedToEditPaymentStatusUser, +"kassier", +"admin");
#[async_trait]
impl<'r> FromRequest<'r> for AllowedForPlannedTripsUser {
type Error = LoginError;
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
let db = req.rocket().state::<SqlitePool>().unwrap();
match User::from_request(req).await {
Outcome::Success(user) => {
if user.has_role(db, "Donau Linz").await | user.has_role(db, "scheckbuch").await {
Outcome::Success(AllowedForPlannedTripsUser(user))
} else {
Outcome::Error((Status::Forbidden, LoginError::NotACox))
}
}
Outcome::Error(f) => Outcome::Error(f),
Outcome::Forward(f) => Outcome::Forward(f),
}
}
}
impl From<AllowedForPlannedTripsUser> for User {
fn from(val: AllowedForPlannedTripsUser) -> Self {
val.0
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct DonauLinzUser(pub(crate) User);
impl From<DonauLinzUser> for User {
fn from(val: DonauLinzUser) -> Self {
val.0
}
}
impl Deref for DonauLinzUser {
type Target = User;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[async_trait]
impl<'r> FromRequest<'r> for DonauLinzUser {
type Error = LoginError;
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
let db = req.rocket().state::<SqlitePool>().unwrap();
match User::from_request(req).await {
Outcome::Success(user) => {
if user.has_role(db, "Donau Linz").await
&& !user.has_role(db, "Unterstützend").await
&& !user.has_role(db, "Förderndes Mitglied").await
{
Outcome::Success(DonauLinzUser(user))
} else {
Outcome::Error((Status::Forbidden, LoginError::NotACox))
}
}
Outcome::Error(f) => Outcome::Error(f),
Outcome::Forward(f) => Outcome::Forward(f),
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct SchnupperBetreuerUser(pub(crate) User);
impl From<SchnupperBetreuerUser> for User {
fn from(val: SchnupperBetreuerUser) -> Self {
val.0
}
}
impl Deref for SchnupperBetreuerUser {
type Target = User;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[async_trait]
impl<'r> FromRequest<'r> for SchnupperBetreuerUser {
type Error = LoginError;
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
let db = req.rocket().state::<SqlitePool>().unwrap();
match User::from_request(req).await {
Outcome::Success(user) => {
if user.has_role(db, "schnupper-betreuer").await {
Outcome::Success(SchnupperBetreuerUser(user))
} else {
Outcome::Forward(Status::Forbidden)
}
}
Outcome::Error(f) => Outcome::Error(f),
Outcome::Forward(f) => Outcome::Forward(f),
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct VorstandUser(pub(crate) User);
impl From<VorstandUser> for User {
fn from(val: VorstandUser) -> Self {
val.0
}
}
impl Deref for VorstandUser {
type Target = User;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[async_trait]
impl<'r> FromRequest<'r> for VorstandUser {
type Error = LoginError;
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
let db = req.rocket().state::<SqlitePool>().unwrap();
match User::from_request(req).await {
Outcome::Success(user) => {
if user.has_role(db, "Vorstand").await {
Outcome::Success(VorstandUser(user))
} else {
Outcome::Forward(Status::Forbidden)
}
}
Outcome::Error(f) => Outcome::Error(f),
Outcome::Forward(f) => Outcome::Forward(f),
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct EventUser(pub(crate) User);
impl From<EventUser> for User {
fn from(val: EventUser) -> Self {
val.0
}
}
impl Deref for EventUser {
type Target = User;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(FromRow, Serialize, Deserialize, Clone, Debug)] #[derive(FromRow, Serialize, Deserialize, Clone, Debug)]
pub struct UserWithRolesAndMembershipPdf { pub struct UserWithRolesAndMembershipPdf {
#[serde(flatten)] #[serde(flatten)]
@ -1221,25 +1037,6 @@ impl UserWithMembershipPdf {
} }
} }
#[async_trait]
impl<'r> FromRequest<'r> for EventUser {
type Error = LoginError;
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
let db = req.rocket().state::<SqlitePool>().unwrap();
match User::from_request(req).await {
Outcome::Success(user) => {
if user.has_role(db, "manage_events").await {
Outcome::Success(EventUser(user))
} else {
Outcome::Error((Status::Forbidden, LoginError::NotACox))
}
}
Outcome::Error(f) => Outcome::Error(f),
Outcome::Forward(f) => Outcome::Forward(f),
}
}
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use std::collections::HashMap; use std::collections::HashMap;

View File

@ -27,7 +27,7 @@ async fn index(
context.insert( context.insert(
"loggedin_user", "loggedin_user",
&UserWithDetails::from_user(admin.0, db).await, &UserWithDetails::from_user(admin.user, db).await,
); );
context.insert("roles", &roles); context.insert("roles", &roles);

View File

@ -27,7 +27,7 @@ async fn index(
} }
context.insert( context.insert(
"loggedin_user", "loggedin_user",
&UserWithDetails::from_user(user.0, db).await, &UserWithDetails::from_user(user.user, db).await,
); );
let users: Vec<User> = User::all(db) let users: Vec<User> = User::all(db)

View File

@ -29,7 +29,7 @@ async fn index(
context.insert("schnupperanten", &users); context.insert("schnupperanten", &users);
context.insert( context.insert(
"loggedin_user", "loggedin_user",
&UserWithDetails::from_user(user.into(), db).await, &UserWithDetails::from_user(user.user, db).await,
); );
Template::render("admin/schnupper/index", context.into_json()) Template::render("admin/schnupper/index", context.into_json())

View File

@ -7,8 +7,8 @@ use crate::{
logbook::Logbook, logbook::Logbook,
role::Role, role::Role,
user::{ user::{
AdminUser, User, UserWithDetails, UserWithMembershipPdf, UserWithRolesAndMembershipPdf, AdminUser, AllowedToEditPaymentStatusUser, User, UserWithDetails,
VorstandUser, UserWithMembershipPdf, UserWithRolesAndMembershipPdf, VorstandUser,
}, },
}, },
tera::Config, tera::Config,
@ -54,7 +54,7 @@ async fn index(
.map(|u| async move { UserWithRolesAndMembershipPdf::from_user(db, u).await }) .map(|u| async move { UserWithRolesAndMembershipPdf::from_user(db, u).await })
.collect(); .collect();
let user: User = user.into(); let user: User = user.into_inner();
let allowed_to_edit = user.has_role(db, "admin").await; let allowed_to_edit = user.has_role(db, "admin").await;
let users: Vec<UserWithRolesAndMembershipPdf> = join_all(user_futures).await; let users: Vec<UserWithRolesAndMembershipPdf> = join_all(user_futures).await;
@ -110,7 +110,7 @@ async fn index_admin(
#[get("/user/fees")] #[get("/user/fees")]
async fn fees( async fn fees(
db: &State<SqlitePool>, db: &State<SqlitePool>,
admin: VorstandUser, user: AllowedToEditPaymentStatusUser,
flash: Option<FlashMessage<'_>>, flash: Option<FlashMessage<'_>>,
) -> Template { ) -> Template {
let mut context = Context::new(); let mut context = Context::new();
@ -130,7 +130,7 @@ async fn fees(
} }
context.insert( context.insert(
"loggedin_user", "loggedin_user",
&UserWithDetails::from_user(admin.into(), db).await, &UserWithDetails::from_user(user.into_inner(), db).await,
); );
Template::render("admin/user/fees", context.into_json()) Template::render("admin/user/fees", context.into_json())
@ -161,7 +161,7 @@ async fn scheckbuch(
} }
context.insert( context.insert(
"loggedin_user", "loggedin_user",
&UserWithDetails::from_user(user.into(), db).await, &UserWithDetails::from_user(user.into_inner(), db).await,
); );
Template::render("admin/user/scheckbuch", context.into_json()) Template::render("admin/user/scheckbuch", context.into_json())
@ -170,7 +170,7 @@ async fn scheckbuch(
#[get("/user/fees/paid?<user_ids>")] #[get("/user/fees/paid?<user_ids>")]
async fn fees_paid( async fn fees_paid(
db: &State<SqlitePool>, db: &State<SqlitePool>,
admin: AdminUser, calling_user: AllowedToEditPaymentStatusUser,
user_ids: Vec<i32>, user_ids: Vec<i32>,
referer: Referer, referer: Referer,
) -> Flash<Redirect> { ) -> Flash<Redirect> {
@ -181,7 +181,10 @@ async fn fees_paid(
if user.has_role(db, "paid").await { if user.has_role(db, "paid").await {
Log::create( Log::create(
db, db,
format!("{} set fees NOT paid for '{}'", admin.user.name, user.name), format!(
"{} set fees NOT paid for '{}'",
calling_user.user.name, user.name
),
) )
.await; .await;
user.remove_role(db, &Role::find_by_name(db, "paid").await.unwrap()) user.remove_role(db, &Role::find_by_name(db, "paid").await.unwrap())
@ -189,7 +192,10 @@ async fn fees_paid(
} else { } else {
Log::create( Log::create(
db, db,
format!("{} set fees paid for '{}'", admin.user.name, user.name), format!(
"{} set fees paid for '{}'",
calling_user.user.name, user.name
),
) )
.await; .await;
user.add_role(db, &Role::find_by_name(db, "paid").await.unwrap()) user.add_role(db, &Role::find_by_name(db, "paid").await.unwrap())

View File

@ -39,7 +39,7 @@ async fn index(
context.insert( context.insert(
"loggedin_user", "loggedin_user",
&UserWithDetails::from_user(admin.into(), db).await, &UserWithDetails::from_user(admin.into_inner(), db).await,
); );
Template::render("board/boathouse", context.into_json()) Template::render("board/boathouse", context.into_json())

View File

@ -59,7 +59,7 @@ async fn index(
context.insert("boats", &boats); context.insert("boats", &boats);
context.insert( context.insert(
"loggedin_user", "loggedin_user",
&UserWithDetails::from_user(user.into(), db).await, &UserWithDetails::from_user(user.into_inner(), db).await,
); );
Template::render("boatdamages", context.into_json()) Template::render("boatdamages", context.into_json())
@ -78,7 +78,7 @@ async fn create<'r>(
data: Form<FormBoatDamageToAdd<'r>>, data: Form<FormBoatDamageToAdd<'r>>,
user: DonauLinzUser, user: DonauLinzUser,
) -> Flash<Redirect> { ) -> Flash<Redirect> {
let user: User = user.into(); let user: User = user.into_inner();
let boatdamage_to_add = BoatDamageToAdd { let boatdamage_to_add = BoatDamageToAdd {
boat_id: data.boat_id, boat_id: data.boat_id,
desc: data.desc, desc: data.desc,

View File

@ -75,7 +75,7 @@ async fn index(
context.insert("user", &User::all(db).await); context.insert("user", &User::all(db).await);
context.insert( context.insert(
"loggedin_user", "loggedin_user",
&UserWithDetails::from_user(user.into(), db).await, &UserWithDetails::from_user(user.into_inner(), db).await,
); );
Template::render("boatreservations", context.into_json()) Template::render("boatreservations", context.into_json())
@ -97,7 +97,7 @@ async fn create<'r>(
data: Form<FormBoatReservationToAdd<'r>>, data: Form<FormBoatReservationToAdd<'r>>,
user: DonauLinzUser, user: DonauLinzUser,
) -> Flash<Redirect> { ) -> Flash<Redirect> {
let user_applicant: User = user.into(); let user_applicant: User = user.into_inner();
let boat = Boat::find_by_id(db, data.boat_id as i32).await.unwrap(); let boat = Boat::find_by_id(db, data.boat_id as i32).await.unwrap();
let boatreservation_to_add = BoatReservationToAdd { let boatreservation_to_add = BoatReservationToAdd {
boat: &boat, boat: &boat,

View File

@ -96,7 +96,7 @@ async fn index(
context.insert("logtypes", &logtypes); context.insert("logtypes", &logtypes);
context.insert( context.insert(
"loggedin_user", "loggedin_user",
&UserWithDetails::from_user(user.into(), db).await, &UserWithDetails::from_user(user.into_inner(), db).await,
); );
context.insert("on_water", &on_water); context.insert("on_water", &on_water);
context.insert("distances", &distances); context.insert("distances", &distances);
@ -110,7 +110,7 @@ async fn show(db: &State<SqlitePool>, user: DonauLinzUser) -> Template {
Template::render( Template::render(
"log.completed", "log.completed",
context!(logs, loggedin_user: &UserWithDetails::from_user(user.into(), db).await), context!(logs, loggedin_user: &UserWithDetails::from_user(user.into_inner(), db).await),
) )
} }
@ -287,7 +287,8 @@ async fn create_kiosk(
) )
.await; .await;
create_logbook(db, data, &DonauLinzUser(creator)).await //TODO: fixme create_logbook(db, data, &DonauLinzUser::new(db, creator).await.unwrap()).await
//TODO: fixme
} }
#[post("/update", data = "<data>")] #[post("/update", data = "<data>")]
@ -302,7 +303,7 @@ async fn update(
return Flash::error(Redirect::to("/log"), &format!("Logbucheintrag kann nicht bearbeitet werden, da es einen Logbuch-Eintrag mit ID={} nicht gibt", data.id)); return Flash::error(Redirect::to("/log"), &format!("Logbucheintrag kann nicht bearbeitet werden, da es einen Logbuch-Eintrag mit ID={} nicht gibt", data.id));
}; };
match logbook.update(db, data.clone(), &user.0).await { match logbook.update(db, data.clone(), &user.user).await {
Ok(()) => { Ok(()) => {
Log::create( Log::create(
db, db,
@ -372,11 +373,14 @@ async fn home_kiosk(
db, db,
data, data,
logbook_id, logbook_id,
&DonauLinzUser( &DonauLinzUser::new(
db,
User::find_by_id(db, logbook.shipmaster as i32) User::find_by_id(db, logbook.shipmaster as i32)
.await .await
.unwrap(), .unwrap(),
), //TODO: fixme )
.await
.unwrap(),
) )
.await .await
} }

View File

@ -22,7 +22,7 @@ async fn index(
user: AllowedForPlannedTripsUser, user: AllowedForPlannedTripsUser,
flash: Option<FlashMessage<'_>>, flash: Option<FlashMessage<'_>>,
) -> Template { ) -> Template {
let user: User = user.into(); let user: User = user.into_inner();
let mut context = Context::new(); let mut context = Context::new();
@ -50,7 +50,7 @@ async fn join(
user: AllowedForPlannedTripsUser, user: AllowedForPlannedTripsUser,
user_note: Option<String>, user_note: Option<String>,
) -> Flash<Redirect> { ) -> Flash<Redirect> {
let user: User = user.into(); let user: User = user.into_inner();
let Some(trip_details) = TripDetails::find_by_id(db, trip_details_id).await else { let Some(trip_details) = TripDetails::find_by_id(db, trip_details_id).await else {
return Flash::error(Redirect::to("/"), "Trip_details do not exist."); return Flash::error(Redirect::to("/"), "Trip_details do not exist.");
@ -113,7 +113,7 @@ async fn remove_guest(
user: AllowedForPlannedTripsUser, user: AllowedForPlannedTripsUser,
name: String, name: String,
) -> Flash<Redirect> { ) -> Flash<Redirect> {
let user: User = user.into(); let user: User = user.into_inner();
let Some(trip_details) = TripDetails::find_by_id(db, trip_details_id).await else { let Some(trip_details) = TripDetails::find_by_id(db, trip_details_id).await else {
return Flash::error(Redirect::to("/planned"), "TripDetailsId does not exist"); return Flash::error(Redirect::to("/planned"), "TripDetailsId does not exist");
@ -160,7 +160,7 @@ async fn remove(
trip_details_id: i64, trip_details_id: i64,
user: AllowedForPlannedTripsUser, user: AllowedForPlannedTripsUser,
) -> Flash<Redirect> { ) -> Flash<Redirect> {
let user: User = user.into(); let user: User = user.into_inner();
let Some(trip_details) = TripDetails::find_by_id(db, trip_details_id).await else { let Some(trip_details) = TripDetails::find_by_id(db, trip_details_id).await else {
return Flash::error(Redirect::to("/planned"), "TripDetailsId does not exist"); return Flash::error(Redirect::to("/planned"), "TripDetailsId does not exist");

View File

@ -16,7 +16,7 @@ async fn index_boat(db: &State<SqlitePool>, user: DonauLinzUser) -> Template {
Template::render( Template::render(
"stat.boats", "stat.boats",
context!(loggedin_user: &UserWithDetails::from_user(user.into(), db).await, stat, kiosk), context!(loggedin_user: &UserWithDetails::from_user(user.into_inner(), db).await, stat, kiosk),
) )
} }
@ -38,7 +38,7 @@ async fn index(db: &State<SqlitePool>, user: DonauLinzUser, year: Option<i32>) -
Template::render( Template::render(
"stat.people", "stat.people",
context!(loggedin_user: &UserWithDetails::from_user(user.into(), db).await, stat, personal, kiosk, guest_km, club_km), context!(loggedin_user: &UserWithDetails::from_user(user.into_inner(), db).await, stat, personal, kiosk, guest_km, club_km),
) )
} }

View File

@ -59,7 +59,7 @@ async fn index(
context.insert("user", &User::all(db).await); context.insert("user", &User::all(db).await);
context.insert( context.insert(
"loggedin_user", "loggedin_user",
&UserWithDetails::from_user(user.into(), db).await, &UserWithDetails::from_user(user.into_inner(), db).await,
); );
Template::render("trailerreservations", context.into_json()) Template::render("trailerreservations", context.into_json())
@ -81,7 +81,7 @@ async fn create<'r>(
data: Form<FormTrailerReservationToAdd<'r>>, data: Form<FormTrailerReservationToAdd<'r>>,
user: DonauLinzUser, user: DonauLinzUser,
) -> Flash<Redirect> { ) -> Flash<Redirect> {
let user_applicant: User = user.into(); let user_applicant: User = user.into_inner();
let trailer = Trailer::find_by_id(db, data.trailer_id as i32) let trailer = Trailer::find_by_id(db, data.trailer_id as i32)
.await .await
.unwrap(); .unwrap();

View File

@ -32,7 +32,7 @@
{% if not loop.last %}+{% endif %} {% if not loop.last %}+{% endif %}
{% endfor %} {% endfor %}
</div> </div>
{% if "admin" in loggedin_user.roles %} {% if "admin" in loggedin_user.roles or "kassier" in loggedin_user.roles %}
<a href="/admin/user/fees/paid?{{ fee.user_ids }}">Zahlungsstatus ändern</a> <a href="/admin/user/fees/paid?{{ fee.user_ids }}">Zahlungsstatus ändern</a>
{% endif %} {% endif %}
</div> </div>

View File

@ -31,7 +31,7 @@
<li>{{ log::show_old(log=trip, state="completed", only_ones=false, index=loop.index) }}</li> <li>{{ log::show_old(log=trip, state="completed", only_ones=false, index=loop.index) }}</li>
{% endfor %} {% endfor %}
</div> </div>
{% if "admin" in loggedin_user.roles %} {% if "admin" in loggedin_user.roles or "kassier" in loggedin_user.roles %}
<a href="/admin/user/fees/paid?user_ids[]={{ user.id }}">Zahlungsstatus ändern</a> <a href="/admin/user/fees/paid?user_ids[]={{ user.id }}">Zahlungsstatus ändern</a>
{% endif %} {% endif %}
</div> </div>