swt23w23/src/main/java/catering/order/OrderController.java
Simon Bruder bac025fd0a
Make project REUSE compliant
This finally makes the licensing under AGPL-3.0-or-later explicit after
I got the okay from the kickstart source owners.

This also checks the REUSE compliance in a pre commit hook, and
therefore also in CI.
2023-12-11 17:59:14 +01:00

339 lines
12 KiB
Java

// SPDX-License-Identifier: AGPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 swt23w23
package catering.order;
import catering.catalog.CateringCatalog;
import catering.catalog.Consumable;
import catering.catalog.Rentable;
import catering.staff.Employee;
import catering.staff.StaffManagement;
import org.salespointframework.catalog.Product;
import org.salespointframework.inventory.UniqueInventory;
import org.salespointframework.inventory.UniqueInventoryItem;
import org.salespointframework.order.*;
import org.salespointframework.quantity.Quantity;
import org.salespointframework.useraccount.Role;
import org.salespointframework.useraccount.UserAccount;
import org.salespointframework.useraccount.web.LoggedIn;
import org.springframework.data.domain.Pageable;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
@Controller
@PreAuthorize("isAuthenticated()")
@SessionAttributes("event")
public class OrderController {
private final OrderManagement<CustomOrder> orderManagement;
private final CustomOrderRepository customOrderRepository;
private final UniqueInventory<UniqueInventoryItem> inventory;
private final CateringCatalog catalog;
private final StaffManagement staffManagement;
public OrderController(OrderManagement<CustomOrder> orderManagement, CustomOrderRepository customOrderRepository, UniqueInventory<UniqueInventoryItem> inventory, CateringCatalog catalog, StaffManagement staffManagement) {
this.orderManagement = orderManagement;
this.customOrderRepository = customOrderRepository;
this.catalog = catalog;
this.inventory = inventory;
this.staffManagement = staffManagement;
}
@GetMapping("/myOrders")
@PreAuthorize("hasRole('CUSTOMER')")
public String orders(Model model, @LoggedIn Optional<UserAccount> userAccount) {
List<CustomOrder> myOrders = orderManagement.findBy(userAccount.get()).stream().collect(Collectors.toList());
model.addAttribute("orders", myOrders);
model.addAttribute("total", myOrders.size());
model.addAttribute("cancelled", OrderStatus.CANCELED);
model.addAttribute("completed", OrderStatus.COMPLETED);
return "orders";
}
@GetMapping("/allOrders")
@PreAuthorize("hasRole('ADMIN')")
public String orders(Model model) {
List<CustomOrder> myOrders = orderManagement.findAll(Pageable.unpaged()).stream().collect(Collectors.toList());
model.addAttribute("orders", myOrders);
model.addAttribute("total", myOrders.size());
model.addAttribute("cancelled", OrderStatus.CANCELED);
model.addAttribute("completed", OrderStatus.COMPLETED);
return "orders";
}
// For Theo: filters orders by day
@GetMapping("/allOrders/{day}")
@PreAuthorize("hasRole('ADMIN')")
public String orders(@PathVariable String day, Model model) {
// Obtains an instance of LocalDate from a text string such as 2007-12-03.
LocalDate date = LocalDate.parse(day);
List<CustomOrder> myOrders = customOrderRepository.findOrdersByInterval(date.atStartOfDay(),
date.atStartOfDay().plusHours(23).withMinute(59).withSecond(59))
.stream().toList();
model.addAttribute("orders", myOrders);
model.addAttribute("total", myOrders.size());
return "orders";
}
@ModelAttribute("event")
CustomCart initializeCart() {
return new CustomCart(OrderType.EVENT_CATERING, LocalDateTime.now().plusDays(7),
LocalDateTime.now().plusDays(7).plusHours(1));
}
@GetMapping("/event")
@PreAuthorize("hasRole('CUSTOMER')")
public String event(Model model, @ModelAttribute("event") CustomCart cart) {
model.addAttribute("cartRentables", cart.stream().filter(item -> item.getProduct() instanceof Rentable).toList());
model.addAttribute("cartConsumables", cart.stream().filter(item -> item.getProduct() instanceof Consumable).toList());
model.addAttribute("totalPrice", cart.getPrice());
Map<Consumable, Quantity> invConsumables = new HashMap<>();
catalog.findConsumablesByCategoriesContains(cart.getOrderType().toString()).forEach(consumable ->
invConsumables.put(
consumable,
inventory.findByProduct(consumable).get().getQuantity())
);
model.addAttribute("invConsumables", invConsumables);
model.addAttribute(
"invRentables",
catalog.findRentablesByCategoriesContains(cart.getOrderType().toString())
.stream()
.collect(Collectors.toMap(rentable -> rentable, rentable -> findFreeAmountInInterval(
rentable,
cart.getStart(),
cart.getFinish(),
inventory,
customOrderRepository))));
model.addAttribute("allStaff", staffManagement.findAll().stream().toList());
model.addAttribute("duration", cart.getDurationInHours());
model.addAttribute("minDate", LocalDate.now().plusDays(7));
return "event";
}
@PostMapping("/event/addEmployee")
@PreAuthorize("hasRole('CUSTOMER')")
String addEmployeeToCart(@RequestParam("sid") long employeeId, @ModelAttribute("event") CustomCart cart) {
Employee employee = staffManagement.findById(employeeId).get();
if (cart.getStaff().contains(employee)) {
return "redirect:/event";
}
cart.addEmployee(employee);
return "redirect:/event";
}
@PostMapping("/event/removeEmployee")
@PreAuthorize("hasRole('CUSTOMER')")
String removeEmployeeFromCart(@RequestParam("sid") long employeeId, @ModelAttribute("event") CustomCart cart) {
Employee employee = staffManagement.findById(employeeId).get();
cart.removeEmployee(employee);
return "redirect:/event";
}
@PostMapping("/allOrders/remove")
@PreAuthorize("hasRole('ADMIN')")
public String removeOrder(@RequestParam Order.OrderIdentifier orderID, @LoggedIn Optional<UserAccount> userAccount) {
return userAccount.map(account -> {
if (account.hasRole(Role.of("ADMIN"))) {
CustomOrder myOrder = orderManagement.get(orderID).get(); // FIXME
orderManagement.cancelOrder(myOrder, "I have my own reasons.");
return "redirect:/allOrders";
}
return "redirect:/";
}).orElse("redirect:/");
}
@PostMapping("/event/addProduct")
@PreAuthorize("hasRole('CUSTOMER')")
public String addProduct(@RequestParam("pid") Product product, @RequestParam("number") int number, @ModelAttribute("event") CustomCart cart) {
Quantity amount = Quantity.of(number > 0 ? number : 1);
Quantity cartQuantity = cart.getQuantity(product);
Quantity available;
if (product instanceof Rentable rentable) {
available = findFreeAmountInInterval(
rentable,
cart.getStart(),
cart.getFinish(),
inventory,
customOrderRepository);
} else {
available = inventory.findByProduct(product).get().getQuantity();
}
// check for possible miss-inputs
if (amount.add(cartQuantity).isGreaterThan(available)) {
cart.addOrUpdateItem(product, cartQuantity.negate().add(available));
} else {
cart.addOrUpdateItem(product, amount);
}
return "redirect:/event";
}
@PostMapping("/event/removeProduct")
@PreAuthorize("hasRole('CUSTOMER')")
public String removeProduct(@RequestParam("itemId") String itemId, @ModelAttribute("event") CustomCart cart) {
cart.removeItem(itemId);
return "redirect:/event";
}
@PostMapping("/event/changeDate")
@PreAuthorize("hasRole('CUSTOMER')")
public String changeDate(@RequestParam("startDate") LocalDate start, @RequestParam("startHour") Optional<Integer> startHour, @RequestParam("finishDate") LocalDate finish, @RequestParam("finishHour") Optional<Integer> finishHour, @ModelAttribute("event") CustomCart cart) {
int unwrappedStartHour = startHour.orElse(cart.getStart().getHour());
int unwrappedFinishHour = finishHour.orElse(cart.getFinish().getHour());
finish = finish.isBefore(start) ? start : finish;
LocalDateTime startTime = LocalDateTime.of(start, LocalTime.of(unwrappedStartHour, 0));
LocalDateTime finishTime = LocalDateTime.of(finish,
LocalTime.of(unwrappedFinishHour <= unwrappedStartHour
&& !finish.isAfter(start) ? (unwrappedStartHour+1) % 24 : unwrappedFinishHour, 0));
cart.setStart(startTime);
cart.setFinish(finishTime);
return "redirect:/event";
}
@PostMapping("/event/checkout")
@PreAuthorize("hasRole('CUSTOMER')")
public String checkout(@ModelAttribute("event") CustomCart cart, @LoggedIn Optional<UserAccount> userAccount) {
if (cart.isEmpty() && cart.getStaff().isEmpty()) {
return "redirect:/event";
}
return userAccount.map(account -> {
CustomOrder myOrder = new CustomOrder(account.getId(), cart);
cart.addItemsTo(myOrder);
cart.addStaffTo(myOrder);
cart.addRentablesToOrder(myOrder, inventory);
orderManagement.payOrder(myOrder);
orderManagement.completeOrder(myOrder);
cart.clear();
return "redirect:/myOrders";
}).orElse("redirect:/event");
}
@PostMapping("/event/changeOrderType")
@PreAuthorize("hasRole('CUSTOMER')")
public String changeOrderType(@RequestParam(name = "type") String orderType, @ModelAttribute("event") CustomCart cart) {
try {
cart.setOrderType(OrderType.valueOf(orderType));
} catch (IllegalArgumentException e) {
cart.setOrderType(OrderType.EVENT_CATERING);
}
return "redirect:/event";
}
public static Quantity findFreeAmountInInterval(Rentable product, LocalDateTime start, LocalDateTime finish, UniqueInventory<UniqueInventoryItem> inventory, CustomOrderRepository customOrderRepository) {
return inventory.findByProduct(product)
.map(item -> item.getQuantity().subtract(
customOrderRepository.findOrdersByInterval(start, finish)
.flatMap(order -> order.getOrderLines(product).stream())
.map(OrderLine::getQuantity).stream()
.reduce(Quantity.NONE, Quantity::add)))
.orElse(Quantity.NONE);
}
@GetMapping("/orders/calender")
public String calender(Model model) {
ArrayList<ArrayList<String>> datesOfMonth = new ArrayList<ArrayList<String>>(28);
LocalDateTime startDateTime = LocalDateTime.now();
LocalDateTime endDateTime = startDateTime.plusDays(27).withHour(23).withMinute(59).withSecond(59); // FIXME: set me to end of day
LocalDate startDate = startDateTime.toLocalDate();
LocalDate endDate = endDateTime.toLocalDate();
// create all dates of the calendar
for (LocalDate date = startDate; !date.isAfter(endDate); date = date.plusDays(1)) {
ArrayList<String> x = new ArrayList<String>(2);
x.add(Integer.toString(date.getDayOfMonth()));
x.add(date.toString());
datesOfMonth.add(x);
}
// add each order overlapping with the calendar to the days it overlaps with
for (CustomOrder order : customOrderRepository.findOrdersByInterval(startDateTime, endDateTime)) {
int start_index_inclusive = Math.max((int) startDate.until(order.getStart().toLocalDate(), ChronoUnit.DAYS),0);
int end_index_exclusive = Math.min((int) startDate.until(order.getFinish().toLocalDate(), ChronoUnit.DAYS), 27) + 1;
String order_id = Objects.requireNonNull(order.getId()).toString();
for (int i = start_index_inclusive; i < end_index_exclusive; i++) {
// FIXME: exchange order ids for a short name or a color
datesOfMonth.get(i).add(order_id);
}
}
// get names of weekdays for table header starting with the current day
LocalDate endOfWeekDate = startDate.plusDays(6);
ArrayList<String> dayNames = new ArrayList<String>(7);
for (LocalDate date = startDate; !date.isAfter(endOfWeekDate); date = date.plusDays(1)) {
dayNames.add(date.getDayOfWeek().toString());
}
// FIXME: Get rid of the following paragraph of code by change of order_calender.html
// put data of datesOfMonth inside current structure used in order_calender.html
ArrayList<ArrayList<ArrayList<String>>> weeksOfMonth = new ArrayList<ArrayList<ArrayList<String>>>();
for (int i = 0; i < 4; i++) {
weeksOfMonth.add(new ArrayList<ArrayList<String>>(7));
for (int j = 0; j < 7; j++) {
weeksOfMonth.get(i).add(new ArrayList<String>());
weeksOfMonth.get(i).get(j).addAll(datesOfMonth.get(
i*7+j
)
);
}
}
model.addAttribute("weeksOfMonth", weeksOfMonth);
model.addAttribute("dayNames", dayNames);
return "orders_calender";
}
@GetMapping("/myOrders/{order}/invoice")
@PreAuthorize("isAuthenticated()")
public String invoice(Model model, @LoggedIn UserAccount userAccount, @PathVariable Order order){
if(!order.getUserAccountIdentifier().equals(userAccount.getId()) &&
!userAccount.hasRole(Role.of("ADMIN"))){
return "redirect:/unauthorized";
}
model.addAttribute("order", order);
model.addAttribute("staffCharges", order.getChargeLines().stream()
.collect(Collectors.toMap(Function.identity(),
cl -> staffManagement.findById(Long.parseLong(cl.getDescription())).get())));
return "invoice";
}
}