From 0061d250826a98654c3d501eab29581e1a357de0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20F=C3=BCrderer?= Date: Fri, 5 Jun 2020 07:49:11 +0200 Subject: [PATCH 1/5] Display financial bookings in the ui --- .../action/booking/CreateBookingAction.java | 1 + .../hso/ecommerce/app/RequestController.java | 40 --- .../controller/BookingController.java | 8 - .../intern/AccountingController.java | 229 ++++++++++++++++++ .../customers/CustomersIndexController.java | 40 ++- .../suppliers/SupplierIndexController.java | 39 ++- .../ecommerce/entities/booking/Booking.java | 4 + .../repos/booking/BookingRepository.java | 18 ++ .../templates/intern/accounting/index.html | 51 +--- .../templates/intern/accounting/main.html | 35 +-- .../templates/intern/accounting/vat.html | 21 +- .../templates/intern/customers/id.html | 29 +-- .../templates/intern/suppliers/id.html | 21 +- 13 files changed, 390 insertions(+), 146 deletions(-) delete mode 100644 prototype/src/main/java/org/hso/ecommerce/controller/BookingController.java create mode 100644 prototype/src/main/java/org/hso/ecommerce/controller/intern/AccountingController.java diff --git a/prototype/src/main/java/org/hso/ecommerce/action/booking/CreateBookingAction.java b/prototype/src/main/java/org/hso/ecommerce/action/booking/CreateBookingAction.java index f01e6a3..edde2cb 100644 --- a/prototype/src/main/java/org/hso/ecommerce/action/booking/CreateBookingAction.java +++ b/prototype/src/main/java/org/hso/ecommerce/action/booking/CreateBookingAction.java @@ -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; diff --git a/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java b/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java index bb0b5d6..6fc32f3 100644 --- a/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java +++ b/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java @@ -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"; diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/BookingController.java b/prototype/src/main/java/org/hso/ecommerce/controller/BookingController.java deleted file mode 100644 index d35093e..0000000 --- a/prototype/src/main/java/org/hso/ecommerce/controller/BookingController.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.hso.ecommerce.controller; - -import org.springframework.stereotype.Controller; - -@Controller -//@RequestMapping("...") -public class BookingController { -} diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/AccountingController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/AccountingController.java new file mode 100644 index 0000000..b425452 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/AccountingController.java @@ -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 bookings; + + public ShortTemplateBookingResult(String balance, List 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 bookings, AccountFilter currentAccount) { + List 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 bookings = bookingRepository.allBookingsReverseChronologically(); + List 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 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 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"; + } +} diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/customers/CustomersIndexController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/customers/CustomersIndexController.java index a15b927..a27d407 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/intern/customers/CustomersIndexController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/customers/CustomersIndexController.java @@ -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 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"; + } + } diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierIndexController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierIndexController.java index d6cefc0..20c29f3 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierIndexController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierIndexController.java @@ -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 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"; + } + } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/booking/Booking.java b/prototype/src/main/java/org/hso/ecommerce/entities/booking/Booking.java index e61ea07..64ef1cd 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/booking/Booking.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/booking/Booking.java @@ -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; diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/booking/BookingRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/booking/BookingRepository.java index 454b3f3..cbec49b 100644 --- a/prototype/src/main/java/org/hso/ecommerce/repos/booking/BookingRepository.java +++ b/prototype/src/main/java/org/hso/ecommerce/repos/booking/BookingRepository.java @@ -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 { + @Query("SELECT b FROM Booking b ORDER BY b.id DESC") + List allBookingsReverseChronologically(); + + @Query("SELECT b FROM Booking b WHERE b.source.isMainAccount = 1 OR b.destination.isMainAccount = 1 ORDER BY b.id DESC") + List mainBookingsReverseChronologically(); + + @Query("SELECT b FROM Booking b WHERE b.source.isVATAccount = 1 OR b.destination.isVATAccount = 1 ORDER BY b.id DESC") + List 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 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 supplierBookingsReverseChronologically(long supplierId); + } diff --git a/prototype/src/main/resources/templates/intern/accounting/index.html b/prototype/src/main/resources/templates/intern/accounting/index.html index 206e218..7d832fa 100644 --- a/prototype/src/main/resources/templates/intern/accounting/index.html +++ b/prototype/src/main/resources/templates/intern/accounting/index.html @@ -49,48 +49,21 @@ Referenz - - 10.09.2019 14:10 - 119,00 EUR - - - - - Kunde 5080 - 0 EUR - Kunden-Bezahlung - Bezahlung mit Kreditkarte XXXXX480 - + + + - - 10.09.2019 13:45 - 19,00 EUR - Hauptkonto - 331,00 EUR - Mehrwertsteuer - 1510,95 EUR - Kunden-Bestellung - 2504 - + + + - - 10.09.2019 13:45 - 100,00 EUR - Kunde 5080 - -100,00 EUR - Hauptkonto - 350,00 EUR - Kunden-Bestellung - 2504 - + + + - - 19.08.2019 12:31 - 250,00 EUR - - - - - Hauptkonto - 250,00 EUR - Startkapital - - + + + diff --git a/prototype/src/main/resources/templates/intern/accounting/main.html b/prototype/src/main/resources/templates/intern/accounting/main.html index 7532df5..ed7345c 100644 --- a/prototype/src/main/resources/templates/intern/accounting/main.html +++ b/prototype/src/main/resources/templates/intern/accounting/main.html @@ -25,7 +25,7 @@

Kontostand

-

331,00 EUR

+

@@ -42,33 +42,18 @@ - - - + + - + + - - + - - - - - - - - - - - - - - - + +
Grund Referenz
10.09.2019 13:45-19,00 EUR
+ - Mehrwertsteuer331,00 EUR - Kunden-Bestellung2504
-
10.09.2019 13:45100,00 EURKunde 5080350,00 EURKunden-Bestellung2504
19.08.2019 12:31250,00 EUR -250,00 EURStartkapital - +
diff --git a/prototype/src/main/resources/templates/intern/accounting/vat.html b/prototype/src/main/resources/templates/intern/accounting/vat.html index 1f43097..806141c 100644 --- a/prototype/src/main/resources/templates/intern/accounting/vat.html +++ b/prototype/src/main/resources/templates/intern/accounting/vat.html @@ -26,7 +26,7 @@

Kontostand

-

1510.95 EUR

+

@@ -43,13 +43,18 @@ - - - - - - - + + + +
Grund Referenz
10.09.2019 13:4519,00 EURHauptkonto1510,95 EURKunden-Bestellung2504
+ + + + + + + +
diff --git a/prototype/src/main/resources/templates/intern/customers/id.html b/prototype/src/main/resources/templates/intern/customers/id.html index 33ba95b..4b41c22 100644 --- a/prototype/src/main/resources/templates/intern/customers/id.html +++ b/prototype/src/main/resources/templates/intern/customers/id.html @@ -146,7 +146,7 @@

Buchungen

Kontostand

-

0,00 EUR

+

@@ -164,21 +164,18 @@ - - - - - - - - - - - - - - - + + + +
Grund Referenz
10.09.2019 14:10119,00 EUR -0 EURKunden-BezahlungBezahlung mit Kreditkarte XXXXXXXX480
10.09.2019 13:45-100,00 EURHauptkonto-100,00 EURKunden-Bestellung2504
+ + + + + + + +

diff --git a/prototype/src/main/resources/templates/intern/suppliers/id.html b/prototype/src/main/resources/templates/intern/suppliers/id.html index f4f6788..2558857 100644 --- a/prototype/src/main/resources/templates/intern/suppliers/id.html +++ b/prototype/src/main/resources/templates/intern/suppliers/id.html @@ -56,7 +56,7 @@

Buchungen

Kontostand

-

-100,00 EUR

+

@@ -68,13 +68,18 @@ - - - - - - - + + + +
Grund Referenz
10.09.2019 13:45100,00 EURHauptkonto-100,00 EURLieferanten-Bestellung2504
+ + + + + + + +

-- 2.47.1 From e53c1e087268a5cf871211df7dc6806aba360d7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20F=C3=BCrderer?= Date: Fri, 5 Jun 2020 18:45:25 +0200 Subject: [PATCH 2/5] Allow the user to create manual bookings --- .../AccountingController.java | 7 +- .../ManualAccountingController.java | 259 ++++++++++++++++++ .../customers/CustomersIndexController.java | 4 +- .../suppliers/SupplierIndexController.java | 4 +- .../intern/accounting/addManual.html | 42 +-- 5 files changed, 285 insertions(+), 31 deletions(-) rename prototype/src/main/java/org/hso/ecommerce/controller/intern/{ => accounting}/AccountingController.java (98%) create mode 100644 prototype/src/main/java/org/hso/ecommerce/controller/intern/accounting/ManualAccountingController.java diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/AccountingController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/accounting/AccountingController.java similarity index 98% rename from prototype/src/main/java/org/hso/ecommerce/controller/intern/AccountingController.java rename to prototype/src/main/java/org/hso/ecommerce/controller/intern/accounting/AccountingController.java index b425452..367995b 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/intern/AccountingController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/accounting/AccountingController.java @@ -1,4 +1,4 @@ -package org.hso.ecommerce.controller.intern; +package org.hso.ecommerce.controller.intern.accounting; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -221,9 +221,4 @@ public class AccountingController { request.setAttribute("bookings", result.bookings); return "intern/accounting/main"; } - - @GetMapping("/intern/accounting/addManual") - public String accountingAddManual() { - return "intern/accounting/addManual"; - } } diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/accounting/ManualAccountingController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/accounting/ManualAccountingController.java new file mode 100644 index 0000000..5376e25 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/accounting/ManualAccountingController.java @@ -0,0 +1,259 @@ +package org.hso.ecommerce.controller.intern.accounting; + +import org.hso.ecommerce.action.booking.CreateBookingAction; +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.BookingAccountEntryRepository; +import org.hso.ecommerce.repos.booking.BookingRepository; +import org.hso.ecommerce.repos.supplier.SupplierRepository; +import org.hso.ecommerce.repos.user.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; + +@Controller +public class ManualAccountingController { + + @Autowired + final BookingRepository bookingRepository = null; + + @Autowired + final BookingAccountEntryRepository bookingAccountEntryRepository = null; + + @Autowired + final UserRepository userRepository = null; + + @Autowired + final SupplierRepository supplierRepository = null; + + /** + * Collection for the form values used to create a manual booking + */ + public static class ManualAccounting { + String amount; + String source; + String sourceCustomer; + String sourceSupplier; + String destination; + String destinationCustomer; + String destinationSupplier; + String reason; + String reasonText; + + /** + * Default constructor with default values for the form + */ + public ManualAccounting() { + amount = "0.00"; + reason = "Manual"; + } + + public String getAmount() { + return amount; + } + + public void setAmount(String amount) { + this.amount = amount; + } + + public String getSource() { + return source; + } + + public void setSource(String source) { + this.source = source; + } + + public String getSourceCustomer() { + return sourceCustomer; + } + + public void setSourceCustomer(String sourceCustomer) { + this.sourceCustomer = sourceCustomer; + } + + public String getSourceSupplier() { + return sourceSupplier; + } + + public void setSourceSupplier(String sourceSupplier) { + this.sourceSupplier = sourceSupplier; + } + + public String getDestination() { + return destination; + } + + public void setDestination(String destination) { + this.destination = destination; + } + + public String getDestinationCustomer() { + return destinationCustomer; + } + + public void setDestinationCustomer(String destinationCustomer) { + this.destinationCustomer = destinationCustomer; + } + + public String getDestinationSupplier() { + return destinationSupplier; + } + + public void setDestinationSupplier(String destinationSupplier) { + this.destinationSupplier = destinationSupplier; + } + + public String getReason() { + return reason; + } + + public void setReason(String reason) { + this.reason = reason; + } + + public String getReasonText() { + return reasonText; + } + + public void setReasonText(String reasonText) { + this.reasonText = reasonText; + } + } + + /** + * An exception to represent errors that can be shown to the user + */ + public static class InvalidFormDataException extends Exception { + public InvalidFormDataException(String msg) { + super(msg); + } + + private static final long serialVersionUID = 1L; + } + + private boolean sameAccountBothSides(ManualAccounting formData) { + // No need to check for NumberFormatException because the numbers were already parsed before. + if (!formData.getSource().equals(formData.getDestination())) { + return false; + } else if (formData.getSource().equals("Cust") + && Long.parseLong(formData.getSourceCustomer()) != Long.parseLong(formData.getDestinationCustomer())) { + return false; + } else if (formData.getSource().equals("Sup") + && Long.parseLong(formData.getSourceSupplier()) != Long.parseLong(formData.getDestinationSupplier())) { + return false; + } else { + return true; + } + } + + public Booking createBooking(ManualAccounting formData) throws InvalidFormDataException { + if (formData.getSource() == null) { + throw new InvalidFormDataException("Bitte wählen Sie ein Quellkonto aus."); + } else if (formData.getDestination() == null) { + throw new InvalidFormDataException("Bitte wählen Sie ein Zielkonto aus."); + } + BookingAccountEntry source = getAccountFromFormData(formData.getSource(), formData.getSourceCustomer(), + formData.getSourceSupplier()); + BookingAccountEntry destination = getAccountFromFormData(formData.getDestination(), + formData.getDestinationCustomer(), formData.getDestinationSupplier()); + + if (sameAccountBothSides(formData)) { + throw new InvalidFormDataException("Quell- und Zielkonto dürfen nicht das selbe sein."); + } + + double doubleAmount; + try { + doubleAmount = Double.parseDouble(formData.amount); + } catch (NumberFormatException e) { + throw new InvalidFormDataException("Der angegebene Betrag ist ungültig."); + } + int amountCent = (int) Math.round(doubleAmount * 100); + + BookingReason reason = new BookingReason(); + if (formData.getReason().equals("Start")) { + reason.isStartBooking = true; + } else if (formData.getReason().equals("Manual")) { + reason.isManuel = true; + } else { + throw new RuntimeException("Invalid form value for the booking reason: " + formData.getReason()); + } + reason.comment = formData.reasonText; + + CreateBookingAction action = new CreateBookingAction(source, destination, reason, amountCent); + return action.finish(); + } + + /** + * Retrieve the corresponding account based on user-input + * + * @param account The chosen value on the radio buttons + * @param customer The given customer id from the corresponding text field + * @param supplier the given supplier id from the corresponding text field + * @return The account entry that can be used to create the new booking + * @throws InvalidFormDataException If the user provided incorrect data in any way + */ + private BookingAccountEntry getAccountFromFormData(String account, String customer, String supplier) + throws InvalidFormDataException { + if (account.equals("None")) { + return null; + } else if (account.equals("Main")) { + return bookingAccountEntryRepository.getByMain().orElseGet(BookingAccountEntry::newMain); + } else if (account.equals("Vat")) { + return bookingAccountEntryRepository.getByVat().orElseGet(BookingAccountEntry::newVat); + } else if (account.equals("Cust")) { + long userId; + try { + userId = Long.parseLong(customer); + } catch (NumberFormatException e) { + throw new InvalidFormDataException("Die angegebene Kunden-Nr. ist ungültig."); + } + return bookingAccountEntryRepository.getByUser(userId).or(() -> { + return userRepository.findById(userId).map((user) -> BookingAccountEntry.newUser(user)); + }).orElseThrow(() -> { + return new InvalidFormDataException("Der Kunde Nr. " + userId + " konnte nicht gefunden werden."); + }); + } else if (account.equals("Sup")) { + long supplierId; + try { + supplierId = Long.parseLong(supplier); + } catch (NumberFormatException e) { + throw new InvalidFormDataException("Die angegebene Lieferanten-Nr. ist ungültig."); + } + return bookingAccountEntryRepository.getBySupplier(supplierId).or(() -> { + return supplierRepository.findById(supplierId).map((sup) -> BookingAccountEntry.newSupplier(sup)); + }).orElseThrow(() -> { + return new InvalidFormDataException( + "Der Lieferant Nr. " + supplierId + " konnte nicht gefunden werden."); + }); + } else { + throw new RuntimeException("Invalid form value for an account: " + account); + } + } + + @GetMapping("/intern/accounting/addManual") + public String accountingAddManual(Model model) { + model.addAttribute("form_vals", new ManualAccounting()); + return "intern/accounting/addManual"; + } + + @PostMapping("/intern/accounting/addManual") + public String accountingAddManualSubmit(Model model, @ModelAttribute ManualAccounting formData) { + Booking booking; + try { + booking = createBooking(formData); + bookingRepository.save(booking); + model.addAttribute("info", "Die Buchung wurde erfolgreich erstellt."); + model.addAttribute("form_vals", new ManualAccounting()); // Reply with empty form on success + } catch (InvalidFormDataException e) { + model.addAttribute("error", e.getMessage()); + model.addAttribute("form_vals", formData); + } + return "intern/accounting/addManual"; + } + +} diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/customers/CustomersIndexController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/customers/CustomersIndexController.java index a27d407..cbda950 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/intern/customers/CustomersIndexController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/customers/CustomersIndexController.java @@ -4,8 +4,8 @@ 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.controller.intern.accounting.AccountingController; +import org.hso.ecommerce.controller.intern.accounting.AccountingController.ShortTemplateBookingResult; import org.hso.ecommerce.entities.booking.Booking; import org.hso.ecommerce.repos.booking.BookingRepository; import org.springframework.beans.factory.annotation.Autowired; diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierIndexController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierIndexController.java index 20c29f3..500843f 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierIndexController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierIndexController.java @@ -4,8 +4,8 @@ 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.controller.intern.accounting.AccountingController; +import org.hso.ecommerce.controller.intern.accounting.AccountingController.ShortTemplateBookingResult; import org.hso.ecommerce.entities.booking.Booking; import org.hso.ecommerce.repos.booking.BookingRepository; import org.springframework.beans.factory.annotation.Autowired; diff --git a/prototype/src/main/resources/templates/intern/accounting/addManual.html b/prototype/src/main/resources/templates/intern/accounting/addManual.html index d872206..bed16ca 100644 --- a/prototype/src/main/resources/templates/intern/accounting/addManual.html +++ b/prototype/src/main/resources/templates/intern/accounting/addManual.html @@ -20,10 +20,10 @@
-
+
-  EUR +  EUR
@@ -31,26 +31,26 @@
- +
- +
- +
- + - +
- + - +
@@ -59,26 +59,26 @@
- +
- +
- +
- + - +
- + - +
@@ -87,13 +87,13 @@
- - + +
- - - + + +

-- 2.47.1 From 6f90b9c9411ecb41327447f15b44deada98f893e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20F=C3=BCrderer?= Date: Fri, 5 Jun 2020 18:58:16 +0200 Subject: [PATCH 3/5] Fix a bug where bookings were invisible if they affected only one account --- .../hso/ecommerce/repos/booking/BookingRepository.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/booking/BookingRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/booking/BookingRepository.java index cbec49b..9847f4a 100644 --- a/prototype/src/main/java/org/hso/ecommerce/repos/booking/BookingRepository.java +++ b/prototype/src/main/java/org/hso/ecommerce/repos/booking/BookingRepository.java @@ -13,16 +13,16 @@ public interface BookingRepository extends JpaRepository { @Query("SELECT b FROM Booking b ORDER BY b.id DESC") List allBookingsReverseChronologically(); - @Query("SELECT b FROM Booking b WHERE b.source.isMainAccount = 1 OR b.destination.isMainAccount = 1 ORDER BY b.id DESC") + @Query("SELECT b FROM Booking b LEFT JOIN b.source s LEFT JOIN b.destination d WHERE s.isMainAccount = 1 OR d.isMainAccount = 1 ORDER BY b.id DESC") List mainBookingsReverseChronologically(); - @Query("SELECT b FROM Booking b WHERE b.source.isVATAccount = 1 OR b.destination.isVATAccount = 1 ORDER BY b.id DESC") + @Query("SELECT b FROM Booking b LEFT JOIN b.source s LEFT JOIN b.destination d WHERE s.isVATAccount = 1 OR d.isVATAccount = 1 ORDER BY b.id DESC") List vatBookingsReverseChronologically(); - @Query("SELECT b FROM Booking b WHERE b.source.userAccount.id = :customerId OR b.destination.userAccount.id = :customerId ORDER BY b.id DESC") + @Query("SELECT b FROM Booking b LEFT JOIN b.source s LEFT JOIN b.destination d WHERE s.userAccount.id = :customerId OR d.userAccount.id = :customerId ORDER BY b.id DESC") List 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") + @Query("SELECT b FROM Booking b LEFT JOIN b.source s LEFT JOIN b.destination d WHERE s.supplierAccount.id = :supplierId OR d.supplierAccount.id = :supplierId ORDER BY b.id DESC") List supplierBookingsReverseChronologically(long supplierId); } -- 2.47.1 From f348d33c9483225dacd09918f84b4d1d18db7b6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20F=C3=BCrderer?= Date: Fri, 5 Jun 2020 19:17:44 +0200 Subject: [PATCH 4/5] Show the balances of the main and vat account in the dashboard --- .../hso/ecommerce/app/RequestController.java | 5 ---- .../intern/InternIndexController.java | 28 ++++++++++++++++++- .../accounting/AccountingController.java | 2 +- .../resources/templates/intern/index.html | 4 +-- 4 files changed, 30 insertions(+), 9 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java b/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java index 6fc32f3..8373179 100644 --- a/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java +++ b/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java @@ -75,11 +75,6 @@ public class RequestController { return "redirect:/"; } - @GetMapping("/intern/") - public String intern() { - return "intern/index"; - } - @GetMapping("/intern/customerOrders/") public String internCustomerOrder() { return "intern/customerOrders/index"; diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/InternIndexController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/InternIndexController.java index 65b3dba..43fe2d9 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/intern/InternIndexController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/InternIndexController.java @@ -1,8 +1,34 @@ package org.hso.ecommerce.controller.intern; +import java.util.Optional; + +import org.hso.ecommerce.controller.intern.accounting.AccountingController; +import org.hso.ecommerce.entities.booking.BookingAccountEntry; +import org.hso.ecommerce.repos.booking.BookingAccountEntryRepository; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; @Controller -//@RequestMapping("...") +@RequestMapping("/intern") public class InternIndexController { + + @Autowired + private final BookingAccountEntryRepository bookingAccountEntryRepository = null; + + @GetMapping("/") + public String intern(Model model) { + Optional mainAccount = bookingAccountEntryRepository.getByMain(); + int mainAccountBalance = mainAccount.map(entry -> entry.newSumCent).orElse(0); + Optional vatAccount = bookingAccountEntryRepository.getByVat(); + int vatAccountBalance = vatAccount.map(entry -> entry.newSumCent).orElse(0); + + model.addAttribute("mainAccountBalance", AccountingController.fmtEuro(mainAccountBalance)); + model.addAttribute("vatAccountBalance", AccountingController.fmtEuro(vatAccountBalance)); + + return "intern/index"; + } + } diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/accounting/AccountingController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/accounting/AccountingController.java index 367995b..4ca0acf 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/intern/accounting/AccountingController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/accounting/AccountingController.java @@ -116,7 +116,7 @@ public class AccountingController { return new ShortTemplateBookingResult(balance, templateBookings); } - private String fmtEuro(long amountCent) { + public static String fmtEuro(long amountCent) { return String.format("%.2f EUR", amountCent / 100.0); } diff --git a/prototype/src/main/resources/templates/intern/index.html b/prototype/src/main/resources/templates/intern/index.html index dff0ab0..95997d7 100644 --- a/prototype/src/main/resources/templates/intern/index.html +++ b/prototype/src/main/resources/templates/intern/index.html @@ -88,12 +88,12 @@

Hauptkonto

-

4080,00 EUR

+

Umsatzsteuerkonto

-

-505,00 EUR

+

-- 2.47.1 From 691916894f12240767ff1d9953fb9aeaaae6b69a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20F=C3=BCrderer?= Date: Wed, 10 Jun 2020 11:48:38 +0200 Subject: [PATCH 5/5] Apply some changes according to comments on the pullrequest --- .../accounting/AccountingController.java | 19 ++++++------ .../ManualAccountingController.java | 31 +++++++++++++------ .../suppliers/SupplierIndexController.java | 2 +- .../intern/accounting/addManual.html | 2 +- 4 files changed, 32 insertions(+), 22 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/accounting/AccountingController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/accounting/AccountingController.java index 92f08f3..c7f9b75 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/intern/accounting/AccountingController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/accounting/AccountingController.java @@ -4,14 +4,13 @@ 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.ui.Model; import org.springframework.web.bind.annotation.GetMapping; @Controller @@ -169,7 +168,7 @@ public class AccountingController { } @GetMapping("/intern/accounting/") - public String accounting(HttpServletRequest request) { + public String accounting(Model model) { List bookings = bookingRepository.allBookingsReverseChronologically(); List templateBookings = new ArrayList<>(); for (Booking booking : bookings) { @@ -200,25 +199,25 @@ public class AccountingController { templateBookings.add(tb); } - request.setAttribute("bookings", templateBookings); + model.addAttribute("bookings", templateBookings); return "intern/accounting/index"; } @GetMapping("/intern/accounting/vat") - public String accountingVat(HttpServletRequest request) { + public String accountingVat(Model model) { List bookings = bookingRepository.vatBookingsReverseChronologically(); ShortTemplateBookingResult result = buildShortTemplate(bookings, account -> account.isVATAccount); - request.setAttribute("balance", result.balance); - request.setAttribute("bookings", result.bookings); + model.addAttribute("balance", result.balance); + model.addAttribute("bookings", result.bookings); return "intern/accounting/vat"; } @GetMapping("/intern/accounting/main") - public String accountingIntern(HttpServletRequest request) { + public String accountingIntern(Model model) { List bookings = bookingRepository.mainBookingsReverseChronologically(); ShortTemplateBookingResult result = buildShortTemplate(bookings, account -> account.isMainAccount); - request.setAttribute("balance", result.balance); - request.setAttribute("bookings", result.bookings); + model.addAttribute("balance", result.balance); + model.addAttribute("bookings", result.bookings); return "intern/accounting/main"; } } diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/accounting/ManualAccountingController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/accounting/ManualAccountingController.java index 5376e25..fd820b1 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/intern/accounting/ManualAccountingController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/accounting/ManualAccountingController.java @@ -1,5 +1,7 @@ package org.hso.ecommerce.controller.intern.accounting; +import java.util.Optional; + import org.hso.ecommerce.action.booking.CreateBookingAction; import org.hso.ecommerce.entities.booking.Booking; import org.hso.ecommerce.entities.booking.BookingAccountEntry; @@ -212,11 +214,15 @@ public class ManualAccountingController { } catch (NumberFormatException e) { throw new InvalidFormDataException("Die angegebene Kunden-Nr. ist ungültig."); } - return bookingAccountEntryRepository.getByUser(userId).or(() -> { - return userRepository.findById(userId).map((user) -> BookingAccountEntry.newUser(user)); - }).orElseThrow(() -> { - return new InvalidFormDataException("Der Kunde Nr. " + userId + " konnte nicht gefunden werden."); - }); + Optional bookingAccount = bookingAccountEntryRepository.getByUser(userId); + if (!bookingAccount.isPresent()) { + bookingAccount = userRepository.findById(userId).map((user) -> BookingAccountEntry.newUser(user)); + } + if (bookingAccount.isPresent()) { + return bookingAccount.get(); + } else { + throw new InvalidFormDataException("Der Kunde Nr. " + userId + " konnte nicht gefunden werden."); + } } else if (account.equals("Sup")) { long supplierId; try { @@ -224,12 +230,17 @@ public class ManualAccountingController { } catch (NumberFormatException e) { throw new InvalidFormDataException("Die angegebene Lieferanten-Nr. ist ungültig."); } - return bookingAccountEntryRepository.getBySupplier(supplierId).or(() -> { - return supplierRepository.findById(supplierId).map((sup) -> BookingAccountEntry.newSupplier(sup)); - }).orElseThrow(() -> { - return new InvalidFormDataException( + Optional bookingAccount = bookingAccountEntryRepository.getBySupplier(supplierId); + if (!bookingAccount.isPresent()) { + bookingAccount = supplierRepository.findById(supplierId) + .map((sup) -> BookingAccountEntry.newSupplier(sup)); + } + if (bookingAccount.isPresent()) { + return bookingAccount.get(); + } else { + throw new InvalidFormDataException( "Der Lieferant Nr. " + supplierId + " konnte nicht gefunden werden."); - }); + } } else { throw new RuntimeException("Invalid form value for an account: " + account); } diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierIndexController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierIndexController.java index 61f14f6..e8881fd 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierIndexController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierIndexController.java @@ -35,7 +35,7 @@ public class SupplierIndexController { private final BookingRepository bookingRepository = null; @Autowired - private AccountingController accountingController = null; + private final AccountingController accountingController = null; @GetMapping("suppliers") public String listSuppliers(Model model) { diff --git a/prototype/src/main/resources/templates/intern/accounting/addManual.html b/prototype/src/main/resources/templates/intern/accounting/addManual.html index bed16ca..9161dc7 100644 --- a/prototype/src/main/resources/templates/intern/accounting/addManual.html +++ b/prototype/src/main/resources/templates/intern/accounting/addManual.html @@ -23,7 +23,7 @@
-  EUR +  EUR
-- 2.47.1