mirror of
https://github.com/st-tu-dresden-praktikum/swt23w23
synced 2024-06-24 14:33:41 +02:00
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.
339 lines
12 KiB
Java
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";
|
|
}
|
|
}
|