mirror of
https://github.com/st-tu-dresden-praktikum/swt23w23
synced 2024-07-19 21:04:36 +02:00
Adapt inventory to new catalog interface
This also does a major restructuring of the inventory mutate form. Some things still are not as they should be, but it mostly works like before. They can be fixed later. Co-authored-by: Theo Reichert <theo.reichert@mailbox.tu-dresden.de>
This commit is contained in:
parent
a4099f1de0
commit
2dff2842fc
|
@ -18,8 +18,7 @@ package Salespoint {
|
|||
|
||||
package catering {
|
||||
package catalog {
|
||||
interface ConsumableCatalog
|
||||
interface RentableCatalog
|
||||
interface CateringCatalog
|
||||
}
|
||||
|
||||
package inventory {
|
||||
|
@ -27,16 +26,21 @@ package catering {
|
|||
+ InventoryController(inventory : UniqueInventory)
|
||||
+ list(model : Model) : String
|
||||
+ edit(model : Model, pid : Product) : String
|
||||
+ edit(model : Model, form : InventoryMutateForm) : String
|
||||
+ edit(model : Model, pid : Product, form : InventoryMutateForm) : String
|
||||
+ editConsumable(form: ConsumableMutateForm, result: Errors, pid : Product, model : Model) : String
|
||||
+ editRentable(form: RentableMutateForm, result: Errors, pid : Product, model : Model) : String
|
||||
+ edit(form : InventoryMutateForm, result : Errors, pid : Product, model : Model) : String
|
||||
+ add(model : Model) : String
|
||||
+ add(model : Model, type : String) : String
|
||||
+ add(model : Model, form : InventoryMutateForm) : String
|
||||
+ addConsumable(form : ConsumableMutateForm, result : Errors, model : Model) : String
|
||||
+ addRentable(form : RentableMutateForm, result : Errors, model : Model) : String
|
||||
+ add(form : InventoryMutateForm, result : Errors, model : Model) : String
|
||||
+ delete(pid : Product) : String
|
||||
}
|
||||
InventoryController --> "1" catering.catalog.ConsumableCatalog : "-consumableCatalog"
|
||||
InventoryController --> "1" catering.catalog.RentableCatalog : "-rentableCatalog"
|
||||
InventoryController --> "1" catering.catalog.CateringCatalog : "-cateringCatalog"
|
||||
InventoryController ..> InventoryMutateForm
|
||||
InventoryController ..> ConsumableMutateForm
|
||||
InventoryController ..> RentableMutateForm
|
||||
InventoryController .u.> Salespoint.Product
|
||||
InventoryController -u-> "1" Salespoint.UniqueInventory : "-inventory"
|
||||
InventoryController .u.> Salespoint.UniqueInventoryItem
|
||||
|
@ -48,13 +52,28 @@ package catering {
|
|||
+ InventoryInitializer(inventory : UniqueInventory, catalog : CateringCatalog)
|
||||
+ initialize() : void
|
||||
}
|
||||
InventoryInitializer --> "1" catering.catalog.ConsumableCatalog : "-consumableCatalog"
|
||||
InventoryInitializer --> "1" catering.catalog.RentableCatalog : "-rentableCatalog"
|
||||
InventoryInitializer --> "1" catering.catalog.CateringCatalog : "-cateringCatalog"
|
||||
InventoryInitializer .u.|> Salespoint.DataInitializer
|
||||
InventoryInitializer .u.> Salespoint.Quantity
|
||||
InventoryInitializer -u-> "1" Salespoint.UniqueInventory : "-inventory"
|
||||
InventoryInitializer .u.> Salespoint.UniqueInventoryItem
|
||||
InventoryInitializer .u.> Spring.Assert
|
||||
|
||||
class InventoryMutateForm
|
||||
class ConsumableMutateForm
|
||||
class RentableMutateForm
|
||||
ConsumableMutateForm <|-- InventoryMutateForm
|
||||
RentableMutateForm <|-- InventoryMutateForm
|
||||
|
||||
InventoryMutateForm ..> Salespoint.Quantity
|
||||
|
||||
InventoryMutateForm ..> Salespoint.Product
|
||||
ConsumableMutateForm ..> Salespoint.Product
|
||||
RentableMutateForm ..> Salespoint.Product
|
||||
|
||||
InventoryMutateForm ..> Salespoint.UniqueInventoryItem
|
||||
ConsumableMutateForm ..> Salespoint.UniqueInventoryItem
|
||||
RentableMutateForm ..> Salespoint.UniqueInventoryItem
|
||||
}
|
||||
}
|
||||
@enduml
|
||||
|
|
BIN
src/main/asciidoc/models/design/inventory.svg
(Stored with Git LFS)
BIN
src/main/asciidoc/models/design/inventory.svg
(Stored with Git LFS)
Binary file not shown.
|
@ -18,12 +18,17 @@ package catering.catalog;
|
|||
|
||||
import static org.salespointframework.core.Currencies.EURO;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.javamoney.moneta.Money;
|
||||
import org.salespointframework.core.DataInitializer;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import catering.order.OrderType;
|
||||
|
||||
@Component
|
||||
@Order(20)
|
||||
class CatalogDataInitializer implements DataInitializer {
|
||||
|
@ -41,9 +46,21 @@ class CatalogDataInitializer implements DataInitializer {
|
|||
return;
|
||||
}
|
||||
|
||||
cateringCatalog.save(new CatalogDummy("Brötchen Vollkorn", CatalogDummyType.CONSUMABLE, Money.of(1, EURO), Money.of(0.5, EURO),
|
||||
Money.of(0.75, EURO)));
|
||||
cateringCatalog.save(new CatalogDummy("Kerze Rot", CatalogDummyType.CONSUMABLE, Money.of(2, EURO), Money.of(1.5, EURO)));
|
||||
cateringCatalog.save(new CatalogDummy("Brotschneidemaschine Power X 3000", CatalogDummyType.RENTABLE, Money.of(25, EURO), Money.of(10000, EURO)));
|
||||
cateringCatalog.save(new Consumable(
|
||||
"Brötchen Vollkorn",
|
||||
Money.of(1, EURO),
|
||||
Money.of(0.5, EURO),
|
||||
Optional.of(Money.of(0.75, EURO)),
|
||||
Set.of(OrderType.EVENT_CATERING, OrderType.MOBILE_BREAKFAST)));
|
||||
cateringCatalog.save(new Rentable(
|
||||
"Kerze Rot",
|
||||
Money.of(2, EURO),
|
||||
Money.of(1.5, EURO),
|
||||
Set.of(OrderType.EVENT_CATERING, OrderType.MOBILE_BREAKFAST)));
|
||||
cateringCatalog.save(new Rentable(
|
||||
"Brotschneidemaschine Power X 3000",
|
||||
Money.of(25, EURO),
|
||||
Money.of(10000, EURO),
|
||||
Set.of(OrderType.EVENT_CATERING, OrderType.MOBILE_BREAKFAST)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,79 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2023 Simon Bruder
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package catering.catalog;
|
||||
|
||||
import javax.money.MonetaryAmount;
|
||||
|
||||
import org.salespointframework.catalog.Product;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
|
||||
@Entity
|
||||
public class CatalogDummy extends Product {
|
||||
MonetaryAmount wholesalePrice;
|
||||
// Optional<MonetaryAmount> promotionPrice;
|
||||
// enterprise java goes brrr
|
||||
MonetaryAmount promotionPrice;
|
||||
CatalogDummyType type;
|
||||
|
||||
// enterprise java goes brrr
|
||||
@SuppressWarnings({ "unused", "deprecation" })
|
||||
private CatalogDummy() {
|
||||
}
|
||||
|
||||
public CatalogDummy(String name, CatalogDummyType type, MonetaryAmount price, MonetaryAmount wholesalePrice,
|
||||
MonetaryAmount promotionPrice) {
|
||||
super(name, price);
|
||||
Assert.notNull(type, "Type must not be null!");
|
||||
Assert.notNull(wholesalePrice, "WholesalePricee must not be null!");
|
||||
// PromotionPrice can be null.
|
||||
this.type = type;
|
||||
this.wholesalePrice = wholesalePrice;
|
||||
this.promotionPrice = promotionPrice;
|
||||
}
|
||||
|
||||
public CatalogDummy(String name, CatalogDummyType type, MonetaryAmount price, MonetaryAmount wholesalePrice) {
|
||||
this(name, type, price, wholesalePrice, null);
|
||||
}
|
||||
|
||||
public MonetaryAmount getWholesalePrice() {
|
||||
return wholesalePrice;
|
||||
}
|
||||
|
||||
public void setWholesalePrice(MonetaryAmount wholesalePrice) {
|
||||
Assert.notNull(wholesalePrice, "WholesalePricee must not be null!");
|
||||
this.wholesalePrice = wholesalePrice;
|
||||
}
|
||||
|
||||
public MonetaryAmount getPromotionPrice() {
|
||||
return promotionPrice;
|
||||
}
|
||||
|
||||
public void setPromotionPrice(MonetaryAmount promotionPrice) {
|
||||
this.promotionPrice = promotionPrice;
|
||||
}
|
||||
|
||||
public CatalogDummyType getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public void setType(CatalogDummyType type) {
|
||||
Assert.notNull(type, "Type must not be null!");
|
||||
this.type = type;
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2023 Simon Bruder
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package catering.catalog;
|
||||
|
||||
public enum CatalogDummyType {
|
||||
CONSUMABLE,
|
||||
RENTABLE,
|
||||
}
|
87
src/main/java/catering/inventory/ConsumableMutateForm.java
Normal file
87
src/main/java/catering/inventory/ConsumableMutateForm.java
Normal file
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright (C) 2023 Simon Bruder
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package catering.inventory;
|
||||
|
||||
import static org.salespointframework.core.Currencies.EURO;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.money.MonetaryAmount;
|
||||
import javax.money.NumberValue;
|
||||
|
||||
import org.javamoney.moneta.Money;
|
||||
import org.salespointframework.catalog.Product;
|
||||
import org.salespointframework.inventory.UniqueInventoryItem;
|
||||
|
||||
import catering.catalog.Consumable;
|
||||
import catering.order.OrderType;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.PositiveOrZero; // NonNegative in enterprise java
|
||||
|
||||
class ConsumableMutateForm extends InventoryMutateForm {
|
||||
private @NotNull @PositiveOrZero Double wholesalePrice;
|
||||
private @NotNull Optional<@PositiveOrZero Double> promotionPrice = Optional.empty();
|
||||
|
||||
public Double getWholesalePrice() {
|
||||
return wholesalePrice;
|
||||
}
|
||||
|
||||
public Optional<Double> getPromotionPrice() {
|
||||
return promotionPrice;
|
||||
}
|
||||
|
||||
public void setWholesalePrice(Double wholesalePrice) {
|
||||
this.wholesalePrice = wholesalePrice;
|
||||
}
|
||||
|
||||
public void setPromotionPrice(Optional<Double> promotionPrice) {
|
||||
this.promotionPrice = promotionPrice;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Product toProduct() {
|
||||
return new Consumable(
|
||||
getName(),
|
||||
Money.of(getRetailPrice(), EURO),
|
||||
Money.of(getWholesalePrice(), EURO),
|
||||
getPromotionPrice().map(price -> Money.of(price, EURO)),
|
||||
// FIXME: categories for creation of consumables are hardcoded
|
||||
Set.of(OrderType.EVENT_CATERING, OrderType.MOBILE_BREAKFAST));
|
||||
}
|
||||
|
||||
public static ConsumableMutateForm of(Consumable product, UniqueInventoryItem item) {
|
||||
ConsumableMutateForm form = new ConsumableMutateForm();
|
||||
form.setName(product.getName());
|
||||
form.setQuantity(item.getQuantity());
|
||||
form.setWholesalePrice(product.getWholesalePrice().getNumber().doubleValueExact());
|
||||
form.setRetailPrice(product.getRetailPrice().getNumber().doubleValueExact());
|
||||
form.setPromotionPrice(
|
||||
product.getPromotionPrice().map(MonetaryAmount::getNumber).map(NumberValue::doubleValueExact));
|
||||
return form;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void modifyProductPrimitive(Product product) {
|
||||
if (product instanceof Consumable consumable) {
|
||||
consumable.setWholesalePrice(Money.of(getWholesalePrice(), EURO));
|
||||
consumable.setPromotionPrice(getPromotionPrice().map(price -> Money.of(price, EURO)));
|
||||
} else {
|
||||
throw new IllegalArgumentException("ConsumableMutateForm can only modify instances of Consumable");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,14 +16,6 @@
|
|||
*/
|
||||
package catering.inventory;
|
||||
|
||||
import static org.salespointframework.core.Currencies.EURO;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.money.MonetaryAmount;
|
||||
import javax.money.NumberValue;
|
||||
|
||||
import org.javamoney.moneta.Money;
|
||||
import org.salespointframework.catalog.Product;
|
||||
import org.salespointframework.inventory.UniqueInventory;
|
||||
import org.salespointframework.inventory.UniqueInventoryItem;
|
||||
|
@ -36,11 +28,35 @@ import org.springframework.web.bind.annotation.GetMapping;
|
|||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import catering.catalog.CatalogDummy;
|
||||
import catering.catalog.CateringCatalog;
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
/*
|
||||
* TODO TL;DR: Use HandlerMethodArgumentResolver
|
||||
*
|
||||
* This class currently has many confusing methods that do a variety of things,
|
||||
* but mostly work around polymorphic restrictions of Spring.
|
||||
* Ideally, spring would allow a form to be polymorphic
|
||||
* based on its attributes, if they are unique,
|
||||
* or based on another parameter.
|
||||
* From what I could find,
|
||||
* this is not possible without implementing a custom HandlerMethodArgumentResolver.
|
||||
* If there is time, the controller should be changed to use this,
|
||||
* which should vastly simplify it.
|
||||
*
|
||||
* Adding will always require a type parameter,
|
||||
* as the type is not known at first.
|
||||
* However, currently there are two (or even three) POST handlers,
|
||||
* as there are two different form types it must support
|
||||
* (and an infinite amount of invalid types that it should ignore).
|
||||
*
|
||||
* Editing should not require passing any additional type parameter around
|
||||
* as the type can be inferred from the product.
|
||||
* Changing the type of a product should not be possible.
|
||||
*/
|
||||
|
||||
@Controller
|
||||
class InventoryController {
|
||||
private final UniqueInventory<UniqueInventoryItem> inventory;
|
||||
|
@ -64,17 +80,10 @@ class InventoryController {
|
|||
@PreAuthorize("hasRole('ADMIN')")
|
||||
@GetMapping("/inventory/edit/{pid}")
|
||||
String edit(Model model, @PathVariable Product pid) {
|
||||
CatalogDummy product = (CatalogDummy) pid;
|
||||
UniqueInventoryItem item = inventory.findByProduct(pid).get();
|
||||
return edit(model,
|
||||
new InventoryMutateForm(product.getType(),
|
||||
product.getName(),
|
||||
item.getQuantity(),
|
||||
product.getWholesalePrice().getNumber().doubleValueExact(),
|
||||
product.getPrice().getNumber().doubleValueExact(),
|
||||
Optional.ofNullable(product.getPromotionPrice())
|
||||
.map(MonetaryAmount::getNumber)
|
||||
.map(NumberValue::doubleValueExact)));
|
||||
final InventoryMutateForm form = InventoryMutateForm.of(pid, item);
|
||||
|
||||
return edit(model, form);
|
||||
}
|
||||
|
||||
String edit(Model model, InventoryMutateForm form) {
|
||||
|
@ -85,23 +94,28 @@ class InventoryController {
|
|||
}
|
||||
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
@PostMapping("/inventory/edit/{pid}")
|
||||
String edit(@Valid @ModelAttribute("form") InventoryMutateForm form, Errors result, @PathVariable Product pid,
|
||||
Model model) {
|
||||
@PostMapping(path = "/inventory/edit/{pid}", params = "type=Consumable")
|
||||
String editConsumable(@Valid @ModelAttribute("form") ConsumableMutateForm form, Errors result,
|
||||
@PathVariable Product pid, Model model) {
|
||||
return edit(form, result, pid, model);
|
||||
}
|
||||
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
@PostMapping(path = "/inventory/edit/{pid}", params = "type=Rentable")
|
||||
String editRentable(@Valid @ModelAttribute("form") RentableMutateForm form, Errors result,
|
||||
@PathVariable Product pid, Model model) {
|
||||
return edit(form, result, pid, model);
|
||||
}
|
||||
|
||||
String edit(InventoryMutateForm form, Errors result, Product product, Model model) {
|
||||
if (result.hasErrors()) {
|
||||
return edit(model, form);
|
||||
}
|
||||
CatalogDummy product = (CatalogDummy) pid;
|
||||
UniqueInventoryItem item = inventory.findByProduct(pid).get();
|
||||
|
||||
product.setName(form.getName());
|
||||
product.setType(form.getType());
|
||||
product.setPrice(Money.of(form.getRetailPrice(), EURO));
|
||||
product.setWholesalePrice(Money.of(form.getWholesalePrice(), EURO));
|
||||
product.setPromotionPrice(form.getPromotionPrice().map(price -> Money.of(price, EURO)).orElse(null));
|
||||
|
||||
form.modifyProduct(product);
|
||||
product = cateringCatalog.save(product);
|
||||
|
||||
UniqueInventoryItem item = inventory.findByProduct(product).get();
|
||||
// no setQuantity in enterprise java
|
||||
// (though returing a modified object is actually nice)
|
||||
inventory.save(item.increaseQuantity(form.getQuantity().subtract(item.getQuantity())));
|
||||
|
@ -109,9 +123,17 @@ class InventoryController {
|
|||
}
|
||||
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
@GetMapping("/inventory/add")
|
||||
String add(Model model) {
|
||||
return add(model, InventoryMutateForm.empty());
|
||||
@GetMapping(path = "/inventory/add")
|
||||
String add(Model model, @RequestParam String type) {
|
||||
switch (type) {
|
||||
case "Consumable":
|
||||
return add(model, new ConsumableMutateForm());
|
||||
case "Rentable":
|
||||
return add(model, new RentableMutateForm());
|
||||
default:
|
||||
// TODO better error handling
|
||||
return "redirect:/inventory";
|
||||
}
|
||||
}
|
||||
|
||||
String add(Model model, InventoryMutateForm form) {
|
||||
|
@ -121,17 +143,22 @@ class InventoryController {
|
|||
}
|
||||
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
@PostMapping("/inventory/add")
|
||||
String add(@Valid @ModelAttribute("form") InventoryMutateForm form, Errors result, Model model) {
|
||||
@PostMapping(path = "/inventory/add", params = "type=Consumable")
|
||||
String addConsumable(@Valid @ModelAttribute("form") ConsumableMutateForm form, Errors result, Model model) {
|
||||
return add(form, result, model);
|
||||
}
|
||||
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
@PostMapping(path = "/inventory/add", params = "type=Rentable")
|
||||
String addRentable(@Valid @ModelAttribute("form") ConsumableMutateForm form, Errors result, Model model) {
|
||||
return add(form, result, model);
|
||||
}
|
||||
|
||||
String add(@Valid InventoryMutateForm form, Errors result, Model model) {
|
||||
if (result.hasErrors()) {
|
||||
return add(model, form);
|
||||
}
|
||||
inventory.save(new UniqueInventoryItem(
|
||||
cateringCatalog
|
||||
.save(new CatalogDummy(form.getName(), form.getType(), Money.of(form.getRetailPrice(), EURO),
|
||||
Money.of(form.getWholesalePrice(), EURO),
|
||||
form.getPromotionPrice().map(price -> Money.of(price, EURO)).orElse(null))),
|
||||
form.getQuantity()));
|
||||
inventory.save(new UniqueInventoryItem(cateringCatalog.save(form.toProduct()), form.getQuantity()));
|
||||
return "redirect:/inventory";
|
||||
}
|
||||
|
||||
|
@ -140,7 +167,7 @@ class InventoryController {
|
|||
String delete(@PathVariable Product pid) {
|
||||
UniqueInventoryItem item = inventory.findByProduct(pid).get();
|
||||
inventory.delete(item);
|
||||
cateringCatalog.delete((CatalogDummy) pid);
|
||||
cateringCatalog.delete(pid);
|
||||
return "redirect:/inventory";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,39 +16,33 @@
|
|||
*/
|
||||
package catering.inventory;
|
||||
|
||||
import java.util.Optional;
|
||||
import static org.salespointframework.core.Currencies.EURO;
|
||||
|
||||
import org.javamoney.moneta.Money;
|
||||
import org.salespointframework.catalog.Product;
|
||||
import org.salespointframework.inventory.UniqueInventoryItem;
|
||||
import org.salespointframework.quantity.Quantity;
|
||||
|
||||
import catering.catalog.CatalogDummyType;
|
||||
import catering.catalog.Consumable;
|
||||
import catering.catalog.Rentable;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.PositiveOrZero; // NonNegative in enterprise java
|
||||
|
||||
class InventoryMutateForm {
|
||||
private final @NotNull CatalogDummyType type;
|
||||
private final @NotEmpty String name;
|
||||
private final @NotNull Quantity quantity;
|
||||
private final @NotNull double wholesalePrice, retailPrice;
|
||||
private final @NotNull Optional<Double> promotionPrice;
|
||||
/**
|
||||
* Abstract class for handling inventory mutations.
|
||||
*
|
||||
* It has children for every product type.
|
||||
*
|
||||
* The current implementation requires {@link #forProductType(Class)} and {@link #of(Product, UniqueInventoryItem)}
|
||||
* to also be updated when a new child class is added.
|
||||
*/
|
||||
abstract class InventoryMutateForm {
|
||||
private @NotEmpty String name;
|
||||
private @NotNull Quantity quantity;
|
||||
private @NotNull @PositiveOrZero Double retailPrice;
|
||||
|
||||
public InventoryMutateForm(@NotNull CatalogDummyType type, @NotEmpty String name,
|
||||
@NotNull Quantity quantity, @PositiveOrZero double wholesalePrice, @PositiveOrZero double retailPrice,
|
||||
@PositiveOrZero Optional<Double> promotionPrice) {
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
this.quantity = quantity;
|
||||
this.wholesalePrice = wholesalePrice;
|
||||
this.retailPrice = retailPrice;
|
||||
this.promotionPrice = promotionPrice;
|
||||
}
|
||||
|
||||
public static InventoryMutateForm empty() {
|
||||
return new InventoryMutateForm(null, "", Quantity.of(0), 0, 0, Optional.empty());
|
||||
}
|
||||
|
||||
public CatalogDummyType getType() {
|
||||
return type;
|
||||
public InventoryMutateForm() {
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
|
@ -59,15 +53,80 @@ class InventoryMutateForm {
|
|||
return quantity;
|
||||
}
|
||||
|
||||
public double getWholesalePrice() {
|
||||
return wholesalePrice;
|
||||
}
|
||||
|
||||
public double getRetailPrice() {
|
||||
public Double getRetailPrice() {
|
||||
return retailPrice;
|
||||
}
|
||||
|
||||
public Optional<Double> getPromotionPrice() {
|
||||
return promotionPrice;
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setQuantity(Quantity quantity) {
|
||||
this.quantity = quantity;
|
||||
}
|
||||
|
||||
public void setRetailPrice(Double retailPrice) {
|
||||
this.retailPrice = retailPrice;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an empty {@link InventoryMutateForm} for {@link Product}s of the given {@link Class}.
|
||||
*
|
||||
* @param <T> a child class of {@link Product}
|
||||
* @param type the concrete {@link Product} the form should mutate
|
||||
* @return an object of an {@link InventoryMutateForm} subclass
|
||||
* @throws IllegalArgumentException if the {@literal type} is not supported
|
||||
*/
|
||||
public static <T extends Product> InventoryMutateForm forProductType(Class<T> type) {
|
||||
// Java can’t switch over Class in JDK17 (without preview features)
|
||||
// See https://openjdk.org/jeps/406 for improvement in higher versions.
|
||||
if (type.equals(Consumable.class)) {
|
||||
return new ConsumableMutateForm();
|
||||
} else if (type.equals(Rentable.class)) {
|
||||
return new RentableMutateForm();
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"InventoryMutateForm::forProductType not supported for given types");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a populated {@link InventoryMutateForm} from a given {@link Product} and {@link UniqueInventoryItem}.
|
||||
*
|
||||
* @param product an instance of a {@link Product} subclass
|
||||
* @param item an {@link UniqueInventoryItem} holding a {@link Quantity}
|
||||
* @return an object of an {@link InventoryMutateForm} subclass
|
||||
*/
|
||||
public static InventoryMutateForm of(Product product, UniqueInventoryItem item) {
|
||||
if (product instanceof Consumable consumable) {
|
||||
return ConsumableMutateForm.of(consumable, item);
|
||||
} else if (product instanceof Rentable rentable) {
|
||||
return RentableMutateForm.of(rentable, item);
|
||||
} else {
|
||||
throw new IllegalArgumentException("InventoryMutateForm::ofProductAndItem not supported for given types");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Product} from a populated {@link InventoryMutateForm}.
|
||||
*
|
||||
* @return an instance of a {@link Product} subclass
|
||||
*/
|
||||
public abstract Product toProduct();
|
||||
|
||||
/**
|
||||
* Modifies a given {@link Product} to match the values from the {@link InventoryMutateForm}.
|
||||
*
|
||||
* As the {@link Quantity} is stored inside of the {@link UniqueInventoryItem},
|
||||
* it has to be updated manually.
|
||||
*
|
||||
* @param product the {@link Product} to be updated
|
||||
*/
|
||||
public void modifyProduct(Product product) {
|
||||
product.setName(getName());
|
||||
product.setPrice(Money.of(getRetailPrice(), EURO));
|
||||
modifyProductPrimitive(product);
|
||||
}
|
||||
|
||||
protected abstract void modifyProductPrimitive(Product product);
|
||||
}
|
||||
|
|
70
src/main/java/catering/inventory/RentableMutateForm.java
Normal file
70
src/main/java/catering/inventory/RentableMutateForm.java
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright (C) 2023 Simon Bruder
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package catering.inventory;
|
||||
|
||||
import static org.salespointframework.core.Currencies.EURO;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.javamoney.moneta.Money;
|
||||
import org.salespointframework.catalog.Product;
|
||||
import org.salespointframework.inventory.UniqueInventoryItem;
|
||||
|
||||
import catering.catalog.Rentable;
|
||||
import catering.order.OrderType;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.PositiveOrZero;
|
||||
|
||||
class RentableMutateForm extends InventoryMutateForm {
|
||||
private @NotNull @PositiveOrZero Double wholesalePrice;
|
||||
|
||||
public Double getWholesalePrice() {
|
||||
return wholesalePrice;
|
||||
}
|
||||
|
||||
public void setWholesalePrice(Double wholesalePrice) {
|
||||
this.wholesalePrice = wholesalePrice;
|
||||
}
|
||||
|
||||
public static RentableMutateForm of(Rentable product, UniqueInventoryItem item) {
|
||||
RentableMutateForm form = new RentableMutateForm();
|
||||
form.setName(product.getName());
|
||||
form.setQuantity(item.getQuantity());
|
||||
form.setWholesalePrice(product.getWholesalePrice().getNumber().doubleValueExact());
|
||||
form.setRetailPrice(product.getRetailPrice().getNumber().doubleValueExact());
|
||||
return form;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Product toProduct() {
|
||||
return new Rentable(
|
||||
getName(),
|
||||
Money.of(getRetailPrice(), EURO),
|
||||
Money.of(getWholesalePrice(), EURO),
|
||||
// FIXME: categories for creation of rentables are hardcoded
|
||||
Set.of(OrderType.EVENT_CATERING, OrderType.MOBILE_BREAKFAST));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void modifyProductPrimitive(Product product) {
|
||||
if (product instanceof Rentable rentable) {
|
||||
rentable.setWholesalePrice(Money.of(getWholesalePrice(), EURO));
|
||||
} else {
|
||||
throw new IllegalArgumentException("RentableMutateForm can only modify instances of Rentable");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,12 +7,9 @@
|
|||
<div layout:fragment="content">
|
||||
<h2 th:text="${'Produkt ' + (actionIsAdd ? 'anlegen' : 'bearbeiten')}"></h2>
|
||||
<form method="post" th:object="${form}">
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="type">Typ</label>
|
||||
<div class="form-check form-check-inline" th:each="type : ${T(catering.catalog.CatalogDummyType).values()}">
|
||||
<input class="form-check-input" type="radio" th:field="*{type}" th:value="${type}" th:text="${type}" th:errorclass="is-invalid" required/>
|
||||
<div th:if="${#fields.hasErrors('type')}" class="invalid-feedback">Ungültiger Typ.</div>
|
||||
</div>
|
||||
<div class="mb-3" th:if="${actionIsAdd}">
|
||||
<a th:href="@{/inventory/add?type=Consumable}" class="btn" th:classappend="${form.getClass().getSimpleName() == 'ConsumableMutateForm' ? 'btn-primary' : 'btn-secondary'}">Verbrauchsmaterial</a>
|
||||
<a th:href="@{/inventory/add?type=Rentable}" class="btn" th:classappend="${form.getClass().getSimpleName() == 'RentableMutateForm' ? 'btn-primary' : 'btn-secondary'}">Leihmaterial</a>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="name">Produktname</label>
|
||||
|
@ -35,8 +32,7 @@
|
|||
<input class="form-control" type="number" name="retailPrice" th:value="${#numbers.formatDecimal(form.retailPrice, 1, 2)}" th:errorclass="is-invalid" step="0.01" min="0" required/>
|
||||
<div th:if="${#fields.hasErrors('retailPrice')}" class="invalid-feedback">Ungültiger Verkaufspreis.</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<!-- FIXME darf nur bei angeboten als teil von partyservice angezeigt werden -->
|
||||
<div class="mb-3" th:if="${form.getClass().getSimpleName() == 'ConsumableMutateForm'}">
|
||||
<label class="form-label" for="promotionPrice">Aktionspreis</label>
|
||||
<input class="form-control" type="number" name="promotionPrice" th:value="${#numbers.formatDecimal(form.promotionPrice.orElse(null), 1, 2)}" th:errorclass="is-invalid" step="0.01" min="0"/>
|
||||
<div th:if="${#fields.hasErrors('promotionPrice')}" class="invalid-feedback">Ungültiger Aktionspreis.</div>
|
||||
|
|
|
@ -24,17 +24,18 @@
|
|||
<td th:text="${item.product.name}"></td>
|
||||
<td th:text="${item.quantity}"></td>
|
||||
<td th:text="${item.product.wholesalePrice}"></td>
|
||||
<td th:if="${item.product.promotionPrice != null}"><del th:text="${item.product.price}"></del> <span th:text="${item.product.promotionPrice}"></span></td>
|
||||
<td th:if="${item.product.promotionPrice == null}" th:text="${item.product.price}"></td>
|
||||
<td th:if="${item.product.getClass().getSimpleName() == 'Consumable' && item.product.promotionPrice.isPresent()}"><del th:text="${item.product.retailPrice}"></del> <span th:text="${item.product.promotionPrice.get()}"></span></td>
|
||||
<td th:if="${item.product.getClass().getSimpleName() != 'Consumable' || item.product.promotionPrice.isEmpty()}" th:text="${item.product.price}"></td>
|
||||
<td th:text="${item.product.wholesalePrice.multiply(item.quantity.getAmount())}"></td>
|
||||
<td>
|
||||
<a th:href="@{/inventory/edit/{id}(id=${item.product.id})}"><button class="btn btn-warning">Bearbeiten</button></a>
|
||||
<a th:href="@{/inventory/edit/{id}(id=${item.product.id},type=${item.product.getClass().getSimpleName()})}"><button class="btn btn-warning">Bearbeiten</button></a>
|
||||
<a th:href="@{/inventory/delete/{id}(id=${item.product.id})}"><button class="btn btn-danger">Entfernen</button></a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<a href="/inventory/add"><button class="btn btn-primary">Artikel hinzufügen</button></a>
|
||||
<a href="/inventory/add?type=Consumable"><button class="btn btn-primary">Verbrauchsmaterial hinzufügen</button></a>
|
||||
<a href="/inventory/add?type=Rentable"><button class="btn btn-primary">Leihmaterial hinzufügen</button></a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -19,8 +19,8 @@ package catering.inventory;
|
|||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.tuple;
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.hamcrest.CoreMatchers.not;
|
||||
import static org.hamcrest.CoreMatchers.endsWith;
|
||||
import static org.hamcrest.CoreMatchers.not;
|
||||
import static org.salespointframework.core.Currencies.EURO;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
|
@ -29,9 +29,12 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
|||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.javamoney.moneta.Money;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.salespointframework.catalog.Product;
|
||||
import org.salespointframework.catalog.Product.ProductIdentifier;
|
||||
import org.salespointframework.inventory.UniqueInventory;
|
||||
import org.salespointframework.inventory.UniqueInventoryItem;
|
||||
|
@ -44,8 +47,7 @@ import org.springframework.security.test.context.support.WithMockUser;
|
|||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.ResultActions;
|
||||
|
||||
import catering.catalog.CatalogDummy;
|
||||
import catering.catalog.CatalogDummyType;
|
||||
import catering.catalog.Consumable;
|
||||
|
||||
@AutoConfigureMockMvc
|
||||
@SpringBootTest
|
||||
|
@ -57,13 +59,13 @@ class InventoryControllerIntegrationTests {
|
|||
UniqueInventory<UniqueInventoryItem> inventory;
|
||||
|
||||
UniqueInventoryItem anyInventoryItem;
|
||||
CatalogDummy anyProduct;
|
||||
Product anyProduct;
|
||||
ProductIdentifier anyPid;
|
||||
|
||||
@BeforeEach
|
||||
void populateAnyInventoryItem() {
|
||||
anyInventoryItem = inventory.findAll().stream().findAny().get();
|
||||
anyProduct = (CatalogDummy) anyInventoryItem.getProduct();
|
||||
anyProduct = anyInventoryItem.getProduct();
|
||||
anyPid = anyProduct.getId();
|
||||
}
|
||||
|
||||
|
@ -77,15 +79,15 @@ class InventoryControllerIntegrationTests {
|
|||
|
||||
@Test
|
||||
@WithMockUser(username = "admin", roles = "ADMIN")
|
||||
void adminCanAdd() throws Exception {
|
||||
mvc.perform(get("/inventory/add"))
|
||||
void adminCanAddConsumable() throws Exception {
|
||||
mvc.perform(get("/inventory/add?type=Consumable"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().string(containsString("Produkt anlegen")));
|
||||
|
||||
long itemCountBefore = inventory.findAll().stream().count();
|
||||
|
||||
mvc.perform(post("/inventory/add")
|
||||
.param("type", "CONSUMABLE")
|
||||
mvc.perform(post("/inventory/add?type=Consumable")
|
||||
.queryParam("type", Consumable.class.getSimpleName())
|
||||
.param("name", "MOCK Schnitzel Wiener Art (vegan)")
|
||||
.param("quantity", "100")
|
||||
.param("wholesalePrice", "3.00")
|
||||
|
@ -97,12 +99,11 @@ class InventoryControllerIntegrationTests {
|
|||
|
||||
assertThat(itemCountAfter).isEqualTo(itemCountBefore + 1);
|
||||
|
||||
// TODO: this must be changed once the catalog split is done
|
||||
assertThat(inventory.findAll().stream())
|
||||
.extracting("product.type", "product.name", "quantity", "product.wholesalePrice", "product.price",
|
||||
assertThat(inventory.findAll().filter(ie -> ie.getProduct() instanceof Consumable).stream())
|
||||
.extracting("product.name", "quantity", "product.wholesalePrice", "product.retailPrice",
|
||||
"product.promotionPrice")
|
||||
.contains(tuple(CatalogDummyType.CONSUMABLE, "MOCK Schnitzel Wiener Art (vegan)", Quantity.of(100),
|
||||
Money.of(3, EURO), Money.of(7.5, EURO), Money.of(6.66, EURO)));
|
||||
.contains(tuple("MOCK Schnitzel Wiener Art (vegan)", Quantity.of(100),
|
||||
Money.of(3, EURO), Money.of(7.5, EURO), Optional.of(Money.of(6.66, EURO))));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -117,54 +118,52 @@ class InventoryControllerIntegrationTests {
|
|||
|
||||
assertThat(itemCountAfter).isEqualTo(itemCountBefore - 1);
|
||||
|
||||
// TODO: this must be changed once the catalog split is done
|
||||
assertThat(inventory.findAll().stream())
|
||||
.extracting("product.type", "product.name", "quantity")
|
||||
.doesNotContain(tuple(anyProduct.getType(), anyProduct.getName(), anyInventoryItem.getQuantity()));
|
||||
.extracting("product.name", "quantity")
|
||||
.doesNotContain(tuple(anyProduct.getName(), anyInventoryItem.getQuantity()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(username = "admin", roles = "ADMIN")
|
||||
void adminCanEditConsumable() throws Exception {
|
||||
// TODO: this must be changed once the catalog split is done
|
||||
CatalogDummy anyConsumable = inventory.findAll().stream()
|
||||
Consumable anyConsumable = inventory.findAll().stream()
|
||||
.map(UniqueInventoryItem::getProduct)
|
||||
.map(p -> (CatalogDummy) p)
|
||||
.filter(cd -> cd.getType().equals(CatalogDummyType.CONSUMABLE))
|
||||
.filter(Consumable.class::isInstance)
|
||||
.map(Consumable.class::cast)
|
||||
.findAny()
|
||||
.get();
|
||||
|
||||
mvc.perform(get("/inventory/edit/" + anyConsumable.getId()))
|
||||
mvc.perform(
|
||||
get("/inventory/edit/" + anyConsumable.getId()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().string(containsString("Produkt bearbeiten")));
|
||||
|
||||
boolean hasPromotionPrice = anyConsumable.getPromotionPrice() != null;
|
||||
|
||||
mvc.perform(post("/inventory/edit/" + anyConsumable.getId())
|
||||
.queryParam("type", Consumable.class.getSimpleName())
|
||||
.param("type", "CONSUMABLE")
|
||||
.param("name", "MOCK edited")
|
||||
.param("quantity", "4711")
|
||||
.param("wholesalePrice", "0.01")
|
||||
.param("retailPrice", "0.01")
|
||||
.param("promotionPrice", hasPromotionPrice ? "" : "5"))
|
||||
.param("retailPrice", "0.03")
|
||||
.param("promotionPrice", "0.02"))
|
||||
.andExpect(redirectedUrl("/inventory"));
|
||||
|
||||
UniqueInventoryItem editedInventoryItem = inventory.findByProductIdentifier(anyConsumable.getId()).stream()
|
||||
.findAny().get();
|
||||
CatalogDummy editedProduct = (CatalogDummy) editedInventoryItem.getProduct();
|
||||
Consumable editedProduct = (Consumable) editedInventoryItem.getProduct();
|
||||
|
||||
assertThat(editedInventoryItem.getQuantity()).isEqualTo(Quantity.of(4711));
|
||||
assertThat(editedProduct)
|
||||
.extracting("type", "name", "wholesalePrice", "price", "promotionPrice")
|
||||
.containsExactly(CatalogDummyType.CONSUMABLE, "MOCK edited", Money.of(0.01, EURO),
|
||||
Money.of(0.01, EURO), hasPromotionPrice ? null : Money.of(0.01, EURO));
|
||||
.extracting("name", "wholesalePrice", "retailPrice", "promotionPrice")
|
||||
.containsExactly("MOCK edited", Money.of(0.01, EURO),
|
||||
Money.of(0.03, EURO), Optional.of(Money.of(0.02, EURO)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(username = "admin", roles = "ADMIN")
|
||||
void invalidAddReturnsNiceError() throws Exception {
|
||||
mvc.perform(post("/inventory/add")
|
||||
.param("type", "CONSUMABLE")
|
||||
.queryParam("type", Consumable.class.getSimpleName())
|
||||
.param("name", "")
|
||||
.param("quantity", "10")
|
||||
.param("wholesalePrice", "1.00")
|
||||
|
@ -176,7 +175,7 @@ class InventoryControllerIntegrationTests {
|
|||
@WithMockUser(username = "admin", roles = "ADMIN")
|
||||
void missingRetailPriceIsNoError() throws Exception {
|
||||
mvc.perform(post("/inventory/add")
|
||||
.param("type", "CONSUMABLE")
|
||||
.queryParam("type", Consumable.class.getSimpleName())
|
||||
.param("name", "MOCK")
|
||||
.param("quantity", "10")
|
||||
.param("wholesalePrice", "1.00")
|
||||
|
@ -188,7 +187,7 @@ class InventoryControllerIntegrationTests {
|
|||
@WithMockUser(username = "admin", roles = "ADMIN")
|
||||
void invalidEditReturnsNiceError() throws Exception {
|
||||
mvc.perform(post("/inventory/edit/" + anyPid)
|
||||
.param("type", "CONSUMABLE")
|
||||
.queryParam("type", Consumable.class.getSimpleName())
|
||||
.param("name", "")
|
||||
.param("quantity", "10")
|
||||
.param("wholesalePrice", "1.00")
|
||||
|
@ -220,22 +219,25 @@ class InventoryControllerIntegrationTests {
|
|||
|
||||
@Test
|
||||
void disallowUnauthorizedEditPage() throws Exception {
|
||||
assertRedirectsToLogin(mvc.perform(get("/inventory/edit/" + anyPid)));
|
||||
assertRedirectsToLogin(
|
||||
mvc.perform(get("/inventory/edit/" + anyPid).queryParam("type", Consumable.class.getSimpleName())));
|
||||
}
|
||||
|
||||
@Test
|
||||
void disallowUnauthorizedEdit() throws Exception {
|
||||
assertRedirectsToLogin(mvc.perform(post("/inventory/edit/" + anyPid)));
|
||||
assertRedirectsToLogin(
|
||||
mvc.perform(post("/inventory/edit/" + anyPid).queryParam("type", Consumable.class.getSimpleName())));
|
||||
}
|
||||
|
||||
@Test
|
||||
void disallowUnauthorizedAddPage() throws Exception {
|
||||
assertRedirectsToLogin(mvc.perform(get("/inventory/add")));
|
||||
assertRedirectsToLogin(mvc.perform(get("/inventory/add").queryParam("type", Consumable.class.getSimpleName())));
|
||||
}
|
||||
|
||||
@Test
|
||||
void disallowUnauthorizedAdd() throws Exception {
|
||||
assertRedirectsToLogin(mvc.perform(post("/inventory/add")));
|
||||
assertRedirectsToLogin(
|
||||
mvc.perform(post("/inventory/add").queryParam("type", Consumable.class.getSimpleName())));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
Loading…
Reference in a new issue