| | |
| | | import com.ruoyi.common.exception.ServiceException; |
| | | import com.ruoyi.production.bean.dto.ProductionOperationTaskDto; |
| | | import com.ruoyi.production.bean.dto.ProductionOrderDto; |
| | | import com.ruoyi.production.bean.vo.ProductionBomStructureVo; |
| | | import com.ruoyi.production.bean.vo.ProductionOperationTaskVo; |
| | | import com.ruoyi.production.bean.vo.ProductionOrderPickVo; |
| | | import com.ruoyi.production.bean.vo.ProductionOrderVo; |
| | | import com.ruoyi.production.bean.vo.ProductionPlanVo; |
| | | import com.ruoyi.production.bean.vo.ProductionOrderWorkOrderDetailVo; |
| | |
| | | import com.ruoyi.production.service.ProductionOrderService; |
| | | import com.ruoyi.sales.mapper.SalesLedgerMapper; |
| | | import com.ruoyi.sales.mapper.SalesLedgerProductMapper; |
| | | import com.ruoyi.stock.mapper.StockInventoryMapper; |
| | | import com.ruoyi.stock.pojo.StockInventory; |
| | | import com.ruoyi.technology.mapper.*; |
| | | import com.ruoyi.technology.pojo.*; |
| | | import lombok.RequiredArgsConstructor; |
| | |
| | | private final ProductionOrderRoutingOperationMapper productionOrderRoutingOperationMapper; |
| | | private final ProductionOrderRoutingOperationParamMapper productionOrderRoutingOperationParamMapper; |
| | | private final ProductionOperationTaskMapper productionOperationTaskMapper; |
| | | private final ProductionOrderBomMapper productionOrderBomMapper; |
| | | private final ProductionBomStructureMapper productionBomStructureMapper; |
| | | private final ProductionOrderMapper productionOrderMapper; |
| | | private final ProductionProductMainMapper productionProductMainMapper; |
| | | private final ProductionProductOutputMapper productionProductOutputMapper; |
| | | private final ProductionOrderPickMapper productionOrderPickMapper; |
| | | private final ProductionOrderPickRecordMapper productionOrderPickRecordMapper; |
| | | private final QualityInspectMapper qualityInspectMapper; |
| | | private final QualityInspectParamMapper qualityInspectParamMapper; |
| | | private final QualityInspectFileMapper qualityInspectFileMapper; |
| | | private final ProductionPlanMapper productionPlanMapper; |
| | | private final StockInventoryMapper stockInventoryMapper; |
| | | private final StorageAttachmentMapper storageAttachmentMapper; |
| | | private final StorageBlobMapper storageBlobMapper; |
| | | private final SalesLedgerMapper salesLedgerMapper; |
| | |
| | | private final TechnologyRoutingOperationMapper technologyRoutingOperationMapper; |
| | | private final TechnologyRoutingOperationParamMapper technologyRoutingOperationParamMapper; |
| | | private final TechnologyOperationMapper technologyOperationMapper; |
| | | private final TechnologyBomMapper technologyBomMapper; |
| | | private final TechnologyBomStructureMapper technologyBomStructureMapper; |
| | | private final FileUtil fileUtil; |
| | | |
| | | @Override |
| | |
| | | |
| | | @Override |
| | | public int syncProductionOrderSnapshot(Long productionOrderId) { |
| | | // 同步订单工艺、工序、参数和BOM快照 |
| | | ProductionOrder productionOrder = this.getById(productionOrderId); |
| | | // 参数与前置条件校验 |
| | | if (productionOrder == null) { |
| | | throw new ServiceException("生产订单不存在"); |
| | | } |
| | |
| | | if (technologyRouting == null) { |
| | | throw new ServiceException("工艺路线不存在"); |
| | | } |
| | | // 订单快照按“先清后建”处理,保证工艺路线、工序、参数、BOM 全部来自同一版本。 |
| | | clearProductionSnapshot(productionOrderId); |
| | | ProductionOrderBom orderBom = syncProductionOrderBomSnapshot(productionOrder, technologyRouting); |
| | | |
| | | //生产订单工艺路线表 |
| | | ProductionOrderRouting orderRouting = new ProductionOrderRouting(); |
| | | orderRouting.setProductionOrderId(productionOrder.getId()); |
| | | orderRouting.setTechnologyRoutingId(technologyRouting.getId()); |
| | | orderRouting.setProductModelId(technologyRouting.getProductModelId()); |
| | | orderRouting.setProcessRouteCode(technologyRouting.getProcessRouteCode()); |
| | | orderRouting.setDescription(technologyRouting.getDescription()); |
| | | orderRouting.setBomId(technologyRouting.getBomId()); |
| | | orderRouting.setOrderBomId(orderBom == null ? null : orderBom.getId()); |
| | | productionOrderRoutingMapper.insert(orderRouting); |
| | | |
| | | int syncedParamCount = 0; |
| | | // 查询并准备业务数据 |
| | | List<TechnologyRoutingOperation> routingOperations = technologyRoutingOperationMapper.selectList( |
| | | Wrappers.<TechnologyRoutingOperation>lambdaQuery() |
| | | .eq(TechnologyRoutingOperation::getTechnologyRoutingId, technologyRouting.getId()) |
| | | .orderByDesc(TechnologyRoutingOperation::getDragSort) |
| | | .orderByDesc(TechnologyRoutingOperation::getId)); |
| | | // Build task plan quantities from order BOM snapshot demand instead of recomputing from technology BOM units. |
| | | Long rootProductModelId = orderBom != null && orderBom.getProductModelId() != null |
| | | ? orderBom.getProductModelId() |
| | | : productionOrder.getProductModelId(); |
| | | List<ProductionBomStructure> orderBomStructureList = orderBom == null || orderBom.getId() == null |
| | | ? Collections.emptyList() |
| | | : productionBomStructureMapper.selectList( |
| | | Wrappers.<ProductionBomStructure>lambdaQuery() |
| | | .eq(ProductionBomStructure::getProductionOrderBomId, orderBom.getId()) |
| | | .orderByAsc(ProductionBomStructure::getId)); |
| | | Map<String, BigDecimal> operationDemandedQuantityMap = |
| | | buildOperationDemandedQuantityMap(orderBomStructureList, rootProductModelId); |
| | | BigDecimal orderQuantity = defaultDecimal(productionOrder.getQuantity()); |
| | | Map<Long, String> operationNameMap = technologyOperationMapper.selectBatchIds( |
| | | // 遍历处理数据并组装结果 |
| | | routingOperations.stream() |
| | | .map(TechnologyRoutingOperation::getTechnologyOperationId) |
| | | .filter(Objects::nonNull) |
| | |
| | | .max(Integer::compareTo) |
| | | .orElse(null); |
| | | for (TechnologyRoutingOperation sourceOperation : routingOperations) { |
| | | // 订单工序保存的是工艺工序快照,后续报工只依赖快照,不再直接引用工艺主数据。 |
| | | ProductionOrderRoutingOperation targetOperation = new ProductionOrderRoutingOperation(); |
| | | targetOperation.setProductionOrderId(productionOrder.getId()); |
| | | targetOperation.setTechnologyRoutingOperationId(sourceOperation.getId()); |
| | |
| | | ProductionOperationTask task = new ProductionOperationTask(); |
| | | task.setProductionOrderRoutingOperationId(targetOperation.getId()); |
| | | task.setProductionOrderId(productionOrder.getId()); |
| | | task.setPlanQuantity(resolveTaskPlanQuantity( |
| | | sourceOperation, |
| | | operationDemandedQuantityMap, |
| | | productionOrder, |
| | | rootProductModelId)); |
| | | task.setPlanQuantity(orderQuantity); |
| | | task.setCompleteQuantity(BigDecimal.ZERO); |
| | | task.setWorkOrderNo(generateNextTaskNo()); |
| | | task.setStatus(2); |
| | |
| | | .eq(TechnologyRoutingOperationParam::getTechnologyRoutingOperationId, sourceOperation.getId()) |
| | | .orderByAsc(TechnologyRoutingOperationParam::getId)); |
| | | for (TechnologyRoutingOperationParam sourceParam : sourceParams) { |
| | | // 工序执行参数同样做快照,避免工艺参数调整影响已下达订单。 |
| | | ProductionOrderRoutingOperationParam targetParam = new ProductionOrderRoutingOperationParam(); |
| | | targetParam.setProductionOrderId(productionOrder.getId()); |
| | | targetParam.setProductionOrderRoutingOperationId(targetOperation.getId()); |
| | |
| | | return syncedParamCount; |
| | | } |
| | | |
| | | private Map<String, BigDecimal> buildOperationDemandedQuantityMap(List<ProductionBomStructure> bomStructures, |
| | | Long rootProductModelId) { |
| | | if (bomStructures == null || bomStructures.isEmpty()) { |
| | | return Collections.emptyMap(); |
| | | } |
| | | Map<Long, ProductionBomStructure> structureById = bomStructures.stream() |
| | | .filter(item -> item != null && item.getId() != null) |
| | | .collect(Collectors.toMap(ProductionBomStructure::getId, item -> item, (left, right) -> left)); |
| | | Map<String, BigDecimal> demandedQuantityMap = new HashMap<>(); |
| | | Set<String> mergedOutputNodeKeySet = new HashSet<>(); |
| | | for (ProductionBomStructure bomStructure : bomStructures) { |
| | | if (bomStructure == null || bomStructure.getTechnologyOperationId() == null) { |
| | | continue; |
| | | } |
| | | // The BOM row points to the producing operation; task quantity should come from that operation's output node. |
| | | ProductionBomStructure outputNode = resolveOperationOutputNode(bomStructure, structureById); |
| | | Long outputProductModelId = resolveOutputProductModelId(outputNode, rootProductModelId); |
| | | if (outputProductModelId == null) { |
| | | continue; |
| | | } |
| | | String mergedOutputNodeKey = buildOperationOutputNodeKey( |
| | | bomStructure.getTechnologyOperationId(), |
| | | outputNode == null ? null : outputNode.getId(), |
| | | outputProductModelId); |
| | | if (!mergedOutputNodeKeySet.add(mergedOutputNodeKey)) { |
| | | continue; |
| | | } |
| | | // demandedQuantity is already the order-level required output quantity for the current output node. |
| | | BigDecimal demandedQuantity = defaultDecimal(outputNode == null ? null : outputNode.getDemandedQuantity()); |
| | | String key = buildOperationDemandedQuantityKey(bomStructure.getTechnologyOperationId(), outputProductModelId); |
| | | demandedQuantityMap.merge(key, demandedQuantity, BigDecimal::add); |
| | | } |
| | | return demandedQuantityMap; |
| | | } |
| | | |
| | | private BigDecimal resolveTaskPlanQuantity(TechnologyRoutingOperation sourceOperation, |
| | | Map<String, BigDecimal> operationDemandedQuantityMap, |
| | | ProductionOrder productionOrder, |
| | | Long rootProductModelId) { |
| | | if (sourceOperation == null || operationDemandedQuantityMap == null || operationDemandedQuantityMap.isEmpty()) { |
| | | return defaultDecimal(productionOrder == null ? null : productionOrder.getQuantity()); |
| | | } |
| | | Long outputProductModelId = sourceOperation.getProductModelId() != null |
| | | ? sourceOperation.getProductModelId() |
| | | : rootProductModelId; |
| | | String key = buildOperationDemandedQuantityKey(sourceOperation.getTechnologyOperationId(), outputProductModelId); |
| | | BigDecimal planQuantity = operationDemandedQuantityMap.get(key); |
| | | return planQuantity != null ? planQuantity : defaultDecimal(productionOrder == null ? null : productionOrder.getQuantity()); |
| | | } |
| | | |
| | | private String buildOperationDemandedQuantityKey(Long operationId, Long outputProductModelId) { |
| | | return String.valueOf(operationId) + "#" + String.valueOf(outputProductModelId); |
| | | } |
| | | |
| | | private String buildOperationOutputNodeKey(Long operationId, Long outputNodeId, Long outputProductModelId) { |
| | | return String.valueOf(operationId) + "#" + String.valueOf(outputNodeId) + "#" + String.valueOf(outputProductModelId); |
| | | } |
| | | |
| | | private ProductionBomStructure resolveOperationOutputNode(ProductionBomStructure bomStructure, |
| | | Map<Long, ProductionBomStructure> structureById) { |
| | | if (bomStructure == null) { |
| | | return null; |
| | | } |
| | | // The root node is the first output node; child rows use their direct parent as the current operation output. |
| | | if (bomStructure.getParentId() == null) { |
| | | return bomStructure; |
| | | } |
| | | ProductionBomStructure parent = structureById.get(bomStructure.getParentId()); |
| | | return parent != null ? parent : bomStructure; |
| | | } |
| | | |
| | | private Long resolveOutputProductModelId(ProductionBomStructure outputNode, |
| | | Long rootProductModelId) { |
| | | if (outputNode == null) { |
| | | return rootProductModelId; |
| | | } |
| | | return outputNode.getProductModelId() != null ? outputNode.getProductModelId() : rootProductModelId; |
| | | } |
| | | |
| | | private ProductionOrderBom syncProductionOrderBomSnapshot(ProductionOrder productionOrder, TechnologyRouting technologyRouting) { |
| | | // 同步订单BOM快照结构 |
| | | if (technologyRouting.getBomId() == null) { |
| | | return null; |
| | | } |
| | | TechnologyBom technologyBom = technologyBomMapper.selectById(technologyRouting.getBomId()); |
| | | if (technologyBom == null) { |
| | | throw new ServiceException("工艺BOM不存在"); |
| | | } |
| | | // 查询并准备业务数据 |
| | | List<TechnologyBomStructure> structureList = technologyBomStructureMapper.selectList( |
| | | Wrappers.<TechnologyBomStructure>lambdaQuery() |
| | | .eq(TechnologyBomStructure::getBomId, technologyBom.getId()) |
| | | .orderByAsc(TechnologyBomStructure::getId)); |
| | | // 遍历处理数据并组装结果 |
| | | TechnologyBomStructure root = structureList.stream().filter(item -> item.getParentId() == null).findFirst().orElse(null); |
| | | BigDecimal orderQuantity = defaultDecimal(productionOrder.getQuantity()); |
| | | |
| | | ProductionOrderBom orderBom = new ProductionOrderBom(); |
| | | orderBom.setProductionOrderId(productionOrder.getId()); |
| | | orderBom.setBomId(Long.valueOf(technologyBom.getId())); |
| | | orderBom.setProductModelId(root != null ? root.getProductModelId() : productionOrder.getProductModelId()); |
| | | orderBom.setRemark(technologyBom.getRemark()); |
| | | orderBom.setBomNo(technologyBom.getBomNo()); |
| | | orderBom.setVersion(technologyBom.getVersion()); |
| | | // 持久化或输出处理结果 |
| | | productionOrderBomMapper.insert(orderBom); |
| | | |
| | | Map<Long, Long> idMap = new HashMap<>(); |
| | | BigDecimal lastProcessDemandedQuantity = orderQuantity; |
| | | for (TechnologyBomStructure source : structureList) { |
| | | // 子节点 parentId 需要映射成新快照节点 id,才能保留原始 BOM 层级。 |
| | | ProductionBomStructure target = new ProductionBomStructure(); |
| | | target.setProductionOrderId(productionOrder.getId()); |
| | | target.setProductionOrderBomId(orderBom.getId()); |
| | | target.setParentId(source.getParentId() == null ? null : idMap.get(source.getParentId())); |
| | | target.setProductModelId(source.getProductModelId()); |
| | | target.setTechnologyOperationId(source.getOperationId()); |
| | | target.setUnitQuantity(source.getUnitQuantity()); |
| | | target.setDemandedQuantity(lastProcessDemandedQuantity.multiply(source.getUnitQuantity())); |
| | | target.setUnit(source.getUnit()); |
| | | productionBomStructureMapper.insert(target); |
| | | idMap.put(source.getId(), target.getId()); |
| | | lastProcessDemandedQuantity = target.getDemandedQuantity(); |
| | | } |
| | | return orderBom; |
| | | } |
| | | |
| | | private void clearProductionSnapshot(Long productionOrderId) { |
| | | // 清理订单已生成的工艺与BOM快照数据 |
| | | boolean hasPickRecord = productionOrderPickRecordMapper.selectCount( |
| | | // 查询并准备业务数据 |
| | | Wrappers.<ProductionOrderPickRecord>lambdaQuery() |
| | | .eq(ProductionOrderPickRecord::getProductionOrderId, productionOrderId)) > 0; |
| | | // 参数与前置条件校验 |
| | | if (hasPickRecord) { |
| | | throw new ServiceException("生产订单已存在领料记录,不能重新生成快照"); |
| | | } |
| | | List<Long> taskIds = productionOperationTaskMapper.selectList( |
| | | Wrappers.<ProductionOperationTask>lambdaQuery() |
| | | .eq(ProductionOperationTask::getProductionOrderId, productionOrderId)) |
| | | // 遍历处理数据并组装结果 |
| | | .stream().map(ProductionOperationTask::getId).collect(Collectors.toList()); |
| | | if (!taskIds.isEmpty()) { |
| | | // 已有报工记录说明订单已开工,此时不允许再重建快照。 |
| | | boolean started = productionProductMainMapper.selectCount( |
| | | Wrappers.<ProductionProductMain>lambdaQuery() |
| | | .in(ProductionProductMain::getProductionOperationTaskId, taskIds)) > 0; |
| | |
| | | .eq(ProductionOrderRoutingOperation::getProductionOrderId, productionOrderId)); |
| | | productionOrderRoutingMapper.delete(Wrappers.<ProductionOrderRouting>lambdaQuery() |
| | | .eq(ProductionOrderRouting::getProductionOrderId, productionOrderId)); |
| | | productionBomStructureMapper.delete(Wrappers.<ProductionBomStructure>lambdaQuery() |
| | | .eq(ProductionBomStructure::getProductionOrderId, productionOrderId)); |
| | | productionOrderBomMapper.delete(Wrappers.<ProductionOrderBom>lambdaQuery() |
| | | .eq(ProductionOrderBom::getProductionOrderId, productionOrderId)); |
| | | productionOrderPickMapper.delete(Wrappers.<ProductionOrderPick>lambdaQuery() |
| | | .eq(ProductionOrderPick::getProductionOrderId, productionOrderId)); |
| | | } |
| | | |
| | | private LambdaQueryWrapper<ProductionOrder> buildQueryWrapper(ProductionOrderDto dto) { |
| | |
| | | throw new ServiceException("生产订单不存在"); |
| | | } |
| | | return productionOrder.getId(); |
| | | } |
| | | |
| | | @Override |
| | | public List<ProductionOrderPickVo> pick(Long productionOrderId) { |
| | | // 查询订单领料、投料与退料明细 |
| | | if (productionOrderId == null) { |
| | | return Collections.emptyList(); |
| | | } |
| | | |
| | | // 查询并准备业务数据 |
| | | ProductionOrderBom orderBom = productionOrderBomMapper.selectOne( |
| | | Wrappers.<ProductionOrderBom>lambdaQuery() |
| | | .eq(ProductionOrderBom::getProductionOrderId, productionOrderId) |
| | | .orderByDesc(ProductionOrderBom::getId) |
| | | .last("limit 1")); |
| | | if (orderBom == null || orderBom.getId() == null) { |
| | | return Collections.emptyList(); |
| | | } |
| | | |
| | | // 查询完整的BOM结构(包括根节点),用于计算层级需求数量 |
| | | List<ProductionBomStructureVo> bomStructureList = productionBomStructureMapper.listByBomId(orderBom.getId()); |
| | | if (bomStructureList == null || bomStructureList.isEmpty()) { |
| | | return Collections.emptyList(); |
| | | } |
| | | |
| | | // 查询生产订单获取订单数量 |
| | | ProductionOrder productionOrder = productionOrderMapper.selectById(productionOrderId); |
| | | BigDecimal orderQuantity = productionOrder != null ? defaultDecimal(productionOrder.getQuantity()) : BigDecimal.ZERO; |
| | | |
| | | // 构建树形结构并计算层级需求数量 |
| | | Map<Long, ProductionBomStructureVo> structureByIdMap = bomStructureList.stream() |
| | | .filter(s -> s != null && s.getId() != null) |
| | | .collect(Collectors.toMap(ProductionBomStructureVo::getId, s -> s)); |
| | | |
| | | // 按层级计算需求数量:子级需求数量 = 父级需求数量 × 子级单位产出所需数量 |
| | | for (ProductionBomStructureVo structure : bomStructureList) { |
| | | if (structure == null) continue; |
| | | |
| | | if (structure.getParentId() == null || structure.getParentId() == 0) { |
| | | // 根节点:需求数量 = 订单数量 |
| | | structure.setDemandedQuantity(orderQuantity); |
| | | } else { |
| | | // 子节点:需求数量 = 父级需求数量 × 子级单位产出所需数量 |
| | | ProductionBomStructureVo parent = structureByIdMap.get(structure.getParentId()); |
| | | if (parent != null) { |
| | | BigDecimal parentDemandedQty = defaultDecimal(parent.getDemandedQuantity()); |
| | | BigDecimal unitQuantity = defaultDecimal(structure.getUnitQuantity()); |
| | | structure.setDemandedQuantity(parentDemandedQty.multiply(unitQuantity)); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 过滤出非根节点(实际领料项) |
| | | // 排除投入品与产出品相同且比例为1的情况(自身加工,不需要领料) |
| | | List<ProductionBomStructureVo> childStructureList = bomStructureList.stream() |
| | | .filter(s -> s != null && s.getParentId() != null && s.getParentId() != 0) |
| | | .filter(s -> { |
| | | ProductionBomStructureVo parent = structureByIdMap.get(s.getParentId()); |
| | | if (parent == null) { |
| | | return true; |
| | | } |
| | | // 投入品与产出品相同且比例为1时,不需要领料 |
| | | boolean sameProduct = Objects.equals(s.getProductModelId(), parent.getProductModelId()); |
| | | boolean unitRatio = BigDecimal.ONE.compareTo(defaultDecimal(s.getUnitQuantity())) == 0; |
| | | return !(sameProduct && unitRatio); |
| | | }) |
| | | .collect(Collectors.toList()); |
| | | |
| | | // 遍历处理数据并组装结果 |
| | | List<Long> productModelIds = childStructureList.stream() |
| | | .map(ProductionBomStructureVo::getProductModelId) |
| | | .filter(Objects::nonNull) |
| | | .distinct() |
| | | .collect(Collectors.toList()); |
| | | Map<Long, BigDecimal> stockQuantityMap = new HashMap<>(); |
| | | Map<Long, LinkedHashSet<String>> stockBatchNoMap = new HashMap<>(); |
| | | if (!productModelIds.isEmpty()) { |
| | | List<StockInventory> stockList = stockInventoryMapper.selectList( |
| | | Wrappers.<StockInventory>lambdaQuery() |
| | | .in(StockInventory::getProductModelId, productModelIds)); |
| | | for (StockInventory stockItem : stockList) { |
| | | if (stockItem == null || stockItem.getProductModelId() == null) { |
| | | continue; |
| | | } |
| | | Long productModelId = stockItem.getProductModelId(); |
| | | stockQuantityMap.merge(productModelId, defaultDecimal(stockItem.getQualitity()), BigDecimal::add); |
| | | String batchNo = stockItem.getBatchNo(); |
| | | if (batchNo != null && !batchNo.trim().isEmpty()) { |
| | | stockBatchNoMap.computeIfAbsent(productModelId, key -> new LinkedHashSet<>()).add(batchNo); |
| | | } |
| | | } |
| | | } |
| | | |
| | | List<ProductionOrderPickVo> pickList = new ArrayList<>(); |
| | | for (ProductionBomStructureVo structure : childStructureList) { |
| | | if (structure == null || structure.getProductModelId() == null) { |
| | | continue; |
| | | } |
| | | Long productModelId = structure.getProductModelId(); |
| | | ProductionOrderPickVo vo = new ProductionOrderPickVo(); |
| | | vo.setProductModelId(productModelId); |
| | | vo.setOperationName(structure.getOperationName()); |
| | | vo.setTechnologyOperationId(structure.getTechnologyOperationId()); |
| | | vo.setProductName(structure.getProductName()); |
| | | vo.setModel(structure.getModel()); |
| | | vo.setDemandedQuantity(defaultDecimal(structure.getDemandedQuantity())); |
| | | vo.setUnit(structure.getUnit()); |
| | | List<String> batchNoList = stockBatchNoMap.get(productModelId) == null |
| | | ? Collections.emptyList() |
| | | : new ArrayList<>(stockBatchNoMap.get(productModelId)); |
| | | vo.setBatchNoList(batchNoList); |
| | | vo.setStockQuantity(stockQuantityMap.getOrDefault(productModelId, BigDecimal.ZERO)); |
| | | vo.setBom(true); |
| | | pickList.add(vo); |
| | | } |
| | | return pickList; |
| | | } |
| | | |
| | | @Override |