| | |
| | | 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.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| | |
| | | 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.productissue.MesWmProductIssueService; |
| | | 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 java.util.Map; |
| | | import java.util.Objects; |
| | | import java.util.Set; |
| | | import java.util.concurrent.atomic.AtomicInteger; |
| | | import java.util.stream.Collectors; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| | | import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; |
| | | import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; |
| | | |
| | | /** |
| | |
| | | @Resource |
| | | private MesWmItemConsumeService itemConsumeService; |
| | | @Resource |
| | | private MesWmProductIssueService productIssueService; |
| | | @Resource |
| | | private MesWmProductProduceService productProduceService; |
| | | @Resource |
| | | private MesWmProductProduceLineService produceLineService; |
| | |
| | | // 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()); |
| | | feedbackMapper.insert(feedback); |
| | |
| | | MesProFeedbackDO updateObj = BeanUtils.toBean(updateReqVO, MesProFeedbackDO.class) |
| | | .setItemId(task.getItemId()); |
| | | feedbackMapper.updateById(updateObj); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public MesProFeedbackImportRespVO importFeedbackList(List<MesProFeedbackImportExcelVO> importList, |
| | | boolean updateSupport) { |
| | | // 1. 校验非空 |
| | | if (CollUtil.isEmpty(importList)) { |
| | | throw exception(MES_PRO_FEEDBACK_IMPORT_LIST_IS_EMPTY); |
| | | } |
| | | |
| | | // 2. 逐行处理 |
| | | MesProFeedbackImportRespVO respVO = MesProFeedbackImportRespVO.builder() |
| | | .createCodes(new ArrayList<>()).updateCodes(new ArrayList<>()) |
| | | .failureCodes(new LinkedHashMap<>()).build(); |
| | | AtomicInteger index = new AtomicInteger(1); |
| | | importList.forEach(importItem -> { |
| | | int currentIndex = index.getAndIncrement(); |
| | | String key = StrUtil.blankToDefault(importItem.getCode(), "第 " + currentIndex + " 行"); |
| | | if (StrUtil.isBlank(importItem.getCode())) { |
| | | respVO.getFailureCodes().put(key, "报工单编号不能为空"); |
| | | return; |
| | | } |
| | | if (StrUtil.isBlank(importItem.getTaskCode())) { |
| | | respVO.getFailureCodes().put(key, "生产任务编码不能为空"); |
| | | return; |
| | | } |
| | | // 2.1 解析生产任务 |
| | | MesProTaskDO task = taskService.getTaskByCode(importItem.getTaskCode().trim()); |
| | | if (task == null) { |
| | | respVO.getFailureCodes().put(key, "生产任务编码[" + importItem.getTaskCode() + "]不存在"); |
| | | return; |
| | | } |
| | | // 2.2 构建请求并创建/更新(复用 create/update 服务,报工人/审核人取当前登录用户) |
| | | MesProFeedbackSaveReqVO reqVO = BeanUtils.toBean(importItem, MesProFeedbackSaveReqVO.class); |
| | | reqVO.setTaskId(task.getId()); |
| | | Long loginUserId = getLoginUserId(); |
| | | reqVO.setFeedbackUserId(ObjUtil.defaultIfNull(reqVO.getFeedbackUserId(), loginUserId)); |
| | | reqVO.setApproveUserId(ObjUtil.defaultIfNull(reqVO.getApproveUserId(), loginUserId)); |
| | | MesProFeedbackDO existFeedback = feedbackMapper.selectByCode(importItem.getCode()); |
| | | try { |
| | | if (existFeedback == null) { |
| | | reqVO.setId(null); |
| | | createFeedback(reqVO); |
| | | respVO.getCreateCodes().add(importItem.getCode()); |
| | | } else if (updateSupport) { |
| | | reqVO.setId(existFeedback.getId()); |
| | | updateFeedback(reqVO); |
| | | respVO.getUpdateCodes().add(importItem.getCode()); |
| | | } else { |
| | | respVO.getFailureCodes().put(key, "报工单编号已存在"); |
| | | } |
| | | } catch (ServiceException e) { |
| | | respVO.getFailureCodes().put(key, e.getMessage()); |
| | | } |
| | | }); |
| | | return respVO; |
| | | } |
| | | |
| | | @Override |
| | |
| | | @Override |
| | | public void rejectFeedback(Long id) { |
| | | // 1. 校验存在 + 审批中状态 |
| | | validateFeedbackStatusApproving(id); |
| | | MesProFeedbackDO feedback = validateFeedbackStatusApproving(id); |
| | | // 1.1 校验当前登录人是该报工单的指定审核人 |
| | | validateCurrentUserIsApprover(feedback); |
| | | |
| | | // 2. 更新状态为草稿 |
| | | feedbackMapper.updateById(new MesProFeedbackDO().setId(id) |
| | |
| | | public boolean approveFeedback(MesProFeedbackApproveReqVO reqVO) { |
| | | // 1.1 校验存在 + 审批中状态 |
| | | MesProFeedbackDO feedback = validateFeedbackStatusApproving(reqVO.getId()); |
| | | // 1.1.1 校验当前登录人是该报工单的指定审核人 |
| | | validateCurrentUserIsApprover(feedback); |
| | | // 1.2 校验报工数量 > 0 |
| | | if (feedback.getFeedbackQuantity() == null |
| | | || feedback.getFeedbackQuantity().compareTo(BigDecimal.ZERO) <= 0) { |
| | |
| | | // 3. 物料消耗:倒冲工序按 BOM 自动扣料;非倒冲工序跳过(物料走手工领料/投料) |
| | | boolean backflushFlag = routeProcess != null && Boolean.TRUE.equals(routeProcess.getBackflushFlag()); |
| | | if (backflushFlag) { |
| | | // 3.1 校验倒冲工序必须配置 BOM 消耗,避免静默不扣料 |
| | | List<MesProWorkOrderBomDO> boms = workOrderBomService.getWorkOrderBomListByWorkOrderId( |
| | | feedback.getWorkOrderId()); |
| | | // 3.1 校验当前倒冲工序必须配置投料 BOM,避免静默不扣料 |
| | | List<MesProWorkOrderBomDO> boms = workOrderBomService.getWorkOrderBomListByWorkOrderIdAndProcessId( |
| | | feedback.getWorkOrderId(), feedback.getProcessId()); |
| | | if (CollUtil.isEmpty(boms)) { |
| | | throw exception(PRO_FEEDBACK_BACKFLUSH_BOM_REQUIRED); |
| | | } |
| | |
| | | MesWmItemConsumeDO itemConsume = itemConsumeService.generateItemConsume(feedback); |
| | | if (itemConsume != null) { |
| | | itemConsumeService.finishItemConsume(itemConsume.getId()); |
| | | // 消耗单负责实际扣库;领料单仅作为倒冲业务凭证,避免重复扣减库存 |
| | | productIssueService.generateBackflushProductIssue(feedback, itemConsume); |
| | | } |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | /** |
| | | * 校验当前登录用户是否为报工单指定的审核人 |
| | | * |
| | | * 报工单创建时必须指定 approveUserId,审批通过/驳回只能由该审核人操作, |
| | | * 避免任意拥有菜单权限的用户越权审批。 |
| | | * |
| | | * @param feedback 报工单 |
| | | */ |
| | | private void validateCurrentUserIsApprover(MesProFeedbackDO feedback) { |
| | | if (feedback.getApproveUserId() == null) { |
| | | throw exception(PRO_FEEDBACK_APPROVER_NOT_CONFIGURED); |
| | | } |
| | | if (!feedback.getApproveUserId().equals(getLoginUserId())) { |
| | | throw exception(PRO_FEEDBACK_NOT_APPROVER); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 校验报工单的业务数据(创建 & 修改共用) |
| | | * |
| | | * <p>以生产任务为核心:前端只需传 taskId,workOrderId、routeId、processId、itemId、workstationId |