true, 'groupName' => 'СВОДНАЯ ИНФОРМАЦИЯ (ВСЕ ОТДЕЛЕНИЯ)'], array_merge(['isTotalRow' => true], $this->grandTotals), ]; $this->allRows = array_merge($summary, $this->rows); } public function title(): string { return $this->sheetTitle; } public function array(): array { return $this->allRows; } /** * Заголовки (с вложенной структурой) */ public function headings(): array { $profitTypeLabels = array_values($this->profitTypes); $countTypes = count($profitTypeLabels); $topRow = array_merge( ['Отделение', 'Поступило по типу оплаты'], array_fill(0, max($countTypes - 1, 0), ''), ['Всего'] ); $subRow = array_merge([''], $profitTypeLabels, ['']); return [ // Шапка отчета (первые 3 строки) [$this->reportName], ['Дата создания: '.now()->format('d.m.Y H:i:s')], [$this->formatDateRange()], [], // Пустая строка для отступа // Первый уровень заголовков (с объединением) $topRow, // Второй уровень заголовков (детализация по типам оплаты) $subRow, ]; } /** * Форматирование дат для шапки */ protected function formatDateRange(): string { if (isset($this->dateRange[0]) && isset($this->dateRange[1])) { $startAt = Carbon::create($this->dateRange[0])->format('d.m.Y H:i'); $endAt = Carbon::create($this->dateRange[1])->format('d.m.Y H:i'); return 'Период: '.$startAt.' - '.$endAt; } return 'Период: За весь период'; } /** * Маппинг данных для каждой строки */ public function map($row): array { $profitTypeIds = array_keys($this->profitTypes); // Заголовок группы (название группы отделений / "Сводная информация") if (isset($row['isGroupHeader']) && $row['isGroupHeader']) { return array_merge([$row['groupName']], array_fill(0, count($profitTypeIds) + 1, '')); } // Обычное отделение и итоговые строки имеют одинаковую форму: department/counts/total $counts = $row['counts'] ?? []; $cells = [$row['department'] ?? '']; foreach ($profitTypeIds as $profitTypeId) { $cells[] = $this->formatZero($counts[$profitTypeId] ?? 0); } $cells[] = $this->formatZero($row['total'] ?? 0); return $cells; } /** * Стилизация Excel файла */ public function styles(Worksheet $sheet) { // Поля (уменьшаем для экономии места) $sheet->getPageMargins()->setLeft(0.2); $sheet->getPageMargins()->setRight(0.2); $sheet->getPageMargins()->setTop(0.2); $sheet->getPageMargins()->setBottom(0.2); $sheet->getPageSetup()->setPaperSize(PageSetup::PAPERSIZE_A4); $sheet->getPageSetup()->setOrientation(PageSetup::ORIENTATION_LANDSCAPE); $sheet->getPageSetup()->setFitToPage(true); $sheet->getPageSetup()->setFitToWidth(1); $sheet->getPageSetup()->setFitToHeight(1); $highestRow = $sheet->getHighestRow(); $countTypes = count($this->profitTypes); $totalColumns = $countTypes + 2; // Отделение + типы оплаты + Всего $highestColumn = Coordinate::stringFromColumnIndex($totalColumns); $firstTypeColumn = Coordinate::stringFromColumnIndex(2); // B $lastTypeColumn = Coordinate::stringFromColumnIndex(1 + $countTypes); // ОБЪЕДИНЕНИЕ ЯЧЕЕК ДЛЯ ШАПКИ (строки 1-3) $sheet->mergeCells('A1:'.$highestColumn.'1'); $sheet->mergeCells('A2:'.$highestColumn.'2'); $sheet->mergeCells('A3:'.$highestColumn.'3'); // ОБЪЕДИНЕНИЕ ДЛЯ ОБЫЧНЫХ ЗАГОЛОВКОВ (Отделение, Всего — по одной колонке на 2 строки) $sheet->mergeCells('A5:A6'); $sheet->mergeCells($highestColumn.'5:'.$highestColumn.'6'); // ОБЪЕДИНЕНИЕ ДЛЯ ВЛОЖЕННОГО ЗАГОЛОВКА "Поступило по типу оплаты" $sheet->mergeCells($firstTypeColumn.'5:'.$lastTypeColumn.'5'); $sheet->setCellValue($firstTypeColumn.'5', 'Поступило по типу оплаты'); // СТИЛИ ДЛЯ ШАПКИ ОТЧЕТА (строки 1-3) $sheet->getStyle('A1:A3')->applyFromArray([ 'font' => [ 'bold' => true, 'size' => 14, ], 'alignment' => [ 'horizontal' => Alignment::HORIZONTAL_LEFT, 'vertical' => Alignment::VERTICAL_CENTER, ], ]); // СТИЛИ ДЛЯ ЗАГОЛОВКОВ (строки 5 и 6) $sheet->getStyle('A5:'.$highestColumn.'6')->applyFromArray([ 'font' => [ 'bold' => true, 'color' => ['argb' => '000000'], 'size' => 11, ], 'alignment' => [ 'horizontal' => Alignment::HORIZONTAL_CENTER, 'vertical' => Alignment::VERTICAL_CENTER, 'wrapText' => true, ], 'borders' => [ 'allBorders' => [ 'borderStyle' => Border::BORDER_THIN, 'color' => ['argb' => '2d2d30'], ], ], ]); // СТИЛИ ДЛЯ ВСЕХ СТРОК С ДАННЫМИ $indexData = 0; for ($row = 7; $row <= $highestRow; $row++) { $currentRowInData = $this->allRows[$indexData]; // Заголовки групп (Сводная информация, Хирургические отделения, Терапевтические отделения...) if (array_key_exists('isGroupHeader', $currentRowInData)) { $sheet->getStyle('A'.$row.':'.$highestColumn.$row)->applyFromArray([ 'font' => [ 'bold' => true, 'size' => 11, ], 'fill' => [ 'fillType' => Fill::FILL_SOLID, 'startColor' => ['argb' => 'D9E1F2'], // Светло-синий ], ]); $sheet->mergeCells('A'.$row.':'.$highestColumn.$row); } // Итоговые строки (ИТОГО:, ОБЩИЕ ИТОГИ:) if (array_key_exists('isTotalRow', $currentRowInData)) { $sheet->getStyle('A'.$row.':'.$highestColumn.$row)->applyFromArray([ 'font' => [ 'bold' => true, ], 'fill' => [ 'fillType' => Fill::FILL_SOLID, 'startColor' => ['argb' => 'F2F2F2'], // Серый ], 'borders' => [ 'top' => [ 'borderStyle' => Border::BORDER_MEDIUM, 'color' => ['argb' => '000000'], ], ], ]); } if ($indexData < count($this->allRows) - 1) { $indexData++; } } // ГРАНИЦЫ ДЛЯ ВСЕЙ ТАБЛИЦЫ $sheet->getStyle('A5:'.$highestColumn.$highestRow)->applyFromArray([ 'borders' => [ 'allBorders' => [ 'borderStyle' => Border::BORDER_THIN, 'color' => ['argb' => '000000'], ], ], ]); // ВЫРАВНИВАНИЕ ДЛЯ ЧИСЛОВЫХ КОЛОНОК $sheet->getStyle('B7:'.$highestColumn.$highestRow)->applyFromArray([ 'alignment' => [ 'horizontal' => Alignment::HORIZONTAL_CENTER, ], ]); // ВЫРАВНИВАНИЕ ДЛЯ НАЗВАНИЙ ОТДЕЛЕНИЙ $sheet->getStyle('A7:A'.$highestRow)->applyFromArray([ 'alignment' => [ 'horizontal' => Alignment::HORIZONTAL_LEFT, 'vertical' => Alignment::VERTICAL_CENTER, ], ]); // Ширина колонок $sheet->getColumnDimension('A')->setWidth(30); // Отделение for ($colIndex = 2; $colIndex <= $totalColumns; $colIndex++) { $sheet->getColumnDimension(Coordinate::stringFromColumnIndex($colIndex))->setWidth(16); } // Увеличиваем высоту строк с заголовками $sheet->getRowDimension(5)->setRowHeight(30); $sheet->getRowDimension(6)->setRowHeight(25); return []; } /** * Форматирование нулевых значений */ protected function formatZero($value) { if (is_null($value) || $value === '' || $value === []) { return '—'; } if (is_array($value)) { return '0'; } if (is_numeric($value) && $value == 0) { return '0'; } return $value; } }