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 static org.salespointframework.core.Currencies.EURO;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import javax.money.MonetaryAmount;
|
import javax.money.MonetaryAmount;
|
||||||
import javax.money.NumberValue;
|
import javax.money.NumberValue;
|
||||||
|
@ -29,7 +28,6 @@ import org.salespointframework.catalog.Product;
|
||||||
import org.salespointframework.inventory.UniqueInventoryItem;
|
import org.salespointframework.inventory.UniqueInventoryItem;
|
||||||
|
|
||||||
import catering.catalog.Consumable;
|
import catering.catalog.Consumable;
|
||||||
import catering.order.OrderType;
|
|
||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
import jakarta.validation.constraints.PositiveOrZero; // NonNegative in enterprise java
|
import jakarta.validation.constraints.PositiveOrZero; // NonNegative in enterprise java
|
||||||
|
|
||||||
|
@ -60,14 +58,14 @@ class ConsumableMutateForm extends InventoryMutateForm {
|
||||||
Money.of(getRetailPrice(), EURO),
|
Money.of(getRetailPrice(), EURO),
|
||||||
Money.of(getWholesalePrice(), EURO),
|
Money.of(getWholesalePrice(), EURO),
|
||||||
getPromotionPrice().map(price -> Money.of(price, EURO)),
|
getPromotionPrice().map(price -> Money.of(price, EURO)),
|
||||||
// FIXME: categories for creation of consumables are hardcoded
|
getOrderTypes());
|
||||||
Set.of(OrderType.EVENT_CATERING, OrderType.MOBILE_BREAKFAST));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ConsumableMutateForm of(Consumable product, UniqueInventoryItem item) {
|
public static ConsumableMutateForm of(Consumable product, UniqueInventoryItem item) {
|
||||||
ConsumableMutateForm form = new ConsumableMutateForm();
|
ConsumableMutateForm form = new ConsumableMutateForm();
|
||||||
form.setName(product.getName());
|
form.setName(product.getName());
|
||||||
form.setQuantity(item.getQuantity());
|
form.setQuantity(item.getQuantity());
|
||||||
|
form.setOrderTypes(orderTypesFromCategories(product.getCategories()));
|
||||||
form.setWholesalePrice(product.getWholesalePrice().getNumber().doubleValueExact());
|
form.setWholesalePrice(product.getWholesalePrice().getNumber().doubleValueExact());
|
||||||
form.setRetailPrice(product.getRetailPrice().getNumber().doubleValueExact());
|
form.setRetailPrice(product.getRetailPrice().getNumber().doubleValueExact());
|
||||||
form.setPromotionPrice(
|
form.setPromotionPrice(
|
||||||
|
|
|
@ -18,13 +18,20 @@ package catering.inventory;
|
||||||
|
|
||||||
import static org.salespointframework.core.Currencies.EURO;
|
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.javamoney.moneta.Money;
|
||||||
import org.salespointframework.catalog.Product;
|
import org.salespointframework.catalog.Product;
|
||||||
import org.salespointframework.inventory.UniqueInventoryItem;
|
import org.salespointframework.inventory.UniqueInventoryItem;
|
||||||
import org.salespointframework.quantity.Quantity;
|
import org.salespointframework.quantity.Quantity;
|
||||||
|
import org.springframework.data.util.Streamable;
|
||||||
|
|
||||||
import catering.catalog.Consumable;
|
import catering.catalog.Consumable;
|
||||||
import catering.catalog.Rentable;
|
import catering.catalog.Rentable;
|
||||||
|
import catering.order.OrderType;
|
||||||
import jakarta.validation.constraints.NotEmpty;
|
import jakarta.validation.constraints.NotEmpty;
|
||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
import jakarta.validation.constraints.PositiveOrZero; // NonNegative in enterprise java
|
import jakarta.validation.constraints.PositiveOrZero; // NonNegative in enterprise java
|
||||||
|
@ -41,6 +48,7 @@ abstract class InventoryMutateForm {
|
||||||
private @NotEmpty String name;
|
private @NotEmpty String name;
|
||||||
private @NotNull Quantity quantity;
|
private @NotNull Quantity quantity;
|
||||||
private @NotNull @PositiveOrZero Double retailPrice;
|
private @NotNull @PositiveOrZero Double retailPrice;
|
||||||
|
private @NotNull Set<OrderType> orderTypes;
|
||||||
|
|
||||||
public InventoryMutateForm() {
|
public InventoryMutateForm() {
|
||||||
}
|
}
|
||||||
|
@ -61,6 +69,10 @@ abstract class InventoryMutateForm {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Set<OrderType> getOrderTypes() {
|
||||||
|
return orderTypes;
|
||||||
|
}
|
||||||
|
|
||||||
public void setQuantity(Quantity quantity) {
|
public void setQuantity(Quantity quantity) {
|
||||||
this.quantity = quantity;
|
this.quantity = quantity;
|
||||||
}
|
}
|
||||||
|
@ -69,6 +81,10 @@ abstract class InventoryMutateForm {
|
||||||
this.retailPrice = retailPrice;
|
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}.
|
* 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) {
|
public void modifyProduct(Product product) {
|
||||||
product.setName(getName());
|
product.setName(getName());
|
||||||
product.setPrice(Money.of(getRetailPrice(), EURO));
|
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);
|
modifyProductPrimitive(product);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void modifyProductPrimitive(Product 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 static org.salespointframework.core.Currencies.EURO;
|
||||||
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.javamoney.moneta.Money;
|
import org.javamoney.moneta.Money;
|
||||||
import org.salespointframework.catalog.Product;
|
import org.salespointframework.catalog.Product;
|
||||||
import org.salespointframework.inventory.UniqueInventoryItem;
|
import org.salespointframework.inventory.UniqueInventoryItem;
|
||||||
|
|
||||||
import catering.catalog.Rentable;
|
import catering.catalog.Rentable;
|
||||||
import catering.order.OrderType;
|
|
||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
import jakarta.validation.constraints.PositiveOrZero;
|
import jakarta.validation.constraints.PositiveOrZero;
|
||||||
|
|
||||||
|
@ -44,6 +41,7 @@ class RentableMutateForm extends InventoryMutateForm {
|
||||||
RentableMutateForm form = new RentableMutateForm();
|
RentableMutateForm form = new RentableMutateForm();
|
||||||
form.setName(product.getName());
|
form.setName(product.getName());
|
||||||
form.setQuantity(item.getQuantity());
|
form.setQuantity(item.getQuantity());
|
||||||
|
form.setOrderTypes(orderTypesFromCategories(product.getCategories()));
|
||||||
form.setWholesalePrice(product.getWholesalePrice().getNumber().doubleValueExact());
|
form.setWholesalePrice(product.getWholesalePrice().getNumber().doubleValueExact());
|
||||||
form.setRetailPrice(product.getRetailPrice().getNumber().doubleValueExact());
|
form.setRetailPrice(product.getRetailPrice().getNumber().doubleValueExact());
|
||||||
return form;
|
return form;
|
||||||
|
@ -55,8 +53,7 @@ class RentableMutateForm extends InventoryMutateForm {
|
||||||
getName(),
|
getName(),
|
||||||
Money.of(getRetailPrice(), EURO),
|
Money.of(getRetailPrice(), EURO),
|
||||||
Money.of(getWholesalePrice(), EURO),
|
Money.of(getWholesalePrice(), EURO),
|
||||||
// FIXME: categories for creation of rentables are hardcoded
|
getOrderTypes());
|
||||||
Set.of(OrderType.EVENT_CATERING, OrderType.MOBILE_BREAKFAST));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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"/>
|
<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 th:if="${#fields.hasErrors('promotionPrice')}" class="invalid-feedback">Ungültiger Aktionspreis.</div>
|
||||||
</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>
|
<button class="btn btn-primary" type="submit" th:text="${actionIsAdd ? 'Hinzufügen' : 'Bearbeiten'}"></button>
|
||||||
<!-- KANN: Bild und Beschreibungstext -->
|
<!-- KANN: Bild und Beschreibungstext -->
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
<th>Menge</th>
|
<th>Menge</th>
|
||||||
<th>Einkaufspreis</th>
|
<th>Einkaufspreis</th>
|
||||||
<th>UVP</th>
|
<th>UVP</th>
|
||||||
|
<th>Buchungstypen</th>
|
||||||
<th>Gesamtwert</th>
|
<th>Gesamtwert</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -23,6 +24,11 @@
|
||||||
<td th:text="${item.product.wholesalePrice}"></td>
|
<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.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: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 th:text="${item.product.wholesalePrice.multiply(item.quantity.getAmount())}"></td>
|
||||||
<td>
|
<td>
|
||||||
<div class="d-inline-flex flex-wrap gap-1 flex-column flex-xxl-row">
|
<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 static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.javamoney.moneta.Money;
|
import org.javamoney.moneta.Money;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
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 org.springframework.test.web.servlet.ResultActions;
|
||||||
|
|
||||||
import catering.catalog.Consumable;
|
import catering.catalog.Consumable;
|
||||||
|
import catering.order.OrderType;
|
||||||
|
|
||||||
@AutoConfigureMockMvc
|
@AutoConfigureMockMvc
|
||||||
@SpringBootTest
|
@SpringBootTest
|
||||||
|
@ -90,6 +92,7 @@ class InventoryControllerIntegrationTests {
|
||||||
.queryParam("type", Consumable.class.getSimpleName())
|
.queryParam("type", Consumable.class.getSimpleName())
|
||||||
.param("name", "MOCK Schnitzel Wiener Art (vegan)")
|
.param("name", "MOCK Schnitzel Wiener Art (vegan)")
|
||||||
.param("quantity", "100")
|
.param("quantity", "100")
|
||||||
|
.param("orderTypes", "MOBILE_BREAKFAST", "PARTY_SERVICE")
|
||||||
.param("wholesalePrice", "3.00")
|
.param("wholesalePrice", "3.00")
|
||||||
.param("retailPrice", "7.50")
|
.param("retailPrice", "7.50")
|
||||||
.param("promotionPrice", "6.66"))
|
.param("promotionPrice", "6.66"))
|
||||||
|
@ -99,11 +102,17 @@ class InventoryControllerIntegrationTests {
|
||||||
|
|
||||||
assertThat(itemCountAfter).isEqualTo(itemCountBefore + 1);
|
assertThat(itemCountAfter).isEqualTo(itemCountBefore + 1);
|
||||||
|
|
||||||
assertThat(inventory.findAll().filter(ie -> ie.getProduct() instanceof Consumable).stream())
|
// extracting is not possible here, as the category sets are not equal
|
||||||
.extracting("product.name", "quantity", "product.wholesalePrice", "product.retailPrice",
|
assertThat(inventory.findAll().stream()
|
||||||
"product.promotionPrice")
|
.filter(ie -> ie.getProduct().getName().equals("MOCK Schnitzel Wiener Art (vegan)")).findAny())
|
||||||
.contains(tuple("MOCK Schnitzel Wiener Art (vegan)", Quantity.of(100),
|
.get()
|
||||||
Money.of(3, EURO), Money.of(7.5, EURO), Optional.of(Money.of(6.66, EURO))));
|
.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
|
@Test
|
||||||
|
@ -142,6 +151,7 @@ class InventoryControllerIntegrationTests {
|
||||||
.queryParam("type", Consumable.class.getSimpleName())
|
.queryParam("type", Consumable.class.getSimpleName())
|
||||||
.param("type", "CONSUMABLE")
|
.param("type", "CONSUMABLE")
|
||||||
.param("name", "MOCK edited")
|
.param("name", "MOCK edited")
|
||||||
|
.param("orderTypes", "PARTY_SERVICE", "EVENT_CATERING")
|
||||||
.param("quantity", "4711")
|
.param("quantity", "4711")
|
||||||
.param("wholesalePrice", "0.01")
|
.param("wholesalePrice", "0.01")
|
||||||
.param("retailPrice", "0.03")
|
.param("retailPrice", "0.03")
|
||||||
|
@ -157,6 +167,7 @@ class InventoryControllerIntegrationTests {
|
||||||
.extracting("name", "wholesalePrice", "retailPrice", "promotionPrice")
|
.extracting("name", "wholesalePrice", "retailPrice", "promotionPrice")
|
||||||
.containsExactly("MOCK edited", Money.of(0.01, EURO),
|
.containsExactly("MOCK edited", Money.of(0.01, EURO),
|
||||||
Money.of(0.03, EURO), Optional.of(Money.of(0.02, EURO)));
|
Money.of(0.03, EURO), Optional.of(Money.of(0.02, EURO)));
|
||||||
|
assertThat(editedProduct.getCategories()).containsExactlyInAnyOrder("PARTY_SERVICE", "EVENT_CATERING");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
Loading…
Reference in a new issue