| | |
| | | import cn.hutool.core.util.ObjectUtil; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | 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.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.MesProFeedbackDO; |
| | | 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.wm.itemconsume.MesWmItemConsumeDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceDO; |
| | |
| | | import cn.iocoder.yudao.module.mes.enums.pro.MesProFeedbackStatusEnum; |
| | | import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum; |
| | | 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.MesProWorkOrderService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.itemconsume.MesWmItemConsumeService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.productproduce.MesWmProductProduceLineService; |
| | |
| | | import org.springframework.validation.annotation.Validated; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDateTime; |
| | | import java.util.Collection; |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | |
| | | @Resource |
| | | private MesProRouteProcessService routeProcessService; |
| | | @Resource |
| | | private MesProRouteProductBomService routeProductBomService; |
| | | private MesProWorkOrderBomService workOrderBomService; |
| | | @Resource |
| | | @Lazy // 避免循环依赖 |
| | | private MesProTaskService taskService; |
| | |
| | | private MesWmProductProduceLineService produceLineService; |
| | | @Resource |
| | | private MesProMpsApi mpsApi; |
| | | @Resource |
| | | private MesMdAutoCodeRecordService autoCodeRecordService; |
| | | @Resource |
| | | private ApplicationEventPublisher eventPublisher; |
| | | |
| | |
| | | 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()); |
| | | List<MesProWorkOrderBomDO> boms = workOrderBomService.getWorkOrderBomListByWorkOrderId( |
| | | feedback.getWorkOrderId()); |
| | | if (CollUtil.isEmpty(boms)) { |
| | | throw exception(PRO_FEEDBACK_BACKFLUSH_BOM_REQUIRED); |
| | | } |
| | |
| | | .setStatus(MesProFeedbackStatusEnum.FINISHED.getStatus()) |
| | | .setUncheckQuantity(BigDecimal.ZERO)); |
| | | |
| | | // 6. 关键工序:生成产出单,并根据是否需要检验决定入库方式 |
| | | if (keyFlag) { |
| | | // 6. 根据是否需要检验,决定产出和入库方式 |
| | | if (checkFlag) { |
| | | // 6.1 需要检验:生成产出单(质量状态=待检验),更新报工状态为待检验,等质检完成回调后再入库 |
| | | if (checkFlag) { |
| | | // 完成时回调见:MesQcIpqcServiceImpl#finishIpqc → splitPendingAndFinishProduce + completeFeedbackFromIpqc |
| | | productProduceService.generateProductProduce(feedback, true); |
| | | feedbackMapper.updateById(new MesProFeedbackDO().setId(id) |
| | | .setStatus(MesProFeedbackStatusEnum.UNCHECK.getStatus())); |
| | | return false; |
| | | } |
| | | // 6.2 无需检验:生成产出单(按合格/不合格拆行),直接完成入库,并更新任务/工单的已生产数量 |
| | | // 完成时回调见:MesQcIpqcServiceImpl#finishIpqc → splitPendingAndFinishProduce + updateProFeedbackWhenIpqcFinish |
| | | productProduceService.generateProductProduce(feedback, true); |
| | | feedbackMapper.updateById(new MesProFeedbackDO().setId(id) |
| | | .setStatus(MesProFeedbackStatusEnum.UNCHECK.getStatus())); |
| | | return false; |
| | | } else { |
| | | // 6.2 无需检验:生成产出单(按合格/不合格拆行),直接完成入库 |
| | | MesWmProductProduceDO produce = productProduceService.generateProductProduce(feedback, false); |
| | | productProduceService.finishProductProduce(produce.getId()); |
| | | updateTaskAndWorkOrderByFeedback(feedback); |
| | | } else { |
| | | // 6.3 非关键工序:只更新任务的已生产数量(工单不更新,工单只看关键工序产出) |
| | | taskService.updateProducedQuantity(feedback.getTaskId(), |
| | | feedback.getFeedbackQuantity(), |
| | | ObjectUtil.defaultIfNull(feedback.getQualifiedQuantity(), BigDecimal.ZERO), |
| | | ObjectUtil.defaultIfNull(feedback.getUnqualifiedQuantity(), BigDecimal.ZERO)); |
| | | // 6.4 非关键工序也需要检查 MPS 完成状态(所有工序报工完成才算完成) |
| | | checkAndPublishProductionFinished(feedback.getWorkOrderId()); |
| | | // 关键工序:更新任务 + 工单的已生产数量;非关键工序:只更新任务的已生产数量(工单只看关键工序产出) |
| | | if (keyFlag) { |
| | | updateTaskAndWorkOrderByFeedback(feedback); |
| | | } else { |
| | | taskService.updateProducedQuantity(feedback.getTaskId(), |
| | | feedback.getFeedbackQuantity(), |
| | | ObjectUtil.defaultIfNull(feedback.getQualifiedQuantity(), BigDecimal.ZERO), |
| | | ObjectUtil.defaultIfNull(feedback.getUnqualifiedQuantity(), BigDecimal.ZERO)); |
| | | checkAndPublishProductionFinished(feedback.getWorkOrderId()); |
| | | } |
| | | } |
| | | |
| | | return true; // 已完成 |
| | |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public MesProFeedbackQuickCreateRespVO quickCreateFeedback(MesProFeedbackQuickCreateReqVO reqVO) { |
| | | // 1. 校验任务存在且非终态 |
| | | MesProTaskDO task = taskService.validateTaskNotFinished(reqVO.getTaskId()); |
| | | // 2. 校验工艺路线工序配置 |
| | | MesProRouteProcessDO routeProcess = routeProcessService.getRouteProcessByRouteIdAndProcessId( |
| | | task.getRouteId(), task.getProcessId()); |
| | | if (routeProcess == null) { |
| | | throw exception(PRO_FEEDBACK_ROUTE_PROCESS_INVALID); |
| | | } |
| | | // 3. 校验数量 |
| | | boolean checkFlag = Boolean.TRUE.equals(routeProcess.getCheckFlag()); |
| | | if (checkFlag) { |
| | | if (reqVO.getFeedbackQuantity() == null |
| | | || reqVO.getFeedbackQuantity().compareTo(BigDecimal.ZERO) <= 0) { |
| | | throw exception(PRO_FEEDBACK_QUANTITY_MUST_POSITIVE); |
| | | } |
| | | } else { |
| | | 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. 构建报工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()) |
| | | .setQualifiedQuantity(reqVO.getQualifiedQuantity()) |
| | | .setUnqualifiedQuantity(reqVO.getUnqualifiedQuantity()) |
| | | .setScheduledQuantity(task.getQuantity()) |
| | | .setFeedbackTime(LocalDateTime.now()) |
| | | .setStatus(MesProFeedbackStatusEnum.APPROVING.getStatus()) |
| | | .setRemark(reqVO.getRemark()); |
| | | feedbackMapper.insert(feedback); |
| | | // 5. 返回结果 |
| | | MesProWorkOrderDO workOrder = workOrderService.getWorkOrder(task.getWorkOrderId()); |
| | | return new MesProFeedbackQuickCreateRespVO() |
| | | .setFeedbackId(feedback.getId()) |
| | | .setTaskStatus(task.getStatus()) |
| | | .setWorkOrderStatus(workOrder != null ? workOrder.getStatus() : null); |
| | | } |
| | | |
| | | @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.setQualifiedQuantity(reqVO.getQualifiedQuantity()); |
| | | singleReq.setUnqualifiedQuantity(reqVO.getUnqualifiedQuantity()); |
| | | 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 |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void updateProFeedbackWhenIpqcFinish(Long feedbackId, Long sourceLineId, |
| | | BigDecimal qualifiedQty, BigDecimal unqualifiedQty, |
| | | BigDecimal laborScrapQty, BigDecimal materialScrapQty, BigDecimal otherScrapQty) { |