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,6 +26,9 @@ public class MesWmProductIssueDetailSaveReqVO {
26 @NotNull(message = "物料编号不能为空") 26 @NotNull(message = "物料编号不能为空")
27 private Long itemId; 27 private Long itemId;
28 28
  29 + @Schema(description = "库存记录编号", example = "1")
  30 + private Long materialStockId;
  31 +
29 @Schema(description = "领料数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "300.00") 32 @Schema(description = "领料数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "300.00")
30 @NotNull(message = "领料数量不能为空") 33 @NotNull(message = "领料数量不能为空")
31 @DecimalMin(value = "0", inclusive = false, message = "领料数量必须大于 0") 34 @DecimalMin(value = "0", inclusive = false, message = "领料数量必须大于 0")
@@ -49,4 +52,4 @@ public class MesWmProductIssueDetailSaveReqVO { @@ -49,4 +52,4 @@ public class MesWmProductIssueDetailSaveReqVO {
49 @Schema(description = "备注", example = "备注") 52 @Schema(description = "备注", example = "备注")
50 private String remark; 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,6 +26,9 @@ public class MesWmReturnIssueDetailSaveReqVO {
26 @NotNull(message = "物料编号不能为空") 26 @NotNull(message = "物料编号不能为空")
27 private Long itemId; 27 private Long itemId;
28 28
  29 + @Schema(description = "库存记录编号", example = "1")
  30 + private Long materialStockId;
  31 +
29 @Schema(description = "退料数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "300.00") 32 @Schema(description = "退料数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "300.00")
30 @NotNull(message = "退料数量不能为空") 33 @NotNull(message = "退料数量不能为空")
31 @DecimalMin(value = "0", inclusive = false, message = "退料数量必须大于0") 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,6 +91,7 @@ public interface ErrorCodeConstants {
91 ErrorCode MD_PRODUCT_BOM_NOT_EXISTS = new ErrorCode(1_040_107_000, "产品BOM不存在"); 91 ErrorCode MD_PRODUCT_BOM_NOT_EXISTS = new ErrorCode(1_040_107_000, "产品BOM不存在");
92 ErrorCode MD_PRODUCT_BOM_SELF_REFERENCE = new ErrorCode(1_040_107_001, "产品不能作为自身的BOM物料"); 92 ErrorCode MD_PRODUCT_BOM_SELF_REFERENCE = new ErrorCode(1_040_107_001, "产品不能作为自身的BOM物料");
93 ErrorCode MD_PRODUCT_BOM_CIRCULAR = new ErrorCode(1_040_107_002, "BOM物料存在闭环,无法新增"); 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 // ========== MES 基础数据-产品SOP(1-040-108-000) ========== 96 // ========== MES 基础数据-产品SOP(1-040-108-000) ==========
96 ErrorCode MD_PRODUCT_SOP_NOT_EXISTS = new ErrorCode(1_040_108_000, "产品SOP不存在"); 97 ErrorCode MD_PRODUCT_SOP_NOT_EXISTS = new ErrorCode(1_040_108_000, "产品SOP不存在");
@@ -167,6 +168,7 @@ public interface ErrorCodeConstants { @@ -167,6 +168,7 @@ public interface ErrorCodeConstants {
167 ErrorCode DV_CHECK_PLAN_NO_MACHINERY = new ErrorCode(1_040_302_003, "启用方案时,至少需要关联一台设备"); 168 ErrorCode DV_CHECK_PLAN_NO_MACHINERY = new ErrorCode(1_040_302_003, "启用方案时,至少需要关联一台设备");
168 ErrorCode DV_CHECK_PLAN_NO_SUBJECT = new ErrorCode(1_040_302_004, "启用方案时,至少需要关联一个点检保养项目"); 169 ErrorCode DV_CHECK_PLAN_NO_SUBJECT = new ErrorCode(1_040_302_004, "启用方案时,至少需要关联一个点检保养项目");
169 ErrorCode DV_CHECK_PLAN_NOT_ENABLED = new ErrorCode(1_040_302_005, "点检保养方案未启用,不允许停用"); 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 // ========== MES 设备管理-点检方案设备(1-040-302-100) ========== 172 // ========== MES 设备管理-点检方案设备(1-040-302-100) ==========
171 ErrorCode DV_CHECK_PLAN_MACHINERY_NOT_EXISTS = new ErrorCode(1_040_302_100, "点检保养方案设备不存在"); 173 ErrorCode DV_CHECK_PLAN_MACHINERY_NOT_EXISTS = new ErrorCode(1_040_302_100, "点检保养方案设备不存在");
172 ErrorCode DV_CHECK_PLAN_MACHINERY_DUPLICATE = new ErrorCode(1_040_302_101, "该设备已关联到当前方案,请勿重复添加"); 174 ErrorCode DV_CHECK_PLAN_MACHINERY_DUPLICATE = new ErrorCode(1_040_302_101, "该设备已关联到当前方案,请勿重复添加");
@@ -241,11 +243,17 @@ public interface ErrorCodeConstants { @@ -241,11 +243,17 @@ public interface ErrorCodeConstants {
241 ErrorCode PRO_WORK_ORDER_NOT_PREPARE = new ErrorCode(1_040_502_002, "只有草稿状态的工单才能执行此操作"); 243 ErrorCode PRO_WORK_ORDER_NOT_PREPARE = new ErrorCode(1_040_502_002, "只有草稿状态的工单才能执行此操作");
242 ErrorCode PRO_WORK_ORDER_NOT_CONFIRMED = new ErrorCode(1_040_502_003, "只有已确认状态的工单才能执行此操作"); 244 ErrorCode PRO_WORK_ORDER_NOT_CONFIRMED = new ErrorCode(1_040_502_003, "只有已确认状态的工单才能执行此操作");
243 ErrorCode PRO_WORK_ORDER_HAS_CHILDREN = new ErrorCode(1_040_502_004, "存在子工单,无法删除"); 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 ErrorCode PRO_WORK_ORDER_BOM_NOT_EXISTS = new ErrorCode(1_040_502_100, "生产工单BOM不存在"); 247 ErrorCode PRO_WORK_ORDER_BOM_NOT_EXISTS = new ErrorCode(1_040_502_100, "生产工单BOM不存在");
245 248
246 // ========== MES 生产管理-生产任务(1-040-503-000) ========== 249 // ========== MES 生产管理-生产任务(1-040-503-000) ==========
247 ErrorCode PRO_TASK_NOT_EXISTS = new ErrorCode(1_040_503_000, "生产任务不存在"); 250 ErrorCode PRO_TASK_NOT_EXISTS = new ErrorCode(1_040_503_000, "生产任务不存在");
248 ErrorCode PRO_TASK_ALREADY_FINISHED = new ErrorCode(1_040_503_001, "生产任务已完成或已取消,不能继续操作"); 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 // ========== MES 生产管理-生产任务投料(1-040-503-100) ========== 257 // ========== MES 生产管理-生产任务投料(1-040-503-100) ==========
250 ErrorCode PRO_TASK_ISSUE_NOT_EXISTS = new ErrorCode(1_040_503_100, "生产任务投料记录不存在"); 258 ErrorCode PRO_TASK_ISSUE_NOT_EXISTS = new ErrorCode(1_040_503_100, "生产任务投料记录不存在");
251 259
@@ -399,6 +407,8 @@ public interface ErrorCodeConstants { @@ -399,6 +407,8 @@ public interface ErrorCodeConstants {
399 ErrorCode WM_TRANSACTION_RELATED_NOT_EXISTS = new ErrorCode(1_040_703_010, "关联的库存事务不存在"); 407 ErrorCode WM_TRANSACTION_RELATED_NOT_EXISTS = new ErrorCode(1_040_703_010, "关联的库存事务不存在");
400 ErrorCode WM_TRANSACTION_LIST_EMPTY = new ErrorCode(1_040_703_011, "库存事务列表不能为空"); 408 ErrorCode WM_TRANSACTION_LIST_EMPTY = new ErrorCode(1_040_703_011, "库存事务列表不能为空");
401 ErrorCode WM_TRANSACTION_BATCH_NOT_EXISTS = new ErrorCode(1_040_703_012, "批次记录不存在"); 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 // ========== MES 仓库管理-到货通知单(1-040-704-000) ========== 413 // ========== MES 仓库管理-到货通知单(1-040-704-000) ==========
404 ErrorCode WM_ARRIVAL_NOTICE_NOT_EXISTS = new ErrorCode(1_040_704_000, "到货通知单不存在"); 414 ErrorCode WM_ARRIVAL_NOTICE_NOT_EXISTS = new ErrorCode(1_040_704_000, "到货通知单不存在");
@@ -408,6 +418,7 @@ public interface ErrorCodeConstants { @@ -408,6 +418,7 @@ public interface ErrorCodeConstants {
408 ErrorCode WM_ARRIVAL_NOTICE_STATUS_NOT_PENDING_RECEIPT = new ErrorCode(1_040_704_004, "只有待入库状态才允许完成"); 418 ErrorCode WM_ARRIVAL_NOTICE_STATUS_NOT_PENDING_RECEIPT = new ErrorCode(1_040_704_004, "只有待入库状态才允许完成");
409 ErrorCode WM_ARRIVAL_NOTICE_IQC_PENDING = new ErrorCode(1_040_704_005, "存在待检验行,无法审批通过"); 419 ErrorCode WM_ARRIVAL_NOTICE_IQC_PENDING = new ErrorCode(1_040_704_005, "存在待检验行,无法审批通过");
410 ErrorCode WM_ARRIVAL_NOTICE_NO_LINE = new ErrorCode(1_040_704_006, "至少需要一条行项目"); 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 ErrorCode WM_ARRIVAL_NOTICE_LINE_NOT_EXISTS = new ErrorCode(1_040_704_100, "到货通知单行不存在"); 422 ErrorCode WM_ARRIVAL_NOTICE_LINE_NOT_EXISTS = new ErrorCode(1_040_704_100, "到货通知单行不存在");
412 ErrorCode WM_ARRIVAL_NOTICE_LINE_NOT_MATCH = new ErrorCode(1_040_704_101, "到货通知单行不属于指定的到货通知单"); 423 ErrorCode WM_ARRIVAL_NOTICE_LINE_NOT_MATCH = new ErrorCode(1_040_704_101, "到货通知单行不属于指定的到货通知单");
413 424
@@ -444,6 +455,8 @@ public interface ErrorCodeConstants { @@ -444,6 +455,8 @@ public interface ErrorCodeConstants {
444 ErrorCode WM_OUTSOURCE_ISSUE_LINE_NOT_EXISTS = new ErrorCode(1_040_707_100, "外协发料单行不存在"); 455 ErrorCode WM_OUTSOURCE_ISSUE_LINE_NOT_EXISTS = new ErrorCode(1_040_707_100, "外协发料单行不存在");
445 ErrorCode WM_OUTSOURCE_ISSUE_LINE_ITEM_NOT_IN_BOM = new ErrorCode(1_040_707_101, "发料单行对应的物料不在当前工单的 BOM 列表中"); 456 ErrorCode WM_OUTSOURCE_ISSUE_LINE_ITEM_NOT_IN_BOM = new ErrorCode(1_040_707_101, "发料单行对应的物料不在当前工单的 BOM 列表中");
446 ErrorCode WM_OUTSOURCE_ISSUE_DETAIL_NOT_EXISTS = new ErrorCode(1_040_707_200, "外协发料单明细不存在"); 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 ErrorCode WM_OUTSOURCE_ISSUE_WORK_ORDER_TYPE_INVALID = new ErrorCode(1_040_707_008, "工单类型不是外协(代工)类型"); 460 ErrorCode WM_OUTSOURCE_ISSUE_WORK_ORDER_TYPE_INVALID = new ErrorCode(1_040_707_008, "工单类型不是外协(代工)类型");
448 461
449 // ========== MES 仓库管理-生产领料出库单(1-040-708-000) ========== 462 // ========== MES 仓库管理-生产领料出库单(1-040-708-000) ==========
@@ -459,6 +472,7 @@ public interface ErrorCodeConstants { @@ -459,6 +472,7 @@ public interface ErrorCodeConstants {
459 ErrorCode WM_PRODUCT_ISSUE_CODE_DUPLICATE = new ErrorCode(1_040_708_102, "领料出库单编码已存在"); 472 ErrorCode WM_PRODUCT_ISSUE_CODE_DUPLICATE = new ErrorCode(1_040_708_102, "领料出库单编码已存在");
460 ErrorCode WM_PRODUCT_ISSUE_DETAIL_NOT_EXISTS = new ErrorCode(1_040_708_200, "生产领料出库单明细不存在"); 473 ErrorCode WM_PRODUCT_ISSUE_DETAIL_NOT_EXISTS = new ErrorCode(1_040_708_200, "生产领料出库单明细不存在");
461 ErrorCode WM_PRODUCT_ISSUE_DETAIL_ITEM_MISMATCH = new ErrorCode(1_040_708_201, "拣货明细的物料与领料单行的物料不一致"); 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 // ========== MES 仓库管理-生产入库单(1-040-709-000) ========== 477 // ========== MES 仓库管理-生产入库单(1-040-709-000) ==========
464 ErrorCode WM_PRODUCT_PRODUCE_NOT_EXISTS = new ErrorCode(1_040_709_000, "生产入库单不存在"); 478 ErrorCode WM_PRODUCT_PRODUCE_NOT_EXISTS = new ErrorCode(1_040_709_000, "生产入库单不存在");
@@ -501,6 +515,8 @@ public interface ErrorCodeConstants { @@ -501,6 +515,8 @@ public interface ErrorCodeConstants {
501 ErrorCode WM_RETURN_ISSUE_DETAIL_QUANTITY_INVALID = new ErrorCode(1_040_710_201, "退料明细数量必须大于0"); 515 ErrorCode WM_RETURN_ISSUE_DETAIL_QUANTITY_INVALID = new ErrorCode(1_040_710_201, "退料明细数量必须大于0");
502 ErrorCode WM_RETURN_ISSUE_DETAIL_QUANTITY_EXCEED = new ErrorCode(1_040_710_202, "退料明细总数量不能超过退料单行数量"); 516 ErrorCode WM_RETURN_ISSUE_DETAIL_QUANTITY_EXCEED = new ErrorCode(1_040_710_202, "退料明细总数量不能超过退料单行数量");
503 ErrorCode WM_RETURN_ISSUE_CODE_DUPLICATE = new ErrorCode(1_040_710_203, "退料单编码已存在"); 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 // ========== MES 仓库管理-供应商退货单(1-040-711-000) ========== 521 // ========== MES 仓库管理-供应商退货单(1-040-711-000) ==========
506 ErrorCode WM_RETURN_VENDOR_NOT_EXISTS = new ErrorCode(1_040_711_000, "供应商退货单不存在"); 522 ErrorCode WM_RETURN_VENDOR_NOT_EXISTS = new ErrorCode(1_040_711_000, "供应商退货单不存在");
@@ -514,6 +530,7 @@ public interface ErrorCodeConstants { @@ -514,6 +530,7 @@ public interface ErrorCodeConstants {
514 ErrorCode WM_RETURN_VENDOR_LINE_NOT_EXISTS = new ErrorCode(1_040_711_100, "供应商退货单行不存在"); 530 ErrorCode WM_RETURN_VENDOR_LINE_NOT_EXISTS = new ErrorCode(1_040_711_100, "供应商退货单行不存在");
515 ErrorCode WM_RETURN_VENDOR_DETAIL_NOT_EXISTS = new ErrorCode(1_040_711_200, "供应商退货单明细不存在"); 531 ErrorCode WM_RETURN_VENDOR_DETAIL_NOT_EXISTS = new ErrorCode(1_040_711_200, "供应商退货单明细不存在");
516 ErrorCode WM_RETURN_VENDOR_DETAIL_QUANTITY_INVALID = new ErrorCode(1_040_711_201, "退货明细数量必须大于 0"); 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 // ========== MES 仓库管理-产品收货单(1-040-712-000) ========== 535 // ========== MES 仓库管理-产品收货单(1-040-712-000) ==========
519 ErrorCode WM_PRODUCT_RECPT_NOT_EXISTS = new ErrorCode(1_040_712_000, "产品收货单不存在"); 536 ErrorCode WM_PRODUCT_RECPT_NOT_EXISTS = new ErrorCode(1_040_712_000, "产品收货单不存在");
@@ -588,6 +605,8 @@ public interface ErrorCodeConstants { @@ -588,6 +605,8 @@ public interface ErrorCodeConstants {
588 ErrorCode WM_PRODUCT_SALES_DETAIL_NOT_EXISTS = new ErrorCode(1_040_714_012, "销售出库明细不存在"); 605 ErrorCode WM_PRODUCT_SALES_DETAIL_NOT_EXISTS = new ErrorCode(1_040_714_012, "销售出库明细不存在");
589 ErrorCode WM_PRODUCT_SALES_STOCK_INSUFFICIENT = new ErrorCode(1_040_714_013, "库存不足,无法拣货"); 606 ErrorCode WM_PRODUCT_SALES_STOCK_INSUFFICIENT = new ErrorCode(1_040_714_013, "库存不足,无法拣货");
590 ErrorCode WM_PRODUCT_SALES_LINE_QUANTITY_INVALID = new ErrorCode(1_040_714_014, "出库数量必须大于 0"); 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 // ========== MES 仓库管理-杂项出库单(1-040-715-000) ========== 611 // ========== MES 仓库管理-杂项出库单(1-040-715-000) ==========
593 ErrorCode WM_MISC_ISSUE_NOT_EXISTS = new ErrorCode(1_040_715_000, "杂项出库单不存在"); 612 ErrorCode WM_MISC_ISSUE_NOT_EXISTS = new ErrorCode(1_040_715_000, "杂项出库单不存在");
@@ -616,6 +635,8 @@ public interface ErrorCodeConstants { @@ -616,6 +635,8 @@ public interface ErrorCodeConstants {
616 ErrorCode WM_SALES_NOTICE_CODE_DUPLICATE = new ErrorCode(1_040_720_001, "通知单编号重复"); 635 ErrorCode WM_SALES_NOTICE_CODE_DUPLICATE = new ErrorCode(1_040_720_001, "通知单编号重复");
617 ErrorCode WM_SALES_NOTICE_STATUS_NOT_ALLOW_DELETE = new ErrorCode(1_040_720_002, "单据状态不允许删除"); 636 ErrorCode WM_SALES_NOTICE_STATUS_NOT_ALLOW_DELETE = new ErrorCode(1_040_720_002, "单据状态不允许删除");
618 ErrorCode WM_SALES_NOTICE_STATUS_NOT_ALLOW_UPDATE = new ErrorCode(1_040_720_003, "单据状态不允许修改"); 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 ErrorCode WM_SALES_NOTICE_LINE_NOT_EXISTS = new ErrorCode(1_040_720_010, "发货通知单行不存在"); 640 ErrorCode WM_SALES_NOTICE_LINE_NOT_EXISTS = new ErrorCode(1_040_720_010, "发货通知单行不存在");
620 ErrorCode WM_SALES_NOTICE_LINE_EMPTY = new ErrorCode(1_040_720_011, "发货通知单行为空,不能提交"); 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,6 +63,15 @@ public interface MesDvCheckPlanService {
63 void validateCheckPlanExists(Long id); 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 * @param planId 方案编号 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,6 +149,15 @@ public class MesDvCheckPlanServiceImpl implements MesDvCheckPlanService {
149 return plan; 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 private void validateCheckPlanCodeUnique(Long id, String code) { 161 private void validateCheckPlanCodeUnique(Long id, String code) {
153 MesDvCheckPlanDO plan = checkPlanMapper.selectByCode(code); 162 MesDvCheckPlanDO plan = checkPlanMapper.selectByCode(code);
154 if (plan == null) { 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,6 +10,7 @@ import cn.iocoder.yudao.module.mes.dal.dataobject.dv.checkplan.MesDvCheckPlanSub
10 import cn.iocoder.yudao.module.mes.dal.dataobject.dv.checkrecord.MesDvCheckRecordDO; 10 import cn.iocoder.yudao.module.mes.dal.dataobject.dv.checkrecord.MesDvCheckRecordDO;
11 import cn.iocoder.yudao.module.mes.dal.dataobject.dv.checkrecord.MesDvCheckRecordLineDO; 11 import cn.iocoder.yudao.module.mes.dal.dataobject.dv.checkrecord.MesDvCheckRecordLineDO;
12 import cn.iocoder.yudao.module.mes.enums.dv.MesDvCheckRecordStatusEnum; 12 import cn.iocoder.yudao.module.mes.enums.dv.MesDvCheckRecordStatusEnum;
  13 +import cn.iocoder.yudao.module.mes.enums.dv.MesDvCheckPlanTypeEnum;
13 import cn.iocoder.yudao.module.mes.enums.dv.MesDvCheckResultEnum; 14 import cn.iocoder.yudao.module.mes.enums.dv.MesDvCheckResultEnum;
14 import cn.iocoder.yudao.module.mes.dal.mysql.dv.checkrecord.MesDvCheckRecordMapper; 15 import cn.iocoder.yudao.module.mes.dal.mysql.dv.checkrecord.MesDvCheckRecordMapper;
15 import cn.iocoder.yudao.module.mes.service.dv.checkplan.MesDvCheckPlanService; 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,6 +38,7 @@ import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
37 @Validated 38 @Validated
38 public class MesDvCheckRecordServiceImpl implements MesDvCheckRecordService { 39 public class MesDvCheckRecordServiceImpl implements MesDvCheckRecordService {
39 40
  41 +
40 @Resource 42 @Resource
41 private MesDvCheckRecordMapper checkRecordMapper; 43 private MesDvCheckRecordMapper checkRecordMapper;
42 @Resource 44 @Resource
@@ -162,7 +164,7 @@ public class MesDvCheckRecordServiceImpl implements MesDvCheckRecordService { @@ -162,7 +164,7 @@ public class MesDvCheckRecordServiceImpl implements MesDvCheckRecordService {
162 machineryService.validateMachineryExists(reqVO.getMachineryId()); 164 machineryService.validateMachineryExists(reqVO.getMachineryId());
163 // 校验点检计划是否存在 165 // 校验点检计划是否存在
164 if (reqVO.getPlanId() != null) { 166 if (reqVO.getPlanId() != null) {
165 - checkPlanService.validateCheckPlanExists(reqVO.getPlanId()); 167 + checkPlanService.validateCheckPlanExistsAndType(reqVO.getPlanId(), MesDvCheckPlanTypeEnum.CHECK.getType());
166 } 168 }
167 // 校验点检人是否存在 169 // 校验点检人是否存在
168 if (reqVO.getUserId() != null) { 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,6 +10,7 @@ import cn.iocoder.yudao.module.mes.dal.dataobject.dv.maintenrecord.MesDvMaintenR
10 import cn.iocoder.yudao.module.mes.dal.dataobject.dv.maintenrecord.MesDvMaintenRecordLineDO; 10 import cn.iocoder.yudao.module.mes.dal.dataobject.dv.maintenrecord.MesDvMaintenRecordLineDO;
11 import cn.iocoder.yudao.module.mes.dal.mysql.dv.maintenrecord.MesDvMaintenRecordMapper; 11 import cn.iocoder.yudao.module.mes.dal.mysql.dv.maintenrecord.MesDvMaintenRecordMapper;
12 import cn.iocoder.yudao.module.mes.enums.dv.MesDvMaintenRecordStatusEnum; 12 import cn.iocoder.yudao.module.mes.enums.dv.MesDvMaintenRecordStatusEnum;
  13 +import cn.iocoder.yudao.module.mes.enums.dv.MesDvCheckPlanTypeEnum;
13 import cn.iocoder.yudao.module.mes.service.dv.checkplan.MesDvCheckPlanService; 14 import cn.iocoder.yudao.module.mes.service.dv.checkplan.MesDvCheckPlanService;
14 import cn.iocoder.yudao.module.mes.service.dv.machinery.MesDvMachineryService; 15 import cn.iocoder.yudao.module.mes.service.dv.machinery.MesDvMachineryService;
15 import jakarta.annotation.Resource; 16 import jakarta.annotation.Resource;
@@ -100,7 +101,7 @@ public class MesDvMaintenRecordServiceImpl implements MesDvMaintenRecordService @@ -100,7 +101,7 @@ public class MesDvMaintenRecordServiceImpl implements MesDvMaintenRecordService
100 machineryService.validateMachineryExists(reqVO.getMachineryId()); 101 machineryService.validateMachineryExists(reqVO.getMachineryId());
101 // 校验保养计划是否存在 102 // 校验保养计划是否存在
102 if (reqVO.getPlanId() != null) { 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,6 +7,7 @@ import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
7 import cn.iocoder.yudao.module.mes.controller.admin.pro.card.vo.MesProCardPageReqVO; 7 import cn.iocoder.yudao.module.mes.controller.admin.pro.card.vo.MesProCardPageReqVO;
8 import cn.iocoder.yudao.module.mes.controller.admin.pro.card.vo.MesProCardSaveReqVO; 8 import cn.iocoder.yudao.module.mes.controller.admin.pro.card.vo.MesProCardSaveReqVO;
9 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.card.MesProCardDO; 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 import cn.iocoder.yudao.module.mes.dal.mysql.pro.card.MesProCardMapper; 11 import cn.iocoder.yudao.module.mes.dal.mysql.pro.card.MesProCardMapper;
11 import cn.iocoder.yudao.module.mes.enums.pro.MesProWorkOrderStatusEnum; 12 import cn.iocoder.yudao.module.mes.enums.pro.MesProWorkOrderStatusEnum;
12 import cn.iocoder.yudao.module.mes.enums.wm.BarcodeBizTypeEnum; 13 import cn.iocoder.yudao.module.mes.enums.wm.BarcodeBizTypeEnum;
@@ -171,9 +172,12 @@ public class MesProCardServiceImpl implements MesProCardService { @@ -171,9 +172,12 @@ public class MesProCardServiceImpl implements MesProCardService {
171 // 校验编码唯一 172 // 校验编码唯一
172 validateCardCodeUnique(reqVO.getId(), reqVO.getCode()); 173 validateCardCodeUnique(reqVO.getId(), reqVO.getCode());
173 // 校验工单存在 174 // 校验工单存在
174 - workOrderService.validateWorkOrderExists(reqVO.getWorkOrderId()); 175 + MesProWorkOrderDO workOrder = workOrderService.validateWorkOrderConfirmed(reqVO.getWorkOrderId());
175 // 校验物料存在 176 // 校验物料存在
176 itemService.validateItemExists(reqVO.getItemId()); 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,6 +46,14 @@ public interface MesProFeedbackService {
46 MesProFeedbackDO getFeedback(Long id); 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 * @param pageReqVO 分页查询 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,11 +4,13 @@ import cn.hutool.core.util.ObjUtil;
4 import cn.hutool.core.util.ObjectUtil; 4 import cn.hutool.core.util.ObjectUtil;
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  7 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkstationDO;
7 import cn.iocoder.yudao.module.mes.controller.admin.pro.feedback.vo.MesProFeedbackPageReqVO; 8 import cn.iocoder.yudao.module.mes.controller.admin.pro.feedback.vo.MesProFeedbackPageReqVO;
8 import cn.iocoder.yudao.module.mes.controller.admin.pro.feedback.vo.MesProFeedbackSaveReqVO; 9 import cn.iocoder.yudao.module.mes.controller.admin.pro.feedback.vo.MesProFeedbackSaveReqVO;
9 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO; 10 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO;
10 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessDO; 11 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessDO;
11 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO; 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 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemconsume.MesWmItemConsumeDO; 14 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemconsume.MesWmItemConsumeDO;
13 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceDO; 15 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceDO;
14 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceLineDO; 16 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceLineDO;
@@ -219,7 +221,8 @@ public class MesProFeedbackServiceImpl implements MesProFeedbackService { @@ -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 MesProFeedbackDO feedback = feedbackMapper.selectById(id); 226 MesProFeedbackDO feedback = feedbackMapper.selectById(id);
224 if (feedback == null) { 227 if (feedback == null) {
225 throw exception(PRO_FEEDBACK_NOT_EXISTS); 228 throw exception(PRO_FEEDBACK_NOT_EXISTS);
@@ -251,7 +254,10 @@ public class MesProFeedbackServiceImpl implements MesProFeedbackService { @@ -251,7 +254,10 @@ public class MesProFeedbackServiceImpl implements MesProFeedbackService {
251 */ 254 */
252 private MesProTaskDO validateFeedbackData(MesProFeedbackSaveReqVO reqVO) { 255 private MesProTaskDO validateFeedbackData(MesProFeedbackSaveReqVO reqVO) {
253 // 1. 校验工作站存在 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 // 2.1 校验工艺路线 + 工序配置有效 262 // 2.1 校验工艺路线 + 工序配置有效
257 MesProRouteProcessDO routeProcess = routeProcessService.getRouteProcessByRouteIdAndProcessId( 263 MesProRouteProcessDO routeProcess = routeProcessService.getRouteProcessByRouteIdAndProcessId(
@@ -277,10 +283,32 @@ public class MesProFeedbackServiceImpl implements MesProFeedbackService { @@ -277,10 +283,32 @@ public class MesProFeedbackServiceImpl implements MesProFeedbackService {
277 } 283 }
278 284
279 // 3. 校验工单已确认 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 // 4. 校验任务存在且未终态(已完成/已取消),并返回任务用于冗余 itemId 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 @Override 314 @Override
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProductBomServiceImpl.java
1 package cn.iocoder.yudao.module.mes.service.pro.route; 1 package cn.iocoder.yudao.module.mes.service.pro.route;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
3 import cn.hutool.core.util.ObjUtil; 4 import cn.hutool.core.util.ObjUtil;
4 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
5 import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.productbom.MesProRouteProductBomSaveReqVO; 6 import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.productbom.MesProRouteProductBomSaveReqVO;
6 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductBomDO; 7 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductBomDO;
7 import cn.iocoder.yudao.module.mes.dal.mysql.pro.route.MesProRouteProductBomMapper; 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 import jakarta.annotation.Resource; 11 import jakarta.annotation.Resource;
9 import org.springframework.context.annotation.Lazy; 12 import org.springframework.context.annotation.Lazy;
10 import org.springframework.stereotype.Service; 13 import org.springframework.stereotype.Service;
@@ -30,13 +33,16 @@ public class MesProRouteProductBomServiceImpl implements MesProRouteProductBomSe @@ -30,13 +33,16 @@ public class MesProRouteProductBomServiceImpl implements MesProRouteProductBomSe
30 @Resource 33 @Resource
31 @Lazy 34 @Lazy
32 private MesProRouteService routeService; 35 private MesProRouteService routeService;
  36 + @Resource
  37 + private MesMdItemService itemService;
  38 + @Resource
  39 + private MesMdProductBomService productBomService;
33 40
34 @Override 41 @Override
35 public Long createRouteProductBom(MesProRouteProductBomSaveReqVO createReqVO) { 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 // 2. 插入 46 // 2. 插入
41 MesProRouteProductBomDO routeProductBom = BeanUtils.toBean(createReqVO, MesProRouteProductBomDO.class); 47 MesProRouteProductBomDO routeProductBom = BeanUtils.toBean(createReqVO, MesProRouteProductBomDO.class);
42 routeProductBomMapper.insert(routeProductBom); 48 routeProductBomMapper.insert(routeProductBom);
@@ -45,13 +51,11 @@ public class MesProRouteProductBomServiceImpl implements MesProRouteProductBomSe @@ -45,13 +51,11 @@ public class MesProRouteProductBomServiceImpl implements MesProRouteProductBomSe
45 51
46 @Override 52 @Override
47 public void updateRouteProductBom(MesProRouteProductBomSaveReqVO updateReqVO) { 53 public void updateRouteProductBom(MesProRouteProductBomSaveReqVO updateReqVO) {
48 - // 1.0 已启用的工艺路线,不允许操作  
49 - routeService.validateRouteNotEnable(updateReqVO.getRouteId());  
50 - // 1.1 校验存在 54 + // 1. 校验存在 + 校验数据
51 validateRouteProductBomExists(updateReqVO.getId()); 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 MesProRouteProductBomDO updateObj = BeanUtils.toBean(updateReqVO, MesProRouteProductBomDO.class); 59 MesProRouteProductBomDO updateObj = BeanUtils.toBean(updateReqVO, MesProRouteProductBomDO.class);
56 routeProductBomMapper.updateById(updateObj); 60 routeProductBomMapper.updateById(updateObj);
57 } 61 }
@@ -75,6 +79,21 @@ public class MesProRouteProductBomServiceImpl implements MesProRouteProductBomSe @@ -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 private void validateBomUnique(Long id, Long itemId, Long processId, Long productId) { 97 private void validateBomUnique(Long id, Long itemId, Long processId, Long productId) {
79 MesProRouteProductBomDO existing = routeProductBomMapper.selectByUnique(itemId, processId, productId); 98 MesProRouteProductBomDO existing = routeProductBomMapper.selectByUnique(itemId, processId, productId);
80 if (existing == null) { 99 if (existing == null) {
@@ -85,6 +104,14 @@ public class MesProRouteProductBomServiceImpl implements MesProRouteProductBomSe @@ -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 @Override 115 @Override
89 public MesProRouteProductBomDO getRouteProductBom(Long id) { 116 public MesProRouteProductBomDO getRouteProductBom(Long id) {
90 return routeProductBomMapper.selectById(id); 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,6 +9,7 @@ import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.bom.MesProW
9 import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO; 9 import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
10 import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdProductBomDO; 10 import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdProductBomDO;
11 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderBomDO; 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 import cn.iocoder.yudao.module.mes.dal.mysql.pro.workorder.MesProWorkOrderBomMapper; 13 import cn.iocoder.yudao.module.mes.dal.mysql.pro.workorder.MesProWorkOrderBomMapper;
13 import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; 14 import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
14 import cn.iocoder.yudao.module.mes.service.md.item.MesMdProductBomService; 15 import cn.iocoder.yudao.module.mes.service.md.item.MesMdProductBomService;
@@ -25,6 +26,7 @@ import java.util.Set; @@ -25,6 +26,7 @@ import java.util.Set;
25 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 26 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
26 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; 27 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
27 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.MD_ITEM_NOT_EXISTS; 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 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_WORK_ORDER_BOM_NOT_EXISTS; 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,8 +51,8 @@ public class MesProWorkOrderBomServiceImpl implements MesProWorkOrderBomService
49 51
50 @Override 52 @Override
51 public Long createWorkOrderBom(MesProWorkOrderBomSaveReqVO createReqVO) { 53 public Long createWorkOrderBom(MesProWorkOrderBomSaveReqVO createReqVO) {
52 - // 校验工单存在  
53 - workOrderService.validateWorkOrderExists(createReqVO.getWorkOrderId()); 54 + // 校验数据
  55 + validateWorkOrderBomSaveData(createReqVO);
54 56
55 // 插入数据 57 // 插入数据
56 MesProWorkOrderBomDO workOrderBom = BeanUtils.toBean(createReqVO, MesProWorkOrderBomDO.class); 58 MesProWorkOrderBomDO workOrderBom = BeanUtils.toBean(createReqVO, MesProWorkOrderBomDO.class);
@@ -62,6 +64,8 @@ public class MesProWorkOrderBomServiceImpl implements MesProWorkOrderBomService @@ -62,6 +64,8 @@ public class MesProWorkOrderBomServiceImpl implements MesProWorkOrderBomService
62 public void updateWorkOrderBom(MesProWorkOrderBomSaveReqVO updateReqVO) { 64 public void updateWorkOrderBom(MesProWorkOrderBomSaveReqVO updateReqVO) {
63 // 校验存在 65 // 校验存在
64 validateWorkOrderBomExists(updateReqVO.getId()); 66 validateWorkOrderBomExists(updateReqVO.getId());
  67 + // 校验数据
  68 + validateWorkOrderBomSaveData(updateReqVO);
65 69
66 // 更新数据 70 // 更新数据
67 MesProWorkOrderBomDO updateObj = BeanUtils.toBean(updateReqVO, MesProWorkOrderBomDO.class); 71 MesProWorkOrderBomDO updateObj = BeanUtils.toBean(updateReqVO, MesProWorkOrderBomDO.class);
@@ -83,6 +87,19 @@ public class MesProWorkOrderBomServiceImpl implements MesProWorkOrderBomService @@ -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 @Override 103 @Override
87 public MesProWorkOrderBomDO getWorkOrderBom(Long id) { 104 public MesProWorkOrderBomDO getWorkOrderBom(Long id) {
88 return workOrderBomMapper.selectById(id); 105 return workOrderBomMapper.selectById(id);
@@ -133,4 +150,4 @@ public class MesProWorkOrderBomServiceImpl implements MesProWorkOrderBomService @@ -133,4 +150,4 @@ public class MesProWorkOrderBomServiceImpl implements MesProWorkOrderBomService
133 workOrderBomMapper.insertBatch(bomList); 150 workOrderBomMapper.insertBatch(bomList);
134 } 151 }
135 152
136 -}  
137 \ No newline at end of file 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,6 +8,7 @@ import cn.iocoder.yudao.module.mes.controller.admin.qc.ipqc.vo.MesQcIpqcPageReqV
8 import cn.iocoder.yudao.module.mes.controller.admin.qc.ipqc.vo.MesQcIpqcSaveReqVO; 8 import cn.iocoder.yudao.module.mes.controller.admin.qc.ipqc.vo.MesQcIpqcSaveReqVO;
9 import cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkstationDO; 9 import cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkstationDO;
10 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductDO; 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 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO; 12 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
12 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO; 13 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO;
13 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.defectrecord.MesQcDefectRecordDO; 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,6 +24,7 @@ import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationServic
23 import cn.iocoder.yudao.module.mes.service.pro.feedback.MesProFeedbackService; 24 import cn.iocoder.yudao.module.mes.service.pro.feedback.MesProFeedbackService;
24 import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProcessService; 25 import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProcessService;
25 import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductService; 26 import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductService;
  27 +import cn.iocoder.yudao.module.mes.service.pro.task.MesProTaskService;
26 import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService; 28 import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService;
27 import cn.iocoder.yudao.module.mes.service.qc.defectrecord.MesQcDefectRecordService; 29 import cn.iocoder.yudao.module.mes.service.qc.defectrecord.MesQcDefectRecordService;
28 import cn.iocoder.yudao.module.mes.service.qc.template.MesQcTemplateItemService; 30 import cn.iocoder.yudao.module.mes.service.qc.template.MesQcTemplateItemService;
@@ -76,6 +78,9 @@ public class MesQcIpqcServiceImpl implements MesQcIpqcService { @@ -76,6 +78,9 @@ public class MesQcIpqcServiceImpl implements MesQcIpqcService {
76 @Resource 78 @Resource
77 @Lazy 79 @Lazy
78 private MesProRouteProcessService routeProcessService; 80 private MesProRouteProcessService routeProcessService;
  81 + @Resource
  82 + @Lazy
  83 + private MesProTaskService taskService;
79 84
80 @Resource 85 @Resource
81 private AdminUserApi adminUserApi; 86 private AdminUserApi adminUserApi;
@@ -136,9 +141,39 @@ public class MesQcIpqcServiceImpl implements MesQcIpqcService { @@ -136,9 +141,39 @@ public class MesQcIpqcServiceImpl implements MesQcIpqcService {
136 // 校验工位、检测人员存在 141 // 校验工位、检测人员存在
137 MesMdWorkstationDO workstation = workstationService.validateWorkstationExists(reqVO.getWorkstationId()); 142 MesMdWorkstationDO workstation = workstationService.validateWorkstationExists(reqVO.getWorkstationId());
138 adminUserApi.validateUser(reqVO.getInspectorUserId()); 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 return workstation; 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 @Override 177 @Override
143 @Transactional(rollbackFor = Exception.class) 178 @Transactional(rollbackFor = Exception.class)
144 public void finishIpqc(Long id) { 179 public void finishIpqc(Long id) {
@@ -238,8 +273,8 @@ public class MesQcIpqcServiceImpl implements MesQcIpqcService { @@ -238,8 +273,8 @@ public class MesQcIpqcServiceImpl implements MesQcIpqcService {
238 return null; 273 return null;
239 } 274 }
240 if (Objects.equals(sourceDocType, MesBizTypeConstants.PRO_FEEDBACK)) { 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 return null; 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,11 +8,13 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
8 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; 8 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
9 import cn.iocoder.yudao.module.mes.controller.admin.wm.itemreceipt.vo.MesWmItemReceiptPageReqVO; 9 import cn.iocoder.yudao.module.mes.controller.admin.wm.itemreceipt.vo.MesWmItemReceiptPageReqVO;
10 import cn.iocoder.yudao.module.mes.controller.admin.wm.itemreceipt.vo.MesWmItemReceiptSaveReqVO; 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 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemreceipt.MesWmItemReceiptDO; 12 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemreceipt.MesWmItemReceiptDO;
12 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemreceipt.MesWmItemReceiptDetailDO; 13 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemreceipt.MesWmItemReceiptDetailDO;
13 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemreceipt.MesWmItemReceiptLineDO; 14 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemreceipt.MesWmItemReceiptLineDO;
14 import cn.iocoder.yudao.module.mes.dal.mysql.wm.itemreceipt.MesWmItemReceiptMapper; 15 import cn.iocoder.yudao.module.mes.dal.mysql.wm.itemreceipt.MesWmItemReceiptMapper;
15 import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants; 16 import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
  17 +import cn.iocoder.yudao.module.mes.enums.wm.MesWmArrivalNoticeStatusEnum;
16 import cn.iocoder.yudao.module.mes.enums.wm.MesWmItemReceiptStatusEnum; 18 import cn.iocoder.yudao.module.mes.enums.wm.MesWmItemReceiptStatusEnum;
17 import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum; 19 import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum;
18 import cn.iocoder.yudao.module.mes.service.md.vendor.MesMdVendorService; 20 import cn.iocoder.yudao.module.mes.service.md.vendor.MesMdVendorService;
@@ -90,7 +92,13 @@ public class MesWmItemReceiptServiceImpl implements MesWmItemReceiptService { @@ -90,7 +92,13 @@ public class MesWmItemReceiptServiceImpl implements MesWmItemReceiptService {
90 vendorService.validateVendorExists(reqVO.getVendorId()); 92 vendorService.validateVendorExists(reqVO.getVendorId());
91 // 校验到货通知单存在 93 // 校验到货通知单存在
92 if (reqVO.getNoticeId() != null) { 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 if (reqVO.getIqcId() != null) { 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,4 +132,20 @@ public interface MesWmMaterialStockService {
132 */ 132 */
133 void checkAreaMixingRule(Long areaId, Long itemId, Long batchId); 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,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 package cn.iocoder.yudao.module.mes.service.wm.outsourceissue; 1 package cn.iocoder.yudao.module.mes.service.wm.outsourceissue;
2 2
  3 +import cn.hutool.core.util.ObjUtil;
3 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 4 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
4 import cn.iocoder.yudao.module.mes.controller.admin.wm.outsourceissue.vo.detail.MesWmOutsourceIssueDetailSaveReqVO; 5 import cn.iocoder.yudao.module.mes.controller.admin.wm.outsourceissue.vo.detail.MesWmOutsourceIssueDetailSaveReqVO;
5 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.outsourceissue.MesWmOutsourceIssueDetailDO; 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 import cn.iocoder.yudao.module.mes.dal.mysql.wm.outsourceissue.MesWmOutsourceIssueDetailMapper; 8 import cn.iocoder.yudao.module.mes.dal.mysql.wm.outsourceissue.MesWmOutsourceIssueDetailMapper;
7 import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; 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 import jakarta.annotation.Resource; 12 import jakarta.annotation.Resource;
9 import org.springframework.context.annotation.Lazy; 13 import org.springframework.context.annotation.Lazy;
10 import org.springframework.stereotype.Service; 14 import org.springframework.stereotype.Service;
@@ -13,7 +17,7 @@ import org.springframework.validation.annotation.Validated; @@ -13,7 +17,7 @@ import org.springframework.validation.annotation.Validated;
13 import java.util.List; 17 import java.util.List;
14 18
15 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 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 * MES 外协发料单明细 Service 实现类 23 * MES 外协发料单明细 Service 实现类
@@ -28,9 +32,13 @@ public class MesWmOutsourceIssueDetailServiceImpl implements MesWmOutsourceIssue @@ -28,9 +32,13 @@ public class MesWmOutsourceIssueDetailServiceImpl implements MesWmOutsourceIssue
28 private MesWmOutsourceIssueDetailMapper outsourceIssueDetailMapper; 32 private MesWmOutsourceIssueDetailMapper outsourceIssueDetailMapper;
29 @Resource 33 @Resource
30 @Lazy 34 @Lazy
31 - private MesWmOutsourceIssueService outsourceIssueService; 35 + private MesWmOutsourceIssueLineService outsourceIssueLineService;
32 @Resource 36 @Resource
33 private MesMdItemService itemService; 37 private MesMdItemService itemService;
  38 + @Resource
  39 + private MesWmWarehouseAreaService warehouseAreaService;
  40 + @Resource
  41 + private MesWmMaterialStockService materialStockService;
34 42
35 @Override 43 @Override
36 public Long createOutsourceIssueDetail(MesWmOutsourceIssueDetailSaveReqVO createReqVO) { 44 public Long createOutsourceIssueDetail(MesWmOutsourceIssueDetailSaveReqVO createReqVO) {
@@ -95,10 +103,26 @@ public class MesWmOutsourceIssueDetailServiceImpl implements MesWmOutsourceIssue @@ -95,10 +103,26 @@ public class MesWmOutsourceIssueDetailServiceImpl implements MesWmOutsourceIssue
95 } 103 }
96 104
97 private void validateOutsourceIssueDetailSaveData(MesWmOutsourceIssueDetailSaveReqVO saveReqVO) { 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 itemService.validateItemExists(saveReqVO.getItemId()); 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,6 +9,8 @@ import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productissue.MesWmProductIs
9 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productissue.MesWmProductIssueLineDO; 9 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productissue.MesWmProductIssueLineDO;
10 import cn.iocoder.yudao.module.mes.dal.mysql.wm.productissue.MesWmProductIssueDetailMapper; 10 import cn.iocoder.yudao.module.mes.dal.mysql.wm.productissue.MesWmProductIssueDetailMapper;
11 import cn.iocoder.yudao.module.mes.enums.wm.MesWmProductIssueStatusEnum; 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 import jakarta.annotation.Resource; 14 import jakarta.annotation.Resource;
13 import org.springframework.context.annotation.Lazy; 15 import org.springframework.context.annotation.Lazy;
14 import org.springframework.stereotype.Service; 16 import org.springframework.stereotype.Service;
@@ -35,6 +37,10 @@ public class MesWmProductIssueDetailServiceImpl implements MesWmProductIssueDeta @@ -35,6 +37,10 @@ public class MesWmProductIssueDetailServiceImpl implements MesWmProductIssueDeta
35 @Resource 37 @Resource
36 @Lazy 38 @Lazy
37 private MesWmProductIssueLineService issueLineService; 39 private MesWmProductIssueLineService issueLineService;
  40 + @Resource
  41 + private MesWmWarehouseAreaService warehouseAreaService;
  42 + @Resource
  43 + private MesWmMaterialStockService materialStockService;
38 44
39 @Override 45 @Override
40 public Long createProductIssueDetail(MesWmProductIssueDetailSaveReqVO createReqVO) { 46 public Long createProductIssueDetail(MesWmProductIssueDetailSaveReqVO createReqVO) {
@@ -116,8 +122,18 @@ public class MesWmProductIssueDetailServiceImpl implements MesWmProductIssueDeta @@ -116,8 +122,18 @@ public class MesWmProductIssueDetailServiceImpl implements MesWmProductIssueDeta
116 private void validateProductIssueDetailSaveData(MesWmProductIssueDetailSaveReqVO reqVO) { 122 private void validateProductIssueDetailSaveData(MesWmProductIssueDetailSaveReqVO reqVO) {
117 // 校验父数据存在 123 // 校验父数据存在
118 MesWmProductIssueLineDO line = issueLineService.validateProductIssueLineExists(reqVO.getLineId()); 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 // 校验物料匹配(明细 itemId 必须与行 itemId 一致) 128 // 校验物料匹配(明细 itemId 必须与行 itemId 一致)
120 validateDetailItemMatch(reqVO.getItemId(), line.getItemId()); 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,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 package cn.iocoder.yudao.module.mes.service.wm.productsales; 1 package cn.iocoder.yudao.module.mes.service.wm.productsales;
2 2
  3 +import cn.hutool.core.util.ObjUtil;
3 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 4 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
4 import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.detail.MesWmProductSalesDetailSaveReqVO; 5 import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.detail.MesWmProductSalesDetailSaveReqVO;
5 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDetailDO; 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 import cn.iocoder.yudao.module.mes.dal.mysql.wm.productsales.MesWmProductSalesDetailMapper; 8 import cn.iocoder.yudao.module.mes.dal.mysql.wm.productsales.MesWmProductSalesDetailMapper;
7 import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; 9 import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
  10 +import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService;
8 import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService; 11 import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService;
9 import jakarta.annotation.Resource; 12 import jakarta.annotation.Resource;
10 import org.springframework.stereotype.Service; 13 import org.springframework.stereotype.Service;
@@ -29,17 +32,17 @@ public class MesWmProductSalesDetailServiceImpl implements MesWmProductSalesDeta @@ -29,17 +32,17 @@ public class MesWmProductSalesDetailServiceImpl implements MesWmProductSalesDeta
29 32
30 @Resource 33 @Resource
31 private MesMdItemService itemService; 34 private MesMdItemService itemService;
32 - 35 + @Resource
  36 + private MesWmProductSalesLineService productSalesLineService;
  37 + @Resource
  38 + private MesWmMaterialStockService materialStockService;
33 @Resource 39 @Resource
34 private MesWmWarehouseAreaService warehouseAreaService; 40 private MesWmWarehouseAreaService warehouseAreaService;
35 41
36 @Override 42 @Override
37 public Long createProductSalesDetail(MesWmProductSalesDetailSaveReqVO createReqVO) { 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 MesWmProductSalesDetailDO detail = BeanUtils.toBean(createReqVO, MesWmProductSalesDetailDO.class); 48 MesWmProductSalesDetailDO detail = BeanUtils.toBean(createReqVO, MesWmProductSalesDetailDO.class);
@@ -51,11 +54,8 @@ public class MesWmProductSalesDetailServiceImpl implements MesWmProductSalesDeta @@ -51,11 +54,8 @@ public class MesWmProductSalesDetailServiceImpl implements MesWmProductSalesDeta
51 public void updateProductSalesDetail(MesWmProductSalesDetailSaveReqVO updateReqVO) { 54 public void updateProductSalesDetail(MesWmProductSalesDetailSaveReqVO updateReqVO) {
52 // 校验存在 55 // 校验存在
53 validateProductSalesDetailExists(updateReqVO.getId()); 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 MesWmProductSalesDetailDO updateObj = BeanUtils.toBean(updateReqVO, MesWmProductSalesDetailDO.class); 61 MesWmProductSalesDetailDO updateObj = BeanUtils.toBean(updateReqVO, MesWmProductSalesDetailDO.class);
@@ -103,4 +103,27 @@ public class MesWmProductSalesDetailServiceImpl implements MesWmProductSalesDeta @@ -103,4 +103,27 @@ public class MesWmProductSalesDetailServiceImpl implements MesWmProductSalesDeta
103 return detail; 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,6 +9,7 @@ import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
9 import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesPageReqVO; 9 import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesPageReqVO;
10 import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesSaveReqVO; 10 import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesSaveReqVO;
11 import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesShippingReqVO; 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 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDO; 13 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDO;
13 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDetailDO; 14 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDetailDO;
14 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesLineDO; 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,8 +17,10 @@ import cn.iocoder.yudao.module.mes.dal.mysql.wm.productsales.MesWmProductSalesMa
16 import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants; 17 import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
17 import cn.iocoder.yudao.module.mes.enums.wm.MesWmProductSalesStatusEnum; 18 import cn.iocoder.yudao.module.mes.enums.wm.MesWmProductSalesStatusEnum;
18 import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum; 19 import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum;
  20 +import cn.iocoder.yudao.module.mes.enums.wm.MesWmSalesNoticeStatusEnum;
19 import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum; 21 import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum;
20 import cn.iocoder.yudao.module.mes.service.md.client.MesMdClientService; 22 import cn.iocoder.yudao.module.mes.service.md.client.MesMdClientService;
  23 +import cn.iocoder.yudao.module.mes.service.wm.salesnotice.MesWmSalesNoticeService;
21 import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService; 24 import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService;
22 import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO; 25 import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO;
23 import jakarta.annotation.Resource; 26 import jakarta.annotation.Resource;
@@ -54,6 +57,9 @@ public class MesWmProductSalesServiceImpl implements MesWmProductSalesService { @@ -54,6 +57,9 @@ public class MesWmProductSalesServiceImpl implements MesWmProductSalesService {
54 private MesMdClientService clientService; 57 private MesMdClientService clientService;
55 58
56 @Resource 59 @Resource
  60 + private MesWmSalesNoticeService salesNoticeService;
  61 +
  62 + @Resource
57 private MesWmTransactionService wmTransactionService; 63 private MesWmTransactionService wmTransactionService;
58 64
59 @Override 65 @Override
@@ -262,6 +268,16 @@ public class MesWmProductSalesServiceImpl implements MesWmProductSalesService { @@ -262,6 +268,16 @@ public class MesWmProductSalesServiceImpl implements MesWmProductSalesService {
262 validateCodeUnique(id, reqVO.getCode()); 268 validateCodeUnique(id, reqVO.getCode());
263 // 校验客户存在 269 // 校验客户存在
264 clientService.validateClientExists(reqVO.getClientId()); 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 private MesWmProductSalesDO validateProductSalesExists(Long id) { 283 private MesWmProductSalesDO validateProductSalesExists(Long id) {
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueDetailServiceImpl.java
1 package cn.iocoder.yudao.module.mes.service.wm.returnissue; 1 package cn.iocoder.yudao.module.mes.service.wm.returnissue;
2 2
  3 +import cn.hutool.core.util.ObjUtil;
3 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; 4 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
4 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
5 import cn.iocoder.yudao.module.mes.controller.admin.wm.returnissue.vo.detail.MesWmReturnIssueDetailSaveReqVO; 6 import cn.iocoder.yudao.module.mes.controller.admin.wm.returnissue.vo.detail.MesWmReturnIssueDetailSaveReqVO;
@@ -17,8 +18,7 @@ import java.math.BigDecimal; @@ -17,8 +18,7 @@ import java.math.BigDecimal;
17 import java.util.List; 18 import java.util.List;
18 19
19 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 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 * MES 生产退料明细 Service 实现类 24 * MES 生产退料明细 Service 实现类
@@ -105,11 +105,21 @@ public class MesWmReturnIssueDetailServiceImpl implements MesWmReturnIssueDetail @@ -105,11 +105,21 @@ public class MesWmReturnIssueDetailServiceImpl implements MesWmReturnIssueDetail
105 private void validateReturnIssueDetailSaveData(MesWmReturnIssueDetailSaveReqVO reqVO) { 105 private void validateReturnIssueDetailSaveData(MesWmReturnIssueDetailSaveReqVO reqVO) {
106 // 校验父数据存在 106 // 校验父数据存在
107 MesWmReturnIssueLineDO line = issueLineService.validateReturnIssueLineExists(reqVO.getLineId()); 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 warehouseAreaService.validateWarehouseAreaExists( 115 warehouseAreaService.validateWarehouseAreaExists(
110 reqVO.getWarehouseId(), reqVO.getLocationId(), reqVO.getAreaId()); 116 reqVO.getWarehouseId(), reqVO.getLocationId(), reqVO.getAreaId());
111 // 校验库位物料/批次混放规则 117 // 校验库位物料/批次混放规则
112 materialStockService.checkAreaMixingRule(reqVO.getAreaId(), reqVO.getItemId(), reqVO.getBatchId()); 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 validateDetailQuantityNotExceed(reqVO.getLineId(), reqVO.getQuantity(), reqVO.getId(), line); 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,12 +3,12 @@ package cn.iocoder.yudao.module.mes.service.wm.returnvendor;
3 import cn.hutool.core.util.ObjUtil; 3 import cn.hutool.core.util.ObjUtil;
4 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 4 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
5 import cn.iocoder.yudao.module.mes.controller.admin.wm.returnvendor.vo.detail.MesWmReturnVendorDetailSaveReqVO; 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 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnvendor.MesWmReturnVendorDetailDO; 6 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnvendor.MesWmReturnVendorDetailDO;
8 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnvendor.MesWmReturnVendorLineDO; 7 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnvendor.MesWmReturnVendorLineDO;
9 import cn.iocoder.yudao.module.mes.dal.mysql.wm.returnvendor.MesWmReturnVendorDetailMapper; 8 import cn.iocoder.yudao.module.mes.dal.mysql.wm.returnvendor.MesWmReturnVendorDetailMapper;
10 import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; 9 import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
11 import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService; 10 import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService;
  11 +import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService;
12 import jakarta.annotation.Resource; 12 import jakarta.annotation.Resource;
13 import org.springframework.context.annotation.Lazy; 13 import org.springframework.context.annotation.Lazy;
14 import org.springframework.stereotype.Service; 14 import org.springframework.stereotype.Service;
@@ -32,12 +32,12 @@ public class MesWmReturnVendorDetailServiceImpl implements MesWmReturnVendorDeta @@ -32,12 +32,12 @@ public class MesWmReturnVendorDetailServiceImpl implements MesWmReturnVendorDeta
32 @Resource 32 @Resource
33 @Lazy 33 @Lazy
34 private MesWmReturnVendorLineService returnVendorLineService; 34 private MesWmReturnVendorLineService returnVendorLineService;
35 -  
36 @Resource 35 @Resource
37 private MesMdItemService itemService; 36 private MesMdItemService itemService;
38 -  
39 @Resource 37 @Resource
40 private MesWmMaterialStockService materialStockService; 38 private MesWmMaterialStockService materialStockService;
  39 + @Resource
  40 + private MesWmWarehouseAreaService warehouseAreaService;
41 41
42 @Override 42 @Override
43 public Long createReturnVendorDetail(MesWmReturnVendorDetailSaveReqVO createReqVO) { 43 public Long createReturnVendorDetail(MesWmReturnVendorDetailSaveReqVO createReqVO) {
@@ -68,12 +68,18 @@ public class MesWmReturnVendorDetailServiceImpl implements MesWmReturnVendorDeta @@ -68,12 +68,18 @@ public class MesWmReturnVendorDetailServiceImpl implements MesWmReturnVendorDeta
68 private void validateDetailSaveData(MesWmReturnVendorDetailSaveReqVO reqVO) { 68 private void validateDetailSaveData(MesWmReturnVendorDetailSaveReqVO reqVO) {
69 // 校验父数据(行)存在 69 // 校验父数据(行)存在
70 MesWmReturnVendorLineDO line = returnVendorLineService.validateReturnVendorLineExists(reqVO.getLineId()); 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 itemService.validateItemExists(reqVO.getItemId()); 75 itemService.validateItemExists(reqVO.getItemId());
73 // 校验物料与行物料一致 76 // 校验物料与行物料一致
74 validateItemConsistency(reqVO.getItemId(), line); 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 @Override 85 @Override
@@ -127,15 +133,11 @@ public class MesWmReturnVendorDetailServiceImpl implements MesWmReturnVendorDeta @@ -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 throw exception(WM_RETURN_VENDOR_DETAIL_ITEM_MISMATCH); 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,6 +40,14 @@ public interface MesWmSalesNoticeService {
40 void deleteSalesNotice(Long id); 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 * @param id 编号 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,7 +111,8 @@ public class MesWmSalesNoticeServiceImpl implements MesWmSalesNoticeService {
111 return salesNoticeMapper.selectByIds(ids); 111 return salesNoticeMapper.selectByIds(ids);
112 } 112 }
113 113
114 - private MesWmSalesNoticeDO validateSalesNoticeExists(Long id) { 114 + @Override
  115 + public MesWmSalesNoticeDO validateSalesNoticeExists(Long id) {
115 MesWmSalesNoticeDO notice = salesNoticeMapper.selectById(id); 116 MesWmSalesNoticeDO notice = salesNoticeMapper.selectById(id);
116 if (notice == null) { 117 if (notice == null) {
117 throw exception(WM_SALES_NOTICE_NOT_EXISTS); 118 throw exception(WM_SALES_NOTICE_NOT_EXISTS);