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;
|
}
|
}
|