restrict business camp overview by current year

This commit is contained in:
Jottyfan
2025-07-19 20:29:23 +02:00
parent 27c1669ed3
commit 30c2bd5fc0
9 changed files with 74 additions and 25 deletions

View File

@@ -12,6 +12,13 @@
<attribute name="gradle_used_by_scope" value="main,test"/> <attribute name="gradle_used_by_scope" value="main,test"/>
</attributes> </attributes>
</classpathentry> </classpathentry>
<classpathentry kind="src" output="bin/test" path="src/test/java">
<attributes>
<attribute name="gradle_scope" value="test"/>
<attribute name="gradle_used_by_scope" value="test"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17/"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17/"/>
<classpathentry kind="con" path="org.eclipse.jst.j2ee.internal.web.container"/> <classpathentry kind="con" path="org.eclipse.jst.j2ee.internal.web.container"/>
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"> <classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer">

View File

@@ -1,8 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?><project-modules id="moduleCoreId" project-version="1.5.0">
<project-modules id="moduleCoreId" project-version="1.5.0">
<wb-module deploy-name="CampOrganizer2"> <wb-module deploy-name="CampOrganizer2">
<property name="context-root" value="CampOrganizer2"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="src/main/resources"/> <property name="context-root" value="CampOrganizer2"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="src/main/java"/>
</wb-module> <wb-resource deploy-path="/WEB-INF/classes" source-path="src/main/resources"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="src/main/java"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/test/java"/>
</wb-module>
</project-modules> </project-modules>

View File

@@ -8,7 +8,7 @@ plugins {
} }
group = 'de.jottyfan.camporganizer' group = 'de.jottyfan.camporganizer'
version = '0.9.7' version = '0.9.8'
description = """CampOrganizer2""" description = """CampOrganizer2"""

View File

@@ -1,10 +1,11 @@
package de.jottyfan.camporganizer.module.business.bookings; package de.jottyfan.camporganizer.module.business.bookings;
import jakarta.annotation.security.RolesAllowed; import java.time.LocalDate;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
@@ -16,6 +17,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import de.jottyfan.camporganizer.module.business.bookings.model.AddPaymentBean; import de.jottyfan.camporganizer.module.business.bookings.model.AddPaymentBean;
import de.jottyfan.camporganizer.module.business.bookings.model.BookerBean; import de.jottyfan.camporganizer.module.business.bookings.model.BookerBean;
import de.jottyfan.camporganizer.module.camplist.CommonController; import de.jottyfan.camporganizer.module.camplist.CommonController;
import jakarta.annotation.security.RolesAllowed;
/** /**
* *
@@ -29,21 +31,23 @@ public class BookingsController extends CommonController {
@Autowired @Autowired
private BookingsService bookingsService; private BookingsService bookingsService;
@GetMapping("/business/bookings") @GetMapping("/business/bookings/{year}")
@RolesAllowed({"business_booking"}) @RolesAllowed({"business_booking"})
public String getBookings(Model model) { public String getBookings(Model model, @Nullable @PathVariable("year") Integer year) {
model.addAttribute("bookers", bookingsService.getBookers(getCurrentUser())); model.addAttribute("bookers", bookingsService.getBookers(getCurrentUser(), year));
model.addAttribute("addBean", new AddPaymentBean()); model.addAttribute("addBean", new AddPaymentBean());
model.addAttribute("year", LocalDate.now().getYear());
model.addAttribute("years", bookingsService.getAllCampYears());
return "business/bookings"; return "business/bookings";
} }
@GetMapping("/business/bookings/{id}") @GetMapping("/business/bookings_by_id/{id}")
@RolesAllowed({"business_booking"}) @RolesAllowed({"business_booking"})
public String getBooking(Model model, @PathVariable("id") Integer id) { public String getBooking(Model model, @PathVariable("id") Integer id) {
BookerBean bean = bookingsService.getBooker(id, getCurrentUser()); BookerBean bean = bookingsService.getBooker(id, getCurrentUser());
model.addAttribute("booker", bean); model.addAttribute("booker", bean);
model.addAttribute("addBean", new AddPaymentBean()); model.addAttribute("addBean", new AddPaymentBean());
return bean == null ? getBookings(model) : "business/booker"; return bean == null ? getBookings(model, LocalDate.now().getYear()) : "business/booker";
} }
@PostMapping("/business/bookings/payment/{id}") @PostMapping("/business/bookings/payment/{id}")
@@ -54,13 +58,13 @@ public class BookingsController extends CommonController {
return getBooking(model, id); return getBooking(model, id);
} }
@PostMapping("/business/bookings/listpayment/{id}") @PostMapping("/business/bookings/listpayment/{id}/{year}")
@RolesAllowed({"business_booking"}) @RolesAllowed({"business_booking"})
public String addListPayment(Model model, @ModelAttribute("bean") AddPaymentBean bean, @PathVariable("id") Integer id, @RequestParam(defaultValue = "") String search) { public String addListPayment(Model model, @ModelAttribute("bean") AddPaymentBean bean, @PathVariable("id") Integer id, @PathVariable("year") Integer year, @RequestParam(defaultValue = "") String search) {
Double payment = bean.getPayment(); Double payment = bean.getPayment();
bookingsService.addPayment(id, payment); bookingsService.addPayment(id, payment);
LOGGER.debug("search is {}", search); LOGGER.debug("search is {}", search);
model.addAttribute("search", search); model.addAttribute("search", search);
return getBookings(model); return getBookings(model, year);
} }
} }

View File

@@ -15,11 +15,14 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.jooq.DSLContext; import org.jooq.DSLContext;
import org.jooq.Record; import org.jooq.Record;
import org.jooq.Record1;
import org.jooq.Record10; import org.jooq.Record10;
import org.jooq.Record13; import org.jooq.Record13;
import org.jooq.SelectConditionStep; import org.jooq.SelectConditionStep;
import org.jooq.SelectSeekStep1;
import org.jooq.SelectSeekStep4; import org.jooq.SelectSeekStep4;
import org.jooq.UpdateConditionStep; import org.jooq.UpdateConditionStep;
import org.jooq.impl.DSL;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@@ -49,10 +52,11 @@ public class BookingsRepository {
* get a list of all registered persons that have booked the camp * get a list of all registered persons that have booked the camp
* *
* @param username the name of the current user in this session * @param username the name of the current user in this session
* @param year the year of the camp; may be null
* *
* @return a list of bookings; an empty one at least * @return a list of bookings; an empty one at least
*/ */
public List<BookerBean> getBookings(String username) { public List<BookerBean> getBookings(String username, Integer year) {
SelectSeekStep4<Record10<Integer, EnumProgress, BigDecimal, String, String, EnumCamprole, EnumSex, LocalDateTime, String, Double>, EnumCamprole, EnumSex, String, String> sql = jooq SelectSeekStep4<Record10<Integer, EnumProgress, BigDecimal, String, String, EnumCamprole, EnumSex, LocalDateTime, String, Double>, EnumCamprole, EnumSex, String, String> sql = jooq
// @formatter:off // @formatter:off
.select(T_PERSON.PK, T_PERSON.PROGRESS, T_PERSON.PAID, T_PERSON.FORENAME, T_PERSON.SURNAME, T_PERSON.CAMPROLE, T_PERSON.SEX, T_PERSON.CREATED, V_CAMP.NAME, V_CAMP.YEAR) .select(T_PERSON.PK, T_PERSON.PROGRESS, T_PERSON.PAID, T_PERSON.FORENAME, T_PERSON.SURNAME, T_PERSON.CAMPROLE, T_PERSON.SEX, T_PERSON.CREATED, V_CAMP.NAME, V_CAMP.YEAR)
@@ -61,6 +65,7 @@ public class BookingsRepository {
.leftJoin(T_CAMPPROFILE).on(T_CAMPPROFILE.FK_CAMP.eq(T_PERSON.FK_CAMP)).and(T_CAMPPROFILE.MODULE.eq(EnumModule.business)) .leftJoin(T_CAMPPROFILE).on(T_CAMPPROFILE.FK_CAMP.eq(T_PERSON.FK_CAMP)).and(T_CAMPPROFILE.MODULE.eq(EnumModule.business))
.leftJoin(T_PROFILE).on(T_PROFILE.PK.eq(T_CAMPPROFILE.FK_PROFILE)) .leftJoin(T_PROFILE).on(T_PROFILE.PK.eq(T_CAMPPROFILE.FK_PROFILE))
.where(T_PROFILE.USERNAME.eq(username)) .where(T_PROFILE.USERNAME.eq(username))
.and(year != null ? V_CAMP.YEAR.eq(year.doubleValue()) : DSL.trueCondition())
.orderBy(T_PERSON.CAMPROLE, T_PERSON.SEX, T_PERSON.SURNAME, T_PERSON.FORENAME); .orderBy(T_PERSON.CAMPROLE, T_PERSON.SEX, T_PERSON.SURNAME, T_PERSON.FORENAME);
// @formatter:on // @formatter:on
LOGGER.trace(sql); LOGGER.trace(sql);
@@ -168,4 +173,20 @@ public class BookingsRepository {
LOGGER.trace(sql); LOGGER.trace(sql);
return sql.execute(); return sql.execute();
} }
/**
* get an ordered list of all camp years found in the database
*
* @return the list of camp years
*/
public List<Integer> getAllCampYears() {
SelectSeekStep1<Record1<Double>, Double> sql = jooq
// @formatter:off
.selectDistinct(V_CAMP.YEAR)
.from(V_CAMP)
.orderBy(V_CAMP.YEAR);
// @formatter:on
LOGGER.trace(sql);
return sql.fetchInto(Integer.class);
}
} }

View File

@@ -18,8 +18,8 @@ public class BookingsService {
@Autowired @Autowired
private BookingsRepository bookingsGateway; private BookingsRepository bookingsGateway;
public List<BookerBean> getBookers(String username) { public List<BookerBean> getBookers(String username, Integer year) {
return bookingsGateway.getBookings(username); return bookingsGateway.getBookings(username, year);
} }
public BookerBean getBooker(Integer id, String username) { public BookerBean getBooker(Integer id, String username) {
@@ -29,4 +29,8 @@ public class BookingsService {
public Integer addPayment(Integer id, Double payment) { public Integer addPayment(Integer id, Double payment) {
return bookingsGateway.addPayment(id, payment); return bookingsGateway.addPayment(id, payment);
} }
public List<Integer> getAllCampYears() {
return bookingsGateway.getAllCampYears();
}
} }

View File

@@ -8,7 +8,14 @@
<th:block layout:fragment="content"> <th:block layout:fragment="content">
<div class="mainpage"> <div class="mainpage">
<div class="card" sec:authorize="hasRole('business_booking')"> <div class="card" sec:authorize="hasRole('business_booking')">
<div class="card-header">Angemeldete Personen</div> <div class="card-header d-flex justify-content-between">
<div>Angemeldete Personen</div>
<div>im Jahr
<span class="ml-2" th:each="y : ${years}">
<a th:class="${year == y ? '' : 'tablelink'}" th:href="@{/business/bookings/{year}(year=${y})}" th:text="${y}"></a>
</span>
</div>
</div>
<div class="card-body"> <div class="card-body">
<table id="bookers" class="table table-striped"> <table id="bookers" class="table table-striped">
<thead> <thead>
@@ -25,12 +32,12 @@
<tbody> <tbody>
<th:block th:each="b : ${bookers}"> <th:block th:each="b : ${bookers}">
<tr> <tr>
<td class="middled"><a class="tablelink" th:href="@{/business/bookings/{id}(id=${b.pk})}" title="bearbeiten" th:text="${b.name}"></a></td> <td class="middled"><a class="tablelink" th:href="@{/business/bookings_by_id/{id}(id=${b.pk})}" title="bearbeiten" th:text="${b.name}"></a></td>
<td class="middled" th:text="${b.sex}"></td> <td class="middled" th:text="${b.sex}"></td>
<td class="middled" th:text="${b.camp}"></td> <td class="middled" th:text="${b.camp}"></td>
<td class="middled" th:text="${b.role}"></td> <td class="middled" th:text="${b.role}"></td>
<td class="middled"> <td class="middled">
<form action="#" th:action="@{'/business/bookings/listpayment/' + ${b.pk}}" th:object="${addBean}" method="post"> <form action="#" th:action="@{'/business/bookings/listpayment/{id}/{year}(id=${b.pk},year=${year})'}" th:object="${addBean}" method="post">
<input th:id="'searchfield' + ${b.pk}" type="hidden" th:name="search" /> <input th:id="'searchfield' + ${b.pk}" type="hidden" th:name="search" />
<div class="container"> <div class="container">
<div class="row"> <div class="row">

View File

@@ -70,7 +70,7 @@
<tbody> <tbody>
<th:block th:each="b : ${bookers}"> <th:block th:each="b : ${bookers}">
<tr> <tr>
<td><a class="tablelink" th:href="@{/business/bookings/{id}(id=${b.pk})}" title="bearbeiten" th:text="${b.name}"></a></td> <td><a class="tablelink" th:href="@{/business/bookings_by_id/{id}(id=${b.pk})}" title="bearbeiten" th:text="${b.name}"></a></td>
<td th:text="${b.sex}"></td> <td th:text="${b.sex}"></td>
<td th:text="${b.role}"></td> <td th:text="${b.role}"></td>
<td><span th:text="${#numbers.formatDecimal(b.paid, 1, 2) + ' €'}" th:if="${b.paid != null}"></span></td> <td><span th:text="${#numbers.formatDecimal(b.paid, 1, 2) + ' €'}" th:if="${b.paid != null}"></span></td>

View File

@@ -79,7 +79,7 @@
<a class="nav-link dropdown-toggle btn-icon-silent menufont" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">Abrechnung</a> <a class="nav-link dropdown-toggle btn-icon-silent menufont" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">Abrechnung</a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li><a th:href="@{/business}" class="dropdown-item menufont">Freizeitübersicht</a></li> <li><a th:href="@{/business}" class="dropdown-item menufont">Freizeitübersicht</a></li>
<li><a th:href="@{/business/bookings}" class="dropdown-item menufont" sec:authorize="hasRole('business_booking')">Buchungsübersicht</a></li> <li><a th:href="@{/business/bookings/{year}(year=${#dates.format(new java.util.Date(), 'yyyy')})}" class="dropdown-item menufont" sec:authorize="hasRole('business_booking')">Buchungsübersicht</a></li>
<li><a th:href="@{/business/outlay}" class="dropdown-item menufont" sec:authorize="hasRole('business_outlay')">Auslagen / Rechnungen</a> <li><a th:href="@{/business/outlay}" class="dropdown-item menufont" sec:authorize="hasRole('business_outlay')">Auslagen / Rechnungen</a>
<li><a th:href="@{/business/privileges}" class="dropdown-item menufont" sec:authorize="hasRole('admin')">Nutzerverwaltung</a></li> <li><a th:href="@{/business/privileges}" class="dropdown-item menufont" sec:authorize="hasRole('admin')">Nutzerverwaltung</a></li>
</ul> </ul>