load(['project', 'user', 'labors', 'equipment', 'activities', 'materials', 'issues']); $spreadsheet = new Spreadsheet(); $sheet = $spreadsheet->getActiveSheet(); $sheet->setTitle('Daily Report'); $sheet->setShowGridlines(true); // Styling helpers $headerStyle = [ 'font' => [ 'name' => 'Arial', 'size' => 16, 'bold' => true, 'color' => ['rgb' => 'FFFFFF'], ], 'fill' => [ 'fillType' => Fill::FILL_SOLID, 'startColor' => ['rgb' => '334155'], // Slate 700 ], 'alignment' => [ 'horizontal' => Alignment::HORIZONTAL_CENTER, 'vertical' => Alignment::VERTICAL_CENTER, ], ]; $subHeaderStyle = [ 'font' => [ 'name' => 'Arial', 'size' => 11, 'bold' => true, 'color' => ['rgb' => '334155'], ], 'fill' => [ 'fillType' => Fill::FILL_SOLID, 'startColor' => ['rgb' => 'F1F5F9'], // Slate 100 ], 'alignment' => [ 'vertical' => Alignment::VERTICAL_CENTER, ], 'borders' => [ 'bottom' => [ 'borderStyle' => Border::BORDER_MEDIUM, 'color' => ['rgb' => 'CBD5E1'], ], ], ]; $sectionTitleStyle = [ 'font' => [ 'name' => 'Arial', 'size' => 12, 'bold' => true, 'color' => ['rgb' => '0F172A'], // Slate 900 ], 'fill' => [ 'fillType' => Fill::FILL_SOLID, 'startColor' => ['rgb' => 'E2E8F0'], // Slate 200 ], 'alignment' => [ 'vertical' => Alignment::VERTICAL_CENTER, ], ]; $boldLabelStyle = [ 'font' => [ 'name' => 'Arial', 'size' => 10, 'bold' => true, 'color' => ['rgb' => '475569'], ], ]; $tableHeaderStyle = [ 'font' => [ 'name' => 'Arial', 'size' => 10, 'bold' => true, 'color' => ['rgb' => '475569'], ], 'fill' => [ 'fillType' => Fill::FILL_SOLID, 'startColor' => ['rgb' => 'F8FAFC'], ], 'borders' => [ 'bottom' => [ 'borderStyle' => Border::BORDER_THIN, 'color' => ['rgb' => 'E2E8F0'], ], ], ]; $borderBottomThin = [ 'borders' => [ 'bottom' => [ 'borderStyle' => Border::BORDER_THIN, 'color' => ['rgb' => 'F1F5F9'], ], ], ]; // 1. Title Block $sheet->mergeCells('A1:F1'); $sheet->setCellValue('A1', 'GSB CONSTRUCTION - DAILY SITE REPORT'); $sheet->getStyle('A1:F1')->applyFromArray($headerStyle); $sheet->getRowDimension(1)->setRowHeight(40); // 2. Metadata Block $sheet->mergeCells('A3:F3'); $sheet->setCellValue('A3', ' REPORT METADATA'); $sheet->getStyle('A3:F3')->applyFromArray($subHeaderStyle); $sheet->getRowDimension(3)->setRowHeight(25); // Row 4 $sheet->setCellValue('A4', 'Report Number:'); $sheet->setCellValue('B4', $dailyReport->report_number); $sheet->setCellValue('C4', 'Report Date:'); $sheet->setCellValue('D4', $dailyReport->report_date ? $dailyReport->report_date->format('Y-m-d') : 'N/A'); $sheet->setCellValue('E4', 'Prepared By:'); $sheet->setCellValue('F4', $dailyReport->user ? $dailyReport->user->name : 'N/A'); // Row 5 $sheet->setCellValue('A5', 'Project Code:'); $sheet->setCellValue('B5', $dailyReport->project ? $dailyReport->project->code : 'N/A'); $sheet->setCellValue('C5', 'Project Name:'); $sheet->mergeCells('D5:F5'); $sheet->setCellValue('D5', $dailyReport->project ? $dailyReport->project->name : 'N/A'); // Row 6 $sheet->setCellValue('A6', 'Weather:'); $sheet->setCellValue('B6', $dailyReport->weather ?: 'N/A'); $sheet->setCellValue('C6', 'Temperature:'); $sheet->setCellValue('D6', $dailyReport->temperature ? $dailyReport->temperature . ' °C' : 'N/A'); $sheet->setCellValue('E6', 'Precipitation:'); $sheet->setCellValue('F6', $dailyReport->precipitation ?: 'N/A'); // Row 7 $sheet->setCellValue('A7', 'Wind:'); $sheet->setCellValue('B7', $dailyReport->wind ?: 'N/A'); $sheet->getStyle('A4')->applyFromArray($boldLabelStyle); $sheet->getStyle('C4')->applyFromArray($boldLabelStyle); $sheet->getStyle('E4')->applyFromArray($boldLabelStyle); $sheet->getStyle('A5')->applyFromArray($boldLabelStyle); $sheet->getStyle('C5')->applyFromArray($boldLabelStyle); $sheet->getStyle('A6')->applyFromArray($boldLabelStyle); $sheet->getStyle('C6')->applyFromArray($boldLabelStyle); $sheet->getStyle('E6')->applyFromArray($boldLabelStyle); $sheet->getStyle('A7')->applyFromArray($boldLabelStyle); // 2.5. Summary Dashboard Block $sheet->mergeCells('A9:F9'); $sheet->setCellValue('A9', ' SUMMARY & KEY METRICS'); $sheet->getStyle('A9:F9')->applyFromArray($subHeaderStyle); $sheet->getRowDimension(9)->setRowHeight(25); // Calculate summary metrics $totalWorkers = 0; $totalManHours = 0; foreach ($dailyReport->labors as $lab) { $totalWorkers += $lab->workers_count; $totalManHours += $lab->workers_count * ($lab->hours ?: 0); } $totalEquipmentHours = $dailyReport->equipment->sum('hours_used') ?: 0; $avgProgress = $dailyReport->activities->isEmpty() ? 0 : ($dailyReport->activities->avg('percentage_completed') ?: 0); // Row 10 $sheet->setCellValue('A10', 'Tasks Logged:'); $sheet->setCellValue('B10', count($dailyReport->activities)); $sheet->setCellValue('C10', 'Materials Logged:'); $sheet->setCellValue('D10', count($dailyReport->materials)); $sheet->setCellValue('E10', 'Manpower Count:'); $sheet->setCellValue('F10', $totalWorkers); // Row 11 $sheet->setCellValue('A11', 'Avg. Progress:'); $sheet->setCellValue('B11', number_format($avgProgress, 1) . '%'); $sheet->setCellValue('C11', 'Reported Issues:'); $sheet->setCellValue('D11', count($dailyReport->issues)); $sheet->setCellValue('E11', 'Total Man-Hours:'); $sheet->setCellValue('F11', number_format($totalManHours, 1) . ' hrs'); // Row 12 $sheet->setCellValue('A12', 'Active Equipment:'); $sheet->setCellValue('B12', count($dailyReport->equipment)); $sheet->setCellValue('C12', 'Equipment Hours:'); $sheet->setCellValue('D12', number_format($totalEquipmentHours, 1) . ' hrs'); $sheet->getStyle('A10')->applyFromArray($boldLabelStyle); $sheet->getStyle('C10')->applyFromArray($boldLabelStyle); $sheet->getStyle('E10')->applyFromArray($boldLabelStyle); $sheet->getStyle('A11')->applyFromArray($boldLabelStyle); $sheet->getStyle('C11')->applyFromArray($boldLabelStyle); $sheet->getStyle('E11')->applyFromArray($boldLabelStyle); $sheet->getStyle('A12')->applyFromArray($boldLabelStyle); $sheet->getStyle('C12')->applyFromArray($boldLabelStyle); $rowIdx = 14; // 3. Section A: Tasks $sheet->mergeCells("A{$rowIdx}:F{$rowIdx}"); $sheet->setCellValue("A{$rowIdx}", ' 1. TASKS'); $sheet->getStyle("A{$rowIdx}:F{$rowIdx}")->applyFromArray($sectionTitleStyle); $sheet->getRowDimension($rowIdx)->setRowHeight(25); $rowIdx++; // Table Headers $sheet->mergeCells("A{$rowIdx}:C{$rowIdx}"); $sheet->setCellValue("A{$rowIdx}", 'Task Name / Activity'); $sheet->setCellValue("D{$rowIdx}", 'Zone / Area'); $sheet->setCellValue("E{$rowIdx}", 'Qty Completed'); $sheet->setCellValue("F{$rowIdx}", 'Completion %'); $sheet->getStyle("A{$rowIdx}:F{$rowIdx}")->applyFromArray($tableHeaderStyle); $rowIdx++; if ($dailyReport->activities->isEmpty()) { $sheet->mergeCells("A{$rowIdx}:F{$rowIdx}"); $sheet->setCellValue("A{$rowIdx}", 'No work activities reported for this shift.'); $sheet->getStyle("A{$rowIdx}:F{$rowIdx}")->applyFromArray(['font' => ['italic' => true, 'color' => ['rgb' => '94A3B8']]]); $rowIdx++; } else { foreach ($dailyReport->activities as $act) { $sheet->mergeCells("A{$rowIdx}:C{$rowIdx}"); $sheet->setCellValue("A{$rowIdx}", $act->task_name); $sheet->setCellValue("D{$rowIdx}", $act->zone_area ?: 'N/A'); $sheet->setCellValue("E{$rowIdx}", $act->quantity_completed ?: 'N/A'); $sheet->setCellValue("F{$rowIdx}", $act->percentage_completed ? $act->percentage_completed . '%' : '0%'); $sheet->getStyle("A{$rowIdx}:F{$rowIdx}")->applyFromArray($borderBottomThin); $rowIdx++; } } $rowIdx++; // 4. Section B: Materials $sheet->mergeCells("A{$rowIdx}:F{$rowIdx}"); $sheet->setCellValue("A{$rowIdx}", ' 2. MATERIALS'); $sheet->getStyle("A{$rowIdx}:F{$rowIdx}")->applyFromArray($sectionTitleStyle); $sheet->getRowDimension($rowIdx)->setRowHeight(25); $rowIdx++; // Table Headers $sheet->mergeCells("A{$rowIdx}:C{$rowIdx}"); $sheet->setCellValue("A{$rowIdx}", 'Material Name'); $sheet->setCellValue("D{$rowIdx}", 'Quantity Received / Used'); $sheet->mergeCells("E{$rowIdx}:F{$rowIdx}"); $sheet->setCellValue("E{$rowIdx}", 'Condition / Remarks'); $sheet->getStyle("A{$rowIdx}:F{$rowIdx}")->applyFromArray($tableHeaderStyle); $rowIdx++; if ($dailyReport->materials->isEmpty()) { $sheet->mergeCells("A{$rowIdx}:F{$rowIdx}"); $sheet->setCellValue("A{$rowIdx}", 'No materials logged for this date.'); $sheet->getStyle("A{$rowIdx}:F{$rowIdx}")->applyFromArray(['font' => ['italic' => true, 'color' => ['rgb' => '94A3B8']]]); $rowIdx++; } else { foreach ($dailyReport->materials as $mat) { $sheet->mergeCells("A{$rowIdx}:C{$rowIdx}"); $sheet->setCellValue("A{$rowIdx}", $mat->material_name); $sheet->setCellValue("D{$rowIdx}", $mat->quantity_received); $sheet->mergeCells("E{$rowIdx}:F{$rowIdx}"); $sheet->setCellValue("E{$rowIdx}", $mat->condition ?: 'Good'); $sheet->getStyle("A{$rowIdx}:F{$rowIdx}")->applyFromArray($borderBottomThin); $rowIdx++; } } $rowIdx++; // 5. Section C: Labor & Manpower $sheet->mergeCells("A{$rowIdx}:F{$rowIdx}"); $sheet->setCellValue("A{$rowIdx}", ' 3. MANPOWER/LABOR'); $sheet->getStyle("A{$rowIdx}:F{$rowIdx}")->applyFromArray($sectionTitleStyle); $sheet->getRowDimension($rowIdx)->setRowHeight(25); $rowIdx++; // Table Headers $sheet->mergeCells("A{$rowIdx}:B{$rowIdx}"); $sheet->setCellValue("A{$rowIdx}", 'Labor Trade / Profession'); $sheet->setCellValue("C{$rowIdx}", 'Workers Count'); $sheet->setCellValue("D{$rowIdx}", 'Hours Rendered'); $sheet->mergeCells("E{$rowIdx}:F{$rowIdx}"); $sheet->setCellValue("E{$rowIdx}", 'Notes / Details'); $sheet->getStyle("A{$rowIdx}:F{$rowIdx}")->applyFromArray($tableHeaderStyle); $rowIdx++; if ($dailyReport->labors->isEmpty()) { $sheet->mergeCells("A{$rowIdx}:F{$rowIdx}"); $sheet->setCellValue("A{$rowIdx}", 'No labor resources logged.'); $sheet->getStyle("A{$rowIdx}:F{$rowIdx}")->applyFromArray(['font' => ['italic' => true, 'color' => ['rgb' => '94A3B8']]]); $rowIdx++; } else { foreach ($dailyReport->labors as $lab) { $sheet->mergeCells("A{$rowIdx}:B{$rowIdx}"); $sheet->setCellValue("A{$rowIdx}", $lab->trade); $sheet->setCellValue("C{$rowIdx}", $lab->workers_count); $sheet->setCellValue("D{$rowIdx}", $lab->hours); $sheet->mergeCells("E{$rowIdx}:F{$rowIdx}"); $sheet->setCellValue("E{$rowIdx}", $lab->notes ?: ''); $sheet->getStyle("A{$rowIdx}:F{$rowIdx}")->applyFromArray($borderBottomThin); $rowIdx++; } } $rowIdx++; // 6. Section D: Equipment Usage $sheet->mergeCells("A{$rowIdx}:F{$rowIdx}"); $sheet->setCellValue("A{$rowIdx}", ' 4. EQUIPMENT'); $sheet->getStyle("A{$rowIdx}:F{$rowIdx}")->applyFromArray($sectionTitleStyle); $sheet->getRowDimension($rowIdx)->setRowHeight(25); $rowIdx++; // Table Headers $sheet->mergeCells("A{$rowIdx}:C{$rowIdx}"); $sheet->setCellValue("A{$rowIdx}", 'Equipment Name'); $sheet->setCellValue("D{$rowIdx}", 'Hours Used'); $sheet->mergeCells("E{$rowIdx}:F{$rowIdx}"); $sheet->setCellValue("E{$rowIdx}", 'Status (Active / Idle)'); $sheet->getStyle("A{$rowIdx}:F{$rowIdx}")->applyFromArray($tableHeaderStyle); $rowIdx++; if ($dailyReport->equipment->isEmpty()) { $sheet->mergeCells("A{$rowIdx}:F{$rowIdx}"); $sheet->setCellValue("A{$rowIdx}", 'No equipment usage logged.'); $sheet->getStyle("A{$rowIdx}:F{$rowIdx}")->applyFromArray(['font' => ['italic' => true, 'color' => ['rgb' => '94A3B8']]]); $rowIdx++; } else { foreach ($dailyReport->equipment as $eq) { $sheet->mergeCells("A{$rowIdx}:C{$rowIdx}"); $sheet->setCellValue("A{$rowIdx}", $eq->equipment_name); $sheet->setCellValue("D{$rowIdx}", $eq->hours_used); $sheet->mergeCells("E{$rowIdx}:F{$rowIdx}"); $sheet->setCellValue("E{$rowIdx}", ucfirst($eq->status)); $sheet->getStyle("A{$rowIdx}:F{$rowIdx}")->applyFromArray($borderBottomThin); $rowIdx++; } } $rowIdx++; // 7. Section E: Reported Issues & Delays $sheet->mergeCells("A{$rowIdx}:F{$rowIdx}"); $sheet->setCellValue("A{$rowIdx}", ' 5. ISSUES & CONCERN'); $sheet->getStyle("A{$rowIdx}:F{$rowIdx}")->applyFromArray($sectionTitleStyle); $sheet->getRowDimension($rowIdx)->setRowHeight(25); $rowIdx++; // Table Headers $sheet->mergeCells("A{$rowIdx}:B{$rowIdx}"); $sheet->setCellValue("A{$rowIdx}", 'Issue / Delay Type'); $sheet->mergeCells("C{$rowIdx}:E{$rowIdx}"); $sheet->setCellValue("C{$rowIdx}", 'Description'); $sheet->setCellValue("F{$rowIdx}", 'Delay Impact (Hrs)'); $sheet->getStyle("A{$rowIdx}:F{$rowIdx}")->applyFromArray($tableHeaderStyle); $rowIdx++; if ($dailyReport->issues->isEmpty()) { $sheet->mergeCells("A{$rowIdx}:F{$rowIdx}"); $sheet->setCellValue("A{$rowIdx}", 'No major issues or delays reported.'); $sheet->getStyle("A{$rowIdx}:F{$rowIdx}")->applyFromArray(['font' => ['italic' => true, 'color' => ['rgb' => '94A3B8']]]); $rowIdx++; } else { foreach ($dailyReport->issues as $iss) { $sheet->mergeCells("A{$rowIdx}:B{$rowIdx}"); $sheet->setCellValue("A{$rowIdx}", ucfirst(str_replace('_', ' ', $iss->issue_type))); $sheet->mergeCells("C{$rowIdx}:E{$rowIdx}"); $sheet->setCellValue("C{$rowIdx}", $iss->description); $sheet->setCellValue("F{$rowIdx}", $iss->delay_impact ?: '0'); $sheet->getStyle("A{$rowIdx}:F{$rowIdx}")->applyFromArray($borderBottomThin); $rowIdx++; } } $rowIdx++; // 8. General Remarks $sheet->mergeCells("A{$rowIdx}:F{$rowIdx}"); $sheet->setCellValue("A{$rowIdx}", ' GENERAL REMARKS & NOTES'); $sheet->getStyle("A{$rowIdx}:F{$rowIdx}")->applyFromArray($subHeaderStyle); $rowIdx++; $sheet->mergeCells("A{$rowIdx}:F" . ($rowIdx + 3)); $sheet->setCellValue("A{$rowIdx}", $dailyReport->remarks ?: 'No additional remarks entered.'); $sheet->getStyle("A{$rowIdx}:F" . ($rowIdx + 3))->applyFromArray([ 'alignment' => [ 'vertical' => Alignment::VERTICAL_TOP, 'wrapText' => true ], 'font' => ['italic' => !$dailyReport->remarks] ]); // Auto-fit column widths foreach (range('A', 'F') as $col) { $sheet->getColumnDimension($col)->setAutoSize(true); } // Output Excel response $filename = 'DailyReport_' . ($dailyReport->project ? $dailyReport->project->code : '') . '_' . $dailyReport->report_date->format('Ymd') . '.xlsx'; header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); header('Content-Disposition: attachment;filename="' . $filename . '"'); header('Cache-Control: max-age=0'); $writer = new Xlsx($spreadsheet); $writer->save('php://output'); exit; } public function pdf(DailyReport $dailyReport) { $dailyReport->load(['project', 'user', 'labors', 'equipment', 'activities', 'materials', 'issues']); // Calculate summary metrics $totalWorkers = 0; $totalManHours = 0; foreach ($dailyReport->labors as $lab) { $totalWorkers += $lab->workers_count; $totalManHours += $lab->workers_count * ($lab->hours ?: 0); } $totalEquipmentHours = 0; foreach ($dailyReport->equipment as $eq) { $totalEquipmentHours += $eq->hours_used ?: 0; } $avgProgress = 0; if (!$dailyReport->activities->isEmpty()) { $avgProgress = $dailyReport->activities->avg('percentage_completed') ?: 0; } $summary = [ 'total_workers' => $totalWorkers, 'total_man_hours' => $totalManHours, 'total_equipment_hours' => $totalEquipmentHours, 'avg_progress' => $avgProgress, ]; $pdf = \Barryvdh\DomPDF\Facade\Pdf::loadView('dailyreports::pdf.daily-report', [ 'report' => $dailyReport, 'summary' => $summary, ]); $filename = 'DailyReport_' . ($dailyReport->project ? $dailyReport->project->code : '') . '_' . $dailyReport->report_date->format('Ymd') . '.pdf'; return $pdf->download($filename); } public function exportRange(Request $request) { $request->validate([ 'start_date' => 'required|date', 'end_date' => 'required|date|after_or_equal:start_date', 'project' => 'nullable|string', ]); $startDate = Carbon::parse($request->input('start_date'))->startOfDay(); $endDate = Carbon::parse($request->input('end_date'))->endOfDay(); $projectUlid = $request->input('project'); $project = $projectUlid ? Project::where('ulid', $projectUlid)->first() : null; $query = DailyReport::with(['project', 'user', 'labors', 'equipment', 'activities', 'materials', 'issues']) ->whereBetween('report_date', [$startDate->format('Y-m-d'), $endDate->format('Y-m-d')]) ->orderBy('report_date', 'asc'); if ($project) { $query->where('project_id', $project->id); } $reports = $query->get(); // 1. Manpower Category Aggregation $allLabors = $reports->flatMap->labors; $totalPeriodManHours = $allLabors->sum(fn ($l) => $l->workers_count * ($l->hours ?: 0)); $laborCategorySummary = $allLabors->groupBy(fn ($l) => $l->trade ?: 'Unspecified Trade') ->map(function ($group, $trade) use ($totalPeriodManHours) { $workerDays = $group->sum('workers_count'); $manHours = $group->sum(fn ($l) => $l->workers_count * ($l->hours ?: 0)); $activeCount = $group->count(); $percent = $totalPeriodManHours > 0 ? ($manHours / $totalPeriodManHours) * 100 : 0; return [ 'trade' => $trade, 'worker_days' => $workerDays, 'total_man_hours' => $manHours, 'days_active' => $activeCount, 'percent_share' => round($percent, 1), ]; })->values()->sortByDesc('total_man_hours')->values(); // 2. Equipment Category Aggregation $allEquipment = $reports->flatMap->equipment; $totalPeriodEquipHours = $allEquipment->sum('hours_used'); $equipmentCategorySummary = $allEquipment->groupBy(fn ($eq) => $eq->equipment_name ?: 'Unspecified Equipment') ->map(function ($group, $name) use ($totalPeriodEquipHours) { $hours = $group->sum('hours_used'); $activeCount = $group->count(); $percent = $totalPeriodEquipHours > 0 ? ($hours / $totalPeriodEquipHours) * 100 : 0; $latestStatus = $group->last()->status ?? 'Active'; return [ 'equipment_name' => $name, 'total_hours' => $hours, 'days_active' => $activeCount, 'percent_share' => round($percent, 1), 'latest_status' => $latestStatus, ]; })->values()->sortByDesc('total_hours')->values(); // 3. Materials Category Aggregation $allMaterials = $reports->flatMap->materials; $materialsCategorySummary = $allMaterials->groupBy(fn ($m) => $m->material_name ?: 'General Material') ->map(function ($group, $name) { $deliveriesCount = $group->count(); $quantities = $group->pluck('quantity_received')->filter()->all(); $numericSum = 0; $unit = ''; foreach ($quantities as $q) { if (preg_match('/^([0-9.]+)\s*(.*)$/', trim($q), $matches)) { $numericSum += (float) $matches[1]; if (empty($unit) && !empty($matches[2])) { $unit = $matches[2]; } } } $qtyDisplay = $numericSum > 0 ? (string)$numericSum : (implode(', ', array_slice($quantities, 0, 3)) ?: '—'); $conditions = $group->pluck('condition')->filter()->unique()->implode(', ') ?: 'Good'; return [ 'material_name' => $name, 'total_quantity' => $qtyDisplay, 'unit' => $unit ?: 'units', 'deliveries_count' => $deliveriesCount, 'condition_summary' => $conditions, ]; })->values()->sortByDesc('deliveries_count')->values(); // 4. Activities Category Aggregation $allActivities = $reports->flatMap->activities; $activitiesCategorySummary = $allActivities->groupBy(fn ($a) => $a->task_name ?: 'General Works') ->map(function ($group, $name) { $zones = $group->pluck('zone_area')->filter()->unique()->implode(', ') ?: 'Site Wide'; $highestProgress = $group->max('percentage_completed') ?: 0; $daysWorked = $group->count(); return [ 'task_name' => $name, 'zones' => $zones, 'highest_progress' => $highestProgress, 'days_worked' => $daysWorked, ]; })->values()->sortByDesc('days_worked')->values(); // 5. Issues Category Aggregation $allIssues = $reports->flatMap->issues; $issuesCategorySummary = $allIssues->groupBy(fn ($i) => $i->issue_type ?: 'General') ->map(function ($group, $type) { $count = $group->count(); $resolved = $group->whereNotNull('resolved_at')->count(); $delays = $group->pluck('delay_impact')->filter()->all(); return [ 'issue_type' => $type, 'incident_count' => $count, 'resolved_count' => $resolved, 'pending_count' => $count - $resolved, 'delay_impact_summary' => !empty($delays) ? implode('; ', array_slice($delays, 0, 3)) : 'None reported', ]; })->values()->sortByDesc('incident_count')->values(); $spreadsheet = new Spreadsheet(); // Style definitions $headerStyle = [ 'font' => ['name' => 'Arial', 'size' => 13, 'bold' => true, 'color' => ['rgb' => 'FFFFFF']], 'fill' => ['fillType' => Fill::FILL_SOLID, 'startColor' => ['rgb' => '1E293B']], // Slate 800 'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER, 'vertical' => Alignment::VERTICAL_CENTER], ]; $sectionHeaderStyle = [ 'font' => ['name' => 'Arial', 'size' => 11, 'bold' => true, 'color' => ['rgb' => 'FFFFFF']], 'fill' => ['fillType' => Fill::FILL_SOLID, 'startColor' => ['rgb' => '0F766E']], // Teal 700 'alignment' => ['vertical' => Alignment::VERTICAL_CENTER], ]; $tableHeaderStyle = [ 'font' => ['name' => 'Arial', 'size' => 9.5, 'bold' => true, 'color' => ['rgb' => 'FFFFFF']], 'fill' => ['fillType' => Fill::FILL_SOLID, 'startColor' => ['rgb' => '334155']], // Slate 700 'alignment' => ['vertical' => Alignment::VERTICAL_CENTER], 'borders' => ['allBorders' => ['borderStyle' => Border::BORDER_THIN, 'color' => ['rgb' => '64748B']]], ]; $totalRowStyle = [ 'font' => ['name' => 'Arial', 'size' => 9.5, 'bold' => true, 'color' => ['rgb' => '0F172A']], 'fill' => ['fillType' => Fill::FILL_SOLID, 'startColor' => ['rgb' => 'E2E8F0']], // Slate 200 'borders' => ['allBorders' => ['borderStyle' => Border::BORDER_THIN, 'color' => ['rgb' => '94A3B8']]], ]; $thinBorder = [ 'borders' => ['allBorders' => ['borderStyle' => Border::BORDER_THIN, 'color' => ['rgb' => 'CBD5E1']]], ]; // ---------------------------------------------------- // 1. SHEET: CONSOLIDATED OVERVIEW & DAILY SUMMARY // ---------------------------------------------------- $sheet = $spreadsheet->getActiveSheet(); $sheet->setTitle('Consolidated Overview'); $sheet->setShowGridlines(true); $sheet->mergeCells('A1:J1'); $sheet->setCellValue('A1', 'CONSOLIDATED DAILY CONSTRUCTION REPORTS — PERIOD OVERVIEW'); $sheet->getStyle('A1:J1')->applyFromArray($headerStyle); $sheet->getRowDimension(1)->setRowHeight(32); $sheet->setCellValue('A2', 'Project: ' . ($project ? "{$project->name} ({$project->code})" : 'All Projects')); $sheet->setCellValue('A3', 'Date Range: ' . $startDate->format('M d, Y') . ' to ' . $endDate->format('M d, Y')); $sheet->setCellValue('A4', 'Total Reports Filed: ' . $reports->count() . ' day(s)'); $sheet->setCellValue('D2', 'Total Man-Days: ' . number_format($allLabors->sum('workers_count'))); $sheet->setCellValue('D3', 'Total Man-Hours: ' . number_format($totalPeriodManHours, 1) . ' hrs'); $sheet->setCellValue('D4', 'Total Equip. Hours: ' . number_format($totalPeriodEquipHours, 1) . ' hrs'); $sheet->setCellValue('G2', 'Activities Logged: ' . $allActivities->count()); $sheet->setCellValue('G3', 'Materials Tracked: ' . $allMaterials->count()); $sheet->setCellValue('G4', 'Issues & Delays: ' . $allIssues->count()); $sheet->getStyle('A2:G4')->getFont()->setBold(true)->setSize(9.5); $headers = ['Date', 'Report #', 'Project', 'Prepared By', 'Weather', 'Workers', 'Man-Hours', 'Equip. Hours', 'Tasks', 'Issues']; $cols = range('A', 'J'); $rowIdx = 6; foreach ($headers as $k => $h) { $sheet->setCellValue("{$cols[$k]}{$rowIdx}", $h); } $sheet->getStyle("A{$rowIdx}:J{$rowIdx}")->applyFromArray($tableHeaderStyle); $sheet->getRowDimension($rowIdx)->setRowHeight(24); $rowIdx++; foreach ($reports as $r) { $workers = $r->labors->sum('workers_count'); $manHours = $r->labors->sum(fn ($l) => $l->workers_count * ($l->hours ?: 0)); $equipHours = $r->equipment->sum('hours_used'); $sheet->setCellValue("A{$rowIdx}", $r->report_date ? $r->report_date->format('Y-m-d') : ''); $sheet->setCellValue("B{$rowIdx}", $r->report_number ?: '#' . $r->id); $sheet->setCellValue("C{$rowIdx}", $r->project ? $r->project->code : '—'); $sheet->setCellValue("D{$rowIdx}", $r->user ? $r->user->name : 'N/A'); $sheet->setCellValue("E{$rowIdx}", $r->weather ?: '—'); $sheet->setCellValue("F{$rowIdx}", $workers); $sheet->setCellValue("G{$rowIdx}", number_format($manHours, 1)); $sheet->setCellValue("H{$rowIdx}", number_format($equipHours, 1)); $sheet->setCellValue("I{$rowIdx}", $r->activities->count()); $sheet->setCellValue("J{$rowIdx}", $r->issues->count()); $sheet->getStyle("A{$rowIdx}:J{$rowIdx}")->applyFromArray($thinBorder); $rowIdx++; } // Summary Row $sheet->setCellValue("A{$rowIdx}", 'TOTAL FOR PERIOD'); $sheet->mergeCells("A{$rowIdx}:E{$rowIdx}"); $sheet->setCellValue("F{$rowIdx}", $allLabors->sum('workers_count')); $sheet->setCellValue("G{$rowIdx}", number_format($totalPeriodManHours, 1)); $sheet->setCellValue("H{$rowIdx}", number_format($totalPeriodEquipHours, 1)); $sheet->setCellValue("I{$rowIdx}", $allActivities->count()); $sheet->setCellValue("J{$rowIdx}", $allIssues->count()); $sheet->getStyle("A{$rowIdx}:J{$rowIdx}")->applyFromArray($totalRowStyle); foreach ($cols as $col) { $sheet->getColumnDimension($col)->setAutoSize(true); } // ---------------------------------------------------- // 2. SHEET: MANPOWER & LABOR CATEGORY SUMMATION // ---------------------------------------------------- $sheetLabor = $spreadsheet->createSheet(); $sheetLabor->setTitle('Manpower by Category'); $sheetLabor->setShowGridlines(true); $sheetLabor->mergeCells('A1:F1'); $sheetLabor->setCellValue('A1', 'MANPOWER & LABOR — SUMMATION PER TRADE / CATEGORY'); $sheetLabor->getStyle('A1:F1')->applyFromArray($sectionHeaderStyle); $sheetLabor->getRowDimension(1)->setRowHeight(28); $labSumHeaders = ['Trade / Labor Category', 'Total Worker-Days', 'Total Man-Hours (hrs)', 'Days Active', 'Avg Workers / Active Day', '% Share']; $labSumCols = range('A', 'F'); $lRow = 3; foreach ($labSumHeaders as $k => $h) { $sheetLabor->setCellValue("{$labSumCols[$k]}{$lRow}", $h); } $sheetLabor->getStyle("A{$lRow}:F{$lRow}")->applyFromArray($tableHeaderStyle); $lRow++; foreach ($laborCategorySummary as $cat) { $avgW = $cat['days_active'] > 0 ? round($cat['worker_days'] / $cat['days_active'], 1) : $cat['worker_days']; $sheetLabor->setCellValue("A{$lRow}", $cat['trade']); $sheetLabor->setCellValue("B{$lRow}", $cat['worker_days']); $sheetLabor->setCellValue("C{$lRow}", number_format($cat['total_man_hours'], 1)); $sheetLabor->setCellValue("D{$lRow}", $cat['days_active']); $sheetLabor->setCellValue("E{$lRow}", $avgW); $sheetLabor->setCellValue("F{$lRow}", $cat['percent_share'] . '%'); $sheetLabor->getStyle("A{$lRow}:F{$lRow}")->applyFromArray($thinBorder); $lRow++; } // Total Row $sheetLabor->setCellValue("A{$lRow}", 'TOTAL MANPOWER'); $sheetLabor->setCellValue("B{$lRow}", $allLabors->sum('workers_count')); $sheetLabor->setCellValue("C{$lRow}", number_format($totalPeriodManHours, 1)); $sheetLabor->setCellValue("D{$lRow}", $reports->count()); $sheetLabor->setCellValue("E{$lRow}", $reports->count() > 0 ? round($allLabors->sum('workers_count') / $reports->count(), 1) : 0); $sheetLabor->setCellValue("F{$lRow}", '100.0%'); $sheetLabor->getStyle("A{$lRow}:F{$lRow}")->applyFromArray($totalRowStyle); $lRow += 3; // Itemized Daily Log Section $sheetLabor->mergeCells("A{$lRow}:G{$lRow}"); $sheetLabor->setCellValue("A{$lRow}", 'ITEMIZED DAILY MANPOWER LOG'); $sheetLabor->getStyle("A{$lRow}:G{$lRow}")->applyFromArray($headerStyle); $lRow++; $labDetailHeaders = ['Date', 'Report #', 'Trade / Category', 'Workers Count', 'Hours/Worker', 'Total Man-Hours', 'Notes / Location']; $labDetailCols = range('A', 'G'); foreach ($labDetailHeaders as $k => $h) { $sheetLabor->setCellValue("{$labDetailCols[$k]}{$lRow}", $h); } $sheetLabor->getStyle("A{$lRow}:G{$lRow}")->applyFromArray($tableHeaderStyle); $lRow++; foreach ($reports as $r) { foreach ($r->labors as $lab) { $totalH = $lab->workers_count * ($lab->hours ?: 0); $sheetLabor->setCellValue("A{$lRow}", $r->report_date ? $r->report_date->format('Y-m-d') : ''); $sheetLabor->setCellValue("B{$lRow}", $r->report_number ?: '#' . $r->id); $sheetLabor->setCellValue("C{$lRow}", $lab->trade ?: 'Labor'); $sheetLabor->setCellValue("D{$lRow}", $lab->workers_count); $sheetLabor->setCellValue("E{$lRow}", $lab->hours ?: 0); $sheetLabor->setCellValue("F{$lRow}", number_format($totalH, 1)); $sheetLabor->setCellValue("G{$lRow}", $lab->notes ?: '—'); $sheetLabor->getStyle("A{$lRow}:G{$lRow}")->applyFromArray($thinBorder); $lRow++; } } foreach ($labDetailCols as $col) { $sheetLabor->getColumnDimension($col)->setAutoSize(true); } // ---------------------------------------------------- // 3. SHEET: EQUIPMENT & MACHINERY CATEGORY SUMMATION // ---------------------------------------------------- $sheetEquip = $spreadsheet->createSheet(); $sheetEquip->setTitle('Equipment by Category'); $sheetEquip->setShowGridlines(true); $sheetEquip->mergeCells('A1:F1'); $sheetEquip->setCellValue('A1', 'EQUIPMENT & MACHINERY — SUMMATION PER MODEL / CATEGORY'); $sheetEquip->getStyle('A1:F1')->applyFromArray($sectionHeaderStyle); $sheetEquip->getRowDimension(1)->setRowHeight(28); $eqSumHeaders = ['Equipment Name / Model', 'Total Hours Operated (hrs)', 'Days Utilized', 'Avg Hours / Utilized Day', 'Latest Status', '% Share']; $eqSumCols = range('A', 'F'); $eRow = 3; foreach ($eqSumHeaders as $k => $h) { $sheetEquip->setCellValue("{$eqSumCols[$k]}{$eRow}", $h); } $sheetEquip->getStyle("A{$eRow}:F{$eRow}")->applyFromArray($tableHeaderStyle); $eRow++; foreach ($equipmentCategorySummary as $eqCat) { $avgH = $eqCat['days_active'] > 0 ? round($eqCat['total_hours'] / $eqCat['days_active'], 1) : $eqCat['total_hours']; $sheetEquip->setCellValue("A{$eRow}", $eqCat['equipment_name']); $sheetEquip->setCellValue("B{$eRow}", number_format($eqCat['total_hours'], 1)); $sheetEquip->setCellValue("C{$eRow}", $eqCat['days_active']); $sheetEquip->setCellValue("D{$eRow}", $avgH); $sheetEquip->setCellValue("E{$eRow}", $eqCat['latest_status']); $sheetEquip->setCellValue("F{$eRow}", $eqCat['percent_share'] . '%'); $sheetEquip->getStyle("A{$eRow}:F{$eRow}")->applyFromArray($thinBorder); $eRow++; } // Total Row $sheetEquip->setCellValue("A{$eRow}", 'TOTAL EQUIPMENT'); $sheetEquip->setCellValue("B{$eRow}", number_format($totalPeriodEquipHours, 1)); $sheetEquip->setCellValue("C{$eRow}", $reports->count()); $sheetEquip->setCellValue("D{$eRow}", $reports->count() > 0 ? round($totalPeriodEquipHours / $reports->count(), 1) : 0); $sheetEquip->setCellValue("E{$eRow}", '—'); $sheetEquip->setCellValue("F{$eRow}", '100.0%'); $sheetEquip->getStyle("A{$eRow}:F{$eRow}")->applyFromArray($totalRowStyle); $eRow += 3; // Itemized Daily Equipment Log $sheetEquip->mergeCells("A{$eRow}:E{$eRow}"); $sheetEquip->setCellValue("A{$eRow}", 'ITEMIZED DAILY EQUIPMENT LOG'); $sheetEquip->getStyle("A{$eRow}:E{$eRow}")->applyFromArray($headerStyle); $eRow++; $eqDetailHeaders = ['Date', 'Report #', 'Equipment Name / Model', 'Hours Used', 'Status']; $eqDetailCols = range('A', 'E'); foreach ($eqDetailHeaders as $k => $h) { $sheetEquip->setCellValue("{$eqDetailCols[$k]}{$eRow}", $h); } $sheetEquip->getStyle("A{$eRow}:E{$eRow}")->applyFromArray($tableHeaderStyle); $eRow++; foreach ($reports as $r) { foreach ($r->equipment as $eq) { $sheetEquip->setCellValue("A{$eRow}", $r->report_date ? $r->report_date->format('Y-m-d') : ''); $sheetEquip->setCellValue("B{$eRow}", $r->report_number ?: '#' . $r->id); $sheetEquip->setCellValue("C{$eRow}", $eq->equipment_name ?: 'Equipment'); $sheetEquip->setCellValue("D{$eRow}", $eq->hours_used ?: 0); $sheetEquip->setCellValue("E{$eRow}", $eq->status ?: 'Active'); $sheetEquip->getStyle("A{$eRow}:E{$eRow}")->applyFromArray($thinBorder); $eRow++; } } foreach ($eqDetailCols as $col) { $sheetEquip->getColumnDimension($col)->setAutoSize(true); } // ---------------------------------------------------- // 4. SHEET: MATERIALS CATEGORY SUMMATION // ---------------------------------------------------- $sheetMat = $spreadsheet->createSheet(); $sheetMat->setTitle('Materials by Category'); $sheetMat->setShowGridlines(true); $sheetMat->mergeCells('A1:E1'); $sheetMat->setCellValue('A1', 'MATERIALS TRACKED — SUMMATION PER MATERIAL CATEGORY'); $sheetMat->getStyle('A1:E1')->applyFromArray($sectionHeaderStyle); $sheetMat->getRowDimension(1)->setRowHeight(28); $matSumHeaders = ['Material Name / Category', 'Cumulative Quantity Received / Used', 'Unit', 'Entries Count', 'Condition Summary']; $matSumCols = range('A', 'E'); $mRow = 3; foreach ($matSumHeaders as $k => $h) { $sheetMat->setCellValue("{$matSumCols[$k]}{$mRow}", $h); } $sheetMat->getStyle("A{$mRow}:E{$mRow}")->applyFromArray($tableHeaderStyle); $mRow++; foreach ($materialsCategorySummary as $mCat) { $sheetMat->setCellValue("A{$mRow}", $mCat['material_name']); $sheetMat->setCellValue("B{$mRow}", $mCat['total_quantity']); $sheetMat->setCellValue("C{$mRow}", $mCat['unit']); $sheetMat->setCellValue("D{$mRow}", $mCat['deliveries_count']); $sheetMat->setCellValue("E{$mRow}", $mCat['condition_summary']); $sheetMat->getStyle("A{$mRow}:E{$mRow}")->applyFromArray($thinBorder); $mRow++; } $mRow += 2; // Itemized Daily Materials Log $sheetMat->mergeCells("A{$mRow}:E{$mRow}"); $sheetMat->setCellValue("A{$mRow}", 'ITEMIZED DAILY MATERIALS LOG'); $sheetMat->getStyle("A{$mRow}:E{$mRow}")->applyFromArray($headerStyle); $mRow++; $matDetailHeaders = ['Date', 'Report #', 'Material Name', 'Quantity Received', 'Condition / Remarks']; $matDetailCols = range('A', 'E'); foreach ($matDetailHeaders as $k => $h) { $sheetMat->setCellValue("{$matDetailCols[$k]}{$mRow}", $h); } $sheetMat->getStyle("A{$mRow}:E{$mRow}")->applyFromArray($tableHeaderStyle); $mRow++; foreach ($reports as $r) { foreach ($r->materials as $mat) { $sheetMat->setCellValue("A{$mRow}", $r->report_date ? $r->report_date->format('Y-m-d') : ''); $sheetMat->setCellValue("B{$mRow}", $r->report_number ?: '#' . $r->id); $sheetMat->setCellValue("C{$mRow}", $mat->material_name ?: 'Material'); $sheetMat->setCellValue("D{$mRow}", $mat->quantity_received ?: '—'); $sheetMat->setCellValue("E{$mRow}", $mat->condition ?: 'Good'); $sheetMat->getStyle("A{$mRow}:E{$mRow}")->applyFromArray($thinBorder); $mRow++; } } foreach ($matDetailCols as $col) { $sheetMat->getColumnDimension($col)->setAutoSize(true); } // ---------------------------------------------------- // 5. SHEET: ACTIVITIES & WORK PROGRESS // ---------------------------------------------------- $sheetAct = $spreadsheet->createSheet(); $sheetAct->setTitle('Activities & Progress'); $sheetAct->setShowGridlines(true); $sheetAct->mergeCells('A1:D1'); $sheetAct->setCellValue('A1', 'WORK ACTIVITIES & TASKS — PROGRESS BY TASK / AREA'); $sheetAct->getStyle('A1:D1')->applyFromArray($sectionHeaderStyle); $sheetAct->getRowDimension(1)->setRowHeight(28); $actSumHeaders = ['Task Name', 'Location / Zone Area', 'Highest Progress (%)', 'Days Worked']; $actSumCols = range('A', 'D'); $aRow = 3; foreach ($actSumHeaders as $k => $h) { $sheetAct->setCellValue("{$actSumCols[$k]}{$aRow}", $h); } $sheetAct->getStyle("A{$aRow}:D{$aRow}")->applyFromArray($tableHeaderStyle); $aRow++; foreach ($activitiesCategorySummary as $aCat) { $sheetAct->setCellValue("A{$aRow}", $aCat['task_name']); $sheetAct->setCellValue("B{$aRow}", $aCat['zones']); $sheetAct->setCellValue("C{$aRow}", $aCat['highest_progress'] . '%'); $sheetAct->setCellValue("D{$aRow}", $aCat['days_worked']); $sheetAct->getStyle("A{$aRow}:D{$aRow}")->applyFromArray($thinBorder); $aRow++; } $aRow += 2; // Itemized Daily Activities Log $sheetAct->mergeCells("A{$aRow}:F{$aRow}"); $sheetAct->setCellValue("A{$aRow}", 'ITEMIZED DAILY ACTIVITIES LOG'); $sheetAct->getStyle("A{$aRow}:F{$aRow}")->applyFromArray($headerStyle); $aRow++; $actDetailHeaders = ['Date', 'Report #', 'Task Name', 'Zone / Area', 'Qty Completed', 'Progress (%)']; $actDetailCols = range('A', 'F'); foreach ($actDetailHeaders as $k => $h) { $sheetAct->setCellValue("{$actDetailCols[$k]}{$aRow}", $h); } $sheetAct->getStyle("A{$aRow}:F{$aRow}")->applyFromArray($tableHeaderStyle); $aRow++; foreach ($reports as $r) { foreach ($r->activities as $act) { $sheetAct->setCellValue("A{$aRow}", $r->report_date ? $r->report_date->format('Y-m-d') : ''); $sheetAct->setCellValue("B{$aRow}", $r->report_number ?: '#' . $r->id); $sheetAct->setCellValue("C{$aRow}", $act->task_name ?: '—'); $sheetAct->setCellValue("D{$aRow}", $act->zone_area ?: '—'); $sheetAct->setCellValue("E{$aRow}", $act->quantity_completed ?: '—'); $sheetAct->setCellValue("F{$aRow}", ($act->percentage_completed ?: 0) . '%'); $sheetAct->getStyle("A{$aRow}:F{$aRow}")->applyFromArray($thinBorder); $aRow++; } } foreach ($actDetailCols as $col) { $sheetAct->getColumnDimension($col)->setAutoSize(true); } // ---------------------------------------------------- // 6. SHEET: ISSUES & DELAYS CATEGORY SUMMATION // ---------------------------------------------------- $sheetIss = $spreadsheet->createSheet(); $sheetIss->setTitle('Issues & Delays'); $sheetIss->setShowGridlines(true); $sheetIss->mergeCells('A1:E1'); $sheetIss->setCellValue('A1', 'ISSUES & DELAYS — SUMMATION PER ISSUE CATEGORY'); $sheetIss->getStyle('A1:E1')->applyFromArray($sectionHeaderStyle); $sheetIss->getRowDimension(1)->setRowHeight(28); $issSumHeaders = ['Issue Category / Type', 'Incident Count', 'Resolved Count', 'Pending Count', 'Delay Impact Summary']; $issSumCols = range('A', 'E'); $iRow = 3; foreach ($issSumHeaders as $k => $h) { $sheetIss->setCellValue("{$issSumCols[$k]}{$iRow}", $h); } $sheetIss->getStyle("A{$iRow}:E{$iRow}")->applyFromArray($tableHeaderStyle); $iRow++; foreach ($issuesCategorySummary as $iCat) { $sheetIss->setCellValue("A{$iRow}", $iCat['issue_type']); $sheetIss->setCellValue("B{$iRow}", $iCat['incident_count']); $sheetIss->setCellValue("C{$iRow}", $iCat['resolved_count']); $sheetIss->setCellValue("D{$iRow}", $iCat['pending_count']); $sheetIss->setCellValue("E{$iRow}", $iCat['delay_impact_summary']); $sheetIss->getStyle("A{$iRow}:E{$iRow}")->applyFromArray($thinBorder); $iRow++; } $iRow += 2; // Itemized Daily Issues Log $sheetIss->mergeCells("A{$iRow}:F{$iRow}"); $sheetIss->setCellValue("A{$iRow}", 'ITEMIZED DAILY ISSUES & CONCERNS LOG'); $sheetIss->getStyle("A{$iRow}:F{$iRow}")->applyFromArray($headerStyle); $iRow++; $issDetailHeaders = ['Date', 'Report #', 'Issue Type', 'Description', 'Delay Impact', 'Status']; $issDetailCols = range('A', 'F'); foreach ($issDetailHeaders as $k => $h) { $sheetIss->setCellValue("{$issDetailCols[$k]}{$iRow}", $h); } $sheetIss->getStyle("A{$iRow}:F{$iRow}")->applyFromArray($tableHeaderStyle); $iRow++; foreach ($reports as $r) { foreach ($r->issues as $iss) { $status = $iss->resolved_at ? 'Resolved' : ($iss->status ?: 'Pending'); $sheetIss->setCellValue("A{$iRow}", $r->report_date ? $r->report_date->format('Y-m-d') : ''); $sheetIss->setCellValue("B{$iRow}", $r->report_number ?: '#' . $r->id); $sheetIss->setCellValue("C{$iRow}", $iss->issue_type ?: 'General'); $sheetIss->setCellValue("D{$iRow}", $iss->description ?: '—'); $sheetIss->setCellValue("E{$iRow}", $iss->delay_impact ?: '—'); $sheetIss->setCellValue("F{$iRow}", $status); $sheetIss->getStyle("A{$iRow}:F{$iRow}")->applyFromArray($thinBorder); $iRow++; } } foreach ($issDetailCols as $col) { $sheetIss->getColumnDimension($col)->setAutoSize(true); } // Set active sheet back to 0 $spreadsheet->setActiveSheetIndex(0); $projectSlug = $project ? $project->code : 'All'; $filename = 'DailyReports_' . $projectSlug . '_' . $startDate->format('Ymd') . '_' . $endDate->format('Ymd') . '.xlsx'; header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); header('Content-Disposition: attachment;filename="' . $filename . '"'); header('Cache-Control: max-age=0'); $writer = new Xlsx($spreadsheet); $writer->save('php://output'); exit; } public function pdfRange(Request $request) { $request->validate([ 'start_date' => 'required|date', 'end_date' => 'required|date|after_or_equal:start_date', 'project' => 'nullable|string', ]); $startDate = Carbon::parse($request->input('start_date'))->startOfDay(); $endDate = Carbon::parse($request->input('end_date'))->endOfDay(); $projectUlid = $request->input('project'); $project = $projectUlid ? Project::where('ulid', $projectUlid)->first() : null; $query = DailyReport::with(['project', 'user', 'labors', 'equipment', 'activities', 'materials', 'issues']) ->whereBetween('report_date', [$startDate->format('Y-m-d'), $endDate->format('Y-m-d')]) ->orderBy('report_date', 'asc'); if ($project) { $query->where('project_id', $project->id); } $reports = $query->get(); $allLabors = $reports->flatMap->labors; $totalPeriodManHours = $allLabors->sum(fn ($l) => $l->workers_count * ($l->hours ?: 0)); $allEquipment = $reports->flatMap->equipment; $totalPeriodEquipHours = $allEquipment->sum('hours_used'); $allMaterials = $reports->flatMap->materials; $allActivities = $reports->flatMap->activities; $allIssues = $reports->flatMap->issues; // 1. Manpower Category Summary $laborCategorySummary = $allLabors->groupBy(fn ($l) => $l->trade ?: 'Unspecified Trade') ->map(function ($group, $trade) use ($totalPeriodManHours) { $workerDays = $group->sum('workers_count'); $manHours = $group->sum(fn ($l) => $l->workers_count * ($l->hours ?: 0)); $activeCount = $group->count(); $percent = $totalPeriodManHours > 0 ? ($manHours / $totalPeriodManHours) * 100 : 0; return [ 'trade' => $trade, 'worker_days' => $workerDays, 'total_man_hours' => $manHours, 'days_active' => $activeCount, 'percent_share' => round($percent, 1), ]; })->values()->sortByDesc('total_man_hours')->values(); // 2. Equipment Category Summary $equipmentCategorySummary = $allEquipment->groupBy(fn ($eq) => $eq->equipment_name ?: 'Unspecified Equipment') ->map(function ($group, $name) use ($totalPeriodEquipHours) { $hours = $group->sum('hours_used'); $activeCount = $group->count(); $percent = $totalPeriodEquipHours > 0 ? ($hours / $totalPeriodEquipHours) * 100 : 0; $latestStatus = $group->last()->status ?? 'Active'; return [ 'equipment_name' => $name, 'total_hours' => $hours, 'days_active' => $activeCount, 'percent_share' => round($percent, 1), 'latest_status' => $latestStatus, ]; })->values()->sortByDesc('total_hours')->values(); // 3. Materials Category Summary $materialsCategorySummary = $allMaterials->groupBy(fn ($m) => $m->material_name ?: 'General Material') ->map(function ($group, $name) { $deliveriesCount = $group->count(); $quantities = $group->pluck('quantity_received')->filter()->all(); $numericSum = 0; $unit = ''; foreach ($quantities as $q) { if (preg_match('/^([0-9.]+)\s*(.*)$/', trim($q), $matches)) { $numericSum += (float) $matches[1]; if (empty($unit) && !empty($matches[2])) { $unit = $matches[2]; } } } $qtyDisplay = $numericSum > 0 ? (string)$numericSum : (implode(', ', array_slice($quantities, 0, 3)) ?: '—'); $conditions = $group->pluck('condition')->filter()->unique()->implode(', ') ?: 'Good'; return [ 'material_name' => $name, 'total_quantity' => $qtyDisplay, 'unit' => $unit ?: 'units', 'deliveries_count' => $deliveriesCount, 'condition_summary' => $conditions, ]; })->values()->sortByDesc('deliveries_count')->values(); // 4. Activities Category Summary $activitiesCategorySummary = $allActivities->groupBy(fn ($a) => $a->task_name ?: 'General Works') ->map(function ($group, $name) { $zones = $group->pluck('zone_area')->filter()->unique()->implode(', ') ?: 'Site Wide'; $highestProgress = $group->max('percentage_completed') ?: 0; $daysWorked = $group->count(); return [ 'task_name' => $name, 'zones' => $zones, 'highest_progress' => $highestProgress, 'days_worked' => $daysWorked, ]; })->values()->sortByDesc('days_worked')->values(); // 5. Issues Category Summary $issuesCategorySummary = $allIssues->groupBy(fn ($i) => $i->issue_type ?: 'General') ->map(function ($group, $type) { $count = $group->count(); $resolved = $group->whereNotNull('resolved_at')->count(); $delays = $group->pluck('delay_impact')->filter()->all(); return [ 'issue_type' => $type, 'incident_count' => $count, 'resolved_count' => $resolved, 'pending_count' => $count - $resolved, 'delay_impact_summary' => !empty($delays) ? implode('; ', array_slice($delays, 0, 3)) : 'None reported', ]; })->values()->sortByDesc('incident_count')->values(); $summary = [ 'total_workers' => $allLabors->sum('workers_count'), 'total_man_hours' => $totalPeriodManHours, 'total_equipment_hours' => $totalPeriodEquipHours, 'total_activities' => $allActivities->count(), 'total_materials' => $allMaterials->count(), 'total_issues' => $allIssues->count(), ]; $pdf = \Barryvdh\DomPDF\Facade\Pdf::loadView('dailyreports::pdf.daily-reports-range', [ 'project' => $project, 'reports' => $reports, 'startDate' => $startDate, 'endDate' => $endDate, 'summary' => $summary, 'laborCategorySummary' => $laborCategorySummary, 'equipmentCategorySummary' => $equipmentCategorySummary, 'materialsCategorySummary' => $materialsCategorySummary, 'activitiesCategorySummary' => $activitiesCategorySummary, 'issuesCategorySummary' => $issuesCategorySummary, ]); $projectSlug = $project ? $project->code : 'All'; $filename = 'DailyReports_' . $projectSlug . '_' . $startDate->format('Ymd') . '_' . $endDate->format('Ymd') . '.pdf'; return $pdf->download($filename); } }