| | |
| | | // 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 非质检工序:合格品/不良品数量由质检确定;本工序不经过质检,报工未填写时默认全部为合格品 |
| | | if (!checkFlag |
| | | && feedback.getQualifiedQuantity() == null |
| | | && feedback.getUnqualifiedQuantity() == null) { |
| | | feedback.setQualifiedQuantity(feedback.getFeedbackQuantity()); |
| | | feedback.setUnqualifiedQuantity(BigDecimal.ZERO); |
| | | feedbackMapper.updateById(new MesProFeedbackDO().setId(feedback.getId()) |
| | | .setQualifiedQuantity(feedback.getFeedbackQuantity()) |
| | | .setUnqualifiedQuantity(BigDecimal.ZERO)); |
| | | } |
| | | // 2.3 非质检工序:仍有待检数量时不能审批(质检工序的 uncheckQuantity > 0 是正常状态,不做拦截) |
| | | if (!checkFlag |
| | | && feedback.getUncheckQuantity() != null |
| | | && feedback.getUncheckQuantity().compareTo(BigDecimal.ZERO) > 0) { |
| | | throw exception(PRO_FEEDBACK_UNCHECK_QUANTITY_EXISTS, feedback.getUncheckQuantity()); |
| | | } |
| | | |
| | | // 3. 物料消耗:倒冲工序按本工序投料 BOM 自动扣料;非倒冲工序跳过(物料走手工领料/投料) |
| | | boolean backflushFlag = routeProcess != null && Boolean.TRUE.equals(routeProcess.getBackflushFlag()); |
| | |
| | | } |
| | | } |
| | | |
| | | // 5. 先更新报工状态为已完成(确保 checkAndFinishMps 检查时当前报工已标记完成) |
| | | // 报工与质检分开:报工审批完成即累计产量,质量数量由质检回填。 |
| | | feedbackMapper.updateById(new MesProFeedbackDO().setId(reqVO.getId()) |
| | | .setStatus(MesProFeedbackStatusEnum.FINISHED.getStatus()) |
| | | .setUncheckQuantity(BigDecimal.ZERO)); |
| | | .setStatus(MesProFeedbackStatusEnum.UNCHECK.getStatus()) |
| | | .setQualifiedQuantity(BigDecimal.ZERO) |
| | | .setUnqualifiedQuantity(BigDecimal.ZERO) |
| | | .setUncheckQuantity(feedback.getFeedbackQuantity())); |
| | | feedback.setQualifiedQuantity(BigDecimal.ZERO).setUnqualifiedQuantity(BigDecimal.ZERO); |
| | | productProduceService.generateProductProduce(feedback, true, reqVO); |
| | | |
| | | // 6. 根据是否需要检验,决定产出和入库方式 |
| | | // 6.2 调整 只要审批成功直接更新 |
| | |
| | | checkAndPublishProductionFinished(feedback.getWorkOrderId()); |
| | | |
| | | } |
| | | if (checkFlag) { |
| | | // 6.1 需要检验:生成产出单(质量状态=待检验),更新报工状态为待检验,等质检完成回调后再入库 |
| | | // 完成时回调见:MesQcIpqcServiceImpl#finishIpqc → splitPendingAndFinishProduce + updateProFeedbackWhenIpqcFinish |
| | | productProduceService.generateProductProduce(feedback, true, reqVO); |
| | | feedbackMapper.updateById(new MesProFeedbackDO().setId(reqVO.getId()) |
| | | .setStatus(MesProFeedbackStatusEnum.UNCHECK.getStatus())); |
| | | equipmentService.afterApproved(feedback); |
| | | return false; |
| | | } else { |
| | | // 6.2 无需检验:生成产出单(按合格/不合格拆行),直接完成入库 |
| | | MesWmProductProduceDO produce = productProduceService.generateProductProduce(feedback, false, reqVO); |
| | | productProduceService.finishProductProduce(produce.getId()); |
| | | |
| | | } |
| | | |
| | | equipmentService.afterApproved(feedback); |
| | | return true; // 已完成 |
| | | return false; // 产出等待质检,报工产量已累计 |
| | | } |
| | | |
| | | /** |
| | |
| | | * @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()); |
| | | } |
| | | } |
| | | // 1.1 兜底:关键非质检工序审批时产出单行尚未生成,直接采用报工单上的合格/不良数量 |
| | | // (非质检工序默认全部为合格品;质检工序的合格/不良在质检回调后按产出行聚合) |
| | | if (CollUtil.isEmpty(lines)) { |
| | | qualifiedQty = ObjectUtil.defaultIfNull(feedback.getQualifiedQuantity(), BigDecimal.ZERO); |
| | | unqualifiedQty = ObjectUtil.defaultIfNull(feedback.getUnqualifiedQuantity(), BigDecimal.ZERO); |
| | | } |
| | | |
| | | // 2. 更新任务的已生产/合格/不合格数量 |
| | | // 报工审批只累计产量;合格与不合格数量在质检完成时累计。 |
| | | taskService.updateProducedQuantity(feedback.getTaskId(), |
| | | feedback.getFeedbackQuantity(), qualifiedQty, unqualifiedQty); |
| | | feedback.getFeedbackQuantity(), BigDecimal.ZERO, BigDecimal.ZERO); |
| | | // 3. 更新订单的已生产数量 |
| | | // 新增逻辑:只有在该笔订单最后一个工序才更新生产订单的已生产数量 |
| | | MesProWorkOrderProcessDO lastProcess = workOrderProcessService.getLastWorkOrderProcessByWorkOrderId(feedback.getWorkOrderId()); |
| | |
| | | private void checkAndPublishProductionFinished(Long workOrderId) { |
| | | // 1. 获取工单关联的 MPS |
| | | MesProMpsRespDTO mps = mpsApi.getMpsByWorkOrderId(workOrderId); |
| | | if (mps == null || mps.getSaleOrderId() == null) { |
| | | if (mps == null || 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())) { |
| | | return; // 非销售订单来源的工单,跳过 |
| | | } |
| | | |
| | | // 2. 检查并自动完成 MPS(如果满足条件) |
| | | mpsApi.checkAndFinishMps(mps.getId()); |
| | | if (!Boolean.TRUE.equals(mpsApi.checkAndFinishMps(mps.getId()))) { |
| | | return; |
| | | } |
| | | |
| | | // 3. 检查销售订单所有生产是否完成 |
| | | if (mpsApi.checkSaleOrderProductionFinished(mps.getSaleOrderId())) { |
| | |
| | | throw exception(PRO_FEEDBACK_ROUTE_PROCESS_INVALID); |
| | | } |
| | | // 4.1 校验数量:无论是否需要检验,报工只需填报工数量,且必须 > 0 |
| | | // 合格品/不良品数量由质检确定(非质检工序在审批时默认全部为合格品) |
| | | // 合格品/不良品数量由质检确定。 |
| | | if (reqVO.getFeedbackQuantity() == null |
| | | || reqVO.getFeedbackQuantity().compareTo(BigDecimal.ZERO) <= 0) { |
| | | throw exception(PRO_FEEDBACK_QUANTITY_MUST_POSITIVE); |
| | |
| | | .setItemId(task.getItemId()) |
| | | .setFeedbackQuantity(reqVO.getFeedbackQuantity()) |
| | | .setScheduledQuantity(task.getQuantity()) |
| | | .setFeedbackTime(LocalDateTime.now()) |
| | | .setFeedbackTime(reqVO.getFeedbackTime() != null ? reqVO.getFeedbackTime() : LocalDateTime.now()) |
| | | .setStatus(MesProFeedbackStatusEnum.APPROVING.getStatus()) |
| | | .setRemark(reqVO.getRemark()); |
| | | equipmentService.validateSubmission(feedback); |
| | |
| | | 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())) { |
| | | 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()); |
| | | } |
| | | } |
| | |
| | | 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() |
| | |
| | | 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); |
| | | } |
| | |
| | | .setOtherScrapQuantity(otherScrapQty) |
| | | .setStatus(MesProFeedbackStatusEnum.FINISHED.getStatus())); |
| | | |
| | | // 4. 更新任务/工单的已生产数量 |
| | | // 4. 产量已在报工审批时累计,此处只回填质量数量。 |
| | | feedback.setQualifiedQuantity(qualifiedQty).setUnqualifiedQuantity(unqualifiedQty); |
| | | taskService.updateQualityQuantity(feedback.getTaskId(), |
| | | ObjectUtil.defaultIfNull(qualifiedQty, BigDecimal.ZERO), |
| | | ObjectUtil.defaultIfNull(unqualifiedQty, BigDecimal.ZERO)); |
| | | //5.检查并发布生产完成事件 |
| | | checkAndPublishProductionFinished(feedback.getWorkOrderId()); |
| | | } |