forked from Ruderverein-Donau-Linz/rowt
Merge pull request 'notfiication-on-new-personal-stat' (#843) from notfiication-on-new-personal-stat into main
Reviewed-on: Ruderverein-Donau-Linz/rowt#843
This commit is contained in:
commit
e5fec411f3
@ -1,5 +1,3 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use rocket::serde::{Deserialize, Serialize};
|
use rocket::serde::{Deserialize, Serialize};
|
||||||
use sqlx::{FromRow, SqlitePool};
|
use sqlx::{FromRow, SqlitePool};
|
||||||
|
|
||||||
@ -7,6 +5,93 @@ use crate::tera::board::boathouse::FormBoathouseToAdd;
|
|||||||
|
|
||||||
use super::boat::Boat;
|
use super::boat::Boat;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct BoathousePlace {
|
||||||
|
boat: Boat,
|
||||||
|
boathouse_id: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct BoathouseRack {
|
||||||
|
boats: [Option<BoathousePlace>; 12],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BoathouseRack {
|
||||||
|
fn new() -> Self {
|
||||||
|
let boats = [
|
||||||
|
None, None, None, None, None, None, None, None, None, None, None, None,
|
||||||
|
];
|
||||||
|
Self { boats }
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn add(&mut self, db: &SqlitePool, boathouse: Boathouse) {
|
||||||
|
self.boats[boathouse.level as usize] = Some(BoathousePlace {
|
||||||
|
boat: Boat::find_by_id(db, boathouse.boat_id as i32)
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
boathouse_id: boathouse.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct BoathouseSide {
|
||||||
|
mountain: BoathouseRack,
|
||||||
|
water: BoathouseRack,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BoathouseSide {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
mountain: BoathouseRack::new(),
|
||||||
|
water: BoathouseRack::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn add(&mut self, db: &SqlitePool, boathouse: Boathouse) {
|
||||||
|
match boathouse.side.as_str() {
|
||||||
|
"mountain" => self.mountain.add(db, boathouse).await,
|
||||||
|
"water" => self.water.add(db, boathouse).await,
|
||||||
|
_ => panic!("db constraint failed"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct BoathouseAisles {
|
||||||
|
mountain: BoathouseSide,
|
||||||
|
middle: BoathouseSide,
|
||||||
|
water: BoathouseSide,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BoathouseAisles {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
mountain: BoathouseSide::new(),
|
||||||
|
middle: BoathouseSide::new(),
|
||||||
|
water: BoathouseSide::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn add(&mut self, db: &SqlitePool, boathouse: Boathouse) {
|
||||||
|
match boathouse.aisle.as_str() {
|
||||||
|
"water" => self.water.add(db, boathouse).await,
|
||||||
|
"middle" => self.middle.add(db, boathouse).await,
|
||||||
|
"mountain" => self.mountain.add(db, boathouse).await,
|
||||||
|
_ => panic!("db constraint failed"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn from(db: &SqlitePool, boathouses: Vec<Boathouse>) -> Self {
|
||||||
|
let mut ret = BoathouseAisles::new();
|
||||||
|
|
||||||
|
for boathouse in boathouses {
|
||||||
|
ret.add(db, boathouse).await;
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(FromRow, Debug, Serialize, Deserialize)]
|
#[derive(FromRow, Debug, Serialize, Deserialize)]
|
||||||
pub struct Boathouse {
|
pub struct Boathouse {
|
||||||
pub id: i64,
|
pub id: i64,
|
||||||
@ -17,54 +102,7 @@ pub struct Boathouse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Boathouse {
|
impl Boathouse {
|
||||||
pub async fn get(db: &SqlitePool) -> HashMap<&str, HashMap<&str, [Option<(i64, Boat)>; 12]>> {
|
pub async fn get(db: &SqlitePool) -> BoathouseAisles {
|
||||||
let mut ret: HashMap<&str, HashMap<&str, [Option<(i64, Boat)>; 12]>> = HashMap::new();
|
|
||||||
|
|
||||||
let mut mountain = HashMap::new();
|
|
||||||
mountain.insert(
|
|
||||||
"mountain",
|
|
||||||
[
|
|
||||||
None, None, None, None, None, None, None, None, None, None, None, None,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
mountain.insert(
|
|
||||||
"water",
|
|
||||||
[
|
|
||||||
None, None, None, None, None, None, None, None, None, None, None, None,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
ret.insert("mountain-aisle", mountain);
|
|
||||||
|
|
||||||
let mut middle = HashMap::new();
|
|
||||||
middle.insert(
|
|
||||||
"mountain",
|
|
||||||
[
|
|
||||||
None, None, None, None, None, None, None, None, None, None, None, None,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
middle.insert(
|
|
||||||
"water",
|
|
||||||
[
|
|
||||||
None, None, None, None, None, None, None, None, None, None, None, None,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
ret.insert("middle-aisle", middle);
|
|
||||||
|
|
||||||
let mut water = HashMap::new();
|
|
||||||
water.insert(
|
|
||||||
"mountain",
|
|
||||||
[
|
|
||||||
None, None, None, None, None, None, None, None, None, None, None, None,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
water.insert(
|
|
||||||
"water",
|
|
||||||
[
|
|
||||||
None, None, None, None, None, None, None, None, None, None, None, None,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
ret.insert("water-aisle", water);
|
|
||||||
|
|
||||||
let boathouses = sqlx::query_as!(
|
let boathouses = sqlx::query_as!(
|
||||||
Boathouse,
|
Boathouse,
|
||||||
"SELECT id, boat_id, aisle, side, level FROM boathouse"
|
"SELECT id, boat_id, aisle, side, level FROM boathouse"
|
||||||
@ -73,21 +111,7 @@ impl Boathouse {
|
|||||||
.await
|
.await
|
||||||
.unwrap(); //TODO: fixme
|
.unwrap(); //TODO: fixme
|
||||||
|
|
||||||
for boathouse in boathouses {
|
BoathouseAisles::from(db, boathouses).await
|
||||||
let aisle = ret
|
|
||||||
.get_mut(format!("{}-aisle", boathouse.aisle).as_str())
|
|
||||||
.unwrap();
|
|
||||||
let side = aisle.get_mut(boathouse.side.as_str()).unwrap();
|
|
||||||
|
|
||||||
side[boathouse.level as usize] = Some((
|
|
||||||
boathouse.id,
|
|
||||||
Boat::find_by_id(db, boathouse.boat_id as i32)
|
|
||||||
.await
|
|
||||||
.unwrap(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create(db: &SqlitePool, data: FormBoathouseToAdd) -> Result<(), String> {
|
pub async fn create(db: &SqlitePool, data: FormBoathouseToAdd) -> Result<(), String> {
|
||||||
|
@ -33,8 +33,7 @@ impl PartialEq for Logbook {
|
|||||||
|
|
||||||
pub(crate) enum Filter {
|
pub(crate) enum Filter {
|
||||||
SingleDayOnly,
|
SingleDayOnly,
|
||||||
MultiDazOnly,
|
MultiDayOnly,
|
||||||
None,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(FromForm, Debug, Clone)]
|
#[derive(FromForm, Debug, Clone)]
|
||||||
@ -362,12 +361,13 @@ ORDER BY departure DESC
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn completed_wanderfahrten_with_user_over_km_in_year(
|
pub(crate) async fn completed_wanderfahrten_with_user_over_km_in_year_tx(
|
||||||
db: &SqlitePool,
|
db: &mut Transaction<'_, Sqlite>,
|
||||||
user: &User,
|
user: &User,
|
||||||
min_distance: i32,
|
min_distance: i32,
|
||||||
year: i32,
|
year: i32,
|
||||||
filter: Filter,
|
filter: Filter,
|
||||||
|
exclude_last_log: bool,
|
||||||
) -> Vec<LogbookWithBoatAndRowers> {
|
) -> Vec<LogbookWithBoatAndRowers> {
|
||||||
let logs: Vec<Logbook> = sqlx::query_as(
|
let logs: Vec<Logbook> = sqlx::query_as(
|
||||||
&format!("
|
&format!("
|
||||||
@ -378,7 +378,7 @@ ORDER BY departure DESC
|
|||||||
ORDER BY arrival DESC
|
ORDER BY arrival DESC
|
||||||
", user.id, min_distance, year)
|
", user.id, min_distance, year)
|
||||||
)
|
)
|
||||||
.fetch_all(db)
|
.fetch_all(db.deref_mut())
|
||||||
.await
|
.await
|
||||||
.unwrap(); //TODO: fixme
|
.unwrap(); //TODO: fixme
|
||||||
|
|
||||||
@ -389,19 +389,20 @@ ORDER BY departure DESC
|
|||||||
match filter {
|
match filter {
|
||||||
Filter::SingleDayOnly => {
|
Filter::SingleDayOnly => {
|
||||||
if trip_days == 0 {
|
if trip_days == 0 {
|
||||||
ret.push(LogbookWithBoatAndRowers::from(db, log).await);
|
ret.push(LogbookWithBoatAndRowers::from_tx(db, log).await);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Filter::MultiDazOnly => {
|
Filter::MultiDayOnly => {
|
||||||
if trip_days > 0 {
|
if trip_days > 0 {
|
||||||
ret.push(LogbookWithBoatAndRowers::from(db, log).await);
|
ret.push(LogbookWithBoatAndRowers::from_tx(db, log).await);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Filter::None => {
|
|
||||||
ret.push(LogbookWithBoatAndRowers::from(db, log).await);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if exclude_last_log {
|
||||||
|
ret.pop();
|
||||||
|
}
|
||||||
|
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,63 +1,64 @@
|
|||||||
|
use crate::model::{logbook::Logbook, stat::Stat, user::User};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
#[derive(Serialize, PartialEq, Debug)]
|
#[derive(Serialize, PartialEq, Debug)]
|
||||||
pub(crate) enum Level {
|
pub(crate) enum Level {
|
||||||
NONE,
|
None,
|
||||||
BRONZE,
|
Bronze,
|
||||||
SILVER,
|
Silver,
|
||||||
GOLD,
|
Gold,
|
||||||
DIAMOND,
|
Diamond,
|
||||||
DONE,
|
Done,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Level {
|
impl Level {
|
||||||
fn required_km(&self) -> i32 {
|
fn required_km(&self) -> i32 {
|
||||||
match self {
|
match self {
|
||||||
Level::BRONZE => 40000,
|
Level::Bronze => 40_000,
|
||||||
Level::SILVER => 80000,
|
Level::Silver => 80_000,
|
||||||
Level::GOLD => 100000,
|
Level::Gold => 100_000,
|
||||||
Level::DIAMOND => 200000,
|
Level::Diamond => 200_000,
|
||||||
Level::DONE => 0,
|
Level::Done => 0,
|
||||||
Level::NONE => 0,
|
Level::None => 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_level(km: i32) -> Self {
|
fn next_level(km: i32) -> Self {
|
||||||
if km < Level::BRONZE.required_km() {
|
if km < Level::Bronze.required_km() {
|
||||||
Level::BRONZE
|
Level::Bronze
|
||||||
} else if km < Level::SILVER.required_km() {
|
} else if km < Level::Silver.required_km() {
|
||||||
Level::SILVER
|
Level::Silver
|
||||||
} else if km < Level::GOLD.required_km() {
|
} else if km < Level::Gold.required_km() {
|
||||||
Level::GOLD
|
Level::Gold
|
||||||
} else if km < Level::DIAMOND.required_km() {
|
} else if km < Level::Diamond.required_km() {
|
||||||
Level::DIAMOND
|
Level::Diamond
|
||||||
} else {
|
} else {
|
||||||
Level::DONE
|
Level::Done
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn curr_level(km: i32) -> Self {
|
pub(crate) fn curr_level(km: i32) -> Self {
|
||||||
if km < Level::BRONZE.required_km() {
|
if km < Level::Bronze.required_km() {
|
||||||
Level::NONE
|
Level::None
|
||||||
} else if km < Level::SILVER.required_km() {
|
} else if km < Level::Silver.required_km() {
|
||||||
Level::BRONZE
|
Level::Bronze
|
||||||
} else if km < Level::GOLD.required_km() {
|
} else if km < Level::Gold.required_km() {
|
||||||
Level::SILVER
|
Level::Silver
|
||||||
} else if km < Level::DIAMOND.required_km() {
|
} else if km < Level::Diamond.required_km() {
|
||||||
Level::GOLD
|
Level::Gold
|
||||||
} else {
|
} else {
|
||||||
Level::DIAMOND
|
Level::Diamond
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn desc(&self) -> &str {
|
pub(crate) fn desc(&self) -> &str {
|
||||||
match self {
|
match self {
|
||||||
Level::BRONZE => "Bronze",
|
Level::Bronze => "Bronze",
|
||||||
Level::SILVER => "Silber",
|
Level::Silver => "Silber",
|
||||||
Level::GOLD => "Gold",
|
Level::Gold => "Gold",
|
||||||
Level::DIAMOND => "Diamant",
|
Level::Diamond => "Diamant",
|
||||||
Level::DONE => "",
|
Level::Done => "",
|
||||||
Level::NONE => "-",
|
Level::None => "-",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -85,3 +86,19 @@ impl Next {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn new_level_with_last_log(
|
||||||
|
db: &mut sqlx::Transaction<'_, sqlx::Sqlite>,
|
||||||
|
user: &User,
|
||||||
|
) -> Option<String> {
|
||||||
|
let rowed_km = Stat::total_km_tx(db, user).await.rowed_km;
|
||||||
|
|
||||||
|
if let Some(last_logbookentry) = Logbook::completed_with_user_tx(db, user).await.last() {
|
||||||
|
let last_trip_km = last_logbookentry.logbook.distance_in_km.unwrap();
|
||||||
|
if Level::curr_level(rowed_km) != Level::curr_level(rowed_km - last_trip_km as i32) {
|
||||||
|
return Some(Level::curr_level(rowed_km).desc().to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
@ -2,7 +2,7 @@ use std::cmp;
|
|||||||
|
|
||||||
use chrono::{Datelike, Local, NaiveDate};
|
use chrono::{Datelike, Local, NaiveDate};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use sqlx::SqlitePool;
|
use sqlx::{Sqlite, SqlitePool, Transaction};
|
||||||
|
|
||||||
use crate::model::{
|
use crate::model::{
|
||||||
logbook::{Filter, Logbook, LogbookWithBoatAndRowers},
|
logbook::{Filter, Logbook, LogbookWithBoatAndRowers},
|
||||||
@ -111,11 +111,44 @@ pub(crate) struct Status {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Status {
|
impl Status {
|
||||||
pub(crate) async fn for_user(db: &SqlitePool, user: &User) -> Option<Self> {
|
fn calc(
|
||||||
|
agebracket: &AgeBracket,
|
||||||
|
rowed_km: i32,
|
||||||
|
single_day_trips_over_required_distance: usize,
|
||||||
|
multi_day_trips_over_required_distance: usize,
|
||||||
|
year: i32,
|
||||||
|
) -> Self {
|
||||||
|
let category = agebracket.cat().to_string();
|
||||||
|
|
||||||
|
let required_km = agebracket.dist_in_km();
|
||||||
|
let missing_km = cmp::max(required_km - rowed_km, 0);
|
||||||
|
|
||||||
|
let achieved = missing_km == 0
|
||||||
|
&& (multi_day_trips_over_required_distance >= 1
|
||||||
|
|| single_day_trips_over_required_distance >= 2);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
year,
|
||||||
|
rowed_km,
|
||||||
|
category,
|
||||||
|
required_km,
|
||||||
|
missing_km,
|
||||||
|
multi_day_trips_over_required_distance: vec![],
|
||||||
|
single_day_trips_over_required_distance: vec![],
|
||||||
|
multi_day_trips_required_distance: agebracket.required_dist_multi_day_in_km(),
|
||||||
|
single_day_trips_required_distance: agebracket.required_dist_single_day_in_km(),
|
||||||
|
achieved,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn for_user_tx(
|
||||||
|
db: &mut Transaction<'_, Sqlite>,
|
||||||
|
user: &User,
|
||||||
|
exclude_last_log: bool,
|
||||||
|
) -> Option<Self> {
|
||||||
let Ok(agebracket) = AgeBracket::try_from(user) else {
|
let Ok(agebracket) = AgeBracket::try_from(user) else {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
let category = agebracket.cat().to_string();
|
|
||||||
|
|
||||||
let year = if Local::now().month() == 1 {
|
let year = if Local::now().month() == 1 {
|
||||||
Local::now().year() - 1
|
Local::now().year() - 1
|
||||||
@ -123,44 +156,66 @@ impl Status {
|
|||||||
Local::now().year()
|
Local::now().year()
|
||||||
};
|
};
|
||||||
|
|
||||||
let rowed_km = Stat::person(db, Some(year), user).await.rowed_km;
|
let rowed_km = Stat::person_tx(db, Some(year), user).await.rowed_km;
|
||||||
let required_km = agebracket.dist_in_km();
|
|
||||||
let missing_km = cmp::max(required_km - rowed_km, 0);
|
|
||||||
|
|
||||||
let single_day_trips_over_required_distance =
|
let single_day_trips_over_required_distance =
|
||||||
Logbook::completed_wanderfahrten_with_user_over_km_in_year(
|
Logbook::completed_wanderfahrten_with_user_over_km_in_year_tx(
|
||||||
db,
|
db,
|
||||||
user,
|
user,
|
||||||
agebracket.required_dist_single_day_in_km(),
|
agebracket.required_dist_single_day_in_km(),
|
||||||
year,
|
year,
|
||||||
Filter::SingleDayOnly,
|
Filter::SingleDayOnly,
|
||||||
|
exclude_last_log,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let multi_day_trips_over_required_distance =
|
let multi_day_trips_over_required_distance =
|
||||||
Logbook::completed_wanderfahrten_with_user_over_km_in_year(
|
Logbook::completed_wanderfahrten_with_user_over_km_in_year_tx(
|
||||||
db,
|
db,
|
||||||
user,
|
user,
|
||||||
agebracket.required_dist_multi_day_in_km(),
|
agebracket.required_dist_multi_day_in_km(),
|
||||||
year,
|
year,
|
||||||
Filter::MultiDazOnly,
|
Filter::MultiDayOnly,
|
||||||
|
exclude_last_log,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let achieved = missing_km == 0
|
let ret = Self::calc(
|
||||||
&& (multi_day_trips_over_required_distance.len() >= 1
|
&agebracket,
|
||||||
|| single_day_trips_over_required_distance.len() >= 2);
|
rowed_km,
|
||||||
|
single_day_trips_over_required_distance.len(),
|
||||||
|
multi_day_trips_over_required_distance.len(),
|
||||||
|
year,
|
||||||
|
);
|
||||||
|
|
||||||
Some(Self {
|
Some(Self {
|
||||||
year,
|
|
||||||
rowed_km,
|
|
||||||
category,
|
|
||||||
required_km,
|
|
||||||
missing_km,
|
|
||||||
multi_day_trips_over_required_distance,
|
multi_day_trips_over_required_distance,
|
||||||
single_day_trips_over_required_distance,
|
single_day_trips_over_required_distance,
|
||||||
multi_day_trips_required_distance: agebracket.required_dist_multi_day_in_km(),
|
..ret
|
||||||
single_day_trips_required_distance: agebracket.required_dist_single_day_in_km(),
|
|
||||||
achieved,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn for_user(db: &SqlitePool, user: &User) -> Option<Self> {
|
||||||
|
let mut tx = db.begin().await.unwrap();
|
||||||
|
let ret = Self::for_user_tx(&mut tx, user, false).await;
|
||||||
|
tx.commit().await.unwrap();
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn completed_with_last_log(
|
||||||
|
db: &mut Transaction<'_, Sqlite>,
|
||||||
|
user: &User,
|
||||||
|
) -> bool {
|
||||||
|
if let Some(status) = Self::for_user_tx(db, user, false).await {
|
||||||
|
// if user has agebracket...
|
||||||
|
if status.achieved {
|
||||||
|
// ... and has achieved the 'Fahrtenabzeichen'
|
||||||
|
let without_last_entry = Self::for_user_tx(db, user, true).await.unwrap();
|
||||||
|
if !without_last_entry.achieved {
|
||||||
|
// ... and this wasn't the case before the last logentry
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use std::collections::HashMap;
|
use std::{collections::HashMap, ops::DerefMut};
|
||||||
|
|
||||||
use crate::model::user::User;
|
use crate::model::user::User;
|
||||||
use chrono::Datelike;
|
use chrono::Datelike;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use sqlx::{FromRow, Row, SqlitePool};
|
use sqlx::{FromRow, Row, Sqlite, SqlitePool, Transaction};
|
||||||
|
|
||||||
use super::boat::Boat;
|
use super::boat::Boat;
|
||||||
|
|
||||||
@ -218,7 +218,7 @@ ORDER BY rowed_km DESC, u.name;
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn total_km(db: &SqlitePool, user: &User) -> Stat {
|
pub async fn total_km_tx(db: &mut Transaction<'_, Sqlite>, user: &User) -> Stat {
|
||||||
//TODO: switch to query! macro again (once upgraded to sqlite 3.42 on server)
|
//TODO: switch to query! macro again (once upgraded to sqlite 3.42 on server)
|
||||||
let row = sqlx::query(&format!(
|
let row = sqlx::query(&format!(
|
||||||
"
|
"
|
||||||
@ -233,7 +233,7 @@ WHERE l.distance_in_km IS NOT NULL;
|
|||||||
",
|
",
|
||||||
user.id
|
user.id
|
||||||
))
|
))
|
||||||
.fetch_one(db)
|
.fetch_one(db.deref_mut())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -244,7 +244,18 @@ WHERE l.distance_in_km IS NOT NULL;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn person(db: &SqlitePool, year: Option<i32>, user: &User) -> Stat {
|
pub async fn total_km(db: &SqlitePool, user: &User) -> Stat {
|
||||||
|
let mut tx = db.begin().await.unwrap();
|
||||||
|
let ret = Self::total_km_tx(&mut tx, user).await;
|
||||||
|
tx.commit().await.unwrap();
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn person_tx(
|
||||||
|
db: &mut Transaction<'_, Sqlite>,
|
||||||
|
year: Option<i32>,
|
||||||
|
user: &User,
|
||||||
|
) -> Stat {
|
||||||
let year = match year {
|
let year = match year {
|
||||||
Some(year) => year,
|
Some(year) => year,
|
||||||
None => chrono::Local::now().year(),
|
None => chrono::Local::now().year(),
|
||||||
@ -263,7 +274,7 @@ WHERE l.distance_in_km IS NOT NULL AND l.arrival LIKE '{year}-%';
|
|||||||
",
|
",
|
||||||
user.id
|
user.id
|
||||||
))
|
))
|
||||||
.fetch_one(db)
|
.fetch_one(db.deref_mut())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -273,6 +284,13 @@ WHERE l.distance_in_km IS NOT NULL AND l.arrival LIKE '{year}-%';
|
|||||||
rowed_km: row.get("rowed_km"),
|
rowed_km: row.get("rowed_km"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn person(db: &SqlitePool, year: Option<i32>, user: &User) -> Stat {
|
||||||
|
let mut tx = db.begin().await.unwrap();
|
||||||
|
let ret = Self::person_tx(&mut tx, year, user).await;
|
||||||
|
tx.commit().await.unwrap();
|
||||||
|
ret
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
|
@ -287,10 +287,8 @@ WHERE day=?
|
|||||||
return Err(TripUpdateError::NotYourTrip);
|
return Err(TripUpdateError::NotYourTrip);
|
||||||
}
|
}
|
||||||
|
|
||||||
if update.trip_type != Some(4) {
|
if update.trip_type != Some(4) && !update.cox.allowed_to_steer(db).await {
|
||||||
if !update.cox.allowed_to_steer(db).await {
|
return Err(TripUpdateError::TripTypeNotAllowed);
|
||||||
return Err(TripUpdateError::TripTypeNotAllowed);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some(trip_details_id) = update.trip.trip_details_id else {
|
let Some(trip_details_id) = update.trip.trip_details_id else {
|
||||||
|
58
src/model/user/fee.rs
Normal file
58
src/model/user/fee.rs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
use super::User;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct Fee {
|
||||||
|
pub sum_in_cents: i32,
|
||||||
|
pub parts: Vec<(String, i32)>,
|
||||||
|
pub name: String,
|
||||||
|
pub user_ids: String,
|
||||||
|
pub paid: bool,
|
||||||
|
pub users: Vec<User>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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: i32) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -15,14 +15,25 @@ use serde::{Deserialize, Serialize};
|
|||||||
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
|
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
family::Family, log::Log, logbook::Logbook, mail::Mail, notification::Notification, role::Role,
|
family::Family,
|
||||||
stat::Stat, tripdetails::TripDetails, Day,
|
log::Log,
|
||||||
|
logbook::Logbook,
|
||||||
|
mail::Mail,
|
||||||
|
notification::Notification,
|
||||||
|
personal::{equatorprice, rowingbadge},
|
||||||
|
role::Role,
|
||||||
|
stat::Stat,
|
||||||
|
tripdetails::TripDetails,
|
||||||
|
Day,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
tera::admin::user::UserEditForm, AMOUNT_DAYS_TO_SHOW_TRIPS_AHEAD, BOAT_STORAGE,
|
tera::admin::user::UserEditForm, AMOUNT_DAYS_TO_SHOW_TRIPS_AHEAD, BOAT_STORAGE,
|
||||||
EINSCHREIBGEBUEHR, FAMILY_THREE_OR_MORE, FAMILY_TWO, FOERDERND, REGULAR, RENNRUDERBEITRAG,
|
EINSCHREIBGEBUEHR, FAMILY_THREE_OR_MORE, FAMILY_TWO, FOERDERND, REGULAR, RENNRUDERBEITRAG,
|
||||||
SCHECKBUCH, STUDENT_OR_PUPIL, UNTERSTUETZEND,
|
SCHECKBUCH, STUDENT_OR_PUPIL, UNTERSTUETZEND,
|
||||||
};
|
};
|
||||||
|
use fee::Fee;
|
||||||
|
|
||||||
|
mod fee;
|
||||||
|
|
||||||
#[derive(FromRow, Serialize, Deserialize, Clone, Debug, Eq, Hash, PartialEq)]
|
#[derive(FromRow, Serialize, Deserialize, Clone, Debug, Eq, Hash, PartialEq)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
@ -83,62 +94,6 @@ pub enum LoginError {
|
|||||||
DeserializationError,
|
DeserializationError,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
pub struct Fee {
|
|
||||||
pub sum_in_cents: i32,
|
|
||||||
pub parts: Vec<(String, i32)>,
|
|
||||||
pub name: String,
|
|
||||||
pub user_ids: String,
|
|
||||||
pub paid: bool,
|
|
||||||
pub users: Vec<User>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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: i32) {
|
|
||||||
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 {
|
impl User {
|
||||||
pub async fn allowed_to_steer(&self, db: &SqlitePool) -> bool {
|
pub async fn allowed_to_steer(&self, db: &SqlitePool) -> bool {
|
||||||
self.has_role(db, "cox").await || self.has_role(db, "Bootsführer").await
|
self.has_role(db, "cox").await || self.has_role(db, "Bootsführer").await
|
||||||
@ -440,12 +395,10 @@ ASKÖ Ruderverein Donau Linz", self.name),
|
|||||||
}
|
}
|
||||||
} else if self.has_role(db, "Ehrenmitglied").await {
|
} else if self.has_role(db, "Ehrenmitglied").await {
|
||||||
fee.add("Ehrenmitglied".into(), 0);
|
fee.add("Ehrenmitglied".into(), 0);
|
||||||
|
} else if halfprice {
|
||||||
|
fee.add("Mitgliedsbeitrag (Halbpreis)".into(), REGULAR / 2);
|
||||||
} else {
|
} else {
|
||||||
if halfprice {
|
fee.add("Mitgliedsbeitrag".into(), REGULAR);
|
||||||
fee.add("Mitgliedsbeitrag (Halbpreis)".into(), REGULAR / 2);
|
|
||||||
} else {
|
|
||||||
fee.add("Mitgliedsbeitrag".into(), REGULAR);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1039,39 +992,76 @@ ORDER BY last_access DESC
|
|||||||
smtp_pw: &str,
|
smtp_pw: &str,
|
||||||
) {
|
) {
|
||||||
if self.has_role_tx(db, "scheckbuch").await {
|
if self.has_role_tx(db, "scheckbuch").await {
|
||||||
let amount_trips = Logbook::completed_with_user_tx(db, &self).await.len();
|
let amount_trips = Logbook::completed_with_user_tx(db, self).await.len();
|
||||||
if amount_trips == 5 {
|
match amount_trips {
|
||||||
if let Some(mail) = &self.mail {
|
5 => {
|
||||||
let _ = self.send_end_mail_scheckbuch(db, mail, smtp_pw).await;
|
if let Some(mail) = &self.mail {
|
||||||
|
let _ = self.send_end_mail_scheckbuch(db, mail, smtp_pw).await;
|
||||||
|
}
|
||||||
|
Notification::create_for_steering_people_tx(
|
||||||
|
db,
|
||||||
|
&format!(
|
||||||
|
"Liebe Steuerberechtigte, {} hat alle Ausfahrten des Scheckbuchs absolviert. Hoffentlich können wir uns bald über ein neues Mitglied freuen :-)",
|
||||||
|
self.name
|
||||||
|
),
|
||||||
|
"Scheckbuch fertig",
|
||||||
|
None,None
|
||||||
|
)
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
Notification::create_for_steering_people_tx(
|
a if a > 5 => {
|
||||||
db,
|
let board = Role::find_by_name_tx(db, "Vorstand").await.unwrap();
|
||||||
&format!(
|
Notification::create_for_role_tx(
|
||||||
"Liebe Steuerberechtigte, {} hat alle Ausfahrten des Scheckbuchs absolviert. Hoffentlich können wir uns bald über ein neues Mitglied freuen :-)",
|
db,
|
||||||
self.name
|
&board,
|
||||||
),
|
&format!(
|
||||||
"Scheckbuch fertig",
|
"Lieber Vorstand, {} hat nun bereits die {}. seiner 5 Scheckbuchausfahrten absolviert.",
|
||||||
None,None
|
self.name, amount_trips
|
||||||
)
|
),
|
||||||
.await;
|
"Scheckbuch überfertig",
|
||||||
} else if amount_trips > 5 {
|
None,None
|
||||||
let board = Role::find_by_name_tx(db, "Vorstand").await.unwrap();
|
)
|
||||||
Notification::create_for_role_tx(
|
.await;
|
||||||
db,
|
}
|
||||||
&board,
|
_ => {}
|
||||||
&format!(
|
|
||||||
"Lieber Vorstand, {} hat nun bereits die {}. seiner 5 Scheckbuchausfahrten absolviert.",
|
|
||||||
self.name, amount_trips
|
|
||||||
),
|
|
||||||
"Scheckbuch überfertig",
|
|
||||||
None,None
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: check fahrtenabzeichen fertig?
|
// check fahrtenabzeichen fertig
|
||||||
// TODO: check äquatorpreis geschafft?
|
if rowingbadge::Status::completed_with_last_log(db, self).await {
|
||||||
|
let board = Role::find_by_name_tx(db, "Vorstand").await.unwrap();
|
||||||
|
Notification::create_for_role_tx(
|
||||||
|
db,
|
||||||
|
&board,
|
||||||
|
&format!(
|
||||||
|
"Lieber Vorstand, zur Info: {} hat gerade alle Anforderungen für das diesjährige Fahrtenabzeichen erfüllt.",
|
||||||
|
self.name
|
||||||
|
),
|
||||||
|
"Fahrtenabzeichen geschafft",
|
||||||
|
None,None
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Notification::create_with_tx(db, self, "Mit deiner letzten Ausfahrt hast du nun alle Anforderungen für das heurige Fahrtenzeichen erfüllt. Gratuliere! 🎉", "Fahrtenabzeichen geschafft", None, None).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check äquatorpreis geschafft?
|
||||||
|
if let Some(level) = equatorprice::new_level_with_last_log(db, self).await {
|
||||||
|
let board = Role::find_by_name_tx(db, "Vorstand").await.unwrap();
|
||||||
|
Notification::create_for_role_tx(
|
||||||
|
db,
|
||||||
|
&board,
|
||||||
|
&format!(
|
||||||
|
"Lieber Vorstand, zur Info: {} hat gerade alle Anforderungen für den Äquatorpreis in {level} geschafft.",
|
||||||
|
self.name
|
||||||
|
),
|
||||||
|
"Äquatorpreis",
|
||||||
|
None,None
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Notification::create_with_tx(db, self, &format!("Mit deiner letzten Ausfahrt erfüllst du nun alle Anforderungen für den Äquatorpreis in {level}. Gratuliere! 🎉"), "Äquatorpreis", None, None).await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -408,7 +408,7 @@ async fn create_scheckbuch(
|
|||||||
format!("{} created new scheckbuch: {data:?}", admin.name),
|
format!("{} created new scheckbuch: {data:?}", admin.name),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
Flash::success(Redirect::to("/admin/user/scheckbuch"), &format!("Scheckbuch erfolgreich erstellt. Eine E-Mail in der alles erklärt wird, wurde an {mail} verschickt."))
|
Flash::success(Redirect::to("/admin/user/scheckbuch"), format!("Scheckbuch erfolgreich erstellt. Eine E-Mail in der alles erklärt wird, wurde an {mail} verschickt."))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/user/move/schnupperant/<id>/to/scheckbuch")]
|
#[get("/user/move/schnupperant/<id>/to/scheckbuch")]
|
||||||
@ -458,7 +458,7 @@ async fn schnupper_to_scheckbuch(
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
Flash::success(Redirect::to("/admin/schnupper"), &format!("Scheckbuch erfolgreich erstellt. Eine E-Mail in der alles erklärt wird, wurde an {} verschickt.", user.mail.unwrap()))
|
Flash::success(Redirect::to("/admin/schnupper"), format!("Scheckbuch erfolgreich erstellt. Eine E-Mail in der alles erklärt wird, wurde an {} verschickt.", user.mail.unwrap()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn routes() -> Vec<Route> {
|
pub fn routes() -> Vec<Route> {
|
||||||
|
@ -18,12 +18,12 @@ async fn index(
|
|||||||
context.insert("flash", &msg.into_inner());
|
context.insert("flash", &msg.into_inner());
|
||||||
}
|
}
|
||||||
|
|
||||||
let role = Role::find_by_name(&db, "Donau Linz").await.unwrap();
|
let role = Role::find_by_name(db, "Donau Linz").await.unwrap();
|
||||||
let users = User::all_with_role(&db, &role).await;
|
let users = User::all_with_role(db, &role).await;
|
||||||
let mut people = Vec::new();
|
let mut people = Vec::new();
|
||||||
let mut rowingbadge_year = None;
|
let mut rowingbadge_year = None;
|
||||||
for user in users {
|
for user in users {
|
||||||
let achievement = Achievements::for_user(&db, &user).await;
|
let achievement = Achievements::for_user(db, &user).await;
|
||||||
if let Some(badge) = &achievement.rowingbadge {
|
if let Some(badge) = &achievement.rowingbadge {
|
||||||
rowingbadge_year = Some(badge.year);
|
rowingbadge_year = Some(badge.year);
|
||||||
}
|
}
|
||||||
|
@ -148,13 +148,13 @@ async fn fixed<'r>(
|
|||||||
|
|
||||||
#[derive(FromForm)]
|
#[derive(FromForm)]
|
||||||
pub struct FormBoatDamageVerified<'r> {
|
pub struct FormBoatDamageVerified<'r> {
|
||||||
pub desc: &'r str,
|
desc: &'r str,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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>,
|
||||||
data: Form<FormBoatDamageFixed<'r>>,
|
data: Form<FormBoatDamageVerified<'r>>,
|
||||||
boatdamage_id: i32,
|
boatdamage_id: i32,
|
||||||
techuser: TechUser,
|
techuser: TechUser,
|
||||||
) -> Flash<Redirect> {
|
) -> Flash<Redirect> {
|
||||||
|
@ -217,7 +217,7 @@ async fn new_thirty(
|
|||||||
eprintln!("Failed to persist file: {:?}", e);
|
eprintln!("Failed to persist file: {:?}", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = data.result.trim_start_matches(|c| c == '0' || c == ' ');
|
let result = data.result.trim_start_matches(['0', ' ']);
|
||||||
|
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"UPDATE user SET dirty_thirty = ? where id = ?",
|
"UPDATE user SET dirty_thirty = ? where id = ?",
|
||||||
@ -318,7 +318,7 @@ async fn new_dozen(
|
|||||||
if let Err(e) = data.proof.move_copy_to(file_path).await {
|
if let Err(e) = data.proof.move_copy_to(file_path).await {
|
||||||
eprintln!("Failed to persist file: {:?}", e);
|
eprintln!("Failed to persist file: {:?}", e);
|
||||||
}
|
}
|
||||||
let result = data.result.trim_start_matches(|c| c == '0' || c == ' ');
|
let result = data.result.trim_start_matches(['0', ' ']);
|
||||||
let result = if result.contains(":") || result.contains(".") {
|
let result = if result.contains(":") || result.contains(".") {
|
||||||
format_time(result)
|
format_time(result)
|
||||||
} else {
|
} else {
|
||||||
|
@ -312,7 +312,7 @@ async fn update(
|
|||||||
let data = data.into_inner();
|
let data = data.into_inner();
|
||||||
|
|
||||||
let Some(logbook) = Logbook::find_by_id(db, data.id).await else {
|
let Some(logbook) = Logbook::find_by_id(db, data.id).await else {
|
||||||
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.user).await {
|
match logbook.update(db, data.clone(), &user.user).await {
|
||||||
|
@ -19,7 +19,7 @@ async fn cal_registered(
|
|||||||
return Err("Invalid".into());
|
return Err("Invalid".into());
|
||||||
};
|
};
|
||||||
|
|
||||||
if &user.user_token != uuid {
|
if user.user_token != uuid {
|
||||||
return Err("Invalid".into());
|
return Err("Invalid".into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,13 +4,12 @@
|
|||||||
{% extends "base" %}
|
{% extends "base" %}
|
||||||
{% macro show_place(aisle_name, side_name, level) %}
|
{% macro show_place(aisle_name, side_name, level) %}
|
||||||
<li class="truncate p-2 flex relative w-full">
|
<li class="truncate p-2 flex relative w-full">
|
||||||
{% set aisle = aisle_name ~ "-aisle" %}
|
{% set place = boathouse[aisle_name][side_name].boats %}
|
||||||
{% set place = boathouse[aisle][side_name] %}
|
|
||||||
{% if place[level] %}
|
{% if place[level] %}
|
||||||
{{ place[level].1.name }}
|
{{ place[level].boat.name }}
|
||||||
{% if "admin" in loggedin_user.roles %}
|
{% if "admin" in loggedin_user.roles %}
|
||||||
<a class="btn btn-primary absolute end-0"
|
<a class="btn btn-primary absolute end-0"
|
||||||
href="/board/boathouse/{{ place[level].0 }}/delete">X</a>
|
href="/board/boathouse/{{ place[level].boathouse_id }}/delete">X</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% elif boats | length > 0 %}
|
{% elif boats | length > 0 %}
|
||||||
{% if "admin" in loggedin_user.roles %}
|
{% if "admin" in loggedin_user.roles %}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user