From 3e2cc3d0b27899905b1d5a2dcebcd699d814e973 Mon Sep 17 00:00:00 2001 From: Simon Bruder Date: Thu, 7 Dec 2023 18:35:52 +0100 Subject: [PATCH] Allow specifying order types in inventory Fixes #95 --- .../inventory/ConsumableMutateForm.java | 6 +-- .../inventory/InventoryMutateForm.java | 43 +++++++++++++++++++ .../inventory/RentableMutateForm.java | 7 +-- .../resources/templates/inventory-mutate.html | 8 ++++ src/main/resources/templates/inventory.html | 6 +++ .../InventoryControllerIntegrationTests.java | 21 ++++++--- 6 files changed, 77 insertions(+), 14 deletions(-) diff --git a/src/main/java/catering/inventory/ConsumableMutateForm.java b/src/main/java/catering/inventory/ConsumableMutateForm.java index 561cde4..e0caa10 100644 --- a/src/main/java/catering/inventory/ConsumableMutateForm.java +++ b/src/main/java/catering/inventory/ConsumableMutateForm.java @@ -19,7 +19,6 @@ 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; @@ -29,7 +28,6 @@ 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 @@ -60,14 +58,14 @@ class ConsumableMutateForm extends InventoryMutateForm { 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)); + getOrderTypes()); } public static ConsumableMutateForm of(Consumable product, UniqueInventoryItem item) { ConsumableMutateForm form = new ConsumableMutateForm(); form.setName(product.getName()); form.setQuantity(item.getQuantity()); + form.setOrderTypes(orderTypesFromCategories(product.getCategories())); form.setWholesalePrice(product.getWholesalePrice().getNumber().doubleValueExact()); form.setRetailPrice(product.getRetailPrice().getNumber().doubleValueExact()); form.setPromotionPrice( diff --git a/src/main/java/catering/inventory/InventoryMutateForm.java b/src/main/java/catering/inventory/InventoryMutateForm.java index 8109620..f7e31f9 100644 --- a/src/main/java/catering/inventory/InventoryMutateForm.java +++ b/src/main/java/catering/inventory/InventoryMutateForm.java @@ -18,13 +18,20 @@ package catering.inventory; import static org.salespointframework.core.Currencies.EURO; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + import org.javamoney.moneta.Money; import org.salespointframework.catalog.Product; import org.salespointframework.inventory.UniqueInventoryItem; import org.salespointframework.quantity.Quantity; +import org.springframework.data.util.Streamable; import catering.catalog.Consumable; import catering.catalog.Rentable; +import catering.order.OrderType; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.PositiveOrZero; // NonNegative in enterprise java @@ -41,6 +48,7 @@ abstract class InventoryMutateForm { private @NotEmpty String name; private @NotNull Quantity quantity; private @NotNull @PositiveOrZero Double retailPrice; + private @NotNull Set orderTypes; public InventoryMutateForm() { } @@ -61,6 +69,10 @@ abstract class InventoryMutateForm { this.name = name; } + public Set getOrderTypes() { + return orderTypes; + } + public void setQuantity(Quantity quantity) { this.quantity = quantity; } @@ -69,6 +81,10 @@ abstract class InventoryMutateForm { this.retailPrice = retailPrice; } + public void setOrderTypes(Set orderTypes) { + this.orderTypes = orderTypes; + } + /** * Creates an empty {@link InventoryMutateForm} for {@link Product}s of the given {@link Class}. * @@ -125,8 +141,35 @@ abstract class InventoryMutateForm { public void modifyProduct(Product product) { product.setName(getName()); product.setPrice(Money.of(getRetailPrice(), EURO)); + + // first, remove all categories that are *not* selected + Stream.of(OrderType.class.getEnumConstants()) + .filter(Predicate.not(getOrderTypes()::contains)) + .map(OrderType::toString) + .forEach(product::removeCategory); + + // then, add all categories that *are* selected + getOrderTypes().stream() + .map(OrderType::toString) + .forEach(product::addCategory); + modifyProductPrimitive(product); } protected abstract void modifyProductPrimitive(Product product); + + /** + * Returns a {@link Set} of {@link OrderType}s from the categories of a + * {@link Product} + * + * @param categories a {@link Streamable} of category {@link String}s + * @return a {@link Set} of all {@link OrderType}s attached to the product + */ + public static Set orderTypesFromCategories(Streamable categories) { + return categories.stream() + .filter(Stream.of(OrderType.class.getEnumConstants()) + .map(OrderType::toString) + .collect(Collectors.toSet())::contains) + .map(OrderType::valueOf).collect(Collectors.toSet()); + } } diff --git a/src/main/java/catering/inventory/RentableMutateForm.java b/src/main/java/catering/inventory/RentableMutateForm.java index 8e4ef15..90a0a7d 100644 --- a/src/main/java/catering/inventory/RentableMutateForm.java +++ b/src/main/java/catering/inventory/RentableMutateForm.java @@ -18,14 +18,11 @@ 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; @@ -44,6 +41,7 @@ class RentableMutateForm extends InventoryMutateForm { RentableMutateForm form = new RentableMutateForm(); form.setName(product.getName()); form.setQuantity(item.getQuantity()); + form.setOrderTypes(orderTypesFromCategories(product.getCategories())); form.setWholesalePrice(product.getWholesalePrice().getNumber().doubleValueExact()); form.setRetailPrice(product.getRetailPrice().getNumber().doubleValueExact()); return form; @@ -55,8 +53,7 @@ class RentableMutateForm extends InventoryMutateForm { 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)); + getOrderTypes()); } @Override diff --git a/src/main/resources/templates/inventory-mutate.html b/src/main/resources/templates/inventory-mutate.html index 91c7035..69c2494 100644 --- a/src/main/resources/templates/inventory-mutate.html +++ b/src/main/resources/templates/inventory-mutate.html @@ -37,6 +37,14 @@
Ungültiger Aktionspreis.
+
+ + +
Ungültiger Buchungstyp.
+
Mehrfachauswahl ist mit Umschalt- bzw. Steuerungstaste möglich.
+
diff --git a/src/main/resources/templates/inventory.html b/src/main/resources/templates/inventory.html index 322aad8..c98bb96 100644 --- a/src/main/resources/templates/inventory.html +++ b/src/main/resources/templates/inventory.html @@ -12,6 +12,7 @@ Menge Einkaufspreis UVP + Buchungstypen Gesamtwert @@ -23,6 +24,11 @@ + +
+ +
+
diff --git a/src/test/java/catering/inventory/InventoryControllerIntegrationTests.java b/src/test/java/catering/inventory/InventoryControllerIntegrationTests.java index 355a64f..68493d8 100644 --- a/src/test/java/catering/inventory/InventoryControllerIntegrationTests.java +++ b/src/test/java/catering/inventory/InventoryControllerIntegrationTests.java @@ -30,6 +30,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import java.util.Optional; +import java.util.Set; import org.javamoney.moneta.Money; import org.junit.jupiter.api.BeforeEach; @@ -48,6 +49,7 @@ import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultActions; import catering.catalog.Consumable; +import catering.order.OrderType; @AutoConfigureMockMvc @SpringBootTest @@ -90,6 +92,7 @@ class InventoryControllerIntegrationTests { .queryParam("type", Consumable.class.getSimpleName()) .param("name", "MOCK Schnitzel Wiener Art (vegan)") .param("quantity", "100") + .param("orderTypes", "MOBILE_BREAKFAST", "PARTY_SERVICE") .param("wholesalePrice", "3.00") .param("retailPrice", "7.50") .param("promotionPrice", "6.66")) @@ -99,11 +102,17 @@ class InventoryControllerIntegrationTests { assertThat(itemCountAfter).isEqualTo(itemCountBefore + 1); - assertThat(inventory.findAll().filter(ie -> ie.getProduct() instanceof Consumable).stream()) - .extracting("product.name", "quantity", "product.wholesalePrice", "product.retailPrice", - "product.promotionPrice") - .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)))); + // extracting is not possible here, as the category sets are not equal + assertThat(inventory.findAll().stream() + .filter(ie -> ie.getProduct().getName().equals("MOCK Schnitzel Wiener Art (vegan)")).findAny()) + .get() + .usingRecursiveComparison() + .ignoringFields("inventoryItemIdentifier.inventoryItemId", "isNew", "product.id.productId", + "product.isNew") + .isEqualTo( + new UniqueInventoryItem(new Consumable("MOCK Schnitzel Wiener Art (vegan)", Money.of(7.5, EURO), + Money.of(3, EURO), Optional.of(Money.of(6.66, EURO)), + Set.of(OrderType.MOBILE_BREAKFAST, OrderType.PARTY_SERVICE)), Quantity.of(100))); } @Test @@ -142,6 +151,7 @@ class InventoryControllerIntegrationTests { .queryParam("type", Consumable.class.getSimpleName()) .param("type", "CONSUMABLE") .param("name", "MOCK edited") + .param("orderTypes", "PARTY_SERVICE", "EVENT_CATERING") .param("quantity", "4711") .param("wholesalePrice", "0.01") .param("retailPrice", "0.03") @@ -157,6 +167,7 @@ class InventoryControllerIntegrationTests { .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))); + assertThat(editedProduct.getCategories()).containsExactlyInAnyOrder("PARTY_SERVICE", "EVENT_CATERING"); } @Test