Commit 3fcae795aba5f1afc1684788a1851b4aaf99f70d

Authored by YunaiV
1 parent dc8883d3

feat(wms):增加 inventory

Showing 17 changed files with 738 additions and 5 deletions
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/inventory/WmsInventoryController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.wms.controller.admin.inventory;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
  7 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  8 +import cn.iocoder.yudao.module.wms.controller.admin.inventory.vo.WmsInventoryPageReqVO;
  9 +import cn.iocoder.yudao.module.wms.controller.admin.inventory.vo.WmsInventoryRespVO;
  10 +import cn.iocoder.yudao.module.wms.dal.dataobject.inventory.WmsInventoryDO;
  11 +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemDO;
  12 +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO;
  13 +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseAreaDO;
  14 +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO;
  15 +import cn.iocoder.yudao.module.wms.framework.config.WmsProperties;
  16 +import cn.iocoder.yudao.module.wms.service.inventory.WmsInventoryService;
  17 +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemService;
  18 +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService;
  19 +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseAreaService;
  20 +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseService;
  21 +import io.swagger.v3.oas.annotations.Operation;
  22 +import io.swagger.v3.oas.annotations.tags.Tag;
  23 +import jakarta.annotation.Resource;
  24 +import jakarta.validation.Valid;
  25 +import org.springframework.security.access.prepost.PreAuthorize;
  26 +import org.springframework.validation.annotation.Validated;
  27 +import org.springframework.web.bind.annotation.GetMapping;
  28 +import org.springframework.web.bind.annotation.RequestMapping;
  29 +import org.springframework.web.bind.annotation.RestController;
  30 +
  31 +import java.util.Collections;
  32 +import java.util.List;
  33 +import java.util.Map;
  34 +
  35 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  36 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  37 +
  38 +@Tag(name = "管理后台 - WMS 库存统计")
  39 +@RestController
  40 +@RequestMapping("/wms/inventory")
  41 +@Validated
  42 +public class WmsInventoryController {
  43 +
  44 + @Resource
  45 + private WmsInventoryService inventoryService;
  46 + @Resource
  47 + private WmsItemService itemService;
  48 + @Resource
  49 + private WmsItemSkuService itemSkuService;
  50 + @Resource
  51 + private WmsWarehouseService warehouseService;
  52 + @Resource
  53 + private WmsWarehouseAreaService warehouseAreaService;
  54 + @Resource
  55 + private WmsProperties wmsProperties;
  56 +
  57 + @GetMapping("/page")
  58 + @Operation(summary = "获得库存统计分页")
  59 + @PreAuthorize("@ss.hasPermission('wms:inventory:query')")
  60 + public CommonResult<PageResult<WmsInventoryRespVO>> getInventoryPage(@Valid WmsInventoryPageReqVO pageReqVO) {
  61 + PageResult<WmsInventoryDO> pageResult = inventoryService.getInventoryPage(pageReqVO);
  62 + return success(new PageResult<>(buildInventoryRespVOList(pageResult.getList()), pageResult.getTotal()));
  63 + }
  64 +
  65 + // ==================== 拼接 VO ====================
  66 +
  67 + private List<WmsInventoryRespVO> buildInventoryRespVOList(List<WmsInventoryDO> list) {
  68 + if (CollUtil.isEmpty(list)) {
  69 + return Collections.emptyList();
  70 + }
  71 + Map<Long, WmsItemSkuDO> skuMap = itemSkuService.getItemSkuMap(convertSet(list, WmsInventoryDO::getSkuId));
  72 + Map<Long, WmsItemDO> itemMap = itemService.getItemMap(convertSet(skuMap.values(), WmsItemSkuDO::getItemId));
  73 + Map<Long, WmsWarehouseDO> warehouseMap = warehouseService.getWarehouseMap(
  74 + convertSet(list, WmsInventoryDO::getWarehouseId));
  75 + Map<Long, WmsWarehouseAreaDO> areaMap = wmsProperties.isAreaEnabled()
  76 + ? warehouseAreaService.getWarehouseAreaMap(convertSet(list, WmsInventoryDO::getAreaId))
  77 + : Collections.emptyMap();
  78 + return BeanUtils.toBean(list, WmsInventoryRespVO.class, vo -> {
  79 + MapUtils.findAndThen(skuMap, vo.getSkuId(), sku -> {
  80 + vo.setSkuCode(sku.getCode()).setSkuName(sku.getName()).setItemId(sku.getItemId());
  81 + MapUtils.findAndThen(itemMap, sku.getItemId(), item -> vo.setItemCode(item.getCode())
  82 + .setItemName(item.getName()).setUnit(item.getUnit()));
  83 + });
  84 + MapUtils.findAndThen(warehouseMap, vo.getWarehouseId(), warehouse -> vo.setWarehouseName(warehouse.getName()));
  85 + MapUtils.findAndThen(areaMap, vo.getAreaId(), area -> vo.setAreaName(area.getName()));
  86 + });
  87 + }
  88 +
  89 +}
... ...
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/inventory/vo/WmsInventoryPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.wms.controller.admin.inventory.vo;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +
  9 +import java.math.BigDecimal;
  10 +
  11 +@Schema(description = "管理后台 - WMS 库存统计分页 Request VO")
  12 +@Data
  13 +@EqualsAndHashCode(callSuper = true)
  14 +@ToString(callSuper = true)
  15 +public class WmsInventoryPageReqVO extends PageParam {
  16 +
  17 + /**
  18 + * 按仓库维度统计
  19 + */
  20 + public static final String TYPE_WAREHOUSE = "warehouse";
  21 +
  22 + /**
  23 + * 按库区维度统计
  24 + */
  25 + public static final String TYPE_AREA = "area";
  26 +
  27 + /**
  28 + * 按商品维度统计
  29 + */
  30 + public static final String TYPE_ITEM = "item";
  31 +
  32 + @Schema(description = "统计维度", example = "warehouse")
  33 + private String type;
  34 +
  35 + @Schema(description = "商品编号", example = "ITEM001")
  36 + private String itemCode;
  37 +
  38 + @Schema(description = "商品名称", example = "红富士苹果")
  39 + private String itemName;
  40 +
  41 + @Schema(description = "商品 SKU 编号", example = "1024")
  42 + private Long skuId;
  43 +
  44 + @Schema(description = "规格编号", example = "SKU001")
  45 + private String skuCode;
  46 +
  47 + @Schema(description = "规格名称", example = "10kg 箱装")
  48 + private String skuName;
  49 +
  50 + @Schema(description = "仓库编号", example = "2048")
  51 + private Long warehouseId;
  52 +
  53 + @Schema(description = "库区编号", example = "4096")
  54 + private Long areaId;
  55 +
  56 + @Schema(description = "最小库存数量", example = "0.01")
  57 + private BigDecimal minQuantity;
  58 +
  59 +}
... ...
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/inventory/vo/WmsInventoryRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.wms.controller.admin.inventory.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.math.BigDecimal;
  7 +import java.time.LocalDateTime;
  8 +
  9 +@Schema(description = "管理后台 - WMS 库存统计 Response VO")
  10 +@Data
  11 +public class WmsInventoryRespVO {
  12 +
  13 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  14 + private Long id;
  15 +
  16 + @Schema(description = "商品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
  17 + private Long itemId;
  18 + @Schema(description = "商品编码", example = "ITEM001")
  19 + private String itemCode;
  20 + @Schema(description = "商品名称", example = "红富士苹果")
  21 + private String itemName;
  22 + @Schema(description = "商品单位", example = "箱")
  23 + private String unit;
  24 +
  25 + @Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "4096")
  26 + private Long skuId;
  27 + @Schema(description = "规格编号", example = "SKU001")
  28 + private String skuCode;
  29 + @Schema(description = "规格名称", example = "10kg 箱装")
  30 + private String skuName;
  31 +
  32 + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "8192")
  33 + private Long warehouseId;
  34 + @Schema(description = "仓库名称", example = "成品仓")
  35 + private String warehouseName;
  36 + @Schema(description = "库区编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
  37 + private Long areaId;
  38 + @Schema(description = "库区名称", example = "A 区")
  39 + private String areaName;
  40 +
  41 + @Schema(description = "库存数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00")
  42 + private BigDecimal quantity;
  43 + @Schema(description = "备注", example = "备注")
  44 + private String remark;
  45 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  46 + private LocalDateTime createTime;
  47 +
  48 +}
... ...
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/inventory/WmsInventoryDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.wms.dal.dataobject.inventory;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO;
  5 +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseAreaDO;
  6 +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO;
  7 +import com.baomidou.mybatisplus.annotation.KeySequence;
  8 +import com.baomidou.mybatisplus.annotation.TableId;
  9 +import com.baomidou.mybatisplus.annotation.TableName;
  10 +import lombok.*;
  11 +
  12 +import java.math.BigDecimal;
  13 +
  14 +/**
  15 + * WMS 库存 DO
  16 + *
  17 + * @author 芋道源码
  18 + */
  19 +@TableName("wms_inventory")
  20 +@KeySequence("wms_inventory_seq")
  21 +@Data
  22 +@EqualsAndHashCode(callSuper = true)
  23 +@ToString(callSuper = true)
  24 +@Builder
  25 +@NoArgsConstructor
  26 +@AllArgsConstructor
  27 +public class WmsInventoryDO extends BaseDO {
  28 +
  29 + /**
  30 + * 主键编号
  31 + */
  32 + @TableId
  33 + private Long id;
  34 + /**
  35 + * 商品 SKU 编号
  36 + *
  37 + * 关联 {@link WmsItemSkuDO#getId()}
  38 + */
  39 + private Long skuId;
  40 + /**
  41 + * 仓库编号
  42 + *
  43 + * 关联 {@link WmsWarehouseDO#getId()}
  44 + */
  45 + private Long warehouseId;
  46 + /**
  47 + * 库区编号
  48 + *
  49 + * 关联 {@link WmsWarehouseAreaDO#getId()}
  50 + */
  51 + private Long areaId;
  52 + /**
  53 + * 库存数量
  54 + */
  55 + private BigDecimal quantity;
  56 + /**
  57 + * 备注
  58 + */
  59 + private String remark;
  60 +
  61 +}
... ...
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/inventory/WmsInventoryDetailDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.wms.dal.dataobject.inventory;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO;
  5 +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseAreaDO;
  6 +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO;
  7 +import cn.iocoder.yudao.module.wms.enums.inventory.WmsInventoryOrderTypeEnum;
  8 +import com.baomidou.mybatisplus.annotation.KeySequence;
  9 +import com.baomidou.mybatisplus.annotation.TableId;
  10 +import com.baomidou.mybatisplus.annotation.TableName;
  11 +import lombok.*;
  12 +
  13 +import java.math.BigDecimal;
  14 +import java.time.LocalDateTime;
  15 +
  16 +/**
  17 + * WMS 库存明细 DO
  18 + * <p>
  19 + * 记录可用库存明细余额,用于按库区、批次、效期扣减库存。
  20 + * 库存操作流水见 {@link WmsInventoryHistoryDO}。
  21 + *
  22 + * @author 芋道源码
  23 + */
  24 +@TableName("wms_inventory_detail")
  25 +@KeySequence("wms_inventory_detail_seq")
  26 +@Data
  27 +@EqualsAndHashCode(callSuper = true)
  28 +@ToString(callSuper = true)
  29 +@Builder
  30 +@NoArgsConstructor
  31 +@AllArgsConstructor
  32 +public class WmsInventoryDetailDO extends BaseDO {
  33 +
  34 + /**
  35 + * 主键编号
  36 + */
  37 + @TableId
  38 + private Long id;
  39 +
  40 + // ========= 库存维度相关字段 =========
  41 +
  42 + /**
  43 + * 商品 SKU 编号
  44 + *
  45 + * 关联 {@link WmsItemSkuDO#getId()}
  46 + */
  47 + private Long skuId;
  48 + /**
  49 + * 仓库编号
  50 + *
  51 + * 关联 {@link WmsWarehouseDO#getId()}
  52 + */
  53 + private Long warehouseId;
  54 + /**
  55 + * 库区编号
  56 + *
  57 + * 关联 {@link WmsWarehouseAreaDO#getId()}
  58 + */
  59 + private Long areaId;
  60 + /**
  61 + * 数量
  62 + */
  63 + private BigDecimal quantity;
  64 + /**
  65 + * 剩余数量
  66 + */
  67 + private BigDecimal remainQuantity;
  68 +
  69 + // ========= 批次效期相关字段 =========
  70 +
  71 + /**
  72 + * 批号
  73 + */
  74 + private String batchNo;
  75 + /**
  76 + * 生产日期
  77 + */
  78 + private LocalDateTime productionDate;
  79 + /**
  80 + * 过期日期
  81 + */
  82 + private LocalDateTime expirationDate;
  83 +
  84 + // ========= 金额备注相关字段 =========
  85 +
  86 + /**
  87 + * 金额(小计、非单价)
  88 + */
  89 + private BigDecimal amount;
  90 + /**
  91 + * 备注
  92 + */
  93 + private String remark;
  94 +
  95 + // ========= 来源单据相关字段 =========
  96 +
  97 + /**
  98 + * 操作单编号
  99 + *
  100 + * 关联来源单据的主键编号,单据类型见 {@link #orderType}
  101 + */
  102 + private Long orderId;
  103 + /**
  104 + * 操作单号
  105 + *
  106 + * 关联来源单据的单号,单据类型见 {@link #orderType}
  107 + */
  108 + private String orderNo;
  109 + /**
  110 + * 操作类型
  111 + *
  112 + * 枚举 {@link WmsInventoryOrderTypeEnum#getType()}
  113 + */
  114 + private Integer orderType;
  115 +
  116 +}
... ...
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/inventory/WmsInventoryHistoryDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.wms.dal.dataobject.inventory;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO;
  5 +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseAreaDO;
  6 +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO;
  7 +import cn.iocoder.yudao.module.wms.enums.inventory.WmsInventoryOrderTypeEnum;
  8 +import com.baomidou.mybatisplus.annotation.KeySequence;
  9 +import com.baomidou.mybatisplus.annotation.TableId;
  10 +import com.baomidou.mybatisplus.annotation.TableName;
  11 +import lombok.*;
  12 +
  13 +import java.math.BigDecimal;
  14 +import java.time.LocalDateTime;
  15 +
  16 +/**
  17 + * WMS 库存流水 DO
  18 + *
  19 + * @author 芋道源码
  20 + */
  21 +@TableName("wms_inventory_history")
  22 +@KeySequence("wms_inventory_history_seq")
  23 +@Data
  24 +@EqualsAndHashCode(callSuper = true)
  25 +@ToString(callSuper = true)
  26 +@Builder
  27 +@NoArgsConstructor
  28 +@AllArgsConstructor
  29 +public class WmsInventoryHistoryDO extends BaseDO {
  30 +
  31 + /**
  32 + * 主键编号
  33 + */
  34 + @TableId
  35 + private Long id;
  36 +
  37 + // ========= 库存维度相关字段 =========
  38 +
  39 + /**
  40 + * 仓库编号
  41 + *
  42 + * 关联 {@link WmsWarehouseDO#getId()}
  43 + */
  44 + private Long warehouseId;
  45 + /**
  46 + * 库区编号
  47 + *
  48 + * 关联 {@link WmsWarehouseAreaDO#getId()}
  49 + */
  50 + private Long areaId;
  51 + /**
  52 + * 商品 SKU 编号
  53 + *
  54 + * 关联 {@link WmsItemSkuDO#getId()}
  55 + */
  56 + private Long skuId;
  57 + /**
  58 + * 库存变化数量
  59 + */
  60 + private BigDecimal quantity;
  61 + /**
  62 + * 变化前库存数量
  63 + */
  64 + private BigDecimal beforeQuantity;
  65 + /**
  66 + * 变化后库存数量
  67 + */
  68 + private BigDecimal afterQuantity;
  69 +
  70 + // ========= 批次效期相关字段 =========
  71 +
  72 + /**
  73 + * 批号
  74 + */
  75 + private String batchNo;
  76 + /**
  77 + * 生产日期
  78 + */
  79 + private LocalDateTime productionDate;
  80 + /**
  81 + * 过期日期
  82 + */
  83 + private LocalDateTime expirationDate;
  84 +
  85 + // ========= 金额备注相关字段 =========
  86 +
  87 + /**
  88 + * 金额
  89 + */
  90 + private BigDecimal amount;
  91 + /**
  92 + * 备注
  93 + */
  94 + private String remark;
  95 +
  96 + // ========= 来源单据相关字段 =========
  97 +
  98 + /**
  99 + * 操作单编号
  100 + */
  101 + private Long orderId;
  102 + /**
  103 + * 操作单号
  104 + */
  105 + private String orderNo;
  106 + /**
  107 + * 操作类型
  108 + *
  109 + * 枚举 {@link WmsInventoryOrderTypeEnum#getType()}
  110 + */
  111 + private Integer orderType;
  112 +
  113 +}
... ...
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/inventory/WmsInventoryMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.wms.dal.mysql.inventory;
  2 +
  3 +import cn.hutool.core.util.StrUtil;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  6 +import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
  7 +import cn.iocoder.yudao.module.wms.controller.admin.inventory.vo.WmsInventoryPageReqVO;
  8 +import cn.iocoder.yudao.module.wms.dal.dataobject.inventory.WmsInventoryDO;
  9 +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemDO;
  10 +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO;
  11 +import org.apache.ibatis.annotations.Mapper;
  12 +
  13 +/**
  14 + * WMS 库存 Mapper
  15 + *
  16 + * @author 芋道源码
  17 + */
  18 +@Mapper
  19 +public interface WmsInventoryMapper extends BaseMapperX<WmsInventoryDO> {
  20 +
  21 + default PageResult<WmsInventoryDO> selectPage(WmsInventoryPageReqVO reqVO) {
  22 + MPJLambdaWrapperX<WmsInventoryDO> query = new MPJLambdaWrapperX<WmsInventoryDO>()
  23 + .selectAll(WmsInventoryDO.class)
  24 + .innerJoin(WmsItemSkuDO.class, WmsItemSkuDO::getId, WmsInventoryDO::getSkuId)
  25 + .innerJoin(WmsItemDO.class, WmsItemDO::getId, WmsItemSkuDO::getItemId)
  26 + .likeIfPresent(WmsItemDO::getCode, reqVO.getItemCode())
  27 + .likeIfPresent(WmsItemDO::getName, reqVO.getItemName())
  28 + .eqIfPresent(WmsInventoryDO::getSkuId, reqVO.getSkuId())
  29 + .likeIfPresent(WmsItemSkuDO::getCode, reqVO.getSkuCode())
  30 + .likeIfPresent(WmsItemSkuDO::getName, reqVO.getSkuName())
  31 + .eqIfPresent(WmsInventoryDO::getWarehouseId, reqVO.getWarehouseId())
  32 + .eqIfPresent(WmsInventoryDO::getAreaId, reqVO.getAreaId())
  33 + .geIfPresent(WmsInventoryDO::getQuantity, reqVO.getMinQuantity());
  34 + appendDimensionOrder(query, reqVO.getType());
  35 + return selectJoinPage(reqVO, WmsInventoryDO.class, query);
  36 + }
  37 +
  38 + default Long selectCountBySkuId(Long skuId) {
  39 + return selectCount(WmsInventoryDO::getSkuId, skuId);
  40 + }
  41 +
  42 + private static void appendDimensionOrder(MPJLambdaWrapperX<WmsInventoryDO> query, String type) {
  43 + if (StrUtil.equals(WmsInventoryPageReqVO.TYPE_WAREHOUSE, type)) {
  44 + query.orderByAsc(WmsInventoryDO::getWarehouseId)
  45 + .orderByAsc(WmsItemSkuDO::getItemId)
  46 + .orderByAsc(WmsInventoryDO::getSkuId)
  47 + .orderByAsc(WmsInventoryDO::getId);
  48 + return;
  49 + }
  50 + if (StrUtil.equals(WmsInventoryPageReqVO.TYPE_ITEM, type)) {
  51 + query.orderByAsc(WmsItemSkuDO::getItemId)
  52 + .orderByAsc(WmsInventoryDO::getSkuId)
  53 + .orderByAsc(WmsInventoryDO::getWarehouseId)
  54 + .orderByAsc(WmsInventoryDO::getAreaId)
  55 + .orderByAsc(WmsInventoryDO::getId);
  56 + return;
  57 + }
  58 + if (StrUtil.equals(WmsInventoryPageReqVO.TYPE_AREA, type)) {
  59 + query.orderByAsc(WmsInventoryDO::getWarehouseId)
  60 + .orderByAsc(WmsInventoryDO::getAreaId)
  61 + .orderByAsc(WmsItemSkuDO::getItemId)
  62 + .orderByAsc(WmsInventoryDO::getSkuId)
  63 + .orderByAsc(WmsInventoryDO::getId);
  64 + return;
  65 + }
  66 + throw new IllegalArgumentException("未知库存统计维度:" + type);
  67 + }
  68 +
  69 +}
... ...
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/md/item/WmsItemSkuMapper.java
... ... @@ -33,6 +33,17 @@ public interface WmsItemSkuMapper extends BaseMapperX&lt;WmsItemSkuDO&gt; {
33 33 .orderByAsc(WmsItemSkuDO::getId));
34 34 }
35 35  
  36 + default List<WmsItemSkuDO> selectList(Collection<Long> itemIds, String code, String name) {
  37 + if (itemIds != null && CollUtil.isEmpty(itemIds)) {
  38 + return Collections.emptyList();
  39 + }
  40 + return selectList(new LambdaQueryWrapperX<WmsItemSkuDO>()
  41 + .inIfPresent(WmsItemSkuDO::getItemId, itemIds)
  42 + .likeIfPresent(WmsItemSkuDO::getCode, code)
  43 + .likeIfPresent(WmsItemSkuDO::getName, name)
  44 + .orderByAsc(WmsItemSkuDO::getId));
  45 + }
  46 +
36 47 default void deleteByItemId(Long itemId) {
37 48 delete(WmsItemSkuDO::getItemId, itemId);
38 49 }
... ...
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/enums/ErrorCodeConstants.java
... ... @@ -42,6 +42,7 @@ public interface ErrorCodeConstants {
42 42 ErrorCode ITEM_SKU_REQUIRED = new ErrorCode(1_060_104_002, "至少包含一个商品规格");
43 43 ErrorCode ITEM_SKU_NAME_DUPLICATE = new ErrorCode(1_060_104_003, "商品规格名称【{}】重复");
44 44 ErrorCode ITEM_SKU_NOT_EXISTS = new ErrorCode(1_060_104_004, "商品规格不存在");
  45 + ErrorCode ITEM_SKU_HAS_INVENTORY = new ErrorCode(1_060_104_005, "删除失败!商品规格【{}】已被库存业务使用!");
45 46  
46 47 // ========== WMS 基础数据-往来企业 1-060-105-000 ==========
47 48 ErrorCode MERCHANT_NOT_EXISTS = new ErrorCode(1_060_105_000, "往来企业不存在");
... ...
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/enums/inventory/WmsInventoryOrderTypeEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.wms.enums.inventory;
  2 +
  3 +import cn.iocoder.yudao.framework.common.core.ArrayValuable;
  4 +import lombok.AllArgsConstructor;
  5 +import lombok.Getter;
  6 +
  7 +import java.util.Arrays;
  8 +
  9 +/**
  10 + * WMS 库存操作类型枚举
  11 + *
  12 + * @author 芋道源码
  13 + */
  14 +@Getter
  15 +@AllArgsConstructor
  16 +public enum WmsInventoryOrderTypeEnum implements ArrayValuable<Integer> {
  17 +
  18 + RECEIPT(1, "入库"),
  19 + SHIPMENT(2, "出库"),
  20 + MOVEMENT(3, "移库"),
  21 + CHECK(4, "盘库");
  22 +
  23 + public static final Integer[] ARRAYS = Arrays.stream(values()).map(WmsInventoryOrderTypeEnum::getType).toArray(Integer[]::new);
  24 +
  25 + /**
  26 + * 类型
  27 + */
  28 + private final Integer type;
  29 + /**
  30 + * 名称
  31 + */
  32 + private final String name;
  33 +
  34 + @Override
  35 + public Integer[] array() {
  36 + return ARRAYS;
  37 + }
  38 +
  39 +}
... ...
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/framework/config/WmsProperties.java
... ... @@ -21,7 +21,7 @@ public class WmsProperties {
21 21 * 是否启用库区模式
22 22 */
23 23 @NotNull(message = "是否启用库区模式不能为空")
24   - private Boolean areaEnable = false;
  24 + private Boolean areaEnable = true;
25 25  
26 26 /**
27 27 * 是否启用批次/效期/库存明细模式
... ... @@ -29,7 +29,7 @@ public class WmsProperties {
29 29 * 依赖 {@link #areaEnable} 为 true
30 30 */
31 31 @NotNull(message = "是否启用批次/效期/库存明细模式不能为空")
32   - private Boolean batchEnable = false;
  32 + private Boolean batchEnable = true;
33 33  
34 34 public boolean isAreaEnabled() {
35 35 return Boolean.TRUE.equals(areaEnable);
... ...
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/md/item/WmsItemService.java
... ... @@ -6,7 +6,11 @@ import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.item.WmsItemSaveR
6 6 import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemDO;
7 7 import jakarta.validation.Valid;
8 8  
  9 +import java.util.Collection;
9 10 import java.util.List;
  11 +import java.util.Map;
  12 +
  13 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
10 14  
11 15 /**
12 16 * WMS 商品 Service 接口
... ... @@ -70,6 +74,24 @@ public interface WmsItemService {
70 74 List<WmsItemDO> getItemList(WmsItemPageReqVO pageReqVO);
71 75  
72 76 /**
  77 + * 按编号集合获得商品列表
  78 + *
  79 + * @param ids 编号集合
  80 + * @return 商品列表
  81 + */
  82 + List<WmsItemDO> getItemList(Collection<Long> ids);
  83 +
  84 + /**
  85 + * 按编号集合获得商品 Map
  86 + *
  87 + * @param ids 编号集合
  88 + * @return 商品 Map
  89 + */
  90 + default Map<Long, WmsItemDO> getItemMap(Collection<Long> ids) {
  91 + return convertMap(getItemList(ids), WmsItemDO::getId);
  92 + }
  93 +
  94 + /**
73 95 * 获得指定商品分类下的商品数量
74 96 *
75 97 * @param categoryId 商品分类编号
... ...
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/md/item/WmsItemServiceImpl.java
1 1 package cn.iocoder.yudao.module.wms.service.md.item;
2 2  
  3 +import cn.hutool.core.collection.CollUtil;
3 4 import cn.hutool.core.util.ObjectUtil;
4 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
... ... @@ -13,6 +14,8 @@ import org.springframework.stereotype.Service;
13 14 import org.springframework.transaction.annotation.Transactional;
14 15 import org.springframework.validation.annotation.Validated;
15 16  
  17 +import java.util.Collection;
  18 +import java.util.Collections;
16 19 import java.util.List;
17 20  
18 21 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
... ... @@ -135,6 +138,14 @@ public class WmsItemServiceImpl implements WmsItemService {
135 138 }
136 139  
137 140 @Override
  141 + public List<WmsItemDO> getItemList(Collection<Long> ids) {
  142 + if (CollUtil.isEmpty(ids)) {
  143 + return Collections.emptyList();
  144 + }
  145 + return itemMapper.selectByIds(ids);
  146 + }
  147 +
  148 + @Override
138 149 public long getItemCountByCategoryId(Long categoryId) {
139 150 return itemMapper.selectCountByCategoryId(categoryId);
140 151 }
... ...
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/md/item/WmsItemSkuService.java
... ... @@ -8,6 +8,7 @@ import java.util.Collection;
8 8 import java.util.List;
9 9 import java.util.Map;
10 10  
  11 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
11 12 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;
12 13  
13 14 /**
... ... @@ -57,6 +58,34 @@ public interface WmsItemSkuService {
57 58 List<WmsItemSkuDO> getItemSkuList(Collection<Long> itemIds);
58 59  
59 60 /**
  61 + * 获得 SKU 列表
  62 + *
  63 + * @param itemIds 商品编号集合
  64 + * @param code SKU 编号
  65 + * @param name SKU 名称
  66 + * @return SKU 列表
  67 + */
  68 + List<WmsItemSkuDO> getItemSkuList(Collection<Long> itemIds, String code, String name);
  69 +
  70 + /**
  71 + * 按编号集合获得 SKU 列表
  72 + *
  73 + * @param ids 编号集合
  74 + * @return SKU 列表
  75 + */
  76 + List<WmsItemSkuDO> getItemSkuListByIds(Collection<Long> ids);
  77 +
  78 + /**
  79 + * 按编号集合获得 SKU Map
  80 + *
  81 + * @param ids 编号集合
  82 + * @return SKU Map
  83 + */
  84 + default Map<Long, WmsItemSkuDO> getItemSkuMap(Collection<Long> ids) {
  85 + return convertMap(getItemSkuListByIds(ids), WmsItemSkuDO::getId);
  86 + }
  87 +
  88 + /**
60 89 * 按商品编号集合获得 SKU MultiMap
61 90 *
62 91 * @param itemIds 商品编号集合
... ...
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/md/item/WmsItemSkuServiceImpl.java
... ... @@ -6,8 +6,10 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6 6 import cn.iocoder.yudao.module.wms.controller.admin.md.item.vo.sku.WmsItemSkuSaveReqVO;
7 7 import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO;
8 8 import cn.iocoder.yudao.module.wms.dal.mysql.md.item.WmsItemSkuMapper;
  9 +import cn.iocoder.yudao.module.wms.service.inventory.WmsInventoryService;
9 10 import cn.iocoder.yudao.module.wms.util.WmsUtils;
10 11 import jakarta.annotation.Resource;
  12 +import org.springframework.context.annotation.Lazy;
11 13 import org.springframework.stereotype.Service;
12 14 import org.springframework.transaction.annotation.Transactional;
13 15 import org.springframework.validation.annotation.Validated;
... ... @@ -32,6 +34,9 @@ public class WmsItemSkuServiceImpl implements WmsItemSkuService {
32 34  
33 35 @Resource
34 36 private WmsItemSkuMapper itemSkuMapper;
  37 + @Resource
  38 + @Lazy // 延迟加载,避免循环依赖
  39 + private WmsInventoryService inventoryService;
35 40  
36 41 @Override
37 42 @Transactional(rollbackFor = Exception.class)
... ... @@ -58,8 +63,9 @@ public class WmsItemSkuServiceImpl implements WmsItemSkuService {
58 63  
59 64 // 第二步,批量添加、修改、删除
60 65 if (CollUtil.isNotEmpty(diffList.get(2))) {
61   - // TODO 库存模块实现后,校验商品规格下不存在库存业务数据后再删除
62   - itemSkuMapper.deleteByIds(convertList(diffList.get(2), WmsItemSkuDO::getId));
  66 + List<Long> deleteSkuIds = convertList(diffList.get(2), WmsItemSkuDO::getId);
  67 + validateItemSkuUnused(diffList.get(2));
  68 + itemSkuMapper.deleteByIds(deleteSkuIds);
63 69 }
64 70 if (CollUtil.isNotEmpty(diffList.get(0))) {
65 71 if (CollUtil.isNotEmpty(convertList(diffList.get(0), WmsItemSkuDO::getId))) {
... ... @@ -79,7 +85,8 @@ public class WmsItemSkuServiceImpl implements WmsItemSkuService {
79 85 @Override
80 86 @Transactional(rollbackFor = Exception.class)
81 87 public void deleteItemSkuListByItemId(Long itemId) {
82   - // TODO 库存模块实现后,校验商品规格下不存在库存业务数据后再删除
  88 + List<WmsItemSkuDO> skus = itemSkuMapper.selectListByItemId(itemId);
  89 + validateItemSkuUnused(skus);
83 90 itemSkuMapper.deleteByItemId(itemId);
84 91 }
85 92  
... ... @@ -93,6 +100,19 @@ public class WmsItemSkuServiceImpl implements WmsItemSkuService {
93 100 return itemSkuMapper.selectListByItemIds(itemIds);
94 101 }
95 102  
  103 + @Override
  104 + public List<WmsItemSkuDO> getItemSkuList(Collection<Long> itemIds, String code, String name) {
  105 + return itemSkuMapper.selectList(itemIds, code, name);
  106 + }
  107 +
  108 + @Override
  109 + public List<WmsItemSkuDO> getItemSkuListByIds(Collection<Long> ids) {
  110 + if (CollUtil.isEmpty(ids)) {
  111 + return List.of();
  112 + }
  113 + return itemSkuMapper.selectByIds(ids);
  114 + }
  115 +
96 116 private void validateItemSkuList(List<WmsItemSkuSaveReqVO> skus) {
97 117 // 校验至少存在一个商品 SKU
98 118 if (CollUtil.isEmpty(skus)) {
... ... @@ -107,6 +127,17 @@ public class WmsItemSkuServiceImpl implements WmsItemSkuService {
107 127 }
108 128 }
109 129  
  130 + private void validateItemSkuUnused(List<WmsItemSkuDO> skus) {
  131 + if (CollUtil.isEmpty(skus)) {
  132 + return;
  133 + }
  134 + for (WmsItemSkuDO sku : skus) {
  135 + if (inventoryService.getInventoryCountBySkuId(sku.getId()) > 0) {
  136 + throw exception(ITEM_SKU_HAS_INVENTORY, sku.getName());
  137 + }
  138 + }
  139 + }
  140 +
110 141 /**
111 142 * 填充空的条形码
112 143 *
... ...
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/md/warehouse/WmsWarehouseAreaService.java
... ... @@ -6,7 +6,11 @@ import cn.iocoder.yudao.module.wms.controller.admin.md.warehouse.vo.area.WmsWare
6 6 import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseAreaDO;
7 7 import jakarta.validation.Valid;
8 8  
  9 +import java.util.Collection;
9 10 import java.util.List;
  11 +import java.util.Map;
  12 +
  13 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
10 14  
11 15 /**
12 16 * WMS 库区 Service 接口
... ... @@ -69,4 +73,22 @@ public interface WmsWarehouseAreaService {
69 73 */
70 74 List<WmsWarehouseAreaDO> getWarehouseAreaList(Long warehouseId);
71 75  
  76 + /**
  77 + * 按编号集合获得库区列表
  78 + *
  79 + * @param ids 编号集合
  80 + * @return 库区列表
  81 + */
  82 + List<WmsWarehouseAreaDO> getWarehouseAreaList(Collection<Long> ids);
  83 +
  84 + /**
  85 + * 按编号集合获得库区 Map
  86 + *
  87 + * @param ids 编号集合
  88 + * @return 库区 Map
  89 + */
  90 + default Map<Long, WmsWarehouseAreaDO> getWarehouseAreaMap(Collection<Long> ids) {
  91 + return convertMap(getWarehouseAreaList(ids), WmsWarehouseAreaDO::getId);
  92 + }
  93 +
72 94 }
... ...
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/md/warehouse/WmsWarehouseAreaServiceImpl.java
1 1 package cn.iocoder.yudao.module.wms.service.md.warehouse;
2 2  
  3 +import cn.hutool.core.collection.CollUtil;
3 4 import cn.hutool.core.util.ObjUtil;
4 5 import cn.hutool.core.util.StrUtil;
5 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
... ... @@ -13,6 +14,8 @@ import jakarta.annotation.Resource;
13 14 import org.springframework.stereotype.Service;
14 15 import org.springframework.validation.annotation.Validated;
15 16  
  17 +import java.util.Collection;
  18 +import java.util.Collections;
16 19 import java.util.List;
17 20  
18 21 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
... ... @@ -127,6 +130,15 @@ public class WmsWarehouseAreaServiceImpl implements WmsWarehouseAreaService {
127 130 return warehouseAreaMapper.selectListByWarehouseId(warehouseId);
128 131 }
129 132  
  133 + @Override
  134 + public List<WmsWarehouseAreaDO> getWarehouseAreaList(Collection<Long> ids) {
  135 + validateAreaEnabled();
  136 + if (CollUtil.isEmpty(ids)) {
  137 + return Collections.emptyList();
  138 + }
  139 + return warehouseAreaMapper.selectByIds(ids);
  140 + }
  141 +
130 142 private void validateAreaEnabled() {
131 143 if (!wmsProperties.isAreaEnabled()) {
132 144 throw exception(WMS_AREA_DISABLED);
... ...