diff --git a/app/Exports/TechnicianReportExport.php b/app/Exports/TechnicianReportExport.php new file mode 100644 index 0000000..67fe940 --- /dev/null +++ b/app/Exports/TechnicianReportExport.php @@ -0,0 +1,411 @@ +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, + ], + ]); + } + ]; + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Reports/ReportTechniciansController.php b/app/Http/Controllers/Reports/ReportTechniciansController.php new file mode 100644 index 0000000..6a7d362 --- /dev/null +++ b/app/Http/Controllers/Reports/ReportTechniciansController.php @@ -0,0 +1,179 @@ +technicianReportService = $technicianReportService; + } + + public function index(Request $request) + { + $menu = Menu::where('link','reports.technician.index')->first(); + abort_if(!Gate::allows('view', $menu), 403); + + return view('reports.technician'); + } + + /** + * Get dealers for filter dropdown + */ + public function getDealers() + { + try { + $dealers = $this->technicianReportService->getDealers(); + // Default ke "Semua Dealer" (tidak ada dealer yang terselect) + return response()->json([ + 'status' => 'success', + 'data' => $dealers, + 'default_dealer' => null + ]); + } catch (\Exception $e) { + Log::error('Error getting dealers: ' . $e->getMessage()); + return response()->json([ + 'status' => 'error', + 'message' => 'Gagal mengambil data dealer' + ], 500); + } + } + + /** + * Get technician report data for DataTable + */ + public function getData(Request $request) + { + try { + $dealerId = $request->input('dealer_id'); + $startDate = $request->input('start_date'); + $endDate = $request->input('end_date'); + + Log::info('Requesting technician report data:', [ + 'dealer_id' => $dealerId, + 'start_date' => $startDate, + 'end_date' => $endDate + ]); + + $reportData = $this->technicianReportService->getTechnicianReportData( + $dealerId, + $startDate, + $endDate + ); + + Log::info('Technician report data response:', [ + 'data_count' => count($reportData['data']), + 'mechanics_count' => $reportData['mechanics']->count(), + 'works_count' => $reportData['works']->count(), + 'mechanics' => $reportData['mechanics']->map(function($mechanic) { + return [ + 'id' => $mechanic->id, + 'name' => $mechanic->name, + 'role_id' => $mechanic->role_id + ]; + }) + ]); + + return response()->json([ + 'status' => 'success', + 'data' => $reportData['data'], + 'mechanics' => $reportData['mechanics'], + 'works' => $reportData['works'] + ]); + } catch (\Exception $e) { + Log::error('Error getting technician report data: ' . $e->getMessage(), [ + 'dealer_id' => $request->input('dealer_id'), + 'start_date' => $request->input('start_date'), + 'end_date' => $request->input('end_date'), + 'trace' => $e->getTraceAsString() + ]); + + return response()->json([ + 'status' => 'error', + 'message' => 'Gagal mengambil data laporan teknisi: ' . $e->getMessage() + ], 500); + } + } + + /** + * Get technician report data for Yajra DataTable + */ + public function getDataTable(Request $request) + { + try { + $dealerId = $request->input('dealer_id'); + $startDate = $request->input('start_date'); + $endDate = $request->input('end_date'); + + Log::info('Requesting technician report data for DataTable:', [ + 'dealer_id' => $dealerId, + 'start_date' => $startDate, + 'end_date' => $endDate + ]); + + $reportData = $this->technicianReportService->getTechnicianReportDataForDataTable( + $dealerId, + $startDate, + $endDate + ); + + return $reportData; + } catch (\Exception $e) { + Log::error('Error getting technician report data for DataTable: ' . $e->getMessage(), [ + 'dealer_id' => $request->input('dealer_id'), + 'start_date' => $request->input('start_date'), + 'end_date' => $request->input('end_date'), + 'trace' => $e->getTraceAsString() + ]); + + return response()->json([ + 'error' => 'Gagal mengambil data laporan teknisi: ' . $e->getMessage() + ], 500); + } + } + + /** + * Export technician report to Excel + */ + public function export(Request $request) + { + try { + $dealerId = $request->input('dealer_id'); + $startDate = $request->input('start_date'); + $endDate = $request->input('end_date'); + + Log::info('Exporting technician report', [ + 'dealer_id' => $dealerId, + 'start_date' => $startDate, + 'end_date' => $endDate + ]); + + return Excel::download(new TechnicianReportExport($dealerId, $startDate, $endDate), 'laporan_teknisi_' . date('Y-m-d') . '.xlsx'); + + } catch (\Exception $e) { + Log::error('Error exporting technician report: ' . $e->getMessage(), [ + 'dealer_id' => $request->input('dealer_id'), + 'start_date' => $request->input('start_date'), + 'end_date' => $request->input('end_date') + ]); + + return response()->json([ + 'status' => 'error', + 'message' => 'Gagal export laporan: ' . $e->getMessage() + ], 500); + } + } + + +} diff --git a/app/Services/TechnicianReportService.php b/app/Services/TechnicianReportService.php new file mode 100644 index 0000000..6b9427b --- /dev/null +++ b/app/Services/TechnicianReportService.php @@ -0,0 +1,398 @@ +get(); + Log::info('All users in database:', [ + 'total_users' => $allUsers->count(), + 'users_with_roles' => $allUsers->map(function($user) { + $roleName = 'No role'; + if ($user->role) { + $roleName = is_string($user->role) ? $user->role : $user->role->name; + } + return [ + 'id' => $user->id, + 'name' => $user->name, + 'role_id' => $user->role_id, + 'role_name' => $roleName, + 'dealer_id' => $user->dealer_id + ]; + }) + ]); + + // Get all works with category in single query + $works = Work::with(['category']) + ->orderBy('name') + ->get(); + + // Get all mechanics (users with role name = 'mechanic') + $mechanics = User::with('role')->whereHas('role', function($query) { + $query->where('name', 'mechanic'); + }) + ->when($dealerId, function($query) use ($dealerId) { + return $query->where('dealer_id', $dealerId); + }) + ->orderBy('name') + ->get(['id', 'name', 'role_id', 'dealer_id']); + + // Fallback: If no mechanics found, get all users with dealer_id + if ($mechanics->isEmpty()) { + Log::info('No users with role "mechanic" found, using fallback: all users with dealer_id'); + $mechanics = User::with('role')->whereNotNull('dealer_id') + ->whereNotNull('role_id') + ->when($dealerId, function($query) use ($dealerId) { + return $query->where('dealer_id', $dealerId); + }) + ->orderBy('name') + ->get(['id', 'name', 'role_id', 'dealer_id']); + } + + Log::info('Mechanics found:', [ + 'count' => $mechanics->count(), + 'dealer_id_filter' => $dealerId, + 'mechanics' => $mechanics->map(function($mechanic) { + $roleName = 'Unknown'; + if ($mechanic->role) { + $roleName = is_string($mechanic->role) ? $mechanic->role : $mechanic->role->name; + } + return [ + 'id' => $mechanic->id, + 'name' => $mechanic->name, + 'role_id' => $mechanic->role_id, + 'role_name' => $roleName, + 'dealer_id' => $mechanic->dealer_id + ]; + }) + ]); + + // Get all transaction data in single optimized query + $transactions = $this->getOptimizedTransactionData($dealerId, $startDate, $endDate, $mechanics->pluck('id'), $works->pluck('id')); + + Log::info('Transaction data:', [ + 'transaction_count' => count($transactions), + 'sample_transactions' => array_slice($transactions, 0, 5, true) + ]); + + $data = []; + + foreach ($works as $work) { + $row = [ + 'work_id' => $work->id, + 'work_name' => $work->name, + 'work_code' => $work->shortname, + 'category_name' => $work->category ? $work->category->name : '-', + 'total_tickets' => 0 + ]; + + // Calculate totals for each mechanic + foreach ($mechanics as $mechanic) { + $key = $work->id . '_' . $mechanic->id; + $mechanicData = $transactions[$key] ?? ['total' => 0, 'completed' => 0, 'pending' => 0]; + + $row["mechanic_{$mechanic->id}_total"] = $mechanicData['total']; + + // Add to totals + $row['total_tickets'] += $mechanicData['total']; + } + + $data[] = $row; + } + + Log::info('Final data prepared:', [ + 'data_count' => count($data), + 'sample_data' => array_slice($data, 0, 2) + ]); + + return [ + 'data' => $data, + 'mechanics' => $mechanics, + 'works' => $works + ]; + + } catch (\Exception $e) { + Log::error('Error in getTechnicianReportData: ' . $e->getMessage(), [ + 'dealer_id' => $dealerId, + 'start_date' => $startDate, + 'end_date' => $endDate, + 'trace' => $e->getTraceAsString() + ]); + + // Return empty data structure but with proper format + return [ + 'data' => [], + 'mechanics' => collect(), + 'works' => collect() + ]; + } + } + + /** + * Get optimized transaction data in single query + */ + private function getOptimizedTransactionData($dealerId = null, $startDate = null, $endDate = null, $mechanicIds = null, $workIds = null) + { + $query = Transaction::select( + 'work_id', + 'user_id', + 'status', + DB::raw('COUNT(*) as count') + ) + ->groupBy('work_id', 'user_id', 'status'); + + if ($dealerId) { + $query->where('dealer_id', $dealerId); + } + + if ($startDate) { + $query->where('date', '>=', $startDate); + } + + if ($endDate) { + $query->where('date', '<=', $endDate); + } + + if ($mechanicIds && $mechanicIds->count() > 0) { + $query->whereIn('user_id', $mechanicIds); + } + + if ($workIds && $workIds->count() > 0) { + $query->whereIn('work_id', $workIds); + } + + // Remove index hint that doesn't exist + $results = $query->get(); + + // Organize data by work_id_user_id key + $organizedData = []; + + foreach ($results as $result) { + $key = $result->work_id . '_' . $result->user_id; + + if (!isset($organizedData[$key])) { + $organizedData[$key] = [ + 'total' => 0, + 'completed' => 0, + 'pending' => 0 + ]; + } + + $organizedData[$key]['total'] += $result->count; + + if ($result->status == 1) { + $organizedData[$key]['completed'] += $result->count; + } else { + $organizedData[$key]['pending'] += $result->count; + } + } + + return $organizedData; + } + + /** + * Get total ticket count for a specific work and mechanic (legacy method for backward compatibility) + */ + private function getTicketCount($workId, $mechanicId, $dealerId = null, $startDate = null, $endDate = null) + { + $query = Transaction::where('work_id', $workId) + ->where('user_id', $mechanicId); + + if ($dealerId) { + $query->where('dealer_id', $dealerId); + } + + if ($startDate) { + $query->where('date', '>=', $startDate); + } + + if ($endDate) { + $query->where('date', '<=', $endDate); + } + + return $query->count(); + } + + /** + * Get completed ticket count for a specific work and mechanic (legacy method for backward compatibility) + */ + private function getCompletedTicketCount($workId, $mechanicId, $dealerId = null, $startDate = null, $endDate = null) + { + $query = Transaction::where('work_id', $workId) + ->where('user_id', $mechanicId) + ->where('status', 1); // Assuming status 1 is completed + + if ($dealerId) { + $query->where('dealer_id', $dealerId); + } + + if ($startDate) { + $query->where('date', '>=', $startDate); + } + + if ($endDate) { + $query->where('date', '<=', $endDate); + } + + return $query->count(); + } + + /** + * Get pending ticket count for a specific work and mechanic (legacy method for backward compatibility) + */ + private function getPendingTicketCount($workId, $mechanicId, $dealerId = null, $startDate = null, $endDate = null) + { + $query = Transaction::where('work_id', $workId) + ->where('user_id', $mechanicId) + ->where('status', 0); // Assuming status 0 is pending + + if ($dealerId) { + $query->where('dealer_id', $dealerId); + } + + if ($startDate) { + $query->where('date', '>=', $startDate); + } + + if ($endDate) { + $query->where('date', '<=', $endDate); + } + + return $query->count(); + } + + /** + * Get all dealers for filter + */ + public function getDealers() + { + return Dealer::orderBy('name')->get(['id', 'name', 'dealer_code']); + } + + /** + * Get default dealer for filter (tidak perlu berbasis user) + */ + public function getDefaultDealer() + { + // Dealer pertama saja jika ada + return Dealer::orderBy('name')->first(); + } + + /** + * Get mechanics for a specific dealer + */ + public function getMechanicsByDealer($dealerId = null) + { + $query = User::with('role')->whereHas('role', function($query) { + $query->where('name', 'mechanic'); + }); + + if ($dealerId) { + $query->where('dealer_id', $dealerId); + } + + return $query->orderBy('name')->get(['id', 'name', 'dealer_id']); + } + + /** + * Get technician report data for Yajra DataTable + */ + public function getTechnicianReportDataForDataTable($dealerId = null, $startDate = null, $endDate = null) + { + try { + // Get all works with category + $works = Work::with(['category']) + ->orderBy('name') + ->get(); + + // Get all mechanics + $mechanics = User::with('role')->whereHas('role', function($query) { + $query->where('name', 'mechanic'); + }) + ->when($dealerId, function($query) use ($dealerId) { + return $query->where('dealer_id', $dealerId); + }) + ->orderBy('name') + ->get(['id', 'name', 'role_id', 'dealer_id']); + + // Fallback: If no mechanics found, get all users with dealer_id + if ($mechanics->isEmpty()) { + Log::info('No users with role "mechanic" found, using fallback: all users with dealer_id'); + $mechanics = User::with('role')->whereNotNull('dealer_id') + ->whereNotNull('role_id') + ->when($dealerId, function($query) use ($dealerId) { + return $query->where('dealer_id', $dealerId); + }) + ->orderBy('name') + ->get(['id', 'name', 'role_id', 'dealer_id']); + } + + // Get transaction data + $transactions = $this->getOptimizedTransactionData($dealerId, $startDate, $endDate, $mechanics->pluck('id'), $works->pluck('id')); + + $data = []; + + foreach ($works as $work) { + $row = [ + 'DT_RowIndex' => count($data) + 1, + 'work_name' => $work->name, + 'work_code' => $work->shortname, + 'category_name' => $work->category ? $work->category->name : '-' + ]; + + // Add mechanic columns + foreach ($mechanics as $mechanic) { + $key = $work->id . '_' . $mechanic->id; + $mechanicData = $transactions[$key] ?? ['total' => 0, 'completed' => 0, 'pending' => 0]; + + $row["mechanic_{$mechanic->id}_total"] = $mechanicData['total']; + } + + $data[] = $row; + } + + // Create DataTable response + return response()->json([ + 'draw' => request()->input('draw', 1), + 'recordsTotal' => count($data), + 'recordsFiltered' => count($data), + 'data' => $data, + 'mechanics' => $mechanics, + 'works' => $works + ]); + + } catch (\Exception $e) { + Log::error('Error in getTechnicianReportDataForDataTable: ' . $e->getMessage(), [ + 'dealer_id' => $dealerId, + 'start_date' => $startDate, + 'end_date' => $endDate, + 'trace' => $e->getTraceAsString() + ]); + + return response()->json([ + 'draw' => request()->input('draw', 1), + 'recordsTotal' => 0, + 'recordsFiltered' => 0, + 'data' => [], + 'mechanics' => collect(), + 'works' => collect() + ]); + } + } + +} \ No newline at end of file diff --git a/database/seeders/MenuSeeder.php b/database/seeders/MenuSeeder.php index 490b44b..e502504 100755 --- a/database/seeders/MenuSeeder.php +++ b/database/seeders/MenuSeeder.php @@ -42,6 +42,10 @@ class MenuSeeder extends Seeder [ 'name' => 'Stock Produk', 'link' => 'reports.stock-product.index' + ], + [ + 'name' => 'Teknisi', + 'link' => 'reports.technician.index' ] ]; diff --git a/resources/views/layouts/partials/sidebarMenu.blade.php b/resources/views/layouts/partials/sidebarMenu.blade.php index 0e7497e..0b82471 100755 --- a/resources/views/layouts/partials/sidebarMenu.blade.php +++ b/resources/views/layouts/partials/sidebarMenu.blade.php @@ -48,7 +48,6 @@ @if(Gate::check('view', $menus['user.index']) || Gate::check('view', $menus['roleprivileges.index']))
@@ -77,7 +76,6 @@ @if(Gate::check('view', $menus['work.index']) || Gate::check('view', $menus['category.index']) || Gate::check('view', $menus['dealer.index'])) @@ -115,7 +113,6 @@ @if(Gate::check('view', $menus['products.index']) || Gate::check('view', $menus['product_categories.index']) || Gate::check('view', $menus['mutations.index']) || Gate::check('view', $menus['opnames.index']) || Gate::check('view', $menus['stock-audit.index'])) @@ -171,7 +168,6 @@ @if(Gate::check('view', $menus['report.transaction_sa']) || Gate::check('view', $menus['report.transaction']) || Gate::check('view', $menus['report.transaction_dealer']) || Gate::check('view', $menus['work.index'])) @@ -214,9 +210,9 @@ @endcan - @can('view', $menus['work.index']) + @can('view', $menus['reports.technician.index'])| No | +Nama Pekerjaan | +Kode Pekerjaan | +Kategori | +
|---|---|---|---|
| Total | +|||