liyong
5 天以前 274dd8b724dd4485658e1d2a3f825a007f625bd7
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
package cn.iocoder.yudao.module.mes.service.wm.transfer;
 
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.transfer.vo.detail.MesWmTransferDetailSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.transfer.MesWmTransferDetailDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.transfer.MesWmTransferLineDO;
import cn.iocoder.yudao.module.mes.dal.mysql.wm.transfer.MesWmTransferDetailMapper;
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.WM_TRANSFER_DETAIL_NOT_EXISTS;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_TRANSFER_DETAIL_QUANTITY_EXCEED;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_TRANSFER_DETAIL_MIXED_GOODS;
 
/**
 * MES 调拨明细 Service 实现类
 */
@Service
@Validated
public class MesWmTransferDetailServiceImpl implements MesWmTransferDetailService {
 
    @Resource
    private MesWmTransferDetailMapper transferDetailMapper;
 
    @Resource
    @Lazy
    private MesWmTransferService transferService;
    @Resource
    @Lazy
    private MesWmTransferLineService transferLineService;
    @Resource
    private MesWmWarehouseAreaService warehouseAreaService;
 
    @Override
    public Long createTransferDetail(MesWmTransferDetailSaveReqVO createReqVO) {
        // 校验
        validateDetailSaveData(createReqVO, null);
 
        // 插入
        MesWmTransferDetailDO detail = BeanUtils.toBean(createReqVO, MesWmTransferDetailDO.class);
        transferDetailMapper.insert(detail);
        return detail.getId();
    }
 
    @Override
    public void updateTransferDetail(MesWmTransferDetailSaveReqVO updateReqVO) {
        // 校验存在
        validateTransferDetailExists(updateReqVO.getId());
        // 校验
        validateDetailSaveData(updateReqVO, updateReqVO.getId());
 
        // 更新
        MesWmTransferDetailDO updateObj = BeanUtils.toBean(updateReqVO, MesWmTransferDetailDO.class);
        transferDetailMapper.updateById(updateObj);
    }
 
    @Override
    public void deleteTransferDetail(Long id) {
        // 校验存在
        MesWmTransferDetailDO detail = transferDetailMapper.selectById(id);
        if (detail == null) {
            throw exception(WM_TRANSFER_DETAIL_NOT_EXISTS);
        }
        // 校验父转移单可编辑(通过 line 找到 transfer)
        MesWmTransferLineDO line = transferLineService.validateTransferLineExists(detail.getLineId());
        transferService.validateTransferEditable(line.getTransferId());
        // 删除
        transferDetailMapper.deleteById(id);
    }
 
    @Override
    public MesWmTransferDetailDO getTransferDetail(Long id) {
        return transferDetailMapper.selectById(id);
    }
 
    @Override
    public List<MesWmTransferDetailDO> getTransferDetailListByLineId(Long lineId) {
        return transferDetailMapper.selectListByLineId(lineId);
    }
 
    @Override
    public List<MesWmTransferDetailDO> getTransferDetailListByTransferId(Long transferId) {
        return transferDetailMapper.selectListByTransferId(transferId);
    }
 
    @Override
    public void deleteTransferDetailByTransferId(Long transferId) {
        transferDetailMapper.deleteByTransferId(transferId);
    }
 
    @Override
    public void deleteTransferDetailByLineId(Long lineId) {
        transferDetailMapper.deleteByLineId(lineId);
    }
 
    private void validateTransferDetailExists(Long id) {
        if (transferDetailMapper.selectById(id) == null) {
            throw exception(WM_TRANSFER_DETAIL_NOT_EXISTS);
        }
    }
 
    /**
     * 校验明细保存数据
     *
     * @param reqVO 保存请求
     * @param excludeDetailId 排除的明细 ID(更新时排除自身,新增时传 null)
     */
    private void validateDetailSaveData(MesWmTransferDetailSaveReqVO reqVO, Long excludeDetailId) {
        // 校验父数据存在
        MesWmTransferLineDO line = transferLineService.validateTransferLineExists(reqVO.getLineId());
        // 校验仓库、库区、库位的关联关系
        warehouseAreaService.validateWarehouseAreaExists(
                reqVO.getToWarehouseId(), reqVO.getToLocationId(), reqVO.getToAreaId());
        // 校验明细总数量不超过行数量
        validateDetailQuantityNotExceed(reqVO.getLineId(), reqVO.getQuantity(), excludeDetailId, line);
        // 校验不能混货:同一个目标仓位下,不能存在不同物料的明细
        validateNoMixedGoods(reqVO.getLineId(), reqVO.getToWarehouseId(),
                reqVO.getToLocationId(), reqVO.getToAreaId(), line.getItemId(), excludeDetailId);
    }
 
    /**
     * 校验明细总数量不超过行数量
     *
     * @param lineId 行 ID
     * @param newQuantity 本次新增/修改的数量
     * @param excludeDetailId 排除的明细 ID(更新时排除自身,新增时传 null)
     * @param line 调拨单行
     */
    private void validateDetailQuantityNotExceed(Long lineId, BigDecimal newQuantity,
                                                 Long excludeDetailId, MesWmTransferLineDO line) {
        // 计算已有明细总数量(排除自身)
        List<MesWmTransferDetailDO> details = transferDetailMapper.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_TRANSFER_DETAIL_QUANTITY_EXCEED);
        }
    }
 
    /**
     * 校验不能混货:同一转移单下,同一个目标仓位(仓库+库区+库位)只能放同一个物料
     *
     * @param lineId 当前行 ID(用于排除同行明细,因为同行物料相同)
     * @param toWarehouseId 目标仓库 ID
     * @param toLocationId 目标库区 ID
     * @param toAreaId 目标库位 ID
     * @param itemId 当前物料 ID
     * @param excludeDetailId 排除的明细 ID(更新时排除自身)
     */
    private void validateNoMixedGoods(Long lineId, Long toWarehouseId, Long toLocationId,
                                      Long toAreaId, Long itemId, Long excludeDetailId) {
        // 1. 获取当前转移单下的所有明细
        // 说明:因为无法直接从参数拿到 transferId,所以先通过 lineId 查询当前行,进而获取 transferId 和所有明细
        MesWmTransferLineDO currentLine = transferLineService.validateTransferLineExists(lineId);
        List<MesWmTransferDetailDO> allDetails = transferDetailMapper.selectListByTransferId(currentLine.getTransferId());
 
        // 2. 遍历所有明细,校验同一个目标仓位不能有不同的物料
        for (MesWmTransferDetailDO detail : allDetails) {
            // 2.1 排除当前正在编辑的明细(更新时)
            if (excludeDetailId != null && excludeDetailId.equals(detail.getId())) {
                continue;
            }
            // 2.2 排除同行的明细(同行属于同一个转移单行,物料必定相同,无需校验)
            if (detail.getLineId().equals(lineId)) {
                continue;
            }
            // 2.3 目标仓位不同,则不会混货,直接跳过
            if (ObjUtil.notEqual(detail.getToWarehouseId(), toWarehouseId)
                    || ObjUtil.notEqual(detail.getToLocationId(), toLocationId)
                    || ObjUtil.notEqual(detail.getToAreaId(), toAreaId)) {
                continue;
            }
            // 2.4 重点:如果目标仓位相同,则必须保证物料也相同
            MesWmTransferLineDO otherLine = transferLineService.validateTransferLineExists(detail.getLineId());
            if (ObjUtil.notEqual(otherLine.getItemId(), itemId)) {
                throw exception(WM_TRANSFER_DETAIL_MIXED_GOODS);
            }
        }
    }
 
}