enable changing paid state
This commit is contained in:
parent
ac5ecbafec
commit
eda072c713
@ -3,8 +3,8 @@ use sqlx::{FromRow, SqlitePool};
|
|||||||
|
|
||||||
#[derive(FromRow, Serialize, Clone)]
|
#[derive(FromRow, Serialize, Clone)]
|
||||||
pub struct Role {
|
pub struct Role {
|
||||||
id: i64,
|
pub(crate) id: i64,
|
||||||
name: String,
|
pub(crate) name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Role {
|
impl Role {
|
||||||
@ -30,6 +30,21 @@ WHERE id like ?
|
|||||||
.ok()
|
.ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn find_by_name(db: &SqlitePool, name: &str) -> Option<Self> {
|
||||||
|
sqlx::query_as!(
|
||||||
|
Self,
|
||||||
|
"
|
||||||
|
SELECT id, name
|
||||||
|
FROM role
|
||||||
|
WHERE name like ?
|
||||||
|
",
|
||||||
|
name
|
||||||
|
)
|
||||||
|
.fetch_one(db)
|
||||||
|
.await
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn mails_from_role(&self, db: &SqlitePool) -> Vec<String> {
|
pub async fn mails_from_role(&self, db: &SqlitePool) -> Vec<String> {
|
||||||
let query = format!(
|
let query = format!(
|
||||||
"SELECT u.mail
|
"SELECT u.mail
|
||||||
|
@ -14,7 +14,7 @@ use rocket::{
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
|
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
|
||||||
|
|
||||||
use super::{family::Family, log::Log, tripdetails::TripDetails, Day};
|
use super::{family::Family, log::Log, role::Role, tripdetails::TripDetails, Day};
|
||||||
use crate::tera::admin::user::UserEditForm;
|
use crate::tera::admin::user::UserEditForm;
|
||||||
|
|
||||||
const RENNRUDERBEITRAG: i32 = 11000;
|
const RENNRUDERBEITRAG: i32 = 11000;
|
||||||
@ -105,6 +105,8 @@ pub(crate) struct Fee {
|
|||||||
pub(crate) sum_in_cents: i32,
|
pub(crate) sum_in_cents: i32,
|
||||||
pub(crate) parts: Vec<(String, i32)>,
|
pub(crate) parts: Vec<(String, i32)>,
|
||||||
pub(crate) name: String,
|
pub(crate) name: String,
|
||||||
|
pub(crate) user_ids: String,
|
||||||
|
pub(crate) paid: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Fee {
|
impl Fee {
|
||||||
@ -113,6 +115,8 @@ impl Fee {
|
|||||||
sum_in_cents: 0,
|
sum_in_cents: 0,
|
||||||
name: "".into(),
|
name: "".into(),
|
||||||
parts: Vec::new(),
|
parts: Vec::new(),
|
||||||
|
user_ids: "".into(),
|
||||||
|
paid: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,8 +126,18 @@ impl Fee {
|
|||||||
self.parts.push((desc, price_in_cents));
|
self.parts.push((desc, price_in_cents));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn name(&mut self, name: String) {
|
pub fn add_person(&mut self, user: &User) {
|
||||||
self.name = name;
|
if !self.name.is_empty() {
|
||||||
|
self.name.push_str(" + ");
|
||||||
|
self.user_ids.push_str("&");
|
||||||
|
}
|
||||||
|
self.name.push_str(&user.name);
|
||||||
|
|
||||||
|
self.user_ids.push_str(&format!("user_ids[]={}", user.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn paid(&mut self) {
|
||||||
|
self.paid = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn merge(&mut self, fee: Fee) {
|
pub fn merge(&mut self, fee: Fee) {
|
||||||
@ -144,20 +158,22 @@ impl User {
|
|||||||
if let Some(family) = Family::find_by_opt_id(db, self.family_id).await {
|
if let Some(family) = Family::find_by_opt_id(db, self.family_id).await {
|
||||||
let mut names = String::new();
|
let mut names = String::new();
|
||||||
for member in family.members(db).await {
|
for member in family.members(db).await {
|
||||||
if !names.is_empty() {
|
fee.add_person(&member);
|
||||||
names.push_str(" + ");
|
if member.has_role(db, "paid").await {
|
||||||
|
fee.paid();
|
||||||
}
|
}
|
||||||
names.push_str(&member.name);
|
|
||||||
fee.merge(member.fee_without_families(db).await);
|
fee.merge(member.fee_without_families(db).await);
|
||||||
}
|
}
|
||||||
fee.name(names);
|
|
||||||
if family.amount_family_members(db).await > 2 {
|
if family.amount_family_members(db).await > 2 {
|
||||||
fee.add("Familie 3+ Personen".into(), FAMILY_THREE_OR_MORE);
|
fee.add("Familie 3+ Personen".into(), FAMILY_THREE_OR_MORE);
|
||||||
} else {
|
} else {
|
||||||
fee.add("Familie 2 Personen".into(), FAMILY_TWO);
|
fee.add("Familie 2 Personen".into(), FAMILY_TWO);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fee.name(self.name.clone());
|
fee.add_person(&self);
|
||||||
|
if self.has_role(db, "paid").await {
|
||||||
|
fee.paid();
|
||||||
|
}
|
||||||
fee.merge(self.fee_without_families(db).await);
|
fee.merge(self.fee_without_families(db).await);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -452,15 +468,36 @@ ORDER BY last_access DESC
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
for role_id in data.roles.into_keys() {
|
for role_id in data.roles.into_keys() {
|
||||||
|
self.add_role(
|
||||||
|
db,
|
||||||
|
&Role::find_by_id(db, role_id.parse::<i32>().unwrap())
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn add_role(&self, db: &SqlitePool, role: &Role) {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"INSERT INTO user_role(user_id, role_id) VALUES (?, ?)",
|
"INSERT INTO user_role(user_id, role_id) VALUES (?, ?)",
|
||||||
self.id,
|
self.id,
|
||||||
role_id
|
role.id
|
||||||
)
|
)
|
||||||
.execute(db)
|
.execute(db)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn remove_role(&self, db: &SqlitePool, role: &Role) {
|
||||||
|
sqlx::query!(
|
||||||
|
"DELETE FROM user_role WHERE user_id = ? and role_id = ?",
|
||||||
|
self.id,
|
||||||
|
role.id
|
||||||
|
)
|
||||||
|
.execute(db)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn login(db: &SqlitePool, name: &str, pw: &str) -> Result<Self, LoginError> {
|
pub async fn login(db: &SqlitePool, name: &str, pw: &str) -> Result<Self, LoginError> {
|
||||||
|
@ -77,6 +77,33 @@ async fn fees(
|
|||||||
Template::render("admin/user/fees", context.into_json())
|
Template::render("admin/user/fees", context.into_json())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[get("/user/fees/paid?<user_ids>")]
|
||||||
|
async fn fees_paid(
|
||||||
|
db: &State<SqlitePool>,
|
||||||
|
admin: AdminUser,
|
||||||
|
user_ids: Vec<i32>,
|
||||||
|
) -> Flash<Redirect> {
|
||||||
|
let mut res = String::new();
|
||||||
|
for user_id in user_ids {
|
||||||
|
let user = User::find_by_id(db, user_id).await.unwrap();
|
||||||
|
res.push_str(&format!("{} + ", user.name));
|
||||||
|
if user.has_role(db, "paid").await {
|
||||||
|
user.remove_role(db, &Role::find_by_name(db, "paid").await.unwrap())
|
||||||
|
.await;
|
||||||
|
} else {
|
||||||
|
user.add_role(db, &Role::find_by_name(db, "paid").await.unwrap())
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res.truncate(res.len() - 3); // remove ' + ' from the end
|
||||||
|
|
||||||
|
Flash::success(
|
||||||
|
Redirect::to("/admin/user/fees"),
|
||||||
|
format!("Zahlungsstatus von {} erfolgreich geändert", res),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[get("/user/<user>/reset-pw")]
|
#[get("/user/<user>/reset-pw")]
|
||||||
async fn resetpw(db: &State<SqlitePool>, _admin: AdminUser, user: i32) -> Flash<Redirect> {
|
async fn resetpw(db: &State<SqlitePool>, _admin: AdminUser, user: i32) -> Flash<Redirect> {
|
||||||
let user = User::find_by_id(db, user).await;
|
let user = User::find_by_id(db, user).await;
|
||||||
@ -165,5 +192,5 @@ async fn create(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn routes() -> Vec<Route> {
|
pub fn routes() -> Vec<Route> {
|
||||||
routes![index, resetpw, update, create, delete, fees]
|
routes![index, resetpw, update, create, delete, fees, fees_paid]
|
||||||
}
|
}
|
||||||
|
@ -3,29 +3,45 @@
|
|||||||
{% extends "base" %}
|
{% extends "base" %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="max-w-screen-lg w-full">
|
<div class="max-w-screen-lg w-full bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5">
|
||||||
|
|
||||||
{% if flash %}
|
{% if flash %}
|
||||||
{{ macros::alert(message=flash.1, type=flash.0, class="my-3") }}
|
{{ macros::alert(message=flash.1, type=flash.0, class="sm:col-span-2 lg:col-span-3") }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div class="grid gap-3">
|
<h1 class="h1">Gebühren</h1>
|
||||||
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5" role="alert">
|
|
||||||
<h2 class="h2">Gebühren</h2>
|
<!-- START filterBar -->
|
||||||
<div class="text-sm p-3">
|
<div class="search-wrapper">
|
||||||
|
<label for="name" class="sr-only">Suche</label>
|
||||||
|
<input type="search" name="name" id="filter-js" class="search-bar" placeholder="Suchen nach Name"/>
|
||||||
|
</div>
|
||||||
|
<!-- END filterBar -->
|
||||||
|
|
||||||
|
<div class="bg-primary-100 dark:bg-primary-950 p-3 rounded-b-md grid gap-4">
|
||||||
|
<div id="filter-result-js" class="text-primary-950 dark:text-white text-right"></div>
|
||||||
|
|
||||||
{% for fee in fees | sort(attribute="name") %}
|
{% for fee in fees | sort(attribute="name") %}
|
||||||
<b>{{ fee.name }}: {{ fee.sum_in_cents / 100 }}€</b><br />
|
<div {% if fee.paid %} style="background-color: green;" {% endif %} data-filterable="true" data-filter="{{ fee.name }}" class="bg-white dark:bg-primary-900 p-3 rounded-md w-full">
|
||||||
|
<div class="grid sm:grid-cols-1 gap-3">
|
||||||
|
<div style="width: 100%" class="col-span-2">
|
||||||
|
<b>{{ fee.name }}</b>
|
||||||
|
</div>
|
||||||
|
<div style="width: 100%">
|
||||||
|
{{ fee.sum_in_cents / 100 }}€:
|
||||||
|
</div>
|
||||||
|
<div style="width: 100%">
|
||||||
{% for p in fee.parts %}
|
{% for p in fee.parts %}
|
||||||
{{ p.0 }} ({{ p.1 / 100 }}€) {% if not loop.last %} + {% endif %}
|
{{ p.0 }} ({{ p.1 / 100 }}€) {% if not loop.last %} + {% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<hr />
|
</div>
|
||||||
|
{% if "admin" in loggedin_user.roles %}
|
||||||
|
<a href="/admin/user/fees/paid?{{ fee.user_ids }}">Zahlungsstatus ändern</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user