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; import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService; import cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService; import jakarta.annotation.Resource; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; 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; /** * MES 供应商退货单行 Service 实现类 */ @Service @Validated public class MesWmReturnVendorLineServiceImpl implements MesWmReturnVendorLineService { @Resource private MesWmReturnVendorLineMapper returnVendorLineMapper; @Resource @Lazy private MesWmReturnVendorService returnVendorService; @Resource @Lazy private MesWmReturnVendorDetailService returnVendorDetailService; @Resource private MesMdmItemService mdmItemService; @Resource private MesWmBatchService batchService; @Resource private ErpPurchaseOrderApi purchaseOrderApi; @Override public Long createReturnVendorLine(MesWmReturnVendorLineSaveReqVO createReqVO) { // 校验数据 MesWmReturnVendorDO returnVendor = validateReturnVendorLineSaveData(createReqVO); // 退货单若已关联采购订单,物料必须属于该采购订单,禁止跨订单随意选料 validateReturnVendorItemInPurchaseOrder(returnVendor, createReqVO.getItemId(), null); // 插入 MesWmReturnVendorLineDO line = BeanUtils.toBean(createReqVO, MesWmReturnVendorLineDO.class); returnVendorLineMapper.insert(line); return line.getId(); } @Override public void updateReturnVendorLine(MesWmReturnVendorLineSaveReqVO updateReqVO) { // 校验存在 MesWmReturnVendorLineDO oldLine = validateReturnVendorLineExists(updateReqVO.getId()); // 固定父单 ID,防止通过接口篡改 updateReqVO.setReturnId(oldLine.getReturnId()); // 校验数据 MesWmReturnVendorDO returnVendor = validateReturnVendorLineSaveData(updateReqVO); // 关联采购订单时,不允许把行物料改成订单外的物料;仅改数量等操作不受影响 validateReturnVendorItemInPurchaseOrder(returnVendor, updateReqVO.getItemId(), oldLine.getItemId()); // 更新 MesWmReturnVendorLineDO updateObj = BeanUtils.toBean(updateReqVO, MesWmReturnVendorLineDO.class); returnVendorLineMapper.updateById(updateObj); } @Override @Transactional(rollbackFor = Exception.class) public void deleteReturnVendorLine(Long id) { // 1.1 校验存在 MesWmReturnVendorLineDO line = validateReturnVendorLineExists(id); // 1.2 校验父数据为草稿状态 returnVendorService.validateReturnVendorExistsAndPrepare(line.getReturnId()); // 2.1 级联删除该行下的明细 returnVendorDetailService.deleteReturnVendorDetailByLineId(id); // 2.2 删除行 returnVendorLineMapper.deleteById(id); } @Override public MesWmReturnVendorLineDO getReturnVendorLine(Long id) { return returnVendorLineMapper.selectById(id); } @Override public PageResult getReturnVendorLinePage(MesWmReturnVendorLinePageReqVO pageReqVO) { return returnVendorLineMapper.selectPage(pageReqVO); } @Override public List getReturnVendorLineListByReturnId(Long returnId) { return returnVendorLineMapper.selectListByReturnId(returnId); } @Override public void deleteReturnVendorLineByReturnId(Long returnId) { returnVendorLineMapper.deleteByReturnId(returnId); } @Override public MesWmReturnVendorLineDO validateReturnVendorLineExists(Long id) { MesWmReturnVendorLineDO line = returnVendorLineMapper.selectById(id); if (line == null) { throw exception(WM_RETURN_VENDOR_LINE_NOT_EXISTS); } return line; } private MesWmReturnVendorDO validateReturnVendorLineSaveData(MesWmReturnVendorLineSaveReqVO reqVO) { // 校验父数据存在且为草稿状态 MesWmReturnVendorDO returnVendor = returnVendorService.validateReturnVendorExistsAndPrepare(reqVO.getReturnId()); // 校验物料存在 mdmItemService.validateItemExistsAndEnable(reqVO.getItemId()); // 校验批次存在且属于当前物料和供应商 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 poItems = purchaseOrderApi .getPurchaseOrderItemList(purchaseOrderId).getCheckedData(); if (CollUtil.isEmpty(poItems)) { // 订单无明细时由"带入物料"流程统一给出提示,此处不拦截 return; } Set 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; } }