Display financial bookings in the ui

This commit is contained in:
Lukas Fürderer 2020-06-05 07:49:11 +02:00
parent 9660e52f34
commit 0061d25082
Signed by: Lukas
GPG Key ID: B0AFA46F94103349
13 changed files with 390 additions and 146 deletions

View File

@ -10,6 +10,7 @@ public class CreateBookingAction {
public CreateBookingAction(BookingAccountEntry source, BookingAccountEntry destination, BookingReason reason, int amountCent) {
booking = new Booking();
booking.created = new java.sql.Timestamp(System.currentTimeMillis());
booking.reason = reason;
booking.amountCent = amountCent;

View File

@ -79,16 +79,6 @@ public class RequestController {
public String intern() {
return "intern/index";
}
@GetMapping("/intern/customers/")
public String internCustomers() {
return "intern/customers/index";
}
@GetMapping("/intern/customers/{id}")
public String internCustomersId() {
return "intern/customers/id";
}
@GetMapping("/intern/customerOrders/")
public String internCustomerOrder() {
@ -100,16 +90,6 @@ public class RequestController {
return "intern/customerOrders/id";
}
@GetMapping("/intern/suppliers/")
public String internSuppliers() {
return "intern/suppliers/index";
}
@GetMapping("/intern/suppliers/{id}")
public String internSuppliersId() {
return "intern/suppliers/id";
}
@GetMapping("/intern/supplierOrders/")
public String internSupplierOrders() {
return "intern/supplierOrders/index";
@ -129,26 +109,6 @@ public class RequestController {
*/
@GetMapping("/intern/accounting/")
public String accounting() {
return "intern/accounting/index";
}
@GetMapping("/intern/accounting/vat")
public String accountingVat() {
return "intern/accounting/vat";
}
@GetMapping("/intern/accounting/main")
public String accountingIntern() {
return "intern/accounting/main";
}
@GetMapping("/intern/accounting/addManual")
public String accountingAddManual() {
return "intern/accounting/addManual";
}
@GetMapping("/intern/warehouse/")
public String accountingWarehouse() {
return "intern/warehouse/index";

View File

@ -1,8 +0,0 @@
package org.hso.ecommerce.controller;
import org.springframework.stereotype.Controller;
@Controller
//@RequestMapping("...")
public class BookingController {
}

View File

@ -0,0 +1,229 @@
package org.hso.ecommerce.controller.intern;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.hso.ecommerce.entities.booking.Booking;
import org.hso.ecommerce.entities.booking.BookingAccountEntry;
import org.hso.ecommerce.entities.booking.BookingReason;
import org.hso.ecommerce.repos.booking.BookingRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class AccountingController {
@Autowired
private BookingRepository bookingRepository = null;
/**
* A description used to render the html template for the overview of all bookings
*/
static class TemplateBooking {
public String datetime;
public String amount;
public String source;
public String sourceAddr;
public String sourceBalance;
public String destination;
public String destinationAddr;
public String destinationBalance;
public String reason;
public String reference;
public String referenceAddr;
}
/**
* A description used to render the html template for bookings on a specific account
*/
static class ShortTemplateBooking {
public String datetime;
public String amount;
public String source;
public String sourceAddr;
public String balance;
public String reason;
public String reference;
public String referenceAddr;
}
public static class ShortTemplateBookingResult {
public final String balance;
public final List<ShortTemplateBooking> bookings;
public ShortTemplateBookingResult(String balance, List<ShortTemplateBooking> bookings) {
this.balance = balance;
this.bookings = bookings;
}
}
/**
* Used to check which side of the booking (source or destination) is the account we are currently looking at.
*/
public interface AccountFilter {
boolean matches(BookingAccountEntry account);
}
/**
* Renders template information for the bookings of one specific account.
*
* @param bookings The (already fetched) booking entities to display
* @param currentAccount A filter matching the account that is to be displayed
* @return A collection result containing the final balance and all booking entries.
*/
public ShortTemplateBookingResult buildShortTemplate(List<Booking> bookings, AccountFilter currentAccount) {
List<ShortTemplateBooking> templateBookings = new ArrayList<>();
for (Booking booking : bookings) {
ShortTemplateBooking tb = new ShortTemplateBooking();
tb.datetime = new SimpleDateFormat("dd.MM.yyyy HH:mm").format(booking.created);
tb.reason = booking.reason.comment;
tb.reference = describeReference(booking.reason);
tb.referenceAddr = linkToReference(booking.reason);
if (booking.destination != null && currentAccount.matches(booking.destination)) {
// Money was transferred onto the account we are looking at.
tb.amount = fmtEuro(booking.amountCent);
if (booking.source != null) {
tb.source = describeAccount(booking.source);
tb.sourceAddr = linkAddrOfAccount(booking.source);
} else {
tb.source = "-";
tb.sourceAddr = null;
}
tb.balance = fmtEuro(booking.destination.newSumCent);
} else if (booking.source != null && currentAccount.matches(booking.source)) {
// Money was transferred away from the account we are looking at.
tb.amount = fmtEuro(-booking.amountCent); // Negative because of the transfer away
if (booking.destination != null) {
tb.source = describeAccount(booking.destination); // 'source' means 'the other account' in this case
tb.sourceAddr = linkAddrOfAccount(booking.destination);
} else {
tb.source = "-";
tb.sourceAddr = null;
}
tb.balance = fmtEuro(booking.source.newSumCent);
} else {
throw new RuntimeException(
"This booking should not appear in this list, because neither source nor destination is the account we are looking at.");
}
templateBookings.add(tb);
}
String balance = templateBookings.isEmpty() ? fmtEuro(0) : templateBookings.get(0).balance;
return new ShortTemplateBookingResult(balance, templateBookings);
}
private String fmtEuro(long amountCent) {
return String.format("%.2f EUR", amountCent / 100.0);
}
private String describeAccount(BookingAccountEntry account) {
if (account.isMainAccount) {
return "Hauptkonto";
} else if (account.isVATAccount) {
return "Mehrwertsteuer";
} else if (account.supplierAccount != null) {
return "Lieferant " + account.supplierAccount.id;
} else if (account.userAccount != null) {
return "Kunde " + account.userAccount.id;
} else {
return "- unbekannt -";
}
}
private String linkAddrOfAccount(BookingAccountEntry account) {
if (account.isMainAccount) {
return "/intern/accounting/main";
} else if (account.isVATAccount) {
return "/intern/accounting/vat";
} else if (account.supplierAccount != null) {
return "/intern/suppliers/" + account.supplierAccount.id;
} else if (account.userAccount != null) {
return "/intern/customers/" + account.userAccount.id;
} else {
return null;
}
}
private String describeReference(BookingReason reason) {
if (reason.customerOrder != null) {
return reason.customerOrder.id + "";
} else if (reason.customerPayment != null) {
return "Bezahlung mit Kreditkarte " + reason.customerPayment.payment.creditCardNumber;
} else if (reason.supplierOrder != null) {
return "Lieferanten-Bestellung " + reason.supplierOrder.id;
} else {
return "-";
}
}
private String linkToReference(BookingReason reason) {
if (reason.customerOrder != null) {
return "/intern/customerOrders/" + reason.customerOrder.id;
} else {
return null;
}
}
@GetMapping("/intern/accounting/")
public String accounting(HttpServletRequest request) {
List<Booking> bookings = bookingRepository.allBookingsReverseChronologically();
List<TemplateBooking> templateBookings = new ArrayList<>();
for (Booking booking : bookings) {
TemplateBooking tb = new TemplateBooking();
tb.datetime = new SimpleDateFormat("dd.MM.yyyy HH:mm").format(booking.created);
tb.amount = fmtEuro(booking.amountCent);
if (booking.source != null) {
tb.source = describeAccount(booking.source);
tb.sourceAddr = linkAddrOfAccount(booking.source);
tb.sourceBalance = fmtEuro(booking.source.newSumCent);
} else {
tb.source = "-";
tb.sourceAddr = null;
tb.sourceBalance = "-";
}
if (booking.destination != null) {
tb.destination = describeAccount(booking.destination);
tb.destinationAddr = linkAddrOfAccount(booking.destination);
tb.destinationBalance = fmtEuro(booking.destination.newSumCent);
} else {
tb.destination = "-";
tb.destinationAddr = null;
tb.destinationBalance = "-";
}
tb.reason = booking.reason.comment;
tb.reference = describeReference(booking.reason);
tb.referenceAddr = linkToReference(booking.reason);
templateBookings.add(tb);
}
request.setAttribute("bookings", templateBookings);
return "intern/accounting/index";
}
@GetMapping("/intern/accounting/vat")
public String accountingVat(HttpServletRequest request) {
List<Booking> bookings = bookingRepository.vatBookingsReverseChronologically();
ShortTemplateBookingResult result = buildShortTemplate(bookings, account -> account.isVATAccount);
request.setAttribute("balance", result.balance);
request.setAttribute("bookings", result.bookings);
return "intern/accounting/vat";
}
@GetMapping("/intern/accounting/main")
public String accountingIntern(HttpServletRequest request) {
List<Booking> bookings = bookingRepository.mainBookingsReverseChronologically();
ShortTemplateBookingResult result = buildShortTemplate(bookings, account -> account.isMainAccount);
request.setAttribute("balance", result.balance);
request.setAttribute("bookings", result.bookings);
return "intern/accounting/main";
}
@GetMapping("/intern/accounting/addManual")
public String accountingAddManual() {
return "intern/accounting/addManual";
}
}

View File

@ -1,8 +1,46 @@
package org.hso.ecommerce.controller.intern.customers;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.hso.ecommerce.controller.intern.AccountingController;
import org.hso.ecommerce.controller.intern.AccountingController.ShortTemplateBookingResult;
import org.hso.ecommerce.entities.booking.Booking;
import org.hso.ecommerce.repos.booking.BookingRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
//@RequestMapping("...")
@RequestMapping("/intern/customers")
public class CustomersIndexController {
@Autowired
private BookingRepository bookingRepository = null;
@Autowired
private AccountingController accountingController = null;
@GetMapping("/")
public String internCustomers() {
return "intern/customers/index";
}
@GetMapping("/{customerId}")
public String internCustomersId(HttpServletRequest request, @PathVariable(required = true) long customerId) {
// Table of bookings
List<Booking> bookings = bookingRepository.customerBookingsReverseChronologically(customerId);
ShortTemplateBookingResult result = accountingController.buildShortTemplate(
bookings,
account -> account.userAccount != null && account.userAccount.id == customerId);
request.setAttribute("balance", result.balance);
request.setAttribute("bookings", result.bookings);
return "intern/customers/id";
}
}

View File

@ -1,8 +1,45 @@
package org.hso.ecommerce.controller.intern.suppliers;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.hso.ecommerce.controller.intern.AccountingController;
import org.hso.ecommerce.controller.intern.AccountingController.ShortTemplateBookingResult;
import org.hso.ecommerce.entities.booking.Booking;
import org.hso.ecommerce.repos.booking.BookingRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
//@RequestMapping("...")
@RequestMapping("/intern/suppliers")
public class SupplierIndexController {
@Autowired
private BookingRepository bookingRepository = null;
@Autowired
private AccountingController accountingController = null;
@GetMapping("/")
public String internSuppliers() {
return "intern/suppliers/index";
}
@GetMapping("/{supplierId}")
public String internSuppliersId(HttpServletRequest request, @PathVariable(required = true) long supplierId) {
// Table of bookings
List<Booking> bookings = bookingRepository.supplierBookingsReverseChronologically(supplierId);
ShortTemplateBookingResult result = accountingController.buildShortTemplate(bookings,
account -> account.supplierAccount != null && account.supplierAccount.id == supplierId);
request.setAttribute("balance", result.balance);
request.setAttribute("bookings", result.bookings);
return "intern/suppliers/id";
}
}

View File

@ -1,6 +1,7 @@
package org.hso.ecommerce.entities.booking;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
@Entity
@Table(name = "bookings")
@ -14,6 +15,9 @@ public class Booking {
// always >= 0
public int amountCent;
@NotNull
public java.sql.Timestamp created;
@ManyToOne(optional = true, cascade = CascadeType.ALL)
public BookingAccountEntry source;

View File

@ -1,12 +1,30 @@
package org.hso.ecommerce.repos.booking;
import java.util.List;
import org.hso.ecommerce.entities.booking.Booking;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
@Repository
public interface BookingRepository extends JpaRepository<Booking, Long> {
@Query("SELECT b FROM Booking b ORDER BY b.id DESC")
List<Booking> allBookingsReverseChronologically();
@Query("SELECT b FROM Booking b WHERE b.source.isMainAccount = 1 OR b.destination.isMainAccount = 1 ORDER BY b.id DESC")
List<Booking> mainBookingsReverseChronologically();
@Query("SELECT b FROM Booking b WHERE b.source.isVATAccount = 1 OR b.destination.isVATAccount = 1 ORDER BY b.id DESC")
List<Booking> vatBookingsReverseChronologically();
@Query("SELECT b FROM Booking b WHERE b.source.userAccount.id = :customerId OR b.destination.userAccount.id = :customerId ORDER BY b.id DESC")
List<Booking> customerBookingsReverseChronologically(long customerId);
@Query("SELECT b FROM Booking b WHERE b.source.supplierAccount.id = :supplierId OR b.destination.supplierAccount.id = :supplierId ORDER BY b.id DESC")
List<Booking> supplierBookingsReverseChronologically(long supplierId);
}

View File

@ -49,48 +49,21 @@
<th>Referenz</th>
</tr>
<tr>
<td>10.09.2019 14:10</td>
<td>119,00&nbsp;EUR</td>
<td> -</td>
<td> -</td>
<td><a th:href="@{/intern/customers/5000}">Kunde 5080</a></td>
<td>0&nbsp;EUR</td>
<td>Kunden-Bezahlung</td>
<td>Bezahlung mit Kreditkarte XXXXX480</td>
</tr>
<tr th:each="booking: ${bookings}">
<td th:text="${booking.datetime}" />
<td th:text="${booking.amount}" />
<tr>
<td>10.09.2019 13:45</td>
<td>19,00&nbsp;EUR</td>
<td><a th:href="@{/intern/accounting/main}">Hauptkonto</a></td>
<td>331,00&nbsp;EUR</td>
<td><a th:href="@{/intern/accounting/vat}">Mehrwertsteuer</a></td>
<td>1510,95&nbsp;EUR</td>
<td>Kunden-Bestellung</td>
<td><a th:href="@{/intern/customerOrders/450}">2504</a></td>
</tr>
<td th:if="${booking.sourceAddr}"><a th:href="@{${booking.sourceAddr}}" th:text="${booking.source}" /></td>
<td th:unless="${booking.sourceAddr}" th:text="${booking.source}" />
<td th:text="${booking.sourceBalance}" />
<tr>
<td>10.09.2019 13:45</td>
<td>100,00&nbsp;EUR</td>
<td><a th:href="@{/intern/customers/5000}">Kunde 5080</a></td>
<td>-100,00&nbsp;EUR</td>
<td><a th:href="@{/intern/accounting/main}">Hauptkonto</a></td>
<td>350,00&nbsp;EUR</td>
<td>Kunden-Bestellung</td>
<td><a th:href="@{/intern/customerOrders/450}">2504</a></td>
</tr>
<td th:if="${booking.destinationAddr}"><a th:href="@{${booking.destinationAddr}}" th:text="${booking.destination}" /></td>
<td th:unless="${booking.destinationAddr}" th:text="${booking.destination}" />
<td th:text="${booking.destinationBalance}" />
<tr>
<td>19.08.2019 12:31</td>
<td>250,00&nbsp;EUR</td>
<td> -</td>
<td> -</td>
<td><a th:href="@{/intern/accounting/main}">Hauptkonto</a></td>
<td>250,00&nbsp;EUR</td>
<td>Startkapital</td>
<td> -</td>
<td th:text="${booking.reason}" />
<td th:if="${booking.referenceAddr}"><a th:href="@{${booking.referenceAddr}}" th:text="${booking.reference}" /></td>
<td th:unless="${booking.referenceAddr}" th:text="${booking.reference}" />
</tr>
</table>

View File

@ -25,7 +25,7 @@
<nav th:replace="fragments/intern :: sidebar"></nav>
<div class="content-width">
<h3> Kontostand </h3>
<h2> 331,00&nbsp;EUR </h2>
<h2 th:text="${balance}" />
<p>
<table id="main-table">
<tr>
@ -42,33 +42,18 @@
<th>Grund</th>
<th>Referenz</th>
</tr>
<tr>
<td>10.09.2019 13:45</td>
<td>-19,00&nbsp;EUR</td>
<tr th:each="booking: ${bookings}">
<td th:text="${booking.datetime}" />
<td th:text="${booking.amount}" />
<td><a th:href="@{/intern/accounting/vat}">Mehrwertsteuer</a></td>
<td>331,00&nbsp;EUR</td>
<td th:if="${booking.sourceAddr}"><a th:href="@{${booking.sourceAddr}}" th:text="${booking.source}" /></td>
<td th:unless="${booking.sourceAddr}" th:text="${booking.source}" />
<td>Kunden-Bestellung</td>
<td><a th:href="@{/intern/customerOrders/450}">2504</a></td>
</tr>
<td th:text="${booking.balance}" />
<tr>
<td>10.09.2019 13:45</td>
<td>100,00&nbsp;EUR</td>
<td><a th:href="@{/intern/customers/5000}">Kunde 5080</a></td>
<td>350,00&nbsp;EUR</td>
<td>Kunden-Bestellung</td>
<td><a th:href="@{/intern/customerOrders/450}">2504</a></td>
</tr>
<tr>
<td>19.08.2019 12:31</td>
<td>250,00&nbsp;EUR</td>
<td> -</td>
<td>250,00&nbsp;EUR</td>
<td>Startkapital</td>
<td> -</td>
<td th:text="${booking.reason}" />
<td th:if="${booking.referenceAddr}"><a th:href="@{${booking.referenceAddr}}" th:text="${booking.reference}" /></td>
<td th:unless="${booking.referenceAddr}" th:text="${booking.reference}" />
</tr>
</table>

View File

@ -26,7 +26,7 @@
<nav th:replace="fragments/intern :: sidebar"></nav>
<div class="content-width">
<h3> Kontostand </h3>
<h2> 1510.95&nbsp;EUR </h2>
<h2 th:text="${balance}" />
<p>
<table id="main-table">
<tr>
@ -43,13 +43,18 @@
<th>Grund</th>
<th>Referenz</th>
</tr>
<tr>
<td>10.09.2019 13:45</td>
<td>19,00&nbsp;EUR</td>
<td><a th:href="@{/intern/accounting/main}">Hauptkonto</a></td>
<td>1510,95&nbsp;EUR</td>
<td>Kunden-Bestellung</td>
<td><a th:href="@{/intern/customerOrders/450}">2504</a></td>
<tr th:each="booking: ${bookings}">
<td th:text="${booking.datetime}" />
<td th:text="${booking.amount}" />
<td th:if="${booking.sourceAddr}"><a th:href="@{${booking.sourceAddr}}" th:text="${booking.source}" /></td>
<td th:unless="${booking.sourceAddr}" th:text="${booking.source}" />
<td th:text="${booking.balance}" />
<td th:text="${booking.reason}" />
<td th:if="${booking.referenceAddr}"><a th:href="@{${booking.referenceAddr}}" th:text="${booking.reference}" /></td>
<td th:unless="${booking.referenceAddr}" th:text="${booking.reference}" />
</tr>
</table>

View File

@ -146,7 +146,7 @@
<h2>Buchungen</h2>
<div>
<h4> Kontostand </h4>
<h3> 0,00&nbsp;EUR </h3>
<h3 th:text="${balance}" />
</div>
<p>
<table id="main-table">
@ -164,21 +164,18 @@
<th>Grund</th>
<th>Referenz</th>
</tr>
<tr>
<td>10.09.2019 14:10</td>
<td>119,00&nbsp;EUR</td>
<td> -</td>
<td>0&nbsp;EUR</td>
<td>Kunden-Bezahlung</td>
<td>Bezahlung mit Kreditkarte XXXXXXXX480</td>
</tr>
<tr>
<td>10.09.2019 13:45</td>
<td>-100,00&nbsp;EUR</td>
<td><a th:href="@{/intern/accounting/main}">Hauptkonto</a></td>
<td>-100,00&nbsp;EUR</td>
<td>Kunden-Bestellung</td>
<td><a th:href="@{/intern/customerOrders/450}">2504</a></td>
<tr th:each="booking: ${bookings}">
<td th:text="${booking.datetime}" />
<td th:text="${booking.amount}" />
<td th:if="${booking.sourceAddr}"><a th:href="@{${booking.sourceAddr}}" th:text="${booking.source}" /></td>
<td th:unless="${booking.sourceAddr}" th:text="${booking.source}" />
<td th:text="${booking.balance}" />
<td th:text="${booking.reason}" />
<td th:if="${booking.referenceAddr}"><a th:href="@{${booking.referenceAddr}}" th:text="${booking.reference}" /></td>
<td th:unless="${booking.referenceAddr}" th:text="${booking.reference}" />
</tr>
</table>
</p>

View File

@ -56,7 +56,7 @@
<h2>Buchungen</h2>
<div>
<h4> Kontostand </h4>
<h3> -100,00&nbsp;EUR </h3>
<h3 th:text="${balance}" />
</div>
<p>
<table id="main-table">
@ -68,13 +68,18 @@
<th>Grund</th>
<th>Referenz</th>
</tr>
<tr>
<td>10.09.2019 13:45</td>
<td>100,00&nbsp;EUR</td>
<td><a th:href="@{/intern/accounting/main}">Hauptkonto</a></td>
<td>-100,00&nbsp;EUR</td>
<td>Lieferanten-Bestellung</td>
<td><a th:href="@{/intern/supplierOrders/#q=4520}">2504</a></td>
<tr th:each="booking: ${bookings}">
<td th:text="${booking.datetime}" />
<td th:text="${booking.amount}" />
<td th:if="${booking.sourceAddr}"><a th:href="@{${booking.sourceAddr}}" th:text="${booking.source}" /></td>
<td th:unless="${booking.sourceAddr}" th:text="${booking.source}" />
<td th:text="${booking.balance}" />
<td th:text="${booking.reason}" />
<td th:if="${booking.referenceAddr}"><a th:href="@{${booking.referenceAddr}}" th:text="${booking.reference}" /></td>
<td th:unless="${booking.referenceAddr}" th:text="${booking.reference}" />
</tr>
</table>
</p>