Car-Repairs-Shop/app/Livewire/Invoices/CreateFromEstimate.php
sackey e3b2b220d2
Some checks are pending
linter / quality (push) Waiting to run
tests / ci (push) Waiting to run
Enhance UI and functionality across various components
- Increased icon sizes in service items, service orders, users, and technician management for better visibility.
- Added custom loading indicators with appropriate icons in search fields for vehicles, work orders, and technicians.
- Introduced invoice management routes for better organization and access control.
- Created a new test for the estimate PDF functionality to ensure proper rendering and data integrity.
2025-08-16 14:36:58 +00:00

197 lines
5.9 KiB
PHP

<?php
namespace App\Livewire\Invoices;
use App\Models\Branch;
use App\Models\Estimate;
use App\Models\Invoice;
use App\Models\Part;
use App\Models\ServiceItem;
use Livewire\Attributes\Validate;
use Livewire\Component;
class CreateFromEstimate extends Component
{
public Estimate $estimate;
#[Validate('required|string|max:255')]
public $subject = '';
#[Validate('nullable|string')]
public $description = '';
#[Validate('required|exists:branches,id')]
public $branch_id = '';
#[Validate('required|date')]
public $invoice_date = '';
#[Validate('required|date|after_or_equal:invoice_date')]
public $due_date = '';
#[Validate('required|numeric|min:0')]
public $tax_rate = 0;
public $lineItems = [];
public $branches = [];
public $serviceItems = [];
public $parts = [];
public function mount(Estimate $estimate)
{
$this->estimate = $estimate;
$this->authorize('create', Invoice::class);
// Pre-fill form with estimate data
$this->subject = $estimate->subject;
$this->description = $estimate->description;
$this->branch_id = $estimate->branch_id;
$this->invoice_date = now()->format('Y-m-d');
$this->due_date = now()->addDays(30)->format('Y-m-d');
$this->tax_rate = $estimate->tax_rate;
// Load estimate line items
$this->lineItems = $estimate->estimateLineItems->map(function ($item) {
return [
'type' => $item->type,
'service_item_id' => $item->service_item_id,
'part_id' => $item->part_id,
'description' => $item->description,
'quantity' => $item->quantity,
'unit_price' => $item->unit_price,
'total' => $item->total,
];
})->toArray();
$this->loadFormData();
}
public function loadFormData()
{
$this->branches = Branch::all();
$this->serviceItems = ServiceItem::all();
$this->parts = Part::all();
}
public function addLineItem()
{
$this->lineItems[] = [
'type' => 'service',
'service_item_id' => '',
'part_id' => '',
'description' => '',
'quantity' => 1,
'unit_price' => 0,
'total' => 0,
];
}
public function removeLineItem($index)
{
unset($this->lineItems[$index]);
$this->lineItems = array_values($this->lineItems);
}
public function updatedLineItems($value, $key)
{
$keyParts = explode('.', $key);
$index = $keyParts[0];
$field = $keyParts[1];
if ($field === 'type') {
// Reset related fields when type changes
$this->lineItems[$index]['service_item_id'] = '';
$this->lineItems[$index]['part_id'] = '';
$this->lineItems[$index]['description'] = '';
$this->lineItems[$index]['unit_price'] = 0;
}
if ($field === 'service_item_id' && $this->lineItems[$index]['type'] === 'service') {
$serviceItem = ServiceItem::find($value);
if ($serviceItem) {
$this->lineItems[$index]['description'] = $serviceItem->name;
$this->lineItems[$index]['unit_price'] = $serviceItem->price;
}
}
if ($field === 'part_id' && $this->lineItems[$index]['type'] === 'part') {
$part = Part::find($value);
if ($part) {
$this->lineItems[$index]['description'] = $part->name;
$this->lineItems[$index]['unit_price'] = $part->sell_price;
}
}
// Recalculate total for this line item
if (in_array($field, ['quantity', 'unit_price'])) {
$quantity = (float) $this->lineItems[$index]['quantity'];
$unitPrice = (float) $this->lineItems[$index]['unit_price'];
$this->lineItems[$index]['total'] = $quantity * $unitPrice;
}
}
public function getSubtotalProperty()
{
return collect($this->lineItems)->sum('total');
}
public function getTaxAmountProperty()
{
return $this->subtotal * ($this->tax_rate / 100);
}
public function getTotalProperty()
{
return $this->subtotal + $this->taxAmount;
}
public function save()
{
$this->validate();
$invoice = Invoice::create([
'job_card_id' => $this->estimate->job_card_id,
'estimate_id' => $this->estimate->id,
'customer_id' => $this->estimate->customer_id,
'branch_id' => $this->branch_id,
'created_by' => auth()->id(),
'invoice_number' => Invoice::generateInvoiceNumber($this->branch_id),
'subject' => $this->subject,
'description' => $this->description,
'invoice_date' => $this->invoice_date,
'due_date' => $this->due_date,
'subtotal' => $this->subtotal,
'tax_rate' => $this->tax_rate,
'tax_amount' => $this->taxAmount,
'total_amount' => $this->total,
'status' => 'draft',
]);
// Create line items
foreach ($this->lineItems as $item) {
$invoice->invoiceLineItems()->create([
'type' => $item['type'],
'service_item_id' => $item['service_item_id'] ?: null,
'part_id' => $item['part_id'] ?: null,
'description' => $item['description'],
'quantity' => $item['quantity'],
'unit_price' => $item['unit_price'],
'total' => $item['total'],
]);
}
session()->flash('success', 'Invoice created successfully from estimate.');
return redirect()->route('invoices.show', $invoice);
}
public function render()
{
return view('livewire.invoices.create-from-estimate')
->layout('layouts.app');
}
}