mirror of
https://github.com/st-tu-dresden-praktikum/swt23w23
synced 2024-07-19 21:04:36 +02:00
parent
0200330625
commit
3e2cc3d0b2
|
@ -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(
|
||||
|
|
|
@ -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<OrderType> orderTypes;
|
||||
|
||||
public InventoryMutateForm() {
|
||||
}
|
||||
|
@ -61,6 +69,10 @@ abstract class InventoryMutateForm {
|
|||
this.name = name;
|
||||
}
|
||||
|
||||
public Set<OrderType> 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<OrderType> 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<OrderType> orderTypesFromCategories(Streamable<String> categories) {
|
||||
return categories.stream()
|
||||
.filter(Stream.of(OrderType.class.getEnumConstants())
|
||||
.map(OrderType::toString)
|
||||
.collect(Collectors.toSet())::contains)
|
||||
.map(OrderType::valueOf).collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -37,6 +37,14 @@
|
|||
<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>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="orderTypes">Buchungstypen</label>
|
||||
<select class="form-select" multiple th:field="*{orderTypes}" th:errorclass="is-invalid">
|
||||
<option th:each="orderType : ${T(catering.order.OrderType).values()}" th:value="${orderType}" th:text="${orderType.toHumanReadable()}"/>
|
||||
</select>
|
||||
<div th:if="${#fields.hasErrors('orderTypes')}" class="invalid-feedback">Ungültiger Buchungstyp.</div>
|
||||
<div class="form-text">Mehrfachauswahl ist mit Umschalt- bzw. Steuerungstaste möglich.</div>
|
||||
</div>
|
||||
<button class="btn btn-primary" type="submit" th:text="${actionIsAdd ? 'Hinzufügen' : 'Bearbeiten'}"></button>
|
||||
<!-- KANN: Bild und Beschreibungstext -->
|
||||
</form>
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
<th>Menge</th>
|
||||
<th>Einkaufspreis</th>
|
||||
<th>UVP</th>
|
||||
<th>Buchungstypen</th>
|
||||
<th>Gesamtwert</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
|
@ -23,6 +24,11 @@
|
|||
<td th:text="${item.product.wholesalePrice}"></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 style="width: 20%;">
|
||||
<div class="d-inline-flex flex-wrap gap-1 flex-column flex-xxl-row">
|
||||
<span class="badge bg-secondary" th:each="orderType : ${T(catering.inventory.InventoryMutateForm).orderTypesFromCategories(item.product.categories)}" th:text="${orderType.toHumanReadable()}"/>
|
||||
</div>
|
||||
</td>
|
||||
<td th:text="${item.product.wholesalePrice.multiply(item.quantity.getAmount())}"></td>
|
||||
<td>
|
||||
<div class="d-inline-flex flex-wrap gap-1 flex-column flex-xxl-row">
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue