latest()->get(); return view('admin.invoice', compact('invoices')); } // ------------------------------------------------------------- // POPUP VIEW (AJAX) // ------------------------------------------------------------- public function popup($id) { $invoice = Invoice::with(['items', 'order'])->findOrFail($id); // Find actual Shipment record $shipment = \App\Models\Shipment::whereHas('items', function ($q) use ($invoice) { $q->where('order_id', $invoice->order_id); }) ->first(); return view('admin.popup_invoice', compact('invoice', 'shipment')); } // ------------------------------------------------------------- // EDIT INVOICE PAGE // ------------------------------------------------------------- public function edit($id) { $invoice = Invoice::with(['order.shipments'])->findOrFail($id); $shipment = $invoice->order?->shipments?->first(); return view('admin.invoice_edit', compact('invoice', 'shipment')); } // ------------------------------------------------------------- // UPDATE INVOICE // ------------------------------------------------------------- public function update(Request $request, $id) { Log::info("🟡 Invoice Update Request Received", [ 'invoice_id' => $id, 'request' => $request->all() ]); $invoice = Invoice::findOrFail($id); $data = $request->validate([ 'invoice_date' => 'required|date', 'due_date' => 'required|date|after_or_equal:invoice_date', 'final_amount' => 'required|numeric|min:0', 'tax_type' => 'required|in:gst,igst', 'tax_percent' => 'required|numeric|min:0|max:28', 'status' => 'required|in:pending,paid,overdue', 'notes' => 'nullable|string', ]); Log::info("✅ Validated Invoice Update Data", $data); $finalAmount = floatval($data['final_amount']); $taxPercent = floatval($data['tax_percent']); $taxAmount = 0; if ($data['tax_type'] === 'gst') { Log::info("🟢 GST Selected", compact('taxPercent')); $data['cgst_percent'] = $taxPercent / 2; $data['sgst_percent'] = $taxPercent / 2; $data['igst_percent'] = 0; } else { Log::info("🔵 IGST Selected", compact('taxPercent')); $data['cgst_percent'] = 0; $data['sgst_percent'] = 0; $data['igst_percent'] = $taxPercent; } $taxAmount = ($finalAmount * $taxPercent) / 100; $data['gst_amount'] = $taxAmount; $data['final_amount_with_gst'] = $finalAmount + $taxAmount; // ✅ store original % for UI reference $data['gst_percent'] = $taxPercent; Log::info("📌 Final Calculated Invoice Values", [ 'invoice_id' => $invoice->id, 'final_amount' => $finalAmount, 'gst_amount' => $data['gst_amount'], 'final_amount_with_gst' => $data['final_amount_with_gst'], 'tax_type' => $data['tax_type'], 'cgst_percent' => $data['cgst_percent'], 'sgst_percent' => $data['sgst_percent'], 'igst_percent' => $data['igst_percent'], ]); $invoice->update($data); Log::info("✅ Invoice Updated Successfully", [ 'invoice_id' => $invoice->id ]); // regenerate PDF $this->generateInvoicePDF($invoice); return redirect() ->route('admin.invoices.index') ->with('success', 'Invoice updated & PDF generated successfully.'); } // ------------------------------------------------------------- // PDF GENERATION USING mPDF // ------------------------------------------------------------- public function generateInvoicePDF($invoice) { // Load relationship including shipment $invoice->load(['items', 'order.shipments']); // Fetch shipment $shipment = $invoice->order?->shipments?->first(); // PDF filename $fileName = 'invoice-' . $invoice->invoice_number . '.pdf'; // Directory path $folder = public_path('invoices/'); if (!file_exists($folder)) { mkdir($folder, 0777, true); } $filePath = $folder . $fileName; // Delete old file if exists if (file_exists($filePath)) { unlink($filePath); } // Create mPDF instance $mpdf = new Mpdf([ 'mode' => 'utf-8', 'format' => 'A4', 'default_font' => 'sans-serif', ]); // Load HTML view $html = view('admin.pdf.invoice', [ 'invoice' => $invoice, 'shipment' => $shipment ])->render(); // Write HTML to PDF $mpdf->WriteHTML($html); // Save PDF $mpdf->Output($filePath, 'F'); // Update DB path $invoice->update([ 'pdf_path' => 'invoices/' . $fileName ]); } public function storeInstallment(Request $request, $invoice_id) { $request->validate([ 'installment_date' => 'required|date', 'payment_method' => 'required|string', 'reference_no' => 'nullable|string', 'amount' => 'required|numeric|min:1', ]); $invoice = Invoice::findOrFail($invoice_id); $paidTotal = $invoice->installments()->sum('amount'); $remaining = $invoice->final_amount - $paidTotal; if ($request->amount > $remaining) { return response()->json([ 'status' => 'error', 'message' => 'Installment amount exceeds remaining balance.' ], 422); } $installment = InvoiceInstallment::create([ 'invoice_id' => $invoice_id, 'installment_date' => $request->installment_date, 'payment_method' => $request->payment_method, 'reference_no' => $request->reference_no, 'amount' => $request->amount, ]); // Update invoice status to paid if fully cleared $newPaid = $paidTotal + $request->amount; if ($newPaid >= $invoice->final_amount) { $invoice->update(['status' => 'paid']); } return response()->json([ 'status' => 'success', 'message' => 'Installment added successfully.', 'installment' => $installment, 'totalPaid' => $newPaid, 'remaining' => max(0, $invoice->final_amount - $newPaid), 'isCompleted' => $newPaid >= $invoice->final_amount ]); } public function deleteInstallment($id) { $installment = InvoiceInstallment::findOrFail($id); $invoice = $installment->invoice; // delete installment $installment->delete(); // recalc totals $paidTotal = $invoice->installments()->sum('amount'); $remaining = $invoice->final_amount - $paidTotal; // auto update invoice status if ($remaining > 0 && $invoice->status === "paid") { $invoice->update(['status' => 'pending']); } return response()->json([ 'status' => 'success', 'message' => 'Installment deleted.', 'totalPaid' => $paidTotal, 'remaining' => $remaining, 'isZero' => $paidTotal == 0 ]); } }