- Added buttons for assigning diagnosis and starting diagnosis based on job card status in the job card view. - Implemented a modal for assigning technicians for diagnosis, including form validation and technician selection. - Updated routes to include a test route for job cards. - Created a new Blade view for testing inspection inputs. - Developed comprehensive feature tests for the estimate module, including creation, viewing, editing, and validation of estimates. - Added tests for estimate model relationships and statistics calculations. - Introduced a basic feature test for job cards index.
274 lines
8.6 KiB
PHP
274 lines
8.6 KiB
PHP
<?php
|
|
|
|
namespace App\Livewire\Estimates;
|
|
|
|
use App\Models\Estimate;
|
|
use App\Notifications\EstimateNotification;
|
|
use Barryvdh\DomPDF\Facade\Pdf;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Livewire\Attributes\Layout;
|
|
use Livewire\Component;
|
|
|
|
class Show extends Component
|
|
{
|
|
public Estimate $estimate;
|
|
|
|
public $showItemDetails = false;
|
|
|
|
public function mount(Estimate $estimate)
|
|
{
|
|
$this->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 {
|
|
// Generate PDF using DomPDF
|
|
$pdf = Pdf::loadView('estimates.pdf', [
|
|
'estimate' => $this->estimate,
|
|
]);
|
|
|
|
// Log the download
|
|
Log::info('Estimate PDF downloaded', [
|
|
'estimate_id' => $this->estimate->id,
|
|
'downloaded_by' => auth()->id(),
|
|
]);
|
|
|
|
return response()->streamDownload(function () use ($pdf) {
|
|
echo $pdf->output();
|
|
}, "estimate-{$this->estimate->estimate_number}.pdf");
|
|
|
|
} catch (\Exception $e) {
|
|
Log::error('Failed to generate estimate PDF', [
|
|
'estimate_id' => $this->estimate->id,
|
|
'error' => $e->getMessage(),
|
|
]);
|
|
|
|
session()->flash('error', 'Failed to generate PDF. Please try again.');
|
|
}
|
|
}
|
|
|
|
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]);
|
|
}
|
|
|
|
#[Layout('components.layouts.app')]
|
|
public function render()
|
|
{
|
|
return view('livewire.estimates.show');
|
|
}
|
|
}
|