162 lines
5.1 KiB
Java
162 lines
5.1 KiB
Java
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||
// SPDX-FileCopyrightText: 2023 swt23w23
|
||
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
|
||
|
||
/**
|
||
* 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;
|
||
private @NotNull Set<OrderType> orderTypes;
|
||
|
||
public InventoryMutateForm() {
|
||
}
|
||
|
||
public String getName() {
|
||
return name;
|
||
}
|
||
|
||
public Quantity getQuantity() {
|
||
return quantity;
|
||
}
|
||
|
||
public Double getRetailPrice() {
|
||
return retailPrice;
|
||
}
|
||
|
||
public void setName(String name) {
|
||
this.name = name;
|
||
}
|
||
|
||
public Set<OrderType> getOrderTypes() {
|
||
return orderTypes;
|
||
}
|
||
|
||
public void setQuantity(Quantity quantity) {
|
||
this.quantity = quantity;
|
||
}
|
||
|
||
public void setRetailPrice(Double 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}.
|
||
*
|
||
* @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));
|
||
|
||
// 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());
|
||
}
|
||
}
|