228 lines
8.4 KiB
PHP
228 lines
8.4 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Admin;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use Illuminate\Http\Request;
|
|
use App\Models\Invoice;
|
|
use App\Models\InvoiceItem;
|
|
use Mpdf\Mpdf;
|
|
use App\Models\InvoiceInstallment;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
class AdminInvoiceController extends Controller
|
|
{
|
|
// -------------------------------------------------------------
|
|
// INVOICE LIST PAGE
|
|
// -------------------------------------------------------------
|
|
public function index()
|
|
{
|
|
$invoices = Invoice::with(['order.shipments'])->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;
|
|
$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)
|
|
{
|
|
$invoice->load(['items', 'order.shipments']);
|
|
$shipment = $invoice->order?->shipments?->first();
|
|
$fileName = 'invoice-' . $invoice->invoice_number . '.pdf';
|
|
$folder = public_path('invoices/');
|
|
|
|
if (!file_exists($folder)) {
|
|
mkdir($folder, 0777, true);
|
|
}
|
|
|
|
$filePath = $folder . $fileName;
|
|
if (file_exists($filePath)) {
|
|
unlink($filePath);
|
|
}
|
|
|
|
$mpdf = new Mpdf(['mode' => 'utf-8', 'format' => 'A4', 'default_font' => 'sans-serif']);
|
|
$html = view('admin.pdf.invoice', ['invoice' => $invoice, 'shipment' => $shipment])->render();
|
|
$mpdf->WriteHTML($html);
|
|
$mpdf->Output($filePath, 'F');
|
|
$invoice->update(['pdf_path' => 'invoices/' . $fileName]);
|
|
}
|
|
|
|
// -------------------------------------------------------------
|
|
// INSTALLMENTS (ADD/DELETE)
|
|
// -------------------------------------------------------------
|
|
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');
|
|
// Use GST-inclusive total for all calculations/checks
|
|
$remaining = $invoice->final_amount_with_gst - $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,
|
|
]);
|
|
|
|
$newPaid = $paidTotal + $request->amount;
|
|
|
|
// Mark as 'paid' if GST-inclusive total is cleared
|
|
if ($newPaid >= $invoice->final_amount_with_gst) {
|
|
$invoice->update(['status' => 'paid']);
|
|
}
|
|
|
|
return response()->json([
|
|
'status' => 'success',
|
|
'message' => 'Installment added successfully.',
|
|
'installment' => $installment,
|
|
'totalPaid' => $newPaid,
|
|
'gstAmount' => $invoice->gst_amount,
|
|
'finalAmountWithGst' => $invoice->final_amount_with_gst,
|
|
'baseAmount' => $invoice->final_amount,
|
|
'remaining' => max(0, $invoice->final_amount_with_gst - $newPaid),
|
|
'isCompleted' => $newPaid >= $invoice->final_amount_with_gst
|
|
]);
|
|
}
|
|
public function deleteInstallment($id)
|
|
{
|
|
$installment = InvoiceInstallment::findOrFail($id);
|
|
$invoice = $installment->invoice;
|
|
|
|
$installment->delete();
|
|
|
|
$paidTotal = $invoice->installments()->sum('amount');
|
|
$remaining = $invoice->final_amount_with_gst - $paidTotal;
|
|
|
|
// Update status if not fully paid anymore
|
|
if ($remaining > 0 && $invoice->status === "paid") {
|
|
$invoice->update(['status' => 'pending']);
|
|
}
|
|
|
|
return response()->json([
|
|
'status' => 'success',
|
|
'message' => 'Installment deleted.',
|
|
'totalPaid' => $paidTotal,
|
|
'gstAmount' => $invoice->gst_amount,
|
|
'finalAmountWithGst' => $invoice->final_amount_with_gst,
|
|
'baseAmount' => $invoice->final_amount,
|
|
'remaining' => $remaining,
|
|
'isZero' => $paidTotal == 0
|
|
]);
|
|
}
|
|
|
|
}
|