| | |
| | | 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.framework.mybatis.core.query.LambdaQueryWrapperX; |
| | | 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.enums.pro.MesProWorkOrderTypeEnum; |
| | | 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.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.mysql.pro.feedback.MesProFeedbackMapper; |
| | | 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.route.MesProRouteProcessService; |
| | | 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.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.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | import java.util.ArrayList; |
| | | import java.util.Collection; |
| | | import java.util.Collections; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Objects; |
| | | import java.util.Set; |
| | | import java.util.stream.Collectors; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| | | import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; |
| | |
| | | private MesProRouteProcessService routeProcessService; |
| | | @Resource |
| | | private MesProWorkOrderBomService workOrderBomService; |
| | | @Resource |
| | | private MesProWorkOrderProcessService workOrderProcessService; |
| | | @Resource |
| | | @Lazy // 避免循环依赖 |
| | | private MesProTaskService taskService; |
| | |
| | | .setUncheckQuantity(BigDecimal.ZERO)); |
| | | |
| | | // 6. 根据是否需要检验,决定产出和入库方式 |
| | | // 6.2 调整 只要审批成功直接更新 |
| | | // 关键工序:更新任务 + 工单的已生产数量;非关键工序:只更新任务的已生产数量(工单只看关键工序产出) |
| | | 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()); |
| | | |
| | | } |
| | | if (checkFlag) { |
| | | // 6.1 需要检验:生成产出单(质量状态=待检验),更新报工状态为待检验,等质检完成回调后再入库 |
| | | // 完成时回调见:MesQcIpqcServiceImpl#finishIpqc → splitPendingAndFinishProduce + updateProFeedbackWhenIpqcFinish |
| | |
| | | // 6.2 无需检验:生成产出单(按合格/不合格拆行),直接完成入库 |
| | | MesWmProductProduceDO produce = productProduceService.generateProductProduce(feedback, false, reqVO); |
| | | productProduceService.finishProductProduce(produce.getId()); |
| | | // 关键工序:更新任务 + 工单的已生产数量;非关键工序:只更新任务的已生产数量(工单只看关键工序产出) |
| | | 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; // 已完成 |
| | |
| | | * <li>使用报工数量(feedbackQuantity),累加任务和工单的已生产数量</li> |
| | | * <li>使用产出单行按 qualityStatus 聚合的合格品/不合格品数量,累加任务的合格品/不合格品数量 |
| | | * (不直接用 feedback 上的数量,确保质检回调场景下数量来源正确)</li> |
| | | * isPush 是否推送生产完成事件 |
| | | * </ul> |
| | | * |
| | | * @param feedback 报工单 |
| | |
| | | } |
| | | } |
| | | |
| | | // 2. 更新任务的已生产/合格/不合格数量 |
| | | // 2. 更新任务的已生产/合格/不合格数量 |
| | | taskService.updateProducedQuantity(feedback.getTaskId(), |
| | | feedback.getFeedbackQuantity(), qualifiedQty, unqualifiedQty); |
| | | // 3. 更新工单的已生产数量 |
| | | workOrderService.updateProducedQuantity(feedback.getWorkOrderId(), |
| | | feedback.getFeedbackQuantity()); |
| | | |
| | | // 4. 更新 MPS 的已生产数量(如果是销售订单来源) |
| | | MesProMpsRespDTO mps = mpsApi.getMpsByWorkOrderId(feedback.getWorkOrderId()); |
| | | if (mps != null) { |
| | | mpsApi.updateMpsProducedQuantity(mps.getId(), feedback.getFeedbackQuantity()); |
| | | // 3. 更新订单的已生产数量 |
| | | // 新增逻辑:只有在该笔订单最后一个工序才更新生产订单的已生产数量 |
| | | MesProWorkOrderProcessDO lastProcess = workOrderProcessService.getLastWorkOrderProcessByWorkOrderId(feedback.getWorkOrderId()); |
| | | if (lastProcess != null && ObjUtil.equal(lastProcess.getProcessId(), feedback.getProcessId())) { |
| | | workOrderService.updateProducedQuantity(feedback.getWorkOrderId(), |
| | | feedback.getFeedbackQuantity()); |
| | | // 4. 更新 MPS 的已生产数量(如果是销售订单来源) |
| | | MesProMpsRespDTO mps = mpsApi.getMpsByWorkOrderId(feedback.getWorkOrderId()); |
| | | if (mps != null) { |
| | | mpsApi.updateMpsProducedQuantity(mps.getId(), feedback.getFeedbackQuantity()); |
| | | } |
| | | } |
| | | |
| | | |
| | | // 5. 检查并发布生产完成事件 |
| | | checkAndPublishProductionFinished(feedback.getWorkOrderId()); |
| | |
| | | // 1. 校验任务存在且未终态 |
| | | MesProTaskDO task = taskService.validateTaskNotFinished(reqVO.getTaskId()); |
| | | |
| | | // 校验停工状态不能报工 |
| | | if (!MesProTaskStatusEnum.canFeedback(task.getStatus())) { |
| | | throw exception(PRO_FEEDBACK_CANNOT_CREATE_WHEN_PAUSED); |
| | | } |
| | | |
| | | // 2. 从任务自动填充关联字段(前端无需传入,后端统一从任务获取) |
| | | reqVO.setWorkOrderId(task.getWorkOrderId()); |
| | | reqVO.setRouteId(task.getRouteId()); |
| | |
| | | public MesProFeedbackQuickCreateRespVO quickCreateFeedback(MesProFeedbackQuickCreateReqVO reqVO) { |
| | | // 1. 校验任务存在且非终态 |
| | | MesProTaskDO task = taskService.validateTaskNotFinished(reqVO.getTaskId()); |
| | | |
| | | // 校验停工状态不能报工 |
| | | if (!MesProTaskStatusEnum.canFeedback(task.getStatus())) { |
| | | throw exception(PRO_FEEDBACK_CANNOT_CREATE_WHEN_PAUSED); |
| | | } |
| | | |
| | | // 2. 校验工艺路线工序配置 |
| | | MesProRouteProcessDO routeProcess = routeProcessService.getRouteProcessByRouteIdAndProcessId( |
| | | task.getRouteId(), task.getProcessId()); |
| | |
| | | |
| | | // 4. 更新任务/工单的已生产数量 |
| | | feedback.setQualifiedQuantity(qualifiedQty).setUnqualifiedQuantity(unqualifiedQty); |
| | | updateTaskAndWorkOrderByFeedback(feedback); |
| | | //5.检查并发布生产完成事件 |
| | | checkAndPublishProductionFinished(feedback.getWorkOrderId()); |
| | | } |
| | | |
| | | @Override |
| | |
| | | return respVO; |
| | | } |
| | | |
| | | @Override |
| | | public MesProFeedbackSummaryRespVO getFeedbackSummary(LocalDate beginDate, LocalDate endDate, |
| | | Integer workOrderType) { |
| | | // 1. 查询有效报工(已批准:待检验 + 已完成) |
| | | LambdaQueryWrapperX<MesProFeedbackDO> query = new LambdaQueryWrapperX<MesProFeedbackDO>() |
| | | .in(MesProFeedbackDO::getStatus, List.of( |
| | | MesProFeedbackStatusEnum.UNCHECK.getStatus(), |
| | | MesProFeedbackStatusEnum.FINISHED.getStatus())) |
| | | .betweenIfPresent(MesProFeedbackDO::getFeedbackTime, |
| | | beginDate != null ? beginDate.atStartOfDay() : null, |
| | | endDate != null ? endDate.plusDays(1).atStartOfDay() : null); |
| | | List<MesProFeedbackDO> feedbackList = feedbackMapper.selectList(query); |
| | | if (CollUtil.isEmpty(feedbackList)) { |
| | | MesProFeedbackSummaryRespVO empty = new MesProFeedbackSummaryRespVO(); |
| | | empty.setTotalFeedbackQuantity(BigDecimal.ZERO); |
| | | empty.setTotalQualifiedQuantity(BigDecimal.ZERO); |
| | | empty.setTotalFeedbackCount(0); |
| | | empty.setByType(Collections.emptyList()); |
| | | empty.setByDate(Collections.emptyList()); |
| | | return empty; |
| | | } |
| | | |
| | | // 2. 加载工单信息,映射 工单ID -> 工单类型 |
| | | Set<Long> workOrderIds = feedbackList.stream() |
| | | .map(MesProFeedbackDO::getWorkOrderId).filter(Objects::nonNull) |
| | | .collect(Collectors.toSet()); |
| | | Map<Long, MesProWorkOrderDO> workOrderMap = workOrderService.getWorkOrderMap(workOrderIds); |
| | | |
| | | // 3. 过滤工单类型 |
| | | List<MesProFeedbackDO> filteredList = feedbackList.stream() |
| | | .filter(feedback -> { |
| | | if (workOrderType == null) { |
| | | return true; |
| | | } |
| | | MesProWorkOrderDO workOrder = workOrderMap.get(feedback.getWorkOrderId()); |
| | | return workOrder != null && Objects.equals(workOrder.getType(), workOrderType); |
| | | }) |
| | | .collect(Collectors.toList()); |
| | | |
| | | // 4. 汇总 |
| | | BigDecimal totalFeedbackQuantity = BigDecimal.ZERO; |
| | | BigDecimal totalQualifiedQuantity = BigDecimal.ZERO; |
| | | int totalCount = 0; |
| | | for (MesProFeedbackDO feedback : filteredList) { |
| | | totalFeedbackQuantity = totalFeedbackQuantity.add(nullToZero(feedback.getFeedbackQuantity())); |
| | | totalQualifiedQuantity = totalQualifiedQuantity.add(nullToZero(feedback.getQualifiedQuantity())); |
| | | totalCount++; |
| | | } |
| | | |
| | | // 5. 按工单类型分组 |
| | | Map<Integer, MesProFeedbackSummaryRespVO.TypeSummaryItem> typeMap = new LinkedHashMap<>(); |
| | | // 6. 按日期分组 |
| | | Map<LocalDate, MesProFeedbackSummaryRespVO.DateSummaryItem> dateMap = new LinkedHashMap<>(); |
| | | for (MesProFeedbackDO feedback : filteredList) { |
| | | MesProWorkOrderDO workOrder = workOrderMap.get(feedback.getWorkOrderId()); |
| | | Integer type = workOrder != null ? workOrder.getType() : null; |
| | | if (type != null) { |
| | | MesProFeedbackSummaryRespVO.TypeSummaryItem item = typeMap.computeIfAbsent(type, key -> { |
| | | MesProFeedbackSummaryRespVO.TypeSummaryItem t = new MesProFeedbackSummaryRespVO.TypeSummaryItem(); |
| | | t.setWorkOrderType(key); |
| | | t.setWorkOrderTypeName(MesProWorkOrderTypeEnum.valueOf(key) != null |
| | | ? MesProWorkOrderTypeEnum.valueOf(key).getName() : String.valueOf(key)); |
| | | t.setFeedbackQuantity(BigDecimal.ZERO); |
| | | t.setQualifiedQuantity(BigDecimal.ZERO); |
| | | t.setFeedbackCount(0); |
| | | return t; |
| | | }); |
| | | item.setFeedbackQuantity(item.getFeedbackQuantity().add(nullToZero(feedback.getFeedbackQuantity()))); |
| | | item.setQualifiedQuantity(item.getQualifiedQuantity().add(nullToZero(feedback.getQualifiedQuantity()))); |
| | | item.setFeedbackCount(item.getFeedbackCount() + 1); |
| | | } |
| | | if (feedback.getFeedbackTime() != null) { |
| | | LocalDate date = feedback.getFeedbackTime().toLocalDate(); |
| | | MesProFeedbackSummaryRespVO.DateSummaryItem item = dateMap.computeIfAbsent(date, key -> { |
| | | MesProFeedbackSummaryRespVO.DateSummaryItem d = new MesProFeedbackSummaryRespVO.DateSummaryItem(); |
| | | d.setDate(key); |
| | | d.setFeedbackQuantity(BigDecimal.ZERO); |
| | | d.setQualifiedQuantity(BigDecimal.ZERO); |
| | | d.setFeedbackCount(0); |
| | | return d; |
| | | }); |
| | | item.setFeedbackQuantity(item.getFeedbackQuantity().add(nullToZero(feedback.getFeedbackQuantity()))); |
| | | item.setQualifiedQuantity(item.getQualifiedQuantity().add(nullToZero(feedback.getQualifiedQuantity()))); |
| | | item.setFeedbackCount(item.getFeedbackCount() + 1); |
| | | } |
| | | } |
| | | |
| | | // 7. 组装响应 |
| | | MesProFeedbackSummaryRespVO respVO = new MesProFeedbackSummaryRespVO(); |
| | | respVO.setTotalFeedbackQuantity(totalFeedbackQuantity); |
| | | respVO.setTotalQualifiedQuantity(totalQualifiedQuantity); |
| | | respVO.setTotalFeedbackCount(totalCount); |
| | | respVO.setByType(new ArrayList<>(typeMap.values())); |
| | | respVO.setByDate(new ArrayList<>(dateMap.values())); |
| | | return respVO; |
| | | } |
| | | |
| | | private static BigDecimal nullToZero(BigDecimal value) { |
| | | return value == null ? BigDecimal.ZERO : value; |
| | | } |
| | | |
| | | } |