| | |
| | | import com.ruoyi.productionPlan.enums.AddressRegionEnum; |
| | | import com.ruoyi.productionPlan.mapper.SalesDeliveryMapper; |
| | | import com.ruoyi.productionPlan.pojo.SalesDelivery; |
| | | import com.ruoyi.productionPlan.pojo.ProductionPlan; |
| | | import com.ruoyi.productionPlan.service.ProductionPlanService; |
| | | import com.ruoyi.project.system.domain.SysDept; |
| | | import com.ruoyi.project.system.domain.SysDictData; |
| | | import com.ruoyi.project.system.mapper.SysDeptMapper; |
| | |
| | | import com.ruoyi.staff.mapper.StaffOnJobMapper; |
| | | import com.ruoyi.staff.pojo.StaffOnJob; |
| | | import com.ruoyi.stock.mapper.StockInventoryMapper; |
| | | import com.ruoyi.energy.mapper.EnergyConsumptionDetailMapper; |
| | | import com.ruoyi.energy.mapper.EnergyMapper; |
| | | import com.ruoyi.energy.pojo.Energy; |
| | | import com.ruoyi.energy.dto.EnergyCostDto; |
| | | import com.ruoyi.energy.mapper.EnergyConsumptionDetailMapper; |
| | | import com.ruoyi.energy.vo.EnergyStatisticsVo; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | |
| | | |
| | | @Autowired |
| | | private SalesDeliveryMapper salesDeliveryMapper; |
| | | |
| | | @Autowired |
| | | private EnergyMapper energyMapper; |
| | | |
| | | |
| | | @Override |
| | | public HomeBusinessDto business() { |
| | |
| | | lastDay = date.with(TemporalAdjusters.lastDayOfYear()); |
| | | } |
| | | LocalDate finalLastDay = lastDay; |
| | | salesDeliveries = salesDeliveries |
| | | List<SalesDelivery> salesDeliverie = salesDeliveries |
| | | .stream() |
| | | .filter(delivery -> { |
| | | LocalDate deliveryDate = delivery.getDeliveryDate(); |
| | |
| | | AddressRegionEnum::getRegionName, // 区域名作为key |
| | | enumItem -> 0L // 初始值全部为0 |
| | | )); |
| | | if (!CollectionUtils.isEmpty(salesDeliveries)) { |
| | | if (!CollectionUtils.isEmpty(salesDeliverie)) { |
| | | // 按区域分组,统计每个区域的销量总和 |
| | | regionCountMap = salesDeliveries.stream() |
| | | regionCountMap = salesDeliverie.stream() |
| | | .filter(delivery -> delivery.getDeliveryPlace() != null) |
| | | .collect(Collectors.groupingBy( |
| | | delivery -> { |
| | |
| | | Collectors.summingLong(delivery -> delivery.getVolume() != null ? delivery.getVolume().longValue() : 0L) |
| | | )); |
| | | } |
| | | regionCountMap.put("全部", salesDeliveries.stream() |
| | | regionCountMap.put("全部", salesDeliverie.stream() |
| | | .mapToLong(item -> item.getVolume() != null ? item.getVolume().longValue() : 0L) |
| | | .sum()); |
| | | maps.add(regionCountMap); |
| | |
| | | salesTotalDto.setDates(dates); |
| | | salesTotalDto.setCustomerTrends(maps); |
| | | return salesTotalDto; |
| | | } |
| | | |
| | | @Override |
| | | public List<SalesTotalDetailDto> salesRanking(SalesDeliveryDto salesDeliveryDto) { |
| | | List<SalesTotalDetailDto> salesTotalDetailDtos = new ArrayList<>(); |
| | | List<LocalDate> dates = convertDateList(salesDeliveryDto.getDays()); |
| | | List<SalesDelivery> salesDeliveries = salesDeliveryMapper.selectList(Wrappers.<SalesDelivery>lambdaQuery() |
| | | .eq(SalesDelivery::getProductName, salesDeliveryDto.getType())); |
| | | for (LocalDate date : dates) { |
| | | LocalDate firstDay = date.with(TemporalAdjusters.firstDayOfMonth()); |
| | | LocalDate lastDay = date.with(TemporalAdjusters.lastDayOfMonth()); |
| | | if (salesDeliveryDto.getDays().equals("年")) { |
| | | lastDay = date.with(TemporalAdjusters.lastDayOfYear()); |
| | | } |
| | | LocalDate finalLastDay = lastDay; |
| | | salesDeliveries = salesDeliveries |
| | | .stream() |
| | | .filter(delivery -> { |
| | | LocalDate deliveryDate = delivery.getDeliveryDate(); |
| | | return !deliveryDate.isBefore(firstDay) && !deliveryDate.isAfter(finalLastDay); |
| | | }) |
| | | .collect(Collectors.toList()); |
| | | Map<String, Long> regionCountMap = Arrays.stream(AddressRegionEnum.values()) |
| | | .collect(Collectors.toMap( |
| | | AddressRegionEnum::getRegionName, // 区域名作为key |
| | | enumItem -> 0L // 初始值全部为0 |
| | | )); |
| | | if (!CollectionUtils.isEmpty(salesDeliveries)) { |
| | | // 按区域分组,统计每个区域的销量总和 |
| | | regionCountMap = salesDeliveries.stream() |
| | | .filter(delivery -> delivery.getDeliveryPlace() != null) |
| | | .collect(Collectors.groupingBy( |
| | | delivery -> { |
| | | AddressRegionEnum regionEnum = AddressRegionEnum.matchRegion(delivery.getDeliveryPlace()); |
| | | return regionEnum != null ? regionEnum.getRegionName() : null; |
| | | }, |
| | | Collectors.summingLong(delivery -> delivery.getVolume() != null ? delivery.getVolume().longValue() : 0L) |
| | | )); |
| | | } |
| | | regionCountMap.put("全部", salesDeliveries.stream() |
| | | .mapToLong(item -> item.getVolume() != null ? item.getVolume().longValue() : 0L) |
| | | .sum()); |
| | | SalesTotalDetailDto salesTotalDetailDto = new SalesTotalDetailDto(); |
| | | salesTotalDetailDto.setDate(date); |
| | | salesTotalDetailDto.setType(salesDeliveryDto.getType()); |
| | | salesTotalDetailDtos.add(salesTotalDetailDto); |
| | | } |
| | | return salesTotalDetailDtos; |
| | | } |
| | | |
| | | @Override |
| | |
| | | lastDay = date.with(TemporalAdjusters.lastDayOfYear()); |
| | | } |
| | | LocalDate finalLastDay = lastDay; |
| | | salesDeliveries = salesDeliveries |
| | | List<SalesDelivery> salesDeliverie = salesDeliveries |
| | | .stream() |
| | | .filter(delivery -> { |
| | | LocalDate deliveryDate = delivery.getDeliveryDate(); |
| | |
| | | AddressRegionEnum::getRegionName, // 区域名作为key |
| | | enumItem -> 0L // 初始值全部为0 |
| | | )); |
| | | if (!CollectionUtils.isEmpty(salesDeliveries)) { |
| | | if (!CollectionUtils.isEmpty(salesDeliverie)) { |
| | | // 按区域分组,统计每个区域的销量总和 |
| | | regionCountMap = salesDeliveries.stream() |
| | | regionCountMap = salesDeliverie.stream() |
| | | .filter(delivery -> delivery.getDeliveryPlace() != null) |
| | | .collect(Collectors.groupingBy( |
| | | delivery -> { |
| | |
| | | Collectors.summingLong(delivery -> delivery.getPrice() != null ? delivery.getPrice().longValue() : 0L) |
| | | )); |
| | | } |
| | | regionCountMap.put("全部", salesDeliveries.stream() |
| | | regionCountMap.put("全部", salesDeliverie.stream() |
| | | .mapToLong(item -> item.getPrice() != null ? item.getPrice().longValue() : 0L) |
| | | .sum()); |
| | | maps.add(regionCountMap); |
| | |
| | | salesTotalDto.setDates(dates); |
| | | salesTotalDto.setCustomerTrends(maps); |
| | | return salesTotalDto; |
| | | } |
| | | |
| | | @Override |
| | | public List<SalesTotalDetailDto> salesDataRanking(SalesDeliveryDto salesDeliveryDto) { |
| | | List<SalesTotalDetailDto> salesTotalDetailDtos = new ArrayList<>(); |
| | | List<LocalDate> dates = convertDateList(salesDeliveryDto.getDays()); |
| | | |
| | | return salesTotalDetailDtos; |
| | | } |
| | | |
| | | @Override |
| | |
| | | } |
| | | |
| | | if (!CollectionUtils.isEmpty(costDtos)) { |
| | | // 获取单位 |
| | | List<Energy> energies = energyMapper.selectList(Wrappers.<Energy>lambdaQuery() |
| | | .in(Energy::getEnergyTyep, Arrays.asList("水", "电", "气"))); |
| | | String waterUnit = energies.stream().filter(e -> "水".equals(e.getEnergyTyep())).map(Energy::getUnit).findFirst().orElse(""); |
| | | String electricityUnit = energies.stream().filter(e -> "电".equals(e.getEnergyTyep())).map(Energy::getUnit).findFirst().orElse(""); |
| | | String steamUnit = energies.stream().filter(e -> "气".equals(e.getEnergyTyep())).map(Energy::getUnit).findFirst().orElse(""); |
| | | |
| | | for (EnergyCostDto costDto : costDtos) { |
| | | String dateStr = costDto.getMeterReadingDate(); |
| | | String label = ""; |
| | |
| | | dayDto.setWater(water); |
| | | dayDto.setElectricity(electricity); |
| | | dayDto.setSteam(steam); |
| | | dayDto.setWaterUnit(waterUnit); |
| | | dayDto.setElectricityUnit(electricityUnit); |
| | | dayDto.setSteamUnit(steamUnit); |
| | | } |
| | | } |
| | | } |
| | |
| | | if (CollectionUtils.isEmpty(mainList)) return new ArrayList<>(); |
| | | |
| | | List<Long> mainIds = mainList.stream().map(ProductionProductMain::getId).collect(Collectors.toList()); |
| | | |
| | | |
| | | // 获取投入明细 |
| | | List<ProductionProductInput> allInputs = productionProductInputService.list(Wrappers.<ProductionProductInput>lambdaQuery() |
| | | .in(ProductionProductInput::getProductMainId, mainIds)); |
| | |
| | | |
| | | return result; |
| | | } |
| | | |
| | | @Override |
| | | public Map<String, Long> manage() { |
| | | Map<String, Long> map = new HashMap<>(); |
| | | //生产订单总数 |
| | | Long total = productOrderMapper.selectCount(new QueryWrapper<ProductOrder>().ne("status", 4)); |
| | | map.put("total", total); |
| | | //已完成订单数 |
| | | Long completed = productOrderMapper.selectCount(new QueryWrapper<ProductOrder>().eq("status", 3)); |
| | | map.put("completed", completed); |
| | | //未完成订单数 |
| | | Long uncompleted = productOrderMapper.selectCount(new QueryWrapper<ProductOrder>().eq("status", 1)); |
| | | map.put("uncompleted", uncompleted); |
| | | //部分完成订单数 |
| | | Long partialCompleted = productOrderMapper.selectCount(new QueryWrapper<ProductOrder>().eq("status", 2)); |
| | | map.put("partialCompleted", partialCompleted); |
| | | //来料检总数 |
| | | Long materialInspection = qualityInspectMapper.selectCount(new QueryWrapper<QualityInspect>().eq("inspect_type", 0)); |
| | | map.put("materialInspection", materialInspection); |
| | | return map; |
| | | } |
| | | |
| | | @Autowired |
| | | private ProductionPlanService productionPlanService; |
| | | |
| | | @Override |
| | | public List<PlanTrendsDto> planTrends(Integer type) { |
| | | LocalDate now = LocalDate.now(); |
| | | LocalDate startDate; |
| | | DateTimeFormatter formatter; |
| | | int points; |
| | | |
| | | if (type == null) type = 1; |
| | | |
| | | if (type == 1) { // 周 |
| | | startDate = now.with(DayOfWeek.MONDAY); |
| | | points = (int) ChronoUnit.DAYS.between(startDate, now) + 1; |
| | | formatter = DateTimeFormatter.ofPattern("MM-dd"); |
| | | } else if (type == 2) { // 月 |
| | | startDate = now.with(TemporalAdjusters.firstDayOfMonth()); |
| | | points = now.getDayOfMonth(); |
| | | formatter = DateTimeFormatter.ofPattern("MM-dd"); |
| | | } else { // 年 |
| | | startDate = now.with(TemporalAdjusters.firstDayOfYear()); |
| | | points = now.getMonthValue(); |
| | | formatter = DateTimeFormatter.ofPattern("yyyy-MM"); |
| | | } |
| | | |
| | | Map<String, PlanTrendsDto> resultMap = new LinkedHashMap<>(); |
| | | for (int i = 0; i < points; i++) { |
| | | String label = (type == 3) |
| | | ? startDate.plusMonths(i).format(formatter) |
| | | : startDate.plusDays(i).format(formatter); |
| | | PlanTrendsDto dto = new PlanTrendsDto(); |
| | | dto.setDateStr(label); |
| | | dto.setPlannedVolume(BigDecimal.ZERO); |
| | | dto.setLowerVolume(BigDecimal.ZERO); |
| | | dto.setCompletionVolume(BigDecimal.ZERO); |
| | | resultMap.put(label, dto); |
| | | } |
| | | // 统计计划量 |
| | | List<ProductionPlan> plans = productionPlanService.list(Wrappers.<ProductionPlan>lambdaQuery() |
| | | .ge(ProductionPlan::getStartDate, Date.from(startDate.atStartOfDay(ZoneId.systemDefault()).toInstant()))); |
| | | for (ProductionPlan plan : plans) { |
| | | if (plan.getStartDate() == null || plan.getVolume() == null) continue; |
| | | LocalDate d = plan.getStartDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); |
| | | String label = d.format(formatter); |
| | | if (resultMap.containsKey(label)) { |
| | | PlanTrendsDto dto = resultMap.get(label); |
| | | dto.setPlannedVolume(dto.getPlannedVolume().add(plan.getVolume())); |
| | | } |
| | | } |
| | | // 统计下发量 |
| | | List<ProductOrder> orders = productOrderMapper.selectList(Wrappers.<ProductOrder>lambdaQuery() |
| | | .ge(ProductOrder::getStartTime, startDate.atStartOfDay())); |
| | | for (ProductOrder order : orders) { |
| | | if (order.getStartTime() == null || order.getQuantity() == null) continue; |
| | | LocalDate d = order.getStartTime().toLocalDate(); |
| | | String label = d.format(formatter); |
| | | if (resultMap.containsKey(label)) { |
| | | PlanTrendsDto dto = resultMap.get(label); |
| | | dto.setLowerVolume(dto.getLowerVolume().add(order.getQuantity())); |
| | | } |
| | | } |
| | | // 统计完成量 |
| | | List<ProductionProductOutput> outputs = productionProductOutputMapper.selectList(Wrappers.<ProductionProductOutput>lambdaQuery() |
| | | .ge(ProductionProductOutput::getCreateTime, startDate.atStartOfDay())); |
| | | for (ProductionProductOutput output : outputs) { |
| | | if (output.getCreateTime() == null || output.getQuantity() == null) continue; |
| | | LocalDate d = output.getCreateTime().toLocalDate(); |
| | | String label = d.format(formatter); |
| | | if (resultMap.containsKey(label)) { |
| | | PlanTrendsDto dto = resultMap.get(label); |
| | | dto.setCompletionVolume(dto.getCompletionVolume().add(output.getQuantity())); |
| | | } |
| | | } |
| | | |
| | | return new ArrayList<>(resultMap.values()); |
| | | } |
| | | @Override |
| | | public List<RawMaterialProductionDto> rawMaterialProductions(String month) { |
| | | YearMonth yearMonth; |
| | | try { |
| | | yearMonth = YearMonth.parse(month); |
| | | } catch (Exception e) { |
| | | log.error("解析月份失败: {}", month); |
| | | return Collections.emptyList(); |
| | | } |
| | | LocalDateTime monthStart = yearMonth.atDay(1).atStartOfDay(); |
| | | LocalDateTime monthEnd = yearMonth.atEndOfMonth().atTime(LocalTime.MAX); |
| | | |
| | | // 获取产品类型字典 |
| | | List<SysDictData> sysDictDataList = sysDictDataMapper.selectDictDataByType("product_type"); |
| | | Map<Long, String> dictCodeToLabelMap = sysDictDataList.stream() |
| | | .filter(d -> d.getDictCode() != null && d.getDictLabel() != null) |
| | | .collect(Collectors.toMap(SysDictData::getDictCode, SysDictData::getDictLabel)); |
| | | |
| | | // 查询当月报工主表 |
| | | List<ProductionProductMain> mainList = productionProductMainService.list(Wrappers.<ProductionProductMain>lambdaQuery() |
| | | .between(ProductionProductMain::getReportingTime, monthStart, monthEnd)); |
| | | |
| | | if (CollectionUtils.isEmpty(mainList)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | |
| | | List<Long> mainIds = mainList.stream().map(ProductionProductMain::getId).collect(Collectors.toList()); |
| | | List<Long> orderIds = mainList.stream().map(ProductionProductMain::getProductOrderId).distinct().collect(Collectors.toList()); |
| | | Map<Long, ProductionProductMain> mainMap = mainList.stream().collect(Collectors.toMap(ProductionProductMain::getId, m -> m)); |
| | | |
| | | // 获取订单对应的产品类型 |
| | | Map<Long, Long> orderRouteMap = new HashMap<>(); |
| | | if (!CollectionUtils.isEmpty(orderIds)) { |
| | | List<ProductionOrderRoute> routes = productionOrderRouteService.list(Wrappers.<ProductionOrderRoute>lambdaQuery() |
| | | .in(ProductionOrderRoute::getOrderId, orderIds)); |
| | | orderRouteMap = routes.stream() |
| | | .filter(r -> r.getOrderId() != null && r.getDictCode() != null) |
| | | .collect(Collectors.toMap(ProductionOrderRoute::getOrderId, ProductionOrderRoute::getDictCode, (v1, v2) -> v1)); |
| | | } |
| | | |
| | | // 获取投入和产出明细 |
| | | List<ProductionProductInput> allInputs = productionProductInputService.list(Wrappers.<ProductionProductInput>lambdaQuery() |
| | | .in(ProductionProductInput::getProductMainId, mainIds)); |
| | | List<ProductionProductOutput> allOutputs = productionProductOutputService.list(Wrappers.<ProductionProductOutput>lambdaQuery() |
| | | .in(ProductionProductOutput::getProductMainId, mainIds)); |
| | | |
| | | // 获取 SKU 的物料名称 |
| | | Set<Long> inputSkuIds = allInputs.stream().map(ProductionProductInput::getProductId).filter(Objects::nonNull).collect(Collectors.toSet()); |
| | | Map<Long, String> skuToMaterialNameMap = new HashMap<>(); |
| | | if (!inputSkuIds.isEmpty()) { |
| | | List<ProductMaterialSku> skus = productMaterialSkuService.listByIds(inputSkuIds); |
| | | Set<Long> productIds = skus.stream().map(ProductMaterialSku::getProductId).filter(Objects::nonNull).collect(Collectors.toSet()); |
| | | Map<Long, String> materialNameMap = productMaterialService.listByIds(productIds).stream() |
| | | .collect(Collectors.toMap(ProductMaterial::getId, ProductMaterial::getProductName)); |
| | | skuToMaterialNameMap = skus.stream() |
| | | .filter(s -> s.getProductId() != null && materialNameMap.containsKey(s.getProductId())) |
| | | .collect(Collectors.toMap(ProductMaterialSku::getId, s -> materialNameMap.get(s.getProductId()))); |
| | | } |
| | | |
| | | BigDecimal yield35 = BigDecimal.ZERO; |
| | | BigDecimal yield50 = BigDecimal.ZERO; |
| | | BigDecimal yieldPlate = BigDecimal.ZERO; |
| | | |
| | | // 材质名称 -> [3.5用量, 5.0用量, 板材用量] |
| | | Map<String, BigDecimal[]> materialConsumptionMap = new LinkedHashMap<>(); |
| | | |
| | | // 统计产量 |
| | | for (ProductionProductOutput output : allOutputs) { |
| | | ProductionProductMain main = mainMap.get(output.getProductMainId()); |
| | | if (main == null) continue; |
| | | Long dictCode = orderRouteMap.get(main.getProductOrderId()); |
| | | String label = dictCodeToLabelMap.get(dictCode); |
| | | BigDecimal qty = output.getQuantity() != null ? output.getQuantity() : BigDecimal.ZERO; |
| | | |
| | | if (label != null) { |
| | | if (label.contains("3.5")) yield35 = yield35.add(qty); |
| | | else if (label.contains("5.0")) yield50 = yield50.add(qty); |
| | | else if (label.contains("板材")) yieldPlate = yieldPlate.add(qty); |
| | | } |
| | | } |
| | | |
| | | // 统计消耗量 |
| | | for (ProductionProductInput input : allInputs) { |
| | | ProductionProductMain main = mainMap.get(input.getProductMainId()); |
| | | if (main == null) continue; |
| | | Long dictCode = orderRouteMap.get(main.getProductOrderId()); |
| | | String label = dictCodeToLabelMap.get(dictCode); |
| | | String materialName = skuToMaterialNameMap.get(input.getProductId()); |
| | | if (materialName == null) continue; |
| | | |
| | | BigDecimal qty = UnitUtils.convertValueToTon(input.getQuantity(), input.getUnit()); |
| | | |
| | | materialConsumptionMap.putIfAbsent(materialName, new BigDecimal[]{BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO}); |
| | | BigDecimal[] consumptions = materialConsumptionMap.get(materialName); |
| | | |
| | | if (label != null) { |
| | | if (label.contains("3.5")) consumptions[0] = consumptions[0].add(qty); |
| | | else if (label.contains("5.0")) consumptions[1] = consumptions[1].add(qty); |
| | | else if (label.contains("板材")) consumptions[2] = consumptions[2].add(qty); |
| | | } |
| | | } |
| | | |
| | | List<RawMaterialProductionDto> result = new ArrayList<>(); |
| | | |
| | | // 产量行 |
| | | RawMaterialProductionDto yieldRow = new RawMaterialProductionDto(); |
| | | yieldRow.setItemName("产量"); |
| | | yieldRow.setUsage35(yield35); |
| | | yieldRow.setUsage50(yield50); |
| | | yieldRow.setUsagePlate(yieldPlate); |
| | | yieldRow.setTotalUsage(yield35.add(yield50).add(yieldPlate)); |
| | | yieldRow.setBlockTotalUsage(yield35.add(yield50)); |
| | | result.add(yieldRow); |
| | | |
| | | // 物料行 |
| | | List<String> targetMaterials = Arrays.asList( |
| | | "粉煤灰", "水泥", "石灰", "铝粉", "石膏", "脱模剂", |
| | | "打包带", "冷拔丝", "氧化镁", "卡扣", "防腐剂" |
| | | ); |
| | | |
| | | for (String materialName : targetMaterials) { |
| | | BigDecimal[] consumptions = materialConsumptionMap.getOrDefault(materialName, |
| | | new BigDecimal[]{BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO}); |
| | | |
| | | RawMaterialProductionDto row = new RawMaterialProductionDto(); |
| | | row.setItemName(materialName); |
| | | row.setUsage35(consumptions[0]); |
| | | row.setUsage50(consumptions[1]); |
| | | row.setUsagePlate(consumptions[2]); |
| | | |
| | | // 计算单耗 |
| | | row.setUnitConsumption35(calculateUnitConsumption(consumptions[0], yield35)); |
| | | row.setUnitConsumption50(calculateUnitConsumption(consumptions[1], yield50)); |
| | | row.setUnitConsumptionPlate(calculateUnitConsumption(consumptions[2], yieldPlate)); |
| | | |
| | | row.setTotalUsage(consumptions[0].add(consumptions[1]).add(consumptions[2])); |
| | | row.setBlockTotalUsage(consumptions[0].add(consumptions[1])); |
| | | result.add(row); |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | private BigDecimal calculateUnitConsumption(BigDecimal usage, BigDecimal yield) { |
| | | if (yield == null || yield.compareTo(BigDecimal.ZERO) == 0) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | | return usage.divide(yield, 4, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | } |