download of outlays

This commit is contained in:
Jottyfan 2024-10-12 23:19:17 +02:00
parent 32ef8a67d9
commit ae9e2018a8
7 changed files with 133 additions and 11 deletions

View File

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

View File

@ -3,6 +3,10 @@ package de.jottyfan.camporganizer.module.business.outlay;
import java.security.Principal; import java.security.Principal;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.validation.BindingResult; import org.springframework.validation.BindingResult;
@ -15,6 +19,7 @@ import de.jottyfan.camporganizer.module.business.outlay.model.OutlayBean;
import de.jottyfan.camporganizer.module.camplist.CommonController; import de.jottyfan.camporganizer.module.camplist.CommonController;
import jakarta.annotation.security.RolesAllowed; import jakarta.annotation.security.RolesAllowed;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import jakarta.ws.rs.core.HttpHeaders;
/** /**
* *
@ -30,7 +35,8 @@ public class OutlayController extends CommonController {
@GetMapping("/business/outlay") @GetMapping("/business/outlay")
public String getOutlayDashboard(Model model, Principal principal) { public String getOutlayDashboard(Model model, Principal principal) {
model.addAttribute("list", service.getListOfUser(super.getCurrentUser(principal))); model.addAttribute("list", service.getListOf(super.getCurrentUser(principal), null));
model.addAttribute("camps", service.getAllCamps());
return "/business/outlay/list"; return "/business/outlay/list";
} }
@ -63,4 +69,19 @@ public class OutlayController extends CommonController {
service.deleteIfAllowedFor(super.getCurrentUser(principal), id); service.deleteIfAllowedFor(super.getCurrentUser(principal), id);
return "redirect:/business/outlay"; return "redirect:/business/outlay";
} }
@GetMapping("/business/outlay/summary/{campid}")
public String getSummaryOfCamp(@PathVariable("campid") Integer campId, Model model, Principal principal) {
model.addAttribute("campid", campId);
model.addAttribute("list", service.getListOf(null, campId));
return "/business/outlay/summary";
}
@GetMapping("/business/outlay/download/{campid}")
public ResponseEntity<Resource> generateCsv(@PathVariable("campid") Integer campId) {
String filename = String.format("rechnung_camp_%d.csv", campId);
InputStreamResource file = new InputStreamResource(service.getCsv(campId));
return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + filename)
.contentType(MediaType.parseMediaType("application/csv")).body(file);
}
} }

View File

@ -16,11 +16,12 @@ import org.jooq.DSLContext;
import org.jooq.DeleteConditionStep; import org.jooq.DeleteConditionStep;
import org.jooq.InsertValuesStep8; import org.jooq.InsertValuesStep8;
import org.jooq.Record10; import org.jooq.Record10;
import org.jooq.Record11;
import org.jooq.Record4; import org.jooq.Record4;
import org.jooq.Record7;
import org.jooq.SelectConditionStep; import org.jooq.SelectConditionStep;
import org.jooq.SelectSeekStep1; import org.jooq.SelectSeekStep1;
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;
@ -68,8 +69,8 @@ public class OutlayRepository {
return list; return list;
} }
public List<OutlayBean> getListOf(String username) { public List<OutlayBean> getListOf(String username, Integer campId) {
SelectConditionStep<Record7<Integer, String, String, LocalDateTime, String, BigDecimal, LocalDateTime>> sql = jooq SelectConditionStep<Record11<Integer, String, String, LocalDateTime, String, BigDecimal, LocalDateTime, String, String, String, String>> sql = jooq
// @formatter:off // @formatter:off
.select(T_SALES.PK, .select(T_SALES.PK,
T_SALES.TRADER, T_SALES.TRADER,
@ -77,26 +78,35 @@ public class OutlayRepository {
T_CAMP.ARRIVE, T_CAMP.ARRIVE,
T_LOCATION.NAME, T_LOCATION.NAME,
T_SALES.CASH, T_SALES.CASH,
T_SALES.BUYDATE) T_SALES.BUYDATE,
T_SALES.PROVIDER,
T_SALES.RECIPENUMBER,
T_SALES.INCREDIENTS,
T_SALES.RECIPENOTE)
.from(T_SALES) .from(T_SALES)
.leftJoin(T_CAMP).on(T_CAMP.PK.eq(T_SALES.FK_CAMP)) .leftJoin(T_CAMP).on(T_CAMP.PK.eq(T_SALES.FK_CAMP))
.leftJoin(T_LOCATION).on(T_LOCATION.PK.eq(T_CAMP.FK_LOCATION)) .leftJoin(T_LOCATION).on(T_LOCATION.PK.eq(T_CAMP.FK_LOCATION))
.where(T_SALES.PROVIDER.eq(username)); .where(username == null ? DSL.trueCondition() : T_SALES.PROVIDER.eq(username))
.and(campId == null ? DSL.trueCondition() : T_SALES.FK_CAMP.eq(campId));
// @formatter:on // @formatter:on
LOGGER.trace(sql); LOGGER.trace(sql);
List<OutlayBean> list = new ArrayList<>(); List<OutlayBean> list = new ArrayList<>();
Iterator<Record7<Integer, String, String, LocalDateTime, String, BigDecimal, LocalDateTime>> i = sql.fetch() Iterator<Record11<Integer, String, String, LocalDateTime, String, BigDecimal, LocalDateTime, String, String, String, String>> i = sql.fetch()
.iterator(); .iterator();
while (i.hasNext()) { while (i.hasNext()) {
Record7<Integer, String, String, LocalDateTime, String, BigDecimal, LocalDateTime> r = i.next(); Record11<Integer, String, String, LocalDateTime, String, BigDecimal, LocalDateTime, String, String, String, String> r = i.next();
String campname = String String campname = String
.format("%s %s %d", r.get(T_CAMP.NAME), r.get(T_LOCATION.NAME), r.get(T_CAMP.ARRIVE).getYear()).trim(); .format("%s %s %d", r.get(T_CAMP.NAME), r.get(T_LOCATION.NAME), r.get(T_CAMP.ARRIVE).getYear()).trim();
OutlayBean bean = new OutlayBean(); OutlayBean bean = new OutlayBean();
bean.setId(r.get(T_SALES.PK)); bean.setId(r.get(T_SALES.PK));
bean.setTrader(r.get(T_SALES.TRADER)); bean.setTrader(r.get(T_SALES.TRADER));
bean.setCampname(campname); bean.setCampname(campname);
bean.setProvider(r.get(T_SALES.PROVIDER));
bean.setCash(r.get(T_SALES.CASH)); bean.setCash(r.get(T_SALES.CASH));
bean.setBuydate(r.get(T_SALES.BUYDATE)); bean.setBuydate(r.get(T_SALES.BUYDATE));
bean.setRecipenumber(r.get(T_SALES.RECIPENUMBER));
bean.setIngredients(r.get(T_SALES.INCREDIENTS));
bean.setRecipenote(r.get(T_SALES.RECIPENOTE));
list.add(bean); list.add(bean);
} }
return list; return list;

View File

@ -1,5 +1,7 @@
package de.jottyfan.camporganizer.module.business.outlay; package de.jottyfan.camporganizer.module.business.outlay;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.List; import java.util.List;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -20,8 +22,8 @@ public class OutlayService {
@Autowired @Autowired
private OutlayRepository repository; private OutlayRepository repository;
public List<OutlayBean> getListOfUser(String username) { public List<OutlayBean> getListOf(String username, Integer campId) {
return repository.getListOf(username); return repository.getListOf(username, campId);
} }
public List<CampBean> getAllCamps() { public List<CampBean> getAllCamps() {
@ -43,4 +45,13 @@ public class OutlayService {
public void deleteIfAllowedFor(String username, Integer id) { public void deleteIfAllowedFor(String username, Integer id) {
repository.deleteBeanIfAllowedFor(username, id); repository.deleteBeanIfAllowedFor(username, id);
} }
public InputStream getCsv(Integer campId) {
List<OutlayBean> list = repository.getListOf(null, campId);
StringBuilder buf = new StringBuilder("Beleg-Nummer;Einkauf bei;Freizeit;bezahlt von;Betrag;Bestandteile;Rechnungsdatum\n");
for (OutlayBean bean : list) {
buf.append(bean.toCsvLine());
}
return new ByteArrayInputStream(buf.toString().getBytes());
}
} }

View File

@ -2,7 +2,9 @@ package de.jottyfan.camporganizer.module.business.outlay.model;
import java.io.Serializable; import java.io.Serializable;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.text.NumberFormat;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.Locale;
import org.springframework.format.annotation.DateTimeFormat; import org.springframework.format.annotation.DateTimeFormat;
@ -38,6 +40,26 @@ public class OutlayBean implements Serializable {
return this; return this;
} }
private static final String quoted(String s) {
return s == null ? "" : String.format("\"%s\"", s.replace("\"", "'"));
}
private static final String cashed(BigDecimal money) {
return money == null ? "" : String.format("%s €", NumberFormat.getNumberInstance(Locale.GERMAN).format(money));
}
public String toCsvLine() {
StringBuilder buf = new StringBuilder();
buf.append(recipenumber).append(";");
buf.append(quoted(trader)).append(";");
buf.append(quoted(campname)).append(";");
buf.append(quoted(provider)).append(";");
buf.append(cashed(cash)).append(";");
buf.append(quoted(ingredients)).append(";");
buf.append(buydate).append("\n");
return buf.toString();
}
/** /**
* @return the id * @return the id
*/ */

View File

@ -31,6 +31,11 @@
</tr> </tr>
</tfoot> </tfoot>
</table> </table>
<div class="row">
<div class="col" th:each="c : ${camps}">
<a th:href="@{/business/outlay/summary/{id}(id=${c.id})}" class="btn btn-outline-primary" th:text="${c.campname}"></a>
</div>
</div>
<script> <script>
$(document).ready(function() { $(document).ready(function() {
$("#table").DataTable({ $("#table").DataTable({

View File

@ -0,0 +1,53 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" layout:decorate="~{template}" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<title>Camp Organizer Business</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<th:block layout:fragment="content">
<div class="mainpage">
<div class="container" style="max-width: 100%" sec:authorize="hasRole('business_outlay')">
<table id="table" class="table table-striped">
<thead>
<tr>
<th>Kassenzettelnummer</th>
<th>Händler / Shop</th>
<th>Freizeit</th>
<th>Auslegender</th>
<th>Betrag</th>
<th>Tag / Uhrzeit</th>
<th>Kurzbeschreibung</th>
<th>Bemerkungen</th>
</tr>
</thead>
<tbody>
<tr th:each="o : ${list}">
<td><a th:href="@{/business/outlay/edit/{id}(id=${o.id})}" th:text="${o.recipenumber}"></a></td>
<td><a th:href="@{/business/outlay/edit/{id}(id=${o.id})}" th:text="${o.trader}"></a></td>
<td><a th:href="@{/business/outlay/edit/{id}(id=${o.id})}" th:text="${o.campname}"></a></td>
<td><a th:href="@{/business/outlay/edit/{id}(id=${o.id})}" th:text="${o.provider}"></a></td>
<td><a th:href="@{/business/outlay/edit/{id}(id=${o.id})}" th:text="${#numbers.formatDecimal(o.cash, 1, 2, 'COMMA')} + ' €'"></a></td>
<td><a th:href="@{/business/outlay/edit/{id}(id=${o.id})}" th:text="${#temporals.format(o.buydate, 'dd.MM.yyyy, HH:mm.ss')}"></a></td>
<td><a th:href="@{/business/outlay/edit/{id}(id=${o.id})}" th:text="${o.ingredients}"></a></td>
<td><a th:href="@{/business/outlay/edit/{id}(id=${o.id})}" th:text="${o.recipenote}"></a></td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="8"><a th:href="@{/business/outlay/download/{campid}(campid=${campid})}" class="btn btn-outline-primary form-control">herunterladen</a></td>
</tr>
</tfoot>
</table>
<script>
$(document).ready(function() {
$("#table").DataTable({
language : locale_de
});
});
</script>
</div>
</div>
</th:block>
</body>
</html>