From 7365b384e3af1e95b776ac92234fe3c428472941 Mon Sep 17 00:00:00 2001 From: Simon Bruder Date: Sun, 7 Jan 2024 00:32:52 +0100 Subject: [PATCH] Make metric variable This was somehow overlooked, but it should work. This needs to change how the InventoryMutateForm handles quantities, as the amount has to be split from the metric for addition to work. --- src/main/asciidoc/models/design/catalog.puml | 6 ++--- .../catalog/CatalogDataInitializer.java | 20 ++++++++++++---- .../java/catering/catalog/Consumable.java | 5 ++-- src/main/java/catering/catalog/Rentable.java | 5 ++-- .../inventory/ConsumableMutateForm.java | 8 ++++--- .../inventory/InventoryController.java | 8 ++++--- .../inventory/InventoryInizializer.java | 5 ++-- .../inventory/InventoryMutateForm.java | 24 +++++++++++++++---- .../inventory/RentableMutateForm.java | 8 ++++--- .../java/catering/order/OrderController.java | 4 ++-- .../resources/templates/inventory-mutate.html | 18 +++++++++++--- .../catering/catalog/CatalogUnitTests.java | 11 +++++---- .../InventoryControllerIntegrationTests.java | 11 +++++---- 13 files changed, 93 insertions(+), 40 deletions(-) diff --git a/src/main/asciidoc/models/design/catalog.puml b/src/main/asciidoc/models/design/catalog.puml index caea3e8..a24affe 100644 --- a/src/main/asciidoc/models/design/catalog.puml +++ b/src/main/asciidoc/models/design/catalog.puml @@ -1,5 +1,5 @@ ' SPDX-License-Identifier: AGPL-3.0-or-later -' SPDX-FileCopyrightText: 2023 swt23w23 +' SPDX-FileCopyrightText: 2023-2024 swt23w23 @startuml skinparam linetype ortho skinparam groupInheritance 2 @@ -57,7 +57,7 @@ package catering.catalog { class Consumable { - promotionPrice : MonetaryAmount - wholesalePrice : MonetaryAmount - + Consumable(name : String, retailPrice : MonetaryAmount, wholesalePrice : MonetaryAmount, promotionPrice : Optional, Set categories) : Consumable + + Consumable(name : String, retailPrice : MonetaryAmount, wholesalePrice : MonetaryAmount, promotionPrice : Optional, Set categories, metric : Metric) : Consumable + getPrice() : MonetaryAmount + getRetailPrice() : MonetaryAmount + setRetailPrice(price : MonetaryAmount) : void @@ -74,7 +74,7 @@ package catering.catalog { class Rentable { - wholesalePrice : MonetaryAmount - + Rentable(name : String, pricePerHour : MonetaryAmount, wholesalePrice : MonetaryAmount, Set categories) : Rentable + + Rentable(name : String, pricePerHour : MonetaryAmount, wholesalePrice : MonetaryAmount, Set categories, metric : Metric) : Rentable + getWholesalePrice() : MonetaryAmount + setWholesalePrice(price : MonetaryAmount) } diff --git a/src/main/java/catering/catalog/CatalogDataInitializer.java b/src/main/java/catering/catalog/CatalogDataInitializer.java index 8f36b20..b60d698 100644 --- a/src/main/java/catering/catalog/CatalogDataInitializer.java +++ b/src/main/java/catering/catalog/CatalogDataInitializer.java @@ -1,5 +1,5 @@ // SPDX-License-Identifier: AGPL-3.0-or-later -// SPDX-FileCopyrightText: 2023 swt23w23 +// SPDX-FileCopyrightText: 2023-2024 swt23w23 package catering.catalog; import static org.salespointframework.core.Currencies.EURO; @@ -9,6 +9,7 @@ import java.util.Optional; import org.javamoney.moneta.Money; import org.salespointframework.core.DataInitializer; +import org.salespointframework.quantity.Metric; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import org.springframework.util.Assert; @@ -32,21 +33,32 @@ class CatalogDataInitializer implements DataInitializer { return; } + // !!! These need to be kept in sync with CatalogUnitTests 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))); + Set.of(OrderType.EVENT_CATERING, OrderType.MOBILE_BREAKFAST), + Metric.UNIT)); + cateringCatalog.save(new Consumable( + "Tafelwasser", + Money.of(5, EURO), + Money.of(0.01, EURO), + Optional.empty(), + Set.of(OrderType.PARTY_SERVICE, OrderType.EVENT_CATERING), + Metric.LITER)); cateringCatalog.save(new Rentable( "Kerze Rot", Money.of(2, EURO), Money.of(1.5, EURO), - Set.of(OrderType.EVENT_CATERING, OrderType.MOBILE_BREAKFAST))); + Set.of(OrderType.EVENT_CATERING, OrderType.MOBILE_BREAKFAST), + Metric.UNIT)); cateringCatalog.save(new Rentable( "Brotschneidemaschine Power X 3000", Money.of(25, EURO), Money.of(10000, EURO), - Set.of(OrderType.EVENT_CATERING, OrderType.MOBILE_BREAKFAST))); + Set.of(OrderType.EVENT_CATERING, OrderType.MOBILE_BREAKFAST), + Metric.UNIT)); } } diff --git a/src/main/java/catering/catalog/Consumable.java b/src/main/java/catering/catalog/Consumable.java index 15c3baa..3453154 100644 --- a/src/main/java/catering/catalog/Consumable.java +++ b/src/main/java/catering/catalog/Consumable.java @@ -9,6 +9,7 @@ import java.util.Set; import javax.money.MonetaryAmount; import org.salespointframework.catalog.Product; +import org.salespointframework.quantity.Metric; import org.springframework.lang.NonNull; import org.springframework.util.Assert; @@ -26,8 +27,8 @@ public class Consumable extends Product { } public Consumable(String name, MonetaryAmount price, MonetaryAmount wholesalePrice, - Optional promotionPrice, Set categories) { - super(name, price); + Optional promotionPrice, Set categories, Metric metric) { + super(name, price, metric); Assert.notNull(wholesalePrice, "wholesalePrice must not be null!"); Assert.notNull(promotionPrice, "promotionPrice must not be null!"); this.wholesalePrice = wholesalePrice; diff --git a/src/main/java/catering/catalog/Rentable.java b/src/main/java/catering/catalog/Rentable.java index 058b4aa..a80b45b 100644 --- a/src/main/java/catering/catalog/Rentable.java +++ b/src/main/java/catering/catalog/Rentable.java @@ -8,6 +8,7 @@ import java.util.Set; import javax.money.MonetaryAmount; import org.salespointframework.catalog.Product; +import org.salespointframework.quantity.Metric; import org.springframework.lang.NonNull; import org.springframework.util.Assert; @@ -25,8 +26,8 @@ public class Rentable extends Product { } public Rentable(String name, MonetaryAmount pricePerHour, MonetaryAmount wholesalePrice, - Set categories) { - super(name, pricePerHour); + Set categories, Metric metric) { + super(name, pricePerHour, metric); this.wholesalePrice = wholesalePrice; Assert.notNull(pricePerHour, "pricePerHour must not be null!"); Assert.notNull(wholesalePrice, "wholesalePrice must not be null!"); diff --git a/src/main/java/catering/inventory/ConsumableMutateForm.java b/src/main/java/catering/inventory/ConsumableMutateForm.java index df8e30c..c41893a 100644 --- a/src/main/java/catering/inventory/ConsumableMutateForm.java +++ b/src/main/java/catering/inventory/ConsumableMutateForm.java @@ -1,5 +1,5 @@ // SPDX-License-Identifier: AGPL-3.0-or-later -// SPDX-FileCopyrightText: 2023 swt23w23 +// SPDX-FileCopyrightText: 2023-2024 swt23w23 package catering.inventory; import static org.salespointframework.core.Currencies.EURO; @@ -44,13 +44,15 @@ class ConsumableMutateForm extends InventoryMutateForm { Money.of(getRetailPrice(), EURO), Money.of(getWholesalePrice(), EURO), getPromotionPrice().map(price -> Money.of(price, EURO)), - getOrderTypes()); + getOrderTypes(), + getMetric()); } public static ConsumableMutateForm of(Consumable product, UniqueInventoryItem item) { ConsumableMutateForm form = new ConsumableMutateForm(); form.setName(product.getName()); - form.setQuantity(item.getQuantity()); + form.setQuantity(item.getQuantity().getAmount().longValueExact()); + form.setMetric(product.createQuantity(0).getMetric()); // hack form.setOrderTypes(orderTypesFromCategories(product.getCategories())); form.setWholesalePrice(product.getWholesalePrice().getNumber().doubleValueExact()); form.setRetailPrice(product.getRetailPrice().getNumber().doubleValueExact()); diff --git a/src/main/java/catering/inventory/InventoryController.java b/src/main/java/catering/inventory/InventoryController.java index b3df29c..06bee3b 100644 --- a/src/main/java/catering/inventory/InventoryController.java +++ b/src/main/java/catering/inventory/InventoryController.java @@ -1,5 +1,5 @@ // SPDX-License-Identifier: AGPL-3.0-or-later -// SPDX-FileCopyrightText: 2023 swt23w23 +// SPDX-FileCopyrightText: 2023-2024 swt23w23 package catering.inventory; import org.salespointframework.catalog.Product; @@ -163,7 +163,7 @@ class InventoryController { 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()))); + inventory.save(item.increaseQuantity(product.createQuantity(form.getQuantity()).subtract(item.getQuantity()))); return "redirect:/inventory"; } @@ -246,7 +246,9 @@ class InventoryController { if (result.hasErrors()) { return add(model, form); } - inventory.save(new UniqueInventoryItem(cateringCatalog.save(form.toProduct()), form.getQuantity())); + Product product = form.toProduct(); + inventory.save( + new UniqueInventoryItem(cateringCatalog.save(product), product.createQuantity(form.getQuantity()))); return "redirect:/inventory"; } diff --git a/src/main/java/catering/inventory/InventoryInizializer.java b/src/main/java/catering/inventory/InventoryInizializer.java index 00483b3..0fbaf82 100644 --- a/src/main/java/catering/inventory/InventoryInizializer.java +++ b/src/main/java/catering/inventory/InventoryInizializer.java @@ -1,11 +1,10 @@ // SPDX-License-Identifier: AGPL-3.0-or-later -// SPDX-FileCopyrightText: 2023 swt23w23 +// SPDX-FileCopyrightText: 2023-2024 swt23w23 package catering.inventory; import org.salespointframework.core.DataInitializer; import org.salespointframework.inventory.UniqueInventory; import org.salespointframework.inventory.UniqueInventoryItem; -import org.salespointframework.quantity.Quantity; import org.springframework.stereotype.Component; import org.springframework.util.Assert; @@ -28,7 +27,7 @@ class InventoryInitializer implements DataInitializer { public void initialize() { cateringCatalog.findAll().forEach(product -> { if (inventory.findByProduct(product).isEmpty()) { - inventory.save(new UniqueInventoryItem(product, Quantity.of(10))); + inventory.save(new UniqueInventoryItem(product, product.createQuantity(10))); } }); } diff --git a/src/main/java/catering/inventory/InventoryMutateForm.java b/src/main/java/catering/inventory/InventoryMutateForm.java index 9afce43..5d6d724 100644 --- a/src/main/java/catering/inventory/InventoryMutateForm.java +++ b/src/main/java/catering/inventory/InventoryMutateForm.java @@ -1,9 +1,11 @@ // SPDX-License-Identifier: AGPL-3.0-or-later -// SPDX-FileCopyrightText: 2023 swt23w23 +// SPDX-FileCopyrightText: 2023-2024 swt23w23 package catering.inventory; import static org.salespointframework.core.Currencies.EURO; +import java.util.Collection; +import java.util.List; import java.util.Set; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -12,6 +14,7 @@ import java.util.stream.Stream; import org.javamoney.moneta.Money; import org.salespointframework.catalog.Product; import org.salespointframework.inventory.UniqueInventoryItem; +import org.salespointframework.quantity.Metric; import org.salespointframework.quantity.Quantity; import org.springframework.data.util.Streamable; @@ -32,9 +35,10 @@ import jakarta.validation.constraints.PositiveOrZero; // NonNegative in enterpri */ abstract class InventoryMutateForm { private @NotEmpty String name; - private @NotNull Quantity quantity; + private @NotNull @PositiveOrZero Long quantity; private @NotNull @PositiveOrZero Double retailPrice; private @NotNull Set orderTypes; + private @NotNull Metric metric = Metric.UNIT; public InventoryMutateForm() { } @@ -43,7 +47,7 @@ abstract class InventoryMutateForm { return name; } - public Quantity getQuantity() { + public Long getQuantity() { return quantity; } @@ -51,6 +55,10 @@ abstract class InventoryMutateForm { return retailPrice; } + public Metric getMetric() { + return metric; + } + public void setName(String name) { this.name = name; } @@ -59,7 +67,7 @@ abstract class InventoryMutateForm { return orderTypes; } - public void setQuantity(Quantity quantity) { + public void setQuantity(Long quantity) { this.quantity = quantity; } @@ -71,6 +79,10 @@ abstract class InventoryMutateForm { this.orderTypes = orderTypes; } + public void setMetric(Metric metric) { + this.metric = metric; + } + /** * Creates an empty {@link InventoryMutateForm} for {@link Product}s of the given {@link Class}. * @@ -158,4 +170,8 @@ abstract class InventoryMutateForm { .collect(Collectors.toSet())::contains) .map(OrderType::valueOf).collect(Collectors.toSet()); } + + public Collection supportedMetrics() { + return List.of(Metric.UNIT, Metric.LITER, Metric.KILOGRAM); + } } diff --git a/src/main/java/catering/inventory/RentableMutateForm.java b/src/main/java/catering/inventory/RentableMutateForm.java index 41d5145..43884fc 100644 --- a/src/main/java/catering/inventory/RentableMutateForm.java +++ b/src/main/java/catering/inventory/RentableMutateForm.java @@ -1,5 +1,5 @@ // SPDX-License-Identifier: AGPL-3.0-or-later -// SPDX-FileCopyrightText: 2023 swt23w23 +// SPDX-FileCopyrightText: 2023-2024 swt23w23 package catering.inventory; import static org.salespointframework.core.Currencies.EURO; @@ -26,7 +26,8 @@ class RentableMutateForm extends InventoryMutateForm { public static RentableMutateForm of(Rentable product, UniqueInventoryItem item) { RentableMutateForm form = new RentableMutateForm(); form.setName(product.getName()); - form.setQuantity(item.getQuantity()); + form.setQuantity(item.getQuantity().getAmount().longValueExact()); + form.setMetric(product.createQuantity(0).getMetric()); // hack form.setOrderTypes(orderTypesFromCategories(product.getCategories())); form.setWholesalePrice(product.getWholesalePrice().getNumber().doubleValueExact()); form.setRetailPrice(product.getRetailPrice().getNumber().doubleValueExact()); @@ -39,7 +40,8 @@ class RentableMutateForm extends InventoryMutateForm { getName(), Money.of(getRetailPrice(), EURO), Money.of(getWholesalePrice(), EURO), - getOrderTypes()); + getOrderTypes(), + getMetric()); } @Override diff --git a/src/main/java/catering/order/OrderController.java b/src/main/java/catering/order/OrderController.java index b854d93..ea27407 100644 --- a/src/main/java/catering/order/OrderController.java +++ b/src/main/java/catering/order/OrderController.java @@ -1,5 +1,5 @@ // SPDX-License-Identifier: AGPL-3.0-or-later -// SPDX-FileCopyrightText: 2023 swt23w23 +// SPDX-FileCopyrightText: 2023-2024 swt23w23 package catering.order; import catering.catalog.CateringCatalog; @@ -178,7 +178,7 @@ public class OrderController { return "redirect:/event"; } - Quantity amount = Quantity.of(number > 0 ? number : 1); + Quantity amount = product.createQuantity(number > 0 ? number : 1); Quantity cartQuantity = cart.getQuantity(product); Quantity available; diff --git a/src/main/resources/templates/inventory-mutate.html b/src/main/resources/templates/inventory-mutate.html index 21a32d5..bcd1fbe 100644 --- a/src/main/resources/templates/inventory-mutate.html +++ b/src/main/resources/templates/inventory-mutate.html @@ -1,6 +1,6 @@ SPDX-License-Identifier: AGPL-3.0-or-later -SPDX-FileCopyrightText: 2023 swt23w23 +SPDX-FileCopyrightText: 2023-2024 swt23w23
- -
Ungültige Menge.
+
+ + +
Ungültige Menge.
+
+
+ + +
Ungültige Einheit.
+
+ +
diff --git a/src/test/java/catering/catalog/CatalogUnitTests.java b/src/test/java/catering/catalog/CatalogUnitTests.java index 8df9a14..1d942b6 100644 --- a/src/test/java/catering/catalog/CatalogUnitTests.java +++ b/src/test/java/catering/catalog/CatalogUnitTests.java @@ -9,6 +9,7 @@ import static org.assertj.core.api.Assertions.*; import static org.salespointframework.core.Currencies.EURO; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.salespointframework.quantity.Metric; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.DirtiesContext; @@ -23,20 +24,21 @@ class CatalogUnitTests { @Test void findByCategories() { + // !!! These need to be kept in sync with CatalogDataIntializer assertThat(cateringCatalog.findRentablesByCategoriesContains(OrderType.EVENT_CATERING.toString())).hasSize(2); assertThat(cateringCatalog.findRentablesByCategoriesContains(OrderType.MOBILE_BREAKFAST.toString())).hasSize(2); assertThat(cateringCatalog.findRentablesByCategoriesContains(OrderType.RENT_A_COOK.toString())).hasSize(0); assertThat(cateringCatalog.findRentablesByCategoriesContains(OrderType.PARTY_SERVICE.toString())).hasSize(0); - assertThat(cateringCatalog.findConsumablesByCategoriesContains(OrderType.EVENT_CATERING.toString())).hasSize(1); + assertThat(cateringCatalog.findConsumablesByCategoriesContains(OrderType.EVENT_CATERING.toString())).hasSize(2); assertThat(cateringCatalog.findConsumablesByCategoriesContains(OrderType.MOBILE_BREAKFAST.toString())).hasSize(1); assertThat(cateringCatalog.findConsumablesByCategoriesContains(OrderType.RENT_A_COOK.toString())).hasSize(0); - assertThat(cateringCatalog.findConsumablesByCategoriesContains(OrderType.PARTY_SERVICE.toString())).hasSize(0); + assertThat(cateringCatalog.findConsumablesByCategoriesContains(OrderType.PARTY_SERVICE.toString())).hasSize(1); } @Test void findConsumables() { - assertThat(cateringCatalog.findConsumables()).hasSize(1); + assertThat(cateringCatalog.findConsumables()).hasSize(2); } @Test @@ -56,7 +58,8 @@ class CatalogUnitTests { Money.of(1.5, EURO), Money.of(0.7, EURO), Optional.of(Money.of(0.90, EURO)), - Set.of(OrderType.MOBILE_BREAKFAST))); + Set.of(OrderType.MOBILE_BREAKFAST), + Metric.UNIT)); assertThat(cateringCatalog.findAll().stream().count()).isEqualTo(1 + countAllBefore); assertThat(cateringCatalog.findConsumables().stream().count()).isEqualTo(1 + countConsumablesBefore); assertThat(cateringCatalog.findConsumables()).contains(addedConsumable); diff --git a/src/test/java/catering/inventory/InventoryControllerIntegrationTests.java b/src/test/java/catering/inventory/InventoryControllerIntegrationTests.java index 11dc811..5badb9c 100644 --- a/src/test/java/catering/inventory/InventoryControllerIntegrationTests.java +++ b/src/test/java/catering/inventory/InventoryControllerIntegrationTests.java @@ -1,5 +1,5 @@ // SPDX-License-Identifier: AGPL-3.0-or-later -// SPDX-FileCopyrightText: 2023 swt23w23 +// SPDX-FileCopyrightText: 2023-2024 swt23w23 package catering.inventory; import static catering.inventory.InventoryControllerIntegrationTests.PermissionResult.FORBIDDEN; @@ -31,6 +31,7 @@ import org.salespointframework.catalog.Product; import org.salespointframework.catalog.Product.ProductIdentifier; import org.salespointframework.inventory.UniqueInventory; import org.salespointframework.inventory.UniqueInventoryItem; +import org.salespointframework.quantity.Metric; import org.salespointframework.quantity.Quantity; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; @@ -82,11 +83,12 @@ class InventoryControllerIntegrationTests { anyPid = anyProduct.getId(); anyConsumableItem = inventory.save(new UniqueInventoryItem( catalog.save(new Consumable("Any Consumable", Money.of(1, EURO), Money.of(0.5, EURO), - Optional.of(Money.of(0.75, EURO)), Set.of(OrderType.EVENT_CATERING, OrderType.PARTY_SERVICE))), + Optional.of(Money.of(0.75, EURO)), Set.of(OrderType.EVENT_CATERING, OrderType.PARTY_SERVICE), + Metric.UNIT)), Quantity.of(1))); anyRentableItem = inventory.save(new UniqueInventoryItem( catalog.save(new Rentable("Any Rentable", Money.of(1, EURO), Money.of(0.5, EURO), - Set.of(OrderType.EVENT_CATERING, OrderType.PARTY_SERVICE))), + Set.of(OrderType.EVENT_CATERING, OrderType.PARTY_SERVICE), Metric.UNIT)), Quantity.of(1))); } @@ -131,7 +133,8 @@ class InventoryControllerIntegrationTests { .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))); + Set.of(OrderType.MOBILE_BREAKFAST, OrderType.PARTY_SERVICE), Metric.UNIT), + Quantity.of(100))); } @Test