swt23w23/src/main/java/catering/inventory/InventoryMutateForm.java

162 lines
5.1 KiB
Java
Raw Normal View History

// SPDX-License-Identifier: AGPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 swt23w23
2023-11-05 16:11:36 +01:00
package catering.inventory;
import static org.salespointframework.core.Currencies.EURO;
2023-11-05 16:11:36 +01:00
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;
2023-11-05 16:11:36 +01:00
import org.salespointframework.quantity.Quantity;
import org.springframework.data.util.Streamable;
2023-11-05 16:11:36 +01:00
import catering.catalog.Consumable;
import catering.catalog.Rentable;
import catering.order.OrderType;
2023-11-05 16:11:36 +01:00
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;
2023-11-05 16:11:36 +01:00
public InventoryMutateForm() {
2023-11-05 16:11:36 +01:00
}
public String getName() {
return name;
}
public Quantity getQuantity() {
return quantity;
}
public Double getRetailPrice() {
return retailPrice;
2023-11-05 16:11:36 +01:00
}
public void setName(String name) {
this.name = name;
2023-11-05 16:11:36 +01:00
}
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 cant 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");
}
2023-11-05 16:11:36 +01:00
}
/**
* 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());
}
2023-11-05 16:11:36 +01:00
}