From 8c65f898ed6e5b0c7a94c792c4562f6ec831dccd Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期六, 07 十一月 2026 08:50:53 +0800
Subject: [PATCH] feat: 完善质检、生产、销售、财务与人事模块并接入审批流程
---
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/feedback/MesProFeedbackServiceImpl.java | 920 +++++++++++++++++++++++++++++++++++++++++++++++++-------
1 files changed, 798 insertions(+), 122 deletions(-)
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 71f0852..ded280e 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
@@ -1,43 +1,82 @@
package cn.iocoder.yudao.module.mes.service.pro.feedback;
import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
-import cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkstationDO;
-import cn.iocoder.yudao.module.mes.controller.admin.pro.feedback.vo.MesProFeedbackPageReqVO;
-import cn.iocoder.yudao.module.mes.controller.admin.pro.feedback.vo.MesProFeedbackSaveReqVO;
+import cn.iocoder.yudao.module.mes.controller.admin.pro.feedback.vo.*;
+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.pro.MesProFeedbackTypeEnum;
+import cn.iocoder.yudao.module.mes.service.md.autocode.MesMdAutoCodeRecordService;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackAuxDO;
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.route.MesProRouteProductBomDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderBomDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderProcessDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemconsume.MesWmItemConsumeDO;
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.qc.ipqc.MesQcIpqcDO;
+import cn.iocoder.yudao.module.mes.dal.mysql.pro.feedback.MesProFeedbackAuxMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.pro.feedback.MesProFeedbackMapper;
+import cn.iocoder.yudao.module.mes.dal.mysql.qc.ipqc.MesQcIpqcMapper;
import cn.iocoder.yudao.module.mes.enums.pro.MesProFeedbackStatusEnum;
+import cn.iocoder.yudao.module.mes.enums.pro.MesProProcessBillingTypeEnum;
+import cn.iocoder.yudao.module.mes.enums.pro.MesProTaskStatusEnum;
+import cn.iocoder.yudao.module.mes.enums.qc.MesQcStatusEnum;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum;
-import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationService;
+import cn.iocoder.yudao.module.mes.service.qc.ipqc.MesQcIpqcService;
+import cn.iocoder.yudao.module.mes.service.qc.ipqc.MesQcIpqcWageFieldService;
+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.route.MesProRouteProductBomService;
import cn.iocoder.yudao.module.mes.service.pro.task.MesProTaskService;
+import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderBomService;
+import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderProcessService;
import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService;
+import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderParamRecordService;
+import cn.iocoder.yudao.module.mes.service.pro.wage.MesProFeedbackQualityResult;
+import cn.iocoder.yudao.module.mes.service.pro.wage.MesProFeedbackWageBreakdown;
+import cn.iocoder.yudao.module.mes.service.pro.wage.MesProFeedbackWageCalculator;
+import cn.iocoder.yudao.module.mes.service.pro.wage.MesProProcessWageRuleService;
+import cn.iocoder.yudao.module.mes.service.pro.worktime.MesProWorktimeService;
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.mdm.MesMdmItemService;
+import cn.iocoder.yudao.module.mes.dal.dataobject.mdm.MesMdmItemDO;
+import cn.iocoder.yudao.module.mdm.api.item.MdmItemBatchConfigApi;
+import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemBatchConfigRespDTO;
+import cn.iocoder.yudao.module.mes.api.mps.MesProMpsApi;
+import cn.iocoder.yudao.module.mes.api.mps.dto.MesProMpsRespDTO;
+import cn.iocoder.yudao.module.mes.api.event.ProductionFinishedEvent;
import jakarta.annotation.Resource;
+import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
@@ -45,23 +84,48 @@
/**
* MES 鐢熶骇鎶ュ伐 Service 瀹炵幇绫�
*
- * @author 鑺嬮亾婧愮爜
+ * @author 瓒呯骇绠$悊鍛�
*/
@Service
@Validated
+@lombok.extern.slf4j.Slf4j
public class MesProFeedbackServiceImpl implements MesProFeedbackService {
@Resource
+ private MesProFeedbackEquipmentService equipmentService;
+
+ @Resource
private MesProFeedbackMapper feedbackMapper;
+ @Resource
+ private MesProFeedbackAuxMapper feedbackAuxMapper;
+ @Resource
+ private MesProWorktimeService worktimeService;
+ @Resource
+ private MesProFeedbackWageCalculator wageCalculator;
+ @Resource
+ private MesProProcessWageRuleService wageRuleService;
+ @Resource
+ private MesQcIpqcMapper ipqcMapper;
+ @Resource
+ @Lazy // IPQC 瀹屾垚鍥炲啓鎶ュ伐锛岄伩鍏嶅惊鐜緷璧�
+ private MesQcIpqcService ipqcService;
+ @Resource
+ @Lazy // 杩囩▼妫�楠屽崟宸ヨ祫瀛楁鍊硷紙璐ㄦ濉啓锛夛紝閬垮厤涓� IPQC 涓绘湇鍔″惊鐜緷璧�
+ private MesQcIpqcWageFieldService ipqcWageFieldService;
@Resource
private MesProWorkOrderService workOrderService;
@Resource
+ @Lazy
+ private MesProWorkOrderParamRecordService paramRecordService;
+ @Resource
private MesProRouteProcessService routeProcessService;
@Resource
- private MesProRouteProductBomService routeProductBomService;
+ private MesProProcessService processService;
@Resource
- private MesMdWorkstationService workstationService;
+ private MesProWorkOrderBomService workOrderBomService;
+ @Resource
+ private MesProWorkOrderProcessService workOrderProcessService;
@Resource
@Lazy // 閬垮厤寰幆渚濊禆
private MesProTaskService taskService;
@@ -71,22 +135,45 @@
private MesWmProductProduceService productProduceService;
@Resource
private MesWmProductProduceLineService produceLineService;
+ @Resource
+ private MesProMpsApi mpsApi;
+ @Resource
+ private MesMdAutoCodeRecordService autoCodeRecordService;
+ @Resource
+ private ApplicationEventPublisher eventPublisher;
+ @Resource
+ private MesMdmItemService mdmItemService;
+ @Resource
+ private MdmItemBatchConfigApi itemBatchConfigApi;
+ @Resource
+ private cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi processInstanceApi;
@Override
+ @Transactional(rollbackFor = Exception.class)
public Long createFeedback(MesProFeedbackSaveReqVO createReqVO) {
// 1. 鏍¢獙
MesProTaskDO task = validateFeedbackData(createReqVO);
- // 2. 鎻掑叆锛堣嚜鍔ㄥ~鍏� itemId锛�
+ // 2. 鎻掑叆锛堜富閿敱鏁版嵁搴撹嚜澧烇紝蹇界暐璇锋眰涓彲鑳芥畫鐣欑殑 id锛涜嚜鍔ㄥ~鍏� itemId锛�
MesProFeedbackDO feedback = BeanUtils.toBean(createReqVO, MesProFeedbackDO.class)
+ .setId(null)
.setStatus(MesProFeedbackStatusEnum.PREPARE.getStatus())
.setItemId(task.getItemId());
+ // 2.1 璁$畻鏍囧噯宸ユ椂蹇収涓庤緟鍔╁伐鏃舵槑缁嗭紙鍙敤浜庤�冩牳瀵规瘮锛屽弬鏁扮己澶变笉闃绘柇鎶ュ伐锛�
+ List<MesProFeedbackAuxDO> auxList = worktimeService.calculateForFeedback(feedback, createReqVO.getAuxList());
+ // 2.2 鎸夊伐搴忚璐圭被鍨嬨�佸崟浠疯鍒欍�佽緟鍔╄钖笌宸ヨ祫瀛楁蹇収宸ヨ祫锛岃皝鎶ョ殑宸ュ氨璁板湪璋佸悕涓�
+ applyProcessBilling(feedback, processService.getProcess(task.getProcessId()), auxList);
feedbackMapper.insert(feedback);
+ saveFeedbackAux(feedback.getId(), auxList);
+ // 3. 淇濆瓨鍙傛暟鍊硷紱蹇呭~鍙傛暟鏍¢獙澶辫触鏃舵暣浣撳洖婊氾紝閬垮厤娈嬬暀鑽夌
+ saveParamValues(task.getId(), createReqVO.getParamValues());
return feedback.getId();
}
@Override
+ @Transactional(rollbackFor = Exception.class)
public void updateFeedback(MesProFeedbackSaveReqVO updateReqVO) {
+ equipmentService.lock();
// 1.1 鏍¢獙瀛樺湪 + 鑽夌鐘舵��
validateFeedbackStatusPrepare(updateReqVO.getId());
// 1.2 鏍¢獙涓氬姟鏁版嵁
@@ -95,16 +182,49 @@
// 2. 鏇存柊锛堣嚜鍔ㄥ~鍏� itemId锛�
MesProFeedbackDO updateObj = BeanUtils.toBean(updateReqVO, MesProFeedbackDO.class)
.setItemId(task.getItemId());
+ // 2.1 閲嶆柊璁$畻鏍囧噯宸ユ椂蹇収涓庤緟鍔╁伐鏃舵槑缁�
+ List<MesProFeedbackAuxDO> auxList = worktimeService.calculateForFeedback(updateObj, updateReqVO.getAuxList());
+ // 2.2 鎸夊伐搴忓綋鍓嶇殑璁¤垂绫诲瀷/鍗曚环瑙勫垯/杈呭姪璁¤柂/宸ヨ祫瀛楁閲嶇畻宸ヨ祫锛堣崏绋块樁娈靛厑璁告敼宸ユ椂/鏁伴噺锛�
+ applyProcessBilling(updateObj, processService.getProcess(task.getProcessId()), auxList);
feedbackMapper.updateById(updateObj);
+ feedbackAuxMapper.deleteByFeedbackId(updateReqVO.getId());
+ saveFeedbackAux(updateReqVO.getId(), auxList);
+ equipmentService.resetSnapshot(updateReqVO.getId());
+ // 3. 淇濆瓨鍙傛暟鍊硷紱蹇呭~鍙傛暟鏍¢獙澶辫触鏃舵暣浣撳洖婊�
+ saveParamValues(task.getId(), updateReqVO.getParamValues());
+ }
+
+ private void saveFeedbackAux(Long feedbackId, List<MesProFeedbackAuxDO> auxList) {
+ if (CollUtil.isEmpty(auxList)) {
+ return;
+ }
+ auxList.forEach(aux -> {
+ aux.setId(null).setFeedbackId(feedbackId);
+ feedbackAuxMapper.insert(aux);
+ });
+ }
+
+ private void saveParamValues(Long taskId, List<MesProFeedbackSaveReqVO.ParamValue> paramValues) {
+ // 鍗充娇鍙傛暟涓虹┖涔熻皟鐢ㄦ洿鏂帮紝浠ヤ究瑙﹀彂蹇呭~鍙傛暟鏍¢獙锛堜换鍔$粦瀹氫簡鍙傛暟鏃跺繀椤诲~鍐欙級
+ if (CollUtil.isEmpty(paramValues)) {
+ paramRecordService.updateTaskParamValues(taskId, null, null);
+ return;
+ }
+ paramRecordService.updateTaskParamValues(taskId,
+ paramValues.stream().map(MesProFeedbackSaveReqVO.ParamValue::getDetailId).toList(),
+ paramValues.stream().map(MesProFeedbackSaveReqVO.ParamValue::getValue).toList());
}
@Override
+ @Transactional(rollbackFor = Exception.class)
public void deleteFeedback(Long id) {
+ equipmentService.lock();
// 1. 鏍¢獙瀛樺湪 + 鑽夌鐘舵��
validateFeedbackStatusPrepare(id);
- // 2. 鍒犻櫎
+ // 2. 鍒犻櫎锛堣緟鍔╁伐鏃舵槑缁嗕竴骞跺垹闄わ級
feedbackMapper.deleteById(id);
+ feedbackAuxMapper.deleteByFeedbackId(id);
}
@Override
@@ -131,17 +251,95 @@
}
@Override
- public void submitFeedback(Long id) {
+ @Transactional(rollbackFor = Exception.class)
+ public void submitFeedback(MesProFeedbackSubmitReqVO reqVO) {
+ equipmentService.lock();
+ Long id = reqVO.getId();
// 1. 鏍¢獙瀛樺湪 + 鑽夌鐘舵��
- validateFeedbackStatusPrepare(id);
+ MesProFeedbackDO feedback = validateFeedbackStatusPrepare(id);
+ equipmentService.validateSubmission(feedback);
+ // 1.1 椤哄簭鎶ュ伐鏍¢獙锛氬悓涓�鐢熶骇宸ュ崟鍐咃紝涓婁竴閬撳伐搴忓繀椤诲凡鎶ュ伐瀹屾垚锛屽惁鍒欎笉鍏佽鎻愪氦
+ if (feedback.getTaskId() != null) {
+ MesProTaskDO submitTask = taskService.getTask(feedback.getTaskId());
+ if (submitTask != null) {
+ validatePreviousProcessFinished(submitTask);
+ }
+ }
- // 2. 鏇存柊鐘舵�佷负瀹℃壒涓紙鎶ュ伐浜哄拰鎶ュ伐鏃堕棿鐢辫〃鍗曚繚瀛樻椂纭畾锛屾彁浜や笉瑕嗙洊锛�
- feedbackMapper.updateById(new MesProFeedbackDO().setId(id)
- .setStatus(MesProFeedbackStatusEnum.APPROVING.getStatus()));
+ MesProWorkOrderDO workOrder = workOrderService.getWorkOrder(feedback.getWorkOrderId());
+
+ // 1.2 鎶ュ伐涓庤川妫�鍒嗙锛氭彁浜ゆ姤宸ヤ笉鍐嶈嚜鍔ㄧ敓鎴愯繃绋嬫楠屽崟锛岃川妫�鐢辫川妫�閮ㄩ棬鎵嬪伐寤哄崟
+ if (!processInstanceApi.isGlobalAutoApprove() && workOrder != null
+ && Boolean.TRUE.equals(workOrder.getFeedbackApprove())
+ && cn.hutool.core.util.StrUtil.isNotBlank(workOrder.getProcessDefinitionKey())) {
+ // 2. 鏇存柊鐘舵�佷负瀹℃壒涓紝骞跺惎鍔ㄦ祦绋�
+ String processInstanceId = processInstanceApi.createProcessInstance(cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId(),
+ new cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO()
+ .setProcessDefinitionKey(workOrder.getProcessDefinitionKey())
+ .setBusinessKey(String.valueOf(id)));
+
+ feedbackMapper.updateById(new MesProFeedbackDO().setId(id)
+ .setStatus(MesProFeedbackStatusEnum.APPROVING.getStatus())
+ .setProcessDefinitionKey(workOrder.getProcessDefinitionKey())
+ .setProcessInstanceId(processInstanceId));
+ } else {
+ // 2. 鍒ゆ柇杩欎釜鎶ュ伐闇�涓嶉渶瑕佹壒娆″彿
+ MesProFeedbackBatchCheckRespVO mesProFeedbackBatchCheckRespVO = checkBatchRequirements(id);
+ if (mesProFeedbackBatchCheckRespVO.getNeedBatchInput()) {
+ if (StrUtil.isBlank(reqVO.getLotNumber())) {
+ throw exception(MD_ITEM_BATCH_CONFIG_AT_LEAST_ONE_APPROVE_FLAG);
+ }
+ }
+
+ // 3. 鏇存柊鐘舵�佷负瀹℃壒涓紝鐒跺悗鐩存帴閫氳繃瀹℃壒
+ feedbackMapper.updateById(new MesProFeedbackDO().setId(id)
+ .setStatus(MesProFeedbackStatusEnum.APPROVING.getStatus()));
+ MesProFeedbackApproveReqVO approveReqVO = cn.iocoder.yudao.framework.common.util.object.BeanUtils.toBean(reqVO, MesProFeedbackApproveReqVO.class);
+ // 鑷姩瀹℃壒锛氳繃绋嬫楠屽崟鍒氱敓鎴愬皻鏈~鍐欙紝涓嶅仛璐ㄦ宸ヨ祫鍓嶇疆鏍¢獙锛堣川閲忚皟鏁寸暀绌猴級
+ approveFeedback(approveReqVO, false);
+ }
}
@Override
+ @Transactional(rollbackFor = Exception.class)
+ public void handleBpmProcessResult(Long id, Integer bpmResult,String reason) {
+ // 1. 鏌旀�ф牎楠岋細鑾峰彇鎶ュ伐鍗曞苟鍒ゆ柇鐘舵��
+ MesProFeedbackDO feedback = feedbackMapper.selectById(id);
+ if (feedback == null) {
+ log.warn("[handleBpmProcessResult] 鐢熶骇鎶ュ伐({}) 涓嶅瓨鍦�", id);
+ return;
+ }
+ if (!MesProFeedbackStatusEnum.APPROVING.getStatus().equals(feedback.getStatus())) {
+ log.warn("[handleBpmProcessResult] 鐢熶骇鎶ュ伐({}) 涓嶅浜庡鎵逛腑鐘舵�侊紝褰撳墠鐘舵�侊細{}", id, feedback.getStatus());
+ return;
+ }
+
+ // 2. 鏍规嵁瀹℃壒缁撴灉杩涜澶勭悊
+ if (cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum.APPROVE.getStatus().equals(bpmResult)) {
+ MesProFeedbackApproveReqVO reqVO = new MesProFeedbackApproveReqVO();
+ reqVO.setId(id);
+ // 鍒ゆ柇杩欎釜鎶ュ伐闇�涓嶉渶瑕佹壒娆″彿
+ MesProFeedbackBatchCheckRespVO mesProFeedbackBatchCheckRespVO = checkBatchRequirements(id);
+ if (mesProFeedbackBatchCheckRespVO.getNeedBatchInput()){
+ if (StrUtil.isBlank(reason)){
+ // 鎶ラ敊 璇峰湪瀹℃壒鎰忚閲屽~鍐欐壒娆″彿
+ throw exception(MD_ITEM_BATCH_CONFIG_AT_LEAST_ONE_APPROVE_FLAG);
+ }else {
+ BeanUtils.copyProperties(mesProFeedbackBatchCheckRespVO,reqVO);
+ reqVO.setLotNumber(reason);
+ }
+ }
+ approveFeedback(reqVO);
+ } else if (cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum.REJECT.getStatus().equals(bpmResult) ||
+ cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum.CANCEL.getStatus().equals(bpmResult)) {
+ rejectFeedback(id);
+ }
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
public void rejectFeedback(Long id) {
+ equipmentService.lock();
// 1. 鏍¢獙瀛樺湪 + 瀹℃壒涓姸鎬�
validateFeedbackStatusApproving(id);
@@ -152,9 +350,22 @@
@Override
@Transactional(rollbackFor = Exception.class)
- public boolean approveFeedback(Long id) {
+ public boolean approveFeedback(MesProFeedbackApproveReqVO reqVO) {
+ return approveFeedback(reqVO, true);
+ }
+
+ private boolean approveFeedback(MesProFeedbackApproveReqVO reqVO, boolean enforceQualityGate) {
+ equipmentService.lock();
// 1.1 鏍¢獙瀛樺湪 + 瀹℃壒涓姸鎬�
- MesProFeedbackDO feedback = validateFeedbackStatusApproving(id);
+ MesProFeedbackDO feedback = validateFeedbackStatusApproving(reqVO.getId());
+ equipmentService.validateApproval(feedback);
+ try (var equipmentScope = equipmentService.beginApproval(feedback)) {
+ return approveValidatedFeedback(reqVO, feedback, enforceQualityGate);
+ }
+ }
+
+ private boolean approveValidatedFeedback(MesProFeedbackApproveReqVO reqVO, MesProFeedbackDO feedback,
+ boolean enforceQualityGate) {
// 1.2 鏍¢獙鎶ュ伐鏁伴噺 > 0
if (feedback.getFeedbackQuantity() == null
|| feedback.getFeedbackQuantity().compareTo(BigDecimal.ZERO) <= 0) {
@@ -163,55 +374,46 @@
// 1.3 鏍¢獙浠诲姟鏈畬鎴�
taskService.validateTaskNotFinished(feedback.getTaskId());
- // 2.1 鏌ヨ宸ュ簭鐨勫叧閿伐搴忔爣璇� + 妫�楠屾爣璇嗭紙闇�鍦� uncheckQuantity 鏍¢獙涔嬪墠锛屽洜涓鸿川妫�宸ュ簭鍏佽 uncheckQuantity > 0锛�
- MesProRouteProcessDO routeProcess = routeProcessService.getRouteProcessByRouteIdAndProcessId(
- feedback.getRouteId(), feedback.getProcessId());
- boolean keyFlag = routeProcess != null && Boolean.TRUE.equals(routeProcess.getKeyFlag());
- boolean checkFlag = routeProcess != null && Boolean.TRUE.equals(routeProcess.getCheckFlag());
- // 2.2 闈炶川妫�宸ュ簭锛氫粛鏈夊緟妫�鏁伴噺鏃朵笉鑳藉鎵癸紙璐ㄦ宸ュ簭鐨� uncheckQuantity > 0 鏄甯哥姸鎬侊紝涓嶅仛鎷︽埅锛�
- if (!checkFlag
- && feedback.getUncheckQuantity() != null
- && feedback.getUncheckQuantity().compareTo(BigDecimal.ZERO) > 0) {
- throw exception(PRO_FEEDBACK_UNCHECK_QUANTITY_EXISTS, feedback.getUncheckQuantity());
- }
+ // 鎶ュ伐涓庤川妫�鍒嗙锛氭姤宸ュ鎵逛笉鍐嶄緷璧栥�屾槸鍚﹁川妫�銆嶅紑鍏筹紝璐ㄩ噺鏁版嵁鐢辫川妫�鍗曞洖濉��
+ // 1.4 宸ヨ祫鎸夋姤宸ユ椂鐐圭殑蹇収閲嶇畻锛岄伩鍏嶅伐搴忓畾棰濅簨鍚庤皟鏁村奖鍝嶅凡鎻愪氦鐨勬姤宸�
+ recalculateWageFromSnapshot(feedback, null);
- // 3. 鐗╂枡娑堣�楋細鍊掑啿宸ュ簭鎸� BOM 鑷姩鎵f枡锛涢潪鍊掑啿宸ュ簭璺宠繃锛堢墿鏂欒蛋鎵嬪伐棰嗘枡/鎶曟枡锛�
- boolean backflushFlag = routeProcess != null && Boolean.TRUE.equals(routeProcess.getBackflushFlag());
- if (backflushFlag) {
- // 3.1 鏍¢獙鍊掑啿宸ュ簭蹇呴』閰嶇疆 BOM 娑堣�楋紝閬垮厤闈欓粯涓嶆墸鏂�
- List<MesProRouteProductBomDO> boms = routeProductBomService.getRouteProductBomList(
- feedback.getRouteId(), feedback.getProcessId(), feedback.getItemId());
- if (CollUtil.isEmpty(boms)) {
- throw exception(PRO_FEEDBACK_BACKFLUSH_BOM_REQUIRED);
- }
- // 3.2 鐢熸垚娑堣�楄褰曞苟鎵ц鎵e噺锛圔OM 脳 鎶ュ伐鏁伴噺锛孎IFO 鎵g嚎杈瑰簱锛�
+ // 3. 鐗╂枡娑堣�楋細鍙鏈伐搴忛厤缃簡鐢熶骇 BOM 灏辫嚜鍔ㄦ墸鏂欙紝涓嶅啀渚濊禆鍊掑啿寮�鍏筹紱
+ // 鏈厤缃� BOM 鐨勫伐搴忚烦杩囷紙鐗╂枡璧版墜宸ラ鏂�/鎶曟枡锛夛紝涓嶉樆鏂姤宸ュ鎵�
+ List<MesProWorkOrderBomDO> boms = workOrderBomService.getWorkOrderBomListByWorkOrderIdAndProcessId(
+ feedback.getWorkOrderId(), feedback.getProcessId());
+ if (CollUtil.isNotEmpty(boms)) {
+ // 3.1 鐢熸垚娑堣�楄褰曞苟鎵ц鎵e噺锛堟湰宸ュ簭鎶曟枡鐢ㄩ噺姣斾緥 脳 鎶ュ伐鏁伴噺锛孎IFO 鎵g嚎杈瑰簱锛�
MesWmItemConsumeDO itemConsume = itemConsumeService.generateItemConsume(feedback);
if (itemConsume != null) {
itemConsumeService.finishItemConsume(itemConsume.getId());
}
}
- // 4. 鍏抽敭宸ュ簭锛氱敓鎴愪骇鍑哄崟锛屽苟鏍规嵁鏄惁闇�瑕佹楠屽喅瀹氬叆搴撴柟寮�
- if (keyFlag) {
- // 4.1 闇�瑕佹楠岋細鐢熸垚浜у嚭鍗曪紙璐ㄩ噺鐘舵��=寰呮楠岋級锛屾洿鏂版姤宸ョ姸鎬佷负寰呮楠岋紝绛夎川妫�瀹屾垚鍥炶皟鍚庡啀鍏ュ簱
- if (checkFlag) {
- // 瀹屾垚鏃跺洖璋冭锛歁esQcIpqcServiceImpl#finishIpqc 鈫� splitPendingAndFinishProduce + completeFeedbackFromIpqc
- productProduceService.generateProductProduce(feedback, true);
- feedbackMapper.updateById(new MesProFeedbackDO().setId(id)
- .setStatus(MesProFeedbackStatusEnum.UNCHECK.getStatus()));
- return false;
- }
- // 4.2 鏃犻渶妫�楠岋細鐢熸垚浜у嚭鍗曪紙鎸夊悎鏍�/涓嶅悎鏍兼媶琛岋級锛岀洿鎺ュ畬鎴愬叆搴擄紝骞舵洿鏂颁换鍔�/宸ュ崟鐨勫凡鐢熶骇鏁伴噺
- MesWmProductProduceDO produce = productProduceService.generateProductProduce(feedback, false);
- productProduceService.finishProductProduce(produce.getId());
- updateTaskAndWorkOrderByFeedback(feedback);
- }
+ // 鎶ュ伐涓庤川妫�鍒嗗紑锛氭姤宸ュ鎵瑰畬鎴愬嵆绱浜ч噺锛岃川閲忔暟閲忕敱璐ㄦ鍥炲~銆�
+ feedbackMapper.updateById(new MesProFeedbackDO().setId(reqVO.getId())
+ .setStatus(MesProFeedbackStatusEnum.UNCHECK.getStatus())
+ .setQualifiedQuantity(BigDecimal.ZERO)
+ .setUnqualifiedQuantity(BigDecimal.ZERO)
+ .setUncheckQuantity(feedback.getFeedbackQuantity())
+ .setBillingType(feedback.getBillingType())
+ .setWageRate(feedback.getWageRate())
+ .setWageRuleId(feedback.getWageRuleId())
+ .setWorkHours(feedback.getWorkHours())
+ .setWageAmount(feedback.getWageAmount())
+ .setAuxPaidHours(feedback.getAuxPaidHours())
+ .setAuxWageAmount(feedback.getAuxWageAmount())
+ .setQualityPassRate(feedback.getQualityPassRate())
+ .setQualityCoefficient(feedback.getQualityCoefficient())
+ .setQualityDeduction(feedback.getQualityDeduction())
+ .setWageBreakdown(feedback.getWageBreakdown()));
+ feedback.setQualifiedQuantity(BigDecimal.ZERO).setUnqualifiedQuantity(BigDecimal.ZERO);
+ productProduceService.generateProductProduce(feedback, true, reqVO);
- // 5. 闈炲叧閿伐搴� / 鍏抽敭闈炶川妫�宸ュ簭锛氱洿鎺ュ畬鎴愶紙娓呴浂 uncheckQuantity 闃叉 !key+check 鐣欒剰鏁版嵁锛�
- feedbackMapper.updateById(new MesProFeedbackDO().setId(id)
- .setStatus(MesProFeedbackStatusEnum.FINISHED.getStatus())
- .setUncheckQuantity(BigDecimal.ZERO));
- return true; // 宸插畬鎴�
+ // 姣忛亾宸ュ簭鍙疮璁℃湰浠诲姟锛涘伐鍗曚笌 MPS 鐢辨渶鍚庝竴閬撳伐搴忓洖鍐欙紝涓庡叧閿伐搴忔爣璁版棤鍏炽��
+ updateTaskAndWorkOrderByFeedback(feedback);
+ equipmentService.afterApproved(feedback);
+ return false; // 浜у嚭绛夊緟璐ㄦ锛屾姤宸ヤ骇閲忓凡绱
}
/**
@@ -221,30 +423,72 @@
* <li>浣跨敤鎶ュ伐鏁伴噺锛坒eedbackQuantity锛夛紝绱姞浠诲姟鍜屽伐鍗曠殑宸茬敓浜ф暟閲�</li>
* <li>浣跨敤浜у嚭鍗曡鎸� qualityStatus 鑱氬悎鐨勫悎鏍煎搧/涓嶅悎鏍煎搧鏁伴噺锛岀疮鍔犱换鍔$殑鍚堟牸鍝�/涓嶅悎鏍煎搧鏁伴噺
* 锛堜笉鐩存帴鐢� feedback 涓婄殑鏁伴噺锛岀‘淇濊川妫�鍥炶皟鍦烘櫙涓嬫暟閲忔潵婧愭纭級</li>
+ * isPush 鏄惁鎺ㄩ�佺敓浜у畬鎴愪簨浠�
* </ul>
*
* @param feedback 鎶ュ伐鍗�
*/
- private void updateTaskAndWorkOrderByFeedback(MesProFeedbackDO feedback) {
- // 1. 鏌ヨ璇ユ姤宸ュ崟鍏宠仈鐨勬墍鏈変骇鍑哄崟琛岋紝鎸夎川閲忕姸鎬佽仛鍚堟暟閲�
- BigDecimal qualifiedQty = BigDecimal.ZERO;
- BigDecimal unqualifiedQty = BigDecimal.ZERO;
- List<MesWmProductProduceLineDO> lines = produceLineService.getProductProduceLineListByFeedbackId(feedback.getId());
- for (MesWmProductProduceLineDO line : lines) {
- if (ObjUtil.equal(line.getQualityStatus(), MesWmQualityStatusEnum.FAIL.getStatus())) {
- unqualifiedQty = unqualifiedQty.add(line.getQuantity());
- }
- if (ObjUtil.equal(line.getQualityStatus(), MesWmQualityStatusEnum.PASS.getStatus())) {
- qualifiedQty = qualifiedQty.add(line.getQuantity());
- }
+ void updateTaskAndWorkOrderByFeedback(MesProFeedbackDO feedback) {
+ // 鎶ュ伐瀹℃壒鍙疮璁′骇閲忥紱鍚堟牸涓庝笉鍚堟牸鏁伴噺鍦ㄨ川妫�瀹屾垚鏃剁疮璁°��
+ taskService.updateProducedQuantity(feedback.getTaskId(),
+ feedback.getFeedbackQuantity(), BigDecimal.ZERO, BigDecimal.ZERO);
+ // 鍙湁鏈�鍚庝竴閬撳伐搴忕殑鎶ュ伐璁″叆宸ュ崟涓� MPS锛岄伩鍏嶄腑闂村伐搴忛噸澶嶇疮璁°��
+ MesProWorkOrderProcessDO lastProcess = workOrderProcessService.getLastWorkOrderProcessByWorkOrderId(feedback.getWorkOrderId());
+ if (lastProcess != null && ObjUtil.equal(lastProcess.getProcessId(), feedback.getProcessId())) {
+ workOrderService.updateProducedQuantity(feedback.getWorkOrderId(),
+ feedback.getFeedbackQuantity());
+ // MPS 鍙褰曢攢鍞鍗曢渶姹傞噺锛涘疄闄呰秴浜т粛鐢卞伐鍗曞拰浜у嚭鍏ュ簱璁板綍瀹屾暣淇濈暀銆�
+ allocateMpsProducedQuantity(feedback.getWorkOrderId(), feedback.getFeedbackQuantity());
}
- // 2. 鏇存柊浠诲姟鐨勫凡鐢熶骇/鍚堟牸/涓嶅悎鏍兼暟閲�
- taskService.updateProducedQuantity(feedback.getTaskId(),
- feedback.getFeedbackQuantity(), qualifiedQty, unqualifiedQty);
- // 3. 鏇存柊宸ュ崟鐨勫凡鐢熶骇鏁伴噺
- workOrderService.updateProducedQuantity(feedback.getWorkOrderId(),
- feedback.getFeedbackQuantity());
+
+ // 5. 妫�鏌ュ苟鍙戝竷鐢熶骇瀹屾垚浜嬩欢
+ checkAndPublishProductionFinished(feedback.getWorkOrderId());
+ }
+
+ /**
+ * 妫�鏌ュ苟鍙戝竷鐢熶骇瀹屾垚浜嬩欢
+ */
+ void checkAndPublishProductionFinished(Long workOrderId) {
+ Set<Long> newlyCompletedSaleOrderIds = new LinkedHashSet<>();
+ for (MesProMpsRespDTO mps : mpsApi.getMpsListByWorkOrderId(workOrderId)) {
+ if (mps.getSaleOrderId() == null
+ || ObjUtil.equal(mps.getStatus(), cn.iocoder.yudao.module.mes.enums.pro.MesProMpsStatusEnum.FINISHED.getStatus())
+ || ObjUtil.equal(mps.getStatus(), cn.iocoder.yudao.module.mes.enums.pro.MesProMpsStatusEnum.CANCELED.getStatus())) {
+ continue;
+ }
+ if (Boolean.TRUE.equals(mpsApi.checkAndFinishMps(mps.getId()))) {
+ newlyCompletedSaleOrderIds.add(mps.getSaleOrderId());
+ }
+ }
+ for (Long saleOrderId : newlyCompletedSaleOrderIds) {
+ if (mpsApi.checkSaleOrderProductionFinished(saleOrderId)) {
+ eventPublisher.publishEvent(new ProductionFinishedEvent(saleOrderId, java.time.LocalDateTime.now()));
+ }
+ }
+ }
+
+ void allocateMpsProducedQuantity(Long workOrderId, BigDecimal actualQuantity) {
+ BigDecimal remaining = actualQuantity;
+ for (MesProMpsRespDTO mps : mpsApi.getMpsListByWorkOrderId(workOrderId)) {
+ if (remaining.signum() <= 0) {
+ break;
+ }
+ if (ObjUtil.equal(mps.getStatus(), cn.iocoder.yudao.module.mes.enums.pro.MesProMpsStatusEnum.CANCELED.getStatus())) {
+ continue;
+ }
+ BigDecimal planned = mps.getQuantity() == null ? BigDecimal.ZERO : mps.getQuantity();
+ BigDecimal produced = mps.getQuantityProduced() == null ? BigDecimal.ZERO : mps.getQuantityProduced();
+ BigDecimal need = planned.subtract(produced).max(BigDecimal.ZERO);
+ BigDecimal assigned = remaining.min(need);
+ if (assigned.signum() > 0) {
+ mpsApi.updateMpsProducedQuantity(mps.getId(), assigned);
+ remaining = remaining.subtract(assigned);
+ }
+ }
+ if (remaining.signum() > 0) {
+ log.info("[allocateMpsProducedQuantity] 宸ュ崟({}) 瓒呬骇鏁伴噺({})淇濈暀鍦ㄤ骇鍑哄簱瀛橈紝涓嶅垎閰嶅埌閿�鍞鍗�", workOrderId, remaining);
+ }
}
// ==================== 鏍¢獙鏂规硶 ====================
@@ -277,66 +521,408 @@
/**
* 鏍¢獙鎶ュ伐鍗曠殑涓氬姟鏁版嵁锛堝垱寤� & 淇敼鍏辩敤锛�
*
+ * <p>浠ョ敓浜т换鍔′负鏍稿績锛氬墠绔彧闇�浼� taskId锛寃orkOrderId銆乺outeId銆乸rocessId銆乮temId銆亀orkstationId
+ * 浠庝换鍔¤嚜鍔ㄥ~鍏咃紝鏃犻渶鍓嶇閲嶅浼犲叆銆�
+ *
* @param reqVO 鎶ュ伐璇锋眰
* @return 鍏宠仈鐨勭敓浜т换鍔�
*/
private MesProTaskDO validateFeedbackData(MesProFeedbackSaveReqVO reqVO) {
- // 1. 鏍¢獙宸ヤ綔绔欏瓨鍦�
- MesMdWorkstationDO workstation = workstationService.validateWorkstationExists(reqVO.getWorkstationId());
- if (ObjUtil.notEqual(workstation.getProcessId(), reqVO.getProcessId())) {
- throw exception(PRO_WORKSTATION_PROCESS_MISMATCH);
+ // 1. 鏍¢獙浠诲姟瀛樺湪涓旀湭缁堟��
+ MesProTaskDO task = taskService.validateTaskNotFinished(reqVO.getTaskId());
+
+ // 鏍¢獙鍋滃伐鐘舵�佷笉鑳芥姤宸�
+ if (!MesProTaskStatusEnum.canFeedback(task.getStatus())) {
+ throw exception(PRO_FEEDBACK_CANNOT_CREATE_WHEN_PAUSED);
}
- // 2.1 鏍¢獙宸ヨ壓璺嚎 + 宸ュ簭閰嶇疆鏈夋晥
+ // 2. 浠庝换鍔¤嚜鍔ㄥ~鍏呭叧鑱斿瓧娈碉紙鍓嶇鏃犻渶浼犲叆锛屽悗绔粺涓�浠庝换鍔¤幏鍙栵級
+ reqVO.setWorkOrderId(task.getWorkOrderId());
+ reqVO.setRouteId(task.getRouteId());
+ reqVO.setProcessId(task.getProcessId());
+ reqVO.setItemId(task.getItemId());
+ reqVO.setWorkstationId(task.getWorkstationId());
+
+ // 3. 鏍¢獙宸ヨ壓璺嚎 + 宸ュ簭閰嶇疆鏈夋晥
MesProRouteProcessDO routeProcess = routeProcessService.getRouteProcessByRouteIdAndProcessId(
- reqVO.getRouteId(), reqVO.getProcessId());
+ task.getRouteId(), task.getProcessId());
if (routeProcess == null) {
throw exception(PRO_FEEDBACK_ROUTE_PROCESS_INVALID);
}
- // 2.2 鏍¢獙鏁伴噺
- boolean checkFlag = Boolean.TRUE.equals(routeProcess.getCheckFlag());
- if (checkFlag) {
- // 闇�瑕佹楠岋細鍙渶濉姤宸ユ暟閲忥紝涓斿繀椤� > 0
- if (reqVO.getFeedbackQuantity() == null
- || reqVO.getFeedbackQuantity().compareTo(BigDecimal.ZERO) <= 0) {
- throw exception(PRO_FEEDBACK_QUANTITY_MUST_POSITIVE);
- }
- } else {
- // 涓嶉渶妫�楠岋細闇�濉悎鏍煎搧 + 涓嶈壇鍝佹暟閲忥紝鍚堣 > 0
- BigDecimal qualified = ObjectUtil.defaultIfNull(reqVO.getQualifiedQuantity(), BigDecimal.ZERO);
- BigDecimal unqualified = ObjectUtil.defaultIfNull(reqVO.getUnqualifiedQuantity(), BigDecimal.ZERO);
- if (qualified.add(unqualified).compareTo(BigDecimal.ZERO) <= 0) {
- throw exception(PRO_FEEDBACK_QUALIFIED_UNQUALIFIED_REQUIRED);
- }
+ // 4.1 鏍¢獙鏁伴噺锛氭棤璁烘槸鍚﹂渶瑕佹楠岋紝鎶ュ伐鍙渶濉姤宸ユ暟閲忥紝涓斿繀椤� > 0
+ // 鍚堟牸鍝�/涓嶈壇鍝佹暟閲忕敱璐ㄦ纭畾銆�
+ if (reqVO.getFeedbackQuantity() == null
+ || reqVO.getFeedbackQuantity().compareTo(BigDecimal.ZERO) <= 0) {
+ throw exception(PRO_FEEDBACK_QUANTITY_MUST_POSITIVE);
}
- // 3. 鏍¢獙宸ュ崟宸茬‘璁�
- MesProWorkOrderDO workOrder = workOrderService.validateWorkOrderConfirmed(reqVO.getWorkOrderId());
- if (ObjUtil.notEqual(workOrder.getProductId(), reqVO.getItemId())) {
- throw exception(PRO_WORK_ORDER_PRODUCT_MISMATCH);
- }
-
- // 4. 鏍¢獙浠诲姟瀛樺湪涓旀湭缁堟�侊紙宸插畬鎴�/宸插彇娑堬級锛屽苟杩斿洖浠诲姟鐢ㄤ簬鍐椾綑 itemId
- MesProTaskDO task = taskService.validateTaskNotFinished(reqVO.getTaskId());
- validateTaskRelation(task, workstation, workOrder, reqVO);
return task;
}
- private void validateTaskRelation(MesProTaskDO task, MesMdWorkstationDO workstation,
- MesProWorkOrderDO workOrder, MesProFeedbackSaveReqVO reqVO) {
- if (ObjUtil.notEqual(task.getWorkOrderId(), workOrder.getId())) {
- throw exception(PRO_TASK_WORK_ORDER_MISMATCH);
+ /**
+ * 鎸夊伐搴忛厤缃妸璁¤垂绫诲瀷銆佸崟浠疯鍒欍�佽緟鍔╄钖笌宸ヨ祫瀛楁蹇収鍒版姤宸ュ崟锛屽苟璁$畻鏈鎶ュ伐宸ヨ祫銆�
+ *
+ * <p>鍙e緞锛�
+ * <ul>
+ * <li>璁℃椂锛氫富宸ヨ祫 = 鏈鎶ュ伐宸ユ椂锛堝皬鏃讹級脳 鍗曚环锛堝厓/灏忔椂锛夛紱鍗曚环澶т簬 0 鏃跺繀椤诲~鍐欏伐鏃�</li>
+ * <li>璁′欢锛氫富宸ヨ祫 = 鍚堟牸鏁伴噺锛堟湭濉椂鍙栨姤宸ユ暟閲忥級脳 鍗曚环锛堝厓/浠讹級</li>
+ * <li>杈呭姪锛氳緟鍔╂槑缁嗗垎閽熸暟 梅 60 脳 杈呭姪灏忔椂鍗曚环锛涘伐搴忚緟鍔╁皬鏃跺伐璧勫厹搴�</li>
+ * <li>瀛楁锛氱郴鏁拌繛涔樹綔鐢ㄤ簬锛堜富宸ヨ祫 + 杈呭姪宸ヨ祫锛夛紝鍐嶅彔鍔犲姞鍑忛噾棰濅笌璁′环鏁伴噺</li>
+ * </ul>
+ * 鍓嶇浼犲叆鐨� billingType/wageRate/wageAmount 涓�寰嬩互鍚庣璁$畻瑕嗙洊锛岄槻姝㈣绡℃敼銆�
+ * 鍗曚环涓� 0 琛ㄧず璇ュ伐搴忓皻鏈畾浠凤紝鎸� 0 鍏冭钖紝涓嶉樆鏂姤宸ワ紙鏍哥畻鏃跺彲鎸変汉璋冩暣锛夈��
+ */
+ private void applyProcessBilling(MesProFeedbackDO feedback, MesProProcessDO process,
+ List<MesProFeedbackAuxDO> auxList) {
+ if (process == null) {
+ throw exception(PRO_FEEDBACK_PROCESS_NOT_EXISTS, feedback.getProcessId());
}
- if (ObjUtil.notEqual(task.getWorkstationId(), workstation.getId())) {
- throw exception(PRO_TASK_WORKSTATION_MISMATCH);
+ MesProFeedbackWageBreakdown breakdown = wageCalculator.calculate(process,
+ feedback.getRouteId(), feedback.getMachineryId(), feedback.getWorkHours(),
+ feedback.getFeedbackQuantity(), feedback.getQualifiedQuantity(), auxList,
+ feedback.getFeedbackTime() != null ? feedback.getFeedbackTime().toLocalDate() : null,
+ getWageFieldValues(feedback.getId()));
+ applyWageBreakdown(feedback, breakdown);
+ }
+
+ /**
+ * 鎶婂伐璧勮绠楃粨鏋滃啓鍥炴姤宸ュ崟
+ */
+ private void applyWageBreakdown(MesProFeedbackDO feedback, MesProFeedbackWageBreakdown breakdown) {
+ if (CollUtil.isNotEmpty(breakdown.getWarnings())) {
+ log.warn("鎶ュ伐宸ヨ祫璁$畻鎻愮ず锛氭姤宸ュ崟[{}] {}", feedback.getCode(), String.join("锛�", breakdown.getWarnings()));
}
- if (ObjUtil.notEqual(task.getRouteId(), reqVO.getRouteId())
- || ObjUtil.notEqual(task.getProcessId(), reqVO.getProcessId())) {
- throw exception(PRO_TASK_ROUTE_PROCESS_MISMATCH);
+ feedback.setBillingType(breakdown.getBillingType())
+ .setWageRate(breakdown.getWageRate())
+ .setWageRuleId(breakdown.getWageRuleId())
+ .setWorkHours(breakdown.getWorkHours())
+ .setAuxPaidHours(breakdown.getAuxPaidHours())
+ .setAuxWageAmount(breakdown.getAuxWage())
+ .setWageAmount(breakdown.getFinalWage())
+ .setQualityPassRate(breakdown.getQuality() != null ? breakdown.getQuality().getPassRate() : null)
+ .setQualityCoefficient(breakdown.getQuality() != null ? breakdown.getQuality().getCoefficient() : null)
+ .setQualityDeduction(breakdown.getQuality() != null && breakdown.getQuality().getDeduction() != null
+ ? breakdown.getQuality().getDeduction() : BigDecimal.ZERO)
+ .setWageBreakdown(JsonUtils.toJsonString(breakdown));
+ }
+
+ /**
+ * 鍙栫涓�涓澶囩紪鍙蜂綔涓轰富璁′环璁惧锛堝揩鎹锋姤宸ョ殑 machineryIds 涓洪�楀彿鍒嗛殧锛�
+ */
+ private static Long firstMachineryId(String machineryIds) {
+ if (StrUtil.isBlank(machineryIds)) {
+ return null;
}
- if (ObjUtil.notEqual(task.getItemId(), reqVO.getItemId())) {
- throw exception(PRO_TASK_ITEM_MISMATCH);
+ for (String part : machineryIds.split(",")) {
+ String trimmed = part.trim();
+ if (StrUtil.isBlank(trimmed)) {
+ continue;
+ }
+ try {
+ return Long.parseLong(trimmed);
+ } catch (NumberFormatException ex) {
+ return null;
+ }
}
+ return null;
+ }
+
+ /**
+ * 瀹℃壒鏃舵寜鎶ュ伐鏃剁偣鐨勫揩鐓ч噸绠楀伐璧勶紝閬垮厤宸ュ簭閰嶇疆浜嬪悗璋冩暣褰卞搷宸叉彁浜ょ殑鎶ュ伐銆�
+ * 鍘嗗彶鏁版嵁锛堟棤宸ヨ祫鎷嗗垎蹇収锛夊垯鎸夊綋鍓嶅伐搴忛厤缃ˉ榻愩��
+ *
+ * @param qualityResult 杩囩▼妫�楠屾眹鎬伙紱绌�=闈炶川妫�宸ュ簭鎴栬嚜鍔ㄥ鎵癸紝涓嶅仛璐ㄩ噺璋冩暣
+ */
+ private void recalculateWageFromSnapshot(MesProFeedbackDO feedback, MesProFeedbackQualityResult qualityResult) {
+ BigDecimal qualifiedQuantity = qualityResult != null
+ ? qualityResult.qualifiedQuantity() : feedback.getQualifiedQuantity();
+ // 璐ㄦ閮ㄩ棬鍦ㄨ繃绋嬫楠屽崟涓婂~鍐欑殑宸ヨ祫瀛楁鍊硷紝浼樺厛浜庡伐搴忓浐瀹氬��
+ Map<String, String> wageFieldValues = getWageFieldValues(feedback.getId());
+ MesProFeedbackWageBreakdown breakdown;
+ if (StrUtil.isBlank(feedback.getWageBreakdown())) {
+ breakdown = wageCalculator.calculate(processService.getProcess(feedback.getProcessId()),
+ feedback.getRouteId(), feedback.getMachineryId(), feedback.getWorkHours(),
+ feedback.getFeedbackQuantity(), qualifiedQuantity,
+ feedback.getId() != null ? feedbackAuxMapper.selectListByFeedbackId(feedback.getId())
+ : Collections.emptyList(),
+ feedback.getFeedbackTime() != null ? feedback.getFeedbackTime().toLocalDate() : null,
+ wageFieldValues);
+ } else {
+ breakdown = wageCalculator.fromSnapshot(feedback, qualifiedQuantity, wageFieldValues);
+ }
+ if (qualityResult != null) {
+ wageCalculator.applyQuality(breakdown, feedback.getProcessId(), feedback.getRouteId(),
+ qualityResult.passRate(), qualityResult.unqualifiedQuantity());
+ }
+ applyWageBreakdown(feedback, breakdown);
+ }
+
+ /**
+ * 鍙栨姤宸ュ崟鍏宠仈鐨勮繃绋嬫楠屽崟涓婅川妫�濉啓鐨勫伐璧勫瓧娈靛��
+ *
+ * <p>鍚屼竴鎶ュ伐鍗曞瓨鍦ㄥ寮犳楠屽崟鏃讹紝宸插畬鎴愮殑浼樺厛锛涘瓧娈垫湭濉啓鏃朵笉鏀惧叆 Map锛岃绠楁椂鍥為��宸ュ簭鍥哄畾鍊笺��
+ */
+ private Map<String, String> getWageFieldValues(Long feedbackId) {
+ if (feedbackId == null) {
+ return Collections.emptyMap();
+ }
+ List<MesQcIpqcDO> ipqcList = ipqcMapper.selectListBySourceDoc(
+ MesBizTypeConstants.PRO_FEEDBACK, feedbackId);
+ if (CollUtil.isEmpty(ipqcList)) {
+ return Collections.emptyMap();
+ }
+ List<Long> ipqcIds = new ArrayList<>(ipqcList.size());
+ ipqcList.stream()
+ .filter(ipqc -> ObjUtil.equal(ipqc.getStatus(), MesQcStatusEnum.FINISHED.getStatus()))
+ .map(MesQcIpqcDO::getId).forEach(ipqcIds::add);
+ ipqcList.stream()
+ .filter(ipqc -> !ObjUtil.equal(ipqc.getStatus(), MesQcStatusEnum.FINISHED.getStatus()))
+ .map(MesQcIpqcDO::getId).forEach(ipqcIds::add);
+ return ipqcWageFieldService.getValueMapByIpqcIds(ipqcIds);
+ }
+
+ /**
+ * 鏍¢獙璐ㄦ宸ュ簭鐨勮繃绋嬫楠屽凡瀹屾垚锛屽苟姹囨�诲悎鏍肩巼涓庝笉鍚堟牸鏁伴噺
+ */
+ private MesProFeedbackQualityResult validateAndGetQualityResult(MesProFeedbackDO feedback) {
+ MesProProcessDO process = processService.getProcess(feedback.getProcessId());
+ String processName = process != null ? process.getName() : "ID=" + feedback.getProcessId();
+ List<MesQcIpqcDO> finishedList = ipqcMapper.selectListBySourceDoc(
+ MesBizTypeConstants.PRO_FEEDBACK, feedback.getId()).stream()
+ .filter(ipqc -> ObjUtil.equal(ipqc.getStatus(), MesQcStatusEnum.FINISHED.getStatus()))
+ .toList();
+ if (CollUtil.isEmpty(finishedList)) {
+ throw new ServiceException(PRO_FEEDBACK_QUALITY_INSPECTION_NOT_FINISHED.getCode(),
+ StrUtil.format("宸ュ簭[{}]涓鸿川妫�宸ュ簭锛屾姤宸ュ崟[{}]灏氭湭瀹屾垚杩囩▼妫�楠岋紱"
+ + "璇峰埌銆岃川閲忕鐞�-杩囩▼妫�楠屻�嶅畬鎴愭楠屽悗鍐嶅鎵�",
+ processName, feedback.getCode()));
+ }
+ BigDecimal checkQuantity = BigDecimal.ZERO;
+ BigDecimal qualifiedQuantity = BigDecimal.ZERO;
+ BigDecimal unqualifiedQuantity = BigDecimal.ZERO;
+ for (MesQcIpqcDO ipqc : finishedList) {
+ checkQuantity = checkQuantity.add(ObjectUtil.defaultIfNull(ipqc.getCheckQuantity(), BigDecimal.ZERO));
+ qualifiedQuantity = qualifiedQuantity.add(ObjectUtil.defaultIfNull(ipqc.getQualifiedQuantity(), BigDecimal.ZERO));
+ unqualifiedQuantity = unqualifiedQuantity.add(
+ ObjectUtil.defaultIfNull(ipqc.getUnqualifiedQuantity(), BigDecimal.ZERO));
+ }
+ if (checkQuantity.signum() <= 0) {
+ throw new ServiceException(PRO_FEEDBACK_QUALITY_INSPECTION_NOT_FINISHED.getCode(),
+ StrUtil.format("宸ュ簭[{}]鐨勮繃绋嬫楠屽崟[{}]妫�娴嬫暟閲忎负 0锛屾棤娉曡绠楀悎鏍肩巼锛�"
+ + "璇峰湪銆岃川閲忕鐞�-杩囩▼妫�楠屻�嶈ˉ褰曟娴嬫暟閲忓悗鍐嶅鎵�",
+ processName, finishedList.get(0).getCode()));
+ }
+ BigDecimal passRate = qualifiedQuantity.multiply(BigDecimal.valueOf(100))
+ .divide(checkQuantity, 2, RoundingMode.HALF_UP);
+ return new MesProFeedbackQualityResult(passRate, checkQuantity, qualifiedQuantity, unqualifiedQuantity);
+ }
+
+ /**
+ * 椤哄簭鎶ュ伐鏍¢獙锛氬悓涓�鐢熶骇宸ュ崟鍐咃紝涓婁竴閬撳伐搴忓繀椤诲凡鎶ュ伐瀹屾垚锛屽惁鍒欎笉鍏佽鎶ユ湰閬撳伐搴�
+ *
+ * <p>鍒ゅ畾鍙e緞锛氫笂涓�閬撳伐搴忕殑鏈夋晥浠诲姟锛堟湭鍙栨秷锛夌疮璁″鎵归�氳繃鐨勪骇閲忓凡杈惧埌鍏舵帓浜ф暟閲忥紝
+ * 鍗充换涓�鏈夋晥浠诲姟 produced_quantity < quantity 閮借涓烘湭瀹屾垚骞舵嫤鎴��
+ * 涔嬫墍浠ヤ笉渚濊禆浠诲姟 status锛氫换鍔$姸鎬佸彧鏈夋暣鍗曞畬宸�/鍙栨秷鏃舵墠缃粓鎬�
+ * 锛坒inishTaskByOrderId / cancelTaskByOrderId锛夛紝鏃犳硶浣滀负"鍗曢亾宸ュ簭宸插畬鎴�"鐨勪緷鎹紝
+ * produced_quantity 鏄敮涓�瀹炴椂鍙潬鐨勮繘搴︿俊鍙枫��
+ *
+ * @param task 褰撳墠寰呮姤宸ヤ换鍔�
+ */
+ private void validatePreviousProcessFinished(MesProTaskDO task) {
+ if (task == null || task.getWorkOrderId() == null || task.getProcessId() == null) {
+ return; // 鏁版嵁涓嶅畬鏁达紝鏃犳硶鎺ㄥ椤哄簭锛屾斁琛�
+ }
+ // 1. 褰撳墠浠诲姟鎵�灞炲伐搴忓湪宸ュ崟宸ュ簭閾撅紙鎸� sort 鍗囧簭锛変腑鐨勪綅缃�
+ List<MesProWorkOrderProcessDO> processes = workOrderProcessService
+ .getWorkOrderProcessListByWorkOrderId(task.getWorkOrderId());
+ if (CollUtil.isEmpty(processes)) {
+ return; // 宸ュ崟鏈厤缃伐搴忥紝鏀捐
+ }
+ // 鎷疯礉鍚庡啀鎺掑簭锛岄伩鍏嶄慨鏀硅皟鐢ㄦ柟杩斿洖鐨勶紙鍙兘鏄笉鍙彉锛夐泦鍚�
+ processes = new ArrayList<>(processes);
+ processes.sort(Comparator.comparing(MesProWorkOrderProcessDO::getSort,
+ Comparator.nullsLast(Comparator.naturalOrder())));
+ int index = -1;
+ for (int i = 0; i < processes.size(); i++) {
+ if (ObjUtil.equal(processes.get(i).getProcessId(), task.getProcessId())) {
+ index = i;
+ break;
+ }
+ }
+ if (index <= 0) {
+ return; // 褰撳墠宸ュ簭涓嶅湪閾句腑锛堟暟鎹紓甯革紝鏀捐锛夋垨鏈韩灏辨槸绗竴閬撳伐搴忥紝鏃犻渶鍓嶇疆
+ }
+ Long prevProcessId = processes.get(index - 1).getProcessId();
+ if (prevProcessId == null) {
+ return;
+ }
+ // 2. 涓婁竴閬撳伐搴忕殑鏈夋晥浠诲姟锛堟帓闄ゅ凡鍙栨秷锛夌疮璁″緟瀹屾垚閲�
+ List<MesProTaskDO> prevTasks = taskService.getTaskListByWorkOrderIds(
+ Collections.singletonList(task.getWorkOrderId())).stream()
+ .filter(t -> ObjUtil.equal(t.getProcessId(), prevProcessId))
+ .filter(t -> !ObjUtil.equal(t.getStatus(), MesProTaskStatusEnum.CANCELED.getStatus()))
+ .toList();
+ if (CollUtil.isEmpty(prevTasks)) {
+ return; // 涓婁竴閬撳伐搴忔棤鏈夋晥鐢熶骇浠诲姟锛堝惈鍏ㄩ儴鍙栨秷锛夛紝瑙嗕负鏃犲墠缃姹傦紝鏀捐
+ }
+ BigDecimal remaining = BigDecimal.ZERO;
+ for (MesProTaskDO prevTask : prevTasks) {
+ BigDecimal qty = prevTask.getQuantity() == null ? BigDecimal.ZERO : prevTask.getQuantity();
+ BigDecimal produced = prevTask.getProducedQuantity() == null ? BigDecimal.ZERO : prevTask.getProducedQuantity();
+ if (produced.compareTo(qty) < 0) {
+ remaining = remaining.add(qty.subtract(produced));
+ }
+ }
+ if (remaining.compareTo(BigDecimal.ZERO) <= 0) {
+ return; // 涓婁竴閬撳伐搴忓凡鍏ㄩ儴鎶ユ弧锛屾斁琛�
+ }
+ // 3. 鎷︽埅骞舵彁绀轰笂涓�閬撳伐搴忓悕绉颁笌鍓╀綑寰呮姤鏁伴噺
+ MesProProcessDO prevProcess = processService.getProcess(prevProcessId);
+ String prevProcessName = prevProcess != null ? prevProcess.getName() : String.valueOf(prevProcessId);
+ throw exception(PRO_FEEDBACK_PREV_PROCESS_NOT_FINISHED, prevProcessName, remaining);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public MesProFeedbackQuickCreateRespVO quickCreateFeedback(MesProFeedbackQuickCreateReqVO reqVO) {
+ equipmentService.lock();
+ // 1. 鏍¢獙浠诲姟瀛樺湪涓旈潪缁堟��
+ MesProTaskDO task = taskService.validateTaskNotFinished(reqVO.getTaskId());
+
+ // 鏍¢獙鍋滃伐鐘舵�佷笉鑳芥姤宸�
+ if (!MesProTaskStatusEnum.canFeedback(task.getStatus())) {
+ throw exception(PRO_FEEDBACK_CANNOT_CREATE_WHEN_PAUSED);
+ }
+ // 1.1 椤哄簭鎶ュ伐鏍¢獙锛氬悓涓�鐢熶骇宸ュ崟鍐咃紝涓婁竴閬撳伐搴忓繀椤诲凡鎶ュ伐瀹屾垚锛屽惁鍒欎笉鍏佽鎶ュ伐
+ validatePreviousProcessFinished(task);
+
+ // 2. 鏍¢獙宸ヨ壓璺嚎宸ュ簭閰嶇疆
+ MesProRouteProcessDO routeProcess = routeProcessService.getRouteProcessByRouteIdAndProcessId(
+ task.getRouteId(), task.getProcessId());
+ 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);
+ }
+ // 4. 鏋勫缓鎶ュ伐DO
+ MesProFeedbackDO feedback = new MesProFeedbackDO()
+ .setCode(autoCodeRecordService.generateAutoCode(MesMdAutoCodeRuleCodeEnum.PRO_FEEDBACK_CODE.getCode()))
+ .setType(MesProFeedbackTypeEnum.SELF.getType())
+ .setWorkstationId(task.getWorkstationId())
+ .setRouteId(task.getRouteId())
+ .setProcessId(task.getProcessId())
+ .setWorkOrderId(task.getWorkOrderId())
+ .setTaskId(reqVO.getTaskId())
+ .setItemId(task.getItemId())
+ .setFeedbackQuantity(reqVO.getFeedbackQuantity())
+ .setWorkHours(reqVO.getWorkHours())
+ .setScheduledQuantity(task.getQuantity())
+ .setFeedbackTime(reqVO.getFeedbackTime() != null ? reqVO.getFeedbackTime() : LocalDateTime.now())
+ .setMachineryIds(reqVO.getMachineryIds())
+ .setMachineryId(firstMachineryId(reqVO.getMachineryIds()))
+ .setStatus(MesProFeedbackStatusEnum.APPROVING.getStatus())
+ .setRemark(reqVO.getRemark());
+ // 4.1 蹇嵎鎶ュ伐榛樿娌℃湁宸ユ椂褰曞叆鍏ュ彛锛氳鏃惰钖笖宸插畾浠风殑宸ュ簭锛屽繀椤绘樉寮忎紶宸ユ椂锛�
+ // 鎵归噺鎶ュ伐绛変笉浼犲伐鏃剁殑鍏ュ彛浼氬緱鍒板彲鎵ц鐨勬彁绀猴紝寮曞鍒版姤宸ラ〉闈㈠~鍐�
+ MesProProcessDO process = processService.getProcess(task.getProcessId());
+ if (process == null) {
+ throw exception(PRO_FEEDBACK_PROCESS_NOT_EXISTS, task.getProcessId());
+ }
+ BigDecimal wageRate = ObjectUtil.defaultIfNull(wageRuleService.resolve(task.getProcessId(), task.getRouteId(),
+ feedback.getMachineryId(), process.getBillingType(), LocalDate.now()).getWageRate(), BigDecimal.ZERO);
+ if (MesProProcessBillingTypeEnum.TIME == MesProProcessBillingTypeEnum.valueOfType(process.getBillingType())
+ && wageRate.signum() > 0 && reqVO.getWorkHours() == null) {
+ throw exception(PRO_FEEDBACK_TIME_BILLING_QUICK_UNSUPPORTED, process.getName());
+ }
+ // 4.2 蹇嵎鎶ュ伐娌℃湁杈呭姪椤瑰叆鍙o紝浠呮寜鐢熶骇闀垮害锛堟姤宸ュ崟浣嶄负绫虫椂锛夎绠楁爣鍑嗗伐鏃跺揩鐓�
+ List<MesProFeedbackAuxDO> auxList = worktimeService.calculateForFeedback(feedback, null);
+ // 4.3 鎸夊伐搴忚璐圭被鍨嬨�佸崟浠疯鍒欎笌宸ヨ祫瀛楁蹇収骞惰绠楁湰娆℃姤宸ュ伐璧�
+ applyProcessBilling(feedback, process, auxList);
+ equipmentService.validateSubmission(feedback);
+ feedbackMapper.insert(feedback);
+ saveFeedbackAux(feedback.getId(), auxList);
+ saveParamValues(task.getId(), reqVO.getParamValues());
+ // 5. 杩斿洖缁撴灉
+ MesProWorkOrderDO workOrder = workOrderService.getWorkOrder(task.getWorkOrderId());
+ // 5.1 鎶ュ伐鍚庡墿浣欏彲鎶ュ伐鏁伴噺 = 鎺掍骇 - 宸茬敓浜� - 鍦ㄩ�旓紙鍚湰娆℃姤宸ワ級
+ BigDecimal reportable = task.getQuantity() == null ? BigDecimal.ZERO : task.getQuantity();
+ BigDecimal produced = task.getProducedQuantity() == null ? BigDecimal.ZERO : task.getProducedQuantity();
+ BigDecimal inTransit = BigDecimal.ZERO;
+ for (MesProFeedbackDO fb : getFeedbackListByTaskId(task.getId())) {
+ if (!ObjUtil.equal(fb.getStatus(), MesProFeedbackStatusEnum.FINISHED.getStatus())
+ && !ObjUtil.equal(fb.getStatus(), MesProFeedbackStatusEnum.UNCHECK.getStatus())) {
+ inTransit = inTransit.add(fb.getFeedbackQuantity() == null ? BigDecimal.ZERO : fb.getFeedbackQuantity());
+ }
+ }
+ BigDecimal reportableQuantity = reportable.subtract(produced).subtract(inTransit).max(BigDecimal.ZERO);
+ return new MesProFeedbackQuickCreateRespVO()
+ .setFeedbackId(feedback.getId())
+ .setTaskStatus(task.getStatus())
+ .setWorkOrderStatus(workOrder != null ? workOrder.getStatus() : null)
+ .setReportableQuantity(reportableQuantity);
+ }
+
+ @Override
+ public MesProFeedbackWageBreakdown previewWage(MesProFeedbackWagePreviewReqVO reqVO) {
+ MesProTaskDO task = taskService.getTask(reqVO.getTaskId());
+ if (task == null) {
+ throw exception(PRO_TASK_NOT_EXISTS);
+ }
+ MesProProcessDO process = processService.getProcess(task.getProcessId());
+ if (process == null) {
+ throw exception(PRO_FEEDBACK_PROCESS_NOT_EXISTS, task.getProcessId());
+ }
+ // 鍊熺敤涓�涓复鏃舵姤宸ュ崟瀵硅薄澶嶇敤鏍囧噯宸ユ椂/杈呭姪宸ユ椂璁$畻
+ MesProFeedbackDO temp = new MesProFeedbackDO()
+ .setTaskId(task.getId())
+ .setRouteId(task.getRouteId())
+ .setProcessId(task.getProcessId())
+ .setWorkstationId(task.getWorkstationId())
+ .setMachineryId(reqVO.getMachineryId())
+ .setWorkHours(reqVO.getWorkHours())
+ .setFeedbackQuantity(reqVO.getFeedbackQuantity())
+ .setQualifiedQuantity(reqVO.getQualifiedQuantity())
+ .setProductionLength(reqVO.getProductionLength());
+ List<MesProFeedbackAuxDO> auxList = worktimeService.calculateForFeedback(temp, reqVO.getAuxList());
+ return wageCalculator.calculate(process, task.getRouteId(), reqVO.getMachineryId(),
+ reqVO.getWorkHours(), reqVO.getFeedbackQuantity(), reqVO.getQualifiedQuantity(), auxList,
+ reqVO.getFeedbackDate());
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public MesProFeedbackBatchCreateRespVO batchCreateFeedback(MesProFeedbackBatchCreateReqVO reqVO) {
+ List<MesProFeedbackBatchCreateRespVO.BatchResult> results = new java.util.ArrayList<>();
+ int successCount = 0;
+ int failCount = 0;
+ for (Long taskId : reqVO.getTaskIds()) {
+ try {
+ MesProFeedbackQuickCreateReqVO singleReq = new MesProFeedbackQuickCreateReqVO();
+ singleReq.setTaskId(taskId);
+ singleReq.setFeedbackQuantity(reqVO.getFeedbackQuantity());
+ singleReq.setFeedbackTime(reqVO.getFeedbackTime());
+ singleReq.setRemark(reqVO.getRemark());
+ MesProFeedbackQuickCreateRespVO resp = quickCreateFeedback(singleReq);
+ results.add(new MesProFeedbackBatchCreateRespVO.BatchResult()
+ .setTaskId(taskId)
+ .setSuccess(true)
+ .setFeedbackId(resp.getFeedbackId()));
+ successCount++;
+ } catch (Exception e) {
+ results.add(new MesProFeedbackBatchCreateRespVO.BatchResult()
+ .setTaskId(taskId)
+ .setSuccess(false)
+ .setErrorMsg(e.getMessage()));
+ failCount++;
+ }
+ }
+ return new MesProFeedbackBatchCreateRespVO()
+ .setSuccessCount(successCount)
+ .setFailCount(failCount)
+ .setResults(results);
}
@Override
@@ -345,7 +931,10 @@
BigDecimal qualifiedQty, BigDecimal unqualifiedQty,
BigDecimal laborScrapQty, BigDecimal materialScrapQty, BigDecimal otherScrapQty) {
// 1. 鏍¢獙鎶ュ伐鍗曞瓨鍦ㄤ笖涓哄緟妫�楠岀姸鎬�
- MesProFeedbackDO feedback = validateFeedbackExists(feedbackId);
+ MesProFeedbackDO feedback = feedbackMapper.selectByIdForUpdate(feedbackId);
+ if (feedback == null) {
+ throw exception(PRO_FEEDBACK_NOT_EXISTS);
+ }
if (ObjUtil.notEqual(feedback.getStatus(), MesProFeedbackStatusEnum.UNCHECK.getStatus())) {
throw exception(PRO_FEEDBACK_NOT_UNCHECK);
}
@@ -363,9 +952,96 @@
.setOtherScrapQuantity(otherScrapQty)
.setStatus(MesProFeedbackStatusEnum.FINISHED.getStatus()));
- // 4. 鏇存柊浠诲姟/宸ュ崟鐨勫凡鐢熶骇鏁伴噺
+ // 4. 浜ч噺宸插湪鎶ュ伐瀹℃壒鏃剁疮璁★紝姝ゅ鍙洖濉川閲忔暟閲忋��
feedback.setQualifiedQuantity(qualifiedQty).setUnqualifiedQuantity(unqualifiedQty);
- updateTaskAndWorkOrderByFeedback(feedback);
+ taskService.updateQualityQuantity(feedback.getTaskId(),
+ ObjectUtil.defaultIfNull(qualifiedQty, BigDecimal.ZERO),
+ ObjectUtil.defaultIfNull(unqualifiedQty, BigDecimal.ZERO));
+ //5.妫�鏌ュ苟鍙戝竷鐢熶骇瀹屾垚浜嬩欢
+ checkAndPublishProductionFinished(feedback.getWorkOrderId());
+ checkAndAutoFinishAfterLastQc(feedback);
+ }
+
+ void checkAndAutoFinishAfterLastQc(MesProFeedbackDO feedback) {
+ MesProWorkOrderProcessDO lastProcess = workOrderProcessService
+ .getLastWorkOrderProcessByWorkOrderId(feedback.getWorkOrderId());
+ if (lastProcess == null || !ObjUtil.equal(lastProcess.getProcessId(), feedback.getProcessId())) {
+ return;
+ }
+ // 鍚屼竴宸ュ崟鐨勬湯閬撴姤宸ュ叏閮ㄥ畬鎴愯川妫�鍚庯紝鎵嶅厑璁哥骇鑱斿畬鎴愬伐鍗曚换鍔°��
+ Long pendingCount = feedbackMapper.selectCount(new LambdaQueryWrapperX<MesProFeedbackDO>()
+ .eq(MesProFeedbackDO::getWorkOrderId, feedback.getWorkOrderId())
+ .eq(MesProFeedbackDO::getProcessId, feedback.getProcessId())
+ .ne(MesProFeedbackDO::getStatus, MesProFeedbackStatusEnum.FINISHED.getStatus()));
+ if (pendingCount == 0) {
+ workOrderService.checkAndAutoFinishWorkOrder(feedback.getWorkOrderId());
+ }
+ }
+
+ @Override
+ public MesProFeedbackBatchCheckRespVO checkBatchRequirements(Long feedbackId) {
+ MesProFeedbackDO feedback = validateFeedbackExists(feedbackId);
+ MesProFeedbackBatchCheckRespVO respVO = new MesProFeedbackBatchCheckRespVO();
+
+ // 1. 妫�鏌ョ墿鏂欐槸鍚﹀惎鐢ㄦ壒娆$鐞�
+ MesMdmItemDO item = mdmItemService.getItem(feedback.getItemId());
+ if (item == null || Boolean.FALSE.equals(item.getIsBatchManaged())) {
+ respVO.setNeedBatchInput(false);
+ respVO.setIsBatchManaged(false);
+ return respVO;
+ }
+ respVO.setIsBatchManaged(true);
+
+ // 2. 鑾峰彇鎵规閰嶇疆
+ MdmItemBatchConfigRespDTO config = itemBatchConfigApi.getItemBatchConfig(feedback.getItemId());
+ if (config == null) {
+ respVO.setNeedBatchInput(false);
+ return respVO;
+ }
+
+ // 3. 濉厖閰嶇疆鏍囧織
+ respVO.setProduceDateFlag(config.getProduceDateFlag());
+ respVO.setExpireDateFlag(config.getExpireDateFlag());
+ respVO.setReceiptDateFlag(config.getReceiptDateFlag());
+ respVO.setVendorFlag(config.getVendorFlag());
+ respVO.setClientFlag(config.getClientFlag());
+ respVO.setSalesOrderCodeFlag(config.getSalesOrderCodeFlag());
+ respVO.setPurchaseOrderCodeFlag(config.getPurchaseOrderCodeFlag());
+ respVO.setWorkOrderFlag(config.getWorkOrderFlag());
+ respVO.setTaskFlag(config.getTaskFlag());
+ respVO.setWorkstationFlag(config.getWorkstationFlag());
+ respVO.setToolFlag(config.getToolFlag());
+ respVO.setMoldFlag(config.getMoldFlag());
+ respVO.setLotNumberFlag(config.getLotNumberFlag());
+ respVO.setQualityStatusFlag(config.getQualityStatusFlag());
+
+ // 4. 鍒ゆ柇鍝簺瀛楁宸茶嚜鍔ㄥ~鍏咃紙鎶ュ伐鍜岀敓浜ц繃绋嬩腑蹇呯劧鏈夊�肩殑瀛楁锛�
+ List<String> autoFilled = new ArrayList<>();
+ // 鐢熶骇鏃ユ湡锛氬鎵规椂鑷姩鍙栧綋澶�
+ autoFilled.add("produceDate");
+ // 鐢熶骇宸ュ崟锛氫粠鎶ュ伐璁板綍鑾峰彇
+ autoFilled.add("workOrderId");
+ // 鐢熶骇浠诲姟锛氫粠鎶ュ伐璁板綍鑾峰彇
+ autoFilled.add("taskId");
+ // 宸ヤ綔绔欙細浠庢姤宸ヨ褰曡幏鍙�
+ autoFilled.add("workstationId");
+ respVO.setAutoFilledFields(autoFilled);
+
+ // 5. 鍒ゆ柇鏄惁闇�瑕佸脊绐楋細瀛樺湪蹇呭~浣嗘棤娉曡嚜鍔ㄥ~鍏呯殑瀛楁
+ boolean needBatchInput = false;
+ if (Boolean.TRUE.equals(config.getExpireDateFlag())) needBatchInput = true;
+ if (Boolean.TRUE.equals(config.getReceiptDateFlag())) needBatchInput = true;
+ if (Boolean.TRUE.equals(config.getVendorFlag())) needBatchInput = true;
+ if (Boolean.TRUE.equals(config.getClientFlag())) needBatchInput = true;
+ if (Boolean.TRUE.equals(config.getSalesOrderCodeFlag())) needBatchInput = true;
+ if (Boolean.TRUE.equals(config.getPurchaseOrderCodeFlag())) needBatchInput = true;
+ if (Boolean.TRUE.equals(config.getToolFlag())) needBatchInput = true;
+ if (Boolean.TRUE.equals(config.getMoldFlag())) needBatchInput = true;
+ if (Boolean.TRUE.equals(config.getLotNumberFlag())) needBatchInput = true;
+ if (Boolean.TRUE.equals(config.getQualityStatusFlag())) needBatchInput = true;
+ respVO.setNeedBatchInput(needBatchInput);
+
+ return respVO;
}
}
--
Gitblit v1.9.3