diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/inventory/WmsInventoryController.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/inventory/WmsInventoryController.java index 7fe1d85..5142e05 100644 --- a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/inventory/WmsInventoryController.java +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/inventory/WmsInventoryController.java @@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.wms.controller.admin.inventory.vo.WmsInventoryListReqVO; import cn.iocoder.yudao.module.wms.controller.admin.inventory.vo.WmsInventoryPageReqVO; import cn.iocoder.yudao.module.wms.controller.admin.inventory.vo.WmsInventoryRespVO; import cn.iocoder.yudao.module.wms.dal.dataobject.inventory.WmsInventoryDO; @@ -55,6 +56,14 @@ public class WmsInventoryController { return success(new PageResult<>(buildInventoryRespVOList(pageResult.getList()), pageResult.getTotal())); } + @GetMapping("/list") + @Operation(summary = "获得库存统计列表") + @PreAuthorize("@ss.hasPermission('wms:inventory:query')") + public CommonResult> getInventoryList(@Valid WmsInventoryListReqVO listReqVO) { + List list = inventoryService.getInventoryList(listReqVO); + return success(buildInventoryRespVOList(list)); + } + // ==================== 拼接 VO ==================== private List buildInventoryRespVOList(List list) { diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/inventory/vo/WmsInventoryListReqVO.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/inventory/vo/WmsInventoryListReqVO.java new file mode 100644 index 0000000..e3deb8c --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/inventory/vo/WmsInventoryListReqVO.java @@ -0,0 +1,15 @@ +package cn.iocoder.yudao.module.wms.controller.admin.inventory.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - WMS 库存统计列表 Request VO") +@Data +public class WmsInventoryListReqVO { + + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + @NotNull(message = "仓库编号不能为空") + private Long warehouseId; + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/inventory/WmsInventoryMapper.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/inventory/WmsInventoryMapper.java index d59eff1..d6268d9 100644 --- a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/inventory/WmsInventoryMapper.java +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/inventory/WmsInventoryMapper.java @@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX; +import cn.iocoder.yudao.module.wms.controller.admin.inventory.vo.WmsInventoryListReqVO; import cn.iocoder.yudao.module.wms.controller.admin.inventory.vo.WmsInventoryPageReqVO; import cn.iocoder.yudao.module.wms.dal.dataobject.inventory.WmsInventoryDO; import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemDO; @@ -48,11 +49,24 @@ public interface WmsInventoryMapper extends BaseMapperX { return selectCount(WmsInventoryDO::getSkuId, skuId); } + default List selectList(WmsInventoryListReqVO reqVO) { + return selectList(new LambdaQueryWrapperX() + .eq(WmsInventoryDO::getWarehouseId, reqVO.getWarehouseId()) + .orderByAsc(WmsInventoryDO::getSkuId) + .orderByAsc(WmsInventoryDO::getId)); + } + default WmsInventoryDO selectBySkuIdAndWarehouseId(Long skuId, Long warehouseId) { return selectOne(WmsInventoryDO::getSkuId, skuId, WmsInventoryDO::getWarehouseId, warehouseId); } + default WmsInventoryDO selectByIdForUpdate(Long id) { + return selectOne(new LambdaQueryWrapperX() + .eq(WmsInventoryDO::getId, id) + .last("FOR UPDATE")); + } + /** * 根据多个唯一键,批量查询库存列表 * diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryService.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryService.java index 76d0b8d..294dbe3 100644 --- a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryService.java +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryService.java @@ -1,9 +1,13 @@ package cn.iocoder.yudao.module.wms.service.inventory; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.wms.controller.admin.inventory.vo.WmsInventoryListReqVO; import cn.iocoder.yudao.module.wms.controller.admin.inventory.vo.WmsInventoryPageReqVO; import cn.iocoder.yudao.module.wms.dal.dataobject.inventory.WmsInventoryDO; import cn.iocoder.yudao.module.wms.service.inventory.dto.WmsInventoryChangeReqDTO; +import cn.iocoder.yudao.module.wms.service.inventory.dto.WmsInventoryCheckReqDTO; + +import java.util.List; /** * WMS 库存 Service 接口 @@ -21,6 +25,14 @@ public interface WmsInventoryService { PageResult getInventoryPage(WmsInventoryPageReqVO pageReqVO); /** + * 获得库存余额列表 + * + * @param listReqVO 列表查询 + * @return 库存余额列表 + */ + List getInventoryList(WmsInventoryListReqVO listReqVO); + + /** * 获得指定 SKU 的库存数量 * * @param skuId SKU 编号 @@ -29,6 +41,13 @@ public interface WmsInventoryService { long getInventoryCountBySkuId(Long skuId); /** + * 盘点库存 + * + * @param reqDTO 盘点库存请求 + */ + void checkInventory(WmsInventoryCheckReqDTO reqDTO); + + /** * 变更库存 * * @param reqDTO 库存变更请求 diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryServiceImpl.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryServiceImpl.java index cc71d66..2009f89 100644 --- a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryServiceImpl.java +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryServiceImpl.java @@ -5,6 +5,7 @@ import cn.hutool.core.lang.Tuple; import cn.hutool.core.util.ObjectUtil; import cn.iocoder.yudao.framework.common.exception.ServiceException; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.wms.controller.admin.inventory.vo.WmsInventoryListReqVO; import cn.iocoder.yudao.module.wms.controller.admin.inventory.vo.WmsInventoryPageReqVO; import cn.iocoder.yudao.module.wms.dal.dataobject.inventory.WmsInventoryDO; import cn.iocoder.yudao.module.wms.dal.dataobject.inventory.WmsInventoryHistoryDO; @@ -12,6 +13,7 @@ import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemDO; import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO; import cn.iocoder.yudao.module.wms.dal.mysql.inventory.WmsInventoryMapper; import cn.iocoder.yudao.module.wms.service.inventory.dto.WmsInventoryChangeReqDTO; +import cn.iocoder.yudao.module.wms.service.inventory.dto.WmsInventoryCheckReqDTO; import cn.iocoder.yudao.module.wms.service.md.item.WmsItemService; import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService; import jakarta.annotation.Resource; @@ -59,12 +61,44 @@ public class WmsInventoryServiceImpl implements WmsInventoryService { } @Override + public List getInventoryList(WmsInventoryListReqVO listReqVO) { + return inventoryMapper.selectList(listReqVO); + } + + @Override public long getInventoryCountBySkuId(Long skuId) { return inventoryMapper.selectCountBySkuId(skuId); } @Override @Transactional(rollbackFor = Exception.class) + public void checkInventory(WmsInventoryCheckReqDTO reqDTO) { + if (reqDTO == null || CollUtil.isEmpty(reqDTO.getItems())) { + return; + } + + List updateInventories = new ArrayList<>(reqDTO.getItems().size()); + List histories = new ArrayList<>(reqDTO.getItems().size()); + for (WmsInventoryCheckReqDTO.Item item : reqDTO.getItems()) { + WmsInventoryDO inventory = getOrCreateCheckInventory(item); + BigDecimal beforeQuantity = inventory.getQuantity(); + BigDecimal afterQuantity = item.getCheckQuantity(); + if (beforeQuantity.compareTo(afterQuantity) == 0) { + continue; + } + updateInventories.add(new WmsInventoryDO().setId(inventory.getId()).setQuantity(afterQuantity)); + histories.add(buildInventoryHistory(reqDTO, item, beforeQuantity, afterQuantity)); + } + if (CollUtil.isNotEmpty(updateInventories)) { + inventoryMapper.updateBatch(updateInventories); + } + if (CollUtil.isNotEmpty(histories)) { + inventoryHistoryService.createInventoryHistoryList(histories); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) public void changeInventory(WmsInventoryChangeReqDTO reqDTO) { if (reqDTO == null || CollUtil.isEmpty(reqDTO.getItems())) { return; @@ -179,6 +213,43 @@ public class WmsInventoryServiceImpl implements WmsInventoryService { .setOrderId(reqDTO.getOrderId()).setOrderNo(reqDTO.getOrderNo()).setOrderType(reqDTO.getOrderType()); } + private WmsInventoryHistoryDO buildInventoryHistory(WmsInventoryCheckReqDTO reqDTO, + WmsInventoryCheckReqDTO.Item item, + BigDecimal beforeQuantity, + BigDecimal afterQuantity) { + return new WmsInventoryHistoryDO() + .setWarehouseId(item.getWarehouseId()).setSkuId(item.getSkuId()) + .setQuantity(afterQuantity.subtract(beforeQuantity)) + .setBeforeQuantity(beforeQuantity).setAfterQuantity(afterQuantity) + .setPrice(item.getPrice()).setRemark(item.getRemark()) + .setOrderId(reqDTO.getOrderId()).setOrderNo(reqDTO.getOrderNo()).setOrderType(reqDTO.getOrderType()); + } + + private WmsInventoryDO getOrCreateCheckInventory(WmsInventoryCheckReqDTO.Item item) { + if (item.getInventoryId() == null) { + return createCheckInventory(item); + } + WmsInventoryDO inventory = inventoryMapper.selectByIdForUpdate(item.getInventoryId()); + if (inventory == null || !isSameInventory(inventory, item) + || inventory.getQuantity().compareTo(item.getQuantity()) != 0) { + throw exception(CHECK_ORDER_INVENTORY_CHANGED); + } + return inventory; + } + + private WmsInventoryDO createCheckInventory(WmsInventoryCheckReqDTO.Item item) { + WmsInventoryDO inventory = new WmsInventoryDO() + .setSkuId(item.getSkuId()).setWarehouseId(item.getWarehouseId()) + .setQuantity(item.getCheckQuantity()); + try { + inventoryMapper.insert(inventory); + } catch (DuplicateKeyException ex) { + throw exception(CHECK_ORDER_INVENTORY_CHANGED); + } + inventory.setQuantity(BigDecimal.ZERO); + return inventory; + } + private static WmsInventoryDO findInventory(List inventories, WmsInventoryChangeReqDTO.Item item) { return CollUtil.findOne(inventories, inventory -> isSameInventory(inventory, item)); } @@ -192,6 +263,11 @@ public class WmsInventoryServiceImpl implements WmsInventoryService { && ObjectUtil.equal(inventory.getWarehouseId(), item.getWarehouseId()); } + private static boolean isSameInventory(WmsInventoryDO inventory, WmsInventoryCheckReqDTO.Item item) { + return ObjectUtil.equal(inventory.getSkuId(), item.getSkuId()) + && ObjectUtil.equal(inventory.getWarehouseId(), item.getWarehouseId()); + } + private static boolean isSameInventory(WmsInventoryDO inventory, WmsInventoryDO key) { return ObjectUtil.equal(inventory.getSkuId(), key.getSkuId()) && ObjectUtil.equal(inventory.getWarehouseId(), key.getWarehouseId()); diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/md/item/WmsItemSkuServiceImpl.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/md/item/WmsItemSkuServiceImpl.java index b89f054..ddb69b4 100644 --- a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/md/item/WmsItemSkuServiceImpl.java +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/md/item/WmsItemSkuServiceImpl.java @@ -21,6 +21,7 @@ import org.springframework.validation.annotation.Validated; import java.util.Collection; import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Set; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; @@ -58,10 +59,8 @@ public class WmsItemSkuServiceImpl implements WmsItemSkuService { @Transactional(rollbackFor = Exception.class) public void createItemSkuList(Long itemId, List skus) { validateItemSkuList(skus); - List list = BeanUtils.toBean(skus, WmsItemSkuDO.class, sku -> sku.setItemId(itemId)); - if (CollUtil.isNotEmpty(convertList(list, WmsItemSkuDO::getId))) { - throw exception(ITEM_SKU_NOT_EXISTS); - } + List list = BeanUtils.toBean(skus, WmsItemSkuDO.class, sku -> + sku.setItemId(itemId).setId(null)); // id 由数据库自增分配,避免前端误操作 itemSkuMapper.insertBatch(list); } @@ -74,7 +73,7 @@ public class WmsItemSkuServiceImpl implements WmsItemSkuService { List oldList = itemSkuMapper.selectListByItemId(itemId); List newList = BeanUtils.toBean(skus, WmsItemSkuDO.class); List> diffList = diffList(oldList, newList, // id 不同,就认为是不同的记录 - (oldVal, newVal) -> oldVal.getId().equals(newVal.getId())); + (oldVal, newVal) -> Objects.equals(oldVal.getId(), newVal.getId())); // 第二步,批量添加、修改、删除 if (CollUtil.isNotEmpty(diffList.get(2))) { @@ -85,10 +84,11 @@ public class WmsItemSkuServiceImpl implements WmsItemSkuService { itemSkuMapper.deleteByIds(deleteSkuIds); } if (CollUtil.isNotEmpty(diffList.get(0))) { - if (CollUtil.isNotEmpty(convertList(diffList.get(0), WmsItemSkuDO::getId))) { - throw exception(ITEM_SKU_NOT_EXISTS); - } - diffList.get(0).forEach(sku -> sku.setItemId(itemId)); + // 新增场景下忽略前端误传的 SKU id:统一置 null,由数据库自增分配 + diffList.get(0).forEach(sku -> { + sku.setItemId(itemId); + sku.setId(null); + }); itemSkuMapper.insertBatch(diffList.get(0)); } if (CollUtil.isNotEmpty(diffList.get(1))) {