Commit 6ccf334c71110d7833090cb9a452e1b1f0489090

Authored by YunaiV
1 parent 6f68fd61

✨ feat(wm): 新增库存记录编号字段及相关校验

在多个请求对象中新增库存记录编号字段,并在相关服务中实现校验逻辑,以确保库存记录的有效性和一致性。这将增强库存管理的准确性,避免潜在的错误提交。

- MesWmProductIssueDetailSaveReqVO.java: 新增 materialStockId 字段
- MesWmReturnIssueDetailSaveReqVO.java: 新增 materialStockId 字段
- MesWmMaterialStockService.java: 新增 validateSelectedStock 方法
- MesWmMaterialStockServiceImpl.java: 实现 validateSelectedStock 方法
- MesWmProductIssueDetailServiceImpl.java: 更新校验逻辑以使用库存记录
- MesWmReturnIssueDetailServiceImpl.java: 更新校验逻辑以使用库存记录
- MesWmOutsourceIssueDetailServiceImpl.java: 更新校验逻辑以使用库存记录
- MesWmProductSalesDetailServiceImpl.java: 更新校验逻辑以使用库存记录
- MesWmReturnVendorDetailServiceImpl.java: 更新校验逻辑以使用库存记录
Showing 25 changed files with 414 additions and 57 deletions
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productissue/vo/detail/MesWmProductIssueDetailSaveReqVO.java
... ... @@ -26,6 +26,9 @@ public class MesWmProductIssueDetailSaveReqVO {
26 26 @NotNull(message = "物料编号不能为空")
27 27 private Long itemId;
28 28  
  29 + @Schema(description = "库存记录编号", example = "1")
  30 + private Long materialStockId;
  31 +
29 32 @Schema(description = "领料数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "300.00")
30 33 @NotNull(message = "领料数量不能为空")
31 34 @DecimalMin(value = "0", inclusive = false, message = "领料数量必须大于 0")
... ... @@ -49,4 +52,4 @@ public class MesWmProductIssueDetailSaveReqVO {
49 52 @Schema(description = "备注", example = "备注")
50 53 private String remark;
51 54  
52   - }
  55 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnissue/vo/detail/MesWmReturnIssueDetailSaveReqVO.java
... ... @@ -26,6 +26,9 @@ public class MesWmReturnIssueDetailSaveReqVO {
26 26 @NotNull(message = "物料编号不能为空")
27 27 private Long itemId;
28 28  
  29 + @Schema(description = "库存记录编号", example = "1")
  30 + private Long materialStockId;
  31 +
29 32 @Schema(description = "退料数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "300.00")
30 33 @NotNull(message = "退料数量不能为空")
31 34 @DecimalMin(value = "0", inclusive = false, message = "退料数量必须大于0")
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
... ... @@ -91,6 +91,7 @@ public interface ErrorCodeConstants {
91 91 ErrorCode MD_PRODUCT_BOM_NOT_EXISTS = new ErrorCode(1_040_107_000, "产品BOM不存在");
92 92 ErrorCode MD_PRODUCT_BOM_SELF_REFERENCE = new ErrorCode(1_040_107_001, "产品不能作为自身的BOM物料");
93 93 ErrorCode MD_PRODUCT_BOM_CIRCULAR = new ErrorCode(1_040_107_002, "BOM物料存在闭环,无法新增");
  94 + ErrorCode MD_PRODUCT_BOM_ITEM_INVALID = new ErrorCode(1_040_107_003, "选择的 BOM 物料不属于当前产品");
94 95  
95 96 // ========== MES 基础数据-产品SOP(1-040-108-000) ==========
96 97 ErrorCode MD_PRODUCT_SOP_NOT_EXISTS = new ErrorCode(1_040_108_000, "产品SOP不存在");
... ... @@ -167,6 +168,7 @@ public interface ErrorCodeConstants {
167 168 ErrorCode DV_CHECK_PLAN_NO_MACHINERY = new ErrorCode(1_040_302_003, "启用方案时,至少需要关联一台设备");
168 169 ErrorCode DV_CHECK_PLAN_NO_SUBJECT = new ErrorCode(1_040_302_004, "启用方案时,至少需要关联一个点检保养项目");
169 170 ErrorCode DV_CHECK_PLAN_NOT_ENABLED = new ErrorCode(1_040_302_005, "点检保养方案未启用,不允许停用");
  171 + ErrorCode DV_CHECK_PLAN_TYPE_MISMATCH = new ErrorCode(1_040_302_006, "点检保养方案类型与当前业务不匹配");
170 172 // ========== MES 设备管理-点检方案设备(1-040-302-100) ==========
171 173 ErrorCode DV_CHECK_PLAN_MACHINERY_NOT_EXISTS = new ErrorCode(1_040_302_100, "点检保养方案设备不存在");
172 174 ErrorCode DV_CHECK_PLAN_MACHINERY_DUPLICATE = new ErrorCode(1_040_302_101, "该设备已关联到当前方案,请勿重复添加");
... ... @@ -241,11 +243,17 @@ public interface ErrorCodeConstants {
241 243 ErrorCode PRO_WORK_ORDER_NOT_PREPARE = new ErrorCode(1_040_502_002, "只有草稿状态的工单才能执行此操作");
242 244 ErrorCode PRO_WORK_ORDER_NOT_CONFIRMED = new ErrorCode(1_040_502_003, "只有已确认状态的工单才能执行此操作");
243 245 ErrorCode PRO_WORK_ORDER_HAS_CHILDREN = new ErrorCode(1_040_502_004, "存在子工单,无法删除");
  246 + ErrorCode PRO_WORK_ORDER_PRODUCT_MISMATCH = new ErrorCode(1_040_502_005, "当前产品物料与生产工单产品不一致");
244 247 ErrorCode PRO_WORK_ORDER_BOM_NOT_EXISTS = new ErrorCode(1_040_502_100, "生产工单BOM不存在");
245 248  
246 249 // ========== MES 生产管理-生产任务(1-040-503-000) ==========
247 250 ErrorCode PRO_TASK_NOT_EXISTS = new ErrorCode(1_040_503_000, "生产任务不存在");
248 251 ErrorCode PRO_TASK_ALREADY_FINISHED = new ErrorCode(1_040_503_001, "生产任务已完成或已取消,不能继续操作");
  252 + ErrorCode PRO_TASK_WORK_ORDER_MISMATCH = new ErrorCode(1_040_503_002, "生产任务不属于当前生产工单");
  253 + ErrorCode PRO_TASK_WORKSTATION_MISMATCH = new ErrorCode(1_040_503_003, "生产任务不属于当前工作站");
  254 + ErrorCode PRO_TASK_ROUTE_PROCESS_MISMATCH = new ErrorCode(1_040_503_004, "生产任务与当前工艺路线或工序不一致");
  255 + ErrorCode PRO_TASK_ITEM_MISMATCH = new ErrorCode(1_040_503_005, "生产任务产品与当前产品物料不一致");
  256 + ErrorCode PRO_WORKSTATION_PROCESS_MISMATCH = new ErrorCode(1_040_503_006, "工作站所属工序与当前工序不一致");
249 257 // ========== MES 生产管理-生产任务投料(1-040-503-100) ==========
250 258 ErrorCode PRO_TASK_ISSUE_NOT_EXISTS = new ErrorCode(1_040_503_100, "生产任务投料记录不存在");
251 259  
... ... @@ -399,6 +407,8 @@ public interface ErrorCodeConstants {
399 407 ErrorCode WM_TRANSACTION_RELATED_NOT_EXISTS = new ErrorCode(1_040_703_010, "关联的库存事务不存在");
400 408 ErrorCode WM_TRANSACTION_LIST_EMPTY = new ErrorCode(1_040_703_011, "库存事务列表不能为空");
401 409 ErrorCode WM_TRANSACTION_BATCH_NOT_EXISTS = new ErrorCode(1_040_703_012, "批次记录不存在");
  410 + ErrorCode WM_MATERIAL_STOCK_REQUIRED = new ErrorCode(1_040_703_013, "库存记录不能为空");
  411 + ErrorCode WM_MATERIAL_STOCK_SELECTION_MISMATCH = new ErrorCode(1_040_703_014, "库存记录与提交的物料、批次或库位信息不一致");
402 412  
403 413 // ========== MES 仓库管理-到货通知单(1-040-704-000) ==========
404 414 ErrorCode WM_ARRIVAL_NOTICE_NOT_EXISTS = new ErrorCode(1_040_704_000, "到货通知单不存在");
... ... @@ -408,6 +418,7 @@ public interface ErrorCodeConstants {
408 418 ErrorCode WM_ARRIVAL_NOTICE_STATUS_NOT_PENDING_RECEIPT = new ErrorCode(1_040_704_004, "只有待入库状态才允许完成");
409 419 ErrorCode WM_ARRIVAL_NOTICE_IQC_PENDING = new ErrorCode(1_040_704_005, "存在待检验行,无法审批通过");
410 420 ErrorCode WM_ARRIVAL_NOTICE_NO_LINE = new ErrorCode(1_040_704_006, "至少需要一条行项目");
  421 + ErrorCode WM_ARRIVAL_NOTICE_VENDOR_MISMATCH = new ErrorCode(1_040_704_007, "到货通知单的供应商与当前单据不一致");
411 422 ErrorCode WM_ARRIVAL_NOTICE_LINE_NOT_EXISTS = new ErrorCode(1_040_704_100, "到货通知单行不存在");
412 423 ErrorCode WM_ARRIVAL_NOTICE_LINE_NOT_MATCH = new ErrorCode(1_040_704_101, "到货通知单行不属于指定的到货通知单");
413 424  
... ... @@ -444,6 +455,8 @@ public interface ErrorCodeConstants {
444 455 ErrorCode WM_OUTSOURCE_ISSUE_LINE_NOT_EXISTS = new ErrorCode(1_040_707_100, "外协发料单行不存在");
445 456 ErrorCode WM_OUTSOURCE_ISSUE_LINE_ITEM_NOT_IN_BOM = new ErrorCode(1_040_707_101, "发料单行对应的物料不在当前工单的 BOM 列表中");
446 457 ErrorCode WM_OUTSOURCE_ISSUE_DETAIL_NOT_EXISTS = new ErrorCode(1_040_707_200, "外协发料单明细不存在");
  458 + ErrorCode WM_OUTSOURCE_ISSUE_DETAIL_LINE_NOT_MATCH = new ErrorCode(1_040_707_201, "拣货明细不属于指定的外协发料单");
  459 + ErrorCode WM_OUTSOURCE_ISSUE_DETAIL_ITEM_MISMATCH = new ErrorCode(1_040_707_202, "拣货明细的物料与外协发料单行的物料不一致");
447 460 ErrorCode WM_OUTSOURCE_ISSUE_WORK_ORDER_TYPE_INVALID = new ErrorCode(1_040_707_008, "工单类型不是外协(代工)类型");
448 461  
449 462 // ========== MES 仓库管理-生产领料出库单(1-040-708-000) ==========
... ... @@ -459,6 +472,7 @@ public interface ErrorCodeConstants {
459 472 ErrorCode WM_PRODUCT_ISSUE_CODE_DUPLICATE = new ErrorCode(1_040_708_102, "领料出库单编码已存在");
460 473 ErrorCode WM_PRODUCT_ISSUE_DETAIL_NOT_EXISTS = new ErrorCode(1_040_708_200, "生产领料出库单明细不存在");
461 474 ErrorCode WM_PRODUCT_ISSUE_DETAIL_ITEM_MISMATCH = new ErrorCode(1_040_708_201, "拣货明细的物料与领料单行的物料不一致");
  475 + ErrorCode WM_PRODUCT_ISSUE_DETAIL_LINE_NOT_MATCH = new ErrorCode(1_040_708_202, "拣货明细不属于指定的领料出库单");
462 476  
463 477 // ========== MES 仓库管理-生产入库单(1-040-709-000) ==========
464 478 ErrorCode WM_PRODUCT_PRODUCE_NOT_EXISTS = new ErrorCode(1_040_709_000, "生产入库单不存在");
... ... @@ -501,6 +515,8 @@ public interface ErrorCodeConstants {
501 515 ErrorCode WM_RETURN_ISSUE_DETAIL_QUANTITY_INVALID = new ErrorCode(1_040_710_201, "退料明细数量必须大于0");
502 516 ErrorCode WM_RETURN_ISSUE_DETAIL_QUANTITY_EXCEED = new ErrorCode(1_040_710_202, "退料明细总数量不能超过退料单行数量");
503 517 ErrorCode WM_RETURN_ISSUE_CODE_DUPLICATE = new ErrorCode(1_040_710_203, "退料单编码已存在");
  518 + ErrorCode WM_RETURN_ISSUE_DETAIL_LINE_NOT_MATCH = new ErrorCode(1_040_710_204, "退料明细不属于指定的退料单");
  519 + ErrorCode WM_RETURN_ISSUE_DETAIL_ITEM_MISMATCH = new ErrorCode(1_040_710_205, "退料明细的物料与退料单行的物料不一致");
504 520  
505 521 // ========== MES 仓库管理-供应商退货单(1-040-711-000) ==========
506 522 ErrorCode WM_RETURN_VENDOR_NOT_EXISTS = new ErrorCode(1_040_711_000, "供应商退货单不存在");
... ... @@ -514,6 +530,7 @@ public interface ErrorCodeConstants {
514 530 ErrorCode WM_RETURN_VENDOR_LINE_NOT_EXISTS = new ErrorCode(1_040_711_100, "供应商退货单行不存在");
515 531 ErrorCode WM_RETURN_VENDOR_DETAIL_NOT_EXISTS = new ErrorCode(1_040_711_200, "供应商退货单明细不存在");
516 532 ErrorCode WM_RETURN_VENDOR_DETAIL_QUANTITY_INVALID = new ErrorCode(1_040_711_201, "退货明细数量必须大于 0");
  533 + ErrorCode WM_RETURN_VENDOR_DETAIL_LINE_NOT_MATCH = new ErrorCode(1_040_711_202, "拣货明细不属于指定的供应商退货单");
517 534  
518 535 // ========== MES 仓库管理-产品收货单(1-040-712-000) ==========
519 536 ErrorCode WM_PRODUCT_RECPT_NOT_EXISTS = new ErrorCode(1_040_712_000, "产品收货单不存在");
... ... @@ -588,6 +605,8 @@ public interface ErrorCodeConstants {
588 605 ErrorCode WM_PRODUCT_SALES_DETAIL_NOT_EXISTS = new ErrorCode(1_040_714_012, "销售出库明细不存在");
589 606 ErrorCode WM_PRODUCT_SALES_STOCK_INSUFFICIENT = new ErrorCode(1_040_714_013, "库存不足,无法拣货");
590 607 ErrorCode WM_PRODUCT_SALES_LINE_QUANTITY_INVALID = new ErrorCode(1_040_714_014, "出库数量必须大于 0");
  608 + ErrorCode WM_PRODUCT_SALES_DETAIL_LINE_NOT_MATCH = new ErrorCode(1_040_714_015, "拣货明细不属于指定的销售出库单");
  609 + ErrorCode WM_PRODUCT_SALES_DETAIL_ITEM_MISMATCH = new ErrorCode(1_040_714_016, "拣货明细的物料与销售出库单行的物料不一致");
591 610  
592 611 // ========== MES 仓库管理-杂项出库单(1-040-715-000) ==========
593 612 ErrorCode WM_MISC_ISSUE_NOT_EXISTS = new ErrorCode(1_040_715_000, "杂项出库单不存在");
... ... @@ -616,6 +635,8 @@ public interface ErrorCodeConstants {
616 635 ErrorCode WM_SALES_NOTICE_CODE_DUPLICATE = new ErrorCode(1_040_720_001, "通知单编号重复");
617 636 ErrorCode WM_SALES_NOTICE_STATUS_NOT_ALLOW_DELETE = new ErrorCode(1_040_720_002, "单据状态不允许删除");
618 637 ErrorCode WM_SALES_NOTICE_STATUS_NOT_ALLOW_UPDATE = new ErrorCode(1_040_720_003, "单据状态不允许修改");
  638 + ErrorCode WM_SALES_NOTICE_STATUS_NOT_APPROVED = new ErrorCode(1_040_720_004, "发货通知单不是待出库状态");
  639 + ErrorCode WM_SALES_NOTICE_CLIENT_MISMATCH = new ErrorCode(1_040_720_005, "发货通知单的客户与当前单据不一致");
619 640 ErrorCode WM_SALES_NOTICE_LINE_NOT_EXISTS = new ErrorCode(1_040_720_010, "发货通知单行不存在");
620 641 ErrorCode WM_SALES_NOTICE_LINE_EMPTY = new ErrorCode(1_040_720_011, "发货通知单行为空,不能提交");
621 642  
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/dv/MesDvCheckPlanTypeEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.enums.dv;
  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 + * MES 点检保养方案类型枚举
  11 + *
  12 + * @author 芋道源码
  13 + */
  14 +@Getter
  15 +@AllArgsConstructor
  16 +public enum MesDvCheckPlanTypeEnum implements ArrayValuable<Integer> {
  17 +
  18 + CHECK(1, "点检"),
  19 + MAINTENANCE(2, "保养");
  20 +
  21 + public static final Integer[] ARRAYS = Arrays.stream(values()).map(MesDvCheckPlanTypeEnum::getType).toArray(Integer[]::new);
  22 +
  23 + /**
  24 + * 类型值
  25 + */
  26 + private final Integer type;
  27 + /**
  28 + * 类型名
  29 + */
  30 + private final String name;
  31 +
  32 + @Override
  33 + public Integer[] array() {
  34 + return ARRAYS;
  35 + }
  36 +
  37 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/dv/checkplan/MesDvCheckPlanService.java
... ... @@ -63,6 +63,15 @@ public interface MesDvCheckPlanService {
63 63 void validateCheckPlanExists(Long id);
64 64  
65 65 /**
  66 + * 校验点检保养方案存在,且类型匹配
  67 + *
  68 + * @param id 方案编号
  69 + * @param type 期望的方案类型
  70 + * @return 方案
  71 + */
  72 + MesDvCheckPlanDO validateCheckPlanExistsAndType(Long id, Integer type);
  73 +
  74 + /**
66 75 * 校验点检保养方案为草稿状态
67 76 *
68 77 * @param planId 方案编号
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/dv/checkplan/MesDvCheckPlanServiceImpl.java
... ... @@ -149,6 +149,15 @@ public class MesDvCheckPlanServiceImpl implements MesDvCheckPlanService {
149 149 return plan;
150 150 }
151 151  
  152 + @Override
  153 + public MesDvCheckPlanDO validateCheckPlanExistsAndType(Long id, Integer type) {
  154 + MesDvCheckPlanDO plan = doValidateCheckPlanExists(id);
  155 + if (ObjUtil.notEqual(plan.getType(), type)) {
  156 + throw exception(DV_CHECK_PLAN_TYPE_MISMATCH);
  157 + }
  158 + return plan;
  159 + }
  160 +
152 161 private void validateCheckPlanCodeUnique(Long id, String code) {
153 162 MesDvCheckPlanDO plan = checkPlanMapper.selectByCode(code);
154 163 if (plan == null) {
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/dv/checkrecord/MesDvCheckRecordServiceImpl.java
... ... @@ -10,6 +10,7 @@ import cn.iocoder.yudao.module.mes.dal.dataobject.dv.checkplan.MesDvCheckPlanSub
10 10 import cn.iocoder.yudao.module.mes.dal.dataobject.dv.checkrecord.MesDvCheckRecordDO;
11 11 import cn.iocoder.yudao.module.mes.dal.dataobject.dv.checkrecord.MesDvCheckRecordLineDO;
12 12 import cn.iocoder.yudao.module.mes.enums.dv.MesDvCheckRecordStatusEnum;
  13 +import cn.iocoder.yudao.module.mes.enums.dv.MesDvCheckPlanTypeEnum;
13 14 import cn.iocoder.yudao.module.mes.enums.dv.MesDvCheckResultEnum;
14 15 import cn.iocoder.yudao.module.mes.dal.mysql.dv.checkrecord.MesDvCheckRecordMapper;
15 16 import cn.iocoder.yudao.module.mes.service.dv.checkplan.MesDvCheckPlanService;
... ... @@ -37,6 +38,7 @@ import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
37 38 @Validated
38 39 public class MesDvCheckRecordServiceImpl implements MesDvCheckRecordService {
39 40  
  41 +
40 42 @Resource
41 43 private MesDvCheckRecordMapper checkRecordMapper;
42 44 @Resource
... ... @@ -162,7 +164,7 @@ public class MesDvCheckRecordServiceImpl implements MesDvCheckRecordService {
162 164 machineryService.validateMachineryExists(reqVO.getMachineryId());
163 165 // 校验点检计划是否存在
164 166 if (reqVO.getPlanId() != null) {
165   - checkPlanService.validateCheckPlanExists(reqVO.getPlanId());
  167 + checkPlanService.validateCheckPlanExistsAndType(reqVO.getPlanId(), MesDvCheckPlanTypeEnum.CHECK.getType());
166 168 }
167 169 // 校验点检人是否存在
168 170 if (reqVO.getUserId() != null) {
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/dv/maintenrecord/MesDvMaintenRecordServiceImpl.java
... ... @@ -10,6 +10,7 @@ import cn.iocoder.yudao.module.mes.dal.dataobject.dv.maintenrecord.MesDvMaintenR
10 10 import cn.iocoder.yudao.module.mes.dal.dataobject.dv.maintenrecord.MesDvMaintenRecordLineDO;
11 11 import cn.iocoder.yudao.module.mes.dal.mysql.dv.maintenrecord.MesDvMaintenRecordMapper;
12 12 import cn.iocoder.yudao.module.mes.enums.dv.MesDvMaintenRecordStatusEnum;
  13 +import cn.iocoder.yudao.module.mes.enums.dv.MesDvCheckPlanTypeEnum;
13 14 import cn.iocoder.yudao.module.mes.service.dv.checkplan.MesDvCheckPlanService;
14 15 import cn.iocoder.yudao.module.mes.service.dv.machinery.MesDvMachineryService;
15 16 import jakarta.annotation.Resource;
... ... @@ -100,7 +101,7 @@ public class MesDvMaintenRecordServiceImpl implements MesDvMaintenRecordService
100 101 machineryService.validateMachineryExists(reqVO.getMachineryId());
101 102 // 校验保养计划是否存在
102 103 if (reqVO.getPlanId() != null) {
103   - checkPlanService.validateCheckPlanExists(reqVO.getPlanId());
  104 + checkPlanService.validateCheckPlanExistsAndType(reqVO.getPlanId(), MesDvCheckPlanTypeEnum.MAINTENANCE.getType());
104 105 }
105 106 }
106 107  
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/card/MesProCardServiceImpl.java
... ... @@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
7 7 import cn.iocoder.yudao.module.mes.controller.admin.pro.card.vo.MesProCardPageReqVO;
8 8 import cn.iocoder.yudao.module.mes.controller.admin.pro.card.vo.MesProCardSaveReqVO;
9 9 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.card.MesProCardDO;
  10 +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
10 11 import cn.iocoder.yudao.module.mes.dal.mysql.pro.card.MesProCardMapper;
11 12 import cn.iocoder.yudao.module.mes.enums.pro.MesProWorkOrderStatusEnum;
12 13 import cn.iocoder.yudao.module.mes.enums.wm.BarcodeBizTypeEnum;
... ... @@ -171,9 +172,12 @@ public class MesProCardServiceImpl implements MesProCardService {
171 172 // 校验编码唯一
172 173 validateCardCodeUnique(reqVO.getId(), reqVO.getCode());
173 174 // 校验工单存在
174   - workOrderService.validateWorkOrderExists(reqVO.getWorkOrderId());
  175 + MesProWorkOrderDO workOrder = workOrderService.validateWorkOrderConfirmed(reqVO.getWorkOrderId());
175 176 // 校验物料存在
176 177 itemService.validateItemExists(reqVO.getItemId());
  178 + if (ObjUtil.notEqual(workOrder.getProductId(), reqVO.getItemId())) {
  179 + throw exception(PRO_WORK_ORDER_PRODUCT_MISMATCH);
  180 + }
177 181 }
178 182  
179 183 }
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/feedback/MesProFeedbackService.java
... ... @@ -46,6 +46,14 @@ public interface MesProFeedbackService {
46 46 MesProFeedbackDO getFeedback(Long id);
47 47  
48 48 /**
  49 + * 校验生产报工存在
  50 + *
  51 + * @param id 编号
  52 + * @return 生产报工
  53 + */
  54 + MesProFeedbackDO validateFeedbackExists(Long id);
  55 +
  56 + /**
49 57 * 获得生产报工分页
50 58 *
51 59 * @param pageReqVO 分页查询
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/feedback/MesProFeedbackServiceImpl.java
... ... @@ -4,11 +4,13 @@ import cn.hutool.core.util.ObjUtil;
4 4 import cn.hutool.core.util.ObjectUtil;
5 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  7 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkstationDO;
7 8 import cn.iocoder.yudao.module.mes.controller.admin.pro.feedback.vo.MesProFeedbackPageReqVO;
8 9 import cn.iocoder.yudao.module.mes.controller.admin.pro.feedback.vo.MesProFeedbackSaveReqVO;
9 10 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO;
10 11 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessDO;
11 12 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO;
  13 +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
12 14 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemconsume.MesWmItemConsumeDO;
13 15 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceDO;
14 16 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceLineDO;
... ... @@ -219,7 +221,8 @@ public class MesProFeedbackServiceImpl implements MesProFeedbackService {
219 221  
220 222 // ==================== 校验方法 ====================
221 223  
222   - private MesProFeedbackDO validateFeedbackExists(Long id) {
  224 + @Override
  225 + public MesProFeedbackDO validateFeedbackExists(Long id) {
223 226 MesProFeedbackDO feedback = feedbackMapper.selectById(id);
224 227 if (feedback == null) {
225 228 throw exception(PRO_FEEDBACK_NOT_EXISTS);
... ... @@ -251,7 +254,10 @@ public class MesProFeedbackServiceImpl implements MesProFeedbackService {
251 254 */
252 255 private MesProTaskDO validateFeedbackData(MesProFeedbackSaveReqVO reqVO) {
253 256 // 1. 校验工作站存在
254   - workstationService.validateWorkstationExists(reqVO.getWorkstationId());
  257 + MesMdWorkstationDO workstation = workstationService.validateWorkstationExists(reqVO.getWorkstationId());
  258 + if (ObjUtil.notEqual(workstation.getProcessId(), reqVO.getProcessId())) {
  259 + throw exception(PRO_WORKSTATION_PROCESS_MISMATCH);
  260 + }
255 261  
256 262 // 2.1 校验工艺路线 + 工序配置有效
257 263 MesProRouteProcessDO routeProcess = routeProcessService.getRouteProcessByRouteIdAndProcessId(
... ... @@ -277,10 +283,32 @@ public class MesProFeedbackServiceImpl implements MesProFeedbackService {
277 283 }
278 284  
279 285 // 3. 校验工单已确认
280   - workOrderService.validateWorkOrderConfirmed(reqVO.getWorkOrderId());
  286 + MesProWorkOrderDO workOrder = workOrderService.validateWorkOrderConfirmed(reqVO.getWorkOrderId());
  287 + if (ObjUtil.notEqual(workOrder.getProductId(), reqVO.getItemId())) {
  288 + throw exception(PRO_WORK_ORDER_PRODUCT_MISMATCH);
  289 + }
281 290  
282 291 // 4. 校验任务存在且未终态(已完成/已取消),并返回任务用于冗余 itemId
283   - return taskService.validateTaskNotFinished(reqVO.getTaskId());
  292 + MesProTaskDO task = taskService.validateTaskNotFinished(reqVO.getTaskId());
  293 + validateTaskRelation(task, workstation, workOrder, reqVO);
  294 + return task;
  295 + }
  296 +
  297 + private void validateTaskRelation(MesProTaskDO task, MesMdWorkstationDO workstation,
  298 + MesProWorkOrderDO workOrder, MesProFeedbackSaveReqVO reqVO) {
  299 + if (ObjUtil.notEqual(task.getWorkOrderId(), workOrder.getId())) {
  300 + throw exception(PRO_TASK_WORK_ORDER_MISMATCH);
  301 + }
  302 + if (ObjUtil.notEqual(task.getWorkstationId(), workstation.getId())) {
  303 + throw exception(PRO_TASK_WORKSTATION_MISMATCH);
  304 + }
  305 + if (ObjUtil.notEqual(task.getRouteId(), reqVO.getRouteId())
  306 + || ObjUtil.notEqual(task.getProcessId(), reqVO.getProcessId())) {
  307 + throw exception(PRO_TASK_ROUTE_PROCESS_MISMATCH);
  308 + }
  309 + if (ObjUtil.notEqual(task.getItemId(), reqVO.getItemId())) {
  310 + throw exception(PRO_TASK_ITEM_MISMATCH);
  311 + }
284 312 }
285 313  
286 314 @Override
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProductBomServiceImpl.java
1 1 package cn.iocoder.yudao.module.mes.service.pro.route;
2 2  
  3 +import cn.hutool.core.collection.CollUtil;
3 4 import cn.hutool.core.util.ObjUtil;
4 5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
5 6 import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.productbom.MesProRouteProductBomSaveReqVO;
6 7 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductBomDO;
7 8 import cn.iocoder.yudao.module.mes.dal.mysql.pro.route.MesProRouteProductBomMapper;
  9 +import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
  10 +import cn.iocoder.yudao.module.mes.service.md.item.MesMdProductBomService;
8 11 import jakarta.annotation.Resource;
9 12 import org.springframework.context.annotation.Lazy;
10 13 import org.springframework.stereotype.Service;
... ... @@ -30,13 +33,16 @@ public class MesProRouteProductBomServiceImpl implements MesProRouteProductBomSe
30 33 @Resource
31 34 @Lazy
32 35 private MesProRouteService routeService;
  36 + @Resource
  37 + private MesMdItemService itemService;
  38 + @Resource
  39 + private MesMdProductBomService productBomService;
33 40  
34 41 @Override
35 42 public Long createRouteProductBom(MesProRouteProductBomSaveReqVO createReqVO) {
36   - // 1.0 已启用的工艺路线,不允许操作
37   - routeService.validateRouteNotEnable(createReqVO.getRouteId());
38   - // 1.1 校验唯一性
39   - validateBomUnique(null, createReqVO.getItemId(), createReqVO.getProcessId(), createReqVO.getProductId());
  43 + // 1. 校验数据
  44 + validateRouteProductBomSaveData(null, createReqVO);
  45 +
40 46 // 2. 插入
41 47 MesProRouteProductBomDO routeProductBom = BeanUtils.toBean(createReqVO, MesProRouteProductBomDO.class);
42 48 routeProductBomMapper.insert(routeProductBom);
... ... @@ -45,13 +51,11 @@ public class MesProRouteProductBomServiceImpl implements MesProRouteProductBomSe
45 51  
46 52 @Override
47 53 public void updateRouteProductBom(MesProRouteProductBomSaveReqVO updateReqVO) {
48   - // 1.0 已启用的工艺路线,不允许操作
49   - routeService.validateRouteNotEnable(updateReqVO.getRouteId());
50   - // 1.1 校验存在
  54 + // 1. 校验存在 + 校验数据
51 55 validateRouteProductBomExists(updateReqVO.getId());
52   - // 1.2 校验唯一性
53   - validateBomUnique(updateReqVO.getId(), updateReqVO.getItemId(), updateReqVO.getProcessId(), updateReqVO.getProductId());
54   - // 3. 更新
  56 + validateRouteProductBomSaveData(updateReqVO.getId(), updateReqVO);
  57 +
  58 + // 2. 更新
55 59 MesProRouteProductBomDO updateObj = BeanUtils.toBean(updateReqVO, MesProRouteProductBomDO.class);
56 60 routeProductBomMapper.updateById(updateObj);
57 61 }
... ... @@ -75,6 +79,21 @@ public class MesProRouteProductBomServiceImpl implements MesProRouteProductBomSe
75 79 }
76 80 }
77 81  
  82 + /**
  83 + * 校验保存时的关联数据
  84 + *
  85 + * @param id 记录编号(新增时为 null)
  86 + * @param reqVO 保存请求
  87 + */
  88 + private void validateRouteProductBomSaveData(Long id, MesProRouteProductBomSaveReqVO reqVO) {
  89 + // 校验已启用的工艺路线,不允许操作
  90 + routeService.validateRouteNotEnable(reqVO.getRouteId());
  91 + // 校验唯一性
  92 + validateBomUnique(id, reqVO.getItemId(), reqVO.getProcessId(), reqVO.getProductId());
  93 + // 校验物料属于产品 BOM
  94 + validateBomItemBelongsToProduct(reqVO.getProductId(), reqVO.getItemId());
  95 + }
  96 +
78 97 private void validateBomUnique(Long id, Long itemId, Long processId, Long productId) {
79 98 MesProRouteProductBomDO existing = routeProductBomMapper.selectByUnique(itemId, processId, productId);
80 99 if (existing == null) {
... ... @@ -85,6 +104,14 @@ public class MesProRouteProductBomServiceImpl implements MesProRouteProductBomSe
85 104 }
86 105 }
87 106  
  107 + private void validateBomItemBelongsToProduct(Long productId, Long itemId) {
  108 + itemService.validateItemExists(itemId);
  109 + if (!CollUtil.anyMatch(productBomService.getProductBomListByItemId(productId),
  110 + productBom -> ObjUtil.equal(productBom.getBomItemId(), itemId))) {
  111 + throw exception(MD_PRODUCT_BOM_ITEM_INVALID);
  112 + }
  113 + }
  114 +
88 115 @Override
89 116 public MesProRouteProductBomDO getRouteProductBom(Long id) {
90 117 return routeProductBomMapper.selectById(id);
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderBomServiceImpl.java
... ... @@ -9,6 +9,7 @@ import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.bom.MesProW
9 9 import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
10 10 import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdProductBomDO;
11 11 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderBomDO;
  12 +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
12 13 import cn.iocoder.yudao.module.mes.dal.mysql.pro.workorder.MesProWorkOrderBomMapper;
13 14 import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
14 15 import cn.iocoder.yudao.module.mes.service.md.item.MesMdProductBomService;
... ... @@ -25,6 +26,7 @@ import java.util.Set;
25 26 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
26 27 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
27 28 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.MD_ITEM_NOT_EXISTS;
  29 +import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.MD_PRODUCT_BOM_ITEM_INVALID;
28 30 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_WORK_ORDER_BOM_NOT_EXISTS;
29 31  
30 32 /**
... ... @@ -49,8 +51,8 @@ public class MesProWorkOrderBomServiceImpl implements MesProWorkOrderBomService
49 51  
50 52 @Override
51 53 public Long createWorkOrderBom(MesProWorkOrderBomSaveReqVO createReqVO) {
52   - // 校验工单存在
53   - workOrderService.validateWorkOrderExists(createReqVO.getWorkOrderId());
  54 + // 校验数据
  55 + validateWorkOrderBomSaveData(createReqVO);
54 56  
55 57 // 插入数据
56 58 MesProWorkOrderBomDO workOrderBom = BeanUtils.toBean(createReqVO, MesProWorkOrderBomDO.class);
... ... @@ -62,6 +64,8 @@ public class MesProWorkOrderBomServiceImpl implements MesProWorkOrderBomService
62 64 public void updateWorkOrderBom(MesProWorkOrderBomSaveReqVO updateReqVO) {
63 65 // 校验存在
64 66 validateWorkOrderBomExists(updateReqVO.getId());
  67 + // 校验数据
  68 + validateWorkOrderBomSaveData(updateReqVO);
65 69  
66 70 // 更新数据
67 71 MesProWorkOrderBomDO updateObj = BeanUtils.toBean(updateReqVO, MesProWorkOrderBomDO.class);
... ... @@ -83,6 +87,19 @@ public class MesProWorkOrderBomServiceImpl implements MesProWorkOrderBomService
83 87 }
84 88 }
85 89  
  90 + private void validateWorkOrderBomSaveData(MesProWorkOrderBomSaveReqVO reqVO) {
  91 + // 校验工单存在
  92 + MesProWorkOrderDO workOrder = workOrderService.validateWorkOrderExists(reqVO.getWorkOrderId());
  93 + // DONE @AI:增加注释
  94 + // 校验物料存在
  95 + itemService.validateItemExists(reqVO.getItemId());
  96 + // 校验物料属于产品 BOM
  97 + if (!CollUtil.anyMatch(productBomService.getProductBomListByItemId(workOrder.getProductId()),
  98 + productBom -> productBom.getBomItemId().equals(reqVO.getItemId()))) {
  99 + throw exception(MD_PRODUCT_BOM_ITEM_INVALID);
  100 + }
  101 + }
  102 +
86 103 @Override
87 104 public MesProWorkOrderBomDO getWorkOrderBom(Long id) {
88 105 return workOrderBomMapper.selectById(id);
... ... @@ -133,4 +150,4 @@ public class MesProWorkOrderBomServiceImpl implements MesProWorkOrderBomService
133 150 workOrderBomMapper.insertBatch(bomList);
134 151 }
135 152  
136   -}
137 153 \ No newline at end of file
  154 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/ipqc/MesQcIpqcServiceImpl.java
... ... @@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.mes.controller.admin.qc.ipqc.vo.MesQcIpqcPageReqV
8 8 import cn.iocoder.yudao.module.mes.controller.admin.qc.ipqc.vo.MesQcIpqcSaveReqVO;
9 9 import cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkstationDO;
10 10 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductDO;
  11 +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO;
11 12 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
12 13 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO;
13 14 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.defectrecord.MesQcDefectRecordDO;
... ... @@ -23,6 +24,7 @@ import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationServic
23 24 import cn.iocoder.yudao.module.mes.service.pro.feedback.MesProFeedbackService;
24 25 import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProcessService;
25 26 import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductService;
  27 +import cn.iocoder.yudao.module.mes.service.pro.task.MesProTaskService;
26 28 import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService;
27 29 import cn.iocoder.yudao.module.mes.service.qc.defectrecord.MesQcDefectRecordService;
28 30 import cn.iocoder.yudao.module.mes.service.qc.template.MesQcTemplateItemService;
... ... @@ -76,6 +78,9 @@ public class MesQcIpqcServiceImpl implements MesQcIpqcService {
76 78 @Resource
77 79 @Lazy
78 80 private MesProRouteProcessService routeProcessService;
  81 + @Resource
  82 + @Lazy
  83 + private MesProTaskService taskService;
79 84  
80 85 @Resource
81 86 private AdminUserApi adminUserApi;
... ... @@ -136,9 +141,39 @@ public class MesQcIpqcServiceImpl implements MesQcIpqcService {
136 141 // 校验工位、检测人员存在
137 142 MesMdWorkstationDO workstation = workstationService.validateWorkstationExists(reqVO.getWorkstationId());
138 143 adminUserApi.validateUser(reqVO.getInspectorUserId());
  144 + // 校验工单存在
  145 + MesProWorkOrderDO workOrder = workOrderService.validateWorkOrderConfirmed(reqVO.getWorkOrderId());
  146 + if (reqVO.getItemId() != null && ObjUtil.notEqual(reqVO.getItemId(), workOrder.getProductId())) {
  147 + throw exception(PRO_WORK_ORDER_PRODUCT_MISMATCH);
  148 + }
  149 + if (reqVO.getProcessId() != null && ObjUtil.notEqual(workstation.getProcessId(), reqVO.getProcessId())) {
  150 + throw exception(PRO_WORKSTATION_PROCESS_MISMATCH);
  151 + }
  152 + // 校验任务关系(如果指定了任务)
  153 + if (reqVO.getTaskId() != null) {
  154 + MesProTaskDO task = taskService.validateTaskNotFinished(reqVO.getTaskId());
  155 + validateTaskRelation(task, workstation, workOrder, reqVO);
  156 + }
139 157 return workstation;
140 158 }
141 159  
  160 + private void validateTaskRelation(MesProTaskDO task, MesMdWorkstationDO workstation,
  161 + MesProWorkOrderDO workOrder, MesQcIpqcSaveReqVO reqVO) {
  162 + if (ObjUtil.notEqual(task.getWorkOrderId(), workOrder.getId())) {
  163 + throw exception(PRO_TASK_WORK_ORDER_MISMATCH);
  164 + }
  165 + if (ObjUtil.notEqual(task.getWorkstationId(), workstation.getId())) {
  166 + throw exception(PRO_TASK_WORKSTATION_MISMATCH);
  167 + }
  168 + Long expectedProcessId = reqVO.getProcessId() != null ? reqVO.getProcessId() : workstation.getProcessId();
  169 + if (expectedProcessId != null && ObjUtil.notEqual(task.getProcessId(), expectedProcessId)) {
  170 + throw exception(PRO_TASK_ROUTE_PROCESS_MISMATCH);
  171 + }
  172 + if (ObjUtil.notEqual(task.getItemId(), workOrder.getProductId())) {
  173 + throw exception(PRO_TASK_ITEM_MISMATCH);
  174 + }
  175 + }
  176 +
142 177 @Override
143 178 @Transactional(rollbackFor = Exception.class)
144 179 public void finishIpqc(Long id) {
... ... @@ -238,8 +273,8 @@ public class MesQcIpqcServiceImpl implements MesQcIpqcService {
238 273 return null;
239 274 }
240 275 if (Objects.equals(sourceDocType, MesBizTypeConstants.PRO_FEEDBACK)) {
241   - MesProFeedbackDO feedback = feedbackService.getFeedback(sourceDocId);
242   - return feedback != null ? feedback.getCode() : null;
  276 + MesProFeedbackDO feedback = feedbackService.validateFeedbackExists(sourceDocId);
  277 + return feedback.getCode();
243 278 }
244 279 return null;
245 280 }
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/itemreceipt/MesWmItemReceiptServiceImpl.java
... ... @@ -8,11 +8,13 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
8 8 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
9 9 import cn.iocoder.yudao.module.mes.controller.admin.wm.itemreceipt.vo.MesWmItemReceiptPageReqVO;
10 10 import cn.iocoder.yudao.module.mes.controller.admin.wm.itemreceipt.vo.MesWmItemReceiptSaveReqVO;
  11 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.arrivalnotice.MesWmArrivalNoticeDO;
11 12 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemreceipt.MesWmItemReceiptDO;
12 13 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemreceipt.MesWmItemReceiptDetailDO;
13 14 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemreceipt.MesWmItemReceiptLineDO;
14 15 import cn.iocoder.yudao.module.mes.dal.mysql.wm.itemreceipt.MesWmItemReceiptMapper;
15 16 import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
  17 +import cn.iocoder.yudao.module.mes.enums.wm.MesWmArrivalNoticeStatusEnum;
16 18 import cn.iocoder.yudao.module.mes.enums.wm.MesWmItemReceiptStatusEnum;
17 19 import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum;
18 20 import cn.iocoder.yudao.module.mes.service.md.vendor.MesMdVendorService;
... ... @@ -90,7 +92,13 @@ public class MesWmItemReceiptServiceImpl implements MesWmItemReceiptService {
90 92 vendorService.validateVendorExists(reqVO.getVendorId());
91 93 // 校验到货通知单存在
92 94 if (reqVO.getNoticeId() != null) {
93   - arrivalNoticeService.validateArrivalNoticeExists(reqVO.getNoticeId());
  95 + MesWmArrivalNoticeDO notice = arrivalNoticeService.validateArrivalNoticeExists(reqVO.getNoticeId());
  96 + if (ObjUtil.notEqual(notice.getStatus(), MesWmArrivalNoticeStatusEnum.PENDING_RECEIPT.getStatus())) {
  97 + throw exception(WM_ARRIVAL_NOTICE_STATUS_NOT_PENDING_RECEIPT);
  98 + }
  99 + if (ObjUtil.notEqual(notice.getVendorId(), reqVO.getVendorId())) {
  100 + throw exception(WM_ARRIVAL_NOTICE_VENDOR_MISMATCH);
  101 + }
94 102 }
95 103 // 校验来料检验单存在
96 104 if (reqVO.getIqcId() != null) {
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockService.java
... ... @@ -132,4 +132,20 @@ public interface MesWmMaterialStockService {
132 132 */
133 133 void checkAreaMixingRule(Long areaId, Long itemId, Long batchId);
134 134  
  135 + /**
  136 + * 校验前端选择的库存记录,兜底避免串单或越权提交
  137 + *
  138 + * @param materialStockId 库存记录编号
  139 + * @param itemId 物料编号
  140 + * @param batchId 批次编号
  141 + * @param batchCode 批次号(可选)
  142 + * @param warehouseId 仓库编号
  143 + * @param locationId 库区编号
  144 + * @param areaId 库位编号
  145 + * @param quantity 数量(可选,校验库存充足)
  146 + * @return 校验通过的库存记录
  147 + */
  148 + MesWmMaterialStockDO validateSelectedStock(Long materialStockId, Long itemId, Long batchId, String batchCode,
  149 + Long warehouseId, Long locationId, Long areaId, BigDecimal quantity);
  150 +
135 151 }
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockServiceImpl.java
... ... @@ -185,4 +185,34 @@ public class MesWmMaterialStockServiceImpl implements MesWmMaterialStockService
185 185 }
186 186 }
187 187  
  188 + @Override
  189 + public MesWmMaterialStockDO validateSelectedStock(Long materialStockId,
  190 + Long itemId,
  191 + Long batchId,
  192 + String batchCode,
  193 + Long warehouseId,
  194 + Long locationId,
  195 + Long areaId,
  196 + BigDecimal quantity) {
  197 + // 1. 校验库存记录必须存在
  198 + if (materialStockId == null) {
  199 + throw exception(WM_MATERIAL_STOCK_REQUIRED);
  200 + }
  201 + MesWmMaterialStockDO stock = validateMaterialStockExists(materialStockId);
  202 + // 2. 校验库存记录的物料、批次、仓库、库区、库位等信息与前端选择的一致,避免串单或越权提交
  203 + if (ObjUtil.notEqual(stock.getItemId(), itemId)
  204 + || ObjUtil.notEqual(stock.getBatchId(), batchId)
  205 + || (batchCode != null && ObjUtil.notEqual(stock.getBatchCode(), batchCode))
  206 + || ObjUtil.notEqual(stock.getWarehouseId(), warehouseId)
  207 + || ObjUtil.notEqual(stock.getLocationId(), locationId)
  208 + || ObjUtil.notEqual(stock.getAreaId(), areaId)) {
  209 + throw exception(WM_MATERIAL_STOCK_SELECTION_MISMATCH);
  210 + }
  211 + // 3. 校验库存数量充足(如果前端传了 quantity,则必须保证库存数量 >= quantity)
  212 + if (quantity != null && stock.getQuantity() != null && stock.getQuantity().compareTo(quantity) < 0) {
  213 + throw exception(WM_MATERIAL_STOCK_INSUFFICIENT);
  214 + }
  215 + return stock;
  216 + }
  217 +
188 218 }
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/outsourceissue/MesWmOutsourceIssueDetailServiceImpl.java
1 1 package cn.iocoder.yudao.module.mes.service.wm.outsourceissue;
2 2  
  3 +import cn.hutool.core.util.ObjUtil;
3 4 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
4 5 import cn.iocoder.yudao.module.mes.controller.admin.wm.outsourceissue.vo.detail.MesWmOutsourceIssueDetailSaveReqVO;
5 6 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.outsourceissue.MesWmOutsourceIssueDetailDO;
  7 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.outsourceissue.MesWmOutsourceIssueLineDO;
6 8 import cn.iocoder.yudao.module.mes.dal.mysql.wm.outsourceissue.MesWmOutsourceIssueDetailMapper;
7 9 import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
  10 +import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService;
  11 +import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService;
8 12 import jakarta.annotation.Resource;
9 13 import org.springframework.context.annotation.Lazy;
10 14 import org.springframework.stereotype.Service;
... ... @@ -13,7 +17,7 @@ import org.springframework.validation.annotation.Validated;
13 17 import java.util.List;
14 18  
15 19 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
16   -import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_OUTSOURCE_ISSUE_DETAIL_NOT_EXISTS;
  20 +import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
17 21  
18 22 /**
19 23 * MES 外协发料单明细 Service 实现类
... ... @@ -28,9 +32,13 @@ public class MesWmOutsourceIssueDetailServiceImpl implements MesWmOutsourceIssue
28 32 private MesWmOutsourceIssueDetailMapper outsourceIssueDetailMapper;
29 33 @Resource
30 34 @Lazy
31   - private MesWmOutsourceIssueService outsourceIssueService;
  35 + private MesWmOutsourceIssueLineService outsourceIssueLineService;
32 36 @Resource
33 37 private MesMdItemService itemService;
  38 + @Resource
  39 + private MesWmWarehouseAreaService warehouseAreaService;
  40 + @Resource
  41 + private MesWmMaterialStockService materialStockService;
34 42  
35 43 @Override
36 44 public Long createOutsourceIssueDetail(MesWmOutsourceIssueDetailSaveReqVO createReqVO) {
... ... @@ -95,10 +103,26 @@ public class MesWmOutsourceIssueDetailServiceImpl implements MesWmOutsourceIssue
95 103 }
96 104  
97 105 private void validateOutsourceIssueDetailSaveData(MesWmOutsourceIssueDetailSaveReqVO saveReqVO) {
98   - // 校验关联的发料单存在
99   - outsourceIssueService.getOutsourceIssue(saveReqVO.getIssueId());
100   - // 校验关联的物料存在
  106 + // 校验父数据(行)存在
  107 + MesWmOutsourceIssueLineDO line = outsourceIssueLineService.getOutsourceIssueLine(saveReqVO.getLineId());
  108 + if (line == null) {
  109 + throw exception(WM_OUTSOURCE_ISSUE_LINE_NOT_EXISTS);
  110 + }
  111 + if (ObjUtil.notEqual(line.getIssueId(), saveReqVO.getIssueId())) {
  112 + throw exception(WM_OUTSOURCE_ISSUE_DETAIL_LINE_NOT_MATCH);
  113 + }
  114 + // 校验物料存在
101 115 itemService.validateItemExists(saveReqVO.getItemId());
  116 + if (ObjUtil.notEqual(line.getItemId(), saveReqVO.getItemId())) {
  117 + throw exception(WM_OUTSOURCE_ISSUE_DETAIL_ITEM_MISMATCH);
  118 + }
  119 + // 校验仓库、库区、库位的关联关系
  120 + warehouseAreaService.validateWarehouseAreaExists(
  121 + saveReqVO.getWarehouseId(), saveReqVO.getLocationId(), saveReqVO.getAreaId());
  122 + // 校验库存记录存在且物料一致
  123 + materialStockService.validateSelectedStock(
  124 + saveReqVO.getMaterialStockId(), saveReqVO.getItemId(), saveReqVO.getBatchId(), null,
  125 + saveReqVO.getWarehouseId(), saveReqVO.getLocationId(), saveReqVO.getAreaId(), saveReqVO.getQuantity());
102 126 }
103 127  
104 128 }
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueDetailServiceImpl.java
... ... @@ -9,6 +9,8 @@ import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productissue.MesWmProductIs
9 9 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productissue.MesWmProductIssueLineDO;
10 10 import cn.iocoder.yudao.module.mes.dal.mysql.wm.productissue.MesWmProductIssueDetailMapper;
11 11 import cn.iocoder.yudao.module.mes.enums.wm.MesWmProductIssueStatusEnum;
  12 +import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService;
  13 +import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService;
12 14 import jakarta.annotation.Resource;
13 15 import org.springframework.context.annotation.Lazy;
14 16 import org.springframework.stereotype.Service;
... ... @@ -35,6 +37,10 @@ public class MesWmProductIssueDetailServiceImpl implements MesWmProductIssueDeta
35 37 @Resource
36 38 @Lazy
37 39 private MesWmProductIssueLineService issueLineService;
  40 + @Resource
  41 + private MesWmWarehouseAreaService warehouseAreaService;
  42 + @Resource
  43 + private MesWmMaterialStockService materialStockService;
38 44  
39 45 @Override
40 46 public Long createProductIssueDetail(MesWmProductIssueDetailSaveReqVO createReqVO) {
... ... @@ -116,8 +122,18 @@ public class MesWmProductIssueDetailServiceImpl implements MesWmProductIssueDeta
116 122 private void validateProductIssueDetailSaveData(MesWmProductIssueDetailSaveReqVO reqVO) {
117 123 // 校验父数据存在
118 124 MesWmProductIssueLineDO line = issueLineService.validateProductIssueLineExists(reqVO.getLineId());
  125 + if (ObjUtil.notEqual(line.getIssueId(), reqVO.getIssueId())) {
  126 + throw exception(WM_PRODUCT_ISSUE_DETAIL_LINE_NOT_MATCH);
  127 + }
119 128 // 校验物料匹配(明细 itemId 必须与行 itemId 一致)
120 129 validateDetailItemMatch(reqVO.getItemId(), line.getItemId());
  130 + // 校验仓库、库区、库位的关联关系
  131 + warehouseAreaService.validateWarehouseAreaExists(
  132 + reqVO.getWarehouseId(), reqVO.getLocationId(), reqVO.getAreaId());
  133 + // 校验库存记录
  134 + materialStockService.validateSelectedStock(
  135 + reqVO.getMaterialStockId(), reqVO.getItemId(), reqVO.getBatchId(), reqVO.getBatchCode(),
  136 + reqVO.getWarehouseId(), reqVO.getLocationId(), reqVO.getAreaId(), reqVO.getQuantity());
121 137 }
122 138  
123 139 /**
... ... @@ -130,4 +146,3 @@ public class MesWmProductIssueDetailServiceImpl implements MesWmProductIssueDeta
130 146 }
131 147  
132 148 }
133   -
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesDetailServiceImpl.java
1 1 package cn.iocoder.yudao.module.mes.service.wm.productsales;
2 2  
  3 +import cn.hutool.core.util.ObjUtil;
3 4 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
4 5 import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.detail.MesWmProductSalesDetailSaveReqVO;
5 6 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDetailDO;
  7 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesLineDO;
6 8 import cn.iocoder.yudao.module.mes.dal.mysql.wm.productsales.MesWmProductSalesDetailMapper;
7 9 import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
  10 +import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService;
8 11 import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService;
9 12 import jakarta.annotation.Resource;
10 13 import org.springframework.stereotype.Service;
... ... @@ -29,17 +32,17 @@ public class MesWmProductSalesDetailServiceImpl implements MesWmProductSalesDeta
29 32  
30 33 @Resource
31 34 private MesMdItemService itemService;
32   -
  35 + @Resource
  36 + private MesWmProductSalesLineService productSalesLineService;
  37 + @Resource
  38 + private MesWmMaterialStockService materialStockService;
33 39 @Resource
34 40 private MesWmWarehouseAreaService warehouseAreaService;
35 41  
36 42 @Override
37 43 public Long createProductSalesDetail(MesWmProductSalesDetailSaveReqVO createReqVO) {
38   - // 校验物料存在
39   - itemService.validateItemExists(createReqVO.getItemId());
40   - // 校验库区关系
41   - warehouseAreaService.validateWarehouseAreaExists(
42   - createReqVO.getWarehouseId(), createReqVO.getLocationId(), createReqVO.getAreaId());
  44 + // 校验数据
  45 + validateProductSalesDetailSaveData(createReqVO);
43 46  
44 47 // 插入
45 48 MesWmProductSalesDetailDO detail = BeanUtils.toBean(createReqVO, MesWmProductSalesDetailDO.class);
... ... @@ -51,11 +54,8 @@ public class MesWmProductSalesDetailServiceImpl implements MesWmProductSalesDeta
51 54 public void updateProductSalesDetail(MesWmProductSalesDetailSaveReqVO updateReqVO) {
52 55 // 校验存在
53 56 validateProductSalesDetailExists(updateReqVO.getId());
54   - // 校验物料存在
55   - itemService.validateItemExists(updateReqVO.getItemId());
56   - // 校验库区关系
57   - warehouseAreaService.validateWarehouseAreaExists(
58   - updateReqVO.getWarehouseId(), updateReqVO.getLocationId(), updateReqVO.getAreaId());
  57 + // 校验数据
  58 + validateProductSalesDetailSaveData(updateReqVO);
59 59  
60 60 // 更新
61 61 MesWmProductSalesDetailDO updateObj = BeanUtils.toBean(updateReqVO, MesWmProductSalesDetailDO.class);
... ... @@ -103,4 +103,27 @@ public class MesWmProductSalesDetailServiceImpl implements MesWmProductSalesDeta
103 103 return detail;
104 104 }
105 105  
  106 + private void validateProductSalesDetailSaveData(MesWmProductSalesDetailSaveReqVO reqVO) {
  107 + // 校验父数据(行)存在
  108 + MesWmProductSalesLineDO line = productSalesLineService.getProductSalesLine(reqVO.getLineId());
  109 + if (line == null) {
  110 + throw exception(WM_PRODUCT_SALES_LINE_NOT_EXISTS);
  111 + }
  112 + if (ObjUtil.notEqual(line.getSalesId(), reqVO.getSalesId())) {
  113 + throw exception(WM_PRODUCT_SALES_DETAIL_LINE_NOT_MATCH);
  114 + }
  115 + // 校验物料存在
  116 + itemService.validateItemExists(reqVO.getItemId());
  117 + if (ObjUtil.notEqual(line.getItemId(), reqVO.getItemId())) {
  118 + throw exception(WM_PRODUCT_SALES_DETAIL_ITEM_MISMATCH);
  119 + }
  120 + // 校验库位存在
  121 + warehouseAreaService.validateWarehouseAreaExists(
  122 + reqVO.getWarehouseId(), reqVO.getLocationId(), reqVO.getAreaId());
  123 + // 校验库存记录存在且物料一致
  124 + materialStockService.validateSelectedStock(
  125 + reqVO.getMaterialStockId(), reqVO.getItemId(), reqVO.getBatchId(), reqVO.getBatchCode(),
  126 + reqVO.getWarehouseId(), reqVO.getLocationId(), reqVO.getAreaId(), reqVO.getQuantity());
  127 + }
  128 +
106 129 }
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesServiceImpl.java
... ... @@ -9,6 +9,7 @@ import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
9 9 import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesPageReqVO;
10 10 import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesSaveReqVO;
11 11 import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesShippingReqVO;
  12 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.salesnotice.MesWmSalesNoticeDO;
12 13 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDO;
13 14 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDetailDO;
14 15 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesLineDO;
... ... @@ -16,8 +17,10 @@ import cn.iocoder.yudao.module.mes.dal.mysql.wm.productsales.MesWmProductSalesMa
16 17 import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
17 18 import cn.iocoder.yudao.module.mes.enums.wm.MesWmProductSalesStatusEnum;
18 19 import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum;
  20 +import cn.iocoder.yudao.module.mes.enums.wm.MesWmSalesNoticeStatusEnum;
19 21 import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum;
20 22 import cn.iocoder.yudao.module.mes.service.md.client.MesMdClientService;
  23 +import cn.iocoder.yudao.module.mes.service.wm.salesnotice.MesWmSalesNoticeService;
21 24 import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService;
22 25 import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO;
23 26 import jakarta.annotation.Resource;
... ... @@ -54,6 +57,9 @@ public class MesWmProductSalesServiceImpl implements MesWmProductSalesService {
54 57 private MesMdClientService clientService;
55 58  
56 59 @Resource
  60 + private MesWmSalesNoticeService salesNoticeService;
  61 +
  62 + @Resource
57 63 private MesWmTransactionService wmTransactionService;
58 64  
59 65 @Override
... ... @@ -262,6 +268,16 @@ public class MesWmProductSalesServiceImpl implements MesWmProductSalesService {
262 268 validateCodeUnique(id, reqVO.getCode());
263 269 // 校验客户存在
264 270 clientService.validateClientExists(reqVO.getClientId());
  271 + // 校验发货通知单
  272 + if (reqVO.getNoticeId() != null) {
  273 + MesWmSalesNoticeDO notice = salesNoticeService.validateSalesNoticeExists(reqVO.getNoticeId());
  274 + if (ObjUtil.notEqual(notice.getStatus(), MesWmSalesNoticeStatusEnum.APPROVED.getStatus())) {
  275 + throw exception(WM_SALES_NOTICE_STATUS_NOT_APPROVED);
  276 + }
  277 + if (ObjUtil.notEqual(notice.getClientId(), reqVO.getClientId())) {
  278 + throw exception(WM_SALES_NOTICE_CLIENT_MISMATCH);
  279 + }
  280 + }
265 281 }
266 282  
267 283 private MesWmProductSalesDO validateProductSalesExists(Long id) {
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueDetailServiceImpl.java
1 1 package cn.iocoder.yudao.module.mes.service.wm.returnissue;
2 2  
  3 +import cn.hutool.core.util.ObjUtil;
3 4 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
4 5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
5 6 import cn.iocoder.yudao.module.mes.controller.admin.wm.returnissue.vo.detail.MesWmReturnIssueDetailSaveReqVO;
... ... @@ -17,8 +18,7 @@ import java.math.BigDecimal;
17 18 import java.util.List;
18 19  
19 20 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
20   -import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_RETURN_ISSUE_DETAIL_NOT_EXISTS;
21   -import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_RETURN_ISSUE_DETAIL_QUANTITY_EXCEED;
  21 +import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
22 22  
23 23 /**
24 24 * MES 生产退料明细 Service 实现类
... ... @@ -105,11 +105,21 @@ public class MesWmReturnIssueDetailServiceImpl implements MesWmReturnIssueDetail
105 105 private void validateReturnIssueDetailSaveData(MesWmReturnIssueDetailSaveReqVO reqVO) {
106 106 // 校验父数据存在
107 107 MesWmReturnIssueLineDO line = issueLineService.validateReturnIssueLineExists(reqVO.getLineId());
  108 + if (ObjUtil.notEqual(line.getIssueId(), reqVO.getIssueId())) {
  109 + throw exception(WM_RETURN_ISSUE_DETAIL_LINE_NOT_MATCH);
  110 + }
  111 + if (ObjUtil.notEqual(line.getItemId(), reqVO.getItemId())) {
  112 + throw exception(WM_RETURN_ISSUE_DETAIL_ITEM_MISMATCH);
  113 + }
108 114 // 校验仓库、库区、库位的关联关系
109 115 warehouseAreaService.validateWarehouseAreaExists(
110 116 reqVO.getWarehouseId(), reqVO.getLocationId(), reqVO.getAreaId());
111 117 // 校验库位物料/批次混放规则
112 118 materialStockService.checkAreaMixingRule(reqVO.getAreaId(), reqVO.getItemId(), reqVO.getBatchId());
  119 + // 校验库存记录
  120 + materialStockService.validateSelectedStock(
  121 + reqVO.getMaterialStockId(), reqVO.getItemId(), reqVO.getBatchId(), reqVO.getBatchCode(),
  122 + reqVO.getWarehouseId(), reqVO.getLocationId(), reqVO.getAreaId(), reqVO.getQuantity());
113 123 // 校验明细总数量不超过行数量(排除自身)
114 124 validateDetailQuantityNotExceed(reqVO.getLineId(), reqVO.getQuantity(), reqVO.getId(), line);
115 125 }
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnvendor/MesWmReturnVendorDetailServiceImpl.java
... ... @@ -3,12 +3,12 @@ package cn.iocoder.yudao.module.mes.service.wm.returnvendor;
3 3 import cn.hutool.core.util.ObjUtil;
4 4 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
5 5 import cn.iocoder.yudao.module.mes.controller.admin.wm.returnvendor.vo.detail.MesWmReturnVendorDetailSaveReqVO;
6   -import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterialStockDO;
7 6 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnvendor.MesWmReturnVendorDetailDO;
8 7 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnvendor.MesWmReturnVendorLineDO;
9 8 import cn.iocoder.yudao.module.mes.dal.mysql.wm.returnvendor.MesWmReturnVendorDetailMapper;
10 9 import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
11 10 import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService;
  11 +import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService;
12 12 import jakarta.annotation.Resource;
13 13 import org.springframework.context.annotation.Lazy;
14 14 import org.springframework.stereotype.Service;
... ... @@ -32,12 +32,12 @@ public class MesWmReturnVendorDetailServiceImpl implements MesWmReturnVendorDeta
32 32 @Resource
33 33 @Lazy
34 34 private MesWmReturnVendorLineService returnVendorLineService;
35   -
36 35 @Resource
37 36 private MesMdItemService itemService;
38   -
39 37 @Resource
40 38 private MesWmMaterialStockService materialStockService;
  39 + @Resource
  40 + private MesWmWarehouseAreaService warehouseAreaService;
41 41  
42 42 @Override
43 43 public Long createReturnVendorDetail(MesWmReturnVendorDetailSaveReqVO createReqVO) {
... ... @@ -68,12 +68,18 @@ public class MesWmReturnVendorDetailServiceImpl implements MesWmReturnVendorDeta
68 68 private void validateDetailSaveData(MesWmReturnVendorDetailSaveReqVO reqVO) {
69 69 // 校验父数据(行)存在
70 70 MesWmReturnVendorLineDO line = returnVendorLineService.validateReturnVendorLineExists(reqVO.getLineId());
  71 + if (ObjUtil.notEqual(line.getReturnId(), reqVO.getReturnId())) {
  72 + throw exception(WM_RETURN_VENDOR_DETAIL_LINE_NOT_MATCH);
  73 + }
71 74 // 校验物料存在
72 75 itemService.validateItemExists(reqVO.getItemId());
73 76 // 校验物料与行物料一致
74 77 validateItemConsistency(reqVO.getItemId(), line);
  78 + // 校验库位存在
  79 + warehouseAreaService.validateWarehouseAreaExists(
  80 + reqVO.getWarehouseId(), reqVO.getLocationId(), reqVO.getAreaId());
75 81 // 校验库存记录存在且物料一致
76   - validateMaterialStock(reqVO.getMaterialStockId(), line);
  82 + validateMaterialStock(reqVO, line);
77 83 }
78 84  
79 85 @Override
... ... @@ -127,15 +133,11 @@ public class MesWmReturnVendorDetailServiceImpl implements MesWmReturnVendorDeta
127 133 /**
128 134 * 校验库存记录存在且物料与行物料一致
129 135 */
130   - private void validateMaterialStock(Long materialStockId, MesWmReturnVendorLineDO line) {
131   - if (materialStockId == null) {
132   - return;
133   - }
134   - MesWmMaterialStockDO stock = materialStockService.getMaterialStock(materialStockId);
135   - if (stock == null) {
136   - throw exception(WM_MATERIAL_STOCK_NOT_EXISTS);
137   - }
138   - if (ObjUtil.notEqual(stock.getItemId(), line.getItemId())) {
  136 + private void validateMaterialStock(MesWmReturnVendorDetailSaveReqVO reqVO, MesWmReturnVendorLineDO line) {
  137 + materialStockService.validateSelectedStock(
  138 + reqVO.getMaterialStockId(), reqVO.getItemId(), reqVO.getBatchId(), reqVO.getBatchCode(),
  139 + reqVO.getWarehouseId(), reqVO.getLocationId(), reqVO.getAreaId(), reqVO.getQuantity());
  140 + if (ObjUtil.notEqual(line.getItemId(), reqVO.getItemId())) {
139 141 throw exception(WM_RETURN_VENDOR_DETAIL_ITEM_MISMATCH);
140 142 }
141 143 }
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/salesnotice/MesWmSalesNoticeService.java
... ... @@ -40,6 +40,14 @@ public interface MesWmSalesNoticeService {
40 40 void deleteSalesNotice(Long id);
41 41  
42 42 /**
  43 + * 校验发货通知单存在
  44 + *
  45 + * @param id 编号
  46 + * @return 发货通知单
  47 + */
  48 + MesWmSalesNoticeDO validateSalesNoticeExists(Long id);
  49 +
  50 + /**
43 51 * 获得发货通知单
44 52 *
45 53 * @param id 编号
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/salesnotice/MesWmSalesNoticeServiceImpl.java
... ... @@ -111,7 +111,8 @@ public class MesWmSalesNoticeServiceImpl implements MesWmSalesNoticeService {
111 111 return salesNoticeMapper.selectByIds(ids);
112 112 }
113 113  
114   - private MesWmSalesNoticeDO validateSalesNoticeExists(Long id) {
  114 + @Override
  115 + public MesWmSalesNoticeDO validateSalesNoticeExists(Long id) {
115 116 MesWmSalesNoticeDO notice = salesNoticeMapper.selectById(id);
116 117 if (notice == null) {
117 118 throw exception(WM_SALES_NOTICE_NOT_EXISTS);
... ...