PackagingUnitFinderImpl.java
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openwms.wms.inventory.impl;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import org.ameba.annotation.Measured;
import org.ameba.annotation.TxService;
import org.ameba.exception.NotFoundException;
import org.ameba.i18n.Translator;
import org.openwms.common.location.api.LocationGroupApi;
import org.openwms.common.location.api.LocationGroupVO;
import org.openwms.core.units.api.Measurable;
import org.openwms.core.units.api.Piece;
import org.openwms.wms.inventory.LoadUnit;
import org.openwms.wms.inventory.LoadUnitService;
import org.openwms.wms.inventory.PackagingUnit;
import org.openwms.wms.inventory.PackagingUnitFinder;
import org.openwms.wms.inventory.Product;
import org.openwms.wms.inventory.ProductService;
import org.openwms.wms.inventory.api.AvailabilityState;
import org.openwms.wms.location.LocationService;
import org.openwms.wms.transport.TransportUnit;
import org.openwms.wms.transport.TransportUnitService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.validation.annotation.Validated;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import static java.lang.String.format;
import static org.openwms.wms.InventoryMessageCodes.PU_NOT_FOUND_BY_PKEY;
/**
* A PackagingUnitFinderImpl is a Spring managed transactional bean that is responsible for all query operations on {@link PackagingUnit}s.
*
* @author Heiko Scherrer
*/
@Validated
@TxService
class PackagingUnitFinderImpl implements PackagingUnitFinder {
private static final Logger LOGGER = LoggerFactory.getLogger(PackagingUnitFinderImpl.class);
private final Translator translator;
private final PackagingUnitRepository repository;
private final ProductService productService;
private final LoadUnitService loadUnitService;
private final LocationService locationService;
private final TransportUnitService transportUnitService;
private final LocationGroupApi locationGroupApi;
PackagingUnitFinderImpl(Translator translator, PackagingUnitRepository repository, ProductService productService,
LoadUnitService loadUnitService, LocationService locationService, TransportUnitService transportUnitService, LocationGroupApi locationGroupApi) {
this.translator = translator;
this.repository = repository;
this.productService = productService;
this.loadUnitService = loadUnitService;
this.locationService = locationService;
this.transportUnitService = transportUnitService;
this.locationGroupApi = locationGroupApi;
}
/**
* {@inheritDoc}
*/
@Override
@Measured
public PackagingUnit findByPKey(@NotEmpty String pKey) {
return findByPKeyInternal(pKey);
}
private PackagingUnit findByPKeyInternal(String pKey) {
return repository.findBypKey(pKey).orElseThrow(() -> new NotFoundException(translator, PU_NOT_FOUND_BY_PKEY,
new Serializable[]{pKey}, pKey));
}
/**
* {@inheritDoc}
*/
@Override
@Measured
public @NotNull List<PackagingUnit> findOnLocation(@NotBlank String erpCode) {
return repository.findOnLocation(erpCode);
}
/**
* {@inheritDoc}
*/
@Override
@Measured
public @NotNull List<PackagingUnit> findInLocationGroup(@NotBlank String locationGroupName) {
return new ArrayList<>(0);
}
/**
* {@inheritDoc}
*/
@Override
@Measured
public @NotNull List<PackagingUnit> findOnTU(@NotEmpty String transportUnitBK) {
TransportUnit tu = transportUnitService.findOneByOrThrow(transportUnitBK);
LOGGER.debug("Find all PackagingUnits on TransportUnit [{}]", tu.getTransportUnitBK());
return loadUnitService.find(transportUnitBK)
.map(loadUnits -> loadUnits.stream()
.map(LoadUnit::getPackagingUnits)
.flatMap(Collection::stream)
.toList()).orElse(Collections.emptyList());
}
/**
* {@inheritDoc}
*/
@Override
@Measured
public @NotNull List<PackagingUnit> findOnTUandLU(@NotEmpty String transportUnitBK, @NotEmpty String luPos) {
TransportUnit tu = transportUnitService.findOneByOrThrow(transportUnitBK);
Optional<LoadUnit> lu = loadUnitService.findOptional(transportUnitBK, luPos);
if (lu.isPresent()) {
LOGGER.debug("Find all PackagingUnits on TransportUnit [{}] and LoadUnit [{}]", tu.getTransportUnitBK(), lu.get().getPersistentKey());
return new ArrayList<>(lu.get().getPackagingUnits());
}
return Collections.emptyList();
}
/**
* {@inheritDoc}
*/
@Override
@Measured
public @NotNull List<PackagingUnit> findForSku(@NotEmpty String sku) {
return repository.findByProduct(productService.findBySKUorThrow(sku));
}
/**
* {@inheritDoc}
*/
@Override
@Measured
public @NotNull List<PackagingUnit> findForSku(@NotEmpty String sku, int amount, String sortDirection, String sortProperty) {
List<PackagingUnit> result;
if (sortDirection != null && !sortDirection.isEmpty() && sortProperty != null && !sortProperty.isEmpty()) {
result = repository.findForProduct(
sku,
PageRequest.of(0, amount, Sort.by(Sort.Direction.fromString(sortDirection.toUpperCase()), sortProperty)));
} else {
result = repository.findForProduct(
sku,
null
);
}
return result == null ? Collections.emptyList() : result;
}
/**
* {@inheritDoc}
*/
@Override
@Measured
public @NotNull List<PackagingUnit> findOfProductInLG(@NotBlank String productPKey, List<String> locationGroupNames,
String sortDirection, String sortProperty) {
List<PackagingUnit> result;
if (sortDirection != null && !sortDirection.isEmpty() && sortProperty != null && !sortProperty.isEmpty()) {
result = repository.findAllOfProductinLG(
productPKey,
Collections.singletonList(AvailabilityState.AVAILABLE),
Collections.singletonList("AVAILABLE"),
locationGroupNames,
0,
false,
PageRequest.of(0, Integer.MAX_VALUE, Sort.by(Sort.Direction.fromString(sortDirection.toUpperCase()), sortProperty)));
} else {
result = repository.findAllOfProductinLG(
productPKey,
Collections.singletonList(AvailabilityState.AVAILABLE),
Collections.singletonList("AVAILABLE"),
locationGroupNames,
0,
false,
null
);
}
return result == null ? Collections.emptyList() : result;
}
/**
* {@inheritDoc}
*/
@Override
@Measured
public @NotNull Measurable findAmountOfProductByPKey(@NotBlank String productPKey, @NotBlank String locationGroupName) {
var byName = locationGroupApi.findByName(locationGroupName);
if (byName.isEmpty()) {
throw new NotFoundException(format("LocationGroup with name [%s] does not exist", locationGroupName));
}
var groupsList = byName.get().streamLocationGroups().map(LocationGroupVO::getName).toList();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Seaching for the amount of Product with pKey [{}] in LocationGroups [{}]", productPKey, groupsList);
}
var result = repository.findAmountOfProductByPKey(
productPKey,
byName.get().streamLocationGroups().map(LocationGroupVO::getName).toList()
);
return Piece.of(BigDecimal.valueOf(result.stream().map(pu -> pu.getQuantity().getMagnitude().intValue()).mapToLong(Long::valueOf).sum()));
}
/**
* {@inheritDoc}
*/
@Override
@Measured
public @NotNull Measurable findAmountOfProductBySKU(@NotBlank String sku, @NotBlank String locationGroupName) {
var byName = locationGroupApi.findByName(locationGroupName);
if (byName.isEmpty()) {
throw new NotFoundException(format("LocationGroup with name [%s] does not exist", locationGroupName));
}
var groupsList = byName.get().streamLocationGroups().map(LocationGroupVO::getName).toList();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Seaching for the amount of Product with SKU [{}] in LocationGroups [{}]", sku, groupsList);
}
var result = repository.findAmountOfProductBySKU(
sku,
groupsList
);
return Piece.of(BigDecimal.valueOf(result.stream().map(pu -> pu.getQuantity().getMagnitude().intValue()).mapToLong(Long::valueOf).sum()));
}
/**
* {@inheritDoc}
*/
@Override
@Measured
public @NotNull Measurable findAmountOfProductBySKUOnLocation(@NotBlank String sku, @NotBlank String locationErpCode) {
var byName = locationService.findByErpCode(locationErpCode);
var result = repository.findAmountOfProductBySKUandErpCode(sku, byName.getErpCode());
return Piece.of(BigDecimal.valueOf(result.stream().map(pu -> pu.getQuantity().getMagnitude().intValue()).mapToLong(Long::valueOf).sum()));
}
/**
* {@inheritDoc}
*/
@Override
@Measured
public @NotNull List<PackagingUnit> findAvailablesForProductOnLocationsOnly(@NotNull Product product) {
return repository.findAvailablesByProductOnLocation(product);
}
/**
* {@inheritDoc}
*/
@Override
@Measured
public @NotNull List<PackagingUnit> findAvailablesForProductInLoadUnitsOnly(@NotNull Product product) {
return repository.findAvailablesByProductInLoadUnit(product);
}
}