| | |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO; |
| | | import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO; |
| | | import cn.iocoder.yudao.module.mes.service.wm.materialstockbag.MesWmMaterialStockBagService; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.wm.materialstockbag.MesWmMaterialStockBagMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstockbag.MesWmMaterialStockBagDO; |
| | | import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseService; |
| | | import jakarta.annotation.Resource; |
| | |
| | | import java.util.function.Function; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| | | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; |
| | | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; |
| | | import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; |
| | | |
| | |
| | | @Resource |
| | | @Lazy |
| | | private MesWmBatchService batchService; |
| | | |
| | | @Resource |
| | | private MesWmMaterialStockBagMapper materialStockBagMapper; |
| | | @Resource |
| | | private MesWmMaterialStockBagService materialStockBagService; |
| | | |
| | | @Override |
| | | public MesWmMaterialStockDO getMaterialStock(Long id) { |
| | |
| | | Assert.notNull(virtualWarehouse, "虚拟仓库(WIP_VIRTUAL_WAREHOUSE)不存在"); |
| | | virtualWarehouseId = virtualWarehouse.getId(); |
| | | } |
| | | // 1.4 解析 warehouseType:转换为该类型仓库 warehouseIds(与 warehouseId / virtualFilter 并存,保留兼容) |
| | | List<Long> warehouseIds = resolveWarehouseIdsByType(pageReqVO.getWarehouseType()); |
| | | if (warehouseIds != null && warehouseIds.isEmpty()) { |
| | | return PageResult.empty(); |
| | | } |
| | | |
| | | // 2. 分页查询 |
| | | return materialStockMapper.selectPage(pageReqVO, itemTypeIds, itemIds, virtualWarehouseId); |
| | | return materialStockMapper.selectPage(pageReqVO, itemTypeIds, itemIds, virtualWarehouseId, warehouseIds); |
| | | } |
| | | |
| | | /** |
| | | * 将仓库类型解析为仓库 id 集合(warehouseType 为 null 时不加过滤,返回 null) |
| | | * |
| | | * @param warehouseType 仓库类型,见 {@link cn.iocoder.yudao.module.mes.enums.wm.MesWmWarehouseTypeEnum} |
| | | * @return 该类型仓库 id 集合;warehouseType 为 null 返回 null;该类型无仓库返回空集合 |
| | | */ |
| | | private List<Long> resolveWarehouseIdsByType(Integer warehouseType) { |
| | | if (warehouseType == null) { |
| | | return null; |
| | | } |
| | | return convertList(warehouseService.getWarehouseListByType(warehouseType), MesWmWarehouseDO::getId); |
| | | } |
| | | |
| | | @Override |
| | |
| | | |
| | | @Override |
| | | public List<MesWmMaterialStockDO> getMaterialStockList(MesWmMaterialStockListReqVO reqVO) { |
| | | return materialStockMapper.selectList(reqVO); |
| | | List<Long> warehouseIds = resolveWarehouseIdsByType(reqVO.getWarehouseType()); |
| | | if (warehouseIds != null && warehouseIds.isEmpty()) { |
| | | return Collections.emptyList(); |
| | | } |
| | | return materialStockMapper.selectList(reqVO, warehouseIds); |
| | | } |
| | | |
| | | @Override |
| | |
| | | @Override |
| | | public MesWmMaterialStockDO getMaterialStockByCompositeKey(Long itemId, Long warehouseId, Long locationId, Long areaId, |
| | | Long batchId, Long vendorId) { |
| | | return materialStockMapper.selectByCompositeKey(itemId, warehouseId, locationId, areaId, batchId, vendorId); |
| | | // 出库/占用只从合格库匹配,避免扣到同批次的暂存库(种子质检按袋拆数量后同批次双状态并存) |
| | | return materialStockMapper.selectQualifiedByCompositeKey(itemId, warehouseId, locationId, areaId, batchId, vendorId); |
| | | } |
| | | |
| | | @Override |
| | | public List<MesWmMaterialStockDO> getQualifiedMaterialStockListByItemAndBatch(Long itemId, Long batchId) { |
| | | List<Long> qualifiedWarehouseIds = warehouseService.getQualifiedWarehouseIds(); |
| | | if (CollUtil.isEmpty(qualifiedWarehouseIds)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | return materialStockMapper.selectQualifiedListByItemAndBatch(itemId, batchId, qualifiedWarehouseIds); |
| | | } |
| | | |
| | | @Override |
| | |
| | | @Override |
| | | public void updateMaterialStockState(Long id, Integer stockState) { |
| | | materialStockMapper.updateById(new MesWmMaterialStockDO().setId(id).setStockState(stockState)); |
| | | } |
| | | |
| | | @Override |
| | | public int updateStockStateByBatchId(Long batchId, Integer toState, Integer fromState) { |
| | | return materialStockMapper.updateStockStateByBatchId(batchId, toState, fromState); |
| | | } |
| | | |
| | | @Override |
| | |
| | | || ObjUtil.notEqual(stock.getAreaId(), areaId)) { |
| | | throw exception(WM_MATERIAL_STOCK_SELECTION_MISMATCH); |
| | | } |
| | | // 3. 校验库存数量充足(如果前端传了 quantity,则必须保证库存数量 >= quantity) |
| | | // 3. 校验库存记录必须是合格库(暂存库存未质检,不可用于出库/占用) |
| | | if (!MesWmStockStateEnum.QUALIFIED.getState().equals(stock.getStockState())) { |
| | | throw exception(WM_MATERIAL_STOCK_NOT_QUALIFIED); |
| | | } |
| | | // 4. 校验库存数量充足(如果前端传了 quantity,则必须保证库存数量 >= quantity) |
| | | if (quantity != null && stock.getQuantity() != null && stock.getQuantity().compareTo(quantity) < 0) { |
| | | throw exception(WM_MATERIAL_STOCK_INSUFFICIENT); |
| | | } |
| | |
| | | .setRelatedTransactionId(outTransactionId) |
| | | .setCheckFlag(false); |
| | | wmTransactionService.createTransaction(inTransaction); |
| | | |
| | | // 9. 整位置移库时,逐袋迁移袋码明细位置(数量等于源库位活动袋数且为整数才视为整位移库) |
| | | MesWmMaterialStockDO finalTargetStock = materialStockMapper.selectByCompositeKeyIgnoreVendor( |
| | | sourceStock.getItemId(), reqVO.getToWarehouseId(), reqVO.getToLocationId(), |
| | | reqVO.getToAreaId(), sourceStock.getBatchId()); |
| | | relocateBagsForMove(sourceStock, reqVO.getQuantity(), inTransaction, finalTargetStock); |
| | | } |
| | | |
| | | /** |
| | | * 整位移库:将源库位的活动袋码明细迁移到目标库位。 |
| | | * <p> |
| | | * 仅当移库数量等于源库位活动袋数且为整数时执行(整位置搬移),将每袋明细的库存汇总、仓库、库区、库位 |
| | | * 更新为目标位置;部分移库/无袋码明细的旧库存不做袋码迁移,保持汇总库存变动即可。 |
| | | */ |
| | | private void relocateBagsForMove(MesWmMaterialStockDO sourceStock, BigDecimal quantity, |
| | | MesWmTransactionSaveReqDTO moveInReq, MesWmMaterialStockDO targetStock) { |
| | | List<MesWmMaterialStockBagDO> bags = materialStockBagMapper.selectActiveListByItemAndBatch( |
| | | sourceStock.getItemId(), sourceStock.getBatchId(), sourceStock.getStockState()); |
| | | if (CollUtil.isEmpty(bags) || targetStock == null || quantity == null |
| | | || quantity.compareTo(BigDecimal.valueOf(bags.size())) != 0) { |
| | | return; |
| | | } |
| | | for (MesWmMaterialStockBagDO bag : bags) { |
| | | moveInReq.setBagCodeId(bag.getBagCodeId()).setBagCode(bag.getBagCode()); |
| | | materialStockBagService.moveIn(moveInReq, targetStock, null); |
| | | } |
| | | } |
| | | |
| | | } |