Commit 5a16d8337575a8de9d9396b06ff369f713e131d1
1 parent
2f4dfb69
✨ feat(mes): 添加库存混放规则校验及相关错误码
新增库存混放规则校验功能,确保在指定库位存放物料和批次时遵循配置的混放规则。同时,添加了多个相关的错误码以支持业务逻辑的处理。
Showing
6 changed files
with
181 additions
and
34 deletions
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
| @@ -356,6 +356,13 @@ public interface ErrorCodeConstants { | @@ -356,6 +356,13 @@ public interface ErrorCodeConstants { | ||
| 356 | ErrorCode WM_MATERIAL_STOCK_NOT_EXISTS = new ErrorCode(1_040_703_000, "库存记录不存在"); | 356 | ErrorCode WM_MATERIAL_STOCK_NOT_EXISTS = new ErrorCode(1_040_703_000, "库存记录不存在"); |
| 357 | ErrorCode WM_MATERIAL_STOCK_INSUFFICIENT = new ErrorCode(1_040_703_001, "库存数量不足"); | 357 | ErrorCode WM_MATERIAL_STOCK_INSUFFICIENT = new ErrorCode(1_040_703_001, "库存数量不足"); |
| 358 | ErrorCode WM_TRANSACTION_TYPE_NOT_EXISTS = new ErrorCode(1_040_703_002, "库存事务类型不存在"); | 358 | ErrorCode WM_TRANSACTION_TYPE_NOT_EXISTS = new ErrorCode(1_040_703_002, "库存事务类型不存在"); |
| 359 | + ErrorCode WM_TRANSACTION_WAREHOUSE_FROZEN = new ErrorCode(1_040_703_003, "仓库({})已被冻结"); | ||
| 360 | + ErrorCode WM_TRANSACTION_LOCATION_FROZEN = new ErrorCode(1_040_703_004, "库区({})已被冻结"); | ||
| 361 | + ErrorCode WM_TRANSACTION_AREA_FROZEN = new ErrorCode(1_040_703_005, "库位({})已被冻结"); | ||
| 362 | + ErrorCode WM_TRANSACTION_STOCK_FROZEN = new ErrorCode(1_040_703_006, "存放于({}/{}/{})下的物料已被冻结"); | ||
| 363 | + ErrorCode WM_TRANSACTION_BATCH_REQUIRED = new ErrorCode(1_040_703_007, "当前物料启用了批次管理,批次号不能为空"); | ||
| 364 | + ErrorCode WM_MATERIAL_STOCK_AREA_ITEM_MIXING_NOT_ALLOWED = new ErrorCode(1_040_703_008, "库位({})不允许物料混放,请选择其他库位"); | ||
| 365 | + ErrorCode WM_MATERIAL_STOCK_AREA_BATCH_MIXING_NOT_ALLOWED = new ErrorCode(1_040_703_009, "库位({})不允许批次混放,请选择其他库位"); | ||
| 359 | 366 | ||
| 360 | // ========== MES 仓库管理-到货通知单(1-040-704-000) ========== | 367 | // ========== MES 仓库管理-到货通知单(1-040-704-000) ========== |
| 361 | ErrorCode WM_ARRIVAL_NOTICE_NOT_EXISTS = new ErrorCode(1_040_704_000, "到货通知单不存在"); | 368 | ErrorCode WM_ARRIVAL_NOTICE_NOT_EXISTS = new ErrorCode(1_040_704_000, "到货通知单不存在"); |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockService.java
| @@ -29,6 +29,27 @@ public interface MesWmMaterialStockService { | @@ -29,6 +29,27 @@ public interface MesWmMaterialStockService { | ||
| 29 | MesWmMaterialStockDO getMaterialStock(Long id); | 29 | MesWmMaterialStockDO getMaterialStock(Long id); |
| 30 | 30 | ||
| 31 | /** | 31 | /** |
| 32 | + * 按组合键查询库存记录 | ||
| 33 | + * | ||
| 34 | + * @param itemId 物料编号 | ||
| 35 | + * @param warehouseId 仓库编号 | ||
| 36 | + * @param locationId 库区编号 | ||
| 37 | + * @param areaId 库位编号 | ||
| 38 | + * @param batchId 批次编号 | ||
| 39 | + * @return 库存记录,不存在返回 null | ||
| 40 | + */ | ||
| 41 | + MesWmMaterialStockDO getMaterialStockByCompositeKey(Long itemId, Long warehouseId, Long locationId, | ||
| 42 | + Long areaId, Long batchId); | ||
| 43 | + | ||
| 44 | + /** | ||
| 45 | + * 查询指定库位的库存记录列表 | ||
| 46 | + * | ||
| 47 | + * @param areaId 库位编号 | ||
| 48 | + * @return 库存记录列表 | ||
| 49 | + */ | ||
| 50 | + List<MesWmMaterialStockDO> getMaterialStockListByAreaId(Long areaId); | ||
| 51 | + | ||
| 52 | + /** | ||
| 32 | * 获得库存台账分页 | 53 | * 获得库存台账分页 |
| 33 | * | 54 | * |
| 34 | * @param pageReqVO 分页参数 | 55 | * @param pageReqVO 分页参数 |
| @@ -97,10 +118,10 @@ public interface MesWmMaterialStockService { | @@ -97,10 +118,10 @@ public interface MesWmMaterialStockService { | ||
| 97 | * @param batchId 批次编号 | 118 | * @param batchId 批次编号 |
| 98 | * @param vendorId 供应商编号 | 119 | * @param vendorId 供应商编号 |
| 99 | * @param receiptTime 入库时间(为空则默认当前时间) | 120 | * @param receiptTime 入库时间(为空则默认当前时间) |
| 100 | - * @return 库存记录编号 | 121 | + * @return 库存记录 |
| 101 | */ | 122 | */ |
| 102 | - Long getOrCreateMaterialStock(Long itemId, Long warehouseId, Long locationId, Long areaId, | ||
| 103 | - Long batchId, Long vendorId, LocalDateTime receiptTime); | 123 | + MesWmMaterialStockDO getOrCreateMaterialStock(Long itemId, Long warehouseId, Long locationId, Long areaId, |
| 124 | + Long batchId, Long vendorId, LocalDateTime receiptTime); | ||
| 104 | 125 | ||
| 105 | /** | 126 | /** |
| 106 | * 更新库存数量 | 127 | * 更新库存数量 |
| @@ -111,4 +132,15 @@ public interface MesWmMaterialStockService { | @@ -111,4 +132,15 @@ public interface MesWmMaterialStockService { | ||
| 111 | */ | 132 | */ |
| 112 | void updateMaterialStockQuantity(Long materialStockId, BigDecimal quantity, boolean checkFlag); | 133 | void updateMaterialStockQuantity(Long materialStockId, BigDecimal quantity, boolean checkFlag); |
| 113 | 134 | ||
| 135 | + /** | ||
| 136 | + * 库位混放规则校验 | ||
| 137 | + * | ||
| 138 | + * 根据库位配置的物料和批次混放规则,检查是否允许在该库位存放指定物料/批次 | ||
| 139 | + * | ||
| 140 | + * @param areaId 库位编号 | ||
| 141 | + * @param itemId 物料编号 | ||
| 142 | + * @param batchId 批次编号(可为 null) | ||
| 143 | + */ | ||
| 144 | + void checkAreaMixingRule(Long areaId, Long itemId, Long batchId); | ||
| 145 | + | ||
| 114 | } | 146 | } |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockServiceImpl.java
| 1 | package cn.iocoder.yudao.module.mes.service.wm.materialstock; | 1 | package cn.iocoder.yudao.module.mes.service.wm.materialstock; |
| 2 | 2 | ||
| 3 | import cn.hutool.core.collection.CollUtil; | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | +import cn.hutool.core.util.ObjUtil; | ||
| 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | import cn.iocoder.yudao.framework.common.util.collection.SetUtils; | 6 | import cn.iocoder.yudao.framework.common.util.collection.SetUtils; |
| 6 | import cn.iocoder.yudao.module.mes.controller.admin.wm.materialstock.vo.MesWmMaterialStockFreezeReqVO; | 7 | import cn.iocoder.yudao.module.mes.controller.admin.wm.materialstock.vo.MesWmMaterialStockFreezeReqVO; |
| @@ -9,9 +10,11 @@ import cn.iocoder.yudao.module.mes.controller.admin.wm.materialstock.vo.MesWmMat | @@ -9,9 +10,11 @@ import cn.iocoder.yudao.module.mes.controller.admin.wm.materialstock.vo.MesWmMat | ||
| 9 | import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO; | 10 | import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO; |
| 10 | import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemTypeDO; | 11 | import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemTypeDO; |
| 11 | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterialStockDO; | 12 | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterialStockDO; |
| 13 | +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO; | ||
| 12 | import cn.iocoder.yudao.module.mes.dal.mysql.wm.materialstock.MesWmMaterialStockMapper; | 14 | import cn.iocoder.yudao.module.mes.dal.mysql.wm.materialstock.MesWmMaterialStockMapper; |
| 13 | import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; | 15 | import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; |
| 14 | import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemTypeService; | 16 | import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemTypeService; |
| 17 | +import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService; | ||
| 15 | import jakarta.annotation.Resource; | 18 | import jakarta.annotation.Resource; |
| 16 | import org.springframework.stereotype.Service; | 19 | import org.springframework.stereotype.Service; |
| 17 | import org.springframework.validation.annotation.Validated; | 20 | import org.springframework.validation.annotation.Validated; |
| @@ -22,8 +25,7 @@ import java.util.*; | @@ -22,8 +25,7 @@ import java.util.*; | ||
| 22 | 25 | ||
| 23 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | 26 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 24 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; | 27 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; |
| 25 | -import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_MATERIAL_STOCK_INSUFFICIENT; | ||
| 26 | -import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_MATERIAL_STOCK_NOT_EXISTS; | 28 | +import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; |
| 27 | 29 | ||
| 28 | /** | 30 | /** |
| 29 | * MES 库存台账 Service 实现类 | 31 | * MES 库存台账 Service 实现类 |
| @@ -37,15 +39,27 @@ public class MesWmMaterialStockServiceImpl implements MesWmMaterialStockService | @@ -37,15 +39,27 @@ public class MesWmMaterialStockServiceImpl implements MesWmMaterialStockService | ||
| 37 | 39 | ||
| 38 | @Resource | 40 | @Resource |
| 39 | private MesMdItemService itemService; | 41 | private MesMdItemService itemService; |
| 40 | - | ||
| 41 | @Resource | 42 | @Resource |
| 42 | private MesMdItemTypeService itemTypeService; | 43 | private MesMdItemTypeService itemTypeService; |
| 44 | + @Resource | ||
| 45 | + private MesWmWarehouseAreaService areaService; | ||
| 43 | 46 | ||
| 44 | @Override | 47 | @Override |
| 45 | public MesWmMaterialStockDO getMaterialStock(Long id) { | 48 | public MesWmMaterialStockDO getMaterialStock(Long id) { |
| 46 | return materialStockMapper.selectById(id); | 49 | return materialStockMapper.selectById(id); |
| 47 | } | 50 | } |
| 48 | 51 | ||
| 52 | + @Override | ||
| 53 | + public MesWmMaterialStockDO getMaterialStockByCompositeKey(Long itemId, Long warehouseId, Long locationId, | ||
| 54 | + Long areaId, Long batchId) { | ||
| 55 | + return materialStockMapper.selectByCompositeKey(itemId, warehouseId, locationId, areaId, batchId); | ||
| 56 | + } | ||
| 57 | + | ||
| 58 | + @Override | ||
| 59 | + public List<MesWmMaterialStockDO> getMaterialStockListByAreaId(Long areaId) { | ||
| 60 | + return materialStockMapper.selectList(MesWmMaterialStockDO::getAreaId, areaId); | ||
| 61 | + } | ||
| 62 | + | ||
| 49 | public MesWmMaterialStockDO validateMaterialStockExists(Long id) { | 63 | public MesWmMaterialStockDO validateMaterialStockExists(Long id) { |
| 50 | MesWmMaterialStockDO stock = materialStockMapper.selectById(id); | 64 | MesWmMaterialStockDO stock = materialStockMapper.selectById(id); |
| 51 | if (stock == null) { | 65 | if (stock == null) { |
| @@ -113,13 +127,13 @@ public class MesWmMaterialStockServiceImpl implements MesWmMaterialStockService | @@ -113,13 +127,13 @@ public class MesWmMaterialStockServiceImpl implements MesWmMaterialStockService | ||
| 113 | } | 127 | } |
| 114 | 128 | ||
| 115 | @Override | 129 | @Override |
| 116 | - public Long getOrCreateMaterialStock(Long itemId, Long warehouseId, Long locationId, Long areaId, | ||
| 117 | - Long batchId, Long vendorId, LocalDateTime receiptTime) { | 130 | + public MesWmMaterialStockDO getOrCreateMaterialStock(Long itemId, Long warehouseId, Long locationId, Long areaId, |
| 131 | + Long batchId, Long vendorId, LocalDateTime receiptTime) { | ||
| 118 | // 1. 查找已有库存记录 | 132 | // 1. 查找已有库存记录 |
| 119 | MesWmMaterialStockDO stock = materialStockMapper.selectByCompositeKey( | 133 | MesWmMaterialStockDO stock = materialStockMapper.selectByCompositeKey( |
| 120 | itemId, warehouseId, locationId, areaId, batchId); | 134 | itemId, warehouseId, locationId, areaId, batchId); |
| 121 | if (stock != null) { | 135 | if (stock != null) { |
| 122 | - return stock.getId(); | 136 | + return stock; |
| 123 | } | 137 | } |
| 124 | 138 | ||
| 125 | // 2. 不存在则新建 | 139 | // 2. 不存在则新建 |
| @@ -133,7 +147,7 @@ public class MesWmMaterialStockServiceImpl implements MesWmMaterialStockService | @@ -133,7 +147,7 @@ public class MesWmMaterialStockServiceImpl implements MesWmMaterialStockService | ||
| 133 | .frozenFlag(false) | 147 | .frozenFlag(false) |
| 134 | .build(); | 148 | .build(); |
| 135 | materialStockMapper.insert(newStock); | 149 | materialStockMapper.insert(newStock); |
| 136 | - return newStock.getId(); | 150 | + return newStock; |
| 137 | } | 151 | } |
| 138 | 152 | ||
| 139 | @Override | 153 | @Override |
| @@ -145,4 +159,28 @@ public class MesWmMaterialStockServiceImpl implements MesWmMaterialStockService | @@ -145,4 +159,28 @@ public class MesWmMaterialStockServiceImpl implements MesWmMaterialStockService | ||
| 145 | } | 159 | } |
| 146 | } | 160 | } |
| 147 | 161 | ||
| 162 | + @Override | ||
| 163 | + public void checkAreaMixingRule(Long areaId, Long itemId, Long batchId) { | ||
| 164 | + // 1.1 校验库位必须存在 | ||
| 165 | + MesWmWarehouseAreaDO area = areaService.validateWarehouseAreaExists(areaId); | ||
| 166 | + | ||
| 167 | + // 2. 查询当前库位上已有的库存记录 | ||
| 168 | + List<MesWmMaterialStockDO> stockList = getMaterialStockListByAreaId(areaId); | ||
| 169 | + if (CollUtil.isEmpty(stockList)) { | ||
| 170 | + return; | ||
| 171 | + } | ||
| 172 | + // 2.1 检查物料混放 | ||
| 173 | + if (Boolean.FALSE.equals(area.getAllowItemMixing()) && itemId != null) { | ||
| 174 | + if (CollUtil.anyMatch(stockList, item -> ObjUtil.notEqual(itemId, item.getItemId()))) { | ||
| 175 | + throw exception(WM_MATERIAL_STOCK_AREA_ITEM_MIXING_NOT_ALLOWED, area.getName()); | ||
| 176 | + } | ||
| 177 | + } | ||
| 178 | + // 2.2 检查批次混放 | ||
| 179 | + if (Boolean.FALSE.equals(area.getAllowBatchMixing()) && batchId != null) { | ||
| 180 | + if (CollUtil.anyMatch(stockList, item -> ObjUtil.notEqual(batchId, item.getBatchId()))) { | ||
| 181 | + throw exception(WM_MATERIAL_STOCK_AREA_BATCH_MIXING_NOT_ALLOWED, area.getName()); | ||
| 182 | + } | ||
| 183 | + } | ||
| 184 | + } | ||
| 185 | + | ||
| 148 | } | 186 | } |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transaction/MesWmTransactionServiceImpl.java
| @@ -3,13 +3,22 @@ package cn.iocoder.yudao.module.mes.service.wm.transaction; | @@ -3,13 +3,22 @@ package cn.iocoder.yudao.module.mes.service.wm.transaction; | ||
| 3 | import cn.hutool.core.lang.Assert; | 3 | import cn.hutool.core.lang.Assert; |
| 4 | import cn.hutool.core.util.ObjUtil; | 4 | import cn.hutool.core.util.ObjUtil; |
| 5 | import cn.hutool.core.util.StrUtil; | 5 | import cn.hutool.core.util.StrUtil; |
| 6 | +import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO; | ||
| 6 | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO; | 7 | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO; |
| 8 | +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterialStockDO; | ||
| 7 | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.transaction.MesWmTransactionDO; | 9 | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.transaction.MesWmTransactionDO; |
| 10 | +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO; | ||
| 11 | +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO; | ||
| 12 | +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseLocationDO; | ||
| 8 | import cn.iocoder.yudao.module.mes.dal.mysql.wm.transaction.MesWmTransactionMapper; | 13 | import cn.iocoder.yudao.module.mes.dal.mysql.wm.transaction.MesWmTransactionMapper; |
| 9 | import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum; | 14 | import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum; |
| 15 | +import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; | ||
| 10 | import cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService; | 16 | import cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService; |
| 11 | import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService; | 17 | import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService; |
| 12 | import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO; | 18 | import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO; |
| 19 | +import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService; | ||
| 20 | +import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseLocationService; | ||
| 21 | +import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseService; | ||
| 13 | import jakarta.annotation.Resource; | 22 | import jakarta.annotation.Resource; |
| 14 | import org.springframework.stereotype.Service; | 23 | import org.springframework.stereotype.Service; |
| 15 | import org.springframework.transaction.annotation.Transactional; | 24 | import org.springframework.transaction.annotation.Transactional; |
| @@ -19,6 +28,9 @@ import java.math.BigDecimal; | @@ -19,6 +28,9 @@ import java.math.BigDecimal; | ||
| 19 | import java.time.LocalDateTime; | 28 | import java.time.LocalDateTime; |
| 20 | import java.util.List; | 29 | import java.util.List; |
| 21 | 30 | ||
| 31 | +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | ||
| 32 | +import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; | ||
| 33 | + | ||
| 22 | /** | 34 | /** |
| 23 | * MES 库存事务流水 Service 实现类 | 35 | * MES 库存事务流水 Service 实现类 |
| 24 | */ | 36 | */ |
| @@ -28,11 +40,18 @@ public class MesWmTransactionServiceImpl implements MesWmTransactionService { | @@ -28,11 +40,18 @@ public class MesWmTransactionServiceImpl implements MesWmTransactionService { | ||
| 28 | 40 | ||
| 29 | @Resource | 41 | @Resource |
| 30 | private MesWmTransactionMapper transactionMapper; | 42 | private MesWmTransactionMapper transactionMapper; |
| 31 | - | ||
| 32 | @Resource | 43 | @Resource |
| 33 | private MesWmMaterialStockService materialStockService; | 44 | private MesWmMaterialStockService materialStockService; |
| 34 | @Resource | 45 | @Resource |
| 35 | private MesWmBatchService batchService; | 46 | private MesWmBatchService batchService; |
| 47 | + @Resource | ||
| 48 | + private MesMdItemService itemService; | ||
| 49 | + @Resource | ||
| 50 | + private MesWmWarehouseService warehouseService; | ||
| 51 | + @Resource | ||
| 52 | + private MesWmWarehouseLocationService locationService; | ||
| 53 | + @Resource | ||
| 54 | + private MesWmWarehouseAreaService areaService; | ||
| 36 | 55 | ||
| 37 | @Override | 56 | @Override |
| 38 | @Transactional(rollbackFor = Exception.class) | 57 | @Transactional(rollbackFor = Exception.class) |
| @@ -42,23 +61,23 @@ public class MesWmTransactionServiceImpl implements MesWmTransactionService { | @@ -42,23 +61,23 @@ public class MesWmTransactionServiceImpl implements MesWmTransactionService { | ||
| 42 | Assert.notNull(typeEnum, "事务类型({}) 不存在", reqDTO.getType()); | 61 | Assert.notNull(typeEnum, "事务类型({}) 不存在", reqDTO.getType()); |
| 43 | // 1.2 校验 quantity 正负号与事务方向一致(入库必须正数,出库必须负数) | 62 | // 1.2 校验 quantity 正负号与事务方向一致(入库必须正数,出库必须负数) |
| 44 | boolean inbound = typeEnum.isInbound(); | 63 | boolean inbound = typeEnum.isInbound(); |
| 64 | + checkQuantity(reqDTO, inbound); | ||
| 65 | + // 1.3 入库事务:批次号校验 + batchId/batchCode 互补 | ||
| 66 | + checkBatch(reqDTO, inbound); | ||
| 67 | + // 1.4 入库事务:库位混放规则校验 | ||
| 45 | if (inbound) { | 68 | if (inbound) { |
| 46 | - Assert.isTrue(reqDTO.getQuantity().compareTo(BigDecimal.ZERO) > 0, | ||
| 47 | - "入库事务数量必须为正数,实际值: {}", reqDTO.getQuantity()); | ||
| 48 | - } else { | ||
| 49 | - Assert.isTrue(reqDTO.getQuantity().compareTo(BigDecimal.ZERO) < 0, | ||
| 50 | - "出库事务数量必须为负数,实际值: {}", reqDTO.getQuantity()); | 69 | + materialStockService.checkAreaMixingRule(reqDTO.getAreaId(), reqDTO.getItemId(), reqDTO.getBatchId()); |
| 51 | } | 70 | } |
| 52 | - // 1.3 batchId / batchCode 互补 | ||
| 53 | - completeBatchInfo(reqDTO); | ||
| 54 | 71 | ||
| 55 | // 2.1 获取或创建库存记录 | 72 | // 2.1 获取或创建库存记录 |
| 56 | - Long materialStockId = materialStockService.getOrCreateMaterialStock( | 73 | + MesWmMaterialStockDO materialStock = materialStockService.getOrCreateMaterialStock( |
| 57 | reqDTO.getItemId(), reqDTO.getWarehouseId(), reqDTO.getLocationId(), reqDTO.getAreaId(), | 74 | reqDTO.getItemId(), reqDTO.getWarehouseId(), reqDTO.getLocationId(), reqDTO.getAreaId(), |
| 58 | reqDTO.getBatchId(), reqDTO.getVendorId(), reqDTO.getReceiptTime()); | 75 | reqDTO.getBatchId(), reqDTO.getVendorId(), reqDTO.getReceiptTime()); |
| 59 | - // 2.2 更新库存数量 | 76 | + // 2.2 冻结校验(仓库、库区、库位、库存记录) |
| 77 | + checkFrozen(reqDTO, materialStock); | ||
| 78 | + // 2.3 更新库存数量 | ||
| 60 | boolean checkFlag = ObjUtil.defaultIfNull(reqDTO.getCheckFlag(), true); | 79 | boolean checkFlag = ObjUtil.defaultIfNull(reqDTO.getCheckFlag(), true); |
| 61 | - materialStockService.updateMaterialStockQuantity(materialStockId, reqDTO.getQuantity(), checkFlag); | 80 | + materialStockService.updateMaterialStockQuantity(materialStock.getId(), reqDTO.getQuantity(), checkFlag); |
| 62 | 81 | ||
| 63 | // 3. 插入事务流水 | 82 | // 3. 插入事务流水 |
| 64 | MesWmTransactionDO transaction = MesWmTransactionDO.builder() | 83 | MesWmTransactionDO transaction = MesWmTransactionDO.builder() |
| @@ -66,7 +85,7 @@ public class MesWmTransactionServiceImpl implements MesWmTransactionService { | @@ -66,7 +85,7 @@ public class MesWmTransactionServiceImpl implements MesWmTransactionService { | ||
| 66 | .itemId(reqDTO.getItemId()).batchId(reqDTO.getBatchId()).batchCode(reqDTO.getBatchCode()) | 85 | .itemId(reqDTO.getItemId()).batchId(reqDTO.getBatchId()).batchCode(reqDTO.getBatchCode()) |
| 67 | .warehouseId(reqDTO.getWarehouseId()).locationId(reqDTO.getLocationId()).areaId(reqDTO.getAreaId()) | 86 | .warehouseId(reqDTO.getWarehouseId()).locationId(reqDTO.getLocationId()).areaId(reqDTO.getAreaId()) |
| 68 | .bizType(reqDTO.getBizType()).bizId(reqDTO.getBizId()).bizCode(reqDTO.getBizCode()).bizLineId(reqDTO.getBizLineId()) | 87 | .bizType(reqDTO.getBizType()).bizId(reqDTO.getBizId()).bizCode(reqDTO.getBizCode()).bizLineId(reqDTO.getBizLineId()) |
| 69 | - .materialStockId(materialStockId).relatedTransactionId(reqDTO.getRelatedTransactionId()) | 88 | + .materialStockId(materialStock.getId()).relatedTransactionId(reqDTO.getRelatedTransactionId()) |
| 70 | .build(); | 89 | .build(); |
| 71 | transactionMapper.insert(transaction); | 90 | transactionMapper.insert(transaction); |
| 72 | return transaction.getId(); | 91 | return transaction.getId(); |
| @@ -78,30 +97,79 @@ public class MesWmTransactionServiceImpl implements MesWmTransactionService { | @@ -78,30 +97,79 @@ public class MesWmTransactionServiceImpl implements MesWmTransactionService { | ||
| 78 | reqDTOs.forEach(this::createTransaction); | 97 | reqDTOs.forEach(this::createTransaction); |
| 79 | } | 98 | } |
| 80 | 99 | ||
| 100 | + // ==================== 私有校验方法 ==================== | ||
| 101 | + | ||
| 81 | /** | 102 | /** |
| 82 | - * batchId / batchCode 互补:有 batchId 无 batchCode 时,查出 batchCode;反之亦然。 | ||
| 83 | - * | ||
| 84 | - * @param reqDTO 事务请求 DTO | 103 | + * 校验数量正负号与事务方向一致 |
| 85 | */ | 104 | */ |
| 86 | - private void completeBatchInfo(MesWmTransactionSaveReqDTO reqDTO) { | ||
| 87 | - if (reqDTO.getBatchId() != null && StrUtil.isNotEmpty(reqDTO.getBatchCode())) { | ||
| 88 | - return; | 105 | + private void checkQuantity(MesWmTransactionSaveReqDTO reqDTO, boolean inbound) { |
| 106 | + if (inbound) { | ||
| 107 | + Assert.isTrue(reqDTO.getQuantity().compareTo(BigDecimal.ZERO) > 0, | ||
| 108 | + "入库事务数量必须为正数,实际值: {}", reqDTO.getQuantity()); | ||
| 109 | + } else { | ||
| 110 | + Assert.isTrue(reqDTO.getQuantity().compareTo(BigDecimal.ZERO) < 0, | ||
| 111 | + "出库事务数量必须为负数,实际值: {}", reqDTO.getQuantity()); | ||
| 89 | } | 112 | } |
| 90 | - // 情况一:有 batchId,补 batchCode | ||
| 91 | - if (reqDTO.getBatchId() != null) { | 113 | + } |
| 114 | + | ||
| 115 | + /** | ||
| 116 | + * 批次校验:batchId/batchCode 互补 + 入库时批次号必填检查 | ||
| 117 | + * | ||
| 118 | + * @param reqDTO 事务请求 DTO | ||
| 119 | + * @param inbound 是否入库 | ||
| 120 | + */ | ||
| 121 | + private void checkBatch(MesWmTransactionSaveReqDTO reqDTO, boolean inbound) { | ||
| 122 | + // 1. batchId / batchCode 互补 | ||
| 123 | + if (reqDTO.getBatchId() != null && StrUtil.isEmpty(reqDTO.getBatchCode())) { | ||
| 92 | MesWmBatchDO batch = batchService.getBatch(reqDTO.getBatchId()); | 124 | MesWmBatchDO batch = batchService.getBatch(reqDTO.getBatchId()); |
| 93 | if (batch != null) { | 125 | if (batch != null) { |
| 94 | reqDTO.setBatchCode(batch.getCode()); | 126 | reqDTO.setBatchCode(batch.getCode()); |
| 95 | } | 127 | } |
| 96 | - return; | ||
| 97 | - } | ||
| 98 | - // 情况二:有 batchCode,补 batchId | ||
| 99 | - if (StrUtil.isNotEmpty(reqDTO.getBatchCode())) { | 128 | + } else if (StrUtil.isNotEmpty(reqDTO.getBatchCode()) && reqDTO.getBatchId() == null) { |
| 100 | MesWmBatchDO batch = batchService.getBatchByCode(reqDTO.getBatchCode()); | 129 | MesWmBatchDO batch = batchService.getBatchByCode(reqDTO.getBatchCode()); |
| 101 | if (batch != null) { | 130 | if (batch != null) { |
| 102 | reqDTO.setBatchId(batch.getId()); | 131 | reqDTO.setBatchId(batch.getId()); |
| 103 | } | 132 | } |
| 104 | } | 133 | } |
| 134 | + // 2. 入库时:启用批次管理的物料,必须传递批次号 | ||
| 135 | + if (inbound) { | ||
| 136 | + MesMdItemDO item = itemService.getItem(reqDTO.getItemId()); | ||
| 137 | + if (item != null && Boolean.TRUE.equals(item.getBatchFlag())) { | ||
| 138 | + if (StrUtil.isEmpty(reqDTO.getBatchCode()) && reqDTO.getBatchId() == null) { | ||
| 139 | + throw exception(WM_TRANSACTION_BATCH_REQUIRED); | ||
| 140 | + } | ||
| 141 | + } | ||
| 142 | + } | ||
| 143 | + } | ||
| 144 | + | ||
| 145 | + /** | ||
| 146 | + * 冻结校验:检查仓库、库区、库位、库存记录是否被冻结 | ||
| 147 | + * | ||
| 148 | + * @param reqDTO 事务请求 DTO | ||
| 149 | + * @param materialStock 库存记录 | ||
| 150 | + */ | ||
| 151 | + private void checkFrozen(MesWmTransactionSaveReqDTO reqDTO, MesWmMaterialStockDO materialStock) { | ||
| 152 | + // 1.1 检查仓库冻结 | ||
| 153 | + MesWmWarehouseDO warehouse = warehouseService.validateWarehouseExists(reqDTO.getWarehouseId()); | ||
| 154 | + if (Boolean.TRUE.equals(warehouse.getFrozen())) { | ||
| 155 | + throw exception(WM_TRANSACTION_WAREHOUSE_FROZEN, warehouse.getName()); | ||
| 156 | + } | ||
| 157 | + // 1.2 检查库区冻结 | ||
| 158 | + MesWmWarehouseLocationDO location = locationService.validateWarehouseLocationExists(reqDTO.getLocationId()); | ||
| 159 | + if (Boolean.TRUE.equals(location.getFrozen())) { | ||
| 160 | + throw exception(WM_TRANSACTION_LOCATION_FROZEN, location.getName()); | ||
| 161 | + } | ||
| 162 | + // 1.3 检查库位冻结 | ||
| 163 | + MesWmWarehouseAreaDO area = areaService.validateWarehouseAreaExists(reqDTO.getAreaId()); | ||
| 164 | + if (Boolean.TRUE.equals(area.getFrozen())) { | ||
| 165 | + throw exception(WM_TRANSACTION_AREA_FROZEN, area.getName()); | ||
| 166 | + } | ||
| 167 | + | ||
| 168 | + // 2. 检查库存记录冻结 | ||
| 169 | + if (Boolean.TRUE.equals(materialStock.getFrozenFlag())) { | ||
| 170 | + throw exception(WM_TRANSACTION_STOCK_FROZEN, | ||
| 171 | + warehouse.getName(), location.getName(), area.getName()); | ||
| 172 | + } | ||
| 105 | } | 173 | } |
| 106 | 174 | ||
| 107 | } | 175 | } |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transaction/dto/MesWmTransactionSaveReqDTO.java
| @@ -85,6 +85,7 @@ public class MesWmTransactionSaveReqDTO { | @@ -85,6 +85,7 @@ public class MesWmTransactionSaveReqDTO { | ||
| 85 | /** | 85 | /** |
| 86 | * 库位 ID | 86 | * 库位 ID |
| 87 | */ | 87 | */ |
| 88 | + @NotNull(message = "库位 ID 不能为空") | ||
| 88 | private Long areaId; | 89 | private Long areaId; |
| 89 | 90 | ||
| 90 | // ===== 台账补充字段(入库时用于初始化 MesWmMaterialStockDO) ===== | 91 | // ===== 台账补充字段(入库时用于初始化 MesWmMaterialStockDO) ===== |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/warehouse/MesWmWarehouseAreaServiceImpl.java
| @@ -39,6 +39,7 @@ public class MesWmWarehouseAreaServiceImpl implements MesWmWarehouseAreaService | @@ -39,6 +39,7 @@ public class MesWmWarehouseAreaServiceImpl implements MesWmWarehouseAreaService | ||
| 39 | @Lazy | 39 | @Lazy |
| 40 | private MesMdWorkstationService workstationService; | 40 | private MesMdWorkstationService workstationService; |
| 41 | @Resource | 41 | @Resource |
| 42 | + @Lazy | ||
| 42 | private MesWmMaterialStockService materialStockService; | 43 | private MesWmMaterialStockService materialStockService; |
| 43 | @Resource | 44 | @Resource |
| 44 | @Lazy | 45 | @Lazy |