membership-pdf-new #284
| @@ -226,6 +226,38 @@ ORDER BY departure DESC | |||||||
|         ret |         ret | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     pub async fn completed_with_user( | ||||||
|  |         db: &SqlitePool, | ||||||
|  |         user: &User, | ||||||
|  |     ) -> Vec<LogbookWithBoatAndRowers> { | ||||||
|  |         let logs = sqlx::query_as( | ||||||
|  |                &format!(" | ||||||
|  |     SELECT id, boat_id, shipmaster, steering_person, shipmaster_only_steering, departure, arrival, destination, distance_in_km, comments, logtype | ||||||
|  |     FROM logbook | ||||||
|  |     JOIN rower ON logbook.id = rower.logbook_id | ||||||
|  |     WHERE arrival is not null AND rower_id = {} | ||||||
|  |     ORDER BY departure DESC | ||||||
|  |             ",  user.id) | ||||||
|  |             ) | ||||||
|  |             .fetch_all(db) | ||||||
|  |             .await | ||||||
|  |             .unwrap(); //TODO: fixme | ||||||
|  |  | ||||||
|  |         let mut ret = Vec::new(); | ||||||
|  |         for log in logs { | ||||||
|  |             ret.push(LogbookWithBoatAndRowers { | ||||||
|  |                 rowers: Rower::for_log(db, &log).await, | ||||||
|  |                 boat: Boat::find_by_id(db, log.boat_id as i32).await.unwrap(), | ||||||
|  |                 shipmaster_user: User::find_by_id(db, log.shipmaster as i32).await.unwrap(), | ||||||
|  |                 steering_user: User::find_by_id(db, log.steering_person as i32) | ||||||
|  |                     .await | ||||||
|  |                     .unwrap(), | ||||||
|  |                 logbook: log, | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  |         ret | ||||||
|  |     } | ||||||
|  |  | ||||||
|     pub async fn completed(db: &SqlitePool) -> Vec<LogbookWithBoatAndRowers> { |     pub async fn completed(db: &SqlitePool) -> Vec<LogbookWithBoatAndRowers> { | ||||||
|         let year = chrono::Utc::now().year(); |         let year = chrono::Utc::now().year(); | ||||||
|         let logs = sqlx::query_as( |         let logs = sqlx::query_as( | ||||||
|   | |||||||
| @@ -225,28 +225,6 @@ impl User { | |||||||
|         .count |         .count | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pub async fn rowed_km(&self, db: &SqlitePool) -> i32 { |  | ||||||
|         sqlx::query!( |  | ||||||
|             "SELECT COALESCE(SUM(distance_in_km),0) as rowed_km |  | ||||||
|         FROM ( |  | ||||||
|             SELECT distance_in_km |  | ||||||
|             FROM logbook  |  | ||||||
|             WHERE shipmaster = ?1 |  | ||||||
|             UNION   |  | ||||||
|             SELECT l.distance_in_km  |  | ||||||
|             FROM logbook l |  | ||||||
|             INNER JOIN rower r ON r.logbook_id = l.id  |  | ||||||
|             WHERE r.rower_id = ?1 |  | ||||||
|          |  | ||||||
|             );", |  | ||||||
|             self.id, |  | ||||||
|         ) |  | ||||||
|         .fetch_one(db) |  | ||||||
|         .await |  | ||||||
|         .unwrap() |  | ||||||
|         .rowed_km |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     pub async fn has_role(&self, db: &SqlitePool, role: &str) -> bool { |     pub async fn has_role(&self, db: &SqlitePool, role: &str) -> bool { | ||||||
|         if sqlx::query!( |         if sqlx::query!( | ||||||
|             "SELECT * FROM user_role WHERE user_id=? AND role_id = (SELECT id FROM role WHERE name = ?)", |             "SELECT * FROM user_role WHERE user_id=? AND role_id = (SELECT id FROM role WHERE name = ?)", | ||||||
| @@ -379,6 +357,22 @@ ORDER BY last_access DESC | |||||||
|         .unwrap() |         .unwrap() | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     pub async fn all_with_role(db: &SqlitePool, role: &Role) -> Vec<Self> { | ||||||
|  |         sqlx::query_as!( | ||||||
|  |             Self, | ||||||
|  |             " | ||||||
|  | SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, notes, phone, address, family_id | ||||||
|  | FROM user u | ||||||
|  | JOIN user_role ur ON u.id = ur.user_id | ||||||
|  | WHERE ur.role_id = ? AND deleted = 0 | ||||||
|  | ORDER BY name; | ||||||
|  |         ", role.id | ||||||
|  |         ) | ||||||
|  |         .fetch_all(db) | ||||||
|  |         .await | ||||||
|  |         .unwrap() | ||||||
|  |     } | ||||||
|  |  | ||||||
|     pub async fn all_payer_groups(db: &SqlitePool) -> Vec<Self> { |     pub async fn all_payer_groups(db: &SqlitePool) -> Vec<Self> { | ||||||
|         sqlx::query_as!( |         sqlx::query_as!( | ||||||
|             Self, |             Self, | ||||||
|   | |||||||
| @@ -2,20 +2,38 @@ use std::collections::HashMap; | |||||||
|  |  | ||||||
| use crate::model::{ | use crate::model::{ | ||||||
|     family::Family, |     family::Family, | ||||||
|  |     logbook::Logbook, | ||||||
|     role::Role, |     role::Role, | ||||||
|     user::{AdminUser, User, UserWithRoles, VorstandUser}, |     user::{AdminUser, User, UserWithRoles, VorstandUser}, | ||||||
| }; | }; | ||||||
| use futures::future::join_all; | use futures::future::join_all; | ||||||
| use rocket::{ | use rocket::{ | ||||||
|     form::Form, |     form::Form, | ||||||
|     get, post, |     get, | ||||||
|     request::FlashMessage, |     http::Status, | ||||||
|  |     post, | ||||||
|  |     request::{FlashMessage, FromRequest, Outcome}, | ||||||
|     response::{Flash, Redirect}, |     response::{Flash, Redirect}, | ||||||
|     routes, FromForm, Route, State, |     routes, FromForm, Request, Route, State, | ||||||
| }; | }; | ||||||
| use rocket_dyn_templates::{tera::Context, Template}; | use rocket_dyn_templates::{tera::Context, Template}; | ||||||
| use sqlx::SqlitePool; | use sqlx::SqlitePool; | ||||||
|  |  | ||||||
|  | // Custom request guard to extract the Referer header | ||||||
|  | struct Referer(String); | ||||||
|  |  | ||||||
|  | #[rocket::async_trait] | ||||||
|  | impl<'r> FromRequest<'r> for Referer { | ||||||
|  |     type Error = (); | ||||||
|  |  | ||||||
|  |     async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> { | ||||||
|  |         match request.headers().get_one("Referer") { | ||||||
|  |             Some(referer) => Outcome::Success(Referer(referer.to_string())), | ||||||
|  |             None => Outcome::Error((Status::BadRequest, ())), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| #[get("/user")] | #[get("/user")] | ||||||
| async fn index( | async fn index( | ||||||
|     db: &State<SqlitePool>, |     db: &State<SqlitePool>, | ||||||
| @@ -111,11 +129,43 @@ async fn fees( | |||||||
|     Template::render("admin/user/fees", context.into_json()) |     Template::render("admin/user/fees", context.into_json()) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #[get("/user/scheckbuch")] | ||||||
|  | async fn scheckbuch( | ||||||
|  |     db: &State<SqlitePool>, | ||||||
|  |     user: VorstandUser, | ||||||
|  |     flash: Option<FlashMessage<'_>>, | ||||||
|  | ) -> Template { | ||||||
|  |     let mut context = Context::new(); | ||||||
|  |  | ||||||
|  |     let scheckbooks = Role::find_by_name(db, "scheckbuch").await.unwrap(); | ||||||
|  |     let scheckbooks = User::all_with_role(db, &scheckbooks).await; | ||||||
|  |     let mut scheckbooks_with_roles = Vec::new(); | ||||||
|  |     for s in scheckbooks { | ||||||
|  |         scheckbooks_with_roles.push(( | ||||||
|  |             Logbook::completed_with_user(db, &s).await, | ||||||
|  |             UserWithRoles::from_user(s, db).await, | ||||||
|  |         )) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     context.insert("scheckbooks", &scheckbooks_with_roles); | ||||||
|  |  | ||||||
|  |     if let Some(msg) = flash { | ||||||
|  |         context.insert("flash", &msg.into_inner()); | ||||||
|  |     } | ||||||
|  |     context.insert( | ||||||
|  |         "loggedin_user", | ||||||
|  |         &UserWithRoles::from_user(user.into(), db).await, | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     Template::render("admin/user/scheckbuch", context.into_json()) | ||||||
|  | } | ||||||
|  |  | ||||||
| #[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, |     _admin: AdminUser, | ||||||
|     user_ids: Vec<i32>, |     user_ids: Vec<i32>, | ||||||
|  |     referer: Referer, | ||||||
| ) -> Flash<Redirect> { | ) -> Flash<Redirect> { | ||||||
|     let mut res = String::new(); |     let mut res = String::new(); | ||||||
|     for user_id in user_ids { |     for user_id in user_ids { | ||||||
| @@ -133,7 +183,7 @@ async fn fees_paid( | |||||||
|     res.truncate(res.len() - 3); // remove ' + ' from the end |     res.truncate(res.len() - 3); // remove ' + ' from the end | ||||||
|  |  | ||||||
|     Flash::success( |     Flash::success( | ||||||
|         Redirect::to("/admin/user/fees"), |         Redirect::to(referer.0), | ||||||
|         format!("Zahlungsstatus von {} erfolgreich geändert", res), |         format!("Zahlungsstatus von {} erfolgreich geändert", res), | ||||||
|     ) |     ) | ||||||
| } | } | ||||||
| @@ -234,6 +284,7 @@ pub fn routes() -> Vec<Route> { | |||||||
|         create, |         create, | ||||||
|         delete, |         delete, | ||||||
|         fees, |         fees, | ||||||
|         fees_paid |         fees_paid, | ||||||
|  |         scheckbuch | ||||||
|     ] |     ] | ||||||
| } | } | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ use tera::Context; | |||||||
|  |  | ||||||
| use crate::model::{ | use crate::model::{ | ||||||
|     log::Log, |     log::Log, | ||||||
|  |     logbook::Logbook, | ||||||
|     tripdetails::TripDetails, |     tripdetails::TripDetails, | ||||||
|     triptype::TripType, |     triptype::TripType, | ||||||
|     user::{AllowedForPlannedTripsUser, User, UserWithRoles}, |     user::{AllowedForPlannedTripsUser, User, UserWithRoles}, | ||||||
| @@ -31,6 +32,11 @@ async fn index( | |||||||
|         context.insert("trip_types", &triptypes); |         context.insert("trip_types", &triptypes); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if user.has_role(db, "scheckbuch").await { | ||||||
|  |         let last_trips = Logbook::completed_with_user(db, &user).await; | ||||||
|  |         context.insert("last_trips", &last_trips); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     let days = user.get_days(db).await; |     let days = user.get_days(db).await; | ||||||
|  |  | ||||||
|     if let Some(msg) = flash { |     if let Some(msg) = flash { | ||||||
|   | |||||||
| @@ -4,10 +4,8 @@ | |||||||
|     {% if flash %}{{ macros::alert(message=flash.1, type=flash.0, class="sm:col-span-2 lg:col-span-3") }}{% endif %} |     {% if flash %}{{ macros::alert(message=flash.1, type=flash.0, class="sm:col-span-2 lg:col-span-3") }}{% endif %} | ||||||
|     <div class="max-w-screen-lg w-full"> |     <div class="max-w-screen-lg w-full"> | ||||||
|         <h1 class="h1">List - Result</h1> |         <h1 class="h1">List - Result</h1> | ||||||
| 	<ol> |         <ol> | ||||||
| 	{% for person in result%} |             {% for person in result %}<li>{{ person }}</li>{% endfor %} | ||||||
| 		<li>{{person}}</li> |         </ol> | ||||||
| 	{% endfor %} |  | ||||||
| 	</ol> |  | ||||||
|     </div> |     </div> | ||||||
| {% endblock content %} | {% endblock content %} | ||||||
|   | |||||||
							
								
								
									
										43
									
								
								templates/admin/user/scheckbuch.html.tera
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								templates/admin/user/scheckbuch.html.tera
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | |||||||
|  | {% import "includes/macros" as macros %} | ||||||
|  | {% import "includes/forms/log" as log %} | ||||||
|  | {% extends "base" %} | ||||||
|  | {% block content %} | ||||||
|  |     <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 %}{{ macros::alert(message=flash.1, type=flash.0, class="sm:col-span-2 lg:col-span-3") }}{% endif %} | ||||||
|  |         <h1 class="h1">Scheckbücher</h1> | ||||||
|  |         <!-- START filterBar --> | ||||||
|  |         <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 scheckbook in scheckbooks %} | ||||||
|  |                 {% set user = scheckbook.1 %} | ||||||
|  |                 {% set trips = scheckbook.0 %} | ||||||
|  |                 <div {% if "paid" in user.roles %}style="background-color: green;"{% endif %} | ||||||
|  |                      data-filterable="true" | ||||||
|  |                      data-filter="{{ user.name }} {% if "paid" in user.roles %} has-already-paid {% else %} has-not-paid {% endif %}" | ||||||
|  |                      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>{{ user.name }} - {{ trips | length }} Ausfahrten</b> | ||||||
|  |                             {% for trip in trips %} | ||||||
|  |                                 <li>{{ log::show_old(log=trip, state="completed", only_ones=false, index=loop.index) }}</li> | ||||||
|  |                             {% endfor %} | ||||||
|  |                         </div> | ||||||
|  |                         {% if "admin" in loggedin_user.roles %} | ||||||
|  |                             <a href="/admin/user/fees/paid?user_ids[]={{ user.id }}">Zahlungsstatus ändern</a> | ||||||
|  |                         {% endif %} | ||||||
|  |                     </div> | ||||||
|  |                 </div> | ||||||
|  |             {% endfor %} | ||||||
|  |         </div> | ||||||
|  |     </div> | ||||||
|  | {% endblock content %} | ||||||
| @@ -181,7 +181,9 @@ | |||||||
|         {% endif %} |         {% endif %} | ||||||
|         <div {% if log.logtype %}class="mt-4 sm:mt-0"{% endif %}> |         <div {% if log.logtype %}class="mt-4 sm:mt-0"{% endif %}> | ||||||
|             <strong class="text-black dark:text-white">{{ log.boat.name }}</strong> <small class="text-gray-600 dark:text-gray-100">({{ log.shipmaster_user.name -}} |             <strong class="text-black dark:text-white">{{ log.boat.name }}</strong> <small class="text-gray-600 dark:text-gray-100">({{ log.shipmaster_user.name -}} | ||||||
|                 {% if log.shipmaster_only_steering %}- handgesteuert{% endif -%} |                 {% if log.shipmaster_only_steering %} | ||||||
|  |                     - handgesteuert | ||||||
|  |                 {% endif -%} | ||||||
|             )</small> |             )</small> | ||||||
|             <small class="block text-gray-600 dark:text-gray-100"> |             <small class="block text-gray-600 dark:text-gray-100"> | ||||||
|                 {% if state == "completed" and log.departure | date(format='%d.%m.%Y') == log.arrival | date(format='%d.%m.%Y') %} |                 {% if state == "completed" and log.departure | date(format='%d.%m.%Y') == log.arrival | date(format='%d.%m.%Y') %} | ||||||
|   | |||||||
| @@ -89,6 +89,9 @@ | |||||||
|                             <li class="py-1"> |                             <li class="py-1"> | ||||||
|                                 <a href="/admin/user/fees" class="link-primary">Übersicht User Gebühren</a> |                                 <a href="/admin/user/fees" class="link-primary">Übersicht User Gebühren</a> | ||||||
|                             </li> |                             </li> | ||||||
|  |                             <li class="py-1"> | ||||||
|  |                                 <a href="/admin/user/scheckbuch" class="link-primary">Scheckbuch</a> | ||||||
|  |                             </li> | ||||||
|                             <li class="py-1"> |                             <li class="py-1"> | ||||||
|                                 <a href="/admin/user" class="link-primary">User</a> |                                 <a href="/admin/user" class="link-primary">User</a> | ||||||
|                             </li> |                             </li> | ||||||
|   | |||||||
| @@ -1,9 +1,26 @@ | |||||||
| {% import "includes/macros" as macros %} | {% import "includes/macros" as macros %} | ||||||
|  | {% import "includes/forms/log" as log %} | ||||||
| {% extends "base" %} | {% extends "base" %} | ||||||
| {% block content %} | {% block content %} | ||||||
|     <div class="max-w-screen-xl w-full grid sm:grid-cols-2 lg:grid-cols-3 gap-4"> |     <div class="max-w-screen-xl w-full grid sm:grid-cols-2 lg:grid-cols-3 gap-4"> | ||||||
|         {% if flash %}{{ macros::alert(message=flash.1, type=flash.0, class="sm:col-span-2 lg:col-span-3") }}{% endif %} |         {% if flash %}{{ macros::alert(message=flash.1, type=flash.0, class="sm:col-span-2 lg:col-span-3") }}{% endif %} | ||||||
|         {% if "paid" not in loggedin_user.roles %} |         {% if "scheckbuch" in loggedin_user.roles %} | ||||||
|  |             <div class="grid gap-3 sm:col-span-2 lg:col-span-3"> | ||||||
|  |                 <div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5" | ||||||
|  |                      role="alert"> | ||||||
|  |                     <h2 class="h2">Scheckbuch</h2> | ||||||
|  |                     <div class="text-sm p-3"> | ||||||
|  |                         <h3>Du hast bisher {{ last_trips | length }} deiner 5 Scheckbuch-Ausfahrten gemacht:</h3> | ||||||
|  |                         <ol> | ||||||
|  |                             {% for last_trip in last_trips %} | ||||||
|  |                                 <li>{{ log::show_old(log=last_trip, state="completed", only_ones=false, index=loop.index) }}</li> | ||||||
|  |                             {% endfor %} | ||||||
|  |                         </ol> | ||||||
|  |                     </div> | ||||||
|  |                 </div> | ||||||
|  |             </div> | ||||||
|  |         {% endif %} | ||||||
|  |         {% if "paid" not in loggedin_user.roles and "Donau Linz" in loggedin_user.roles %} | ||||||
|             <div class="grid gap-3 sm:col-span-2 lg:col-span-3"> |             <div class="grid gap-3 sm:col-span-2 lg:col-span-3"> | ||||||
|                 <div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5" |                 <div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5" | ||||||
|                      role="alert"> |                      role="alert"> | ||||||
| @@ -238,7 +255,9 @@ | |||||||
|                                                 <strong class="text-primary-900 dark:text-white">{{ trip.planned_starting_time }} |                                                 <strong class="text-primary-900 dark:text-white">{{ trip.planned_starting_time }} | ||||||
|                                                 Uhr</strong> |                                                 Uhr</strong> | ||||||
|                                                 <small class="text-gray-600 dark:text-gray-100">({{ trip.cox_name -}} |                                                 <small class="text-gray-600 dark:text-gray-100">({{ trip.cox_name -}} | ||||||
|                                                     {% if trip.trip_type %}- {{ trip.trip_type.icon | safe }} {{ trip.trip_type.name }}{% endif -%} |                                                     {% if trip.trip_type %} | ||||||
|  |                                                         - {{ trip.trip_type.icon | safe }} {{ trip.trip_type.name }} | ||||||
|  |                                                     {% endif -%} | ||||||
|                                                 )</small> |                                                 )</small> | ||||||
|                                             {% endif %} |                                             {% endif %} | ||||||
|                                             <br /> |                                             <br /> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user