Allow the user to create manual bookings

This commit is contained in:
Lukas Fürderer 2020-06-05 18:45:25 +02:00
parent 0061d25082
commit e53c1e0872
Signed by: Lukas
GPG Key ID: B0AFA46F94103349
5 changed files with 285 additions and 31 deletions

View File

@ -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";
}
}

View File

@ -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";
}
}

View File

@ -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;

View File

@ -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;

View File

@ -20,10 +20,10 @@
<main class="sidebar-layout content-width">
<nav th:replace="fragments/intern :: sidebar"></nav>
<div class="content-width">
<form class="detailgrid">
<form th:action="@{/intern/accounting/addManual}" th:object="${form_vals}" method="post" class="detailgrid">
<div class="s">
<label for="amount">Betrag</label>
<input type="number" step="0.01" name="amount" value="0.00"/>&nbsp;EUR
<input type="number" step="0.01" th:field="*{amount}" />&nbsp;EUR
</div>
<div class="spacer"></div>
@ -31,26 +31,26 @@
<fieldset>
<label for="source">Von Konto:</label>
<fieldset>
<input type="radio" id="s-no" name="destination" value="None" required>
<input type="radio" id="s-no" th:field="*{source}" value="None" required>
<label for="s-no">Kein Konto</label>
</fieldset>
<fieldset>
<input type="radio" id="s-main" name="destination" value="Main" required>
<input type="radio" id="s-main" th:field="*{source}" value="Main" required>
<label for="s-main">Hauptkonto</label>
</fieldset>
<fieldset>
<input type="radio" id="s-vat" name="destination" value="Vat" required>
<input type="radio" id="s-vat" th:field="*{source}" value="Vat" required>
<label for="s-vat">Mehrwertsteuerkonto</label>
</fieldset>
<fieldset>
<input type="radio" id="s-cust" name="destination" value="Cust" required>
<input type="radio" id="s-cust" th:field="*{source}" value="Cust" required>
<label for="s-cust">Kundenkonto</label>
<input placeholder="Kunden Nr." type="text" name="destination-customer" value=""/>
<input placeholder="Kunden Nr." type="text" th:field="*{sourceCustomer}" value=""/>
</fieldset>
<fieldset>
<input type="radio" id="s-sup" name="destination" value="Sup" required>
<input type="radio" id="s-sup" th:field="*{source}" value="Sup" required>
<label for="s-sup">Lieferanten Konto</label>
<input placeholder="Lieferanten Nr." type="text" name="destination-sup" value=""/>
<input placeholder="Lieferanten Nr." type="text" th:field="*{sourceSupplier}" value=""/>
</fieldset>
</fieldset>
</div>
@ -59,26 +59,26 @@
<fieldset>
<label for="destination">Nach Konto:</label>
<fieldset>
<input type="radio" id="d-no" name="destination" value="None" required>
<input type="radio" id="d-no" th:field="*{destination}" value="None" required>
<label for="d-no">Kein Konto</label>
</fieldset>
<fieldset>
<input type="radio" id="d-main" name="destination" value="Main" required>
<input type="radio" id="d-main" th:field="*{destination}" value="Main" required>
<label for="d-main">Hauptkonto</label>
</fieldset>
<fieldset>
<input type="radio" id="d-vat" name="destination" value="Vat" required>
<input type="radio" id="d-vat" th:field="*{destination}" value="Vat" required>
<label for="d-vat">Mehrwertsteuerkonto</label>
</fieldset>
<fieldset>
<input type="radio" id="d-cust" name="destination" value="Cus" required>
<input type="radio" id="d-cust" th:field="*{destination}" value="Cust" required>
<label for="d-cust">Kundenkonto</label>
<input placeholder="Kunden Nr." type="text" name="destination-customer" value=""/>
<input placeholder="Kunden Nr." type="text" th:field="*{destinationCustomer}" value=""/>
</fieldset>
<fieldset>
<input type="radio" id="d-sup" name="destination" value="Sup" required>
<input type="radio" id="d-sup" th:field="*{destination}" value="Sup" required>
<label for="d-sup">Lieferanten Konto</label>
<input placeholder="Lieferanten Nr." type="text" name="destination-sup" value=""/>
<input placeholder="Lieferanten Nr." type="text" th:field="*{destinationSupplier}" value=""/>
</fieldset>
</fieldset>
</div>
@ -87,13 +87,13 @@
<fieldset>
<label for="reason">Buchungsgrund:</label>
<fieldset>
<input type="radio" name="reason" value="Start">
<label for="d-sup">Startbuchung</label>
<input type="radio" id="r-start" th:field="*{reason}" value="Start">
<label for="r-start">Startbuchung</label>
</fieldset>
<fieldset>
<input type="radio" name="reason" value="Manual" checked>
<label for="d-sup">Manuell</label>
<input placeholder="Grund" class="full-width" type="text" name="reason-man" value=""/>
<input type="radio" id="r-manual" th:field="*{reason}" value="Manual">
<label for="r-manual">Manuell</label>
<input placeholder="Grund" class="full-width" type="text" th:field="*{reasonText}" value=""/>
</fieldset>
</fieldset>
</div>