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; 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; 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())); // 产åºäº§åï¼æ¯æå¤éï¼ 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 = "æ¯å¦åå²ï¼true=æ¥å·¥æ¶æBOMèªå¨æ£æï¼false=ä¸èªå¨æ£æï¼", 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; 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')") 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; 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; } 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) 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; 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; 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 = "ä»åº/åºåº/åºä½åé æç»ï¼isStock=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; } } 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; 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; 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=æ¥å·¥å¿ 须填忰éï¼false=æ¥å·¥æ°éå 许为空 */ private Boolean needReportQuantity; /** * 夿³¨ */ private String remark; 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; /** * 客æ·ç¼å· 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; 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}ï¼æ¥å·¥äº§åºè¡å¸¦å ¥ï¼PASS/FAILï¼ */ private Integer qualityStatus; /** * æ¯å¦å ¥åº * * true=å·²å¾éå ¥åºå¹¶åé ä»åº/åºåº/åºä½ï¼false/空=æ§è¡å ¥åºæ¶é»è®¤è¿ä¸åæ ¼ä»åº */ private Boolean isStock; /** * 夿³¨ */ private String remark; 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 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 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 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) 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); } } 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 æ ¼å¼ï¼mes:pro:task:wait:remind:{taskId}:{level}:{waitStartTime} * VALUE æ°æ®æ ¼å¼ï¼1 */ String TASK_WAIT_REMIND = "mes:pro:task:wait:remind:"; /** * çäº§ä»»å¡æ¥å·¥åè®¡æ¶æéæ«æé¨é© * * KEY æ ¼å¼ï¼mes:pro:task:wait:remind:scan * VALUE æ°æ®æ ¼å¼ï¼1 */ String TASK_WAIT_REMIND_SCAN = "mes:pro:task:wait:remind:scan"; } 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)); } } 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, "æ¶è´§åç¶æä¸æ£ç¡®"); 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, "äº§åæ¶è´§æç»ä¸åå¨"); 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", "产åå ¥åºåç¼ç "); /** * è§å代ç 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(); } } 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 èªå¨æ£æï¼éåå²å·¥åºè·³è¿ï¼ç©æèµ°æå·¥é¢æ/ææï¼ // æ¥å·¥æ°é为空/0 æ¶è·³è¿å岿£æï¼é¿å 空æ°é误æ£çº¿è¾¹åº 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()) 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); } 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; } } 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(); } 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. ä¸ä¸ªç级ç¬ç«å¤å®ï¼Redis å»éï¼é¦æ¬¡æ è®°æåæåéï¼ 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; } } } 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); } } 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>ä» å è®¸å¾ ä¸æ¶ï¼APPROVINGï¼ç¶ææ§è¡ï¼å¾éå ¥åºçè¡è¦æ±åé æ°éä¹åçäºè¡æ¶è´§æ°éï¼ * æªå¾éå ¥åºçè¡ä¸åé æç»ï¼æ§è¡å ¥åºæ¶èªå¨è¿ä¸åæ ¼ä»åºã * * @param reqVO 䏿¶è¯·æ± */ void submitStock(MesWmProductReceiptStockReqVO reqVO); /** * æ ¡éªäº§åæ¶è´§ååå¨ä¸å¤äºå¯ç¼è¾ç¶æï¼è稿æå¾ 䏿¶ï¼ * * @param id ç¼å· 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) { // æ ¡éªå ¥åºååå¨ä¸ä¸ºå¾ 䏿¶ï¼APPROVINGï¼ 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);