From 74ad846852f42b4e2258bfddd5c2a33cc1ce9847 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期六, 29 八月 2026 18:03:46 +0800
Subject: [PATCH] feat(mes): 新增工序等待时长和报工数量配置功能
---
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productproduce/MesWmProductProduceServiceImpl.java | 8
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productreceipt/vo/line/MesWmProductReceiptLineRespVO.java | 6
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskServiceImpl.java | 24 +
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/MesProTaskController.java | 9
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productreceipt/MesWmProductReceiptController.java | 9
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/task/MesProTaskMapper.java | 8
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/process/vo/MesProProcessSaveReqVO.java | 8
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/process/vo/MesProProcessRespVO.java | 9
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/productreceipt/MesWmProductReceiptDO.java | 7
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/task/MesProTaskDO.java | 6
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/process/MesProRouteProcessRespVO.java | 7
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/productreceipt/MesWmProductReceiptMapper.java | 4
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/productreceipt/MesWmProductReceiptLineDO.java | 13
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/MesProRouteProcessController.java | 4
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productreceipt/vo/MesWmProductReceiptRespVO.java | 3
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/md/autocode/MesMdAutoCodeRuleCodeEnum.java | 3
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/warehouse/MesWmWarehouseDO.java | 8
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productreceipt/MesWmProductReceiptService.java | 22 +
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskService.java | 10
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskWaitRemindServiceImpl.java | 151 ++++++++++
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/job/pro/task/MesProTaskWaitRemindJob.java | 28 ++
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/vo/MesProTaskStartWaitCountdownReqVO.java | 15 +
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productreceipt/vo/MesWmProductReceiptSaveReqVO.java | 3
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/warehouse/MesWmWarehouseAreaDO.java | 5
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/feedback/MesProFeedbackServiceImpl.java | 98 +++++-
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java | 5
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/redis/RedisKeyConstants.java | 16 +
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productreceipt/MesWmProductReceiptServiceImpl.java | 157 +++++++++++
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/process/MesProProcessDO.java | 14 +
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/warehouse/MesWmWarehouseLocationDO.java | 5
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/redis/pro/task/MesProTaskWaitRemindRedisDAO.java | 54 +++
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/vo/MesProTaskRespVO.java | 3
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productreceipt/vo/MesWmProductReceiptStockReqVO.java | 60 ++++
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productreceipt/vo/line/MesWmProductReceiptLineSaveReqVO.java | 6
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskWaitRemindService.java | 15 +
35 files changed, 783 insertions(+), 20 deletions(-)
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/process/vo/MesProProcessRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/process/vo/MesProProcessRespVO.java
index 5cdd462..2a8e162 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/process/vo/MesProProcessRespVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/process/vo/MesProProcessRespVO.java
@@ -7,6 +7,7 @@
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
+import java.math.BigDecimal;
import java.time.LocalDateTime;
@Schema(description = "绠$悊鍚庡彴 - MES 鐢熶骇宸ュ簭 Response VO")
@@ -34,6 +35,14 @@
@Schema(description = "鐘舵��", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
private Integer status;
+ @ExcelProperty("绛夊緟鏃堕暱锛堝皬鏃讹級")
+ @Schema(description = "绛夊緟鏃堕暱锛堝皬鏃讹級锛屾姤宸ュ墠闇�鍊掕鏃剁瓑寰�", example = "2.00")
+ private BigDecimal waitDuration;
+
+ @ExcelProperty("鏄惁闇�瑕佸~鍐欐姤宸ユ暟閲�")
+ @Schema(description = "鏄惁闇�瑕佸~鍐欐姤宸ユ暟閲�", example = "true")
+ private Boolean needReportQuantity;
+
@ExcelProperty("澶囨敞")
@Schema(description = "澶囨敞", example = "閲戝睘鏉挎潗涓嬫枡")
private String remark;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/process/vo/MesProProcessSaveReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/process/vo/MesProProcessSaveReqVO.java
index 2abbd0c..1f511a6 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/process/vo/MesProProcessSaveReqVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/process/vo/MesProProcessSaveReqVO.java
@@ -5,6 +5,8 @@
import jakarta.validation.constraints.NotNull;
import lombok.Data;
+import java.math.BigDecimal;
+
@Schema(description = "绠$悊鍚庡彴 - MES 鐢熶骇宸ュ簭鏂板/淇敼 Request VO")
@Data
public class MesProProcessSaveReqVO {
@@ -27,6 +29,12 @@
@NotNull(message = "鐘舵�佷笉鑳戒负绌�")
private Integer status;
+ @Schema(description = "绛夊緟鏃堕暱锛堝皬鏃讹級锛屾姤宸ュ墠闇�鍊掕鏃剁瓑寰�", example = "2.00")
+ private BigDecimal waitDuration;
+
+ @Schema(description = "鏄惁闇�瑕佸~鍐欐姤宸ユ暟閲�", example = "true")
+ private Boolean needReportQuantity;
+
@Schema(description = "澶囨敞", example = "閲戝睘鏉挎潗涓嬫枡")
private String remark;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/MesProRouteProcessController.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/MesProRouteProcessController.java
index 1bc0813..19b56a8 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/MesProRouteProcessController.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/MesProRouteProcessController.java
@@ -166,7 +166,9 @@
for (int i = 0; i < result.size(); i++) {
MesProRouteProcessRespVO vo = result.get(i);
MapUtils.findAndThen(processMap, vo.getProcessId(),
- process -> vo.setProcessCode(process.getCode()).setProcessName(process.getName()));
+ process -> vo.setProcessCode(process.getCode()).setProcessName(process.getName())
+ .setWaitDuration(process.getWaitDuration())
+ .setNeedReportQuantity(process.getNeedReportQuantity()));
MapUtils.findAndThen(processMap, vo.getNextProcessId(),
nextProcess -> vo.setNextProcessName(nextProcess.getName()));
// 浜у嚭浜у搧锛堟敮鎸佸閫夛級
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/process/MesProRouteProcessRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/process/MesProRouteProcessRespVO.java
index 1966158..8bdfa4e 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/process/MesProRouteProcessRespVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/process/MesProRouteProcessRespVO.java
@@ -3,6 +3,7 @@
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
+import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
@@ -58,6 +59,12 @@
@Schema(description = "鏄惁鍊掑啿锛坱rue=鎶ュ伐鏃舵寜BOM鑷姩鎵f枡锛沠alse=涓嶈嚜鍔ㄦ墸鏂欙級", example = "true")
private Boolean backflushFlag;
+ @Schema(description = "绛夊緟鏃堕暱锛堝皬鏃讹級锛屾姤宸ュ墠闇�鍊掕鏃剁瓑寰�", example = "2.00")
+ private BigDecimal waitDuration;
+
+ @Schema(description = "鏄惁闇�瑕佸~鍐欐姤宸ユ暟閲忥紙false=鎶ュ伐鏁伴噺鍏佽涓虹┖锛�", example = "true")
+ private Boolean needReportQuantity;
+
@Schema(description = "浜у嚭浜у搧鐗╂枡缂栧彿锛堢涓�涓紝鍚戝悗鍏煎锛�", example = "100")
private Long outputItemId;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/MesProTaskController.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/MesProTaskController.java
index 7557506..f8a3a94 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/MesProTaskController.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/MesProTaskController.java
@@ -13,6 +13,7 @@
import cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskAutoGeneratePreviewRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskAutoGenerateReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskPauseReqVO;
+import cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskStartWaitCountdownReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskResumeReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskRespVO;
@@ -59,6 +60,7 @@
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
+import java.time.LocalDateTime;
import java.util.*;
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
@@ -151,6 +153,13 @@
return success(true);
}
+ @PostMapping("/start-wait-countdown")
+ @Operation(summary = "寮�濮嬫姤宸ュ�掕鏃�", description = "宸ュ簭閰嶇疆浜嗙瓑寰呮椂闀挎椂锛岀偣鍑诲紑濮嬪�掕鏃讹紝璁板綍鍊掕鏃跺紑濮嬫椂闂�")
+ @PreAuthorize("@ss.hasPermission('mes:pro-task:update')")
+ public CommonResult<LocalDateTime> startWaitCountdown(@Valid @RequestBody MesProTaskStartWaitCountdownReqVO reqVO) {
+ return success(taskService.startWaitCountdown(reqVO.getTaskId()));
+ }
+
@PostMapping("/auto-generate/preview")
@Operation(summary = "涓�閿帓浜ч瑙�")
@PreAuthorize("@ss.hasPermission('mes:pro-task:create')")
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/vo/MesProTaskRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/vo/MesProTaskRespVO.java
index f1c8e05..83507e2 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/vo/MesProTaskRespVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/vo/MesProTaskRespVO.java
@@ -92,6 +92,9 @@
@Schema(description = "璋冩暣鏁伴噺", example = "0")
private BigDecimal changedQuantity;
+ @Schema(description = "鎶ュ伐鍊掕鏃跺紑濮嬫椂闂�")
+ private LocalDateTime waitStartTime;
+
@Schema(description = "瀹㈡埛缂栧彿", example = "300")
private Long clientId;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/vo/MesProTaskStartWaitCountdownReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/vo/MesProTaskStartWaitCountdownReqVO.java
new file mode 100644
index 0000000..519c5ca
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/vo/MesProTaskStartWaitCountdownReqVO.java
@@ -0,0 +1,15 @@
+package cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Schema(description = "绠$悊鍚庡彴 - 鐢熶骇浠诲姟寮�濮嬫姤宸ュ�掕鏃� Request VO")
+@Data
+public class MesProTaskStartWaitCountdownReqVO {
+
+ @Schema(description = "鐢熶骇浠诲姟缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+ @NotNull(message = "鐢熶骇浠诲姟缂栧彿涓嶈兘涓虹┖")
+ private Long taskId;
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productreceipt/MesWmProductReceiptController.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productreceipt/MesWmProductReceiptController.java
index d8e5af5..42a8a9e 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productreceipt/MesWmProductReceiptController.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productreceipt/MesWmProductReceiptController.java
@@ -11,6 +11,7 @@
import cn.iocoder.yudao.module.mes.controller.admin.wm.productreceipt.vo.MesWmProductReceiptPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.productreceipt.vo.MesWmProductReceiptRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.productreceipt.vo.MesWmProductReceiptSaveReqVO;
+import cn.iocoder.yudao.module.mes.controller.admin.wm.productreceipt.vo.MesWmProductReceiptStockReqVO;
import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi;
import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO;
@@ -131,6 +132,14 @@
return success(true);
}
+ @PostMapping("/submit-stock")
+ @Operation(summary = "鎵归噺涓婃灦锛堝嬀閫夋槸鍚﹀叆搴� + 浠撳簱/搴撳尯/搴撲綅鍒嗛厤鏁伴噺锛�")
+ @PreAuthorize("@ss.hasPermission('mes:wm-product-receipt:update')")
+ public CommonResult<Boolean> submitStock(@Valid @RequestBody MesWmProductReceiptStockReqVO reqVO) {
+ productReceiptService.submitStock(reqVO);
+ return success(true);
+ }
+
@PutMapping("/finish")
@Operation(summary = "鎵ц鍏ュ簱")
@Parameter(name = "id", description = "缂栧彿", required = true)
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productreceipt/vo/MesWmProductReceiptRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productreceipt/vo/MesWmProductReceiptRespVO.java
index 617bde9..3dd1502 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productreceipt/vo/MesWmProductReceiptRespVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productreceipt/vo/MesWmProductReceiptRespVO.java
@@ -34,6 +34,9 @@
@Schema(description = "浜у搧鐗╂枡缂栧彿", example = "1")
private Long itemId;
+ @Schema(description = "鏉ユ簮鎶ュ伐缂栧彿", example = "1")
+ private Long feedbackId;
+
@Schema(description = "浜у搧鐗╂枡缂栫爜", example = "P001")
@ExcelProperty("浜у搧鐗╂枡缂栫爜")
private String itemCode;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productreceipt/vo/MesWmProductReceiptSaveReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productreceipt/vo/MesWmProductReceiptSaveReqVO.java
index 19d8e34..bdeaac8 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productreceipt/vo/MesWmProductReceiptSaveReqVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productreceipt/vo/MesWmProductReceiptSaveReqVO.java
@@ -25,6 +25,9 @@
@Schema(description = "鐢熶骇宸ュ崟缂栧彿", example = "1")
private Long workOrderId;
+ @Schema(description = "鏉ユ簮鎶ュ伐缂栧彿", example = "1")
+ private Long feedbackId;
+
@Schema(description = "鏀惰揣鏃ユ湡", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "鏀惰揣鏃ユ湡涓嶈兘涓虹┖")
private LocalDateTime receiptDate;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productreceipt/vo/MesWmProductReceiptStockReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productreceipt/vo/MesWmProductReceiptStockReqVO.java
new file mode 100644
index 0000000..8388b75
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productreceipt/vo/MesWmProductReceiptStockReqVO.java
@@ -0,0 +1,60 @@
+package cn.iocoder.yudao.module.mes.controller.admin.wm.productreceipt.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+@Schema(description = "绠$悊鍚庡彴 - MES 浜у搧鍏ュ簱鍗曟壒閲忎笂鏋讹紙鍕鹃�夋槸鍚﹀叆搴� + 浠撳簱/搴撳尯/搴撲綅鍒嗛厤锛� Request VO")
+@Data
+public class MesWmProductReceiptStockReqVO {
+
+ @Schema(description = "浜у搧鍏ュ簱鍗曠紪鍙�", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @NotNull(message = "浜у搧鍏ュ簱鍗曠紪鍙蜂笉鑳戒负绌�")
+ private Long receiptId;
+
+ @Schema(description = "琛屼笂鏋跺垪琛�")
+ @Valid
+ private List<Row> rows;
+
+ @Schema(description = "鍏ュ簱鍗曡涓婃灦")
+ @Data
+ public static class Row {
+
+ @Schema(description = "琛岀紪鍙�", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @NotNull(message = "琛岀紪鍙蜂笉鑳戒负绌�")
+ private Long lineId;
+
+ @Schema(description = "鏄惁鍏ュ簱", example = "true")
+ private Boolean isStock;
+
+ @Schema(description = "浠撳簱/搴撳尯/搴撲綅鍒嗛厤鏄庣粏锛坕sStock=true 鏃跺繀椤伙紝涓旀暟閲忎箣鍜�=琛屾敹璐ф暟閲忥級")
+ @Valid
+ private List<Detail> details;
+ }
+
+ @Schema(description = "浠撳簱/搴撳尯/搴撲綅鍒嗛厤鏄庣粏")
+ @Data
+ public static class Detail {
+
+ @Schema(description = "浠撳簱缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @NotNull(message = "浠撳簱缂栧彿涓嶈兘涓虹┖")
+ private Long warehouseId;
+
+ @Schema(description = "搴撳尯缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @NotNull(message = "搴撳尯缂栧彿涓嶈兘涓虹┖")
+ private Long locationId;
+
+ @Schema(description = "搴撲綅缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @NotNull(message = "搴撲綅缂栧彿涓嶈兘涓虹┖")
+ private Long areaId;
+
+ @Schema(description = "鏁伴噺", requiredMode = Schema.RequiredMode.REQUIRED, example = "50.00")
+ @NotNull(message = "鏁伴噺涓嶈兘涓虹┖")
+ private BigDecimal quantity;
+ }
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productreceipt/vo/line/MesWmProductReceiptLineRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productreceipt/vo/line/MesWmProductReceiptLineRespVO.java
index 7c1177f..18a22cd 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productreceipt/vo/line/MesWmProductReceiptLineRespVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productreceipt/vo/line/MesWmProductReceiptLineRespVO.java
@@ -43,6 +43,12 @@
@Schema(description = "鎵规鍙�", example = "BATCH20260301001")
private String batchCode;
+ @Schema(description = "璐ㄩ噺鐘舵��", example = "1")
+ private Integer qualityStatus;
+
+ @Schema(description = "鏄惁鍏ュ簱", example = "false")
+ private Boolean isStock;
+
@Schema(description = "澶囨敞", example = "澶囨敞")
private String remark;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productreceipt/vo/line/MesWmProductReceiptLineSaveReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productreceipt/vo/line/MesWmProductReceiptLineSaveReqVO.java
index 7b08692..0256bf2 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productreceipt/vo/line/MesWmProductReceiptLineSaveReqVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productreceipt/vo/line/MesWmProductReceiptLineSaveReqVO.java
@@ -36,6 +36,12 @@
@Schema(description = "鎵规鍙�", example = "BATCH20260301001")
private String batchCode;
+ @Schema(description = "璐ㄩ噺鐘舵��", example = "1")
+ private Integer qualityStatus;
+
+ @Schema(description = "鏄惁鍏ュ簱", example = "false")
+ private Boolean isStock;
+
@Schema(description = "澶囨敞", example = "澶囨敞")
private String remark;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/process/MesProProcessDO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/process/MesProProcessDO.java
index 4c8a24a..c0bee6e 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/process/MesProProcessDO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/process/MesProProcessDO.java
@@ -6,6 +6,8 @@
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
+import java.math.BigDecimal;
+
/**
* MES 鐢熶骇宸ュ簭 DO
*
@@ -45,6 +47,18 @@
*/
private Integer status;
/**
+ * 绛夊緟鏃堕暱锛堝皬鏃讹級
+ *
+ * 鏈夊�硷紙>0锛夋椂锛岃宸ュ簭鎶ュ伐鍓嶉渶鍏堢偣鍑�"寮�濮嬪�掕鏃�"锛屽�掕鏃剁粨鏉熸墠鑳芥姤宸�
+ */
+ private BigDecimal waitDuration;
+ /**
+ * 鏄惁闇�瑕佸~鍐欐姤宸ユ暟閲�
+ *
+ * true=鎶ュ伐蹇呴』濉啓鏁伴噺锛沠alse=鎶ュ伐鏁伴噺鍏佽涓虹┖
+ */
+ private Boolean needReportQuantity;
+ /**
* 澶囨敞
*/
private String remark;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/task/MesProTaskDO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/task/MesProTaskDO.java
index edea2e0..63ed314 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/task/MesProTaskDO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/task/MesProTaskDO.java
@@ -99,6 +99,12 @@
* 璋冩暣鏁伴噺
*/
private BigDecimal changedQuantity;
+ /**
+ * 鎶ュ伐鍊掕鏃跺紑濮嬫椂闂�
+ *
+ * 宸ュ簭閰嶇疆浜嗙瓑寰呮椂闀挎椂锛屾搷浣滃憳鐐瑰嚮"寮�濮嬪�掕鏃�"璁板綍锛涘�掕鏃剁粨鏉熸墠鑳芥姤宸�
+ */
+ private LocalDateTime waitStartTime;
/**
* 瀹㈡埛缂栧彿
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/productreceipt/MesWmProductReceiptDO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/productreceipt/MesWmProductReceiptDO.java
index c9a99e4..84c3a72 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/productreceipt/MesWmProductReceiptDO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/productreceipt/MesWmProductReceiptDO.java
@@ -51,6 +51,13 @@
*/
private Long itemId;
/**
+ * 鏉ユ簮鎶ュ伐缂栧彿
+ *
+ * 鍏宠仈 {@link cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO#getId()}锛�
+ * 涓�娆℃姤宸�=涓�鏉′骇鍝佸叆搴撳崟锛岀敤浜庡箓绛変笌杩芥函
+ */
+ private Long feedbackId;
+ /**
* 鏀惰揣鏃ユ湡
*/
private LocalDateTime receiptDate;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/productreceipt/MesWmProductReceiptLineDO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/productreceipt/MesWmProductReceiptLineDO.java
index bf0d378..23aec0d 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/productreceipt/MesWmProductReceiptLineDO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/productreceipt/MesWmProductReceiptLineDO.java
@@ -3,6 +3,7 @@
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO;
+import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@@ -63,6 +64,18 @@
*/
private String batchCode;
/**
+ * 璐ㄩ噺鐘舵��
+ *
+ * 瀛楀吀 {@link DictTypeConstants#MES_WM_QUALITY_STATUS}锛屾姤宸ヤ骇鍑鸿甯﹀叆锛圥ASS/FAIL锛�
+ */
+ private Integer qualityStatus;
+ /**
+ * 鏄惁鍏ュ簱
+ *
+ * true=宸插嬀閫夊叆搴撳苟鍒嗛厤浠撳簱/搴撳尯/搴撲綅锛沠alse/绌�=鎵ц鍏ュ簱鏃堕粯璁よ繘涓嶅悎鏍间粨搴�
+ */
+ private Boolean isStock;
+ /**
* 澶囨敞
*/
private String remark;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/warehouse/MesWmWarehouseAreaDO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/warehouse/MesWmWarehouseAreaDO.java
index ea9a3ab..637be9f 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/warehouse/MesWmWarehouseAreaDO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/warehouse/MesWmWarehouseAreaDO.java
@@ -27,6 +27,11 @@
public static final String WIP_VIRTUAL_AREA = "WIP_VIRTUAL_AREA";
/**
+ * 涓嶅悎鏍煎簱浣嶇紪鐮侊紙閰嶅悎 {@link MesWmWarehouseDO#DEFECT_WAREHOUSE} 浣跨敤锛�
+ */
+ public static final String DEFECT_AREA = "DEFECT_AREA";
+
+ /**
* 缂栧彿
*/
@TableId
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/warehouse/MesWmWarehouseDO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/warehouse/MesWmWarehouseDO.java
index 0c8a9b2..74a703e 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/warehouse/MesWmWarehouseDO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/warehouse/MesWmWarehouseDO.java
@@ -31,6 +31,14 @@
public static final String WIP_VIRTUAL_WAREHOUSE = "WIP_VIRTUAL_WAREHOUSE";
/**
+ * 涓嶅悎鏍间粨搴撶紪鐮�
+ *
+ * 涓氬姟鑳屾櫙锛氭姤宸ュ畬鎴愬悗鑷姩鐢熸垚鐨勪骇鍝佸叆搴撳崟锛屾湭鍕鹃�夊叆搴撴垨鏈垎閰嶅畬鐨勬暟閲忛粯璁ゅ叆鍒版浠撳簱锛�
+ * 涓� {@link #WIP_VIRTUAL_WAREHOUSE} 涓�鏍锋寜缂栫爜绾﹀畾璇嗗埆銆�
+ */
+ public static final String DEFECT_WAREHOUSE = "DEFECT_WAREHOUSE";
+
+ /**
* 缂栧彿
*/
@TableId
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/warehouse/MesWmWarehouseLocationDO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/warehouse/MesWmWarehouseLocationDO.java
index 1c68ff8..026572c 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/warehouse/MesWmWarehouseLocationDO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/warehouse/MesWmWarehouseLocationDO.java
@@ -27,6 +27,11 @@
public static final String WIP_VIRTUAL_LOCATION = "WIP_VIRTUAL_LOCATION";
/**
+ * 涓嶅悎鏍煎簱鍖虹紪鐮侊紙閰嶅悎 {@link MesWmWarehouseDO#DEFECT_WAREHOUSE} 浣跨敤锛�
+ */
+ public static final String DEFECT_LOCATION = "DEFECT_LOCATION";
+
+ /**
* 缂栧彿
*/
@TableId
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/task/MesProTaskMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/task/MesProTaskMapper.java
index a90270a..cb3ec88 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/task/MesProTaskMapper.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/task/MesProTaskMapper.java
@@ -69,6 +69,14 @@
return selectPage(reqVO, query);
}
+ default List<MesProTaskDO> selectListByWaitStartTimeNotNull() {
+ return selectList(new LambdaQueryWrapperX<MesProTaskDO>()
+ .isNotNull(MesProTaskDO::getWaitStartTime)
+ .in(MesProTaskDO::getStatus, MesProTaskStatusEnum.PREPARE.getStatus(),
+ MesProTaskStatusEnum.PRODUCING.getStatus(),
+ MesProTaskStatusEnum.PARTIAL_FINISHED.getStatus()));
+ }
+
default List<MesProTaskDO> selectListByWorkOrderId(Long workOrderId) {
return selectList(new LambdaQueryWrapperX<MesProTaskDO>()
.eqIfPresent(MesProTaskDO::getWorkOrderId, workOrderId)
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/productreceipt/MesWmProductReceiptMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/productreceipt/MesWmProductReceiptMapper.java
index 00f5f02..b89947c 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/productreceipt/MesWmProductReceiptMapper.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/productreceipt/MesWmProductReceiptMapper.java
@@ -26,4 +26,8 @@
return selectOne(MesWmProductReceiptDO::getCode, code);
}
+ default MesWmProductReceiptDO selectByFeedbackId(Long feedbackId) {
+ return selectOne(MesWmProductReceiptDO::getFeedbackId, feedbackId);
+ }
+
}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/redis/RedisKeyConstants.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/redis/RedisKeyConstants.java
index 74929ab..9271620 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/redis/RedisKeyConstants.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/redis/RedisKeyConstants.java
@@ -41,4 +41,20 @@
*/
String METERING_EXPIRE_REMIND_OVERDUE = "mes:dv:metering:overdue:remind:";
+ /**
+ * 鐢熶骇浠诲姟鎶ュ伐鍊掕鏃舵彁閱掑幓閲�
+ *
+ * KEY 鏍煎紡锛歮es:pro:task:wait:remind:{taskId}:{level}:{waitStartTime}
+ * VALUE 鏁版嵁鏍煎紡锛�1
+ */
+ String TASK_WAIT_REMIND = "mes:pro:task:wait:remind:";
+
+ /**
+ * 鐢熶骇浠诲姟鎶ュ伐鍊掕鏃舵彁閱掓壂鎻忛棬闂�
+ *
+ * KEY 鏍煎紡锛歮es:pro:task:wait:remind:scan
+ * VALUE 鏁版嵁鏍煎紡锛�1
+ */
+ String TASK_WAIT_REMIND_SCAN = "mes:pro:task:wait:remind:scan";
+
}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/redis/pro/task/MesProTaskWaitRemindRedisDAO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/redis/pro/task/MesProTaskWaitRemindRedisDAO.java
new file mode 100644
index 0000000..4d83540
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/redis/pro/task/MesProTaskWaitRemindRedisDAO.java
@@ -0,0 +1,54 @@
+package cn.iocoder.yudao.module.mes.dal.redis.pro.task;
+
+import cn.iocoder.yudao.module.mes.dal.redis.RedisKeyConstants;
+import jakarta.annotation.Resource;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.stereotype.Repository;
+
+import java.time.Duration;
+import java.time.LocalDateTime;
+
+/**
+ * MES 鐢熶骇浠诲姟鎶ュ伐鍊掕鏃舵彁閱� Redis DAO
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@Repository
+public class MesProTaskWaitRemindRedisDAO {
+
+ /**
+ * 鎻愰啋鍘婚噸 KEY 鐨勮繃鏈熸椂闂达紙24 灏忔椂锛夛紝鍚屼竴浠诲姟鍚屼竴绛夌骇鍚屼竴杞�掕鏃� 24 灏忔椂鍐呭彧鎻愰啋涓�娆�
+ */
+ private static final Duration REMIND_TIMEOUT = Duration.ofHours(24);
+ /**
+ * 鎵弿闂ㄩ棭鐨勮繃鏈熸椂闂达紙2 鍒嗛挓锛夛紝閬垮厤澶氬疄渚�/澶氭骞跺彂閲嶅鎵弿
+ */
+ private static final Duration SCAN_LOCK_TIMEOUT = Duration.ofMinutes(2);
+
+ @Resource
+ private StringRedisTemplate stringRedisTemplate;
+
+ /**
+ * 灏濊瘯鏍囪宸叉彁閱掞紙鍚屼竴浠诲姟鍚屼竴绛夌骇鍚屼竴杞�掕鏃跺彧鎻愰啋涓�娆★級
+ *
+ * @param taskId 鐢熶骇浠诲姟缂栧彿
+ * @param level 鎻愰啋绛夌骇锛�1=鍓╀綑10鍒嗛挓銆�2=鍓╀綑5鍒嗛挓銆�3=鍊掕鏃剁粨鏉燂級
+ * @param waitStartTime 鍊掕鏃跺紑濮嬫椂闂达紝鍙備笌 KEY 淇濊瘉閲嶆柊寮�濮嬪�掕鏃跺悗浼氶噸鏂版彁閱�
+ * @return true 琛ㄧず棣栨鏍囪鎴愬姛锛岄渶瑕佸彂閫佹彁閱�
+ */
+ public boolean tryMarkReminded(Long taskId, Integer level, LocalDateTime waitStartTime) {
+ String key = RedisKeyConstants.TASK_WAIT_REMIND + taskId + ":" + level + ":" + waitStartTime;
+ return Boolean.TRUE.equals(stringRedisTemplate.opsForValue().setIfAbsent(key, "1", REMIND_TIMEOUT));
+ }
+
+ /**
+ * 灏濊瘯鑾峰彇鎵弿闂ㄩ棭锛岄伩鍏嶅瀹炰緥骞跺彂閲嶅鎵弿
+ *
+ * @return true 琛ㄧず鑾峰彇鎴愬姛锛堝彲浠ユ墽琛屾壂鎻忥級
+ */
+ public boolean tryAcquireScanLock() {
+ String key = RedisKeyConstants.TASK_WAIT_REMIND_SCAN;
+ return Boolean.TRUE.equals(stringRedisTemplate.opsForValue().setIfAbsent(key, "1", SCAN_LOCK_TIMEOUT));
+ }
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
index 2d176dd..8138b88 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
@@ -300,6 +300,9 @@
ErrorCode PRO_WORKSTATION_PROCESS_MISMATCH = new ErrorCode(1_040_503_009, "宸ヤ綔绔欐墍灞炲伐搴忎笌褰撳墠宸ュ簭涓嶄竴鑷�"); // 淇缂栧彿鍐茬獊
ErrorCode PRO_TASK_CANNOT_PAUSE = new ErrorCode(1_040_503_007, "鍙湁銆愮敓浜т腑銆戞垨銆愰儴鍒嗗畬鎴愩�戠殑浠诲姟鍙互鍋滃伐");
ErrorCode PRO_TASK_NOT_PAUSED = new ErrorCode(1_040_503_008, "浠诲姟涓嶅浜庛�愬凡鍋滃伐銆戠姸鎬侊紝鏃犳硶澶嶅伐");
+ ErrorCode PRO_TASK_WAIT_NOT_CONFIGURED = new ErrorCode(1_040_503_010, "璇ュ伐搴忔湭閰嶇疆绛夊緟鏃堕暱锛屾棤闇�鍊掕鏃�");
+ ErrorCode PRO_TASK_WAIT_NOT_STARTED = new ErrorCode(1_040_503_011, "璇ュ伐搴忚缃簡绛夊緟鏃堕暱锛岃鍏堢偣鍑籠"寮�濮嬪�掕鏃禱"");
+ ErrorCode PRO_TASK_WAIT_NOT_ELAPSED = new ErrorCode(1_040_503_012, "绛夊緟鍊掕鏃舵湭缁撴潫锛岃繕闇�绛夊緟 {}");
// ========== MES 鐢熶骇绠$悊-鐢熶骇浠诲姟鎶曟枡锛�1-040-503-100锛� ==========
ErrorCode PRO_TASK_ISSUE_NOT_EXISTS = new ErrorCode(1_040_503_100, "鐢熶骇浠诲姟鎶曟枡璁板綍涓嶅瓨鍦�");
@@ -630,6 +633,8 @@
ErrorCode WM_PRODUCT_RECPT_STATUS_ERROR = new ErrorCode(1_040_712_005, "鏀惰揣鍗曠姸鎬佷笉姝g‘");
ErrorCode WM_PRODUCT_RECPT_CANCEL_NOT_ALLOWED = new ErrorCode(1_040_712_006, "宸插畬鎴愭垨宸插彇娑堢殑鏀惰揣鍗曚笉鍏佽鍙栨秷");
ErrorCode WM_PRODUCT_RECPT_NO_DETAIL = new ErrorCode(1_040_712_007, "鏀惰揣鍗曟病鏈変笂鏋舵槑缁嗭紝鏃犳硶鎵ц鍏ュ簱");
+ ErrorCode WM_PRODUCT_RECPT_NO_DEFECT_WAREHOUSE = new ErrorCode(1_040_712_008, "鏈厤缃笉鍚堟牸浠撳簱锛岃鍏堝垱寤� DEFECT_WAREHOUSE 浠撳簱");
+ ErrorCode WM_PRODUCT_RECPT_STOCK_QUANTITY_MISMATCH = new ErrorCode(1_040_712_009, "鍕鹃�夊叆搴撶殑鍒嗛厤鏁伴噺涓庤鏀惰揣鏁伴噺涓嶄竴鑷�");
ErrorCode WM_PRODUCT_RECPT_LINE_NOT_EXISTS = new ErrorCode(1_040_712_100, "浜у搧鏀惰揣鍗曡涓嶅瓨鍦�");
ErrorCode WM_PRODUCT_RECPT_DETAIL_NOT_EXISTS = new ErrorCode(1_040_712_200, "浜у搧鏀惰揣鏄庣粏涓嶅瓨鍦�");
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/md/autocode/MesMdAutoCodeRuleCodeEnum.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/md/autocode/MesMdAutoCodeRuleCodeEnum.java
index 0e2a210..ecfba32 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/md/autocode/MesMdAutoCodeRuleCodeEnum.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/md/autocode/MesMdAutoCodeRuleCodeEnum.java
@@ -56,7 +56,8 @@
PD_DOCUMENT_AUDIT_CODE("PD_DOCUMENT_AUDIT_CODE", "璧勬枡瀹℃牳鍗曠紪鐮�"),
PD_ARCHIVE_CODE("PD_ARCHIVE_CODE", "褰掓。鍙拌处缂栫爜"),
PD_PROCESS_PARAM_CODE("PD_PROCESS_PARAM_CODE", "宸ヨ壓鍙傛暟缂栫爜"),
- PD_PROCESS_PARAM_TEMPLATE_CODE("PD_PROCESS_PARAM_TEMPLATE_CODE", "宸ヨ壓鍙傛暟妯℃澘缂栫爜");
+ PD_PROCESS_PARAM_TEMPLATE_CODE("PD_PROCESS_PARAM_TEMPLATE_CODE", "宸ヨ壓鍙傛暟妯℃澘缂栫爜"),
+ PRODUCTRECPT_CODE("PRODUCTRECPT_CODE", "浜у搧鍏ュ簱鍗曠紪鐮�");
/**
* 瑙勫垯浠g爜
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/job/pro/task/MesProTaskWaitRemindJob.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/job/pro/task/MesProTaskWaitRemindJob.java
new file mode 100644
index 0000000..c68371e
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/job/pro/task/MesProTaskWaitRemindJob.java
@@ -0,0 +1,28 @@
+package cn.iocoder.yudao.module.mes.job.pro.task;
+
+import cn.iocoder.yudao.module.mes.service.pro.task.MesProTaskWaitRemindService;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+/**
+ * MES 鐢熶骇浠诲姟鎶ュ伐鍊掕鏃舵彁閱掑畾鏃朵换鍔�
+ *
+ * <p>姣忓垎閽熸壂鎻忎竴娆★紝鎸変笁涓瓑绾э紙鍓╀綑 10 鍒嗛挓 / 5 鍒嗛挓 / 缁撴潫锛夊彂閫佺珯鍐呬俊鎻愰啋
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@Slf4j
+@Component
+public class MesProTaskWaitRemindJob {
+
+ @Resource
+ private MesProTaskWaitRemindService waitRemindService;
+
+ @Scheduled(cron = "0 * * * * ?")
+ public void execute() {
+ waitRemindService.sendWaitRemind();
+ }
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/feedback/MesProFeedbackServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/feedback/MesProFeedbackServiceImpl.java
index 016e10a..489c6b4 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/feedback/MesProFeedbackServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/feedback/MesProFeedbackServiceImpl.java
@@ -12,6 +12,7 @@
import cn.iocoder.yudao.module.mes.enums.pro.MesProFeedbackTypeEnum;
import cn.iocoder.yudao.module.mes.service.md.autocode.MesMdAutoCodeRecordService;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.process.MesProProcessDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderBomDO;
@@ -24,6 +25,7 @@
import cn.iocoder.yudao.module.mes.enums.pro.MesProFeedbackStatusEnum;
import cn.iocoder.yudao.module.mes.enums.pro.MesProTaskStatusEnum;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum;
+import cn.iocoder.yudao.module.mes.service.pro.process.MesProProcessService;
import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProcessService;
import cn.iocoder.yudao.module.mes.service.pro.task.MesProTaskService;
import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderBomService;
@@ -33,6 +35,7 @@
import cn.iocoder.yudao.module.mes.service.wm.itemconsume.MesWmItemConsumeService;
import cn.iocoder.yudao.module.mes.service.wm.productproduce.MesWmProductProduceLineService;
import cn.iocoder.yudao.module.mes.service.wm.productproduce.MesWmProductProduceService;
+import cn.iocoder.yudao.module.mes.service.wm.productreceipt.MesWmProductReceiptService;
import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService;
import cn.iocoder.yudao.module.mes.dal.dataobject.mdm.MesMdmItemDO;
import cn.iocoder.yudao.module.mdm.api.item.MdmItemBatchConfigApi;
@@ -48,6 +51,7 @@
import org.springframework.validation.annotation.Validated;
import java.math.BigDecimal;
+import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collection;
@@ -78,6 +82,8 @@
@Resource
private MesProRouteProcessService routeProcessService;
@Resource
+ private MesProProcessService processService;
+ @Resource
private MesProWorkOrderBomService workOrderBomService;
@Resource
private MesProWorkOrderProcessService workOrderProcessService;
@@ -88,6 +94,9 @@
private MesWmItemConsumeService itemConsumeService;
@Resource
private MesWmProductProduceService productProduceService;
+ @Resource
+ @Lazy // 閬垮厤寰幆渚濊禆锛氫骇鍝佹敹璐у崟鑷姩鐢熸垚閫昏緫渚濊禆鎶ュ伐 Service
+ private MesWmProductReceiptService productReceiptService;
@Resource
private MesWmProductProduceLineService produceLineService;
@Resource
@@ -266,13 +275,17 @@
public boolean approveFeedback(MesProFeedbackApproveReqVO reqVO) {
// 1.1 鏍¢獙瀛樺湪 + 瀹℃壒涓姸鎬�
MesProFeedbackDO feedback = validateFeedbackStatusApproving(reqVO.getId());
- // 1.2 鏍¢獙鎶ュ伐鏁伴噺 > 0
- if (feedback.getFeedbackQuantity() == null
- || feedback.getFeedbackQuantity().compareTo(BigDecimal.ZERO) <= 0) {
- throw exception(PRO_FEEDBACK_QUANTITY_MUST_POSITIVE);
+ // 1.2 鏍¢獙浠诲姟鏈畬鎴� + 绛夊緟鏃堕暱鍊掕鏃�
+ MesProTaskDO task = taskService.validateTaskNotFinished(feedback.getTaskId());
+ MesProProcessDO process = validateWaitDuration(task);
+ BigDecimal fbQty = feedback.getFeedbackQuantity() == null
+ ? BigDecimal.ZERO : feedback.getFeedbackQuantity();
+ // 1.3 鏍¢獙鎶ュ伐鏁伴噺锛氶粯璁ゅ繀椤� > 0锛涘伐搴忛厤缃�"涓嶉渶瑕佸~鍐欐姤宸ユ暟閲�"鏃跺厑璁镐负绌�
+ if (process == null || !Boolean.FALSE.equals(process.getNeedReportQuantity())) {
+ if (fbQty.compareTo(BigDecimal.ZERO) <= 0) {
+ throw exception(PRO_FEEDBACK_QUANTITY_MUST_POSITIVE);
+ }
}
- // 1.3 鏍¢獙浠诲姟鏈畬鎴�
- taskService.validateTaskNotFinished(feedback.getTaskId());
// 2.1 鏌ヨ宸ュ簭鐨勫叧閿伐搴忔爣璇� + 妫�楠屾爣璇嗭紙闇�鍦� uncheckQuantity 鏍¢獙涔嬪墠锛屽洜涓鸿川妫�宸ュ簭鍏佽 uncheckQuantity > 0锛�
MesProRouteProcessDO routeProcess = routeProcessService.getRouteProcessByRouteIdAndProcessId(
@@ -280,13 +293,14 @@
boolean keyFlag = routeProcess != null && Boolean.TRUE.equals(routeProcess.getKeyFlag());
boolean checkFlag = routeProcess != null && Boolean.TRUE.equals(routeProcess.getCheckFlag());
// 2.2 闈炶川妫�宸ュ簭锛氬悎鏍煎搧/涓嶈壇鍝佹暟閲忕敱璐ㄦ纭畾锛涙湰宸ュ簭涓嶇粡杩囪川妫�锛屾姤宸ユ湭濉啓鏃堕粯璁ゅ叏閮ㄤ负鍚堟牸鍝�
+ // 锛堟暟閲忎负绌�/0 鏃舵寜 0 澶勭悊锛岄伩鍏嶅啓鍏� null锛�
if (!checkFlag
&& feedback.getQualifiedQuantity() == null
&& feedback.getUnqualifiedQuantity() == null) {
- feedback.setQualifiedQuantity(feedback.getFeedbackQuantity());
+ feedback.setQualifiedQuantity(fbQty);
feedback.setUnqualifiedQuantity(BigDecimal.ZERO);
feedbackMapper.updateById(new MesProFeedbackDO().setId(feedback.getId())
- .setQualifiedQuantity(feedback.getFeedbackQuantity())
+ .setQualifiedQuantity(fbQty)
.setUnqualifiedQuantity(BigDecimal.ZERO));
}
// 2.3 闈炶川妫�宸ュ簭锛氫粛鏈夊緟妫�鏁伴噺鏃朵笉鑳藉鎵癸紙璐ㄦ宸ュ簭鐨� uncheckQuantity > 0 鏄甯哥姸鎬侊紝涓嶅仛鎷︽埅锛�
@@ -297,8 +311,9 @@
}
// 3. 鐗╂枡娑堣�楋細鍊掑啿宸ュ簭鎸� BOM 鑷姩鎵f枡锛涢潪鍊掑啿宸ュ簭璺宠繃锛堢墿鏂欒蛋鎵嬪伐棰嗘枡/鎶曟枡锛�
+ // 鎶ュ伐鏁伴噺涓虹┖/0 鏃惰烦杩囧�掑啿鎵f枡锛岄伩鍏嶇┖鏁伴噺璇墸绾胯竟搴�
boolean backflushFlag = routeProcess != null && Boolean.TRUE.equals(routeProcess.getBackflushFlag());
- if (backflushFlag) {
+ if (backflushFlag && fbQty.compareTo(BigDecimal.ZERO) > 0) {
// 3.1 鏍¢獙鍊掑啿宸ュ簭蹇呴』閰嶇疆 BOM 娑堣�楋紝閬垮厤闈欓粯涓嶆墸鏂�
List<MesProWorkOrderBomDO> boms = workOrderBomService.getWorkOrderBomListByWorkOrderId(
feedback.getWorkOrderId());
@@ -316,6 +331,14 @@
feedbackMapper.updateById(new MesProFeedbackDO().setId(reqVO.getId())
.setStatus(MesProFeedbackStatusEnum.FINISHED.getStatus())
.setUncheckQuantity(BigDecimal.ZERO));
+
+ // 5.1 鎶ュ伐鏁伴噺涓虹┖/0锛�"涓嶉渶瑕佸~鍐欐姤宸ユ暟閲�"鐨勫伐搴忥級锛氭棤浜у嚭鍙叆搴擄紝浠呮洿鏂颁换鍔¤繘搴﹀苟缁撴潫
+ if (fbQty.compareTo(BigDecimal.ZERO) <= 0) {
+ taskService.updateProducedQuantity(feedback.getTaskId(),
+ BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO);
+ checkAndPublishProductionFinished(feedback.getWorkOrderId());
+ return true;
+ }
// 6. 鏍规嵁鏄惁闇�瑕佹楠岋紝鍐冲畾浜у嚭鍜屽叆搴撴柟寮�
// 6.2 璋冩暣 鍙瀹℃壒鎴愬姛鐩存帴鏇存柊
@@ -341,6 +364,8 @@
// 6.2 鏃犻渶妫�楠岋細鐢熸垚浜у嚭鍗曪紙鎸夊悎鏍�/涓嶅悎鏍兼媶琛岋級锛岀洿鎺ュ畬鎴愬叆搴�
MesWmProductProduceDO produce = productProduceService.generateProductProduce(feedback, false, reqVO);
productProduceService.finishProductProduce(produce.getId());
+ // 6.3 鎶ュ伐浜у嚭瀹氱鍚庤嚜鍔ㄧ敓鎴愪骇鍝佸叆搴撳崟锛堜竴娆℃姤宸�=涓�鏉★紝骞傜瓑锛�
+ productReceiptService.createProductReceiptByFeedback(feedback.getId());
}
@@ -477,14 +502,47 @@
if (routeProcess == null) {
throw exception(PRO_FEEDBACK_ROUTE_PROCESS_INVALID);
}
- // 4.1 鏍¢獙鏁伴噺锛氭棤璁烘槸鍚﹂渶瑕佹楠岋紝鎶ュ伐鍙渶濉姤宸ユ暟閲忥紝涓斿繀椤� > 0
+ // 4. 鏍¢獙绛夊緟鏃堕暱鍊掕鏃讹細宸ュ簭閰嶇疆浜嗙瓑寰呮椂闀匡紙waitDuration > 0锛夋椂锛岄渶鍏堝紑濮嬪�掕鏃跺苟绛夊緟缁撴潫
+ MesProProcessDO process = validateWaitDuration(task);
+ // 4.1 鏍¢獙鏁伴噺锛氶粯璁ゅ繀椤� > 0锛涘伐搴忛厤缃�"涓嶉渶瑕佸~鍐欐姤宸ユ暟閲�"鏃跺厑璁镐负绌�
// 鍚堟牸鍝�/涓嶈壇鍝佹暟閲忕敱璐ㄦ纭畾锛堥潪璐ㄦ宸ュ簭鍦ㄥ鎵规椂榛樿鍏ㄩ儴涓哄悎鏍煎搧锛�
- if (reqVO.getFeedbackQuantity() == null
- || reqVO.getFeedbackQuantity().compareTo(BigDecimal.ZERO) <= 0) {
- throw exception(PRO_FEEDBACK_QUANTITY_MUST_POSITIVE);
+ if (process == null || !Boolean.FALSE.equals(process.getNeedReportQuantity())) {
+ if (reqVO.getFeedbackQuantity() == null
+ || reqVO.getFeedbackQuantity().compareTo(BigDecimal.ZERO) <= 0) {
+ throw exception(PRO_FEEDBACK_QUANTITY_MUST_POSITIVE);
+ }
}
return task;
+ }
+
+ /**
+ * 鏍¢獙宸ュ簭绛夊緟鏃堕暱鍊掕鏃讹紙鎶ュ伐鍓嶇疆鏉′欢锛�
+ *
+ * <p>璇诲彇浠诲姟鍏宠仈鐨勫伐搴忎富鏁版嵁 waitDuration锛堝皬鏃讹級锛氳嫢鏈厤缃垨涓� 0 鍒欑洿鎺ヨ繑鍥烇紱閰嶇疆浜嗗垯闇�瑕�
+ * 浠诲姟宸茬偣鍑�"寮�濮嬪�掕鏃�"涓斿�掕鏃跺凡缁撴潫锛屽惁鍒欐姏鍑哄搴斿紓甯搞�傝繑鍥炲伐搴忎富鏁版嵁锛屼緵璋冪敤鏂瑰鐢�
+ * needReportQuantity 绛夊瓧娈点��
+ *
+ * @param task 鐢熶骇浠诲姟
+ * @return 宸ュ簭涓绘暟鎹�
+ */
+ private MesProProcessDO validateWaitDuration(MesProTaskDO task) {
+ MesProProcessDO process = processService.getProcess(task.getProcessId());
+ if (process == null || process.getWaitDuration() == null
+ || process.getWaitDuration().compareTo(BigDecimal.ZERO) <= 0) {
+ return process; // 鏈厤缃瓑寰呮椂闀匡紝鏃犻渶鍊掕鏃�
+ }
+ if (task.getWaitStartTime() == null) {
+ throw exception(PRO_TASK_WAIT_NOT_STARTED); // 璇峰厛鐐瑰嚮"寮�濮嬪�掕鏃�"
+ }
+ LocalDateTime readyTime = task.getWaitStartTime()
+ .plusMinutes((long) (process.getWaitDuration().doubleValue() * 60));
+ if (LocalDateTime.now().isBefore(readyTime)) {
+ Duration remaining = Duration.between(LocalDateTime.now(), readyTime);
+ throw exception(PRO_TASK_WAIT_NOT_ELAPSED,
+ remaining.toHours() + "灏忔椂" + remaining.toMinutesPart() + "鍒嗛挓");
+ }
+ return process;
}
@Override
@@ -504,12 +562,16 @@
if (routeProcess == null) {
throw exception(PRO_FEEDBACK_ROUTE_PROCESS_INVALID);
}
- // 3. 鏍¢獙鏁伴噺锛氬彧濉姤宸ユ暟閲忥紝涓斿繀椤� > 0锛堝悎鏍煎搧/涓嶈壇鍝佹暟閲忕敱璐ㄦ纭畾锛�
- if (reqVO.getFeedbackQuantity() == null
- || reqVO.getFeedbackQuantity().compareTo(BigDecimal.ZERO) <= 0) {
- throw exception(PRO_FEEDBACK_QUANTITY_MUST_POSITIVE);
+ // 3. 鏍¢獙绛夊緟鏃堕暱鍊掕鏃讹細宸ュ簭閰嶇疆浜嗙瓑寰呮椂闀匡紙waitDuration > 0锛夋椂锛岄渶鍏堝紑濮嬪�掕鏃跺苟绛夊緟缁撴潫
+ MesProProcessDO process = validateWaitDuration(task);
+ // 4. 鏍¢獙鏁伴噺锛氶粯璁ゅ繀椤� > 0锛涘伐搴忛厤缃�"涓嶉渶瑕佸~鍐欐姤宸ユ暟閲�"鏃跺厑璁镐负绌猴紙鍚堟牸鍝�/涓嶈壇鍝佹暟閲忕敱璐ㄦ纭畾锛�
+ if (process == null || !Boolean.FALSE.equals(process.getNeedReportQuantity())) {
+ if (reqVO.getFeedbackQuantity() == null
+ || reqVO.getFeedbackQuantity().compareTo(BigDecimal.ZERO) <= 0) {
+ throw exception(PRO_FEEDBACK_QUANTITY_MUST_POSITIVE);
+ }
}
- // 4. 鏋勫缓鎶ュ伐DO
+ // 5. 鏋勫缓鎶ュ伐DO
MesProFeedbackDO feedback = new MesProFeedbackDO()
.setCode(autoCodeRecordService.generateAutoCode(MesMdAutoCodeRuleCodeEnum.PRO_FEEDBACK_CODE.getCode()))
.setType(MesProFeedbackTypeEnum.SELF.getType())
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskService.java
index c2e0d11..6bfa82b 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskService.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskService.java
@@ -195,4 +195,14 @@
*/
List<cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskAssigneeDO> getTaskAssigneeListByTaskIds(Collection<Long> taskIds);
+ /**
+ * 寮�濮嬫姤宸ュ�掕鏃�
+ *
+ * <p>宸ュ簭閰嶇疆浜嗙瓑寰呮椂闀匡紙waitDuration > 0锛夋椂锛岃褰曞�掕鏃跺紑濮嬫椂闂村埌浠诲姟锛涢噸澶嶈皟鐢ㄤ細閲嶇疆寮�濮嬫椂闂淬��
+ *
+ * @param taskId 鐢熶骇浠诲姟缂栧彿
+ * @return 鍊掕鏃跺紑濮嬫椂闂�
+ */
+ java.time.LocalDateTime startWaitCountdown(Long taskId);
+
}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskServiceImpl.java
index 71194e3..a38e19e 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskServiceImpl.java
@@ -12,6 +12,7 @@
import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkstationMachineDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.process.MesProProcessDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.process.MesProProcessParamDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
import cn.iocoder.yudao.module.mes.dal.mysql.pro.task.MesProTaskMapper;
@@ -52,11 +53,13 @@
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
+import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_FEEDBACK_CANNOT_CREATE_WHEN_PAUSED;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_TASK_ALREADY_FINISHED;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_TASK_CANNOT_PAUSE;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_TASK_HAS_FEEDBACK;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_TASK_NOT_EXISTS;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_TASK_NOT_PAUSED;
+import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_TASK_WAIT_NOT_CONFIGURED;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskPauseRecordDO;
@@ -574,4 +577,25 @@
}
}
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public LocalDateTime startWaitCountdown(Long taskId) {
+ // 1. 鏍¢獙浠诲姟瀛樺湪涓旀湭缁堟��
+ MesProTaskDO task = validateTaskNotFinished(taskId);
+ // 2. 鏍¢獙鍋滃伐鐘舵�佷笉鑳芥姤宸�
+ if (!MesProTaskStatusEnum.canFeedback(task.getStatus())) {
+ throw exception(PRO_FEEDBACK_CANNOT_CREATE_WHEN_PAUSED);
+ }
+ // 3. 鏍¢獙宸ュ簭閰嶇疆浜嗙瓑寰呮椂闀�
+ MesProProcessDO process = processService.getProcess(task.getProcessId());
+ if (process == null || process.getWaitDuration() == null
+ || process.getWaitDuration().compareTo(BigDecimal.ZERO) <= 0) {
+ throw exception(PRO_TASK_WAIT_NOT_CONFIGURED);
+ }
+ // 4. 璁板綍鍊掕鏃跺紑濮嬫椂闂达紙閲嶅鐐瑰嚮閲嶇疆锛�
+ LocalDateTime now = LocalDateTime.now();
+ taskMapper.updateById(new MesProTaskDO().setId(taskId).setWaitStartTime(now));
+ return now;
+ }
+
}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskWaitRemindService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskWaitRemindService.java
new file mode 100644
index 0000000..d78c0c2
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskWaitRemindService.java
@@ -0,0 +1,15 @@
+package cn.iocoder.yudao.module.mes.service.pro.task;
+
+/**
+ * MES 鐢熶骇浠诲姟鎶ュ伐鍊掕鏃舵彁閱� Service 鎺ュ彛
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+public interface MesProTaskWaitRemindService {
+
+ /**
+ * 鎵弿鐢熶骇浠诲姟鎶ュ伐鍊掕鏃讹紝鎸変笁涓瓑绾э紙鍓╀綑 10 鍒嗛挓 / 5 鍒嗛挓 / 缁撴潫锛夊彂閫佺珯鍐呬俊鎻愰啋
+ */
+ void sendWaitRemind();
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskWaitRemindServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskWaitRemindServiceImpl.java
new file mode 100644
index 0000000..9de2ee9
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskWaitRemindServiceImpl.java
@@ -0,0 +1,151 @@
+package cn.iocoder.yudao.module.mes.service.pro.task;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.process.MesProProcessDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskAssigneeDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO;
+import cn.iocoder.yudao.module.mes.dal.mysql.pro.task.MesProTaskAssigneeMapper;
+import cn.iocoder.yudao.module.mes.dal.mysql.pro.task.MesProTaskMapper;
+import cn.iocoder.yudao.module.mes.dal.redis.pro.task.MesProTaskWaitRemindRedisDAO;
+import cn.iocoder.yudao.module.mes.service.pro.process.MesProProcessService;
+import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi;
+import cn.iocoder.yudao.module.system.api.notify.dto.NotifySendSingleToUserReqDTO;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;
+
+/**
+ * MES 鐢熶骇浠诲姟鎶ュ伐鍊掕鏃舵彁閱� Service 瀹炵幇绫�
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@Slf4j
+@Service
+public class MesProTaskWaitRemindServiceImpl implements MesProTaskWaitRemindService {
+
+ /**
+ * 涓変釜鎻愰啋绛夌骇瀵瑰簲鐨勭珯鍐呬俊妯℃澘缂栧彿
+ */
+ private static final String[] REMIND_TEMPLATE_CODES = {
+ "mes_task_wait_remind_10", // 绛夌骇 1锛氬墿浣� 10 鍒嗛挓
+ "mes_task_wait_remind_5", // 绛夌骇 2锛氬墿浣� 5 鍒嗛挓
+ "mes_task_wait_remind_end" // 绛夌骇 3锛氬�掕鏃剁粨鏉�
+ };
+
+ @Resource
+ private MesProTaskMapper taskMapper;
+ @Resource
+ private MesProTaskAssigneeMapper taskAssigneeMapper;
+ @Resource
+ private MesProProcessService processService;
+ @Resource
+ private MesProTaskWaitRemindRedisDAO waitRemindRedisDAO;
+ @Resource
+ private NotifyMessageSendApi notifyMessageSendApi;
+
+ @Override
+ public void sendWaitRemind() {
+ try {
+ // 1. 鎶㈡壂鎻忛棬闂╋紝閬垮厤澶氬疄渚�/澶氭骞跺彂閲嶅鎵弿
+ if (!waitRemindRedisDAO.tryAcquireScanLock()) {
+ return;
+ }
+ // 2. 鏌ヨ宸插紑濮嬪�掕鏃朵笖鏈粓鎬�/鏈仠宸ョ殑浠诲姟
+ List<MesProTaskDO> taskList = taskMapper.selectListByWaitStartTimeNotNull();
+ if (CollUtil.isEmpty(taskList)) {
+ return;
+ }
+ // 3. 鎸変换鍔$紪鍙锋壒閲忔煡璇㈡姤宸ヤ汉鍛�
+ Map<Long, List<MesProTaskAssigneeDO>> assigneeMap = convertMultiMap(
+ taskAssigneeMapper.selectListByTaskIds(
+ taskList.stream().map(MesProTaskDO::getId).collect(Collectors.toSet())),
+ MesProTaskAssigneeDO::getTaskId);
+ // 4. 閫愪换鍔℃寜涓変釜绛夌骇鍙戦�佹彁閱�
+ for (MesProTaskDO task : taskList) {
+ try {
+ processTaskRemind(task, assigneeMap.get(task.getId()));
+ } catch (Exception ex) {
+ // best-effort锛氬崟涓换鍔℃彁閱掑け璐ヤ笉褰卞搷鍏朵綑浠诲姟
+ log.error("[sendWaitRemind][浠诲姟 {} 鍙戦�佸�掕鏃舵彁閱掑け璐", task.getId(), ex);
+ }
+ }
+ } catch (Exception ex) {
+ // best-effort锛氭彁閱掑け璐ヤ笉褰卞搷鍏朵粬娴佺▼
+ log.error("[sendWaitRemind][鍙戦�佹姤宸ュ�掕鏃舵彁閱掑け璐", ex);
+ }
+ }
+
+ private void processTaskRemind(MesProTaskDO task, List<MesProTaskAssigneeDO> assigneeList) {
+ // 1. 鍙栧伐搴忕瓑寰呮椂闀匡紝鏈厤缃垯鏃犻渶鍊掕鏃舵彁閱�
+ MesProProcessDO process = processService.getProcess(task.getProcessId());
+ if (process == null || process.getWaitDuration() == null
+ || process.getWaitDuration().compareTo(BigDecimal.ZERO) <= 0) {
+ return;
+ }
+ // 2. 璁$畻鍊掕鏃剁粨鏉熸椂闂翠笌鍓╀綑鍒嗛挓
+ LocalDateTime waitStartTime = task.getWaitStartTime();
+ LocalDateTime readyTime = waitStartTime
+ .plusMinutes((long) (process.getWaitDuration().doubleValue() * 60));
+ long remainingMinutes = Duration.between(LocalDateTime.now(), readyTime).toMinutes(); // 鍙负璐�
+ // 3. 涓変釜绛夌骇鐙珛鍒ゅ畾锛孯edis 鍘婚噸锛堥娆℃爣璁版垚鍔熸墠鍙戦�侊級
+ sendRemindIfDue(task, process, assigneeList, waitStartTime, 1, remainingMinutes <= 10);
+ sendRemindIfDue(task, process, assigneeList, waitStartTime, 2, remainingMinutes <= 5);
+ sendRemindIfDue(task, process, assigneeList, waitStartTime, 3, remainingMinutes <= 0);
+ }
+
+ private void sendRemindIfDue(MesProTaskDO task, MesProProcessDO process,
+ List<MesProTaskAssigneeDO> assigneeList, LocalDateTime waitStartTime,
+ int level, boolean due) {
+ if (!due) {
+ return;
+ }
+ if (!waitRemindRedisDAO.tryMarkReminded(task.getId(), level, waitStartTime)) {
+ return; // 璇ョ瓑绾у凡鎻愰啋杩�
+ }
+ // 缁勮妯℃澘鍙傛暟
+ Map<String, Object> params = new HashMap<>();
+ params.put("taskCode", task.getCode());
+ params.put("taskName", task.getName());
+ params.put("processName", process.getName());
+ // 鎺ユ敹浜猴細浼樺厛鎶ュ伐浜哄憳锛涙湭鎸囧畾鍒欑敤浠诲姟鍒涘缓浜哄厹搴�
+ List<Long> receiverUserIds = assigneeList == null ? null
+ : assigneeList.stream().map(MesProTaskAssigneeDO::getUserId).collect(Collectors.toList());
+ if (CollUtil.isEmpty(receiverUserIds)) {
+ Long creatorUserId = parseCreatorUserId(task.getCreator());
+ if (creatorUserId == null) {
+ return;
+ }
+ receiverUserIds = List.of(creatorUserId);
+ }
+ for (Long userId : receiverUserIds) {
+ NotifySendSingleToUserReqDTO reqDTO = new NotifySendSingleToUserReqDTO()
+ .setUserId(userId)
+ .setTemplateCode(REMIND_TEMPLATE_CODES[level - 1])
+ .setTemplateParams(params);
+ notifyMessageSendApi.sendSingleMessageToAdmin(reqDTO);
+ }
+ }
+
+ private Long parseCreatorUserId(String creator) {
+ if (StrUtil.isBlank(creator)) {
+ return null;
+ }
+ try {
+ return Long.valueOf(creator.trim());
+ } catch (NumberFormatException ex) {
+ return null;
+ }
+ }
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productproduce/MesWmProductProduceServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productproduce/MesWmProductProduceServiceImpl.java
index 1c45ee4..1de46d4 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productproduce/MesWmProductProduceServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productproduce/MesWmProductProduceServiceImpl.java
@@ -24,10 +24,12 @@
import cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService;
import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService;
import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO;
+import cn.iocoder.yudao.module.mes.service.wm.productreceipt.MesWmProductReceiptService;
import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService;
import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseLocationService;
import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseService;
import jakarta.annotation.Resource;
+import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
@@ -66,6 +68,9 @@
private MesWmWarehouseLocationService locationService;
@Resource
private MesWmWarehouseAreaService areaService;
+ @Resource
+ @Lazy // 閬垮厤寰幆渚濊禆锛氫骇鍝佹敹璐у崟鑷姩鐢熸垚閫昏緫渚濊禆鐢熶骇鍏ュ簱鍗� Mapper/琛� Service
+ private MesWmProductReceiptService productReceiptService;
@Override
@Transactional(rollbackFor = Exception.class)
@@ -300,6 +305,9 @@
// 4. 瀹屾垚浜у嚭鍗曪紙鍒涘缓搴撳瓨浜嬪姟 + 鏇存柊鐘舵�佷负宸插畬鎴愶級
finishProductProduce(produce.getId());
+
+ // 5. 鎶ュ伐浜у嚭瀹氱鍚庤嚜鍔ㄧ敓鎴愪骇鍝佸叆搴撳崟锛堜竴娆℃姤宸�=涓�鏉★紝骞傜瓑锛�
+ productReceiptService.createProductReceiptByFeedback(feedbackId);
}
}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productreceipt/MesWmProductReceiptService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productreceipt/MesWmProductReceiptService.java
index e2459b5..f8b86a0 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productreceipt/MesWmProductReceiptService.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productreceipt/MesWmProductReceiptService.java
@@ -3,6 +3,7 @@
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.mes.controller.admin.wm.productreceipt.vo.MesWmProductReceiptPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.productreceipt.vo.MesWmProductReceiptSaveReqVO;
+import cn.iocoder.yudao.module.mes.controller.admin.wm.productreceipt.vo.MesWmProductReceiptStockReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productreceipt.MesWmProductReceiptDO;
import jakarta.validation.Valid;
@@ -78,6 +79,27 @@
void cancelProductReceipt(Long id);
/**
+ * 鎶ュ伐浜у嚭瀹氱鍚庤嚜鍔ㄧ敓鎴愪骇鍝佸叆搴撳崟锛堜竴娆℃姤宸�=涓�鏉★紝骞傜瓑锛�
+ *
+ * <p>琛屾暟鎹潵鑷敓浜у叆搴撳崟浜у嚭琛岋紙鍚堟牸/涓嶅悎鏍煎垎琛岋級锛岃川閲忕姸鎬佸師鏍峰甫鍏ワ紝
+ * is_stock 榛樿 false锛堟湭鍕鹃�夛紝鎵ц鍏ュ簱鏃堕粯璁よ繘涓嶅悎鏍间粨搴擄級銆�
+ *
+ * @param feedbackId 鎶ュ伐缂栧彿
+ * @return 浜у搧鍏ュ簱鍗曠紪鍙�
+ */
+ Long createProductReceiptByFeedback(Long feedbackId);
+
+ /**
+ * 鎵归噺涓婃灦锛堝嬀閫夋槸鍚﹀叆搴� + 浠撳簱/搴撳尯/搴撲綅鍒嗛厤鏁伴噺锛�
+ *
+ * <p>浠呭厑璁稿緟涓婃灦锛圓PPROVING锛夌姸鎬佹墽琛岋紱鍕鹃�夊叆搴撶殑琛岃姹傚垎閰嶆暟閲忎箣鍜岀瓑浜庤鏀惰揣鏁伴噺锛�
+ * 鏈嬀閫夊叆搴撶殑琛屼笉鍒嗛厤鏄庣粏锛屾墽琛屽叆搴撴椂鑷姩杩涗笉鍚堟牸浠撳簱銆�
+ *
+ * @param reqVO 涓婃灦璇锋眰
+ */
+ void submitStock(MesWmProductReceiptStockReqVO reqVO);
+
+ /**
* 鏍¢獙浜у搧鏀惰揣鍗曞瓨鍦ㄤ笖澶勪簬鍙紪杈戠姸鎬侊紙鑽夌鎴栧緟涓婃灦锛�
*
* @param id 缂栧彿
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productreceipt/MesWmProductReceiptServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productreceipt/MesWmProductReceiptServiceImpl.java
index 100627f..a7bf199 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productreceipt/MesWmProductReceiptServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productreceipt/MesWmProductReceiptServiceImpl.java
@@ -8,18 +8,29 @@
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.module.mes.controller.admin.wm.productreceipt.vo.MesWmProductReceiptPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.productreceipt.vo.MesWmProductReceiptSaveReqVO;
+import cn.iocoder.yudao.module.mes.controller.admin.wm.productreceipt.vo.MesWmProductReceiptStockReqVO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceLineDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productreceipt.MesWmProductReceiptDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productreceipt.MesWmProductReceiptDetailDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productreceipt.MesWmProductReceiptLineDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseLocationDO;
+import cn.iocoder.yudao.module.mes.dal.mysql.wm.productproduce.MesWmProductProduceMapper;
+import cn.iocoder.yudao.module.mes.dal.mysql.wm.productreceipt.MesWmProductReceiptDetailMapper;
+import cn.iocoder.yudao.module.mes.dal.mysql.wm.productreceipt.MesWmProductReceiptLineMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.wm.productreceipt.MesWmProductReceiptMapper;
import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
+import cn.iocoder.yudao.module.mes.enums.md.autocode.MesMdAutoCodeRuleCodeEnum;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmProductReceiptStatusEnum;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum;
+import cn.iocoder.yudao.module.mes.service.md.autocode.MesMdAutoCodeRecordService;
+import cn.iocoder.yudao.module.mes.service.pro.feedback.MesProFeedbackService;
import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService;
+import cn.iocoder.yudao.module.mes.service.wm.productproduce.MesWmProductProduceLineService;
import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService;
import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO;
import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService;
@@ -31,7 +42,9 @@
import org.springframework.validation.annotation.Validated;
import java.math.BigDecimal;
+import java.time.LocalDateTime;
import java.util.List;
+import java.util.Objects;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
@@ -45,6 +58,10 @@
@Resource
private MesWmProductReceiptMapper productReceiptMapper;
+ @Resource
+ private MesWmProductReceiptLineMapper productReceiptLineMapper;
+ @Resource
+ private MesWmProductReceiptDetailMapper productReceiptDetailMapper;
@Resource
private MesWmProductReceiptLineService productReceiptLineService;
@@ -60,6 +77,14 @@
private MesWmWarehouseLocationService locationService;
@Resource
private MesWmWarehouseAreaService areaService;
+ @Resource
+ private MesWmProductProduceMapper productProduceMapper;
+ @Resource
+ private MesWmProductProduceLineService productProduceLineService;
+ @Resource
+ private MesProFeedbackService feedbackService;
+ @Resource
+ private MesMdAutoCodeRecordService autoCodeRecordService;
@Override
public Long createProductReceipt(MesWmProductReceiptSaveReqVO createReqVO) {
@@ -166,6 +191,8 @@
if (ObjUtil.notEqual(MesWmProductReceiptStatusEnum.APPROVED.getStatus(), receipt.getStatus())) {
throw exception(WM_PRODUCT_RECPT_STATUS_ERROR);
}
+ // 鏈嬀閫夊叆搴撴垨鏈垎閰嶅畬鐨勬暟閲忛粯璁よˉ鍒颁笉鍚堟牸浠撳簱
+ supplementDefectDetails(receipt);
// 鏍¢獙鏄庣粏闈炵┖
List<MesWmProductReceiptDetailDO> details = productReceiptDetailService.getProductReceiptDetailListByRecptId(id);
if (CollUtil.isEmpty(details)) {
@@ -180,6 +207,136 @@
.setId(id).setStatus(MesWmProductReceiptStatusEnum.FINISHED.getStatus()));
}
+ /**
+ * 涓烘湭鍕鹃�夊叆搴擄紙is_stock=false/绌猴級鎴栧垎閰嶆暟閲忎笉瓒崇殑琛岃ˉ涓�鏉′笉鍚堟牸浠撳簱鏄庣粏
+ *
+ * <p>姣忚鍓╀綑鏁伴噺 = 琛屾敹璐ф暟閲� - 宸插垎閰嶆槑缁嗘暟閲忎箣鍜岋紝鍓╀綑 > 0 鏃惰嚜鍔ㄦ彃鍏ヤ笉鍚堟牸浠撳簱涓夌骇鏄庣粏锛�
+ * 淇濊瘉鎵ц鍏ュ簱鏃朵竴涓暟閲忓搴斾竴鏉℃祦姘淬��
+ */
+ private void supplementDefectDetails(MesWmProductReceiptDO receipt) {
+ List<MesWmProductReceiptLineDO> lines = productReceiptLineService.getProductReceiptLineListByRecptId(receipt.getId());
+ for (MesWmProductReceiptLineDO line : lines) {
+ List<MesWmProductReceiptDetailDO> details = productReceiptDetailService.getProductReceiptDetailListByLineId(line.getId());
+ BigDecimal sumDetailQty = CollectionUtils.getSumValue(details,
+ MesWmProductReceiptDetailDO::getQuantity, BigDecimal::add, BigDecimal.ZERO);
+ BigDecimal remaining = line.getQuantity().subtract(sumDetailQty);
+ if (remaining.compareTo(BigDecimal.ZERO) <= 0) {
+ continue;
+ }
+ // 鏌ヨ涓嶅悎鏍间粨搴撲笁绾э紙鎸夌紪鐮佺害瀹氳瘑鍒級
+ MesWmWarehouseDO defectWarehouse = warehouseService.getWarehouseByCode(MesWmWarehouseDO.DEFECT_WAREHOUSE);
+ MesWmWarehouseLocationDO defectLocation = locationService.getWarehouseLocationByCode(MesWmWarehouseLocationDO.DEFECT_LOCATION);
+ MesWmWarehouseAreaDO defectArea = areaService.getWarehouseAreaByCode(MesWmWarehouseAreaDO.DEFECT_AREA);
+ if (defectWarehouse == null || defectLocation == null || defectArea == null) {
+ throw exception(WM_PRODUCT_RECPT_NO_DEFECT_WAREHOUSE);
+ }
+ productReceiptDetailMapper.insert(new MesWmProductReceiptDetailDO()
+ .setLineId(line.getId())
+ .setReceiptId(receipt.getId())
+ .setItemId(line.getItemId())
+ .setQuantity(remaining)
+ .setBatchId(line.getBatchId())
+ .setWarehouseId(defectWarehouse.getId())
+ .setLocationId(defectLocation.getId())
+ .setAreaId(defectArea.getId()));
+ }
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Long createProductReceiptByFeedback(Long feedbackId) {
+ // 骞傜瓑锛氫竴娆℃姤宸ュ彧鐢熸垚涓�鏉′骇鍝佸叆搴撳崟
+ MesWmProductReceiptDO existing = productReceiptMapper.selectByFeedbackId(feedbackId);
+ if (existing != null) {
+ return existing.getId();
+ }
+ // 鏍¢獙鎶ュ伐瀛樺湪
+ MesProFeedbackDO feedback = feedbackService.validateFeedbackExists(feedbackId);
+ // 鐢熸垚缂栫爜骞舵彃鍏ュ叆搴撳崟
+ MesWmProductReceiptDO receipt = new MesWmProductReceiptDO()
+ .setCode(autoCodeRecordService.generateAutoCode(MesMdAutoCodeRuleCodeEnum.PRODUCTRECPT_CODE.getCode()))
+ .setName("浜у搧鍏ュ簱-" + feedback.getCode())
+ .setWorkOrderId(feedback.getWorkOrderId())
+ .setItemId(feedback.getItemId())
+ .setFeedbackId(feedbackId)
+ .setReceiptDate(LocalDateTime.now())
+ .setStatus(MesWmProductReceiptStatusEnum.PREPARE.getStatus());
+ productReceiptMapper.insert(receipt);
+ // 浠庝骇鍑哄崟浜у嚭琛岀敓鎴愬叆搴撳崟琛岋紙鍚堟牸/涓嶅悎鏍煎垎琛岋紝璐ㄩ噺鐘舵�佸師鏍峰甫鍏ワ級
+ MesWmProductProduceDO produce = productProduceMapper.selectByFeedbackId(feedbackId);
+ if (produce != null) {
+ List<MesWmProductProduceLineDO> produceLines = productProduceLineService.getProductProduceLineListByProduceId(produce.getId());
+ for (MesWmProductProduceLineDO produceLine : produceLines) {
+ if (produceLine.getQuantity() == null || produceLine.getQuantity().compareTo(BigDecimal.ZERO) <= 0) {
+ continue;
+ }
+ productReceiptLineMapper.insert(new MesWmProductReceiptLineDO()
+ .setReceiptId(receipt.getId())
+ .setItemId(produceLine.getItemId())
+ .setQuantity(produceLine.getQuantity())
+ .setBatchId(produceLine.getBatchId())
+ .setBatchCode(produceLine.getBatchCode())
+ .setQualityStatus(produceLine.getQualityStatus())
+ .setIsStock(Boolean.FALSE));
+ }
+ }
+ return receipt.getId();
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void submitStock(MesWmProductReceiptStockReqVO reqVO) {
+ // 鏍¢獙鍏ュ簱鍗曞瓨鍦ㄤ笖涓哄緟涓婃灦锛圓PPROVING锛�
+ MesWmProductReceiptDO receipt = validateProductReceiptExists(reqVO.getReceiptId());
+ if (ObjUtil.notEqual(MesWmProductReceiptStatusEnum.APPROVING.getStatus(), receipt.getStatus())) {
+ throw exception(WM_PRODUCT_RECPT_STATUS_ERROR);
+ }
+ List<MesWmProductReceiptStockReqVO.Row> rows = reqVO.getRows();
+ if (CollUtil.isEmpty(rows)) {
+ throw exception(WM_PRODUCT_RECPT_NO_LINE);
+ }
+ // 閫愯澶勭悊锛氭洿鏂版槸鍚﹀叆搴� + 閲嶅缓涓婃灦鏄庣粏
+ for (MesWmProductReceiptStockReqVO.Row row : rows) {
+ MesWmProductReceiptLineDO line = productReceiptLineMapper.selectById(row.getLineId());
+ if (line == null || !ObjUtil.equal(line.getReceiptId(), receipt.getId())) {
+ throw exception(WM_PRODUCT_RECPT_LINE_NOT_EXISTS);
+ }
+ Boolean isStock = Boolean.TRUE.equals(row.getIsStock());
+ productReceiptLineMapper.updateById(new MesWmProductReceiptLineDO()
+ .setId(line.getId()).setIsStock(isStock));
+ // 鍒犻櫎璇ヨ鏃ф槑缁嗭紝閲嶅缓鍒嗛厤
+ productReceiptDetailService.deleteProductReceiptDetailByLineId(line.getId());
+ if (!isStock) {
+ continue; // 鏈嬀閫夛細涓嶅垎閰嶏紝鎵ц鍏ュ簱鏃惰嚜鍔ㄨ繘涓嶅悎鏍间粨搴�
+ }
+ // 鍕鹃�夊叆搴擄細鍒嗛厤鏁伴噺涔嬪拰蹇呴』绛変簬琛屾敹璐ф暟閲�
+ List<MesWmProductReceiptStockReqVO.Detail> details = row.getDetails();
+ if (CollUtil.isEmpty(details)) {
+ throw exception(WM_PRODUCT_RECPT_STOCK_QUANTITY_MISMATCH);
+ }
+ BigDecimal sumDetailQty = details.stream()
+ .map(MesWmProductReceiptStockReqVO.Detail::getQuantity)
+ .filter(Objects::nonNull)
+ .reduce(BigDecimal.ZERO, BigDecimal::add);
+ if (line.getQuantity() == null || sumDetailQty.compareTo(line.getQuantity()) != 0) {
+ throw exception(WM_PRODUCT_RECPT_STOCK_QUANTITY_MISMATCH);
+ }
+ // 鏍¢獙浠撳簱灞傜骇鍏崇郴骞舵彃鍏ユ槑缁�
+ for (MesWmProductReceiptStockReqVO.Detail detail : details) {
+ areaService.validateWarehouseAreaExists(detail.getWarehouseId(), detail.getLocationId(), detail.getAreaId());
+ productReceiptDetailMapper.insert(new MesWmProductReceiptDetailDO()
+ .setLineId(line.getId())
+ .setReceiptId(receipt.getId())
+ .setItemId(line.getItemId())
+ .setQuantity(detail.getQuantity())
+ .setBatchId(line.getBatchId())
+ .setWarehouseId(detail.getWarehouseId())
+ .setLocationId(detail.getLocationId())
+ .setAreaId(detail.getAreaId()));
+ }
+ }
+ }
+
private void createTransactionList(MesWmProductReceiptDO receipt, List<MesWmProductReceiptDetailDO> details) {
// 1. 鏌ヨ铏氭嫙绾胯竟搴�
MesWmWarehouseDO virtualWarehouse = warehouseService.getWarehouseByCode(MesWmWarehouseDO.WIP_VIRTUAL_WAREHOUSE);
--
Gitblit v1.9.3