liu
8 天以前 06c312e584b99df613728d5ea8b32df4e156f3cc
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnvendor/MesWmReturnVendorLineServiceImpl.java
@@ -1,9 +1,18 @@
package cn.iocoder.yudao.module.mes.service.wm.returnvendor;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.erp.api.purchase.ErpPurchaseOrderApi;
import cn.iocoder.yudao.module.erp.api.purchase.dto.ErpPurchaseOrderItemRespDTO;
import cn.iocoder.yudao.module.erp.api.purchase.dto.ErpPurchaseOrderRespDTO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.returnvendor.vo.line.MesWmReturnVendorLinePageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.returnvendor.vo.line.MesWmReturnVendorLineSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.mdm.MesMdmItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnvendor.MesWmReturnVendorLineDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnvendor.MesWmReturnVendorDO;
import cn.iocoder.yudao.module.mes.dal.mysql.wm.returnvendor.MesWmReturnVendorLineMapper;
@@ -16,8 +25,10 @@
import org.springframework.validation.annotation.Validated;
import java.util.List;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_RETURN_VENDOR_ITEM_NOT_IN_PURCHASE_ORDER;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_RETURN_VENDOR_LINE_NOT_EXISTS;
/**
@@ -40,11 +51,15 @@
    private MesMdmItemService mdmItemService;
    @Resource
    private MesWmBatchService batchService;
    @Resource
    private ErpPurchaseOrderApi purchaseOrderApi;
    @Override
    public Long createReturnVendorLine(MesWmReturnVendorLineSaveReqVO createReqVO) {
        // 校验数据
        validateReturnVendorLineSaveData(createReqVO);
        MesWmReturnVendorDO returnVendor = validateReturnVendorLineSaveData(createReqVO);
        // 退货单若已关联采购订单,物料必须属于该采购订单,禁止跨订单随意选料
        validateReturnVendorItemInPurchaseOrder(returnVendor, createReqVO.getItemId(), null);
        // 插入
        MesWmReturnVendorLineDO line = BeanUtils.toBean(createReqVO, MesWmReturnVendorLineDO.class);
@@ -59,7 +74,9 @@
        // 固定父单 ID,防止通过接口篡改
        updateReqVO.setReturnId(oldLine.getReturnId());
        // 校验数据
        validateReturnVendorLineSaveData(updateReqVO);
        MesWmReturnVendorDO returnVendor = validateReturnVendorLineSaveData(updateReqVO);
        // 关联采购订单时,不允许把行物料改成订单外的物料;仅改数量等操作不受影响
        validateReturnVendorItemInPurchaseOrder(returnVendor, updateReqVO.getItemId(), oldLine.getItemId());
        // 更新
        MesWmReturnVendorLineDO updateObj = BeanUtils.toBean(updateReqVO, MesWmReturnVendorLineDO.class);
@@ -109,7 +126,7 @@
        return line;
    }
    private void validateReturnVendorLineSaveData(MesWmReturnVendorLineSaveReqVO reqVO) {
    private MesWmReturnVendorDO validateReturnVendorLineSaveData(MesWmReturnVendorLineSaveReqVO reqVO) {
        // 校验父数据存在且为草稿状态
        MesWmReturnVendorDO returnVendor = returnVendorService.validateReturnVendorExistsAndPrepare(reqVO.getReturnId());
        // 校验物料存在
@@ -118,6 +135,64 @@
        if (reqVO.getBatchId() != null) {
            batchService.validateBatchExists(reqVO.getBatchId(), reqVO.getItemId(), null, returnVendor.getVendorId());
        }
        return returnVendor;
    }
    /**
     * 校验退货行物料属于所关联采购订单(退货单已关联采购订单时)。
     *
     * 采购退货的物料应取自关联采购订单,此处为后端兜底,防止通过接口跨订单随意选料。
     * 物料与带入逻辑保持一致:以采购订单明细的产品 ID 作为物料集合。
     *
     * @param oldItemId 修改前物料 ID;为空表示新增。物料未变化时(仅改数量等)不重复校验
     */
    private void validateReturnVendorItemInPurchaseOrder(MesWmReturnVendorDO returnVendor, Long itemId, Long oldItemId) {
        Long purchaseOrderId = returnVendor.getPurchaseOrderId();
        if (purchaseOrderId == null) {
            return;
        }
        // 编辑行时物料未变(只改数量/批次等),无需校验
        if (ObjUtil.equal(itemId, oldItemId)) {
            return;
        }
        List<ErpPurchaseOrderItemRespDTO> poItems = purchaseOrderApi
                .getPurchaseOrderItemList(purchaseOrderId).getCheckedData();
        if (CollUtil.isEmpty(poItems)) {
            // 订单无明细时由"带入物料"流程统一给出提示,此处不拦截
            return;
        }
        Set<Long> poProductIds = CollectionUtils.convertSet(poItems, ErpPurchaseOrderItemRespDTO::getProductId);
        if (poProductIds.contains(itemId)) {
            return;
        }
        throw new ServiceException(WM_RETURN_VENDOR_ITEM_NOT_IN_PURCHASE_ORDER.getCode(),
                String.format("退货单[%s]已关联采购订单[%s],退货物料%s不在该采购订单明细中。"
                                + "退货只能针对该采购订单的物料,请选择订单内的物料并仅调整退货数量;"
                                + "如需退回订单外物料,请先解除当前采购订单关联。",
                        returnVendor.getCode(), resolvePurchaseOrderNo(purchaseOrderId), resolveItemDesc(itemId)));
    }
    /**
     * 解析采购订单号,解析失败时回退为可读 ID
     */
    private String resolvePurchaseOrderNo(Long purchaseOrderId) {
        ErpPurchaseOrderRespDTO purchaseOrder = purchaseOrderApi.getPurchaseOrder(purchaseOrderId).getCheckedData();
        if (purchaseOrder != null && StrUtil.isNotBlank(purchaseOrder.getNo())) {
            return purchaseOrder.getNo();
        }
        return "ID=" + purchaseOrderId;
    }
    /**
     * 解析物料可读描述(编码+名称),解析失败时回退为可读 ID
     */
    private String resolveItemDesc(Long itemId) {
        MesMdmItemDO item = mdmItemService.getItem(itemId);
        if (item == null) {
            return "ID=" + itemId;
        }
        String name = StrUtil.nullToDefault(item.getName(), "");
        return (StrUtil.isNotBlank(item.getCode()) ? "[" + item.getCode() + "]" : "[ID=" + itemId + "]") + name;
    }
}