fix:修改盘点执行相关接口及添加库存总览接口
parent
48e624ace6
commit
e3d1dac817
@ -0,0 +1,26 @@
|
|||||||
|
package cn.iocoder.yudao.module.erp.enums.stock;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public enum ErpStockCheckSourceTypeEnum implements IntArrayValuable {
|
||||||
|
|
||||||
|
BY_LOCATION(1, "按库存"),
|
||||||
|
BY_PRODUCT(2, "按产品");
|
||||||
|
|
||||||
|
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ErpStockCheckSourceTypeEnum::getType).toArray();
|
||||||
|
|
||||||
|
private final Integer type;
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[] array() {
|
||||||
|
return ARRAYS;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,66 @@
|
|||||||
|
package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.warehouse;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - ERP 仓库总览 Response VO")
|
||||||
|
@Data
|
||||||
|
public class ErpWarehouseSummaryRespVO {
|
||||||
|
|
||||||
|
@Schema(description = "仓库编号", example = "1")
|
||||||
|
private Long warehouseId;
|
||||||
|
|
||||||
|
@Schema(description = "仓库名称", example = "A仓")
|
||||||
|
private String warehouseName;
|
||||||
|
|
||||||
|
@Schema(description = "产品库存量", example = "1000")
|
||||||
|
private BigDecimal productStockCount;
|
||||||
|
|
||||||
|
@Schema(description = "产品库存展示", example = "2托3包4个")
|
||||||
|
private String productStockDisplay;
|
||||||
|
|
||||||
|
@Schema(description = "物料库存量", example = "1000")
|
||||||
|
private BigDecimal materialStockCount;
|
||||||
|
|
||||||
|
@Schema(description = "物料库存展示", example = "10采购单位20库存单位")
|
||||||
|
private String materialStockDisplay;
|
||||||
|
|
||||||
|
@Schema(description = "备件库存量", example = "1000")
|
||||||
|
private BigDecimal sparePartStockCount;
|
||||||
|
|
||||||
|
@Schema(description = "备件库存展示", example = "10采购单位20库存单位")
|
||||||
|
private String sparePartStockDisplay;
|
||||||
|
|
||||||
|
@Schema(description = "今日入库单数量", example = "5")
|
||||||
|
private Long todayStockInOrderCount;
|
||||||
|
|
||||||
|
@Schema(description = "今日入库明细行数量", example = "18")
|
||||||
|
private Long todayStockInItemCount;
|
||||||
|
|
||||||
|
@Schema(description = "今日入库产成品展示", example = "2托3包4个")
|
||||||
|
private String todayProductStockInDisplay;
|
||||||
|
|
||||||
|
@Schema(description = "今日入库物料展示", example = "100个")
|
||||||
|
private String todayMaterialStockInDisplay;
|
||||||
|
|
||||||
|
@Schema(description = "今日入库备件展示", example = "100个")
|
||||||
|
private String todaySparePartStockInDisplay;
|
||||||
|
|
||||||
|
@Schema(description = "今日出库单数量", example = "3")
|
||||||
|
private Long todayStockOutOrderCount;
|
||||||
|
|
||||||
|
@Schema(description = "今日出库明细行数量", example = "10")
|
||||||
|
private Long todayStockOutItemCount;
|
||||||
|
|
||||||
|
@Schema(description = "今日出库产成品展示", example = "2托3包4个")
|
||||||
|
private String todayProductStockOutDisplay;
|
||||||
|
|
||||||
|
@Schema(description = "今日出库物料展示", example = "100个")
|
||||||
|
private String todayMaterialStockOutDisplay;
|
||||||
|
|
||||||
|
@Schema(description = "今日出库备件展示", example = "100个")
|
||||||
|
private String todaySparePartStockOutDisplay;
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package cn.iocoder.yudao.module.erp.service.stock;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.warehouse.ErpWarehouseSummaryRespVO;
|
||||||
|
|
||||||
|
public interface ErpWarehouseSummaryService {
|
||||||
|
|
||||||
|
ErpWarehouseSummaryRespVO getWarehouseSummary(Long warehouseId);
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,261 @@
|
|||||||
|
package cn.iocoder.yudao.module.erp.service.stock;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO;
|
||||||
|
import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.warehouse.ErpWarehouseSummaryRespVO;
|
||||||
|
import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;
|
||||||
|
import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockDO;
|
||||||
|
import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockInDO;
|
||||||
|
import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockInItemDO;
|
||||||
|
import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockOutDO;
|
||||||
|
import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockOutItemDO;
|
||||||
|
import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpWarehouseDO;
|
||||||
|
import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockInItemMapper;
|
||||||
|
import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockInMapper;
|
||||||
|
import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockMapper;
|
||||||
|
import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockOutItemMapper;
|
||||||
|
import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockOutMapper;
|
||||||
|
import cn.iocoder.yudao.module.erp.service.product.ErpProductService;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Validated
|
||||||
|
public class ErpWarehouseSummaryServiceImpl implements ErpWarehouseSummaryService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ErpWarehouseService warehouseService;
|
||||||
|
@Resource
|
||||||
|
private ErpStockMapper stockMapper;
|
||||||
|
@Resource
|
||||||
|
private ErpStockInMapper stockInMapper;
|
||||||
|
@Resource
|
||||||
|
private ErpStockInItemMapper stockInItemMapper;
|
||||||
|
@Resource
|
||||||
|
private ErpStockOutMapper stockOutMapper;
|
||||||
|
@Resource
|
||||||
|
private ErpStockOutItemMapper stockOutItemMapper;
|
||||||
|
@Resource
|
||||||
|
private ErpProductService productService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ErpWarehouseSummaryRespVO getWarehouseSummary(Long warehouseId) {
|
||||||
|
ErpWarehouseDO warehouse = warehouseService.getWarehouse(warehouseId);
|
||||||
|
ErpWarehouseSummaryRespVO respVO = new ErpWarehouseSummaryRespVO();
|
||||||
|
respVO.setWarehouseId(warehouseId);
|
||||||
|
respVO.setWarehouseName(warehouse != null ? warehouse.getName() : null);
|
||||||
|
|
||||||
|
List<ErpStockDO> stockList = stockMapper.selectListByWarehouseIdsAndAreaIds(Collections.singletonList(warehouseId), null);
|
||||||
|
fillStockSummary(respVO, stockList);
|
||||||
|
|
||||||
|
LocalDateTime beginTime = LocalDate.now().atStartOfDay();
|
||||||
|
LocalDateTime endTime = beginTime.plusDays(1);
|
||||||
|
fillTodayInSummary(respVO, warehouseId, beginTime, endTime);
|
||||||
|
fillTodayOutSummary(respVO, warehouseId, beginTime, endTime);
|
||||||
|
return respVO;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillStockSummary(ErpWarehouseSummaryRespVO respVO, List<ErpStockDO> stockList) {
|
||||||
|
if (CollUtil.isEmpty(stockList)) {
|
||||||
|
respVO.setProductStockCount(BigDecimal.ZERO);
|
||||||
|
respVO.setMaterialStockCount(BigDecimal.ZERO);
|
||||||
|
respVO.setSparePartStockCount(BigDecimal.ZERO);
|
||||||
|
respVO.setProductStockDisplay(formatProductDisplay(BigDecimal.ZERO, null, null, null));
|
||||||
|
respVO.setMaterialStockDisplay(formatUnitDisplay(BigDecimal.ZERO, null, null, null));
|
||||||
|
respVO.setSparePartStockDisplay(formatUnitDisplay(BigDecimal.ZERO, null, null, null));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<Integer, List<ErpStockDO>> categoryMap = stockList.stream().collect(Collectors.groupingBy(this::resolveCategoryType));
|
||||||
|
BigDecimal productCount = sumCount(categoryMap.get(1));
|
||||||
|
BigDecimal materialCount = sumCount(categoryMap.get(2));
|
||||||
|
BigDecimal sparePartCount = sumCount(categoryMap.get(3));
|
||||||
|
|
||||||
|
respVO.setProductStockCount(productCount);
|
||||||
|
respVO.setMaterialStockCount(materialCount);
|
||||||
|
respVO.setSparePartStockCount(sparePartCount);
|
||||||
|
|
||||||
|
ErpStockDO productStock = firstStock(categoryMap.get(1));
|
||||||
|
ErpStockDO materialStock = firstStock(categoryMap.get(2));
|
||||||
|
ErpStockDO sparePartStock = firstStock(categoryMap.get(3));
|
||||||
|
respVO.setProductStockDisplay(formatProductDisplay(productCount,
|
||||||
|
productStock != null ? productStock.getPalletTotalQuantity() : null,
|
||||||
|
productStock != null ? productStock.getPalletPackageQuantity() : null,
|
||||||
|
productStock != null ? productStock.getPackageQuantity() : null));
|
||||||
|
respVO.setMaterialStockDisplay(formatUnitDisplay(materialCount,
|
||||||
|
materialStock != null ? materialStock.getPurchaseUnitConvertQuantity() : null,
|
||||||
|
materialStock != null ? materialStock.getPurchaseUnitName() : null,
|
||||||
|
materialStock != null ? materialStock.getUnitName() : null));
|
||||||
|
respVO.setSparePartStockDisplay(formatUnitDisplay(sparePartCount,
|
||||||
|
sparePartStock != null ? sparePartStock.getPurchaseUnitConvertQuantity() : null,
|
||||||
|
sparePartStock != null ? sparePartStock.getPurchaseUnitName() : null,
|
||||||
|
sparePartStock != null ? sparePartStock.getUnitName() : null));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillTodayInSummary(ErpWarehouseSummaryRespVO respVO, Long warehouseId, LocalDateTime beginTime, LocalDateTime endTime) {
|
||||||
|
List<ErpStockInDO> stockInList = stockInMapper.selectList(new LambdaQueryWrapper<ErpStockInDO>()
|
||||||
|
.eq(ErpStockInDO::getStatus, 20)
|
||||||
|
.between(ErpStockInDO::getInTime, beginTime, endTime));
|
||||||
|
List<Long> inIds = stockInList.stream()
|
||||||
|
.filter(stockIn -> hasWarehouseIn(stockIn.getId(), warehouseId))
|
||||||
|
.map(ErpStockInDO::getId)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
respVO.setTodayStockInOrderCount((long) inIds.size());
|
||||||
|
|
||||||
|
List<ErpStockInItemDO> itemList = stockInItemMapper.selectListByInIds(inIds);
|
||||||
|
respVO.setTodayStockInItemCount((long) itemList.size());
|
||||||
|
fillInOutDisplay(respVO, true, itemList);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillTodayOutSummary(ErpWarehouseSummaryRespVO respVO, Long warehouseId, LocalDateTime beginTime, LocalDateTime endTime) {
|
||||||
|
List<ErpStockOutDO> stockOutList = stockOutMapper.selectList(new LambdaQueryWrapper<ErpStockOutDO>()
|
||||||
|
.eq(ErpStockOutDO::getStatus, 20)
|
||||||
|
.between(ErpStockOutDO::getOutTime, beginTime, endTime));
|
||||||
|
List<Long> outIds = stockOutList.stream()
|
||||||
|
.filter(stockOut -> hasWarehouseOut(stockOut.getId(), warehouseId))
|
||||||
|
.map(ErpStockOutDO::getId)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
respVO.setTodayStockOutOrderCount((long) outIds.size());
|
||||||
|
|
||||||
|
List<ErpStockOutItemDO> itemList = stockOutItemMapper.selectListByOutIds(outIds);
|
||||||
|
respVO.setTodayStockOutItemCount((long) itemList.size());
|
||||||
|
fillInOutDisplay(respVO, false, itemList);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillInOutDisplay(ErpWarehouseSummaryRespVO respVO, boolean inbound, List<?> itemList) {
|
||||||
|
if (CollUtil.isEmpty(itemList)) {
|
||||||
|
if (inbound) {
|
||||||
|
respVO.setTodayProductStockInDisplay(formatProductDisplay(BigDecimal.ZERO, null, null, null));
|
||||||
|
respVO.setTodayMaterialStockInDisplay(formatUnitDisplay(BigDecimal.ZERO, null, null, null));
|
||||||
|
respVO.setTodaySparePartStockInDisplay(formatUnitDisplay(BigDecimal.ZERO, null, null, null));
|
||||||
|
} else {
|
||||||
|
respVO.setTodayProductStockOutDisplay(formatProductDisplay(BigDecimal.ZERO, null, null, null));
|
||||||
|
respVO.setTodayMaterialStockOutDisplay(formatUnitDisplay(BigDecimal.ZERO, null, null, null));
|
||||||
|
respVO.setTodaySparePartStockOutDisplay(formatUnitDisplay(BigDecimal.ZERO, null, null, null));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<Long> productIds = itemList.stream()
|
||||||
|
.map(item -> item instanceof ErpStockInItemDO ? ((ErpStockInItemDO) item).getProductId() : ((ErpStockOutItemDO) item).getProductId())
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
Map<Long, ErpProductRespVO> productMap = productService.getProductVOMap(productIds);
|
||||||
|
|
||||||
|
BigDecimal productCount = BigDecimal.ZERO;
|
||||||
|
BigDecimal materialCount = BigDecimal.ZERO;
|
||||||
|
BigDecimal sparePartCount = BigDecimal.ZERO;
|
||||||
|
|
||||||
|
for (Object item : itemList) {
|
||||||
|
Long productId = item instanceof ErpStockInItemDO ? ((ErpStockInItemDO) item).getProductId() : ((ErpStockOutItemDO) item).getProductId();
|
||||||
|
BigDecimal count = item instanceof ErpStockInItemDO ? ((ErpStockInItemDO) item).getCount() : ((ErpStockOutItemDO) item).getCount();
|
||||||
|
ErpProductRespVO product = productMap.get(productId);
|
||||||
|
Integer categoryType = product != null ? product.getCategoryType() : null;
|
||||||
|
if (Integer.valueOf(1).equals(categoryType)) {
|
||||||
|
productCount = productCount.add(safeCount(count));
|
||||||
|
} else if (Integer.valueOf(2).equals(categoryType)) {
|
||||||
|
materialCount = materialCount.add(safeCount(count));
|
||||||
|
} else if (Integer.valueOf(3).equals(categoryType)) {
|
||||||
|
sparePartCount = sparePartCount.add(safeCount(count));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inbound) {
|
||||||
|
respVO.setTodayProductStockInDisplay(formatProductDisplay(productCount, null, null, null));
|
||||||
|
respVO.setTodayMaterialStockInDisplay(formatUnitDisplay(materialCount, null, null, null));
|
||||||
|
respVO.setTodaySparePartStockInDisplay(formatUnitDisplay(sparePartCount, null, null, null));
|
||||||
|
} else {
|
||||||
|
respVO.setTodayProductStockOutDisplay(formatProductDisplay(productCount, null, null, null));
|
||||||
|
respVO.setTodayMaterialStockOutDisplay(formatUnitDisplay(materialCount, null, null, null));
|
||||||
|
respVO.setTodaySparePartStockOutDisplay(formatUnitDisplay(sparePartCount, null, null, null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasWarehouseIn(Long inId, Long warehouseId) {
|
||||||
|
return stockInItemMapper.selectListByInId(inId).stream().anyMatch(item -> warehouseId.equals(item.getWarehouseId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasWarehouseOut(Long outId, Long warehouseId) {
|
||||||
|
return stockOutItemMapper.selectListByOutId(outId).stream().anyMatch(item -> warehouseId.equals(item.getWarehouseId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer resolveCategoryType(ErpStockDO stock) {
|
||||||
|
if (stock.getCategoryType() != null) {
|
||||||
|
return stock.getCategoryType();
|
||||||
|
}
|
||||||
|
ErpProductRespVO product = productService.getProductVOMap(Collections.singleton(stock.getProductId())).get(stock.getProductId());
|
||||||
|
return product != null ? product.getCategoryType() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ErpStockDO firstStock(List<ErpStockDO> list) {
|
||||||
|
return CollUtil.isEmpty(list) ? null : list.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigDecimal sumCount(List<ErpStockDO> list) {
|
||||||
|
if (CollUtil.isEmpty(list)) {
|
||||||
|
return BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
BigDecimal total = BigDecimal.ZERO;
|
||||||
|
for (ErpStockDO stock : list) {
|
||||||
|
total = total.add(safeCount(stock.getCount()));
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigDecimal safeCount(BigDecimal count) {
|
||||||
|
return count == null ? BigDecimal.ZERO : count;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formatProductDisplay(BigDecimal count, BigDecimal palletTotalQuantity, BigDecimal palletPackageQuantity,
|
||||||
|
BigDecimal packageQuantity) {
|
||||||
|
BigDecimal safeCount = safeCount(count);
|
||||||
|
if (palletTotalQuantity == null || palletTotalQuantity.compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
|
return formatNumber(safeCount) + "个";
|
||||||
|
}
|
||||||
|
BigDecimal palletCount = safeCount.divideToIntegralValue(palletTotalQuantity);
|
||||||
|
BigDecimal remainAfterPallet = safeCount.remainder(palletTotalQuantity);
|
||||||
|
if (palletPackageQuantity == null || palletPackageQuantity.compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
|
return formatNumber(palletCount) + "托" + formatNumber(remainAfterPallet) + "个";
|
||||||
|
}
|
||||||
|
BigDecimal packageCount = remainAfterPallet.divideToIntegralValue(palletPackageQuantity);
|
||||||
|
BigDecimal remainAfterPackage = remainAfterPallet.remainder(palletPackageQuantity);
|
||||||
|
if (packageQuantity == null || packageQuantity.compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
|
return formatNumber(palletCount) + "托" + formatNumber(packageCount) + "包" + formatNumber(remainAfterPackage) + "个";
|
||||||
|
}
|
||||||
|
BigDecimal unitCount = remainAfterPackage.divideToIntegralValue(packageQuantity);
|
||||||
|
return formatNumber(palletCount) + "托" + formatNumber(packageCount) + "包" + formatNumber(unitCount) + "个";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formatUnitDisplay(BigDecimal count, BigDecimal purchaseUnitConvertQuantity, String purchaseUnitName, String unitName) {
|
||||||
|
BigDecimal safeCount = safeCount(count);
|
||||||
|
if (purchaseUnitConvertQuantity == null || purchaseUnitConvertQuantity.compareTo(BigDecimal.ZERO) <= 0
|
||||||
|
|| purchaseUnitName == null || unitName == null) {
|
||||||
|
return formatNumber(safeCount) + (unitName != null ? unitName : "个");
|
||||||
|
}
|
||||||
|
BigDecimal purchaseCount = safeCount.divideToIntegralValue(purchaseUnitConvertQuantity);
|
||||||
|
BigDecimal unitCount = safeCount.remainder(purchaseUnitConvertQuantity);
|
||||||
|
StringBuilder display = new StringBuilder();
|
||||||
|
if (purchaseCount.compareTo(BigDecimal.ZERO) > 0) {
|
||||||
|
display.append(formatNumber(purchaseCount)).append(purchaseUnitName);
|
||||||
|
}
|
||||||
|
if (unitCount.compareTo(BigDecimal.ZERO) > 0 || display.length() == 0) {
|
||||||
|
display.append(formatNumber(unitCount)).append(unitName);
|
||||||
|
}
|
||||||
|
return display.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formatNumber(BigDecimal value) {
|
||||||
|
return value == null ? "0" : value.stripTrailingZeros().toPlainString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue