From 47cff107855187497d853a3f0b621be3761f0a3c Mon Sep 17 00:00:00 2001 From: Joshi <3040996759@qq.com> Date: Tue, 12 Aug 2025 10:12:13 +0800 Subject: [PATCH] =?UTF-8?q?feat(stock):=20=E6=B7=BB=E5=8A=A0=E9=80=80?= =?UTF-8?q?=E5=BA=93=E5=8A=9F=E8=83=BD-=20=E5=9C=A8=20IWmsStockIoService?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E4=B8=AD=E6=B7=BB=E5=8A=A0=20scanReturnStock?= =?UTF-8?q?ByBo=20=E6=96=B9=E6=B3=95=20-=20=E5=9C=A8=20WmsStockIo=20?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E4=B8=AD=E6=B7=BB=E5=8A=A0=20parentId=20?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=E7=94=A8=E4=BA=8E=E5=85=B3=E8=81=94=E5=8E=9F?= =?UTF-8?q?=E5=87=BA=E5=BA=93=E5=8D=95=20-=20=E5=9C=A8=20WmsStockIoBo=20?= =?UTF-8?q?=E4=B8=AD=E6=B7=BB=E5=8A=A0=20parentId=20=E5=AD=97=E6=AE=B5=20-?= =?UTF-8?q?=20=E5=9C=A8=20WmsStockIoController=20=E4=B8=AD=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=20scanReturnStock=20=E6=8E=A7=E5=88=B6=E5=99=A8?= =?UTF-8?q?=E6=96=B9=E6=B3=95=20-=20=E5=9C=A8=20WmsStockIoServiceImpl=20?= =?UTF-8?q?=E4=B8=AD=E5=AE=9E=E7=8E=B0=20scanReturnStockByBo=20=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E7=9A=84=E4=B8=9A=E5=8A=A1=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../klp/controller/WmsStockIoController.java | 10 + .../main/java/com/klp/domain/WmsStockIo.java | 2 + .../java/com/klp/domain/bo/WmsStockIoBo.java | 4 + .../com/klp/service/IWmsStockIoService.java | 2 + .../service/impl/WmsStockIoServiceImpl.java | 187 ++++++++++++++++++ 5 files changed, 205 insertions(+) diff --git a/klp-wms/src/main/java/com/klp/controller/WmsStockIoController.java b/klp-wms/src/main/java/com/klp/controller/WmsStockIoController.java index fa60bc58..e0fd32a4 100644 --- a/klp-wms/src/main/java/com/klp/controller/WmsStockIoController.java +++ b/klp-wms/src/main/java/com/klp/controller/WmsStockIoController.java @@ -173,4 +173,14 @@ public class WmsStockIoController extends BaseController { return R.fail(e.getMessage()); } } + //退库操作 + @PostMapping("/scanReturnStock") + public R scanReturnStock(@RequestBody WmsStockIoBo bo) { + try { + boolean result = iWmsStockIoService.scanReturnStockByBo(bo); + return toAjax(result); + } catch (Exception e) { + return R.fail(e.getMessage()); + } + } } diff --git a/klp-wms/src/main/java/com/klp/domain/WmsStockIo.java b/klp-wms/src/main/java/com/klp/domain/WmsStockIo.java index 21f22938..8e312fef 100644 --- a/klp-wms/src/main/java/com/klp/domain/WmsStockIo.java +++ b/klp-wms/src/main/java/com/klp/domain/WmsStockIo.java @@ -44,6 +44,8 @@ public class WmsStockIo extends BaseEntity { * 备注 */ private String remark; + // + private Long parentId; /** * 删除标志(0=正常,1=已删除) */ diff --git a/klp-wms/src/main/java/com/klp/domain/bo/WmsStockIoBo.java b/klp-wms/src/main/java/com/klp/domain/bo/WmsStockIoBo.java index 9cae56f3..1d3820f7 100644 --- a/klp-wms/src/main/java/com/klp/domain/bo/WmsStockIoBo.java +++ b/klp-wms/src/main/java/com/klp/domain/bo/WmsStockIoBo.java @@ -53,5 +53,9 @@ public class WmsStockIoBo extends BaseEntity { */ private String remark; + /** + * 父级ID(用于退库时关联原出库单) + */ + private Long parentId; } diff --git a/klp-wms/src/main/java/com/klp/service/IWmsStockIoService.java b/klp-wms/src/main/java/com/klp/service/IWmsStockIoService.java index 91277b82..535d33d3 100644 --- a/klp-wms/src/main/java/com/klp/service/IWmsStockIoService.java +++ b/klp-wms/src/main/java/com/klp/service/IWmsStockIoService.java @@ -79,4 +79,6 @@ public interface IWmsStockIoService { boolean scanInStockByBo(WmsStockIoDetailBo bo); boolean scanOutStockByBo(WmsStockIoDetailBo bo); + + boolean scanReturnStockByBo(WmsStockIoBo bo); } diff --git a/klp-wms/src/main/java/com/klp/service/impl/WmsStockIoServiceImpl.java b/klp-wms/src/main/java/com/klp/service/impl/WmsStockIoServiceImpl.java index 78150b88..7f853c1b 100644 --- a/klp-wms/src/main/java/com/klp/service/impl/WmsStockIoServiceImpl.java +++ b/klp-wms/src/main/java/com/klp/service/impl/WmsStockIoServiceImpl.java @@ -42,6 +42,7 @@ public class WmsStockIoServiceImpl implements IWmsStockIoService { private final WmsStockMapper stockMapper; private final WmsProductMapper productMapper; private final WmsRawMaterialMapper rawMaterialMapper; + private final WmsWarehouseMapper warehouseMapper; @Resource private WmsStockLogMapper stockLogMapper; @@ -132,6 +133,7 @@ public class WmsStockIoServiceImpl implements IWmsStockIoService { public void addWithDetail(WmsStockIoWithDetailBo bo) { // 插入主表 WmsStockIo stockIo = BeanUtil.toBean(bo, WmsStockIo.class); + stockIo.setParentId(bo.getStockIoId()); baseMapper.insert(stockIo); // 插入明细 if (bo.getDetails() != null && !bo.getDetails().isEmpty()) { @@ -186,6 +188,191 @@ public class WmsStockIoServiceImpl implements IWmsStockIoService { return true; } + @Override + @Transactional(rollbackFor = Exception.class) + public boolean scanReturnStockByBo(WmsStockIoBo bo) { + // 退库操作:根据id查询明细表,根据parent_id获取原出库单明细,验证退库数量 + if (bo.getStockIoId() == null) { + throw new ServiceException("退库单ID不能为空"); + } + + // 1. 根据id查询退库单明细 + WmsStockIoDetail returnDetail = stockIoDetailMapper.selectById(bo.getStockIoId()); + if (returnDetail == null) { + throw new ServiceException("退库明细不存在"); + } + + // 验证退库数量必须大于0 + if (returnDetail.getQuantity() == null || returnDetail.getQuantity().compareTo(BigDecimal.ZERO) <= 0) { + throw new ServiceException("退库数量必须大于0"); + } + + // 验证物品类型和物品ID的有效性 + if (returnDetail.getItemType() == null || returnDetail.getItemId() == null) { + throw new ServiceException("物品类型和物品ID不能为空"); + } + + if (!"product".equals(returnDetail.getItemType()) && !"raw_material".equals(returnDetail.getItemType())) { + throw new ServiceException("物品类型必须是product或raw_material"); + } + + // 验证库位ID的有效性 + if (returnDetail.getWarehouseId() == null) { + throw new ServiceException("库位ID不能为空"); + } + + // 验证物品是否存在 + if ("product".equals(returnDetail.getItemType())) { + WmsProduct product = productMapper.selectById(returnDetail.getItemId()); + if (product == null) { + throw new ServiceException("产品不存在"); + } + } else if ("raw_material".equals(returnDetail.getItemType())) { + WmsRawMaterial rawMaterial = rawMaterialMapper.selectById(returnDetail.getItemId()); + if (rawMaterial == null) { + throw new ServiceException("原材料不存在"); + } + } + + // 验证库位是否存在 + WmsWarehouse warehouse = warehouseMapper.selectById(returnDetail.getWarehouseId()); + if (warehouse == null) { + throw new ServiceException("库位不存在"); + } + + // 验证库位是否启用 + if (warehouse.getIsEnabled() == null || warehouse.getIsEnabled() != 1) { + throw new ServiceException("库位未启用,无法进行退库操作"); + } + + // 2. 获取退库单主表信息 + WmsStockIo returnStockIo = baseMapper.selectById(returnDetail.getStockIoId()); + if (returnStockIo == null) { + throw new ServiceException("退库单不存在"); + } + + // 验证退库单类型必须是入库类型 + if (!"in".equals(returnStockIo.getIoType())) { + throw new ServiceException("退库单类型必须是入库类型"); + } + + // 验证退库单状态必须是已提交或已审核状态 + if (returnStockIo.getStatus() == null || (returnStockIo.getStatus() != 1 && returnStockIo.getStatus() != 2)) { + throw new ServiceException("退库单必须是已提交或已审核状态才能执行退库操作"); + } + + // 3. 根据parent_id获取原出库单的所有明细 + if (returnStockIo.getParentId() == null) { + throw new ServiceException("退库单未关联原出库单"); + } + + WmsStockIo originalOutStockIo = baseMapper.selectById(returnStockIo.getParentId()); + if (originalOutStockIo == null) { + throw new ServiceException("原出库单不存在"); + } + + if (!"out".equals(originalOutStockIo.getIoType())) { + throw new ServiceException("原单据不是出库单"); + } + + // 验证原出库单状态必须是已审核状态 + if (originalOutStockIo.getStatus() == null || originalOutStockIo.getStatus() != 2) { + throw new ServiceException("原出库单必须是已审核状态才能进行退库操作"); + } + + // 4. 获取原出库单的所有明细 + List originalOutDetails = stockIoDetailMapper.selectList( + Wrappers.lambdaQuery() + .eq(WmsStockIoDetail::getStockIoId, returnStockIo.getParentId()) + ); + + if (originalOutDetails == null || originalOutDetails.isEmpty()) { + throw new ServiceException("原出库单明细不存在"); + } + + // 5. 查找对应的原出库明细(根据物品类型、物品ID、库位ID匹配) + WmsStockIoDetail originalOutDetail = null; + for (WmsStockIoDetail detail : originalOutDetails) { + if (detail.getItemType().equals(returnDetail.getItemType()) && + detail.getItemId().equals(returnDetail.getItemId()) && + detail.getWarehouseId().equals(returnDetail.getWarehouseId())) { + originalOutDetail = detail; + break; + } + } + + if (originalOutDetail == null) { + throw new ServiceException("未找到对应的原出库明细"); + } + + // 6. 查询该物品已退库的总数量 + BigDecimal totalReturnedQty = BigDecimal.ZERO; + List returnStockIos = baseMapper.selectList( + Wrappers.lambdaQuery() + .eq(WmsStockIo::getParentId, returnStockIo.getParentId()) + .eq(WmsStockIo::getIoType, "in") // 退库单类型为入库 + ); + + for (WmsStockIo returnStockIoItem : returnStockIos) { + // 排除当前正在处理的退库单 + if (returnStockIoItem.getStockIoId().equals(returnStockIo.getStockIoId())) { + continue; + } + + List returnDetails = stockIoDetailMapper.selectList( + Wrappers.lambdaQuery() + .eq(WmsStockIoDetail::getStockIoId, returnStockIoItem.getStockIoId()) + .eq(WmsStockIoDetail::getItemType, returnDetail.getItemType()) + .eq(WmsStockIoDetail::getItemId, returnDetail.getItemId()) + .eq(WmsStockIoDetail::getWarehouseId, returnDetail.getWarehouseId()) + ); + + for (WmsStockIoDetail detail : returnDetails) { + totalReturnedQty = totalReturnedQty.add(detail.getQuantity()); + } + } + + // 7. 验证退库数量不能超过原出库数量 + BigDecimal remainingQty = originalOutDetail.getQuantity().subtract(totalReturnedQty); + if (returnDetail.getQuantity().compareTo(remainingQty) > 0) { + throw new ServiceException("退库数量超过原出库数量,原出库数量:" + originalOutDetail.getQuantity() + + ",已退库数量:" + totalReturnedQty + ",剩余可退数量:" + remainingQty + + ",本次退库数量:" + returnDetail.getQuantity()); + } + + // 8. 执行退库操作(增加库存) + String unit = returnDetail.getUnit(); + // 如果unit为空,自动查item表补全 + if (unit == null || unit.trim().isEmpty()) { + if ("product".equals(returnDetail.getItemType())) { + WmsProduct p = productMapper.selectById(returnDetail.getItemId()); + unit = p != null ? p.getUnit() : null; + } else if ("raw_material".equals(returnDetail.getItemType())) { + WmsRawMaterial r = rawMaterialMapper.selectById(returnDetail.getItemId()); + unit = r != null ? r.getUnit() : null; + } + } + if (unit == null || unit.trim().isEmpty()) { + throw new ServiceException("未能获取到单位"); + } + + // 执行入库操作(退库就是入库) + changeStock(returnDetail.getWarehouseId(), returnDetail.getItemType(), + returnDetail.getItemId(), returnDetail.getQuantity(), true, unit); + + // 记录退库操作日志 + WmsStockLog returnLog = new WmsStockLog(); + returnLog.setWarehouseId(returnDetail.getWarehouseId()); + returnLog.setItemType(returnDetail.getItemType()); + returnLog.setItemId(returnDetail.getItemId()); + returnLog.setChangeQty(returnDetail.getQuantity()); + returnLog.setChangeType("退库"); + returnLog.setChangeTime(new Date()); + stockLogMapper.insert(returnLog); + + return true; + } + /** * 审核出入库/移库单,变更库存,含库存校验