435 lines
17 KiB
PHP
435 lines
17 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 {
|
|
// Check user access for "Semua Dealer"
|
|
$user = auth()->user();
|
|
if ($user && $user->role_id) {
|
|
$role = \App\Models\Role::with('dealers')->find($user->role_id);
|
|
if ($role) {
|
|
$technicianReportService = new \App\Services\TechnicianReportService();
|
|
if ($technicianReportService->isAdminRole($role)) {
|
|
$this->filterInfo[] = "Dealer: Semua Dealer (Admin)";
|
|
} else if ($role->dealers->count() > 0) {
|
|
$dealerNames = $role->dealers->pluck('name')->implode(', ');
|
|
$this->filterInfo[] = "Dealer: Semua Dealer (Pivot: {$dealerNames})";
|
|
} else {
|
|
$this->filterInfo[] = "Dealer: Semua Dealer";
|
|
}
|
|
} else {
|
|
$this->filterInfo[] = "Dealer: Semua Dealer";
|
|
}
|
|
} 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) {
|
|
if ($column === 'A') {
|
|
// Set specific width for column A (No) - don't auto-size
|
|
$sheet->getColumnDimension($column)->setWidth(5);
|
|
} else {
|
|
$sheet->getColumnDimension($column)->setAutoSize(true);
|
|
}
|
|
}
|
|
|
|
} catch (\Exception $e) {
|
|
Log::error('Error applying styles: ' . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
public function columnWidths(): array
|
|
{
|
|
$widths = [
|
|
'A' => 5, // No - reduced from 8 to 5
|
|
'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,
|
|
],
|
|
]);
|
|
}
|
|
];
|
|
}
|
|
}
|