2 天以前 1ba0dbfde5634c8bf2ec20891d6b226b996f6b59
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
package cn.iocoder.yudao.module.mes.service.wm.materialstockbag;
 
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterialStockDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstockbag.MesWmMaterialStockBagDO;
import cn.iocoder.yudao.module.mes.dal.mysql.wm.materialstockbag.MesWmMaterialStockBagMapper;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmMaterialStockBagStatusEnum;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmStockStateEnum;
import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
 
import java.math.BigDecimal;
import java.time.LocalDateTime;
 
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
 
/**
 * MES 库存袋码明细 Service 实现类
 *
 * 袋码明细生命周期:首次入库建立(ACTIVE)→ 移库/质检迁移位置状态 → 出库/报废结束(OUT/SCRAPPED)。
 * 唯一约束 bag_code 保证每个袋码仅一条明细,复用同一行完成全生命周期流转。
 */
@Service
@Validated
public class MesWmMaterialStockBagServiceImpl implements MesWmMaterialStockBagService {
 
    @Resource
    private MesWmMaterialStockBagMapper materialStockBagMapper;
 
    @Override
    @Transactional(rollbackFor = Exception.class)
    public MesWmMaterialStockBagDO inbound(MesWmTransactionSaveReqDTO reqDTO, MesWmMaterialStockDO stock, Long transactionId) {
        String bagCode = reqDTO.getBagCode();
        if (StrUtil.isBlank(bagCode)) {
            throw exception(WM_MATERIAL_STOCK_BAG_REQUIRED);
        }
        // 1. 查询该袋码既有明细:优先按 bagCodeId,其次按 bagCode
        MesWmMaterialStockBagDO existing = materialStockBagMapper.selectByBagCode(bagCode);
        if (existing == null && reqDTO.getBagCodeId() != null) {
            existing = materialStockBagMapper.selectByBagCodeId(reqDTO.getBagCodeId());
        }
        if (existing != null) {
            // 2. 已在库 → 重复入库,禁止
            if (MesWmMaterialStockBagStatusEnum.ACTIVE.getStatus().equals(existing.getStatus())) {
                throw exception(WM_MATERIAL_STOCK_BAG_DUPLICATE, bagCode);
            }
            // 3. 已出库/报废 → 重新激活并更新位置
            return reactivate(existing, reqDTO, stock, transactionId);
        }
        // 4. 首次入库 → 新建明细
        MesWmMaterialStockBagDO bag = MesWmMaterialStockBagDO.builder()
                .materialStockId(stock.getId())
                .bagCodeId(reqDTO.getBagCodeId())
                .bagCode(bagCode)
                .itemId(stock.getItemId())
                .batchId(stock.getBatchId())
                .batchCode(stock.getBatchCode())
                .vendorId(stock.getVendorId())
                .warehouseId(stock.getWarehouseId())
                .locationId(stock.getLocationId())
                .areaId(stock.getAreaId())
                .stockState(ObjUtil.defaultIfNull(reqDTO.getStockState(), stock.getStockState()))
                .status(MesWmMaterialStockBagStatusEnum.ACTIVE.getStatus())
                .quantity(BigDecimal.ONE)
                .frozen(false)
                .lastTransactionId(transactionId)
                .inboundTime(LocalDateTime.now())
                .build();
        materialStockBagMapper.insert(bag);
        return bag;
    }
 
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void outbound(MesWmTransactionSaveReqDTO reqDTO, Long transactionId) {
        MesWmMaterialStockBagDO bag = requireActiveBag(reqDTO);
        MesWmMaterialStockBagDO update = new MesWmMaterialStockBagDO();
        update.setId(bag.getId());
        update.setStatus(MesWmMaterialStockBagStatusEnum.OUT.getStatus());
        update.setStockState(MesWmStockStateEnum.OUT.getState());
        update.setLastTransactionId(transactionId);
        update.setOutboundTime(LocalDateTime.now());
        materialStockBagMapper.updateById(update);
    }
 
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void moveIn(MesWmTransactionSaveReqDTO reqDTO, MesWmMaterialStockDO targetStock, Long transactionId) {
        MesWmMaterialStockBagDO bag = requireActiveBag(reqDTO);
        MesWmMaterialStockBagDO update = new MesWmMaterialStockBagDO();
        update.setId(bag.getId());
        update.setMaterialStockId(targetStock.getId());
        update.setWarehouseId(targetStock.getWarehouseId());
        update.setLocationId(targetStock.getLocationId());
        update.setAreaId(targetStock.getAreaId());
        update.setStockState(ObjUtil.defaultIfNull(reqDTO.getStockState(), targetStock.getStockState()));
        update.setLastTransactionId(transactionId);
        materialStockBagMapper.updateById(update);
    }
 
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void scrap(MesWmTransactionSaveReqDTO reqDTO, Long transactionId) {
        MesWmMaterialStockBagDO bag = requireActiveBag(reqDTO);
        MesWmMaterialStockBagDO update = new MesWmMaterialStockBagDO();
        update.setId(bag.getId());
        update.setStatus(MesWmMaterialStockBagStatusEnum.SCRAPPED.getStatus());
        update.setStockState(MesWmStockStateEnum.OUT.getState());
        update.setQuantity(BigDecimal.ZERO);
        update.setLastTransactionId(transactionId);
        update.setOutboundTime(LocalDateTime.now());
        materialStockBagMapper.updateById(update);
    }
 
    @Override
    public MesWmMaterialStockBagDO getActiveByMaterialStockId(Long materialStockId) {
        return materialStockBagMapper.selectOneActiveByMaterialStockId(materialStockId);
    }
 
    /**
     * 查询指定袋码的活动明细,不存在或已出库/报废则抛异常。
     */
    private MesWmMaterialStockBagDO requireActiveBag(MesWmTransactionSaveReqDTO reqDTO) {
        MesWmMaterialStockBagDO bag = null;
        if (reqDTO.getBagCodeId() != null) {
            bag = materialStockBagMapper.selectActiveByBagCodeId(reqDTO.getBagCodeId());
        }
        if (bag == null && StrUtil.isNotBlank(reqDTO.getBagCode())) {
            bag = materialStockBagMapper.selectActiveByBagCode(reqDTO.getBagCode());
        }
        if (bag == null) {
            String code = StrUtil.blankToDefault(reqDTO.getBagCode(), String.valueOf(reqDTO.getBagCodeId()));
            throw exception(WM_MATERIAL_STOCK_BAG_NOT_ACTIVE, code);
        }
        return bag;
    }
 
    private MesWmMaterialStockBagDO reactivate(MesWmMaterialStockBagDO existing, MesWmTransactionSaveReqDTO reqDTO,
                                               MesWmMaterialStockDO stock, Long transactionId) {
        MesWmMaterialStockBagDO update = new MesWmMaterialStockBagDO();
        update.setId(existing.getId());
        update.setMaterialStockId(stock.getId());
        update.setItemId(stock.getItemId());
        update.setBatchId(stock.getBatchId());
        update.setBatchCode(stock.getBatchCode());
        update.setVendorId(stock.getVendorId());
        update.setWarehouseId(stock.getWarehouseId());
        update.setLocationId(stock.getLocationId());
        update.setAreaId(stock.getAreaId());
        update.setStockState(ObjUtil.defaultIfNull(reqDTO.getStockState(), stock.getStockState()));
        update.setStatus(MesWmMaterialStockBagStatusEnum.ACTIVE.getStatus());
        update.setQuantity(BigDecimal.ONE);
        update.setFrozen(false);
        update.setLastTransactionId(transactionId);
        update.setInboundTime(LocalDateTime.now());
        update.setOutboundTime(null);
        materialStockBagMapper.updateById(update);
        return existing;
    }
}