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

162 lines
5.1 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// 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 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");
}
}
/**
* 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());
}
}