Skip to content

Commit

Permalink
st-tu-dresdenGH-424 - add methods for checking if UniqueInventory has…
Browse files Browse the repository at this point in the history
… sufficient stock

There are a few edge cases when checking if a `UnqiueInventory` has sufficient
stock for completing orders which are solved by providing tested methods for this.
  • Loading branch information
Deric-W committed Dec 13, 2022
1 parent dd8f50d commit 30a5350
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,19 @@
*/
package org.salespointframework.inventory;

import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

import org.salespointframework.catalog.Product;
import org.salespointframework.catalog.Product.ProductIdentifier;
import org.salespointframework.core.SalespointRepository;
import org.salespointframework.inventory.InventoryItem.InventoryItemIdentifier;
import org.salespointframework.order.Order;
import org.salespointframework.order.OrderLine;
import org.salespointframework.quantity.Quantity;
import org.springframework.data.jpa.repository.Query;
import org.springframework.util.Assert;

/**
* A {@link UniqueInventory} manages {@link UniqueInventoryItem}s, i.e. only a single {@link InventoryItem} can exist
Expand Down Expand Up @@ -61,4 +65,44 @@ public interface UniqueInventory<T extends UniqueInventoryItem>
default Optional<T> findByProduct(Product product) {
return findByProductIdentifier(product.getId());
}

/**
* Returns whether the {@link UniqueInventoryItem} associated with the
* {@link ProductIdentifier} is available in exactly or more of the
* given {@link Quantity}.
*
* @param productIdentifier must not be {@literal null}
* @param quantity must not be {@literal null}
* @return
*/
default boolean hasSufficientQuantity(ProductIdentifier productIdentifier, Quantity quantity) {
Assert.notNull(productIdentifier, "ProductIdentifier must not be null!");
Assert.notNull(quantity, "Quantity must not be null!");

var item = this.findByProductIdentifier(productIdentifier);

return item.map(it -> it.hasSufficientQuantity(quantity))
.orElseGet(quantity::isZeroOrNegative);
}

/**
* Returns whether the {@link UniqueInventory} contains enough stock to satisfy
* the {@link OrderLine}s of the given {@link Order}.
* <p>
* If there are multiple {@link OrderLine}s per {@link Product} their
* quantities are added together.
*
* @param order must not be {@literal null}
* @return
*/
default boolean hasSufficientQuantity(Order order) {
Map<ProductIdentifier, Quantity> quantities = order
.getOrderLines()
.stream()
.collect(Collectors.groupingByConcurrent(OrderLine::getProductIdentifier,
Collectors.reducing(Quantity.NONE, OrderLine::getQuantity, Quantity::add)));

return quantities.entrySet().stream()
.allMatch(entry -> this.hasSufficientQuantity(entry.getKey(), entry.getValue()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@
import org.salespointframework.core.Currencies;
import org.salespointframework.inventory.InventoryEvents.QuantityReduced;
import org.salespointframework.inventory.InventoryEvents.StockShort;
import org.salespointframework.order.Order;
import org.salespointframework.quantity.Quantity;
import org.salespointframework.useraccount.UserAccountManagement;
import org.salespointframework.useraccount.Password.UnencryptedPassword;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
Expand All @@ -58,6 +61,7 @@ class InventoryTests {
@Autowired UniqueInventory<UniqueInventoryItem> unique;
@Autowired MultiInventory<MultiInventoryItem> multiple;
@Autowired Catalog<Product> catalog;
@Autowired UserAccountManagement users;

@Autowired EntityManager em;
@Autowired NamedParameterJdbcOperations jdbc;
Expand Down Expand Up @@ -155,6 +159,55 @@ void findsInventoryItemsOutOfStock() {
});
}

@Test
void hasSufficientQuantity() {

var user = this.users.create("Test user", UnencryptedPassword.of("password"));
var order = new Order(user);

assertThat(this.unique.hasSufficientQuantity(this.cookie.getId(), Quantity.of(-4))).isTrue();
assertThat(this.unique.hasSufficientQuantity(order)).isTrue();

for (long amount : new long[] { -4, 0, 11, 3 }) {
var quantity = Quantity.of(amount);
order.addOrderLine(this.cookie, quantity);

assertThat(this.unique.hasSufficientQuantity(order)).isTrue();
}
}

@Test
void hasInsufficientQuantity() {

var user = this.users.create("Test user", UnencryptedPassword.of("password"));
var order = new Order(user);
var orderLine = order.addOrderLine(this.cookie, Quantity.of(11));

assertThat(this.unique.hasSufficientQuantity(this.cookie.getId(), Quantity.of(11))).isFalse();
assertThat(this.unique.hasSufficientQuantity(order)).isFalse();

order.remove(orderLine);

for (long amount : new long[] { -4, 6, 5, 4 }) {
order.addOrderLine(this.cookie, Quantity.of(amount));
}

assertThat(this.unique.hasSufficientQuantity(order)).isFalse();
}

@Test
void hasNoQuantity() {
var product = this.catalog.save(new Product("Test Product", Currencies.ZERO_EURO));
var user = this.users.create("Test user", UnencryptedPassword.of("password"));
var order = new Order(user);
order.addOrderLine(this.cookie, Quantity.of(8));
order.addOrderLine(product, Quantity.of(1));

assertThat(this.unique.hasSufficientQuantity(product.getId(), Quantity.of(0))).isTrue();
assertThat(this.unique.hasSufficientQuantity(product.getId(), Quantity.of(1))).isFalse();
assertThat(this.unique.hasSufficientQuantity(order)).isFalse();
}

@Test // #163
void looksUpMultipleInventoryItemsPerProduct() {

Expand Down

0 comments on commit 30a5350

Please sign in to comment.