云
昨天 ac2e2b5af3726ee64dc37c3ebf26989a0346f22a
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productreceipt/MesWmProductReceiptServiceImpl.java
@@ -11,6 +11,7 @@
import cn.iocoder.yudao.module.mes.controller.admin.wm.productreceipt.vo.MesWmProductReceiptImportRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.productreceipt.vo.MesWmProductReceiptPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.productreceipt.vo.MesWmProductReceiptSaveReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.productreceipt.vo.detail.MesWmProductReceiptDetailSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterialStockDO;
@@ -102,7 +103,8 @@
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long generateProductReceipt(MesWmProductProduceDO produce,
                                       List<MesWmProductProduceLineDO> produceLines) {
                                       List<MesWmProductProduceLineDO> produceLines,
                                       boolean lastProcess) {
        List<MesWmProductProduceLineDO> qualifiedLines = produceLines.stream()
                .filter(line -> ObjUtil.equal(line.getQualityStatus(), MesWmQualityStatusEnum.PASS.getStatus()))
                .filter(line -> line.getQuantity() != null && line.getQuantity().compareTo(BigDecimal.ZERO) > 0)
@@ -128,22 +130,16 @@
        MesWmProductReceiptDO receipt = MesWmProductReceiptDO.builder()
                .code(code)
                .name("生产完成自动入库-" + workOrderCode)
                .name((lastProcess ? "生产完成自动入库-" : "工序产出自动入库-") + workOrderCode)
                .workOrderId(produce.getWorkOrderId())
                .produceId(produce.getId())
                .itemId(firstMdmItemId)
                .receiptDate(LocalDateTime.now())
                .status(MesWmProductReceiptStatusEnum.PREPARE.getStatus())
                .remark("末道工序产出自动生成,产出单ID:" + produce.getId())
                .remark((lastProcess ? "末道工序产出自动生成,产出单ID:" : "中间工序产出自动生成(半成品),产出单ID:")
                        + produce.getId())
                .build();
        productReceiptMapper.insert(receipt);
        MesWmWarehouseDO virtualWarehouse = warehouseService.getWarehouseByCode(
                MesWmWarehouseDO.WIP_VIRTUAL_WAREHOUSE);
        MesWmWarehouseLocationDO virtualLocation = locationService.getWarehouseLocationByCode(
                MesWmWarehouseLocationDO.WIP_VIRTUAL_LOCATION);
        MesWmWarehouseAreaDO virtualArea = areaService.getWarehouseAreaByCode(
                MesWmWarehouseAreaDO.WIP_VIRTUAL_AREA);
        for (MesWmProductProduceLineDO produceLine : qualifiedLines) {
            MesMdItemDO item = mdItemService.getItem(produceLine.getItemId());
@@ -151,21 +147,35 @@
                    ? item.getMdmItemId() : produceLine.getItemId();
            List<cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceDetailDO> details =
                    productProduceDetailService.getProductProduceDetailListByLineId(produceLine.getId());
            Long warehouseId = CollUtil.isNotEmpty(details) ? details.get(0).getWarehouseId() : virtualWarehouse.getId();
            Long locationId = CollUtil.isNotEmpty(details) ? details.get(0).getLocationId() : virtualLocation.getId();
            Long areaId = CollUtil.isNotEmpty(details) ? details.get(0).getAreaId() : virtualArea.getId();
            if (CollUtil.isEmpty(details)) throw exception(WM_PRODUCT_RECPT_NO_DETAIL);
            Long warehouseId = details.get(0).getWarehouseId();
            Long locationId = details.get(0).getLocationId();
            Long areaId = details.get(0).getAreaId();
            MesWmMaterialStockDO stock = materialStockService.getMaterialStockByCompositeKey(
                    mdmItemId, warehouseId, locationId, areaId,
                    produceLine.getBatchId(), null);
            productReceiptLineMapper.insert(MesWmProductReceiptLineDO.builder()
            MesWmProductReceiptLineDO receiptLine = MesWmProductReceiptLineDO.builder()
                    .receiptId(receipt.getId())
                    .itemId(mdmItemId)
                    .materialStockId(stock != null ? stock.getId() : null)
                    .quantity(produceLine.getQuantity())
                    .batchId(produceLine.getBatchId())
                    .batchCode(produceLine.getBatchCode())
                    .remark("末道工序合格品自动生成")
                    .build());
                    .remark(lastProcess ? "末道工序合格品自动生成" : "中间工序合格品自动生成")
                    .build();
            productReceiptLineMapper.insert(receiptLine);
            for (var detail : details) {
                MesWmProductReceiptDetailSaveReqVO detailReq = new MesWmProductReceiptDetailSaveReqVO();
                detailReq.setReceiptId(receipt.getId());
                detailReq.setLineId(receiptLine.getId());
                detailReq.setItemId(mdmItemId);
                detailReq.setQuantity(detail.getQuantity());
                detailReq.setBatchId(detail.getBatchId());
                detailReq.setWarehouseId(detail.getWarehouseId());
                detailReq.setLocationId(detail.getLocationId());
                detailReq.setAreaId(detail.getAreaId());
                productReceiptDetailService.createProductReceiptDetail(detailReq);
            }
        }
        return receipt.getId();
    }
@@ -266,11 +276,8 @@
            throw exception(WM_PRODUCT_RECPT_NO_DETAIL);
        }
        // 自动入库单来源产出单已按工艺路线仓库记账,此处仅完成业务单据,避免实际仓库重复入库。
        // 手工创建的产品入库单仍执行“虚拟线边库 OUT + 实际仓库 IN”。
        if (receipt.getProduceId() == null) {
            createTransactionList(receipt, details);
        }
        // 执行入库时才登记实际仓库库存,保证“未上架不上账”
        createTransactionList(receipt, details);
        // 更新收货单状态
        productReceiptMapper.updateById(new MesWmProductReceiptDO()
@@ -283,9 +290,17 @@
        MesWmWarehouseLocationDO virtualLocation = locationService.getWarehouseLocationByCode(MesWmWarehouseLocationDO.WIP_VIRTUAL_LOCATION);
        MesWmWarehouseAreaDO virtualArea = areaService.getWarehouseAreaByCode(MesWmWarehouseAreaDO.WIP_VIRTUAL_AREA);
        // 2. 遍历明细,每条明细产生 OUT(虚拟线边库扣减)+ IN(实际仓库增加)
        // 2. 遍历明细登记库存事务
        for (MesWmProductReceiptDetailDO detail : details) {
            // 2.1 先从虚拟线边库出库(库存减少)
            // 2.1 自动入库单:产出单完成时已把虚拟线边库产出记账,这里只登记实际仓库入库
            if (receipt.getProduceId() != null) {
                if (virtualWarehouse != null && ObjUtil.equal(detail.getWarehouseId(), virtualWarehouse.getId())) {
                    continue;
                }
                createInTransaction(receipt, detail, null);
                continue;
            }
            // 2.2 手工入库单:虚拟线边库出库 + 实际仓库入库
            Long outTransactionId = wmTransactionService.createTransaction(new MesWmTransactionSaveReqDTO()
                    .setType(MesWmTransactionTypeEnum.OUT.getType()).setItemId(detail.getItemId())
                    .setQuantity(detail.getQuantity().negate()) // 库存减少
@@ -294,18 +309,22 @@
                    .setCheckFlag(false) // 线边库允许负库存
                    .setBizType(MesBizTypeConstants.WM_PRODUCT_RECPT).setBizId(receipt.getId())
                    .setBizCode(receipt.getCode()).setBizLineId(detail.getLineId()));
            // 2.2 再入实际仓库(库存增加)
            wmTransactionService.createTransaction(new MesWmTransactionSaveReqDTO()
                    .setType(MesWmTransactionTypeEnum.IN.getType()).setItemId(detail.getItemId())
                    .setQuantity(detail.getQuantity()) // 库存增加
                    .setBatchId(detail.getBatchId())
                    .setWarehouseId(detail.getWarehouseId()).setLocationId(detail.getLocationId()).setAreaId(detail.getAreaId())
                    .setBizType(MesBizTypeConstants.WM_PRODUCT_RECPT).setBizId(receipt.getId())
                    .setBizCode(receipt.getCode()).setBizLineId(detail.getLineId())
                    .setRelatedTransactionId(outTransactionId)); // 关联出库事务
            createInTransaction(receipt, detail, outTransactionId);
        }
    }
    private void createInTransaction(MesWmProductReceiptDO receipt, MesWmProductReceiptDetailDO detail,
                                     Long relatedTransactionId) {
        wmTransactionService.createTransaction(new MesWmTransactionSaveReqDTO()
                .setType(MesWmTransactionTypeEnum.IN.getType()).setItemId(detail.getItemId())
                .setQuantity(detail.getQuantity()) // 库存增加
                .setBatchId(detail.getBatchId())
                .setWarehouseId(detail.getWarehouseId()).setLocationId(detail.getLocationId()).setAreaId(detail.getAreaId())
                .setBizType(MesBizTypeConstants.WM_PRODUCT_RECPT).setBizId(receipt.getId())
                .setBizCode(receipt.getCode()).setBizLineId(detail.getLineId())
                .setRelatedTransactionId(relatedTransactionId)); // 关联出库事务
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelProductReceipt(Long id) {