package cn.iocoder.yudao.module.mes.service.wm.sn;
|
|
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.util.IdUtil;
|
import cn.hutool.core.util.ObjUtil;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
import cn.iocoder.yudao.module.mes.controller.admin.wm.sn.vo.MesWmSnDetailRespVO;
|
import cn.iocoder.yudao.module.mes.controller.admin.wm.sn.vo.MesWmSnGenerateReqVO;
|
import cn.iocoder.yudao.module.mes.controller.admin.wm.sn.vo.MesWmSnGroupRespVO;
|
import cn.iocoder.yudao.module.mes.controller.admin.wm.sn.vo.MesWmSnInStockPageReqVO;
|
import cn.iocoder.yudao.module.mes.controller.admin.wm.sn.vo.MesWmSnInStockReqVO;
|
import cn.iocoder.yudao.module.mes.controller.admin.wm.sn.vo.MesWmSnOutStockReqVO;
|
import cn.iocoder.yudao.module.mes.controller.admin.wm.sn.vo.MesWmSnPageReqVO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.mdm.MesMdmItemDO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.sn.MesWmSnDO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.transaction.MesWmTransactionDO;
|
import cn.iocoder.yudao.module.mes.dal.mysql.wm.sn.MesWmSnMapper;
|
import cn.iocoder.yudao.module.mes.dal.mysql.wm.transaction.MesWmTransactionMapper;
|
import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
|
import cn.iocoder.yudao.module.mes.enums.md.autocode.MesMdAutoCodeRuleCodeEnum;
|
import cn.iocoder.yudao.module.mes.enums.wm.BarcodeBizTypeEnum;
|
import cn.iocoder.yudao.module.mes.enums.wm.MesWmSnStatusEnum;
|
import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum;
|
import cn.iocoder.yudao.module.mes.service.md.autocode.MesMdAutoCodeRecordService;
|
import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService;
|
import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService;
|
import cn.iocoder.yudao.module.mes.service.wm.barcode.MesWmBarcodeService;
|
import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService;
|
import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO;
|
import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService;
|
import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseLocationService;
|
import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseService;
|
import jakarta.annotation.Resource;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.validation.annotation.Validated;
|
|
import java.math.BigDecimal;
|
import java.util.ArrayList;
|
import java.util.List;
|
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
|
|
/**
|
* MES SN 码 Service 实现类
|
*
|
* @author 超级管理员
|
*/
|
@Service
|
@Validated
|
@Slf4j
|
public class MesWmSnServiceImpl implements MesWmSnService {
|
|
@Resource
|
private MesWmSnMapper snMapper;
|
@Resource
|
private MesWmTransactionMapper transactionMapper;
|
|
@Resource
|
private MesMdAutoCodeRecordService autoCodeRecordService;
|
@Resource
|
private MesMdmItemService mdmItemService;
|
@Resource
|
private MesProWorkOrderService workOrderService;
|
@Resource
|
private MesWmBarcodeService barcodeService;
|
@Resource
|
private MesWmTransactionService transactionService;
|
@Resource
|
private MesWmWarehouseService warehouseService;
|
@Resource
|
private MesWmWarehouseLocationService locationService;
|
@Resource
|
private MesWmWarehouseAreaService areaService;
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public void generateSnCodes(MesWmSnGenerateReqVO reqVO) {
|
// 校验物料是否存在
|
mdmItemService.validateItemExists(reqVO.getItemId());
|
// 校验工单是否存在
|
if (reqVO.getWorkOrderId() != null) {
|
workOrderService.validateWorkOrderExists(reqVO.getWorkOrderId());
|
}
|
|
List<MesWmSnDO> sns = new ArrayList<>(reqVO.getCount());
|
// 生成批次 UUID
|
String uuid = IdUtil.fastSimpleUUID();
|
// 批量生成 SN 码
|
for (int i = 0; i < reqVO.getCount(); i++) {
|
String snCode = autoCodeRecordService.generateAutoCode(MesMdAutoCodeRuleCodeEnum.WM_SN_CODE.getCode());
|
sns.add(new MesWmSnDO().setUuid(uuid).setCode(snCode)
|
.setItemId(reqVO.getItemId()).setBatchCode(reqVO.getBatchCode())
|
.setWorkOrderId(reqVO.getWorkOrderId()));
|
}
|
// 批量插入
|
snMapper.insertBatch(sns);
|
// 自动生成条码
|
sns.forEach(sn -> barcodeService.autoGenerateBarcode(BarcodeBizTypeEnum.SN.getValue(),
|
sn.getId(), sn.getCode(), sn.getCode()));
|
}
|
|
@Override
|
public PageResult<MesWmSnGroupRespVO> getSnGroupPage(MesWmSnPageReqVO reqVO) {
|
return snMapper.selectPageGroupByUuid(reqVO);
|
}
|
|
@Override
|
public List<MesWmSnDO> getSnListByUuid(String uuid) {
|
return snMapper.selectListByUuid(uuid);
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public void deleteSnByUuid(String uuid) {
|
List<MesWmSnDO> sns = snMapper.selectListByUuid(uuid);
|
if (CollUtil.isEmpty(sns)) {
|
return;
|
}
|
|
// 删除 SN 码关联的条码记录
|
barcodeService.deleteBarcodeByBizTypeAndBizIds(BarcodeBizTypeEnum.SN.getValue(),
|
convertList(sns, MesWmSnDO::getId));
|
// 删除 SN 码
|
snMapper.deleteByUuid(uuid);
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public void registerSnInStock(MesWmSnInStockReqVO reqVO) {
|
// 1. 校验 SN 存在且物料匹配
|
MesWmSnDO sn = validateSnExists(reqVO.getCode());
|
validateSnItemMatch(sn, reqVO.getItemId());
|
// 2. 校验物料启用序列号管理
|
MesMdmItemDO item = mdmItemService.validateItemExists(reqVO.getItemId());
|
if (!Boolean.TRUE.equals(item.getIsSerialManaged())) {
|
throw exception(WM_SN_ITEM_NOT_SERIAL);
|
}
|
// 3. 状态校验:已在库则拒绝重复登记
|
if (ObjUtil.equal(sn.getStatus(), MesWmSnStatusEnum.IN_STOCK.getStatus())) {
|
throw exception(WM_SN_ALREADY_IN_STOCK);
|
}
|
// 4. 校验库存位置存在(冻结与混放规则由事务内部兜底)
|
warehouseService.validateWarehouseExists(reqVO.getWarehouseId());
|
locationService.validateWarehouseLocationExists(reqVO.getLocationId());
|
areaService.validateWarehouseAreaExists(reqVO.getAreaId());
|
// 5. 创建单件入库事务(+1)
|
transactionService.createTransaction(new MesWmTransactionSaveReqDTO()
|
.setType(MesWmTransactionTypeEnum.IN.getType())
|
.setBizType(MesBizTypeConstants.WM_SN)
|
.setBizId(sn.getId()).setBizCode(sn.getCode()).setBizLineId(sn.getId())
|
.setItemId(sn.getItemId()).setQuantity(BigDecimal.ONE)
|
.setBatchId(sn.getBatchId()).setBatchCode(sn.getBatchCode())
|
.setSnCode(sn.getCode())
|
.setWarehouseId(reqVO.getWarehouseId()).setLocationId(reqVO.getLocationId()).setAreaId(reqVO.getAreaId()));
|
// 6. 更新 SN:在库 + 记录库位
|
snMapper.updateById(new MesWmSnDO().setId(sn.getId())
|
.setStatus(MesWmSnStatusEnum.IN_STOCK.getStatus())
|
.setWarehouseId(reqVO.getWarehouseId()).setLocationId(reqVO.getLocationId()).setAreaId(reqVO.getAreaId())
|
.setRemark(reqVO.getRemark()));
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public void outboundSn(MesWmSnOutStockReqVO reqVO) {
|
// 1. 校验 SN 存在且物料匹配
|
MesWmSnDO sn = validateSnExists(reqVO.getCode());
|
validateSnItemMatch(sn, reqVO.getItemId());
|
// 2. 状态校验:必须在库且记录过库位
|
if (!ObjUtil.equal(sn.getStatus(), MesWmSnStatusEnum.IN_STOCK.getStatus())
|
|| sn.getWarehouseId() == null || sn.getLocationId() == null || sn.getAreaId() == null) {
|
throw exception(WM_SN_NOT_IN_STOCK);
|
}
|
// 3. 创建单件出库事务(-1),按 SN 在库位置精确定位库存记录
|
transactionService.createTransaction(new MesWmTransactionSaveReqDTO()
|
.setType(MesWmTransactionTypeEnum.OUT.getType())
|
.setBizType(MesBizTypeConstants.WM_SN)
|
.setBizId(sn.getId()).setBizCode(sn.getCode()).setBizLineId(sn.getId())
|
.setItemId(sn.getItemId()).setQuantity(BigDecimal.ONE.negate())
|
.setBatchId(sn.getBatchId()).setBatchCode(sn.getBatchCode())
|
.setSnCode(sn.getCode())
|
.setWarehouseId(sn.getWarehouseId()).setLocationId(sn.getLocationId()).setAreaId(sn.getAreaId()));
|
// 4. 更新 SN:已出库 + 清空在库位置
|
snMapper.markOutStockAndClearLocation(sn.getId());
|
snMapper.updateById(new MesWmSnDO().setId(sn.getId()).setRemark(reqVO.getRemark()));
|
}
|
|
@Override
|
public MesWmSnDetailRespVO getSnDetail(String snCode) {
|
MesWmSnDO sn = validateSnExists(snCode);
|
MesWmSnDetailRespVO resp = BeanUtils.toBean(sn, MesWmSnDetailRespVO.class);
|
MesWmTransactionDO transaction = transactionMapper.selectLatestBySnCode(snCode);
|
if (transaction != null) {
|
MesWmTransactionTypeEnum typeEnum = MesWmTransactionTypeEnum.valueOf(transaction.getType());
|
resp.setLastTransactionType(transaction.getType());
|
resp.setLastTransactionTypeName(typeEnum != null ? typeEnum.getName() : null);
|
resp.setLastTransactionQuantity(transaction.getQuantity());
|
resp.setLastTransactionBizType(transaction.getBizType());
|
resp.setLastTransactionBizCode(transaction.getBizCode());
|
resp.setLastTransactionTime(transaction.getTransactionTime());
|
}
|
return resp;
|
}
|
|
@Override
|
public PageResult<MesWmSnDO> getSnInStockPage(MesWmSnInStockPageReqVO reqVO) {
|
return snMapper.selectStatusPage(reqVO);
|
}
|
|
private MesWmSnDO validateSnExists(String code) {
|
MesWmSnDO sn = snMapper.selectByCode(code);
|
if (sn == null) {
|
throw exception(WM_SN_NOT_EXISTS);
|
}
|
return sn;
|
}
|
|
private void validateSnItemMatch(MesWmSnDO sn, Long itemId) {
|
if (ObjUtil.notEqual(sn.getItemId(), itemId)) {
|
throw exception(WM_SN_ITEM_MISMATCH);
|
}
|
}
|
|
}
|