estimate = $estimate->load([ 'jobCard.customer', 'jobCard.vehicle', 'jobCard.branch', 'customer', // For standalone estimates 'vehicle', // For standalone estimates 'diagnosis', 'preparedBy', 'lineItems', 'workOrders', ]); } public function approveEstimate() { if (! auth()->user()->can('approve', $this->estimate)) { session()->flash('error', 'You do not have permission to approve this estimate.'); return; } try { $this->estimate->update([ 'customer_approval_status' => 'approved', 'customer_approved_at' => now(), 'customer_approval_method' => 'staff_portal', 'status' => 'approved', ]); // Update job card status to approved (if job card exists) if ($this->estimate->jobCard) { $this->estimate->jobCard->update([ 'status' => 'approved', ]); } // Notify relevant parties $customer = $this->estimate->customer ?? $this->estimate->jobCard?->customer; if ($customer) { $customer->notify( new EstimateNotification($this->estimate, 'approved') ); } Log::info('Estimate approved', [ 'estimate_id' => $this->estimate->id, 'approved_by' => auth()->id(), ]); session()->flash('success', 'Estimate approved successfully.'); // Refresh estimate data $this->estimate->refresh(); $this->dispatch('$refresh'); } catch (\Exception $e) { Log::error('Failed to approve estimate', [ 'estimate_id' => $this->estimate->id, 'error' => $e->getMessage(), ]); session()->flash('error', 'Failed to approve estimate. Please try again.'); } } public function rejectEstimate() { if (! auth()->user()->can('reject', $this->estimate)) { session()->flash('error', 'You do not have permission to reject this estimate.'); return; } try { $this->estimate->update([ 'customer_approval_status' => 'rejected', 'customer_approved_at' => now(), 'customer_approval_method' => 'staff_portal', 'status' => 'rejected', ]); // Notify relevant parties $customer = $this->estimate->customer ?? $this->estimate->jobCard?->customer; if ($customer) { $customer->notify( new EstimateNotification($this->estimate, 'rejected') ); } Log::info('Estimate rejected', [ 'estimate_id' => $this->estimate->id, 'rejected_by' => auth()->id(), ]); session()->flash('success', 'Estimate rejected.'); // Refresh estimate data $this->estimate->refresh(); $this->dispatch('$refresh'); } catch (\Exception $e) { Log::error('Failed to reject estimate', [ 'estimate_id' => $this->estimate->id, 'error' => $e->getMessage(), ]); session()->flash('error', 'Failed to reject estimate. Please try again.'); } } public function sendToCustomer() { try { $this->estimate->update([ 'status' => 'sent', 'sent_to_customer_at' => now(), 'sent_by_id' => auth()->id(), ]); // Send notification to customer $customer = $this->estimate->customer ?? $this->estimate->jobCard?->customer; if ($customer) { $customer->notify( new EstimateNotification($this->estimate, 'sent') ); } // Update job card status (if job card exists) if ($this->estimate->jobCard) { $this->estimate->jobCard->update([ 'status' => 'estimate_sent', ]); } Log::info('Estimate sent to customer', [ 'estimate_id' => $this->estimate->id, 'customer_id' => $this->estimate->customer_id ?? $this->estimate->jobCard?->customer?->id, 'sent_by' => auth()->id(), ]); session()->flash('success', 'Estimate sent to customer successfully.'); // Refresh estimate data $this->estimate->refresh(); $this->dispatch('$refresh'); } catch (\Exception $e) { Log::error('Failed to send estimate to customer', [ 'estimate_id' => $this->estimate->id, 'error' => $e->getMessage(), ]); session()->flash('error', 'Failed to send estimate to customer. Please try again.'); } } public function duplicateEstimate() { try { // Create a new estimate based on current one $newEstimate = $this->estimate->replicate(); $newEstimate->estimate_number = 'EST-'.str_pad(Estimate::max('id') + 1, 6, '0', STR_PAD_LEFT); $newEstimate->status = 'draft'; $newEstimate->customer_approval_status = 'pending'; $newEstimate->sent_to_customer_at = null; $newEstimate->customer_viewed_at = null; $newEstimate->customer_approved_at = null; $newEstimate->prepared_by_id = auth()->id(); $newEstimate->save(); // Duplicate line items foreach ($this->estimate->lineItems as $lineItem) { $newLineItem = $lineItem->replicate(); $newLineItem->estimate_id = $newEstimate->id; $newLineItem->save(); } Log::info('Estimate duplicated', [ 'original_estimate_id' => $this->estimate->id, 'new_estimate_id' => $newEstimate->id, 'duplicated_by' => auth()->id(), ]); session()->flash('success', 'Estimate has been duplicated successfully.'); return redirect()->route('estimates.edit', $newEstimate); } catch (\Exception $e) { Log::error('Failed to duplicate estimate', [ 'estimate_id' => $this->estimate->id, 'error' => $e->getMessage(), ]); session()->flash('error', 'Failed to duplicate estimate. Please try again.'); } } public function downloadPDF() { try { // Load the estimate with relationships for PDF generation $estimate = $this->estimate->load([ 'customer', 'jobCard.customer', 'jobCard.vehicle', 'vehicle', 'lineItems.part', 'preparedBy', 'diagnosis', ]); // For now, let's use a simple approach that should work // First, verify HTML generation works $html = view('estimates.pdf', compact('estimate'))->render(); if (empty($html)) { throw new \Exception('Failed to generate HTML template'); } // Try different approaches for PDF generation try { // Approach 1: Use Laravel's PDF facade if available if (class_exists('\Barryvdh\DomPDF\Facade\Pdf')) { $pdf = \Barryvdh\DomPDF\Facade\Pdf::loadHtml($html); $pdf->setPaper('letter', 'portrait'); $output = $pdf->output(); } else { throw new \Exception('PDF facade not available'); } } catch (\Exception $e1) { try { // Approach 2: Direct DomPDF instantiation $dompdf = new \Dompdf\Dompdf; $dompdf->loadHtml($html); $dompdf->setPaper('letter', 'portrait'); $dompdf->render(); $output = $dompdf->output(); } catch (\Exception $e2) { // Approach 3: Return HTML for now $filename = 'estimate-'.$estimate->estimate_number.'.html'; return response()->streamDownload(function () use ($html) { echo $html; }, $filename, [ 'Content-Type' => 'text/html', 'Content-Disposition' => 'attachment; filename="'.$filename.'"', ]); } } // Create filename with estimate number $filename = 'estimate-'.$estimate->estimate_number.'.pdf'; // Return PDF for download return response()->streamDownload(function () use ($output) { echo $output; }, $filename, [ 'Content-Type' => 'application/pdf', 'Content-Disposition' => 'attachment; filename="'.$filename.'"', ]); } catch (\Exception $e) { // Log the error for debugging Log::error('PDF generation failed for estimate '.$this->estimate->id, [ 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString(), ]); // Show user-friendly error message session()->flash('error', 'Failed to generate PDF: '.$e->getMessage()); return null; } } public function toggleItemDetails() { $this->showItemDetails = ! $this->showItemDetails; } public function refreshEstimate() { $this->estimate->refresh(); session()->flash('success', 'Estimate data refreshed.'); $this->dispatch('$refresh'); } public function createWorkOrder() { if ($this->estimate->customer_approval_status !== 'approved') { session()->flash('error', 'Estimate must be approved before creating a work order.'); return; } return redirect()->route('work-orders.create', ['estimate' => $this->estimate->id]); } public function convertToInvoice() { if ($this->estimate->customer_approval_status !== 'approved') { session()->flash('error', 'Estimate must be approved before converting to invoice.'); return; } if (! auth()->user()->can('create', \App\Models\Invoice::class)) { session()->flash('error', 'You do not have permission to create invoices.'); return; } return redirect()->route('invoices.create-from-estimate', $this->estimate); } #[Layout('components.layouts.app')] public function render() { return view('livewire.estimates.show'); } }