Files
CKB/app/Exports/TechnicianReportExport.php

411 lines
16 KiB
PHP

<?php
namespace App\Exports;
use App\Services\TechnicianReportService;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithStyles;
use Maatwebsite\Excel\Concerns\WithColumnWidths;
use Maatwebsite\Excel\Concerns\WithEvents;
use Maatwebsite\Excel\Events\AfterSheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Style\Fill;
use PhpOffice\PhpSpreadsheet\Style\Border;
use Illuminate\Support\Facades\Log;
use Carbon\Carbon;
class TechnicianReportExport implements FromCollection, WithHeadings, WithStyles, WithColumnWidths, WithEvents
{
protected $dealerId;
protected $startDate;
protected $endDate;
protected $technicianReportService;
protected $mechanics;
protected $headings;
protected $filterInfo;
public function __construct($dealerId = null, $startDate = null, $endDate = null)
{
$this->dealerId = $dealerId;
$this->startDate = $startDate;
$this->endDate = $endDate;
$this->technicianReportService = new TechnicianReportService();
// Get mechanics and prepare headings
$this->prepareHeadings();
$this->prepareFilterInfo();
}
private function prepareHeadings()
{
try {
$reportData = $this->technicianReportService->getTechnicianReportData(
$this->dealerId,
$this->startDate,
$this->endDate
);
$this->mechanics = $reportData['mechanics'];
// Build headings - simplified structure
$this->headings = [
'No',
'Nama Pekerjaan',
'Kode Pekerjaan',
'Kategori'
];
// Add mechanic columns (only total, no completed/pending)
foreach ($this->mechanics as $mechanic) {
$mechanicName = $this->cleanName($mechanic->name);
$this->headings[] = $mechanicName;
}
// Add total column at the end
$this->headings[] = 'Total';
} catch (\Exception $e) {
Log::error('Error preparing headings: ' . $e->getMessage());
$this->headings = ['Error preparing data'];
$this->mechanics = collect();
}
}
private function prepareFilterInfo()
{
$this->filterInfo = [];
// Dealer filter
if ($this->dealerId) {
$dealer = \App\Models\Dealer::find($this->dealerId);
$dealerName = $dealer ? $dealer->name : 'Unknown Dealer';
$this->filterInfo[] = "Dealer: {$dealerName}";
} else {
$this->filterInfo[] = "Dealer: Semua Dealer";
}
// Date range filter
if ($this->startDate && $this->endDate) {
$startDateFormatted = Carbon::parse($this->startDate)->format('d/m/Y');
$endDateFormatted = Carbon::parse($this->endDate)->format('d/m/Y');
$this->filterInfo[] = "Periode: {$startDateFormatted} - {$endDateFormatted}";
} elseif ($this->startDate) {
$startDateFormatted = Carbon::parse($this->startDate)->format('d/m/Y');
$this->filterInfo[] = "Tanggal Mulai: {$startDateFormatted}";
} elseif ($this->endDate) {
$endDateFormatted = Carbon::parse($this->endDate)->format('d/m/Y');
$this->filterInfo[] = "Tanggal Akhir: {$endDateFormatted}";
} else {
$this->filterInfo[] = "Periode: Semua Periode";
}
// Export date
$exportDate = Carbon::now()->format('d/m/Y H:i:s');
$this->filterInfo[] = "Tanggal Export: {$exportDate}";
}
/**
* Clean name for Excel compatibility
*/
private function cleanName($name)
{
// Remove special characters and limit length
$cleaned = preg_replace('/[^a-zA-Z0-9\s]/', '', $name);
$cleaned = trim($cleaned);
// Limit to 31 characters (Excel sheet name limit)
if (strlen($cleaned) > 31) {
$cleaned = substr($cleaned, 0, 31);
}
return $cleaned ?: 'Unknown';
}
public function collection()
{
try {
$reportData = $this->technicianReportService->getTechnicianReportData(
$this->dealerId,
$this->startDate,
$this->endDate
);
$data = [];
$no = 1;
$columnTotals = [];
foreach ($this->mechanics as $mechanic) {
$columnTotals["mechanic_{$mechanic->id}_total"] = 0;
}
$columnTotals['row_total'] = 0;
foreach ($reportData['data'] as $row) {
$rowTotal = 0;
$exportRow = [
$no++,
$row['work_name'],
$row['work_code'],
$row['category_name']
];
foreach ($this->mechanics as $mechanic) {
$mechanicTotal = $row["mechanic_{$mechanic->id}_total"] ?? 0;
$exportRow[] = $mechanicTotal;
$rowTotal += $mechanicTotal;
$columnTotals["mechanic_{$mechanic->id}_total"] += $mechanicTotal;
}
$exportRow[] = $rowTotal;
$columnTotals['row_total'] += $rowTotal;
$data[] = $exportRow;
}
// Add total row
$totalRow = ['', 'TOTAL', '', ''];
foreach ($this->mechanics as $mechanic) {
$totalRow[] = $columnTotals["mechanic_{$mechanic->id}_total"];
}
$totalRow[] = $columnTotals['row_total'];
$data[] = $totalRow;
return collect($data);
} catch (\Exception $e) {
Log::error('Error in collection: ' . $e->getMessage());
return collect([['Error loading data']]);
}
}
public function headings(): array
{
return $this->headings;
}
public function styles(Worksheet $sheet)
{
try {
$lastColumn = $sheet->getHighestColumn();
$lastRow = $sheet->getHighestRow();
// Calculate positions
$titleRow = 1;
$headerRow = 1; // Headers are now in row 2
$dataStartRow = 2; // Data starts in row 3
// Calculate total row position (after data)
$dataRows = count($this->technicianReportService->getTechnicianReportData($this->dealerId, $this->startDate, $this->endDate)['data']);
$totalRow = $dataStartRow + $dataRows;
$filterStartRow = $totalRow + 2; // After total row + empty row
// Style the title row (row 1)
$sheet->getStyle('A' . $titleRow . ':' . $lastColumn . $titleRow)->applyFromArray([
'font' => [
'bold' => true,
'size' => 16,
],
'alignment' => [
'horizontal' => Alignment::HORIZONTAL_CENTER,
'vertical' => Alignment::VERTICAL_CENTER,
],
]);
// Header styling (row 2)
$sheet->getStyle('A' . $headerRow . ':' . $lastColumn . $headerRow)->applyFromArray([
'font' => [
'bold' => true,
'color' => ['rgb' => 'FFFFFF'],
'size' => 10,
],
'fill' => [
'fillType' => Fill::FILL_SOLID,
'startColor' => ['rgb' => '2E5BBA'],
],
'alignment' => [
'horizontal' => Alignment::HORIZONTAL_CENTER,
'vertical' => Alignment::VERTICAL_CENTER,
],
'borders' => [
'allBorders' => [
'borderStyle' => Border::BORDER_THIN,
'color' => ['rgb' => '000000'],
],
],
]);
// Data styling (starting from row 3)
if ($lastRow > $headerRow) {
$dataEndRow = $totalRow;
$sheet->getStyle('A' . $dataStartRow . ':' . $lastColumn . $dataEndRow)->applyFromArray([
'borders' => [
'allBorders' => [
'borderStyle' => Border::BORDER_THIN,
'color' => ['rgb' => '000000'],
],
],
'alignment' => [
'horizontal' => Alignment::HORIZONTAL_CENTER,
'vertical' => Alignment::VERTICAL_CENTER,
],
]);
// Left align text columns
$sheet->getStyle('B' . $dataStartRow . ':D' . $dataEndRow)->getAlignment()->setHorizontal(Alignment::HORIZONTAL_LEFT);
// Style the total row
$sheet->getStyle('A' . $totalRow . ':' . $lastColumn . $totalRow)->applyFromArray([
'font' => [
'bold' => true,
'size' => 11,
],
'fill' => [
'fillType' => Fill::FILL_SOLID,
'startColor' => ['rgb' => 'F2F2F2'],
],
'borders' => [
'allBorders' => [
'borderStyle' => Border::BORDER_THIN,
'color' => ['rgb' => '000000'],
],
],
]);
}
// Style the export information section
if ($filterStartRow <= $lastRow) {
$exportInfoRow = $totalRow + 2; // After total row + empty row
$filterEndRow = $lastRow;
// Style the "INFORMASI EXPORT" title
$sheet->getStyle('A' . $exportInfoRow . ':' . $lastColumn . $exportInfoRow)->applyFromArray([
'font' => [
'bold' => true,
'size' => 12,
],
'fill' => [
'fillType' => Fill::FILL_SOLID,
'startColor' => ['rgb' => 'E6E6E6'],
],
'alignment' => [
'horizontal' => Alignment::HORIZONTAL_LEFT,
'vertical' => Alignment::VERTICAL_CENTER,
],
]);
// Style the filter info rows
$filterInfoStartRow = $exportInfoRow + 3; // After title + empty + "Filter yang Digunakan:"
$sheet->getStyle('A' . $filterInfoStartRow . ':' . $lastColumn . $filterEndRow)->applyFromArray([
'font' => [
'size' => 10,
],
'alignment' => [
'horizontal' => Alignment::HORIZONTAL_LEFT,
'vertical' => Alignment::VERTICAL_TOP,
],
]);
}
// Auto-size columns
foreach (range('A', $lastColumn) as $column) {
$sheet->getColumnDimension($column)->setAutoSize(true);
}
} catch (\Exception $e) {
Log::error('Error applying styles: ' . $e->getMessage());
}
}
public function columnWidths(): array
{
$widths = [
'A' => 8, // No
'B' => 30, // Nama Pekerjaan
'C' => 15, // Kode Pekerjaan
'D' => 20, // Kategori
];
// Add widths for mechanic columns
$currentColumn = 'E';
foreach ($this->mechanics as $mechanic) {
$widths[$currentColumn++] = 15; // Mechanic total
}
// Add width for total column
$widths[$currentColumn] = 15; // Total
return $widths;
}
public function registerEvents(): array
{
return [
AfterSheet::class => function(AfterSheet $event) {
$sheet = $event->sheet->getDelegate();
$highestColumn = $sheet->getHighestColumn();
$highestRow = $sheet->getHighestRow();
// Header styling ONLY for row 1
$sheet->getStyle('A1:' . $highestColumn . '1')->applyFromArray([
'font' => [
'bold' => true,
'color' => ['rgb' => 'FFFFFF'],
'size' => 10,
],
'fill' => [
'fillType' => Fill::FILL_SOLID,
'startColor' => ['rgb' => '2E5BBA'],
],
'alignment' => [
'horizontal' => Alignment::HORIZONTAL_CENTER,
'vertical' => Alignment::VERTICAL_CENTER,
],
'borders' => [
'allBorders' => [
'borderStyle' => Border::BORDER_THIN,
'color' => ['rgb' => '000000'],
],
],
]);
// Total row styling (only last row)
$sheet->getStyle('A' . $highestRow . ':' . $highestColumn . $highestRow)->applyFromArray([
'font' => [
'bold' => true,
'size' => 11,
],
'fill' => [
'fillType' => Fill::FILL_SOLID,
'startColor' => ['rgb' => 'F2F2F2'],
],
'borders' => [
'allBorders' => [
'borderStyle' => Border::BORDER_THIN,
'color' => ['rgb' => '000000'],
],
],
]);
// Export info below table
$infoStartRow = $highestRow + 2;
$sheet->setCellValue('A' . $infoStartRow, 'INFORMASI EXPORT');
$sheet->getStyle('A' . $infoStartRow . ':' . $highestColumn . $infoStartRow)->applyFromArray([
'font' => [
'bold' => true,
'size' => 12,
],
'fill' => [
'fillType' => Fill::FILL_SOLID,
'startColor' => ['rgb' => 'E6E6E6'],
],
'alignment' => [
'horizontal' => Alignment::HORIZONTAL_LEFT,
'vertical' => Alignment::VERTICAL_CENTER,
],
]);
$sheet->setCellValue('A' . ($infoStartRow + 2), 'Filter yang Digunakan:');
$row = $infoStartRow + 3;
foreach ($this->filterInfo as $info) {
$sheet->setCellValue('A' . $row, $info);
$row++;
}
$sheet->getStyle('A' . ($infoStartRow + 2) . ':A' . ($row-1))->applyFromArray([
'font' => [ 'size' => 10 ],
'alignment' => [
'horizontal' => Alignment::HORIZONTAL_LEFT,
'vertical' => Alignment::VERTICAL_TOP,
],
]);
}
];
}
}