liu
昨天 1eb432710b7a3a2e820be10b17c0138b377ee1b8
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/mps/MesProMpsServiceImpl.java
@@ -22,6 +22,7 @@
import cn.iocoder.yudao.module.mes.enums.pro.MesProMpsStatusEnum;
import cn.iocoder.yudao.module.mes.enums.pro.MesProFeedbackStatusEnum;
import cn.iocoder.yudao.module.mes.enums.pro.MesProWorkOrderSourceTypeEnum;
import cn.iocoder.yudao.module.mes.enums.pro.MesProWorkOrderStatusEnum;
import cn.iocoder.yudao.module.mes.service.md.autocode.MesMdAutoCodeRecordService;
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductService;
@@ -40,10 +41,12 @@
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
@@ -51,7 +54,7 @@
/**
 * MES 生产主计划(MPS)Service 实现类
 *
 * @author 芋道源码
 * @author 超级管理员
 */
@Service
@Validated
@@ -280,14 +283,26 @@
            throw exception(PRO_MPS_NOT_ISSUED);
        }
        // 级联取消关联的生产工单
        // 级联取消关联的生产工单(跟随工单当前状态,且不误伤合并下发的共享工单)
        if (mps.getWorkOrderId() != null) {
            try {
                workOrderService.cancelWorkOrder(mps.getWorkOrderId());
                log.info("[cancelMps] 级联取消生产工单,workOrderId={}", mps.getWorkOrderId());
            } catch (Exception e) {
                // 工单可能已完成或已取消,记录日志但不阻止 MPS 取消
                log.warn("[cancelMps] 取消生产工单失败,workOrderId={},原因:{}", mps.getWorkOrderId(), e.getMessage());
            // 1. 统计该工单被多少条 MPS 引用:合并下发的多条 MPS 共享同一个工单
            Long refCount = mpsMapper.selectCount(MesProMpsDO::getWorkOrderId, mps.getWorkOrderId());
            if (refCount != null && refCount > 1) {
                // 共享工单:只取消当前 MPS,不动工单
                log.info("[cancelMps] 生产工单({}) 被 {} 条 MPS 共用,跳过级联取消工单",
                        mps.getWorkOrderId(), refCount);
            } else {
                MesProWorkOrderDO workOrder = workOrderService.getWorkOrder(mps.getWorkOrderId());
                if (workOrder != null
                        && MesProWorkOrderStatusEnum.CONFIRMED.getStatus().equals(workOrder.getStatus())) {
                    // 只有"已确认"状态的工单才需要级联取消
                    workOrderService.cancelWorkOrder(mps.getWorkOrderId());
                    log.info("[cancelMps] 级联取消生产工单,workOrderId={}", mps.getWorkOrderId());
                } else {
                    // 工单已是草稿/已完成/已取消,无需级联取消,直接取消 MPS
                    log.info("[cancelMps] 生产工单({}) 状态为 {},无需级联取消",
                            mps.getWorkOrderId(), workOrder != null ? workOrder.getStatus() : "null");
                }
            }
        }
@@ -422,6 +437,10 @@
     * 判断单个 MPS 是否完成
     */
    private boolean isMpsFinished(MesProMpsDO mps) {
        // 已取消的 MPS 视为终态,不再阻塞生产完成判断(与 checkCanCreateSalesNotice 的口径保持一致)
        if (MesProMpsStatusEnum.CANCELED.getStatus().equals(mps.getStatus())) {
            return true;
        }
        // 1. 已生产数量 >= 计划数量
        BigDecimal quantityProduced = ObjUtil.defaultIfNull(mps.getQuantityProduced(), BigDecimal.ZERO);
        if (quantityProduced.compareTo(mps.getQuantity()) < 0) {
@@ -552,7 +571,11 @@
            process.setKeyFlag(rp.getKeyFlag());
            process.setCheckFlag(rp.getCheckFlag());
            process.setBackflushFlag(rp.getBackflushFlag());
            process.setOutputItemId(rp.getOutputItemId());
            if (rp.getOutputItemId() != null && !rp.getOutputItemId().isBlank()) {
                process.setOutputItemIds(Arrays.stream(rp.getOutputItemId().split(","))
                        .map(String::trim).filter(s -> !s.isEmpty())
                        .map(Long::valueOf).collect(Collectors.toList()));
            }
            process.setColorCode(rp.getColorCode());
            // 带入工序投料BOM
            List<MesProRouteProductBomDO> routeBomItems = bomMap != null ? bomMap.get(rp.getProcessId()) : null;