partial update report technician
This commit is contained in:
411
app/Exports/TechnicianReportExport.php
Normal file
411
app/Exports/TechnicianReportExport.php
Normal file
@@ -0,0 +1,411 @@
|
|||||||
|
<?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,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
179
app/Http/Controllers/Reports/ReportTechniciansController.php
Normal file
179
app/Http/Controllers/Reports/ReportTechniciansController.php
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Reports;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use App\Models\Menu;
|
||||||
|
use App\Services\TechnicianReportService;
|
||||||
|
use App\Exports\TechnicianReportExport;
|
||||||
|
use Illuminate\Support\Facades\Gate;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Maatwebsite\Excel\Facades\Excel;
|
||||||
|
|
||||||
|
class ReportTechniciansController extends Controller
|
||||||
|
{
|
||||||
|
protected $technicianReportService;
|
||||||
|
|
||||||
|
public function __construct(TechnicianReportService $technicianReportService)
|
||||||
|
{
|
||||||
|
$this->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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
398
app/Services/TechnicianReportService.php
Normal file
398
app/Services/TechnicianReportService.php
Normal file
@@ -0,0 +1,398 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Services;
|
||||||
|
|
||||||
|
use App\Models\Work;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Models\Transaction;
|
||||||
|
use App\Models\Dealer;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
class TechnicianReportService
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get technician report data for all works and mechanics on a specific date range
|
||||||
|
*/
|
||||||
|
public function getTechnicianReportData($dealerId = null, $startDate = null, $endDate = null)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// Debug: Check all users and roles
|
||||||
|
$allUsers = User::with('role')->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()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -42,6 +42,10 @@ class MenuSeeder extends Seeder
|
|||||||
[
|
[
|
||||||
'name' => 'Stock Produk',
|
'name' => 'Stock Produk',
|
||||||
'link' => 'reports.stock-product.index'
|
'link' => 'reports.stock-product.index'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'Teknisi',
|
||||||
|
'link' => 'reports.technician.index'
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,6 @@
|
|||||||
@if(Gate::check('view', $menus['user.index']) || Gate::check('view', $menus['roleprivileges.index']))
|
@if(Gate::check('view', $menus['user.index']) || Gate::check('view', $menus['roleprivileges.index']))
|
||||||
<div class="kt-menu__section" style="padding: 15px 20px; margin-top: 10px; margin-bottom: 5px;">
|
<div class="kt-menu__section" style="padding: 15px 20px; margin-top: 10px; margin-bottom: 5px;">
|
||||||
<div style="display: flex; align-items: center; color: #a7abc3; font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px;">
|
<div style="display: flex; align-items: center; color: #a7abc3; font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px;">
|
||||||
{{-- <i class="fa fa-users" style="margin-right: 8px; font-size: 14px;"></i> --}}
|
|
||||||
<span>Manajemen Pengguna</span>
|
<span>Manajemen Pengguna</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -77,7 +76,6 @@
|
|||||||
@if(Gate::check('view', $menus['work.index']) || Gate::check('view', $menus['category.index']) || Gate::check('view', $menus['dealer.index']))
|
@if(Gate::check('view', $menus['work.index']) || Gate::check('view', $menus['category.index']) || Gate::check('view', $menus['dealer.index']))
|
||||||
<div class="kt-menu__section" style="padding: 15px 20px; margin-top: 10px; margin-bottom: 5px;">
|
<div class="kt-menu__section" style="padding: 15px 20px; margin-top: 10px; margin-bottom: 5px;">
|
||||||
<div style="display: flex; align-items: center; color: #a7abc3; font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px;">
|
<div style="display: flex; align-items: center; color: #a7abc3; font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px;">
|
||||||
{{-- <i class="fa fa-exchange-alt" style="margin-right: 8px; font-size: 14px;"></i> --}}
|
|
||||||
<span>Manajemen Transaksi</span>
|
<span>Manajemen Transaksi</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -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']))
|
@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']))
|
||||||
<div class="kt-menu__section" style="padding: 15px 20px; margin-top: 10px; margin-bottom: 5px;">
|
<div class="kt-menu__section" style="padding: 15px 20px; margin-top: 10px; margin-bottom: 5px;">
|
||||||
<div style="display: flex; align-items: center; color: #a7abc3; font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px;">
|
<div style="display: flex; align-items: center; color: #a7abc3; font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px;">
|
||||||
{{-- <i class="fa fa-warehouse" style="margin-right: 8px; font-size: 14px;"></i> --}}
|
|
||||||
<span>Manajemen Gudang</span>
|
<span>Manajemen Gudang</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -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']))
|
@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']))
|
||||||
<div class="kt-menu__section" style="padding: 15px 20px; margin-top: 10px; margin-bottom: 5px;">
|
<div class="kt-menu__section" style="padding: 15px 20px; margin-top: 10px; margin-bottom: 5px;">
|
||||||
<div style="display: flex; align-items: center; color: #a7abc3; font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px;">
|
<div style="display: flex; align-items: center; color: #a7abc3; font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px;">
|
||||||
{{-- <i class="fa fa-chart-bar" style="margin-right: 8px; font-size: 14px;"></i> --}}
|
|
||||||
<span>Laporan</span>
|
<span>Laporan</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -214,9 +210,9 @@
|
|||||||
</li>
|
</li>
|
||||||
@endcan
|
@endcan
|
||||||
|
|
||||||
@can('view', $menus['work.index'])
|
@can('view', $menus['reports.technician.index'])
|
||||||
<li class="kt-menu__item" aria-haspopup="true">
|
<li class="kt-menu__item" aria-haspopup="true">
|
||||||
<a href="{{ route('work.index') }}" class="kt-menu__link">
|
<a href="{{ route('reports.technician.index') }}" class="kt-menu__link">
|
||||||
<i class="fa fa-user-cog" style="display: flex; align-items: center; margin-right: 10px;"></i>
|
<i class="fa fa-user-cog" style="display: flex; align-items: center; margin-right: 10px;"></i>
|
||||||
<span class="kt-menu__link-text">Teknisi</span>
|
<span class="kt-menu__link-text">Teknisi</span>
|
||||||
</a>
|
</a>
|
||||||
@@ -227,7 +223,6 @@
|
|||||||
@if(Gate::check('view', $menus['kpi.targets.index']))
|
@if(Gate::check('view', $menus['kpi.targets.index']))
|
||||||
<div class="kt-menu__section" style="padding: 15px 20px; margin-top: 10px; margin-bottom: 5px;">
|
<div class="kt-menu__section" style="padding: 15px 20px; margin-top: 10px; margin-bottom: 5px;">
|
||||||
<div style="display: flex; align-items: center; color: #a7abc3; font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px;">
|
<div style="display: flex; align-items: center; color: #a7abc3; font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px;">
|
||||||
{{-- <i class="fa fa-user-cog" style="margin-right: 8px; font-size: 14px;"></i> --}}
|
|
||||||
<span>KPI</span>
|
<span>KPI</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
1255
resources/views/reports/technician.blade.php
Normal file
1255
resources/views/reports/technician.blade.php
Normal file
File diff suppressed because it is too large
Load Diff
@@ -16,6 +16,7 @@ use App\Http\Controllers\WarehouseManagement\MutationsController;
|
|||||||
use App\Http\Controllers\WarehouseManagement\StockAuditController;
|
use App\Http\Controllers\WarehouseManagement\StockAuditController;
|
||||||
use App\Http\Controllers\KPI\TargetsController;
|
use App\Http\Controllers\KPI\TargetsController;
|
||||||
use App\Http\Controllers\Reports\ReportStockProductsController;
|
use App\Http\Controllers\Reports\ReportStockProductsController;
|
||||||
|
use App\Http\Controllers\Reports\ReportTechniciansController;
|
||||||
use App\Models\Menu;
|
use App\Models\Menu;
|
||||||
use App\Models\Privilege;
|
use App\Models\Privilege;
|
||||||
use App\Models\Role;
|
use App\Models\Role;
|
||||||
@@ -323,6 +324,13 @@ Route::group(['middleware' => 'auth'], function() {
|
|||||||
Route::get('stock-products/data', [ReportStockProductsController::class, 'getData'])->name('reports.stock-product.data');
|
Route::get('stock-products/data', [ReportStockProductsController::class, 'getData'])->name('reports.stock-product.data');
|
||||||
Route::get('stock-products/dealers', [ReportStockProductsController::class, 'getDealers'])->name('reports.stock-product.dealers');
|
Route::get('stock-products/dealers', [ReportStockProductsController::class, 'getDealers'])->name('reports.stock-product.dealers');
|
||||||
Route::get('stock-products/export', [ReportStockProductsController::class, 'export'])->name('reports.stock-product.export');
|
Route::get('stock-products/export', [ReportStockProductsController::class, 'export'])->name('reports.stock-product.export');
|
||||||
|
|
||||||
|
Route::get('technician', [ReportTechniciansController::class, 'index'])->name('reports.technician.index');
|
||||||
|
Route::get('technician/data', [ReportTechniciansController::class, 'getData'])->name('reports.technician.data');
|
||||||
|
Route::get('technician/datatable', [ReportTechniciansController::class, 'getDataTable'])->name('reports.technician.datatable');
|
||||||
|
Route::get('technician/dealers', [ReportTechniciansController::class, 'getDealers'])->name('reports.technician.dealers');
|
||||||
|
Route::get('technician/export', [ReportTechniciansController::class, 'export'])->name('reports.technician.export');
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user