Commit 2d49ac272ee5e6c775e3a42e233711a9ec6e6e21

Authored by YunaiV
1 parent f7b06a09

feat(mes): 新增仓库管理-库存台账模块(wm_material_stock)

从 ktg-mes 迁移库存台账功能,支持库存列表查看、按条件搜索、冻结/解冻、导出 Excel。
去除 16 个冗余字段,仅存储 ID 关联,由 Controller 层批量拼接名称。

新增:
- SQL DDL(mes_wm_material_stock 表 + 菜单权限)
- MesWmMaterialStockDO / PageReqVO / RespVO / SaveReqVO
- MesWmMaterialStockMapper(分页 + 计数)
- MesWmMaterialStockService / ServiceImpl
- MesWmMaterialStockController(get/page/update-frozen/export-excel)
- ErrorCodeConstants 新增 3 个错误码

修改:
- MesWmWarehouseServiceImpl:删除仓库时校验库存记录
- MesWmWarehouseLocationServiceImpl:删除库区时校验库存记录
- MesWmWarehouseAreaServiceImpl:删除库位时校验库存记录
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/MesWmMaterialStockController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.controller.admin.wm.materialstock;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
  5 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  6 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  7 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  8 +import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
  9 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  10 +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
  11 +import cn.iocoder.yudao.module.mes.controller.admin.wm.materialstock.vo.MesWmMaterialStockPageReqVO;
  12 +import cn.iocoder.yudao.module.mes.controller.admin.wm.materialstock.vo.MesWmMaterialStockRespVO;
  13 +import cn.iocoder.yudao.module.mes.controller.admin.wm.materialstock.vo.MesWmMaterialStockSaveReqVO;
  14 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
  15 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO;
  16 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.vendor.MesMdVendorDO;
  17 +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
  18 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterialStockDO;
  19 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO;
  20 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO;
  21 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseLocationDO;
  22 +import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
  23 +import cn.iocoder.yudao.module.mes.service.md.unitmeasure.MesMdUnitMeasureService;
  24 +import cn.iocoder.yudao.module.mes.service.md.vendor.MesMdVendorService;
  25 +import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService;
  26 +import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService;
  27 +import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService;
  28 +import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseLocationService;
  29 +import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseService;
  30 +import io.swagger.v3.oas.annotations.Operation;
  31 +import io.swagger.v3.oas.annotations.Parameter;
  32 +import io.swagger.v3.oas.annotations.tags.Tag;
  33 +import jakarta.annotation.Resource;
  34 +import jakarta.servlet.http.HttpServletResponse;
  35 +import jakarta.validation.Valid;
  36 +import org.springframework.security.access.prepost.PreAuthorize;
  37 +import org.springframework.validation.annotation.Validated;
  38 +import org.springframework.web.bind.annotation.*;
  39 +
  40 +import java.io.IOException;
  41 +import java.util.Collections;
  42 +import java.util.List;
  43 +import java.util.Map;
  44 +
  45 +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
  46 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  47 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  48 +
  49 +@Tag(name = "管理后台 - MES 库存台账")
  50 +@RestController
  51 +@RequestMapping("/mes/wm/material-stock")
  52 +@Validated
  53 +public class MesWmMaterialStockController {
  54 +
  55 + @Resource
  56 + private MesWmMaterialStockService materialStockService;
  57 +
  58 + @Resource
  59 + private MesMdItemService itemService;
  60 +
  61 + @Resource
  62 + private MesMdUnitMeasureService unitMeasureService;
  63 +
  64 + @Resource
  65 + private MesWmWarehouseService warehouseService;
  66 +
  67 + @Resource
  68 + private MesWmWarehouseLocationService locationService;
  69 +
  70 + @Resource
  71 + private MesWmWarehouseAreaService areaService;
  72 +
  73 + @Resource
  74 + private MesMdVendorService vendorService;
  75 +
  76 + @Resource
  77 + private MesProWorkOrderService workOrderService;
  78 +
  79 + @GetMapping("/get")
  80 + @Operation(summary = "获得库存记录")
  81 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  82 + @PreAuthorize("@ss.hasPermission('mes:wm-material-stock:query')")
  83 + public CommonResult<MesWmMaterialStockRespVO> getMaterialStock(@RequestParam("id") Long id) {
  84 + MesWmMaterialStockDO stock = materialStockService.getMaterialStock(id);
  85 + if (stock == null) {
  86 + return success(null);
  87 + }
  88 + return success(buildRespVOList(Collections.singletonList(stock)).get(0));
  89 + }
  90 +
  91 + @GetMapping("/page")
  92 + @Operation(summary = "获得库存台账分页")
  93 + @PreAuthorize("@ss.hasPermission('mes:wm-material-stock:query')")
  94 + public CommonResult<PageResult<MesWmMaterialStockRespVO>> getMaterialStockPage(
  95 + @Valid MesWmMaterialStockPageReqVO pageReqVO) {
  96 + PageResult<MesWmMaterialStockDO> pageResult = materialStockService.getMaterialStockPage(pageReqVO);
  97 + return success(new PageResult<>(buildRespVOList(pageResult.getList()), pageResult.getTotal()));
  98 + }
  99 +
  100 + @PutMapping("/update-frozen")
  101 + @Operation(summary = "更新库存冻结状态")
  102 + @PreAuthorize("@ss.hasPermission('mes:wm-material-stock:update')")
  103 + public CommonResult<Boolean> updateMaterialStockFrozen(
  104 + @Valid @RequestBody MesWmMaterialStockSaveReqVO updateReqVO) {
  105 + materialStockService.updateMaterialStockFrozen(updateReqVO);
  106 + return success(true);
  107 + }
  108 +
  109 + @GetMapping("/export-excel")
  110 + @Operation(summary = "导出库存台账 Excel")
  111 + @PreAuthorize("@ss.hasPermission('mes:wm-material-stock:export')")
  112 + @ApiAccessLog(operateType = EXPORT)
  113 + public void exportMaterialStockExcel(@Valid MesWmMaterialStockPageReqVO pageReqVO,
  114 + HttpServletResponse response) throws IOException {
  115 + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
  116 + PageResult<MesWmMaterialStockDO> pageResult = materialStockService.getMaterialStockPage(pageReqVO);
  117 + ExcelUtils.write(response, "库存台账.xls", "数据", MesWmMaterialStockRespVO.class,
  118 + buildRespVOList(pageResult.getList()));
  119 + }
  120 +
  121 + // ==================== 拼接 VO ====================
  122 +
  123 + private List<MesWmMaterialStockRespVO> buildRespVOList(List<MesWmMaterialStockDO> list) {
  124 + if (CollUtil.isEmpty(list)) {
  125 + return Collections.emptyList();
  126 + }
  127 + // 1. 获得关联数据
  128 + Map<Long, MesMdItemDO> itemMap = itemService.getItemMap(
  129 + convertSet(list, MesWmMaterialStockDO::getItemId));
  130 + Map<Long, MesMdUnitMeasureDO> unitMeasureMap = unitMeasureService.getUnitMeasureMap(
  131 + convertSet(list, MesWmMaterialStockDO::getUnitMeasureId));
  132 + Map<Long, MesWmWarehouseDO> warehouseMap = warehouseService.getWarehouseMap(
  133 + convertSet(list, MesWmMaterialStockDO::getWarehouseId));
  134 + Map<Long, MesWmWarehouseLocationDO> locationMap = locationService.getWarehouseLocationMap(
  135 + convertSet(list, MesWmMaterialStockDO::getLocationId));
  136 + Map<Long, MesWmWarehouseAreaDO> areaMap = areaService.getWarehouseAreaMap(
  137 + convertSet(list, MesWmMaterialStockDO::getAreaId));
  138 + Map<Long, MesMdVendorDO> vendorMap = vendorService.getVendorMap(
  139 + convertSet(list, MesWmMaterialStockDO::getVendorId));
  140 + Map<Long, MesProWorkOrderDO> workOrderMap = workOrderService.getWorkOrderMap(
  141 + convertSet(list, MesWmMaterialStockDO::getWorkOrderId));
  142 + // TODO @芋艿:待 mes_wm_batch 模块迁移后,补充 batchCode 的拼接
  143 +
  144 + // 2. 构建结果
  145 + return BeanUtils.toBean(list, MesWmMaterialStockRespVO.class, vo -> {
  146 + MapUtils.findAndThen(itemMap, vo.getItemId(), item ->
  147 + vo.setItemCode(item.getCode()).setItemName(item.getName()).setSpecification(item.getSpecification()));
  148 + MapUtils.findAndThen(unitMeasureMap, vo.getUnitMeasureId(),
  149 + unitMeasure -> vo.setUnitMeasureName(unitMeasure.getName()));
  150 + MapUtils.findAndThen(warehouseMap, vo.getWarehouseId(),
  151 + warehouse -> vo.setWarehouseName(warehouse.getName()));
  152 + MapUtils.findAndThen(locationMap, vo.getLocationId(),
  153 + location -> vo.setLocationName(location.getName()));
  154 + MapUtils.findAndThen(areaMap, vo.getAreaId(),
  155 + area -> vo.setAreaName(area.getName()));
  156 + MapUtils.findAndThen(vendorMap, vo.getVendorId(),
  157 + vendor -> vo.setVendorName(vendor.getName()));
  158 + MapUtils.findAndThen(workOrderMap, vo.getWorkOrderId(),
  159 + workOrder -> vo.setWorkOrderCode(workOrder.getCode()));
  160 + });
  161 + }
  162 +
  163 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/vo/MesWmMaterialStockPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.controller.admin.wm.materialstock.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 +@Schema(description = "管理后台 - MES 库存台账分页 Request VO")
  10 +@Data
  11 +@EqualsAndHashCode(callSuper = true)
  12 +@ToString(callSuper = true)
  13 +public class MesWmMaterialStockPageReqVO extends PageParam {
  14 +
  15 + @Schema(description = "物料分类编号", example = "1")
  16 + private Long itemTypeId;
  17 +
  18 + @Schema(description = "物料编码", example = "M001")
  19 + private String itemCode;
  20 +
  21 + @Schema(description = "物料名称", example = "钢板")
  22 + private String itemName;
  23 +
  24 + @Schema(description = "批次号", example = "B20260101")
  25 + private String batchCode;
  26 +
  27 + @Schema(description = "仓库编号", example = "1")
  28 + private Long warehouseId;
  29 +
  30 + @Schema(description = "库区编号", example = "1")
  31 + private Long locationId;
  32 +
  33 + @Schema(description = "是否冻结", example = "false")
  34 + private Boolean frozen;
  35 +
  36 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/vo/MesWmMaterialStockRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.controller.admin.wm.materialstock.vo;
  2 +
  3 +import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
  4 +import cn.idev.excel.annotation.ExcelProperty;
  5 +import io.swagger.v3.oas.annotations.media.Schema;
  6 +import lombok.Data;
  7 +
  8 +import java.math.BigDecimal;
  9 +import java.time.LocalDateTime;
  10 +
  11 +@Schema(description = "管理后台 - MES 库存台账 Response VO")
  12 +@Data
  13 +@ExcelIgnoreUnannotated
  14 +public class MesWmMaterialStockRespVO {
  15 +
  16 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  17 + @ExcelProperty("编号")
  18 + private Long id;
  19 +
  20 + @Schema(description = "物料分类编号", example = "1")
  21 + private Long itemTypeId;
  22 +
  23 + @Schema(description = "物料编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  24 + private Long itemId;
  25 +
  26 + @Schema(description = "物料编码", example = "M001")
  27 + @ExcelProperty("物料编码")
  28 + private String itemCode;
  29 +
  30 + @Schema(description = "物料名称", example = "钢板")
  31 + @ExcelProperty("物料名称")
  32 + private String itemName;
  33 +
  34 + @Schema(description = "规格型号", example = "10mm*100mm")
  35 + @ExcelProperty("规格型号")
  36 + private String specification;
  37 +
  38 + @Schema(description = "计量单位编号", example = "1")
  39 + private Long unitMeasureId;
  40 +
  41 + @Schema(description = "计量单位名称", example = "千克")
  42 + @ExcelProperty("计量单位")
  43 + private String unitMeasureName;
  44 +
  45 + @Schema(description = "批次编号", example = "1")
  46 + private Long batchId;
  47 +
  48 + @Schema(description = "批次号", example = "B20260101")
  49 + @ExcelProperty("批次号")
  50 + private String batchCode;
  51 +
  52 + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  53 + private Long warehouseId;
  54 +
  55 + @Schema(description = "仓库名称", example = "原料仓")
  56 + @ExcelProperty("仓库")
  57 + private String warehouseName;
  58 +
  59 + @Schema(description = "库区编号", example = "1")
  60 + private Long locationId;
  61 +
  62 + @Schema(description = "库区名称", example = "A 区")
  63 + @ExcelProperty("库区")
  64 + private String locationName;
  65 +
  66 + @Schema(description = "库位编号", example = "1")
  67 + private Long areaId;
  68 +
  69 + @Schema(description = "库位名称", example = "A-01")
  70 + @ExcelProperty("库位")
  71 + private String areaName;
  72 +
  73 + @Schema(description = "供应商编号", example = "1")
  74 + private Long vendorId;
  75 +
  76 + @Schema(description = "供应商名称", example = "某供应商")
  77 + @ExcelProperty("供应商")
  78 + private String vendorName;
  79 +
  80 + @Schema(description = "生产工单编号", example = "1")
  81 + private Long workOrderId;
  82 +
  83 + @Schema(description = "生产工单编码", example = "WO20260101")
  84 + @ExcelProperty("生产工单")
  85 + private String workOrderCode;
  86 +
  87 + @Schema(description = "在库数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.0000")
  88 + @ExcelProperty("在库数量")
  89 + private BigDecimal quantityOnhand;
  90 +
  91 + @Schema(description = "入库时间")
  92 + @ExcelProperty("入库时间")
  93 + private LocalDateTime recptDate;
  94 +
  95 + @Schema(description = "生产日期")
  96 + @ExcelProperty("生产日期")
  97 + private LocalDateTime productionDate;
  98 +
  99 + @Schema(description = "库存有效期")
  100 + @ExcelProperty("有效期")
  101 + private LocalDateTime expireDate;
  102 +
  103 + @Schema(description = "是否冻结", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
  104 + @ExcelProperty("是否冻结")
  105 + private Boolean frozen;
  106 +
  107 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  108 + @ExcelProperty("创建时间")
  109 + private LocalDateTime createTime;
  110 +
  111 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/vo/MesWmMaterialStockSaveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.controller.admin.wm.materialstock.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotNull;
  5 +import lombok.Data;
  6 +
  7 +// TODO @AI:不应该叫 save?!
  8 +@Schema(description = "管理后台 - MES 库存台账冻结/解冻 Request VO")
  9 +@Data
  10 +public class MesWmMaterialStockSaveReqVO {
  11 +
  12 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  13 + @NotNull(message = "编号不能为空")
  14 + private Long id;
  15 +
  16 + @Schema(description = "是否冻结", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
  17 + @NotNull(message = "冻结状态不能为空")
  18 + private Boolean frozen;
  19 +
  20 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/materialstock/MesWmMaterialStockDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
  5 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemTypeDO;
  6 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO;
  7 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.vendor.MesMdVendorDO;
  8 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO;
  9 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO;
  10 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseLocationDO;
  11 +import com.baomidou.mybatisplus.annotation.KeySequence;
  12 +import com.baomidou.mybatisplus.annotation.TableId;
  13 +import com.baomidou.mybatisplus.annotation.TableName;
  14 +import lombok.*;
  15 +
  16 +import java.math.BigDecimal;
  17 +import java.time.LocalDateTime;
  18 +
  19 +// TODO @芋艿:这个包名,还有优化的空间么?类似 erp_stock_record?
  20 +/**
  21 + * MES 库存台账(仓库现有量)DO
  22 + */
  23 +@TableName("mes_wm_material_stock")
  24 +@KeySequence("mes_wm_material_stock_seq")
  25 +@Data
  26 +@EqualsAndHashCode(callSuper = true)
  27 +@ToString(callSuper = true)
  28 +@Builder
  29 +@NoArgsConstructor
  30 +@AllArgsConstructor
  31 +public class MesWmMaterialStockDO extends BaseDO {
  32 +
  33 + /**
  34 + * 编号
  35 + */
  36 + @TableId
  37 + private Long id;
  38 + // TODO @AI:是不是不用冗余?通过 itemId 查询;
  39 + /**
  40 + * 物料分类编号
  41 + *
  42 + * 关联 {@link MesMdItemTypeDO#getId()}
  43 + */
  44 + private Long itemTypeId;
  45 + /**
  46 + * 物料编号
  47 + *
  48 + * 关联 {@link MesMdItemDO#getId()}
  49 + */
  50 + private Long itemId;
  51 + // TODO @AI:是不是不用冗余?通过 itemId 查询;
  52 + /**
  53 + * 计量单位编号
  54 + *
  55 + * 关联 {@link MesMdUnitMeasureDO#getId()}
  56 + */
  57 + private Long unitMeasureId;
  58 + /**
  59 + * 批次编号
  60 + *
  61 + * TODO @芋艿:待 mes_wm_batch 模块迁移后关联
  62 + */
  63 + private Long batchId;
  64 + /**
  65 + * 仓库编号
  66 + *
  67 + * 关联 {@link MesWmWarehouseDO#getId()}
  68 + */
  69 + private Long warehouseId;
  70 + /**
  71 + * 库区编号
  72 + *
  73 + * 关联 {@link MesWmWarehouseLocationDO#getId()}
  74 + */
  75 + private Long locationId;
  76 + /**
  77 + * 库位编号
  78 + *
  79 + * 关联 {@link MesWmWarehouseAreaDO#getId()}
  80 + */
  81 + private Long areaId;
  82 + /**
  83 + * 供应商编号
  84 + *
  85 + * 关联 {@link MesMdVendorDO#getId()}
  86 + */
  87 + private Long vendorId;
  88 + // TODO @AI:去关联下;
  89 + /**
  90 + * 生产工单编号
  91 + *
  92 + * TODO @芋艿:待 mes_pro_work_order 模块完成后关联
  93 + */
  94 + private Long workOrderId;
  95 + /**
  96 + * 在库数量
  97 + */
  98 + private BigDecimal quantityOnhand;
  99 + /**
  100 + * 入库时间
  101 + */
  102 + private LocalDateTime recptDate;
  103 + /**
  104 + * 库存有效期
  105 + */
  106 + private LocalDateTime expireDate;
  107 + /**
  108 + * 生产日期
  109 + */
  110 + private LocalDateTime productionDate;
  111 + /**
  112 + * 是否冻结
  113 + */
  114 + private Boolean frozen;
  115 + /**
  116 + * 预留字段1
  117 + */
  118 + private String attribute1;
  119 + /**
  120 + * 预留字段2
  121 + */
  122 + private String attribute2;
  123 + /**
  124 + * 预留字段3
  125 + */
  126 + private Integer attribute3;
  127 + /**
  128 + * 预留字段4
  129 + */
  130 + private Integer attribute4;
  131 +
  132 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/materialstock/MesWmMaterialStockMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.dal.mysql.wm.materialstock;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  5 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  6 +import cn.iocoder.yudao.module.mes.controller.admin.wm.materialstock.vo.MesWmMaterialStockPageReqVO;
  7 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterialStockDO;
  8 +import org.apache.ibatis.annotations.Mapper;
  9 +
  10 +import java.math.BigDecimal;
  11 +import java.util.Collection;
  12 +import java.util.List;
  13 +
  14 +/**
  15 + * MES 库存台账 Mapper
  16 + */
  17 +@Mapper
  18 +public interface MesWmMaterialStockMapper extends BaseMapperX<MesWmMaterialStockDO> {
  19 +
  20 + default PageResult<MesWmMaterialStockDO> selectPage(MesWmMaterialStockPageReqVO reqVO,
  21 + Collection<Long> itemTypeIds,
  22 + Collection<Long> itemIds) {
  23 + return selectPage(reqVO, new LambdaQueryWrapperX<MesWmMaterialStockDO>()
  24 + .inIfPresent(MesWmMaterialStockDO::getItemTypeId, itemTypeIds)
  25 + .inIfPresent(MesWmMaterialStockDO::getItemId, itemIds)
  26 + .eqIfPresent(MesWmMaterialStockDO::getWarehouseId, reqVO.getWarehouseId())
  27 + .eqIfPresent(MesWmMaterialStockDO::getLocationId, reqVO.getLocationId())
  28 + .eqIfPresent(MesWmMaterialStockDO::getFrozen, reqVO.getFrozen())
  29 + .ne(MesWmMaterialStockDO::getQuantityOnhand, BigDecimal.ZERO)
  30 + .orderByAsc(MesWmMaterialStockDO::getRecptDate));
  31 + }
  32 +
  33 + default Long selectCountByWarehouseId(Long warehouseId) {
  34 + return selectCount(MesWmMaterialStockDO::getWarehouseId, warehouseId);
  35 + }
  36 +
  37 + default Long selectCountByLocationId(Long locationId) {
  38 + return selectCount(MesWmMaterialStockDO::getLocationId, locationId);
  39 + }
  40 +
  41 + default Long selectCountByAreaId(Long areaId) {
  42 + return selectCount(MesWmMaterialStockDO::getAreaId, areaId);
  43 + }
  44 +
  45 + default List<MesWmMaterialStockDO> selectListByIds(Collection<Long> ids) {
  46 + return selectByIds(ids);
  47 + }
  48 +
  49 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
... ... @@ -198,6 +198,7 @@ public interface ErrorCodeConstants {
198 198 ErrorCode WM_WAREHOUSE_NAME_DUPLICATE = new ErrorCode(1_040_700_002, "仓库名称已存在");
199 199 ErrorCode WM_WAREHOUSE_HAS_LOCATION = new ErrorCode(1_040_700_003, "仓库下存在库区,无法删除");
200 200 ErrorCode WM_WAREHOUSE_HAS_WORKSTATION = new ErrorCode(1_040_700_004, "仓库已被工作站引用,无法删除");
  201 + ErrorCode WM_WAREHOUSE_HAS_MATERIAL_STOCK = new ErrorCode(1_040_700_005, "仓库下有库存记录,无法删除");
201 202  
202 203 // ========== MES 仓库管理-库区(1-040-701-000) ==========
203 204 ErrorCode WM_WAREHOUSE_LOCATION_NOT_EXISTS = new ErrorCode(1_040_701_000, "库区不存在");
... ... @@ -207,6 +208,7 @@ public interface ErrorCodeConstants {
207 208 ErrorCode WM_WAREHOUSE_LOCATION_HAS_WORKSTATION = new ErrorCode(1_040_701_004, "库区已被工作站引用,无法删除");
208 209 ErrorCode WM_WAREHOUSE_REQUIRED = new ErrorCode(1_040_701_005, "选择库区时,仓库不能为空");
209 210 ErrorCode WM_WAREHOUSE_LOCATION_RELATION_INVALID = new ErrorCode(1_040_701_006, "库区不属于所选仓库");
  211 + ErrorCode WM_WAREHOUSE_LOCATION_HAS_MATERIAL_STOCK = new ErrorCode(1_040_701_007, "库区下有库存记录,无法删除");
210 212  
211 213 // ========== MES 仓库管理-库位(1-040-702-000) ==========
212 214 ErrorCode WM_WAREHOUSE_AREA_NOT_EXISTS = new ErrorCode(1_040_702_000, "库位不存在");
... ... @@ -215,6 +217,7 @@ public interface ErrorCodeConstants {
215 217 ErrorCode WM_WAREHOUSE_AREA_HAS_WORKSTATION = new ErrorCode(1_040_702_003, "库位已被工作站引用,无法删除");
216 218 ErrorCode WM_WAREHOUSE_LOCATION_REQUIRED = new ErrorCode(1_040_702_004, "选择库位时,库区不能为空");
217 219 ErrorCode WM_WAREHOUSE_AREA_RELATION_INVALID = new ErrorCode(1_040_702_005, "库位不属于所选库区");
  220 + ErrorCode WM_WAREHOUSE_AREA_HAS_MATERIAL_STOCK = new ErrorCode(1_040_702_006, "库位下有库存记录,无法删除");
218 221  
219 222 // ========== MES 仓库管理-库存(1-040-703-000) ==========
220 223 ErrorCode WM_MATERIAL_STOCK_NOT_EXISTS = new ErrorCode(1_040_703_000, "库存记录不存在");
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.service.wm.materialstock;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.mes.controller.admin.wm.materialstock.vo.MesWmMaterialStockPageReqVO;
  5 +import cn.iocoder.yudao.module.mes.controller.admin.wm.materialstock.vo.MesWmMaterialStockSaveReqVO;
  6 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterialStockDO;
  7 +import jakarta.validation.Valid;
  8 +
  9 +import java.util.Collection;
  10 +import java.util.List;
  11 +import java.util.Map;
  12 +
  13 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
  14 +
  15 +/**
  16 + * MES 库存台账 Service 接口
  17 + */
  18 +public interface MesWmMaterialStockService {
  19 +
  20 + /**
  21 + * 获得库存记录
  22 + *
  23 + * @param id 编号
  24 + * @return 库存记录
  25 + */
  26 + MesWmMaterialStockDO getMaterialStock(Long id);
  27 +
  28 + /**
  29 + * 校验库存记录存在
  30 + *
  31 + * @param id 编号
  32 + * @return 库存记录
  33 + */
  34 + MesWmMaterialStockDO validateMaterialStockExists(Long id);
  35 +
  36 + /**
  37 + * 获得库存台账分页
  38 + *
  39 + * @param pageReqVO 分页参数
  40 + * @return 库存台账分页
  41 + */
  42 + PageResult<MesWmMaterialStockDO> getMaterialStockPage(MesWmMaterialStockPageReqVO pageReqVO);
  43 +
  44 + /**
  45 + * 更新库存冻结状态
  46 + *
  47 + * @param updateReqVO 更新信息
  48 + */
  49 + void updateMaterialStockFrozen(@Valid MesWmMaterialStockSaveReqVO updateReqVO);
  50 +
  51 + /**
  52 + * 获得仓库下库存记录数量
  53 + *
  54 + * @param warehouseId 仓库编号
  55 + * @return 记录数量
  56 + */
  57 + Long getMaterialStockCountByWarehouseId(Long warehouseId);
  58 +
  59 + /**
  60 + * 获得库区下库存记录数量
  61 + *
  62 + * @param locationId 库区编号
  63 + * @return 记录数量
  64 + */
  65 + Long getMaterialStockCountByLocationId(Long locationId);
  66 +
  67 + /**
  68 + * 获得库位下库存记录数量
  69 + *
  70 + * @param areaId 库位编号
  71 + * @return 记录数量
  72 + */
  73 + Long getMaterialStockCountByAreaId(Long areaId);
  74 +
  75 + /**
  76 + * 按编号集合获得库存记录列表
  77 + *
  78 + * @param ids 编号集合
  79 + * @return 库存记录列表
  80 + */
  81 + List<MesWmMaterialStockDO> getMaterialStockList(Collection<Long> ids);
  82 +
  83 + default Map<Long, MesWmMaterialStockDO> getMaterialStockMap(Collection<Long> ids) {
  84 + return convertMap(getMaterialStockList(ids), MesWmMaterialStockDO::getId);
  85 + }
  86 +
  87 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.service.wm.materialstock;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.util.StrUtil;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.module.mes.controller.admin.wm.materialstock.vo.MesWmMaterialStockPageReqVO;
  7 +import cn.iocoder.yudao.module.mes.controller.admin.wm.materialstock.vo.MesWmMaterialStockSaveReqVO;
  8 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
  9 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemTypeDO;
  10 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterialStockDO;
  11 +import cn.iocoder.yudao.module.mes.dal.mysql.md.item.MesMdItemMapper;
  12 +import cn.iocoder.yudao.module.mes.dal.mysql.wm.materialstock.MesWmMaterialStockMapper;
  13 +import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemTypeService;
  14 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  15 +import jakarta.annotation.Resource;
  16 +import org.springframework.stereotype.Service;
  17 +import org.springframework.validation.annotation.Validated;
  18 +
  19 +import java.util.*;
  20 +
  21 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  22 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  23 +import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_MATERIAL_STOCK_NOT_EXISTS;
  24 +
  25 +/**
  26 + * MES 库存台账 Service 实现类
  27 + */
  28 +@Service
  29 +@Validated
  30 +public class MesWmMaterialStockServiceImpl implements MesWmMaterialStockService {
  31 +
  32 + @Resource
  33 + private MesWmMaterialStockMapper materialStockMapper;
  34 +
  35 + @Resource
  36 + private MesMdItemTypeService itemTypeService;
  37 +
  38 + @Resource
  39 + private MesMdItemMapper itemMapper;
  40 +
  41 + @Override
  42 + public MesWmMaterialStockDO getMaterialStock(Long id) {
  43 + return materialStockMapper.selectById(id);
  44 + }
  45 +
  46 + @Override
  47 + public MesWmMaterialStockDO validateMaterialStockExists(Long id) {
  48 + MesWmMaterialStockDO stock = materialStockMapper.selectById(id);
  49 + if (stock == null) {
  50 + throw exception(WM_MATERIAL_STOCK_NOT_EXISTS);
  51 + }
  52 + return stock;
  53 + }
  54 +
  55 + @Override
  56 + public PageResult<MesWmMaterialStockDO> getMaterialStockPage(MesWmMaterialStockPageReqVO pageReqVO) {
  57 + // 1. 解析 itemTypeId:包含子分类
  58 + Set<Long> itemTypeIds = null;
  59 + if (pageReqVO.getItemTypeId() != null) {
  60 + itemTypeIds = new HashSet<>();
  61 + itemTypeIds.add(pageReqVO.getItemTypeId());
  62 + List<MesMdItemTypeDO> children = itemTypeService.getItemTypeChildrenList(pageReqVO.getItemTypeId());
  63 + itemTypeIds.addAll(convertSet(children, MesMdItemTypeDO::getId));
  64 + }
  65 +
  66 + // 2. 解析 itemCode/itemName:查 md_item 找匹配的 itemId 集合
  67 + Set<Long> itemIds = null;
  68 + if (StrUtil.isNotBlank(pageReqVO.getItemCode()) || StrUtil.isNotBlank(pageReqVO.getItemName())) {
  69 + List<MesMdItemDO> matchingItems = itemMapper.selectList(new LambdaQueryWrapperX<MesMdItemDO>()
  70 + .eqIfPresent(MesMdItemDO::getCode, pageReqVO.getItemCode())
  71 + .likeIfPresent(MesMdItemDO::getName, pageReqVO.getItemName()));
  72 + if (matchingItems.isEmpty()) {
  73 + return PageResult.empty();
  74 + }
  75 + itemIds = convertSet(matchingItems, MesMdItemDO::getId);
  76 + }
  77 +
  78 + // 3. 分页查询
  79 + return materialStockMapper.selectPage(pageReqVO, itemTypeIds, itemIds);
  80 + }
  81 +
  82 + @Override
  83 + public void updateMaterialStockFrozen(MesWmMaterialStockSaveReqVO updateReqVO) {
  84 + // 校验存在
  85 + validateMaterialStockExists(updateReqVO.getId());
  86 + // 更新冻结状态
  87 + MesWmMaterialStockDO updateObj = new MesWmMaterialStockDO();
  88 + updateObj.setId(updateReqVO.getId());
  89 + updateObj.setFrozen(updateReqVO.getFrozen());
  90 + materialStockMapper.updateById(updateObj);
  91 + }
  92 +
  93 + @Override
  94 + public Long getMaterialStockCountByWarehouseId(Long warehouseId) {
  95 + return materialStockMapper.selectCountByWarehouseId(warehouseId);
  96 + }
  97 +
  98 + @Override
  99 + public Long getMaterialStockCountByLocationId(Long locationId) {
  100 + return materialStockMapper.selectCountByLocationId(locationId);
  101 + }
  102 +
  103 + @Override
  104 + public Long getMaterialStockCountByAreaId(Long areaId) {
  105 + return materialStockMapper.selectCountByAreaId(areaId);
  106 + }
  107 +
  108 + @Override
  109 + public List<MesWmMaterialStockDO> getMaterialStockList(Collection<Long> ids) {
  110 + if (CollUtil.isEmpty(ids)) {
  111 + return Collections.emptyList();
  112 + }
  113 + return materialStockMapper.selectListByIds(ids);
  114 + }
  115 +
  116 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/warehouse/MesWmWarehouseAreaServiceImpl.java
... ... @@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.mes.controller.admin.wm.warehouse.vo.area.MesWmWa
8 8 import cn.iocoder.yudao.module.mes.controller.admin.wm.warehouse.vo.area.MesWmWarehouseAreaSaveReqVO;
9 9 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO;
10 10 import cn.iocoder.yudao.module.mes.dal.mysql.md.workstation.MesMdWorkstationMapper;
  11 +import cn.iocoder.yudao.module.mes.dal.mysql.wm.materialstock.MesWmMaterialStockMapper;
11 12 import cn.iocoder.yudao.module.mes.dal.mysql.wm.warehouse.MesWmWarehouseAreaMapper;
12 13 import jakarta.annotation.Resource;
13 14 import org.springframework.stereotype.Service;
... ... @@ -20,6 +21,7 @@ import java.util.List;
20 21 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
21 22 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
22 23  
  24 +// TODO @AI:不应该直接跨模块调用 mapper,通过他们的 service 操作
23 25 /**
24 26 * MES 库位 Service 实现类
25 27 */
... ... @@ -34,6 +36,9 @@ public class MesWmWarehouseAreaServiceImpl implements MesWmWarehouseAreaService
34 36 private MesMdWorkstationMapper workstationMapper;
35 37  
36 38 @Resource
  39 + private MesWmMaterialStockMapper materialStockMapper;
  40 +
  41 + @Resource
37 42 private MesWmWarehouseLocationService locationService;
38 43  
39 44 @Override
... ... @@ -70,10 +75,13 @@ public class MesWmWarehouseAreaServiceImpl implements MesWmWarehouseAreaService
70 75 // 校验存在
71 76 validateWarehouseAreaExists(id);
72 77 // 校验是否被工作站引用
73   - if (ObjUtil.defaultIfNull(workstationMapper.selectCountByAreaId(id), 0L) > 0) {
  78 + if (workstationMapper.selectCountByAreaId(id) > 0) {
74 79 throw exception(WM_WAREHOUSE_AREA_HAS_WORKSTATION);
75 80 }
76   - // DONE @芋艿:本轮范围不接入库存占用校验,待 mes_wm_material_stock 迁移后补充
  81 + // 校验是否有库存记录
  82 + if (materialStockMapper.selectCountByAreaId(id) > 0) {
  83 + throw exception(WM_WAREHOUSE_AREA_HAS_MATERIAL_STOCK);
  84 + }
77 85  
78 86 areaMapper.deleteById(id);
79 87 }
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/warehouse/MesWmWarehouseLocationServiceImpl.java
... ... @@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.mes.controller.admin.wm.warehouse.vo.location.Mes
8 8 import cn.iocoder.yudao.module.mes.controller.admin.wm.warehouse.vo.location.MesWmWarehouseLocationSaveReqVO;
9 9 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseLocationDO;
10 10 import cn.iocoder.yudao.module.mes.dal.mysql.md.workstation.MesMdWorkstationMapper;
  11 +import cn.iocoder.yudao.module.mes.dal.mysql.wm.materialstock.MesWmMaterialStockMapper;
11 12 import cn.iocoder.yudao.module.mes.dal.mysql.wm.warehouse.MesWmWarehouseAreaMapper;
12 13 import cn.iocoder.yudao.module.mes.dal.mysql.wm.warehouse.MesWmWarehouseLocationMapper;
13 14 import jakarta.annotation.Resource;
... ... @@ -21,6 +22,7 @@ import java.util.List;
21 22 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
22 23 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
23 24  
  25 +// TODO @AI:不应该直接跨模块调用 mapper,通过他们的 service 操作
24 26 /**
25 27 * MES 库区 Service 实现类
26 28 */
... ... @@ -38,6 +40,9 @@ public class MesWmWarehouseLocationServiceImpl implements MesWmWarehouseLocation
38 40 private MesMdWorkstationMapper workstationMapper;
39 41  
40 42 @Resource
  43 + private MesWmMaterialStockMapper materialStockMapper;
  44 +
  45 + @Resource
41 46 private MesWmWarehouseService warehouseService;
42 47  
43 48 @Override
... ... @@ -74,14 +79,17 @@ public class MesWmWarehouseLocationServiceImpl implements MesWmWarehouseLocation
74 79 // 校验存在
75 80 validateWarehouseLocationExists(id);
76 81 // 校验是否有库位
77   - if (ObjUtil.defaultIfNull(areaMapper.selectCountByLocationId(id), 0L) > 0) {
  82 + if (areaMapper.selectCountByLocationId(id) > 0) {
78 83 throw exception(WM_WAREHOUSE_LOCATION_HAS_AREA);
79 84 }
80 85 // 校验是否被工作站引用
81   - if (ObjUtil.defaultIfNull(workstationMapper.selectCountByLocationId(id), 0L) > 0) {
  86 + if (workstationMapper.selectCountByLocationId(id) > 0) {
82 87 throw exception(WM_WAREHOUSE_LOCATION_HAS_WORKSTATION);
83 88 }
84   - // DONE @芋艿:本轮范围不接入库存占用校验,待 mes_wm_material_stock 迁移后补充
  89 + // 校验是否有库存记录
  90 + if (materialStockMapper.selectCountByLocationId(id) > 0) {
  91 + throw exception(WM_WAREHOUSE_LOCATION_HAS_MATERIAL_STOCK);
  92 + }
85 93  
86 94 locationMapper.deleteById(id);
87 95 }
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/warehouse/MesWmWarehouseServiceImpl.java
... ... @@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.mes.controller.admin.wm.warehouse.vo.MesWmWarehou
8 8 import cn.iocoder.yudao.module.mes.controller.admin.wm.warehouse.vo.MesWmWarehouseSaveReqVO;
9 9 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO;
10 10 import cn.iocoder.yudao.module.mes.dal.mysql.md.workstation.MesMdWorkstationMapper;
  11 +import cn.iocoder.yudao.module.mes.dal.mysql.wm.materialstock.MesWmMaterialStockMapper;
11 12 import cn.iocoder.yudao.module.mes.dal.mysql.wm.warehouse.MesWmWarehouseLocationMapper;
12 13 import cn.iocoder.yudao.module.mes.dal.mysql.wm.warehouse.MesWmWarehouseMapper;
13 14 import jakarta.annotation.Resource;
... ... @@ -21,6 +22,7 @@ import java.util.List;
21 22 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
22 23 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
23 24  
  25 +// TODO @AI:不应该直接跨模块调用 mapper,通过他们的 service 操作
24 26 /**
25 27 * MES 仓库 Service 实现类
26 28 */
... ... @@ -37,6 +39,9 @@ public class MesWmWarehouseServiceImpl implements MesWmWarehouseService {
37 39 @Resource
38 40 private MesMdWorkstationMapper workstationMapper;
39 41  
  42 + @Resource
  43 + private MesWmMaterialStockMapper materialStockMapper;
  44 +
40 45 @Override
41 46 public Long createWarehouse(MesWmWarehouseSaveReqVO createReqVO) {
42 47 // 校验编码唯一
... ... @@ -76,7 +81,10 @@ public class MesWmWarehouseServiceImpl implements MesWmWarehouseService {
76 81 if (workstationMapper.selectCountByWarehouseId(id) > 0) {
77 82 throw exception(WM_WAREHOUSE_HAS_WORKSTATION);
78 83 }
79   - // DONE @芋艿:本轮范围不接入库存占用校验,待 mes_wm_material_stock 迁移后补充
  84 + // 校验是否有库存记录
  85 + if (materialStockMapper.selectCountByWarehouseId(id) > 0) {
  86 + throw exception(WM_WAREHOUSE_HAS_MATERIAL_STOCK);
  87 + }
80 88  
81 89 // 删除
82 90 warehouseMapper.deleteById(id);
... ...