invoice = $invoice; // Check if invoice can be edited if (in_array($invoice->status, ['paid', 'cancelled'])) { session()->flash('error', 'This invoice cannot be edited as it is '.$invoice->status.'.'); return $this->redirect(route('invoices.show', $invoice)); } // Populate form data $this->customer_id = $invoice->customer_id; $this->branch_id = $invoice->branch_id; $this->invoice_date = $invoice->invoice_date->format('Y-m-d'); $this->due_date = $invoice->due_date->format('Y-m-d'); $this->description = $invoice->description; $this->notes = $invoice->notes; $this->terms_and_conditions = $invoice->terms_and_conditions; $this->tax_rate = $invoice->tax_rate; $this->discount_amount = $invoice->discount_amount; // Load line items $this->line_items = $invoice->lineItems->map(function ($item) { return [ 'id' => $item->id, 'type' => $item->type, 'description' => $item->description, 'quantity' => $item->quantity, 'unit_price' => $item->unit_price, 'part_id' => $item->part_id, 'part_number' => $item->part_number, 'technical_notes' => $item->technical_notes, ]; })->toArray(); // Load data $this->customers = Customer::orderBy('first_name')->orderBy('last_name')->get(); $this->branches = Branch::orderBy('name')->get(); $this->parts = Part::where('status', 'active')->orderBy('name')->get(); $this->service_items = ServiceItem::where('status', 'active')->orderBy('service_name')->get(); // Add empty line item if none exist if (empty($this->line_items)) { $this->addLineItem(); } } public function addLineItem() { $this->line_items[] = [ 'id' => null, 'type' => 'labour', 'description' => '', 'quantity' => 1, 'unit_price' => 0, 'part_id' => '', 'part_number' => '', 'technical_notes' => '', ]; } public function removeLineItem($index) { unset($this->line_items[$index]); $this->line_items = array_values($this->line_items); } public function updatedLineItems($value, $key) { // Auto-populate part details when part is selected if (str_contains($key, 'part_id')) { $index = explode('.', $key)[0]; $partId = $this->line_items[$index]['part_id']; if ($partId) { $part = Part::find($partId); if ($part) { $this->line_items[$index]['description'] = $part->name; $this->line_items[$index]['unit_price'] = $part->sell_price ?? 0; $this->line_items[$index]['part_number'] = $part->part_number; } } } // Reset part selection when type changes if (str_contains($key, 'type')) { $index = explode('.', $key)[0]; $this->line_items[$index]['part_id'] = ''; $this->line_items[$index]['part_number'] = ''; if ($this->line_items[$index]['type'] !== 'parts') { $this->line_items[$index]['description'] = ''; $this->line_items[$index]['unit_price'] = 0; } } // Auto-populate service item details if (str_contains($key, 'service_item_id')) { $index = explode('.', $key)[0]; $serviceItemId = $this->line_items[$index]['service_item_id'] ?? null; if ($serviceItemId) { $serviceItem = ServiceItem::find($serviceItemId); if ($serviceItem) { $this->line_items[$index]['description'] = $serviceItem->service_name; $this->line_items[$index]['unit_price'] = $serviceItem->price ?? 0; } } } } public function calculateSubtotal() { return collect($this->line_items)->sum(function ($item) { return ($item['quantity'] ?? 0) * ($item['unit_price'] ?? 0); }); } public function calculateTax() { $subtotal = $this->calculateSubtotal() - $this->discount_amount; return $subtotal * ($this->tax_rate / 100); } public function calculateTotal() { $subtotal = $this->calculateSubtotal(); $tax = $this->calculateTax(); return $subtotal + $tax - $this->discount_amount; } public function save() { $this->validate(); // Validate line items if (empty($this->line_items)) { $this->addError('line_items', 'At least one line item is required.'); return; } foreach ($this->line_items as $index => $item) { if (empty($item['description'])) { $this->addError("line_items.{$index}.description", 'Description is required.'); return; } if ($item['quantity'] <= 0) { $this->addError("line_items.{$index}.quantity", 'Quantity must be greater than 0.'); return; } if ($item['unit_price'] < 0) { $this->addError("line_items.{$index}.unit_price", 'Unit price cannot be negative.'); return; } } // Update invoice $this->invoice->update([ 'customer_id' => $this->customer_id, 'branch_id' => $this->branch_id, 'invoice_date' => $this->invoice_date, 'due_date' => $this->due_date, 'description' => $this->description, 'notes' => $this->notes, 'terms_and_conditions' => $this->terms_and_conditions, 'tax_rate' => $this->tax_rate, 'discount_amount' => $this->discount_amount, ]); // Get existing line item IDs $existingIds = collect($this->line_items)->pluck('id')->filter()->toArray(); // Delete line items not in the current list $this->invoice->lineItems()->whereNotIn('id', $existingIds)->delete(); // Update or create line items foreach ($this->line_items as $item) { if ($item['id']) { // Update existing line item InvoiceLineItem::where('id', $item['id'])->update([ 'type' => $item['type'], 'description' => $item['description'], 'quantity' => $item['quantity'], 'unit_price' => $item['unit_price'], 'part_id' => $item['part_id'] ?: null, 'part_number' => $item['part_number'] ?: null, 'technical_notes' => $item['technical_notes'] ?: null, ]); } else { // Create new line item InvoiceLineItem::create([ 'invoice_id' => $this->invoice->id, 'type' => $item['type'], 'description' => $item['description'], 'quantity' => $item['quantity'], 'unit_price' => $item['unit_price'], 'part_id' => $item['part_id'] ?: null, 'part_number' => $item['part_number'] ?: null, 'technical_notes' => $item['technical_notes'] ?: null, ]); } } session()->flash('success', 'Invoice updated successfully.'); return $this->redirect(route('invoices.show', $this->invoice)); } #[Layout('components.layouts.app.sidebar')] public function render() { return view('livewire.invoices.edit'); } }