liyong
4 天以前 0f792c9b3b88dc4b1b7b306f89e257d931d2d0bd
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
package cn.iocoder.yudao.module.mes.service.wm.returnissue;
 
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.mes.controller.admin.wm.returnissue.vo.detail.MesWmReturnIssueDetailSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnissue.MesWmReturnIssueDetailDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnissue.MesWmReturnIssueLineDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterialStockDO;
import cn.iocoder.yudao.module.mes.dal.mysql.wm.returnissue.MesWmReturnIssueDetailMapper;
import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService;
import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
 
import java.math.BigDecimal;
import java.util.List;
 
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
 
/**
 * MES 生产退料明细 Service 实现类
 */
@Service
@Validated
public class MesWmReturnIssueDetailServiceImpl implements MesWmReturnIssueDetailService {
 
    @Resource
    private MesWmReturnIssueDetailMapper issueDetailMapper;
 
    @Resource
    @Lazy
    private MesWmReturnIssueLineService issueLineService;
    @Resource
    private MesWmWarehouseAreaService warehouseAreaService;
    @Resource
    @Lazy
    private MesWmMaterialStockService materialStockService;
 
    @Override
    public Long createReturnIssueDetail(MesWmReturnIssueDetailSaveReqVO createReqVO) {
        validateReturnIssueDetailSaveData(createReqVO);
 
        // 插入
        MesWmReturnIssueDetailDO detail = BeanUtils.toBean(createReqVO, MesWmReturnIssueDetailDO.class);
        issueDetailMapper.insert(detail);
        return detail.getId();
    }
 
    @Override
    public void updateReturnIssueDetail(MesWmReturnIssueDetailSaveReqVO updateReqVO) {
        // 校验存在
        validateReturnIssueDetailExists(updateReqVO.getId());
        validateReturnIssueDetailSaveData(updateReqVO);
 
        // 更新
        MesWmReturnIssueDetailDO updateObj = BeanUtils.toBean(updateReqVO, MesWmReturnIssueDetailDO.class);
        issueDetailMapper.updateById(updateObj);
    }
 
    @Override
    public void deleteReturnIssueDetail(Long id) {
        // 校验存在
        validateReturnIssueDetailExists(id);
        // 删除
        issueDetailMapper.deleteById(id);
    }
 
    @Override
    public MesWmReturnIssueDetailDO getReturnIssueDetail(Long id) {
        return issueDetailMapper.selectById(id);
    }
 
    @Override
    public List<MesWmReturnIssueDetailDO> getReturnIssueDetailListByLineId(Long lineId) {
        return issueDetailMapper.selectListByLineId(lineId);
    }
 
    @Override
    public List<MesWmReturnIssueDetailDO> getReturnIssueDetailListByIssueId(Long issueId) {
        return issueDetailMapper.selectListByIssueId(issueId);
    }
 
    @Override
    public void deleteReturnIssueDetailByIssueId(Long issueId) {
        issueDetailMapper.deleteByIssueId(issueId);
    }
 
    @Override
    public void deleteReturnIssueDetailByLineId(Long lineId) {
        issueDetailMapper.deleteByLineId(lineId);
    }
 
    private void validateReturnIssueDetailExists(Long id) {
        if (issueDetailMapper.selectById(id) == null) {
            throw exception(WM_RETURN_ISSUE_DETAIL_NOT_EXISTS);
        }
    }
 
    /**
     * 校验保存时的关联数据
     */
    private void validateReturnIssueDetailSaveData(MesWmReturnIssueDetailSaveReqVO reqVO) {
        // 校验父数据存在
        MesWmReturnIssueLineDO line = issueLineService.validateReturnIssueLineExists(reqVO.getLineId());
        if (ObjUtil.notEqual(line.getIssueId(), reqVO.getIssueId())) {
            throw exception(WM_RETURN_ISSUE_DETAIL_LINE_NOT_MATCH);
        }
        if (ObjUtil.notEqual(line.getItemId(), reqVO.getItemId())) {
            throw exception(WM_RETURN_ISSUE_DETAIL_ITEM_MISMATCH);
        }
        // 校验上架目标仓库、库区、库位的关联关系
        warehouseAreaService.validateWarehouseAreaExists(
                reqVO.getWarehouseId(), reqVO.getLocationId(), reqVO.getAreaId());
        // 校验上架目标库位的物料/批次混放规则
        materialStockService.checkAreaMixingRule(reqVO.getAreaId(), reqVO.getItemId(), reqVO.getBatchId());
        // 校验来源库存记录(materialStockId 指向虚拟线边库,warehouseId 是上架目标仓库,两者仓库不同是正常的)
        if (reqVO.getMaterialStockId() != null) {
            MesWmMaterialStockDO stock = materialStockService.getMaterialStock(reqVO.getMaterialStockId());
            if (stock == null) {
                throw exception(WM_MATERIAL_STOCK_NOT_EXISTS);
            }
            // 校验物料和批次一致
            if (ObjUtil.notEqual(stock.getItemId(), reqVO.getItemId())) {
                throw exception(WM_MATERIAL_STOCK_SELECTION_MISMATCH);
            }
            // 校验库存数量充足
            if (reqVO.getQuantity() != null && stock.getQuantity() != null
                    && stock.getQuantity().compareTo(reqVO.getQuantity()) < 0) {
                throw exception(WM_MATERIAL_STOCK_INSUFFICIENT);
            }
        }
        // 校验明细总数量不超过行数量(排除自身)
        validateDetailQuantityNotExceed(reqVO.getLineId(), reqVO.getQuantity(), reqVO.getId(), line);
    }
 
    /**
     * 校验明细总数量不超过行数量
     *
     * @param lineId 行 ID
     * @param newQuantity 本次新增/修改的数量
     * @param excludeDetailId 排除的明细 ID(更新时排除自身,新增时传 null)
     * @param line 退料单行
     */
    private void validateDetailQuantityNotExceed(Long lineId, BigDecimal newQuantity,
                                                  Long excludeDetailId, MesWmReturnIssueLineDO line) {
        // 计算已有明细总数量(排除自身)
        List<MesWmReturnIssueDetailDO> details = issueDetailMapper.selectListByLineId(lineId);
        BigDecimal existingTotal = CollectionUtils.getSumValue(details,
                detail -> excludeDetailId != null && excludeDetailId.equals(detail.getId())
                        ? BigDecimal.ZERO : detail.getQuantity(),
                BigDecimal::add, BigDecimal.ZERO);
        // 校验:已有 + 本次 <= 行数量
        if (existingTotal.add(newQuantity).compareTo(line.getQuantity()) > 0) {
            throw exception(WM_RETURN_ISSUE_DETAIL_QUANTITY_EXCEED);
        }
    }
 
}