From b1e4a6df6620aab1f023b50a29566e59621f5015 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 22 Mar 2026 16:32:00 +0800 Subject: [PATCH] ✨ feat(mes): 更新库存管理逻辑,重命名字段并添加事务记录 --- yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/MesWmMaterialStockController.java | 22 ++++++---------------- yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/vo/MesWmMaterialStockFreezeReqVO.java | 4 ++-- yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/vo/MesWmMaterialStockRespVO.java | 24 +++--------------------- yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/materialstock/MesWmMaterialStockDO.java | 73 +++++++++++++++++++++++++++++++++---------------------------------------- yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/transaction/MesWmTransactionDO.java | 152 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/itemconsume/MesWmItemConsumeLineMapper.java | 6 ++++++ yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/materialstock/MesWmMaterialStockMapper.java | 28 +++++++++++++++++++--------- yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/transaction/MesWmTransactionMapper.java | 13 +++++++++++++ yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java | 1 + yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockService.java | 19 +++++++++++++------ yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockServiceImpl.java | 29 ++++++++++++++++++++--------- 11 files changed, 268 insertions(+), 103 deletions(-) create mode 100644 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/transaction/MesWmTransactionDO.java create mode 100644 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/transaction/MesWmTransactionMapper.java diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/MesWmMaterialStockController.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/MesWmMaterialStockController.java index 335d50b..bd40c07 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/MesWmMaterialStockController.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/MesWmMaterialStockController.java @@ -14,7 +14,6 @@ import cn.iocoder.yudao.module.mes.controller.admin.wm.materialstock.vo.MesWmMat import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO; import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO; import cn.iocoder.yudao.module.mes.dal.dataobject.md.vendor.MesMdVendorDO; -import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO; import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterialStockDO; import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO; import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO; @@ -22,7 +21,6 @@ import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseLoc import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; import cn.iocoder.yudao.module.mes.service.md.unitmeasure.MesMdUnitMeasureService; import cn.iocoder.yudao.module.mes.service.md.vendor.MesMdVendorService; -import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService; import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService; import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService; import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseLocationService; @@ -73,9 +71,6 @@ public class MesWmMaterialStockController { @Resource private MesMdVendorService vendorService; - @Resource - private MesProWorkOrderService workOrderService; - @GetMapping("/get") @Operation(summary = "获得库存记录") @Parameter(name = "id", description = "编号", required = true, example = "1024") @@ -128,7 +123,7 @@ public class MesWmMaterialStockController { Map itemMap = itemService.getItemMap( convertSet(list, MesWmMaterialStockDO::getItemId)); Map unitMeasureMap = unitMeasureService.getUnitMeasureMap( - convertSet(list, MesWmMaterialStockDO::getUnitMeasureId)); + convertSet(itemMap.values(), MesMdItemDO::getUnitMeasureId)); Map warehouseMap = warehouseService.getWarehouseMap( convertSet(list, MesWmMaterialStockDO::getWarehouseId)); Map locationMap = locationService.getWarehouseLocationMap( @@ -137,16 +132,13 @@ public class MesWmMaterialStockController { convertSet(list, MesWmMaterialStockDO::getAreaId)); Map vendorMap = vendorService.getVendorMap( convertSet(list, MesWmMaterialStockDO::getVendorId)); - Map workOrderMap = workOrderService.getWorkOrderMap( - convertSet(list, MesWmMaterialStockDO::getWorkOrderId)); - // TODO DONE @芋艿:待 mes_wm_batch 模块迁移后,补充 batchCode 的拼接 - // 2. 构建结果 return BeanUtils.toBean(list, MesWmMaterialStockRespVO.class, vo -> { - MapUtils.findAndThen(itemMap, vo.getItemId(), item -> - vo.setItemCode(item.getCode()).setItemName(item.getName()).setSpecification(item.getSpecification())); - MapUtils.findAndThen(unitMeasureMap, vo.getUnitMeasureId(), - unitMeasure -> vo.setUnitMeasureName(unitMeasure.getName())); + MapUtils.findAndThen(itemMap, vo.getItemId(), item -> { + vo.setItemCode(item.getCode()).setItemName(item.getName()).setSpecification(item.getSpecification()); + MapUtils.findAndThen(unitMeasureMap, item.getUnitMeasureId(), + unitMeasure -> vo.setUnitMeasureName(unitMeasure.getName())); + }); MapUtils.findAndThen(warehouseMap, vo.getWarehouseId(), warehouse -> vo.setWarehouseName(warehouse.getName())); MapUtils.findAndThen(locationMap, vo.getLocationId(), @@ -155,8 +147,6 @@ public class MesWmMaterialStockController { area -> vo.setAreaName(area.getName())); MapUtils.findAndThen(vendorMap, vo.getVendorId(), vendor -> vo.setVendorName(vendor.getName())); - MapUtils.findAndThen(workOrderMap, vo.getWorkOrderId(), - workOrder -> vo.setWorkOrderCode(workOrder.getCode())); }); } diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/vo/MesWmMaterialStockFreezeReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/vo/MesWmMaterialStockFreezeReqVO.java index 65a3dcd..8b8b691 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/vo/MesWmMaterialStockFreezeReqVO.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/vo/MesWmMaterialStockFreezeReqVO.java @@ -4,7 +4,6 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; import lombok.Data; -// TODO DONE @AI:已重命名为 FreezeReqVO,语义与冻结/解冻操作一致 @Schema(description = "管理后台 - MES 库存台账冻结/解冻 Request VO") @Data public class MesWmMaterialStockFreezeReqVO { @@ -15,6 +14,7 @@ public class MesWmMaterialStockFreezeReqVO { @Schema(description = "是否冻结", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") @NotNull(message = "冻结状态不能为空") - private Boolean frozen; + private Boolean frozenFlag; } + diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/vo/MesWmMaterialStockRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/vo/MesWmMaterialStockRespVO.java index 8fc6548..d752eb1 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/vo/MesWmMaterialStockRespVO.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/vo/MesWmMaterialStockRespVO.java @@ -35,9 +35,6 @@ public class MesWmMaterialStockRespVO { @ExcelProperty("规格型号") private String specification; - @Schema(description = "计量单位编号", example = "1") - private Long unitMeasureId; - @Schema(description = "计量单位名称", example = "千克") @ExcelProperty("计量单位") private String unitMeasureName; @@ -77,32 +74,17 @@ public class MesWmMaterialStockRespVO { @ExcelProperty("供应商") private String vendorName; - @Schema(description = "生产工单编号", example = "1") - private Long workOrderId; - - @Schema(description = "生产工单编码", example = "WO20260101") - @ExcelProperty("生产工单") - private String workOrderCode; - @Schema(description = "在库数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.0000") @ExcelProperty("在库数量") - private BigDecimal quantityOnhand; + private BigDecimal quantity; @Schema(description = "入库时间") @ExcelProperty("入库时间") - private LocalDateTime recptDate; - - @Schema(description = "生产日期") - @ExcelProperty("生产日期") - private LocalDateTime productionDate; - - @Schema(description = "库存有效期") - @ExcelProperty("有效期") - private LocalDateTime expireDate; + private LocalDateTime receiptTime; @Schema(description = "是否冻结", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") @ExcelProperty("是否冻结") - private Boolean frozen; + private Boolean frozenFlag; @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) @ExcelProperty("创建时间") diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/materialstock/MesWmMaterialStockDO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/materialstock/MesWmMaterialStockDO.java index 399ef3e..7974b68 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/materialstock/MesWmMaterialStockDO.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/materialstock/MesWmMaterialStockDO.java @@ -3,9 +3,8 @@ package cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO; import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemTypeDO; -import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO; import cn.iocoder.yudao.module.mes.dal.dataobject.md.vendor.MesMdVendorDO; -import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO; +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO; import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO; import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO; import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseLocationDO; @@ -17,7 +16,6 @@ import lombok.*; import java.math.BigDecimal; import java.time.LocalDateTime; -// TODO DONE @芋艿:当前包名 materialstock 与表名 mes_wm_material_stock 保持一致,语义清晰,暂不调整 /** * MES 库存台账(仓库现有量)DO */ @@ -36,6 +34,25 @@ public class MesWmMaterialStockDO extends BaseDO { */ @TableId private Long id; + + /** + * 在库数量 + */ + private BigDecimal quantity; + /** + * 入库时间 + */ + private LocalDateTime receiptTime; + + /** + * 供应商编号 + * + * 关联 {@link MesMdVendorDO#getId()} + */ + private Long vendorId; + + // ==================== 物料维度 ==================== + /** * 物料分类编号 * @@ -49,17 +66,20 @@ public class MesWmMaterialStockDO extends BaseDO { */ private Long itemId; /** - * 计量单位编号 + * 批次编号 * - * 关联 {@link MesMdUnitMeasureDO#getId()} + * 关联 {@link MesWmBatchDO#getId()} */ - private Long unitMeasureId; + private Long batchId; /** - * 批次编号 + * 批次号 * - * TODO DONE @芋艿:保留。待 mes_wm_batch 模块迁移后补充 @link 关联 + * 关联 {@link MesWmBatchDO#getCode()} */ - private Long batchId; + private String batchCode; + + // ==================== 库存位置 ==================== + /** * 仓库编号 * @@ -78,39 +98,12 @@ public class MesWmMaterialStockDO extends BaseDO { * 关联 {@link MesWmWarehouseAreaDO#getId()} */ private Long areaId; - /** - * 供应商编号 - * - * 关联 {@link MesMdVendorDO#getId()} - */ - private Long vendorId; - // TODO @AI:这个字段,应该没有的。 - /** - * 生产工单编号 - * - * 关联 {@link MesProWorkOrderDO#getId()} - */ - private Long workOrderId; - // TODO @AI:onhand_quantity 更合理;更符合现在项目的风格。改下; - /** - * 在库数量 - */ - private BigDecimal quantityOnhand; - /** - * 入库时间 - */ - private LocalDateTime recptDate; - /** - * 库存有效期 - */ - private LocalDateTime expireDate; - /** - * 生产日期 - */ - private LocalDateTime productionDate; + + // ==================== 状态 ==================== + /** * 是否冻结 */ - private Boolean frozen; + private Boolean frozenFlag; } diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/transaction/MesWmTransactionDO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/transaction/MesWmTransactionDO.java new file mode 100644 index 0000000..f0b5e8a --- /dev/null +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/transaction/MesWmTransactionDO.java @@ -0,0 +1,152 @@ +package cn.iocoder.yudao.module.mes.dal.dataobject.wm.transaction; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO; +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO; +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterialStockDO; +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO; +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO; +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseLocationDO; +import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants; +import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * MES 库存事务流水 DO + * + * 记录每一笔库存增减事件,系统自动生成,只读查询,不允许人工维护。 + */ +@TableName("mes_wm_transaction") +@KeySequence("mes_wm_transaction_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class MesWmTransactionDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + + // ==================== 业务归属 ==================== + + // codex reply:`transactionType` 收敛成 `type` 是对的,和项目里其他 DO 的命名保持一致。 + // codex reply:不过这类改名要连同 `MesWmTransactionSaveReqVO`、Service 和查询条件一起同步,不然 DO 与上下游模型会割裂。 + /** + * 事务类型 + * + * 枚举 {@link MesWmTransactionTypeEnum} + */ + private Integer type; + // codex reply:`transactionQuantity` 收敛成 `quantity` 是对的;在事务流水这个上下文里,`transaction` 前缀没有额外信息。 + // codex reply:`transactionFlag` 删除也合理,直接存带符号数量更符合当前写入逻辑,后续聚合查询也可以直接 `SUM(quantity)`。 + /** + * 本次变动数量 + * + * 正数=入库,负数=出库 + */ + private BigDecimal quantity; + + /** + * 业务类型 + * + * 对应 {@link MesBizTypeConstants} 中的常量 + */ + private Integer bizType; + /** + * 来源业务主单 ID + */ + private Long bizId; + /** + * 来源业务单号 + */ + private String bizCode; + /** + * 来源业务行 ID + */ + private Long bizLineId; + + /** + * 库存记录 ID + * + * 关联 {@link MesWmMaterialStockDO#getId()} + */ + private Long materialStockId; + /** + * 关联的事务 ID + * + * 关联 {@link #getId()},用于调拨等成对流水关联 + */ + private Long relatedTransactionId; + + // ==================== 物料维度 ==================== + + /** + * 物料 ID + * + * 关联 {@link MesMdItemDO#getId()} + */ + private Long itemId; + /** + * 批次 ID + * + * 关联 {@link MesWmBatchDO#getId()} + */ + private Long batchId; + /** + * 批次号 + * + * 关联 {@link MesWmBatchDO#getCode()} + */ + private String batchCode; + // codex reply:`packageId` 当前删除是合理的。事务流水链路没有包装维度的输入输出,先不保留“预留字段”更干净。 + + // ==================== 库存位置 ==================== + + /** + * 仓库 ID + * + * 关联 {@link MesWmWarehouseDO#getId()} + */ + private Long warehouseId; + /** + * 库区 ID + * + * 关联 {@link MesWmWarehouseLocationDO#getId()} + */ + private Long locationId; + /** + * 库位 ID + * + * 关联 {@link MesWmWarehouseAreaDO#getId()} + */ + private Long areaId; + + // ==================== 时间 ==================== + + // codex reply:这里我不建议继续沿用 `erpTime` / `receiptTime` 这组命名;项目里的同类字段基本统一为 `receiptDate`,单独改成 `Time` 会破坏一致性。 + // codex reply:如果这两个字段暂时没有业务赋值,建议宁可先删;若后续保留,ERP 侧更建议 `erpAccountDate`,收货侧保持 `receiptDate`。 + /** + * 事务发生时间 + */ + private LocalDateTime transactionTime; + /** + * ERP 账期 + */ + private LocalDateTime erpTime; + /** + * 入库时间 + */ + private LocalDateTime receiptTime; + +} diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/itemconsume/MesWmItemConsumeLineMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/itemconsume/MesWmItemConsumeLineMapper.java index 9fd90a1..722c63d 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/itemconsume/MesWmItemConsumeLineMapper.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/itemconsume/MesWmItemConsumeLineMapper.java @@ -7,6 +7,8 @@ import cn.iocoder.yudao.module.mes.controller.admin.wm.itemconsume.vo.MesWmItemC import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemconsume.MesWmItemConsumeLineDO; import org.apache.ibatis.annotations.Mapper; +import java.util.List; + /** * MES 物料消耗记录行 Mapper * @@ -22,4 +24,8 @@ public interface MesWmItemConsumeLineMapper extends BaseMapperX selectListByConsumeId(Long consumeId) { + return selectList(MesWmItemConsumeLineDO::getConsumeId, consumeId); + } + } diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/materialstock/MesWmMaterialStockMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/materialstock/MesWmMaterialStockMapper.java index 5dae320..bef9a10 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/materialstock/MesWmMaterialStockMapper.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/materialstock/MesWmMaterialStockMapper.java @@ -27,9 +27,9 @@ public interface MesWmMaterialStockMapper extends BaseMapperX() + /** + * 增量更新库存数量 + * + * @param id 库存记录编号 + * @param count 变动数量(正数=增加,负数=扣减) + * @return 影响行数(扣减时为 0 表示库存不足) + */ + default int updateQuantity(Long id, BigDecimal count) { + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper() .eq(MesWmMaterialStockDO::getId, id) - .setSql("quantity_onhand = quantity_onhand + " + quantity)); + .setSql("quantity = quantity + " + count); + if (count.compareTo(BigDecimal.ZERO) < 0) { + updateWrapper.ge(MesWmMaterialStockDO::getQuantity, count.abs()); // CAS 防负库存 + } + return update(null, updateWrapper); } default MesWmMaterialStockDO selectByCompositeKey(Long itemId, Long warehouseId, Long locationId, @@ -74,8 +84,8 @@ public interface MesWmMaterialStockMapper extends BaseMapperX { + +} diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java index 661b939..8ea3e46 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java @@ -355,6 +355,7 @@ public interface ErrorCodeConstants { // ========== MES 仓库管理-库存(1-040-703-000) ========== ErrorCode WM_MATERIAL_STOCK_NOT_EXISTS = new ErrorCode(1_040_703_000, "库存记录不存在"); ErrorCode WM_MATERIAL_STOCK_INSUFFICIENT = new ErrorCode(1_040_703_001, "库存数量不足"); + ErrorCode WM_TRANSACTION_MATERIAL_STOCK_REQUIRED = new ErrorCode(1_040_703_002, "出库事务必须指定库存记录 ID"); // ========== MES 仓库管理-到货通知单(1-040-704-000) ========== ErrorCode WM_ARRIVAL_NOTICE_NOT_EXISTS = new ErrorCode(1_040_704_000, "到货通知单不存在"); diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockService.java index 7849a9f..d38fde8 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockService.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockService.java @@ -8,7 +8,6 @@ import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterial import jakarta.validation.Valid; import java.math.BigDecimal; -import java.time.LocalDateTime; import java.util.Collection; import java.util.List; import java.util.Map; @@ -99,7 +98,7 @@ public interface MesWmMaterialStockService { * 增加库存 * * 按 itemId + warehouseId + locationId + areaId + batchId 查找已有库存, - * 存在则增加 quantityOnhand;不存在则新建记录。 + * 存在则增加 quantity;不存在则新建记录。 * * @param itemId 物料编号 * @param warehouseId 仓库编号 @@ -108,11 +107,19 @@ public interface MesWmMaterialStockService { * @param batchId 批次编号 * @param quantity 增加数量 * @param vendorId 供应商编号 - * @param productionDate 生产日期 - * @param expireDate 过期日期 */ void increaseStock(Long itemId, Long warehouseId, Long locationId, Long areaId, - Long batchId, BigDecimal quantity, Long vendorId, - LocalDateTime productionDate, LocalDateTime expireDate); + Long batchId, BigDecimal quantity, Long vendorId); + + /** + * 扣减库存 + * + * 根据库存记录 ID 直接扣减数量。 + * + * @param materialStockId 库存记录编号 + * @param quantity 扣减数量(正数) + * @param storageCheckFlag 是否校验库存充足 + */ + void decreaseStock(Long materialStockId, BigDecimal quantity, boolean storageCheckFlag); } diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockServiceImpl.java index a19ca30..3efd747 100644 --- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockServiceImpl.java +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockServiceImpl.java @@ -22,6 +22,7 @@ import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_MATERIAL_STOCK_INSUFFICIENT; import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_MATERIAL_STOCK_NOT_EXISTS; /** @@ -81,7 +82,7 @@ public class MesWmMaterialStockServiceImpl implements MesWmMaterialStockService // 更新冻结状态 MesWmMaterialStockDO updateObj = new MesWmMaterialStockDO(); updateObj.setId(updateReqVO.getId()); - updateObj.setFrozen(updateReqVO.getFrozen()); + updateObj.setFrozenFlag(updateReqVO.getFrozenFlag()); materialStockMapper.updateById(updateObj); } @@ -113,30 +114,40 @@ public class MesWmMaterialStockServiceImpl implements MesWmMaterialStockService return materialStockMapper.selectListForStockTaking(reqVO); } - // TODO DONE @AI:increaseStock 方法命名合理,语义清晰 @Override public void increaseStock(Long itemId, Long warehouseId, Long locationId, Long areaId, - Long batchId, BigDecimal quantity, Long vendorId, - LocalDateTime productionDate, LocalDateTime expireDate) { + Long batchId, BigDecimal quantity, Long vendorId) { // 1. 查找已有库存记录 MesWmMaterialStockDO stock = materialStockMapper.selectByCompositeKey( itemId, warehouseId, locationId, areaId, batchId); // 2a. 存在则增加数量 if (stock != null) { - materialStockMapper.incrQuantityOnhand(stock.getId(), quantity); + materialStockMapper.updateQuantity(stock.getId(), quantity); return; } - // 2. 不存在则新建 + // 2b. 不存在则新建 MesMdItemDO item = itemService.validateItemExists(itemId); MesWmMaterialStockDO newStock = MesWmMaterialStockDO.builder() - .itemId(itemId).itemTypeId(item.getItemTypeId()).unitMeasureId(item.getUnitMeasureId()) + .itemId(itemId).itemTypeId(item.getItemTypeId()) .warehouseId(warehouseId).locationId(locationId).areaId(areaId) - .batchId(batchId).vendorId(vendorId).quantityOnhand(quantity) - .recptDate(LocalDateTime.now()).productionDate(productionDate).expireDate(expireDate).frozen(false) + .batchId(batchId).vendorId(vendorId).quantity(quantity) + .receiptTime(LocalDateTime.now()).frozenFlag(false) .build(); materialStockMapper.insert(newStock); } + @Override + public void decreaseStock(Long materialStockId, BigDecimal quantity, boolean storageCheckFlag) { + // 1. 校验库存记录存在 + validateMaterialStockExists(materialStockId); + + // 2. 扣减库存(Mapper 层 CAS 防负库存) + int updatedRows = materialStockMapper.updateQuantity(materialStockId, quantity.negate()); + if (storageCheckFlag && updatedRows == 0) { + throw exception(WM_MATERIAL_STOCK_INSUFFICIENT); + } + } + } -- libgit2 0.21.4