云
9 天以前 bf5f52656e128f578ed42c7979c31cf4b476ce46
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteServiceImpl.java
@@ -13,6 +13,7 @@
import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRouteSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessOutputDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductBomDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
@@ -35,6 +36,7 @@
import java.util.Set;
import java.util.Comparator;
import java.util.Arrays;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.concurrent.atomic.AtomicInteger;
@@ -187,8 +189,13 @@
                .max(Comparator.comparing(MesProRouteProcessDO::getSort)).orElseThrow();
        for (MesProRouteProcessDO process : processList) {
            Set<Long> outputIds = parseOutputItemIds(process.getOutputItemId());
            boolean last = process.getId().equals(lastProcess.getId());
            if (CollUtil.isEmpty(outputIds)) {
                throw exception(PRO_ROUTE_PROCESS_OUTPUT_REQUIRED, process.getProcessId());
                // 中间工序不一定形成可入库的独立半成品,允许不配置产出;末道工序必须产出成品。
                if (last) {
                    throw exception(PRO_ROUTE_LAST_OUTPUT_INVALID, process.getProcessId());
                }
                continue;
            }
            for (Long outputId : outputIds) {
                if (!boundItemIds.contains(outputId)) {
@@ -196,12 +203,26 @@
                    throw exception(PRO_ROUTE_PROCESS_OUTPUT_NOT_BOUND, item.getName());
                }
            }
            if (process.getId().equals(lastProcess.getId())) {
            if (last) {
                if (finishedItemIds.isEmpty() || !outputIds.equals(finishedItemIds)) {
                    throw exception(PRO_ROUTE_LAST_OUTPUT_INVALID, process.getProcessId());
                }
            } else if (outputIds.stream().anyMatch(itemId -> !isItemUnderCategory(itemId, "SF"))) {
                throw exception(PRO_ROUTE_MIDDLE_OUTPUT_INVALID, process.getProcessId());
            }
        }
        // 4. 末道工序成品产出必须按产品归属,避免“通用”产出串到其它产品的工单
        List<MesProRouteProcessOutputDO> lastOutputs = routeProcessService
                .getRouteProcessOutputListByRouteProcessIds(Collections.singletonList(lastProcess.getId()));
        if (CollUtil.isNotEmpty(lastOutputs)) {
            boolean attributionInvalid = lastOutputs.stream().anyMatch(output ->
                    output.getProductId() == null || !output.getProductId().equals(output.getItemId()));
            Set<Long> attributedProducts = lastOutputs.stream()
                    .map(MesProRouteProcessOutputDO::getProductId)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toSet());
            if (attributionInvalid || !attributedProducts.containsAll(finishedItemIds)) {
                throw exception(PRO_ROUTE_LAST_OUTPUT_ATTRIBUTION_REQUIRED, lastProcess.getProcessId());
            }
        }
    }
@@ -295,6 +316,10 @@
                routeMapper.insert(route);
                respVO.getCreateCodes().add(importItem.getCode());
            } else if (updateSupport) {
                if (CommonStatusEnum.ENABLE.getStatus().equals(existRoute.getStatus())) {
                    respVO.getFailureCodes().put(key, "路线已启用,请先停用后再更新");
                    return;
                }
                // 2.1.2 更新(状态由启用/禁用接口单独维护,导入不修改状态)
                MesProRouteDO updateObj = BeanUtils.toBean(importItem, MesProRouteDO.class);
                updateObj.setId(existRoute.getId());