云
2026-10-27 ffca46b39d25439a06a27cafda2ec523a3fb67cf
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/feedback/MesProFeedbackServiceImpl.java
@@ -308,27 +308,10 @@
        // 1.3 校验任务未完成
        taskService.validateTaskNotFinished(feedback.getTaskId());
        // 2.1 查询工序的关键工序标识 + 检验标识(需在 uncheckQuantity 校验之前,因为质检工序允许 uncheckQuantity > 0)
        // 工序检验标识不再控制产出入库:所有新报工产出均需人工质检。
        MesProRouteProcessDO routeProcess = routeProcessService.getRouteProcessByRouteIdAndProcessId(
                feedback.getRouteId(), feedback.getProcessId());
        boolean keyFlag = routeProcess != null && Boolean.TRUE.equals(routeProcess.getKeyFlag());
        boolean checkFlag = routeProcess != null && Boolean.TRUE.equals(routeProcess.getCheckFlag());
        // 2.2 非质检工序:合格品/不良品数量由质检确定;本工序不经过质检,报工未填写时默认全部为合格品
        if (!checkFlag
                && feedback.getQualifiedQuantity() == null
                && feedback.getUnqualifiedQuantity() == null) {
            feedback.setQualifiedQuantity(feedback.getFeedbackQuantity());
            feedback.setUnqualifiedQuantity(BigDecimal.ZERO);
            feedbackMapper.updateById(new MesProFeedbackDO().setId(feedback.getId())
                    .setQualifiedQuantity(feedback.getFeedbackQuantity())
                    .setUnqualifiedQuantity(BigDecimal.ZERO));
        }
        // 2.3 非质检工序:仍有待检数量时不能审批(质检工序的 uncheckQuantity > 0 是正常状态,不做拦截)
        if (!checkFlag
                && feedback.getUncheckQuantity() != null
                && feedback.getUncheckQuantity().compareTo(BigDecimal.ZERO) > 0) {
            throw exception(PRO_FEEDBACK_UNCHECK_QUANTITY_EXISTS, feedback.getUncheckQuantity());
        }
        // 3. 物料消耗:倒冲工序按本工序投料 BOM 自动扣料;非倒冲工序跳过(物料走手工领料/投料)
        boolean backflushFlag = routeProcess != null && Boolean.TRUE.equals(routeProcess.getBackflushFlag());
@@ -347,10 +330,14 @@
            }
        }
        // 5. 先更新报工状态为已完成(确保 checkAndFinishMps 检查时当前报工已标记完成)
        // 报工与质检分开:报工审批完成即累计产量,质量数量由质检回填。
        feedbackMapper.updateById(new MesProFeedbackDO().setId(reqVO.getId())
                .setStatus(MesProFeedbackStatusEnum.FINISHED.getStatus())
                .setUncheckQuantity(BigDecimal.ZERO));
                .setStatus(MesProFeedbackStatusEnum.UNCHECK.getStatus())
                .setQualifiedQuantity(BigDecimal.ZERO)
                .setUnqualifiedQuantity(BigDecimal.ZERO)
                .setUncheckQuantity(feedback.getFeedbackQuantity()));
        feedback.setQualifiedQuantity(BigDecimal.ZERO).setUnqualifiedQuantity(BigDecimal.ZERO);
        productProduceService.generateProductProduce(feedback, true, reqVO);
        // 6. 根据是否需要检验,决定产出和入库方式
        // 6.2 调整 只要审批成功直接更新
@@ -365,23 +352,8 @@
                checkAndPublishProductionFinished(feedback.getWorkOrderId());
        }
        if (checkFlag) {
            // 6.1 需要检验:生成产出单(质量状态=待检验),更新报工状态为待检验,等质检完成回调后再入库
            // 完成时回调见:MesQcIpqcServiceImpl#finishIpqc → splitPendingAndFinishProduce + updateProFeedbackWhenIpqcFinish
            productProduceService.generateProductProduce(feedback, true, reqVO);
            feedbackMapper.updateById(new MesProFeedbackDO().setId(reqVO.getId())
                    .setStatus(MesProFeedbackStatusEnum.UNCHECK.getStatus()));
            equipmentService.afterApproved(feedback);
            return false;
        } else {
            // 6.2 无需检验:生成产出单(按合格/不合格拆行),直接完成入库
            MesWmProductProduceDO produce = productProduceService.generateProductProduce(feedback, false, reqVO);
            productProduceService.finishProductProduce(produce.getId());
        }
        equipmentService.afterApproved(feedback);
        return true; // 已完成
        return false; // 产出等待质检,报工产量已累计
    }
    /**
@@ -397,28 +369,9 @@
     * @param feedback 报工单
     */
    private void updateTaskAndWorkOrderByFeedback(MesProFeedbackDO feedback) {
        // 1. 查询该报工单关联的所有产出单行,按质量状态聚合数量
        BigDecimal qualifiedQty = BigDecimal.ZERO;
        BigDecimal unqualifiedQty = BigDecimal.ZERO;
        List<MesWmProductProduceLineDO> lines = produceLineService.getProductProduceLineListByFeedbackId(feedback.getId());
        for (MesWmProductProduceLineDO line : lines) {
            if (ObjUtil.equal(line.getQualityStatus(), MesWmQualityStatusEnum.FAIL.getStatus())) {
                unqualifiedQty = unqualifiedQty.add(line.getQuantity());
            }
            if (ObjUtil.equal(line.getQualityStatus(), MesWmQualityStatusEnum.PASS.getStatus())) {
                qualifiedQty = qualifiedQty.add(line.getQuantity());
            }
        }
        // 1.1 兜底:关键非质检工序审批时产出单行尚未生成,直接采用报工单上的合格/不良数量
        //     (非质检工序默认全部为合格品;质检工序的合格/不良在质检回调后按产出行聚合)
        if (CollUtil.isEmpty(lines)) {
            qualifiedQty = ObjectUtil.defaultIfNull(feedback.getQualifiedQuantity(), BigDecimal.ZERO);
            unqualifiedQty = ObjectUtil.defaultIfNull(feedback.getUnqualifiedQuantity(), BigDecimal.ZERO);
        }
        //   2. 更新任务的已生产/合格/不合格数量
        // 报工审批只累计产量;合格与不合格数量在质检完成时累计。
        taskService.updateProducedQuantity(feedback.getTaskId(),
                feedback.getFeedbackQuantity(), qualifiedQty, unqualifiedQty);
                feedback.getFeedbackQuantity(), BigDecimal.ZERO, BigDecimal.ZERO);
        // 3. 更新订单的已生产数量
        // 新增逻辑:只有在该笔订单最后一个工序才更新生产订单的已生产数量
        MesProWorkOrderProcessDO lastProcess = workOrderProcessService.getLastWorkOrderProcessByWorkOrderId(feedback.getWorkOrderId());
@@ -443,12 +396,16 @@
    private void checkAndPublishProductionFinished(Long workOrderId) {
        // 1. 获取工单关联的 MPS
        MesProMpsRespDTO mps = mpsApi.getMpsByWorkOrderId(workOrderId);
        if (mps == null || mps.getSaleOrderId() == null) {
        if (mps == null || mps.getSaleOrderId() == null
                || ObjUtil.equal(mps.getStatus(), cn.iocoder.yudao.module.mes.enums.pro.MesProMpsStatusEnum.FINISHED.getStatus())
                || ObjUtil.equal(mps.getStatus(), cn.iocoder.yudao.module.mes.enums.pro.MesProMpsStatusEnum.CANCELED.getStatus())) {
            return; // 非销售订单来源的工单,跳过
        }
        // 2. 检查并自动完成 MPS(如果满足条件)
        mpsApi.checkAndFinishMps(mps.getId());
        if (!Boolean.TRUE.equals(mpsApi.checkAndFinishMps(mps.getId()))) {
            return;
        }
        // 3. 检查销售订单所有生产是否完成
        if (mpsApi.checkSaleOrderProductionFinished(mps.getSaleOrderId())) {
@@ -515,7 +472,7 @@
            throw exception(PRO_FEEDBACK_ROUTE_PROCESS_INVALID);
        }
        // 4.1 校验数量:无论是否需要检验,报工只需填报工数量,且必须 > 0
        // 合格品/不良品数量由质检确定(非质检工序在审批时默认全部为合格品)
        // 合格品/不良品数量由质检确定。
        if (reqVO.getFeedbackQuantity() == null
                || reqVO.getFeedbackQuantity().compareTo(BigDecimal.ZERO) <= 0) {
            throw exception(PRO_FEEDBACK_QUANTITY_MUST_POSITIVE);
@@ -626,7 +583,7 @@
                .setItemId(task.getItemId())
                .setFeedbackQuantity(reqVO.getFeedbackQuantity())
                .setScheduledQuantity(task.getQuantity())
                .setFeedbackTime(LocalDateTime.now())
                .setFeedbackTime(reqVO.getFeedbackTime() != null ? reqVO.getFeedbackTime() : LocalDateTime.now())
                .setStatus(MesProFeedbackStatusEnum.APPROVING.getStatus())
                .setRemark(reqVO.getRemark());
        equipmentService.validateSubmission(feedback);
@@ -639,7 +596,8 @@
        BigDecimal produced = task.getProducedQuantity() == null ? BigDecimal.ZERO : task.getProducedQuantity();
        BigDecimal inTransit = BigDecimal.ZERO;
        for (MesProFeedbackDO fb : getFeedbackListByTaskId(task.getId())) {
            if (!ObjUtil.equal(fb.getStatus(), MesProFeedbackStatusEnum.FINISHED.getStatus())) {
            if (!ObjUtil.equal(fb.getStatus(), MesProFeedbackStatusEnum.FINISHED.getStatus())
                    && !ObjUtil.equal(fb.getStatus(), MesProFeedbackStatusEnum.UNCHECK.getStatus())) {
                inTransit = inTransit.add(fb.getFeedbackQuantity() == null ? BigDecimal.ZERO : fb.getFeedbackQuantity());
            }
        }
@@ -662,6 +620,7 @@
                MesProFeedbackQuickCreateReqVO singleReq = new MesProFeedbackQuickCreateReqVO();
                singleReq.setTaskId(taskId);
                singleReq.setFeedbackQuantity(reqVO.getFeedbackQuantity());
                singleReq.setFeedbackTime(reqVO.getFeedbackTime());
                singleReq.setRemark(reqVO.getRemark());
                MesProFeedbackQuickCreateRespVO resp = quickCreateFeedback(singleReq);
                results.add(new MesProFeedbackBatchCreateRespVO.BatchResult()
@@ -689,7 +648,10 @@
                                                BigDecimal qualifiedQty, BigDecimal unqualifiedQty,
                                                BigDecimal laborScrapQty, BigDecimal materialScrapQty, BigDecimal otherScrapQty) {
        // 1. 校验报工单存在且为待检验状态
        MesProFeedbackDO feedback = validateFeedbackExists(feedbackId);
        MesProFeedbackDO feedback = feedbackMapper.selectByIdForUpdate(feedbackId);
        if (feedback == null) {
            throw exception(PRO_FEEDBACK_NOT_EXISTS);
        }
        if (ObjUtil.notEqual(feedback.getStatus(), MesProFeedbackStatusEnum.UNCHECK.getStatus())) {
            throw exception(PRO_FEEDBACK_NOT_UNCHECK);
        }
@@ -707,8 +669,11 @@
                .setOtherScrapQuantity(otherScrapQty)
                .setStatus(MesProFeedbackStatusEnum.FINISHED.getStatus()));
        // 4. 更新任务/工单的已生产数量
        // 4. 产量已在报工审批时累计,此处只回填质量数量。
        feedback.setQualifiedQuantity(qualifiedQty).setUnqualifiedQuantity(unqualifiedQty);
        taskService.updateQualityQuantity(feedback.getTaskId(),
                ObjectUtil.defaultIfNull(qualifiedQty, BigDecimal.ZERO),
                ObjectUtil.defaultIfNull(unqualifiedQty, BigDecimal.ZERO));
        //5.检查并发布生产完成事件
        checkAndPublishProductionFinished(feedback.getWorkOrderId());
    }