| | |
| | | |
| | | 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 芋道源码 |
| | | * @author 超级管理员 |
| | | */ |
| | | @Service |
| | | @Validated |
| | |
| | | |
| | | @Resource |
| | | private MesWmSnMapper snMapper; |
| | | @Resource |
| | | private MesWmTransactionMapper transactionMapper; |
| | | |
| | | @Resource |
| | | private MesMdAutoCodeRecordService autoCodeRecordService; |
| | |
| | | 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) |
| | |
| | | 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); |
| | | } |
| | | } |
| | | |
| | | } |