Commit eb566426396a052423527b418079cc22b8acb924

Authored by YunaiV
1 parent b1f836ab

✨ feat(mes): 添加批次管理功能,支持批次生成与获取

新增批次生成请求 VO 和批次管理服务接口,支持根据物料批次配置生成或获取批次记录。更新相关数据对象和服务实现,确保批次管理功能的完整性和可用性。
Showing 15 changed files with 617 additions and 18 deletions
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/batch/vo/MesWmBatchGenerateReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.controller.admin.wm.batch.vo;
  2 +
  3 +import lombok.Data;
  4 +
  5 +import jakarta.validation.constraints.NotNull;
  6 +import java.time.LocalDateTime;
  7 +
  8 +/**
  9 + * 批次生成请求 VO
  10 + *
  11 + * @author 芋道源码
  12 + */
  13 +@Data
  14 +public class MesWmBatchGenerateReqVO {
  15 +
  16 + /**
  17 + * 物料ID
  18 + */
  19 + @NotNull(message = "物料 ID 不能为空")
  20 + private Long itemId;
  21 +
  22 + /**
  23 + * 生产日期
  24 + */
  25 + private LocalDateTime produceDate;
  26 + /**
  27 + * 有效期
  28 + */
  29 + private LocalDateTime expireDate;
  30 + /**
  31 + * 入库日期
  32 + */
  33 + private LocalDateTime receiptDate;
  34 +
  35 + /**
  36 + * 供应商 ID
  37 + */
  38 + private Long vendorId;
  39 + /**
  40 + * 客户 ID
  41 + */
  42 + private Long clientId;
  43 +
  44 + /**
  45 + * 采购订单编号
  46 + */
  47 + private String purchaseOrderCode;
  48 + /**
  49 + * 销售订单编号
  50 + */
  51 + private String salesOrderCode;
  52 +
  53 + /**
  54 + * 生产工单 ID
  55 + */
  56 + private Long workOrderId;
  57 + /**
  58 + * 生产任务 ID
  59 + */
  60 + private Long taskId;
  61 + /**
  62 + * 工作站 ID
  63 + */
  64 + private Long workstationId;
  65 + /**
  66 + * 工具 ID
  67 + */
  68 + private Long toolId;
  69 + /**
  70 + * 模具 ID
  71 + */
  72 + private Long moldId;
  73 +
  74 + /**
  75 + * 生产批号
  76 + */
  77 + private String lotNumber;
  78 + /**
  79 + * 质量状态
  80 + */
  81 + private String qualityStatus;
  82 +
  83 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/itemreceipt/vo/MesWmItemReceiptSaveReqVO.java
... ... @@ -39,4 +39,4 @@ public class MesWmItemReceiptSaveReqVO {
39 39 @Schema(description = "备注", example = "备注")
40 40 private String remark;
41 41  
42   - }
  42 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/itemreceipt/vo/line/MesWmItemReceiptLineRespVO.java
... ... @@ -40,6 +40,9 @@ public class MesWmItemReceiptLineRespVO {
40 40 @Schema(description = "批次编号", example = "1")
41 41 private Long batchId;
42 42  
  43 + @Schema(description = "批次编码", example = "BATCH20260110")
  44 + private String batchCode;
  45 +
43 46 @Schema(description = "生产日期")
44 47 private LocalDateTime productionDate;
45 48  
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/md/item/MesMdItemBatchConfigDO.java
... ... @@ -63,7 +63,7 @@ public class MesMdItemBatchConfigDO extends BaseDO {
63 63 /**
64 64 * 批次属性-生产工单
65 65 */
66   - private Boolean workorderFlag;
  66 + private Boolean workOrderFlag;
67 67 /**
68 68 * 批次属性-生产任务
69 69 */
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/batch/MesWmBatchDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.client.MesMdClientDO;
  5 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
  6 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.vendor.MesMdVendorDO;
  7 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkstationDO;
  8 +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO;
  9 +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
  10 +import cn.iocoder.yudao.module.mes.dal.dataobject.tm.tool.MesTmToolDO;
  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.time.LocalDateTime;
  17 +
  18 +/**
  19 + * 批次管理 DO
  20 + *
  21 + * @author 芋道源码
  22 + */
  23 +@TableName("mes_wm_batch")
  24 +@KeySequence("mes_wm_batch_seq")
  25 +@Data
  26 +@EqualsAndHashCode(callSuper = true)
  27 +@ToString(callSuper = true)
  28 +@Builder
  29 +@NoArgsConstructor
  30 +@AllArgsConstructor
  31 +public class MesWmBatchDO extends BaseDO {
  32 +
  33 + /**
  34 + * 批次ID
  35 + */
  36 + @TableId
  37 + private Long id;
  38 + /**
  39 + * 批次编码
  40 + */
  41 + private String code;
  42 + /**
  43 + * 物料ID
  44 + *
  45 + * 关联 {@link MesMdItemDO#getId()}
  46 + */
  47 + private Long itemId;
  48 + /**
  49 + * 生产日期
  50 + */
  51 + private LocalDateTime produceDate;
  52 + /**
  53 + * 有效期
  54 + */
  55 + private LocalDateTime expireDate;
  56 + /**
  57 + * 入库日期
  58 + */
  59 + private LocalDateTime receiptDate;
  60 + /**
  61 + * 供应商ID
  62 + *
  63 + * 关联 {@link MesMdVendorDO#getId()}
  64 + */
  65 + private Long vendorId;
  66 + /**
  67 + * 客户ID
  68 + *
  69 + * 关联 {@link MesMdClientDO#getId()}
  70 + */
  71 + private Long clientId;
  72 +
  73 + /**
  74 + * 销售订单编号
  75 + */
  76 + private String salesOrderCode;
  77 + /**
  78 + * 采购订单编号
  79 + */
  80 + private String purchaseOrderCode;
  81 + /**
  82 + * 生产工单ID
  83 + *
  84 + * 关联 {@link MesProWorkOrderDO#getId()}
  85 + */
  86 + private Long workOrderId;
  87 + /**
  88 + * 生产任务ID
  89 + *
  90 + * 关联 {@link MesProTaskDO#getId()}
  91 + */
  92 + private Long taskId;
  93 + /**
  94 + * 工作站ID
  95 + *
  96 + * 关联 {@link MesMdWorkstationDO#getId()}
  97 + */
  98 + private Long workstationId;
  99 + /**
  100 + * 工具ID
  101 + *
  102 + * 关联 {@link MesTmToolDO#getId()}
  103 + */
  104 + private Long toolId;
  105 + /**
  106 + * 模具 ID
  107 + */
  108 + private Long moldId;
  109 + /**
  110 + * 生产批号
  111 + */
  112 + private String lotNumber;
  113 + /**
  114 + * 质量状态
  115 + */
  116 + private String qualityStatus;
  117 + /**
  118 + * 备注
  119 + */
  120 + private String remark;
  121 +
  122 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/itemreceipt/MesWmItemReceiptLineDO.java
... ... @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemreceipt;
3 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 4 import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
5 5 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.arrivalnotice.MesWmArrivalNoticeLineDO;
  6 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO;
6 7 import com.baomidou.mybatisplus.annotation.KeySequence;
7 8 import com.baomidou.mybatisplus.annotation.TableId;
8 9 import com.baomidou.mybatisplus.annotation.TableName;
... ... @@ -54,10 +55,16 @@ public class MesWmItemReceiptLineDO extends BaseDO {
54 55 /**
55 56 * 批次编号
56 57 *
57   - * TODO DONE @芋艿:保留。待 mes_wm_batch 模块迁移后补充 @link 关联
  58 + * 关联 {@link MesWmBatchDO#getId()}
58 59 */
59 60 private Long batchId;
60 61 /**
  62 + * 批次编码
  63 + *
  64 + * 关联 {@link MesWmBatchDO#getCode()}
  65 + */
  66 + private String batchCode;
  67 + /**
61 68 * 生产日期
62 69 */
63 70 private LocalDateTime productionDate;
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/batch/MesWmBatchMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.dal.mysql.wm.batch;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  5 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO;
  6 +import cn.hutool.core.util.ObjUtil;
  7 +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  8 +import org.apache.ibatis.annotations.Mapper;
  9 +
  10 +import java.util.List;
  11 +
  12 +/**
  13 + * 批次管理 Mapper
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +@Mapper
  18 +public interface MesWmBatchMapper extends BaseMapperX<MesWmBatchDO> {
  19 +
  20 + /**
  21 + * 根据参数查询匹配的第一条批次记录
  22 + * <p>
  23 + * 使用 NULL 值精确匹配,返回 ID 最小的批次
  24 + *
  25 + * @param batch 批次参数
  26 + * @return 匹配的批次记录
  27 + */
  28 + default MesWmBatchDO selectFirst(MesWmBatchDO batch) {
  29 + LambdaQueryWrapper<MesWmBatchDO> query = new LambdaQueryWrapper<>();
  30 + query.eq(MesWmBatchDO::getItemId, batch.getItemId());
  31 + if (ObjUtil.isNull(batch.getVendorId())) {
  32 + query.isNull(MesWmBatchDO::getVendorId);
  33 + } else {
  34 + query.eq(MesWmBatchDO::getVendorId, batch.getVendorId());
  35 + }
  36 + if (ObjUtil.isNull(batch.getClientId())) {
  37 + query.isNull(MesWmBatchDO::getClientId);
  38 + } else {
  39 + query.eq(MesWmBatchDO::getClientId, batch.getClientId());
  40 + }
  41 + if (ObjUtil.isNull(batch.getSalesOrderCode())) {
  42 + query.isNull(MesWmBatchDO::getSalesOrderCode);
  43 + } else {
  44 + query.eq(MesWmBatchDO::getSalesOrderCode, batch.getSalesOrderCode());
  45 + }
  46 + if (ObjUtil.isNull(batch.getPurchaseOrderCode())) {
  47 + query.isNull(MesWmBatchDO::getPurchaseOrderCode);
  48 + } else {
  49 + query.eq(MesWmBatchDO::getPurchaseOrderCode, batch.getPurchaseOrderCode());
  50 + }
  51 + if (ObjUtil.isNull(batch.getWorkOrderId())) {
  52 + query.isNull(MesWmBatchDO::getWorkOrderId);
  53 + } else {
  54 + query.eq(MesWmBatchDO::getWorkOrderId, batch.getWorkOrderId());
  55 + }
  56 + if (ObjUtil.isNull(batch.getTaskId())) {
  57 + query.isNull(MesWmBatchDO::getTaskId);
  58 + } else {
  59 + query.eq(MesWmBatchDO::getTaskId, batch.getTaskId());
  60 + }
  61 + if (ObjUtil.isNull(batch.getWorkstationId())) {
  62 + query.isNull(MesWmBatchDO::getWorkstationId);
  63 + } else {
  64 + query.eq(MesWmBatchDO::getWorkstationId, batch.getWorkstationId());
  65 + }
  66 + if (ObjUtil.isNull(batch.getToolId())) {
  67 + query.isNull(MesWmBatchDO::getToolId);
  68 + } else {
  69 + query.eq(MesWmBatchDO::getToolId, batch.getToolId());
  70 + }
  71 + if (ObjUtil.isNull(batch.getMoldId())) {
  72 + query.isNull(MesWmBatchDO::getMoldId);
  73 + } else {
  74 + query.eq(MesWmBatchDO::getMoldId, batch.getMoldId());
  75 + }
  76 + if (ObjUtil.isNull(batch.getLotNumber())) {
  77 + query.isNull(MesWmBatchDO::getLotNumber);
  78 + } else {
  79 + query.eq(MesWmBatchDO::getLotNumber, batch.getLotNumber());
  80 + }
  81 + if (ObjUtil.isNull(batch.getQualityStatus())) {
  82 + query.isNull(MesWmBatchDO::getQualityStatus);
  83 + } else {
  84 + query.eq(MesWmBatchDO::getQualityStatus, batch.getQualityStatus());
  85 + }
  86 + if (ObjUtil.isNull(batch.getProduceDate())) {
  87 + query.isNull(MesWmBatchDO::getProduceDate);
  88 + } else {
  89 + query.eq(MesWmBatchDO::getProduceDate, batch.getProduceDate());
  90 + }
  91 + if (ObjUtil.isNull(batch.getExpireDate())) {
  92 + query.isNull(MesWmBatchDO::getExpireDate);
  93 + } else {
  94 + query.eq(MesWmBatchDO::getExpireDate, batch.getExpireDate());
  95 + }
  96 + if (ObjUtil.isNull(batch.getReceiptDate())) {
  97 + query.isNull(MesWmBatchDO::getReceiptDate);
  98 + } else {
  99 + query.eq(MesWmBatchDO::getReceiptDate, batch.getReceiptDate());
  100 + }
  101 +
  102 + // 返回 ID 最小的批次
  103 + query.orderByAsc(MesWmBatchDO::getId);
  104 + List<MesWmBatchDO> list = selectList(query);
  105 + return CollUtil.getFirst(list);
  106 + }
  107 +
  108 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
... ... @@ -35,6 +35,22 @@ public interface ErrorCodeConstants {
35 35 ErrorCode MD_ITEM_BATCH_CONFIG_NOT_EXISTS = new ErrorCode(1_040_102_100, "物料批次属性配置不存在");
36 36 ErrorCode MD_ITEM_BATCH_CONFIG_AT_LEAST_ONE_FLAG = new ErrorCode(1_040_102_101, "批次管理已启用,至少需要配置一个批次属性");
37 37  
  38 + // ========== MES 仓库管理-批次管理(1-040-717-000) ==========
  39 + ErrorCode WM_BATCH_PRODUCE_DATE_REQUIRED = new ErrorCode(1_040_717_000, "批次配置要求生产日期不能为空");
  40 + ErrorCode WM_BATCH_RECEIPT_DATE_REQUIRED = new ErrorCode(1_040_717_001, "批次配置要求入库日期不能为空");
  41 + ErrorCode WM_BATCH_EXPIRE_DATE_REQUIRED = new ErrorCode(1_040_717_002, "批次配置要求有效期不能为空");
  42 + ErrorCode WM_BATCH_VENDOR_REQUIRED = new ErrorCode(1_040_717_003, "批次配置要求供应商不能为空");
  43 + ErrorCode WM_BATCH_CLIENT_REQUIRED = new ErrorCode(1_040_717_004, "批次配置要求客户不能为空");
  44 + ErrorCode WM_BATCH_PURCHASE_ORDER_CODE_REQUIRED = new ErrorCode(1_040_717_005, "批次配置要求采购订单编号不能为空");
  45 + ErrorCode WM_BATCH_CUSTOMER_ORDER_CODE_REQUIRED = new ErrorCode(1_040_717_006, "批次配置要求销售订单编号不能为空");
  46 + ErrorCode WM_BATCH_WORK_ORDER_REQUIRED = new ErrorCode(1_040_717_007, "批次配置要求生产工单不能为空");
  47 + ErrorCode WM_BATCH_TASK_REQUIRED = new ErrorCode(1_040_717_008, "批次配置要求生产任务不能为空");
  48 + ErrorCode WM_BATCH_WORKSTATION_REQUIRED = new ErrorCode(1_040_717_009, "批次配置要求工作站不能为空");
  49 + ErrorCode WM_BATCH_TOOL_REQUIRED = new ErrorCode(1_040_717_010, "批次配置要求工具不能为空");
  50 + ErrorCode WM_BATCH_MOLD_REQUIRED = new ErrorCode(1_040_717_011, "批次配置要求模具不能为空");
  51 + ErrorCode WM_BATCH_LOT_NUMBER_REQUIRED = new ErrorCode(1_040_717_012, "批次配置要求生产批号不能为空");
  52 + ErrorCode WM_BATCH_QUALITY_STATUS_REQUIRED = new ErrorCode(1_040_717_013, "批次配置要求质量状态不能为空");
  53 +
38 54 // ========== MES 基础数据-客户(1-040-103-000) ==========
39 55 ErrorCode MD_CLIENT_NOT_EXISTS = new ErrorCode(1_040_103_000, "客户不存在");
40 56 ErrorCode MD_CLIENT_CODE_DUPLICATE = new ErrorCode(1_040_103_001, "客户编码已存在");
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/md/autocode/MesMdAutoCodeRuleCodeEnum.java
... ... @@ -13,7 +13,8 @@ import lombok.Getter;
13 13 public enum MesMdAutoCodeRuleCodeEnum {
14 14  
15 15 SN_CODE("SN_CODE", "SN 码"),
16   - PACKAGE_CODE("PACKAGE_CODE", "装箱单编码");
  16 + PACKAGE_CODE("PACKAGE_CODE", "装箱单编码"),
  17 + BATCH_CODE("BATCH_CODE", "批次编码");
17 18  
18 19 /**
19 20 * 规则代码
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/item/MesMdItemBatchConfigService.java
... ... @@ -20,6 +20,14 @@ public interface MesMdItemBatchConfigService {
20 20 MesMdItemBatchConfigDO getItemBatchConfigByItemId(Long itemId);
21 21  
22 22 /**
  23 + * 校验物料批次配置存在
  24 + *
  25 + * @param itemId 物料 ID
  26 + * @return 物料批次配置
  27 + */
  28 + MesMdItemBatchConfigDO validateItemBatchConfigExists(Long itemId);
  29 +
  30 + /**
23 31 * 保存批次属性配置(新增或更新)
24 32 *
25 33 * @param saveReqVO 保存信息
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/item/MesMdItemBatchConfigServiceImpl.java
... ... @@ -10,6 +10,7 @@ import org.springframework.stereotype.Service;
10 10 import org.springframework.validation.annotation.Validated;
11 11  
12 12 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  13 +import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.MD_ITEM_BATCH_CONFIG_NOT_EXISTS;
13 14 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.MD_ITEM_NOT_EXISTS;
14 15  
15 16 /**
... ... @@ -34,6 +35,15 @@ public class MesMdItemBatchConfigServiceImpl implements MesMdItemBatchConfigServ
34 35 }
35 36  
36 37 @Override
  38 + public MesMdItemBatchConfigDO validateItemBatchConfigExists(Long itemId) {
  39 + MesMdItemBatchConfigDO config = getItemBatchConfigByItemId(itemId);
  40 + if (config == null) {
  41 + throw exception(MD_ITEM_BATCH_CONFIG_NOT_EXISTS);
  42 + }
  43 + return config;
  44 + }
  45 +
  46 + @Override
37 47 public Long saveItemBatchConfig(MesMdItemBatchConfigSaveReqVO saveReqVO) {
38 48 // 1. 校验物料存在
39 49 if (itemService.getItem(saveReqVO.getItemId()) == null) {
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/item/MesMdItemServiceImpl.java
... ... @@ -198,7 +198,7 @@ public class MesMdItemServiceImpl implements MesMdItemService {
198 198 || Boolean.TRUE.equals(config.getClientFlag())
199 199 || Boolean.TRUE.equals(config.getSalesOrderCodeFlag())
200 200 || Boolean.TRUE.equals(config.getPurchaseOrderCodeFlag())
201   - || Boolean.TRUE.equals(config.getWorkorderFlag())
  201 + || Boolean.TRUE.equals(config.getWorkOrderFlag())
202 202 || Boolean.TRUE.equals(config.getTaskFlag())
203 203 || Boolean.TRUE.equals(config.getWorkstationFlag())
204 204 || Boolean.TRUE.equals(config.getToolFlag())
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/batch/MesWmBatchService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.service.wm.batch;
  2 +
  3 +import cn.iocoder.yudao.module.mes.controller.admin.wm.batch.vo.MesWmBatchGenerateReqVO;
  4 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO;
  5 +
  6 +/**
  7 + * 批次管理 Service 接口
  8 + *
  9 + * @author 芋道源码
  10 + */
  11 +public interface MesWmBatchService {
  12 +
  13 + /**
  14 + * 获取或生成批次编码
  15 + * <p>
  16 + * 根据物料批次配置,查询或生成批次记录
  17 + *
  18 + * @param reqVO 批次参数(包含 itemId 及其他可选属性)
  19 + * @return 批次记录(如果物料未启用批次管理则返回 null)
  20 + */
  21 + MesWmBatchDO getOrGenerateBatchCode(MesWmBatchGenerateReqVO reqVO);
  22 +
  23 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/batch/MesWmBatchServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.service.wm.batch;
  2 +
  3 +import cn.hutool.core.util.ObjUtil;
  4 +import cn.iocoder.yudao.module.mes.controller.admin.wm.batch.vo.MesWmBatchGenerateReqVO;
  5 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemBatchConfigDO;
  6 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
  7 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO;
  8 +import cn.iocoder.yudao.module.mes.dal.mysql.wm.batch.MesWmBatchMapper;
  9 +import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
  10 +import cn.iocoder.yudao.module.mes.enums.md.autocode.MesMdAutoCodeRuleCodeEnum;
  11 +import cn.iocoder.yudao.module.mes.service.md.autocode.MesMdAutoCodeRecordService;
  12 +import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemBatchConfigService;
  13 +import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
  14 +import cn.iocoder.yudao.module.mes.service.wm.barcode.MesWmBarcodeService;
  15 +import jakarta.annotation.Resource;
  16 +import lombok.extern.slf4j.Slf4j;
  17 +import org.springframework.stereotype.Service;
  18 +import org.springframework.transaction.annotation.Transactional;
  19 +import org.springframework.validation.annotation.Validated;
  20 +
  21 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  22 +import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
  23 +
  24 +/**
  25 + * 批次管理 Service 实现类
  26 + *
  27 + * @author 芋道源码
  28 + */
  29 +@Service
  30 +@Validated
  31 +@Slf4j
  32 +public class MesWmBatchServiceImpl implements MesWmBatchService {
  33 +
  34 + @Resource
  35 + private MesWmBatchMapper batchMapper;
  36 + @Resource
  37 + private MesMdItemService itemService;
  38 + @Resource
  39 + private MesMdItemBatchConfigService itemBatchConfigService;
  40 + @Resource
  41 + private MesMdAutoCodeRecordService autoCodeRecordService;
  42 + @Resource
  43 + private MesWmBarcodeService barcodeService;
  44 +
  45 + @Override
  46 + @Transactional(rollbackFor = Exception.class)
  47 + public MesWmBatchDO getOrGenerateBatchCode(MesWmBatchGenerateReqVO reqVO) {
  48 + // 1.1 查询物料信息,检查是否启用批次管理
  49 + MesMdItemDO item = itemService.validateItemExists(reqVO.getItemId());
  50 + if (Boolean.FALSE.equals(item.getBatchFlag())) {
  51 + // 未启用批次管理,返回 null
  52 + return null;
  53 + }
  54 + // 1.2 查询物料批次配置
  55 + MesMdItemBatchConfigDO config = itemBatchConfigService.validateItemBatchConfigExists(reqVO.getItemId());
  56 +
  57 + // 2. 根据配置校验必填参数并清空不需要的参数
  58 + MesWmBatchDO batch = MesWmBatchDO.builder().itemId(reqVO.getItemId()).build();
  59 + // 生产日期
  60 + if (Boolean.TRUE.equals(config.getProduceDateFlag())) {
  61 + if (ObjUtil.isNull(reqVO.getProduceDate())) {
  62 + throw exception(WM_BATCH_PRODUCE_DATE_REQUIRED);
  63 + }
  64 + batch.setProduceDate(reqVO.getProduceDate());
  65 + }
  66 + // 入库日期
  67 + if (Boolean.TRUE.equals(config.getReceiptDateFlag())) {
  68 + if (ObjUtil.isNull(reqVO.getReceiptDate())) {
  69 + throw exception(WM_BATCH_RECEIPT_DATE_REQUIRED);
  70 + }
  71 + batch.setReceiptDate(reqVO.getReceiptDate());
  72 + }
  73 + // 有效期
  74 + if (Boolean.TRUE.equals(config.getExpireDateFlag())) {
  75 + if (ObjUtil.isNull(reqVO.getExpireDate())) {
  76 + throw exception(WM_BATCH_EXPIRE_DATE_REQUIRED);
  77 + }
  78 + batch.setExpireDate(reqVO.getExpireDate());
  79 + }
  80 + // 供应商
  81 + if (Boolean.TRUE.equals(config.getVendorFlag())) {
  82 + if (ObjUtil.isNull(reqVO.getVendorId())) {
  83 + throw exception(WM_BATCH_VENDOR_REQUIRED);
  84 + }
  85 + batch.setVendorId(reqVO.getVendorId());
  86 + }
  87 + // 客户
  88 + if (Boolean.TRUE.equals(config.getClientFlag())) {
  89 + if (ObjUtil.isNull(reqVO.getClientId())) {
  90 + throw exception(WM_BATCH_CLIENT_REQUIRED);
  91 + }
  92 + batch.setClientId(reqVO.getClientId());
  93 + }
  94 + // 采购订单编号
  95 + if (Boolean.TRUE.equals(config.getPurchaseOrderCodeFlag())) {
  96 + if (ObjUtil.isNull(reqVO.getPurchaseOrderCode())) {
  97 + throw exception(WM_BATCH_PURCHASE_ORDER_CODE_REQUIRED);
  98 + }
  99 + batch.setPurchaseOrderCode(reqVO.getPurchaseOrderCode());
  100 + }
  101 + // 销售订单编号
  102 + if (Boolean.TRUE.equals(config.getSalesOrderCodeFlag())) {
  103 + if (ObjUtil.isNull(reqVO.getSalesOrderCode())) {
  104 + throw exception(WM_BATCH_CUSTOMER_ORDER_CODE_REQUIRED);
  105 + }
  106 + batch.setSalesOrderCode(reqVO.getSalesOrderCode());
  107 + }
  108 + // 生产工单
  109 + if (Boolean.TRUE.equals(config.getWorkOrderFlag())) {
  110 + if (ObjUtil.isNull(reqVO.getWorkOrderId())) {
  111 + throw exception(WM_BATCH_WORK_ORDER_REQUIRED);
  112 + }
  113 + batch.setWorkOrderId(reqVO.getWorkOrderId());
  114 + }
  115 + // 生产任务
  116 + if (Boolean.TRUE.equals(config.getTaskFlag())) {
  117 + if (ObjUtil.isNull(reqVO.getTaskId())) {
  118 + throw exception(WM_BATCH_TASK_REQUIRED);
  119 + }
  120 + batch.setTaskId(reqVO.getTaskId());
  121 + }
  122 + // 工作站
  123 + if (Boolean.TRUE.equals(config.getWorkstationFlag())) {
  124 + if (ObjUtil.isNull(reqVO.getWorkstationId())) {
  125 + throw exception(WM_BATCH_WORKSTATION_REQUIRED);
  126 + }
  127 + batch.setWorkstationId(reqVO.getWorkstationId());
  128 + }
  129 + // 工具
  130 + if (Boolean.TRUE.equals(config.getToolFlag())) {
  131 + if (ObjUtil.isNull(reqVO.getToolId())) {
  132 + throw exception(WM_BATCH_TOOL_REQUIRED);
  133 + }
  134 + batch.setToolId(reqVO.getToolId());
  135 + }
  136 + // 模具
  137 + if (Boolean.TRUE.equals(config.getMoldFlag())) {
  138 + if (ObjUtil.isNull(reqVO.getMoldId())) {
  139 + throw exception(WM_BATCH_MOLD_REQUIRED);
  140 + }
  141 + batch.setMoldId(reqVO.getMoldId());
  142 + }
  143 + // 生产批号
  144 + if (Boolean.TRUE.equals(config.getLotNumberFlag())) {
  145 + if (ObjUtil.isNull(reqVO.getLotNumber())) {
  146 + throw exception(WM_BATCH_LOT_NUMBER_REQUIRED);
  147 + }
  148 + batch.setLotNumber(reqVO.getLotNumber());
  149 + }
  150 + // 质量状态
  151 + if (Boolean.TRUE.equals(config.getQualityStatusFlag())) {
  152 + if (ObjUtil.isNull(reqVO.getQualityStatus())) {
  153 + throw exception(WM_BATCH_QUALITY_STATUS_REQUIRED);
  154 + }
  155 + batch.setQualityStatus(reqVO.getQualityStatus());
  156 + }
  157 +
  158 + // 3.1 情况一:查询是否存在匹配的批次
  159 + MesWmBatchDO existingBatch = batchMapper.selectFirst(batch);
  160 + if (existingBatch != null) {
  161 + return existingBatch;
  162 + }
  163 +
  164 + // 3.2 情况二:生成新批次
  165 + String batchCode = autoCodeRecordService.generateAutoCode(MesMdAutoCodeRuleCodeEnum.BATCH_CODE.getCode());
  166 + batch.setCode(batchCode);
  167 + batchMapper.insert(batch);
  168 +
  169 + // 4. 生成条码
  170 + barcodeService.autoGenerateBarcode(MesBizTypeConstants.WM_BATCH, batch.getId(), batch.getCode(), item.getName());
  171 + return batch;
  172 + }
  173 +
  174 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/itemreceipt/MesWmItemReceiptLineServiceImpl.java
... ... @@ -2,12 +2,17 @@ package cn.iocoder.yudao.module.mes.service.wm.itemreceipt;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 4 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  5 +import cn.iocoder.yudao.module.mes.controller.admin.wm.batch.vo.MesWmBatchGenerateReqVO;
5 6 import cn.iocoder.yudao.module.mes.controller.admin.wm.itemreceipt.vo.line.MesWmItemReceiptLinePageReqVO;
6 7 import cn.iocoder.yudao.module.mes.controller.admin.wm.itemreceipt.vo.line.MesWmItemReceiptLineSaveReqVO;
  8 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.arrivalnotice.MesWmArrivalNoticeDO;
  9 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO;
7 10 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemreceipt.MesWmItemReceiptDO;
8 11 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemreceipt.MesWmItemReceiptLineDO;
9 12 import cn.iocoder.yudao.module.mes.dal.mysql.wm.itemreceipt.MesWmItemReceiptLineMapper;
10 13 import cn.iocoder.yudao.module.mes.service.wm.arrivalnotice.MesWmArrivalNoticeLineService;
  14 +import cn.iocoder.yudao.module.mes.service.wm.arrivalnotice.MesWmArrivalNoticeService;
  15 +import cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService;
11 16 import jakarta.annotation.Resource;
12 17 import org.springframework.context.annotation.Lazy;
13 18 import org.springframework.stereotype.Service;
... ... @@ -32,43 +37,55 @@ public class MesWmItemReceiptLineServiceImpl implements MesWmItemReceiptLineServ
32 37 @Resource
33 38 @Lazy
34 39 private MesWmItemReceiptService itemReceiptService;
35   -
36 40 @Resource
37 41 private MesWmItemReceiptDetailService itemReceiptDetailService;
38   -
39 42 @Resource
40 43 @Lazy
41 44 private MesWmArrivalNoticeLineService arrivalNoticeLineService;
  45 + @Resource
  46 + @Lazy
  47 + private MesWmArrivalNoticeService arrivalNoticeService;
  48 + @Resource
  49 + private MesWmBatchService batchService;
42 50  
43 51 @Override
44 52 public Long createItemReceiptLine(MesWmItemReceiptLineSaveReqVO createReqVO) {
45   - // 校验父单据存在且为可编辑状态
  53 + // 1.1 校验父单据存在且为可编辑状态
46 54 MesWmItemReceiptDO receipt = itemReceiptService.validateItemReceiptEditable(createReqVO.getReceiptId());
47   - // 校验关联到货通知单行存在
  55 + // 1.2 校验关联到货通知单行存在
48 56 validateArrivalNoticeLine(receipt, createReqVO.getArrivalNoticeLineId());
49 57  
50   - // 新增
  58 + // 2.1 创建入库单行
51 59 MesWmItemReceiptLineDO line = BeanUtils.toBean(createReqVO, MesWmItemReceiptLineDO.class);
  60 + // 2.2 生成或获取批次
  61 + MesWmBatchDO batch = generateOrGetBatch(receipt, createReqVO);
  62 + if (batch != null) {
  63 + line.setBatchId(batch.getId());
  64 + line.setBatchCode(batch.getCode());
  65 + }
  66 + // 2.3 插入数据库
52 67 itemReceiptLineMapper.insert(line);
53   -
54   - // TODO @芋艿:【暂时不处理】wmBatchService 需要生成下;基于 batchCode
55 68 return line.getId();
56 69 }
57 70  
58 71 @Override
59 72 public void updateItemReceiptLine(MesWmItemReceiptLineSaveReqVO updateReqVO) {
60   - // 校验存在
  73 + // 1.1 校验存在
61 74 MesWmItemReceiptLineDO line = validateItemReceiptLineExists(updateReqVO.getId());
62   - // 校验父单据存在且为可编辑状态
  75 + // 1.2 校验父单据存在且为可编辑状态
63 76 MesWmItemReceiptDO receipt = itemReceiptService.validateItemReceiptEditable(line.getReceiptId());
64   - // 校验关联到货通知单行存在
  77 + // 1.3 校验关联到货通知单行存在
65 78 validateArrivalNoticeLine(receipt, updateReqVO.getArrivalNoticeLineId());
66 79  
67   - // 更新
  80 + // 2.1 更新
68 81 MesWmItemReceiptLineDO updateObj = BeanUtils.toBean(updateReqVO, MesWmItemReceiptLineDO.class);
  82 + // 2.2 生成或获取批次
  83 + MesWmBatchDO batch = generateOrGetBatch(receipt, updateReqVO);
  84 + if (batch != null) {
  85 + updateObj.setBatchId(batch.getId()).setBatchCode(batch.getCode());
  86 + }
  87 + // 2.3 更新数据库
69 88 itemReceiptLineMapper.updateById(updateObj);
70   -
71   - // TODO @芋艿:【暂时不处理】wmBatchService 需要生成下;基于 batchCode
72 89 }
73 90  
74 91 @Override
... ... @@ -134,4 +151,31 @@ public class MesWmItemReceiptLineServiceImpl implements MesWmItemReceiptLineServ
134 151 return line;
135 152 }
136 153  
  154 + /**
  155 + * 生成或获取批次
  156 + *
  157 + * @param receipt 入库单
  158 + * @param reqVO 入库单行请求VO
  159 + * @return 批次记录(如果物料未启用批次管理则返回 null)
  160 + */
  161 + private MesWmBatchDO generateOrGetBatch(MesWmItemReceiptDO receipt, MesWmItemReceiptLineSaveReqVO reqVO) {
  162 + // 构建批次参数 VO
  163 + MesWmBatchGenerateReqVO batchReqVO = new MesWmBatchGenerateReqVO();
  164 + // 从入库单行获取
  165 + batchReqVO.setItemId(reqVO.getItemId()).setProduceDate(reqVO.getProductionDate())
  166 + .setExpireDate(reqVO.getExpireDate()).setLotNumber(reqVO.getProductionBatchNumber());
  167 + // 从父单据获取
  168 + batchReqVO.setVendorId(receipt.getVendorId()).setReceiptDate(receipt.getReceiptDate());
  169 + // 从到货通知单获取采购订单号(如果关联了到货通知单)
  170 + if (receipt.getNoticeId() != null) {
  171 + MesWmArrivalNoticeDO notice = arrivalNoticeService.getArrivalNotice(receipt.getNoticeId());
  172 + if (notice != null) {
  173 + batchReqVO.setPurchaseOrderCode(notice.getPurchaseOrderCode());
  174 + }
  175 + }
  176 +
  177 + // 生成或获取批次
  178 + return batchService.getOrGenerateBatchCode(batchReqVO);
  179 + }
  180 +
137 181 }
... ...