liu
8 天以前 06c312e584b99df613728d5ea8b32df4e156f3cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
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<MesWmReturnVendorLineDO> getReturnVendorLinePage(MesWmReturnVendorLinePageReqVO pageReqVO) {
        return returnVendorLineMapper.selectPage(pageReqVO);
    }
 
    @Override
    public List<MesWmReturnVendorLineDO> 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<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;
    }
 
}