| | |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.alibaba.fastjson2.JSON; |
| | | import com.ruoyi.approve.bean.dto.ApprovalInstanceDto; |
| | | import com.ruoyi.approve.bean.vo.ApprovalTemplateVo; |
| | | import com.ruoyi.approve.service.ApprovalInstanceService; |
| | | import com.ruoyi.approve.service.ApprovalTemplateService; |
| | | import com.ruoyi.basic.mapper.ProductMapper; |
| | | import com.ruoyi.basic.mapper.ProductModelMapper; |
| | | import com.ruoyi.basic.pojo.Product; |
| | | import com.ruoyi.basic.pojo.ProductModel; |
| | | import com.ruoyi.common.enums.TypeEnums; |
| | | import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum; |
| | | import com.ruoyi.common.enums.StockInUnQualifiedRecordTypeEnum; |
| | | import com.ruoyi.common.enums.StockOutQualifiedRecordTypeEnum; |
| | | import com.ruoyi.common.exception.ServiceException; |
| | | import com.ruoyi.common.utils.bean.BeanUtils; |
| | |
| | | import com.ruoyi.project.system.mapper.SysUserMapper; |
| | | import com.ruoyi.quality.mapper.*; |
| | | import com.ruoyi.quality.pojo.*; |
| | | import com.ruoyi.stock.pojo.StockInRecord; |
| | | import com.ruoyi.stock.service.StockInRecordService; |
| | | import com.ruoyi.stock.dto.StockInventoryDto; |
| | | import com.ruoyi.stock.service.StockInventoryService; |
| | | import com.ruoyi.technology.mapper.TechnologyOperationMapper; |
| | | import com.ruoyi.technology.mapper.TechnologyRoutingOperationMapper; |
| | | import com.ruoyi.technology.pojo.TechnologyOperation; |
| | |
| | | import java.time.LocalDateTime; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.ArrayList; |
| | | import java.util.Collections; |
| | | import java.util.HashMap; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.LinkedHashSet; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Objects; |
| | | import java.util.Set; |
| | | import java.util.stream.Collectors; |
| | | |
| | | @Service |
| | |
| | | private final ProductionAccountMapper productionAccountMapper; |
| | | private final ProductionOperationTaskMapper productionOperationTaskMapper; |
| | | private final ProductionOrderMapper productionOrderMapper; |
| | | private final ProductionOrderBomMapper productionOrderBomMapper; |
| | | private final ProductionBomStructureMapper productionBomStructureMapper; |
| | | private final ProductionOrderRoutingOperationMapper productionOrderRoutingOperationMapper; |
| | | private final ProductionOrderRoutingOperationParamMapper productionOrderRoutingOperationParamMapper; |
| | | private final TechnologyRoutingOperationMapper technologyRoutingOperationMapper; |
| | | private final TechnologyOperationMapper technologyOperationMapper; |
| | | private final ApprovalTemplateService approvalTemplateService; |
| | | private final ApprovalInstanceService approvalInstanceService; |
| | | private final StockUtils stockUtils; |
| | | private final StockInRecordService stockInRecordService; |
| | | private final StockInventoryService stockInventoryService; |
| | | |
| | | @Override |
| | | public IPage<ProductionProductMainDto> listPageProductionProductMainDto(Page page, ProductionProductMainDto productionProductMainDto) { |
| | | return productionProductMainMapper.listPageProductionProductMainDto(page, productionProductMainDto); |
| | | // 分页查询生产报工主表 |
| | | IPage<ProductionProductMainDto> result = productionProductMainMapper.listPageProductionProductMainDto(page, productionProductMainDto); |
| | | fillOperationParamList(result.getRecords()); |
| | | return result; |
| | | } |
| | | |
| | | @Override |
| | | public IPage<ProductionProductMainDto> pageProductionProductMain(Page page, ProductionProductMainDto productionProductMainDto) { |
| | | // 分页查询生产报工主表 |
| | | return listPageProductionProductMainDto(page, productionProductMainDto); |
| | | } |
| | | |
| | | @Override |
| | | public ProductionProductMainDto getProductionProductMainInfo(Long id) { |
| | | return productionProductMainMapper.listPageProductionProductMainDto(new Page<>(1, 1), new ProductionProductMainDto() {{ |
| | | // 获取生产产品主表详情 |
| | | return listPageProductionProductMainDto(new Page<>(1, 1), new ProductionProductMainDto() {{ |
| | | setId(id); |
| | | }}).getRecords().stream().findFirst().orElse(null); |
| | | } |
| | | |
| | | private void fillOperationParamList(List<ProductionProductMainDto> recordList) { |
| | | // 填充工序参数列表 |
| | | if (recordList == null || recordList.isEmpty()) { |
| | | return; |
| | | } |
| | | // 遍历处理数据并组装结果 |
| | | Set<Long> mainIdSet = recordList.stream() |
| | | .map(ProductionProductMainDto::getId) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toCollection(LinkedHashSet::new)); |
| | | if (mainIdSet.isEmpty()) { |
| | | recordList.forEach(item -> item.setProductionOperationParamList(Collections.emptyList())); |
| | | return; |
| | | } |
| | | |
| | | // 查询并准备业务数据 |
| | | List<ProductionOrderRoutingOperationParam> paramList = productionOrderRoutingOperationParamMapper.selectList( |
| | | Wrappers.<ProductionOrderRoutingOperationParam>lambdaQuery() |
| | | .in(ProductionOrderRoutingOperationParam::getProductionProductMainId, mainIdSet) |
| | | .orderByAsc(ProductionOrderRoutingOperationParam::getId)); |
| | | Map<Long, List<ProductionOrderRoutingOperationParam>> paramGroupMap = new HashMap<>(); |
| | | for (ProductionOrderRoutingOperationParam param : paramList) { |
| | | if (param == null || param.getProductionProductMainId() == null) { |
| | | continue; |
| | | } |
| | | paramGroupMap.computeIfAbsent(param.getProductionProductMainId(), key -> new ArrayList<>()).add(param); |
| | | } |
| | | |
| | | Set<Long> missingMainIdSet = new LinkedHashSet<>(); |
| | | for (ProductionProductMainDto item : recordList) { |
| | | Long mainId = item.getId(); |
| | | if (mainId == null) { |
| | | item.setProductionOperationParamList(Collections.emptyList()); |
| | | continue; |
| | | } |
| | | List<ProductionOrderRoutingOperationParam> params = paramGroupMap.get(mainId); |
| | | if (params != null && !params.isEmpty()) { |
| | | item.setProductionOperationParamList(params); |
| | | continue; |
| | | } |
| | | missingMainIdSet.add(mainId); |
| | | } |
| | | if (missingMainIdSet.isEmpty()) { |
| | | return; |
| | | } |
| | | |
| | | // 兼容历史数据:旧报工记录没有按报工ID落参数快照时,回退展示工序模板参数。 |
| | | List<ProductionProductMain> mainList = productionProductMainMapper.selectBatchIds(missingMainIdSet); |
| | | Map<Long, Long> mainIdToTaskIdMap = mainList.stream() |
| | | .filter(Objects::nonNull) |
| | | .filter(item -> item.getId() != null) |
| | | .collect(Collectors.toMap(ProductionProductMain::getId, |
| | | ProductionProductMain::getProductionOperationTaskId, (left, right) -> left)); |
| | | Set<Long> taskIdSet = mainIdToTaskIdMap.values().stream() |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toCollection(LinkedHashSet::new)); |
| | | if (taskIdSet.isEmpty()) { |
| | | for (ProductionProductMainDto item : recordList) { |
| | | if (item.getId() != null && missingMainIdSet.contains(item.getId())) { |
| | | item.setProductionOperationParamList(Collections.emptyList()); |
| | | } |
| | | } |
| | | return; |
| | | } |
| | | |
| | | List<ProductionOperationTask> taskList = productionOperationTaskMapper.selectList( |
| | | Wrappers.<ProductionOperationTask>lambdaQuery() |
| | | .in(ProductionOperationTask::getId, taskIdSet)); |
| | | Map<Long, Long> taskIdToRoutingOperationIdMap = taskList.stream() |
| | | .filter(Objects::nonNull) |
| | | .filter(item -> item.getId() != null) |
| | | .collect(Collectors.toMap(ProductionOperationTask::getId, |
| | | ProductionOperationTask::getProductionOrderRoutingOperationId, (left, right) -> left)); |
| | | Set<Long> routingOperationIdSet = taskIdToRoutingOperationIdMap.values().stream() |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toCollection(LinkedHashSet::new)); |
| | | if (routingOperationIdSet.isEmpty()) { |
| | | for (ProductionProductMainDto item : recordList) { |
| | | if (item.getId() != null && missingMainIdSet.contains(item.getId())) { |
| | | item.setProductionOperationParamList(Collections.emptyList()); |
| | | } |
| | | } |
| | | return; |
| | | } |
| | | |
| | | List<ProductionOrderRoutingOperationParam> fallbackParamList = productionOrderRoutingOperationParamMapper.selectList( |
| | | Wrappers.<ProductionOrderRoutingOperationParam>lambdaQuery() |
| | | .in(ProductionOrderRoutingOperationParam::getProductionOrderRoutingOperationId, routingOperationIdSet) |
| | | .isNull(ProductionOrderRoutingOperationParam::getProductionProductMainId) |
| | | .orderByAsc(ProductionOrderRoutingOperationParam::getId)); |
| | | Map<Long, List<ProductionOrderRoutingOperationParam>> fallbackGroupMap = new HashMap<>(); |
| | | for (ProductionOrderRoutingOperationParam param : fallbackParamList) { |
| | | if (param == null || param.getProductionOrderRoutingOperationId() == null) { |
| | | continue; |
| | | } |
| | | fallbackGroupMap.computeIfAbsent(param.getProductionOrderRoutingOperationId(), key -> new ArrayList<>()).add(param); |
| | | } |
| | | |
| | | for (ProductionProductMainDto item : recordList) { |
| | | Long mainId = item.getId(); |
| | | if (mainId == null || !missingMainIdSet.contains(mainId)) { |
| | | continue; |
| | | } |
| | | Long taskId = mainIdToTaskIdMap.get(mainId); |
| | | Long routingOperationId = taskId == null ? null : taskIdToRoutingOperationIdMap.get(taskId); |
| | | if (routingOperationId == null) { |
| | | item.setProductionOperationParamList(Collections.emptyList()); |
| | | continue; |
| | | } |
| | | item.setProductionOperationParamList(fallbackGroupMap.getOrDefault(routingOperationId, Collections.emptyList())); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public Boolean addProductMain(ProductionProductMainDto dto) { |
| | | if (dto.getProductionOperationTaskId() == null) { |
| | | // 新增生产报工主记录 |
| | | Long taskId = resolveTaskId(dto); |
| | | if (taskId == null) { |
| | | throw new ServiceException("请传入生产工单ID"); |
| | | } |
| | | return addProductMainByProductionTask(dto); |
| | |
| | | |
| | | @Override |
| | | public Boolean saveProductionProductMain(ProductionProductMainDto productionProductMainDto) { |
| | | // 保存生产报工主记录 |
| | | return addProductMain(productionProductMainDto); |
| | | } |
| | | |
| | | @Override |
| | | public Boolean removeProductMain(Long id) { |
| | | // 删除生产报工主记录 |
| | | ProductionProductMain currentMain = productionProductMainMapper.selectById(id); |
| | | if (currentMain == null) { |
| | | return true; |
| | |
| | | } |
| | | |
| | | private Boolean addProductMainByProductionTask(ProductionProductMainDto dto) { |
| | | // 报工以订单工序快照为准,避免工艺主数据变更后影响历史工单执行。 |
| | | // 按生产任务新增报工主记录 |
| | | Long taskId = resolveTaskId(dto); |
| | | if (taskId == null) { |
| | | throw new ServiceException("生产工单ID不能为空"); |
| | | } |
| | | SysUser user = userMapper.selectUserById(dto.getUserId()); |
| | | ProductionOperationTask productionOperationTask = productionOperationTaskMapper.selectById(dto.getProductionOperationTaskId()); |
| | | ProductionOperationTask productionOperationTask = productionOperationTaskMapper.selectById(taskId); |
| | | if (productionOperationTask == null) { |
| | | throw new ServiceException("生产工单不存在"); |
| | | } |
| | | ProductionOrderRoutingOperation routingOperation = productionOrderRoutingOperationMapper.selectById(productionOperationTask.getTechnologyRoutingOperationId()); |
| | | ProductionOrderRoutingOperation routingOperation = productionOrderRoutingOperationMapper.selectById(productionOperationTask.getProductionOrderRoutingOperationId()); |
| | | if (routingOperation == null) { |
| | | throw new ServiceException("订单工艺路线工序不存在"); |
| | | } |
| | |
| | | |
| | | ProductionProductMain productionProductMain = new ProductionProductMain(); |
| | | productionProductMain.setProductNo(generateProductNo()); |
| | | productionProductMain.setUserId(dto.getUserId()); |
| | | productionProductMain.setUserName(dto.getUserName()); |
| | | productionProductMain.setProductionOperationTaskId(productionOperationTask.getId()); |
| | | productionProductMain.setWorkOrderId(productionOperationTask.getId()); |
| | | productionProductMain.setUserId(user == null ? dto.getUserId() : user.getUserId()); |
| | | productionProductMain.setUserName(user == null ? dto.getUserName() : user.getNickName()); |
| | | productionProductMain.setProductionOperationTaskId(taskId); |
| | | productionProductMain.setStatus(0); |
| | | productionProductMain.setReportType(dto.getReportType() == null ? 0 : dto.getReportType()); |
| | | productionProductMain.setAuditStatus(dto.getAuditStatus() == null ? 0 : dto.getAuditStatus()); |
| | | productionProductMain.setAuditRemark(dto.getAuditRemark()); |
| | | productionProductMain.setWorkHour(dto.getWorkHour()); |
| | | productionProductMainMapper.insert(productionProductMain); |
| | | syncOperationParamInputValue(dto, routingOperation.getId(), productionProductMain.getId()); |
| | | createReportApprovalInstance(productionProductMain); |
| | | |
| | | List<ProductStructureDto> productStructureDtos = new ArrayList<>(); |
| | | ProductStructureDto productStructureDto = new ProductStructureDto(); |
| | | productStructureDto.setProductModelId(productModel.getId()); |
| | | productStructureDto.setUnitQuantity(BigDecimal.ONE); |
| | | productStructureDtos.add(productStructureDto); |
| | | List<ProductStructureDto> productStructureDtos = resolveInputStructures( |
| | | productionOrder.getId(), routingOperation, productModel.getId()); |
| | | // 如果没有bom子节点了,那么投入就是他本身 |
| | | if (productStructureDtos.isEmpty()) { |
| | | ProductStructureDto fallbackInput = new ProductStructureDto(); |
| | | fallbackInput.setProductModelId(productModel.getId()); |
| | | fallbackInput.setUnitQuantity(BigDecimal.ONE); |
| | | productStructureDtos.add(fallbackInput); |
| | | } |
| | | for (ProductStructureDto item : productStructureDtos) { |
| | | // 当前实现按工序成品直接作为投入,后续若接入领料记录可在这里替换来源。 |
| | | ProductionProductInput productionProductInput = new ProductionProductInput(); |
| | | productionProductInput.setProductionProductMainId(productionProductMain.getId()); |
| | | productionProductInput.setProductMainId(productionProductMain.getId()); |
| | | productionProductInput.setProductModelId(item.getProductModelId()); |
| | | productionProductInput.setInputQuantity(item.getUnitQuantity().multiply(defaultDecimal(dto.getQuantity()))); |
| | | productionProductInput.setQuantity(productionProductInput.getInputQuantity()); |
| | | productionProductInputMapper.insert(productionProductInput); |
| | | stockUtils.substractStock(item.getProductModelId(), productionProductInput.getInputQuantity(), |
| | | StockOutQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_OUT.getCode(), productionProductMain.getId()); |
| | | } |
| | | |
| | | ProductionProductOutput productionProductOutput = new ProductionProductOutput(); |
| | | productionProductOutput.setProductionProductMainId(productionProductMain.getId()); |
| | | productionProductOutput.setProductMainId(productionProductMain.getId()); |
| | | productionProductOutput.setProductModelId(productModel.getId()); |
| | | productionProductOutput.setQuantity(defaultDecimal(dto.getQuantity())); |
| | | productionProductOutput.setScrapQty(defaultDecimal(dto.getScrapQty())); |
| | | productionProductOutputMapper.insert(productionProductOutput); |
| | | BigDecimal productQty = productionProductOutput.getQuantity().subtract(productionProductOutput.getScrapQty()); |
| | | BigDecimal reportQty = defaultDecimal(productionProductOutput.getQuantity()); |
| | | BigDecimal scrapQty = defaultDecimal(productionProductOutput.getScrapQty()); |
| | | BigDecimal productQty = reportQty; |
| | | String qualifiedBatchNo = null; |
| | | |
| | | List<ProductionOrderRoutingOperation> routingOperationList = productionOrderRoutingOperationMapper.selectList( |
| | | Wrappers.<ProductionOrderRoutingOperation>lambdaQuery() |
| | | .eq(ProductionOrderRoutingOperation::getTechnologyRoutingId, routingOperation.getTechnologyRoutingId()) |
| | | .eq(ProductionOrderRoutingOperation::getOrderRoutingId, routingOperation.getOrderRoutingId()) |
| | | .eq(ProductionOrderRoutingOperation::getProductionOrderId, routingOperation.getProductionOrderId())); |
| | | boolean isLastOperation = routingOperation.getDragSort() != null && routingOperation.getDragSort().equals(routingOperationList.size()); |
| | | if (productQty.compareTo(BigDecimal.ZERO) > 0) { |
| | |
| | | qualityInspect.setProductModelId(productModel.getId()); |
| | | qualityInspectMapper.insert(qualityInspect); |
| | | List<QualityTestStandard> qualityTestStandard = qualityTestStandardMapper.getQualityTestStandardByProductId(product.getId(), inspectType, process); |
| | | if (qualityTestStandard.size() > 0) { |
| | | if (!qualityTestStandard.isEmpty()) { |
| | | qualityInspect.setTestStandardId(qualityTestStandard.get(0).getId()); |
| | | qualityInspectMapper.updateById(qualityInspect); |
| | | qualityTestStandardParamMapper.selectList(Wrappers.<QualityTestStandardParam>lambdaQuery() |
| | |
| | | }); |
| | | } |
| | | } else { |
| | | stockUtils.addStock(productModel.getId(), productQty, |
| | | StockInQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_IN.getCode(), productionProductMain.getId()); |
| | | StockInventoryDto stockInventoryDto = new StockInventoryDto(); |
| | | stockInventoryDto.setRecordId(productionProductMain.getId()); |
| | | stockInventoryDto.setRecordType(String.valueOf(StockInQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_IN.getCode())); |
| | | stockInventoryDto.setQualitity(productQty); |
| | | stockInventoryDto.setProductModelId(productModel.getId()); |
| | | stockInventoryService.addStockInRecordOnly(stockInventoryDto); |
| | | qualifiedBatchNo = resolveLatestStockInBatchNo( |
| | | productionProductMain.getId(), |
| | | StockInQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_IN.getCode(), |
| | | productModel.getId(), |
| | | "0"); |
| | | } |
| | | |
| | | productionOperationTask.setCompleteQuantity(defaultDecimal(productionOperationTask.getCompleteQuantity()).add(productQty)); |
| | |
| | | if (ObjectUtils.isNull(productionOrder.getStartTime())) { |
| | | productionOrder.setStartTime(LocalDateTime.now()); |
| | | } |
| | | // 订单状态由最后一道工序的合格产出推动,避免中间工序提前完工。 |
| | | productionOrder.setStatus(ProductOrderStatusEnum.RUNNING.getCode()); |
| | | // 仅待开始状态才推进到进行中,避免非末道工序报工时覆盖已完成状态。 |
| | | if (ProductOrderStatusEnum.WAIT.getCode().equals(productionOrder.getStatus())) { |
| | | productionOrder.setStatus(ProductOrderStatusEnum.RUNNING.getCode()); |
| | | } |
| | | if (isLastOperation) { |
| | | productionOrder.setCompleteQuantity(defaultDecimal(productionOrder.getCompleteQuantity()).add(productQty)); |
| | | if (productionOrder.getQuantity() != null |
| | |
| | | productionAccount.setSchedulingUserId(user == null ? null : user.getUserId()); |
| | | productionAccount.setSchedulingUserName(user == null ? dto.getUserName() : user.getNickName()); |
| | | productionAccount.setFinishedNum(productQty); |
| | | productionAccount.setWorkHours(workHours); |
| | | productionAccount.setWorkHours(technologyOperation != null ? technologyOperation.getSalaryQuota() : null); |
| | | productionAccount.setTechnologyOperationName(technologyOperation == null ? null : technologyOperation.getName()); |
| | | productionAccount.setSchedulingDate(LocalDateTime.now()); |
| | | productionAccountMapper.insert(productionAccount); |
| | | } |
| | | if (defaultDecimal(dto.getScrapQty()).compareTo(BigDecimal.ZERO) > 0) { |
| | | stockUtils.addUnStock(productModel.getId(), dto.getScrapQty(), |
| | | StockInUnQualifiedRecordTypeEnum.PRODUCTION_SCRAP.getCode(), productionProductMain.getId()); |
| | | if (scrapQty.compareTo(BigDecimal.ZERO) > 0) { |
| | | stockUtils.addUnStockWithBatchNo( |
| | | productModel.getId(), |
| | | scrapQty, |
| | | StockInQualifiedRecordTypeEnum.PRODUCTION_SCRAP.getCode(), |
| | | productionProductMain.getId(), |
| | | qualifiedBatchNo); |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | private void createReportApprovalInstance(ProductionProductMain productionProductMain) { |
| | | if (productionProductMain == null || productionProductMain.getId() == null) { |
| | | return; |
| | | } |
| | | ApprovalTemplateVo templateVo = approvalTemplateService.getEnabledTemplateByBusinessType( |
| | | TypeEnums.PRODUCTION_REPORT_APPROVAL.getCode() |
| | | ); |
| | | if (templateVo == null) { |
| | | throw new ServiceException("请先配置报工审批模板"); |
| | | } |
| | | ApprovalInstanceDto approvalInstanceDto = new ApprovalInstanceDto(); |
| | | approvalInstanceDto.setTemplateId(templateVo.getId()); |
| | | approvalInstanceDto.setTemplateName(templateVo.getTemplateName()); |
| | | approvalInstanceDto.setBusinessId(productionProductMain.getId()); |
| | | approvalInstanceDto.setBusinessType(TypeEnums.PRODUCTION_REPORT_APPROVAL.getCode()); |
| | | approvalInstanceDto.setTitle(productionProductMain.getProductNo()); |
| | | approvalInstanceDto.setApplicantId(productionProductMain.getUserId()); |
| | | approvalInstanceDto.setApplicantName(productionProductMain.getUserName()); |
| | | approvalInstanceDto.setApplyTime(LocalDateTime.now()); |
| | | approvalInstanceDto.setStatus("PENDING"); |
| | | approvalInstanceDto.setFormConfig(JSON.toJSONString(buildReportApprovalFormConfig(productionProductMain))); |
| | | approvalInstanceService.add(approvalInstanceDto); |
| | | } |
| | | |
| | | private Map<String, Object> buildReportApprovalFormConfig(ProductionProductMain productionProductMain) { |
| | | Map<String, Object> formConfig = new LinkedHashMap<>(); |
| | | formConfig.put("summaryPlaceholder", "报工审批"); |
| | | formConfig.put("approvalType", "production_report"); |
| | | |
| | | List<Map<String, Object>> fields = new ArrayList<>(); |
| | | fields.add(buildReportField("productNo", "报工单号", "text", productionProductMain.getProductNo())); |
| | | fields.add(buildReportField("userName", "报工人员", "text", productionProductMain.getUserName())); |
| | | fields.add(buildReportField("workOrderNo", "工单编号", "text", resolveWorkOrderNo(productionProductMain.getProductionOperationTaskId()))); |
| | | fields.add(buildReportField("reportType", "报工结果", "select", resolveReportTypeLabel(productionProductMain.getReportType()))); |
| | | fields.add(buildReportField("workHour", "工时", "number", productionProductMain.getWorkHour())); |
| | | fields.add(buildReportField("auditStatus", "审核状态", "select", resolveAuditStatusLabel(productionProductMain.getAuditStatus()))); |
| | | fields.add(buildReportField("auditRemark", "审核意见", "textarea", productionProductMain.getAuditRemark())); |
| | | formConfig.put("fields", fields); |
| | | |
| | | Map<String, Object> formPayload = new LinkedHashMap<>(); |
| | | formPayload.put("productNo", productionProductMain.getProductNo()); |
| | | formPayload.put("userName", productionProductMain.getUserName()); |
| | | formPayload.put("workOrderNo", resolveWorkOrderNo(productionProductMain.getProductionOperationTaskId())); |
| | | formPayload.put("reportType", resolveReportTypeLabel(productionProductMain.getReportType())); |
| | | formPayload.put("workHour", productionProductMain.getWorkHour()); |
| | | formPayload.put("auditStatus", resolveAuditStatusLabel(productionProductMain.getAuditStatus())); |
| | | formPayload.put("auditRemark", productionProductMain.getAuditRemark()); |
| | | formConfig.put("formPayload", formPayload); |
| | | return formConfig; |
| | | } |
| | | |
| | | private Map<String, Object> buildReportField(String key, String label, String type, Object defaultValue) { |
| | | Map<String, Object> field = new LinkedHashMap<>(); |
| | | field.put("key", key); |
| | | field.put("label", label); |
| | | field.put("type", type); |
| | | field.put("required", Boolean.FALSE); |
| | | field.put("defaultValue", defaultValue); |
| | | return field; |
| | | } |
| | | |
| | | private String resolveWorkOrderNo(Long productionOperationTaskId) { |
| | | if (productionOperationTaskId == null) { |
| | | return null; |
| | | } |
| | | ProductionOperationTask task = productionOperationTaskMapper.selectById(productionOperationTaskId); |
| | | if (task != null && task.getWorkOrderNo() != null) { |
| | | return task.getWorkOrderNo(); |
| | | } |
| | | if (task == null || task.getProductionOrderRoutingOperationId() == null) { |
| | | return null; |
| | | } |
| | | ProductionOrderRoutingOperation routingOperation = productionOrderRoutingOperationMapper.selectById(task.getProductionOrderRoutingOperationId()); |
| | | if (routingOperation == null || routingOperation.getProductionOrderId() == null) { |
| | | return null; |
| | | } |
| | | ProductionOrder productionOrder = productionOrderMapper.selectById(routingOperation.getProductionOrderId()); |
| | | return productionOrder == null ? null : productionOrder.getNpsNo(); |
| | | } |
| | | |
| | | private String resolveReportTypeLabel(Integer reportType) { |
| | | if (reportType == null) { |
| | | return "—"; |
| | | } |
| | | switch (reportType) { |
| | | case 0: |
| | | return "合格"; |
| | | case 1: |
| | | return "轻微返工"; |
| | | case 2: |
| | | return "严重返工"; |
| | | case 3: |
| | | return "报废"; |
| | | default: |
| | | return "—"; |
| | | } |
| | | } |
| | | |
| | | private String resolveAuditStatusLabel(Integer auditStatus) { |
| | | if (auditStatus == null) { |
| | | return "待审核"; |
| | | } |
| | | switch (auditStatus) { |
| | | case 0: |
| | | return "待审核"; |
| | | case 1: |
| | | return "审核通过"; |
| | | case 2: |
| | | return "审核不通过"; |
| | | default: |
| | | return "—"; |
| | | } |
| | | } |
| | | |
| | | private String resolveLatestStockInBatchNo(Long recordId, |
| | | String recordType, |
| | | Long productModelId, |
| | | String stockType) { |
| | | if (recordId == null || productModelId == null) { |
| | | return null; |
| | | } |
| | | StockInRecord stockInRecord = stockInRecordService.getOne( |
| | | Wrappers.<StockInRecord>lambdaQuery() |
| | | .eq(StockInRecord::getRecordId, recordId) |
| | | .eq(StockInRecord::getRecordType, recordType) |
| | | .eq(StockInRecord::getProductModelId, productModelId) |
| | | .eq(StockInRecord::getType, stockType) |
| | | .orderByDesc(StockInRecord::getId) |
| | | .last("limit 1"), |
| | | false); |
| | | if (stockInRecord == null) { |
| | | throw new ServiceException("未找到对应的入库申请记录"); |
| | | } |
| | | return stockInRecord.getBatchNo(); |
| | | } |
| | | |
| | | private void syncOperationParamInputValue(ProductionProductMainDto dto, |
| | | Long productionOrderRoutingOperationId, |
| | | Long productionProductMainId) { |
| | | if (dto == null || productionOrderRoutingOperationId == null || productionProductMainId == null) { |
| | | return; |
| | | } |
| | | List<ProductionOrderRoutingOperationParam> paramList = dto.getProductionOperationParamList(); |
| | | if (paramList == null || paramList.isEmpty()) { |
| | | return; |
| | | } |
| | | Set<Long> sourceParamIdSet = paramList.stream() |
| | | .filter(Objects::nonNull) |
| | | .map(ProductionOrderRoutingOperationParam::getId) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toCollection(LinkedHashSet::new)); |
| | | if (sourceParamIdSet.isEmpty()) { |
| | | return; |
| | | } |
| | | |
| | | List<ProductionOrderRoutingOperationParam> dbParamList = productionOrderRoutingOperationParamMapper.selectList( |
| | | Wrappers.<ProductionOrderRoutingOperationParam>lambdaQuery() |
| | | .in(ProductionOrderRoutingOperationParam::getId, sourceParamIdSet) |
| | | .eq(ProductionOrderRoutingOperationParam::getProductionOrderRoutingOperationId, productionOrderRoutingOperationId)); |
| | | if (dbParamList == null || dbParamList.isEmpty()) { |
| | | return; |
| | | } |
| | | |
| | | Map<Long, ProductionOrderRoutingOperationParam> dbParamMap = dbParamList.stream() |
| | | .filter(item -> item != null && item.getId() != null) |
| | | .collect(Collectors.toMap(ProductionOrderRoutingOperationParam::getId, item -> item, (left, right) -> left)); |
| | | for (ProductionOrderRoutingOperationParam param : paramList) { |
| | | if (param == null || param.getId() == null) { |
| | | continue; |
| | | } |
| | | ProductionOrderRoutingOperationParam dbParam = dbParamMap.get(param.getId()); |
| | | if (dbParam == null) { |
| | | throw new ServiceException("工序参数不存在或不属于当前工单工序,ID=" + param.getId()); |
| | | } |
| | | productionOrderRoutingOperationParamMapper.insert(buildReportParamSnapshot(dbParam, param.getInputValue(), productionProductMainId)); |
| | | } |
| | | } |
| | | |
| | | private ProductionOrderRoutingOperationParam buildReportParamSnapshot(ProductionOrderRoutingOperationParam source, |
| | | String inputValue, |
| | | Long productionProductMainId) { |
| | | ProductionOrderRoutingOperationParam target = new ProductionOrderRoutingOperationParam(); |
| | | target.setProductionOrderId(source.getProductionOrderId()); |
| | | target.setTechnologyRoutingOperationParamId(source.getTechnologyRoutingOperationParamId()); |
| | | target.setParamCode(source.getParamCode()); |
| | | target.setParamName(source.getParamName()); |
| | | target.setParamType(source.getParamType()); |
| | | target.setParamFormat(source.getParamFormat()); |
| | | target.setUnit(source.getUnit()); |
| | | target.setIsRequired(source.getIsRequired()); |
| | | target.setRemark(source.getRemark()); |
| | | target.setParamId(source.getParamId()); |
| | | target.setTechnologyOperationId(source.getTechnologyOperationId()); |
| | | target.setTechnologyOperationParamId(source.getTechnologyOperationParamId()); |
| | | target.setStandardValue(source.getStandardValue()); |
| | | target.setInputValue(inputValue); |
| | | target.setProductionOrderRoutingOperationId(source.getProductionOrderRoutingOperationId()); |
| | | target.setProductionProductMainId(productionProductMainId); |
| | | return target; |
| | | } |
| | | |
| | | private List<ProductStructureDto> resolveInputStructures(Long productionOrderId, |
| | | ProductionOrderRoutingOperation routingOperation, |
| | | Long outputProductModelId) { |
| | | if (productionOrderId == null || routingOperation == null || routingOperation.getTechnologyOperationId() == null) { |
| | | return new ArrayList<>(); |
| | | } |
| | | ProductionOrderBom orderBom = productionOrderBomMapper.selectOne( |
| | | Wrappers.<ProductionOrderBom>lambdaQuery() |
| | | .eq(ProductionOrderBom::getProductionOrderId, productionOrderId) |
| | | .orderByDesc(ProductionOrderBom::getId) |
| | | .last("limit 1")); |
| | | if (orderBom == null || orderBom.getId() == null) { |
| | | return new ArrayList<>(); |
| | | } |
| | | |
| | | List<ProductionBomStructure> bomNodeList = productionBomStructureMapper.selectList( |
| | | Wrappers.<ProductionBomStructure>lambdaQuery() |
| | | .eq(ProductionBomStructure::getProductionOrderBomId, orderBom.getId()) |
| | | .orderByAsc(ProductionBomStructure::getId)); |
| | | if (bomNodeList.isEmpty()) { |
| | | return new ArrayList<>(); |
| | | } |
| | | |
| | | Map<Long, ProductionBomStructure> nodeMap = bomNodeList.stream() |
| | | .filter(item -> item != null && item.getId() != null) |
| | | .collect(Collectors.toMap(ProductionBomStructure::getId, item -> item, (left, right) -> left)); |
| | | Long currentOutputModelId = routingOperation.getProductModelId() != null |
| | | ? routingOperation.getProductModelId() |
| | | : outputProductModelId; |
| | | |
| | | Map<Long, BigDecimal> unitQtyByProductModel = new LinkedHashMap<>(); |
| | | for (ProductionBomStructure node : bomNodeList) { |
| | | if (node == null || node.getParentId() == null || node.getProductModelId() == null) { |
| | | continue; |
| | | } |
| | | if (!Objects.equals(node.getTechnologyOperationId(), routingOperation.getTechnologyOperationId())) { |
| | | continue; |
| | | } |
| | | ProductionBomStructure parent = nodeMap.get(node.getParentId()); |
| | | if (parent == null || !Objects.equals(parent.getProductModelId(), currentOutputModelId)) { |
| | | continue; |
| | | } |
| | | unitQtyByProductModel.merge(node.getProductModelId(), defaultDecimal(node.getUnitQuantity()), BigDecimal::add); |
| | | } |
| | | |
| | | List<ProductStructureDto> result = new ArrayList<>(); |
| | | for (Map.Entry<Long, BigDecimal> entry : unitQtyByProductModel.entrySet()) { |
| | | if (entry.getValue().compareTo(BigDecimal.ZERO) <= 0) { |
| | | continue; |
| | | } |
| | | ProductStructureDto item = new ProductStructureDto(); |
| | | item.setProductModelId(entry.getKey()); |
| | | item.setUnitQuantity(entry.getValue()); |
| | | result.add(item); |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | private Boolean removeProductMainByProductionTask(ProductionProductMain productionProductMain) { |
| | | // 删除报工需要同步回滚质检、库存、工时核算和订单/工单进度。 |
| | | // 按生产任务回滚并删除报工主记录 |
| | | List<QualityInspect> qualityInspects = qualityInspectMapper.selectList( |
| | | Wrappers.<QualityInspect>lambdaQuery().eq(QualityInspect::getProductMainId, productionProductMain.getId())); |
| | | // 参数与前置条件校验 |
| | | if (qualityInspects.size() > 0) { |
| | | List<QualityUnqualified> qualityUnqualifieds = qualityUnqualifiedMapper.selectList( |
| | | Wrappers.<QualityUnqualified>lambdaQuery() |
| | | // 遍历处理数据并组装结果 |
| | | .in(QualityUnqualified::getInspectId, qualityInspects.stream().map(QualityInspect::getId).collect(Collectors.toList()))); |
| | | if (qualityUnqualifieds.size() > 0 && qualityUnqualifieds.get(0).getInspectState() == 1) { |
| | | throw new ServiceException("该条报工已经不合格处理了,不允许删除"); |
| | |
| | | |
| | | ProductionOperationTask productionOperationTask = productionOperationTaskMapper.selectById(productionProductMain.getProductionOperationTaskId()); |
| | | if (productionOperationTask != null && productionProductOutput != null) { |
| | | BigDecimal validQuantity = defaultDecimal(productionProductOutput.getQuantity()).subtract(defaultDecimal(productionProductOutput.getScrapQty())); |
| | | productionOperationTask.setCompleteQuantity(defaultDecimal(productionOperationTask.getCompleteQuantity()).subtract(validQuantity)); |
| | | BigDecimal reportQuantity = defaultDecimal(productionProductOutput.getQuantity()); |
| | | productionOperationTask.setCompleteQuantity(defaultDecimal(productionOperationTask.getCompleteQuantity()).subtract(reportQuantity)); |
| | | productionOperationTask.setActualEndTime(null); |
| | | if (defaultDecimal(productionOperationTask.getCompleteQuantity()).compareTo(BigDecimal.ZERO) <= 0) { |
| | | productionOperationTask.setCompleteQuantity(BigDecimal.ZERO); |
| | |
| | | } else { |
| | | productionOperationTask.setStatus(3); |
| | | } |
| | | // 持久化或输出处理结果 |
| | | productionOperationTaskMapper.updateById(productionOperationTask); |
| | | |
| | | ProductionOrder productionOrder = productionOrderMapper.selectById(productionOperationTask.getProductionOrderId()); |
| | | ProductionOrderRoutingOperation routingOperation = productionOrderRoutingOperationMapper.selectById(productionOperationTask.getTechnologyRoutingOperationId()); |
| | | ProductionOrderRoutingOperation routingOperation = productionOrderRoutingOperationMapper.selectById(productionOperationTask.getProductionOrderRoutingOperationId()); |
| | | if (productionOrder != null && routingOperation != null) { |
| | | // 只有最后一道工序的报工才会影响生产订单完工数量。 |
| | | List<ProductionOrderRoutingOperation> routingOperationList = productionOrderRoutingOperationMapper.selectList( |
| | | Wrappers.<ProductionOrderRoutingOperation>lambdaQuery() |
| | | .eq(ProductionOrderRoutingOperation::getTechnologyRoutingId, routingOperation.getTechnologyRoutingId()) |
| | | .eq(ProductionOrderRoutingOperation::getOrderRoutingId, routingOperation.getOrderRoutingId()) |
| | | .eq(ProductionOrderRoutingOperation::getProductionOrderId, routingOperation.getProductionOrderId())); |
| | | boolean isLastOperation = routingOperation.getDragSort() != null && routingOperation.getDragSort().equals(routingOperationList.size()); |
| | | if (isLastOperation) { |
| | | BigDecimal newCompleteQty = defaultDecimal(productionOrder.getCompleteQuantity()).subtract(validQuantity); |
| | | BigDecimal newCompleteQty = defaultDecimal(productionOrder.getCompleteQuantity()).subtract(reportQuantity); |
| | | productionOrder.setCompleteQuantity(newCompleteQty.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : newCompleteQty); |
| | | productionOrder.setEndTime(null); |
| | | } |
| | |
| | | .eq(ProductionProductOutput::getProductionProductMainId, productionProductMain.getId())); |
| | | productionProductInputMapper.delete(new LambdaQueryWrapper<ProductionProductInput>() |
| | | .eq(ProductionProductInput::getProductionProductMainId, productionProductMain.getId())); |
| | | stockUtils.deleteStockInRecord(productionProductMain.getId(), StockInUnQualifiedRecordTypeEnum.PRODUCTION_SCRAP.getCode()); |
| | | productionOrderRoutingOperationParamMapper.delete( |
| | | Wrappers.<ProductionOrderRoutingOperationParam>lambdaQuery() |
| | | .eq(ProductionOrderRoutingOperationParam::getProductionProductMainId, productionProductMain.getId())); |
| | | stockUtils.deleteStockInRecord(productionProductMain.getId(), StockInQualifiedRecordTypeEnum.PRODUCTION_SCRAP.getCode()); |
| | | stockUtils.deleteStockInRecord(productionProductMain.getId(), StockInQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_IN.getCode()); |
| | | stockUtils.deleteStockOutRecord(productionProductMain.getId(), StockOutQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_OUT.getCode()); |
| | | productionProductMainMapper.deleteById(productionProductMain.getId()); |
| | |
| | | } |
| | | |
| | | private String generateProductNo() { |
| | | // 生成下一个生产产品编号 |
| | | String datePrefix = "BG" + LocalDate.now().format(DateTimeFormatter.ofPattern("yyMMdd")); |
| | | QueryWrapper<ProductionProductMain> queryWrapper = new QueryWrapper<>(); |
| | | queryWrapper.select("MAX(product_no) as maxNo").likeRight("product_no", datePrefix); |
| | |
| | | } |
| | | |
| | | private BigDecimal defaultDecimal(BigDecimal value) { |
| | | // 将空数量兜底为0,避免空指针异常 |
| | | return value == null ? BigDecimal.ZERO : value; |
| | | } |
| | | |
| | | private Long resolveTaskId(ProductionProductMainDto dto) { |
| | | // 从入参中解析生产工单ID并校验 |
| | | if (dto == null) { |
| | | return null; |
| | | } |
| | | return dto.getProductionOperationTaskId(); |
| | | } |
| | | |
| | | private boolean isCountingWageReport(ProductionProductMain productionProductMain) { |
| | | return productionProductMain != null |
| | | && productionProductMain.getAuditStatus() != null |
| | | && productionProductMain.getAuditStatus() == 1 |
| | | && productionProductMain.getReportType() != null |
| | | && (productionProductMain.getReportType() == 0 || productionProductMain.getReportType() == 1); |
| | | } |
| | | |
| | | private boolean isExcludedFromWage(ProductionProductMain productionProductMain) { |
| | | return productionProductMain != null |
| | | && productionProductMain.getReportType() != null |
| | | && productionProductMain.getReportType() != 0 |
| | | && productionProductMain.getReportType() != 1; |
| | | } |
| | | |
| | | @Override |
| | | public Boolean auditProductMain(ProductionProductMainDto productionProductMainDto) { |
| | | if (productionProductMainDto == null || productionProductMainDto.getId() == null) { |
| | | throw new ServiceException("参数错误"); |
| | | } |
| | | ProductionProductMain productionProductMain = productionProductMainMapper.selectById(productionProductMainDto.getId()); |
| | | if (productionProductMain == null) { |
| | | throw new ServiceException("报工不存在"); |
| | | } |
| | | Integer auditStatus = productionProductMainDto.getAuditStatus(); |
| | | if (auditStatus == null) { |
| | | throw new ServiceException("审核状态不能为空"); |
| | | } |
| | | productionProductMain.setAuditStatus(auditStatus); |
| | | productionProductMain.setAuditRemark(productionProductMainDto.getAuditRemark()); |
| | | productionProductMainMapper.updateById(productionProductMain); |
| | | productionAccountMapper.delete(new LambdaQueryWrapper<ProductionAccount>() |
| | | .eq(ProductionAccount::getProductionProductMainId, productionProductMain.getId())); |
| | | if (auditStatus == 1 && !isExcludedFromWage(productionProductMain)) { |
| | | rebuildProductionAccount(productionProductMain); |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | private void rebuildProductionAccount(ProductionProductMain productionProductMain) { |
| | | if (productionProductMain == null || productionProductMain.getId() == null) { |
| | | return; |
| | | } |
| | | ProductionProductOutput output = productionProductOutputMapper.selectOne( |
| | | Wrappers.<ProductionProductOutput>lambdaQuery() |
| | | .eq(ProductionProductOutput::getProductionProductMainId, productionProductMain.getId()) |
| | | .last("limit 1")); |
| | | if (output == null) { |
| | | return; |
| | | } |
| | | ProductionOperationTask productionOperationTask = productionOperationTaskMapper.selectById(productionProductMain.getProductionOperationTaskId()); |
| | | if (productionOperationTask == null) { |
| | | return; |
| | | } |
| | | ProductionOrderRoutingOperation routingOperation = productionOrderRoutingOperationMapper.selectById(productionOperationTask.getProductionOrderRoutingOperationId()); |
| | | if (routingOperation == null) { |
| | | return; |
| | | } |
| | | ProductionOrder productionOrder = productionOrderMapper.selectById(productionOperationTask.getProductionOrderId()); |
| | | if (productionOrder == null) { |
| | | return; |
| | | } |
| | | TechnologyRoutingOperation technologyRoutingOperation = technologyRoutingOperationMapper.selectById(routingOperation.getTechnologyRoutingOperationId()); |
| | | TechnologyOperation technologyOperation = technologyRoutingOperation == null ? null |
| | | : technologyOperationMapper.selectById(technologyRoutingOperation.getTechnologyOperationId()); |
| | | SysUser user = userMapper.selectUserById(productionProductMain.getUserId()); |
| | | BigDecimal productQty = defaultDecimal(output.getQuantity()); |
| | | if (productQty.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return; |
| | | } |
| | | ProductionAccount productionAccount = new ProductionAccount(); |
| | | productionAccount.setProductionProductMainId(productionProductMain.getId()); |
| | | productionAccount.setSchedulingUserId(user == null ? productionProductMain.getUserId() : user.getUserId()); |
| | | productionAccount.setSchedulingUserName(user == null ? productionProductMain.getUserName() : user.getNickName()); |
| | | productionAccount.setFinishedNum(productQty); |
| | | productionAccount.setWorkHours(technologyOperation != null ? technologyOperation.getSalaryQuota() : null); |
| | | productionAccount.setTechnologyOperationName(technologyOperation == null ? null : technologyOperation.getName()); |
| | | productionAccount.setSchedulingDate(LocalDateTime.now()); |
| | | productionAccountMapper.insert(productionAccount); |
| | | } |
| | | |
| | | @Override |
| | | public ArrayList<Long> listMain(List<Long> idList) { |
| | | // 查询主表ID集合 |
| | | return productionProductMainMapper.listMain(idList); |
| | | } |
| | | } |