All Kent Code Updated
This commit is contained in:
@@ -3,21 +3,51 @@
|
||||
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 App\Models\InvoiceChargeGroup;
|
||||
use App\Models\InvoiceChargeGroupItem;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Mpdf\Mpdf;
|
||||
|
||||
class AdminInvoiceController extends Controller
|
||||
{
|
||||
// -------------------------------------------------------------
|
||||
// INVOICE LIST PAGE
|
||||
// -------------------------------------------------------------
|
||||
public function index()
|
||||
public function index(Request $request)
|
||||
{
|
||||
$invoices = Invoice::with(['order.shipments'])->latest()->get();
|
||||
$query = Invoice::with(['items', 'customer', 'container']);
|
||||
|
||||
// Search (जर पुढे form मधून पाठवलंस तर)
|
||||
if ($request->filled('search')) {
|
||||
$search = $request->search;
|
||||
$query->where(function ($q) use ($search) {
|
||||
$q->where('invoice_number', 'like', "%{$search}%")
|
||||
->orWhere('customer_name', 'like', "%{$search}%");
|
||||
});
|
||||
}
|
||||
|
||||
// Status filter
|
||||
if ($request->filled('status') && $request->status !== 'all') {
|
||||
$query->where('status', $request->status);
|
||||
}
|
||||
|
||||
// Date range filter (invoice_date वर)
|
||||
if ($request->filled('start_date')) {
|
||||
$query->whereDate('invoice_date', '>=', $request->start_date);
|
||||
}
|
||||
|
||||
if ($request->filled('end_date')) {
|
||||
$query->whereDate('invoice_date', '<=', $request->end_date);
|
||||
}
|
||||
|
||||
// Latest first
|
||||
$invoices = $query->latest()->get();
|
||||
|
||||
return view('admin.invoice', compact('invoices'));
|
||||
}
|
||||
|
||||
@@ -26,13 +56,16 @@ class AdminInvoiceController extends Controller
|
||||
// -------------------------------------------------------------
|
||||
public function popup($id)
|
||||
{
|
||||
$invoice = Invoice::with(['items', 'order', 'installments'])->findOrFail($id);
|
||||
$invoice = Invoice::with([
|
||||
'items',
|
||||
'customer',
|
||||
'container',
|
||||
'chargeGroups.items.item',
|
||||
])->findOrFail($id);
|
||||
|
||||
$shipment = \App\Models\Shipment::whereHas('items', function ($q) use ($invoice) {
|
||||
$q->where('order_id', $invoice->order_id);
|
||||
})->first();
|
||||
$shipment = null;
|
||||
|
||||
return view('admin.popup_invoice', compact('invoice', 'shipment'));
|
||||
return view('admin.popup_invoice', compact('invoice', 'shipment'));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
@@ -40,76 +73,102 @@ class AdminInvoiceController extends Controller
|
||||
// -------------------------------------------------------------
|
||||
public function edit($id)
|
||||
{
|
||||
$invoice = Invoice::with(['order.shipments'])->findOrFail($id);
|
||||
$shipment = $invoice->order?->shipments?->first();
|
||||
$invoice = Invoice::with([
|
||||
'items',
|
||||
'customer',
|
||||
'container',
|
||||
'chargeGroups.items',
|
||||
])->findOrFail($id);
|
||||
|
||||
return view('admin.invoice_edit', compact('invoice', 'shipment'));
|
||||
$shipment = null;
|
||||
|
||||
$groupedItemIds = $invoice->chargeGroups
|
||||
->flatMap(function ($group) {
|
||||
return $group->items->pluck('invoice_item_id');
|
||||
})
|
||||
->unique()
|
||||
->values()
|
||||
->toArray();
|
||||
|
||||
return view('admin.invoice_edit', compact('invoice', 'shipment', 'groupedItemIds'));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// UPDATE INVOICE
|
||||
// UPDATE INVOICE (HEADER LEVEL)
|
||||
// -------------------------------------------------------------
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
Log::info("🟡 Invoice Update Request Received", [
|
||||
Log::info('🟡 Invoice Update Request Received', [
|
||||
'invoice_id' => $id,
|
||||
'request' => $request->all()
|
||||
'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',
|
||||
'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);
|
||||
Log::info('✅ Validated Invoice Update Data', $data);
|
||||
|
||||
$finalAmount = floatval($data['final_amount']);
|
||||
$taxPercent = floatval($data['tax_percent']);
|
||||
$taxAmount = 0;
|
||||
$finalAmount = (float) $data['final_amount'];
|
||||
$taxPercent = (float) $data['tax_percent'];
|
||||
|
||||
if ($data['tax_type'] === 'gst') {
|
||||
Log::info("🟢 GST Selected", compact('taxPercent'));
|
||||
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'));
|
||||
Log::info('🔵 IGST Selected', compact('taxPercent'));
|
||||
|
||||
$data['cgst_percent'] = 0;
|
||||
$data['sgst_percent'] = 0;
|
||||
$data['igst_percent'] = $taxPercent;
|
||||
}
|
||||
|
||||
$taxAmount = ($finalAmount * $taxPercent) / 100;
|
||||
$gstAmount = ($finalAmount * $taxPercent) / 100;
|
||||
$data['gst_amount'] = $gstAmount;
|
||||
$data['final_amount_with_gst'] = $finalAmount + $gstAmount;
|
||||
$data['gst_percent'] = $taxPercent;
|
||||
|
||||
$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'],
|
||||
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
|
||||
Log::info('✅ Invoice Updated Successfully', [
|
||||
'invoice_id' => $invoice->id,
|
||||
]);
|
||||
|
||||
$invoice->refresh();
|
||||
Log::info('🔍 Invoice AFTER UPDATE (DB values)', [
|
||||
'invoice_id' => $invoice->id,
|
||||
'final_amount' => $invoice->final_amount,
|
||||
'gst_percent' => $invoice->gst_percent,
|
||||
'gst_amount' => $invoice->gst_amount,
|
||||
'final_amount_with_gst' => $invoice->final_amount_with_gst,
|
||||
'tax_type' => $invoice->tax_type,
|
||||
'cgst_percent' => $invoice->cgst_percent,
|
||||
'sgst_percent' => $invoice->sgst_percent,
|
||||
'igst_percent' => $invoice->igst_percent,
|
||||
]);
|
||||
|
||||
// regenerate PDF
|
||||
$this->generateInvoicePDF($invoice);
|
||||
|
||||
return redirect()
|
||||
@@ -117,50 +176,120 @@ class AdminInvoiceController extends Controller
|
||||
->with('success', 'Invoice updated & PDF generated successfully.');
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// 🔹 UPDATE INVOICE ITEMS (price + ttl_amount)
|
||||
// -------------------------------------------------------------
|
||||
public function updateItems(Request $request, Invoice $invoice)
|
||||
{
|
||||
Log::info('🟡 Invoice Items Update Request', [
|
||||
'invoice_id' => $invoice->id,
|
||||
'payload' => $request->all(),
|
||||
]);
|
||||
|
||||
$data = $request->validate([
|
||||
'items' => ['required', 'array'],
|
||||
'items.*.price' => ['required', 'numeric', 'min:0'],
|
||||
'items.*.ttl_amount' => ['required', 'numeric', 'min:0'],
|
||||
]);
|
||||
|
||||
$itemsInput = $data['items'];
|
||||
|
||||
foreach ($itemsInput as $itemId => $itemData) {
|
||||
$item = InvoiceItem::where('id', $itemId)
|
||||
->where('invoice_id', $invoice->id)
|
||||
->first();
|
||||
|
||||
if (!$item) {
|
||||
Log::warning('Invoice item not found or mismatched invoice', [
|
||||
'invoice_id' => $invoice->id,
|
||||
'item_id' => $itemId,
|
||||
]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$item->price = $itemData['price'];
|
||||
$item->ttl_amount = $itemData['ttl_amount'];
|
||||
$item->save();
|
||||
}
|
||||
|
||||
$newBaseAmount = InvoiceItem::where('invoice_id', $invoice->id)
|
||||
->sum('ttl_amount');
|
||||
|
||||
$taxType = $invoice->tax_type;
|
||||
$cgstPercent = (float) ($invoice->cgst_percent ?? 0);
|
||||
$sgstPercent = (float) ($invoice->sgst_percent ?? 0);
|
||||
$igstPercent = (float) ($invoice->igst_percent ?? 0);
|
||||
|
||||
$gstPercent = 0;
|
||||
if ($taxType === 'gst') {
|
||||
$gstPercent = $cgstPercent + $sgstPercent;
|
||||
} elseif ($taxType === 'igst') {
|
||||
$gstPercent = $igstPercent;
|
||||
}
|
||||
|
||||
$gstAmount = $newBaseAmount * $gstPercent / 100;
|
||||
$finalWithGst = $newBaseAmount + $gstAmount;
|
||||
|
||||
$invoice->final_amount = $newBaseAmount;
|
||||
$invoice->gst_amount = $gstAmount;
|
||||
$invoice->final_amount_with_gst = $finalWithGst;
|
||||
$invoice->gst_percent = $gstPercent;
|
||||
$invoice->save();
|
||||
|
||||
Log::info('✅ Invoice items updated & totals recalculated', [
|
||||
'invoice_id' => $invoice->id,
|
||||
'final_amount' => $invoice->final_amount,
|
||||
'gst_amount' => $invoice->gst_amount,
|
||||
'final_amount_with_gst' => $invoice->final_amount_with_gst,
|
||||
'tax_type' => $invoice->tax_type,
|
||||
'cgst_percent' => $invoice->cgst_percent,
|
||||
'sgst_percent' => $invoice->sgst_percent,
|
||||
'igst_percent' => $invoice->igst_percent,
|
||||
]);
|
||||
|
||||
return back()->with('success', 'Invoice items updated successfully.');
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// PDF GENERATION USING mPDF
|
||||
// -------------------------------------------------------------
|
||||
public function generateInvoicePDF($invoice)
|
||||
{
|
||||
$invoice->load(['items', 'order.shipments']);
|
||||
$shipment = $invoice->order?->shipments?->first();
|
||||
$invoice->load(['items', 'customer', 'container']);
|
||||
$shipment = null;
|
||||
|
||||
$fileName = 'invoice-' . $invoice->invoice_number . '.pdf';
|
||||
$folder = public_path('invoices/');
|
||||
$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 = 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]);
|
||||
}
|
||||
|
||||
public function downloadInvoice($id)
|
||||
{
|
||||
$invoice = Invoice::findOrFail($id);
|
||||
|
||||
// Generate PDF if missing
|
||||
if (
|
||||
!$invoice->pdf_path ||
|
||||
!file_exists(public_path($invoice->pdf_path))
|
||||
) {
|
||||
$this->generateInvoicePDF($invoice);
|
||||
$invoice->refresh();
|
||||
}
|
||||
|
||||
return response()->download(public_path($invoice->pdf_path));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// INSTALLMENTS (ADD/DELETE)
|
||||
// INSTALLMENTS (ADD)
|
||||
// -------------------------------------------------------------
|
||||
public function storeInstallment(Request $request, $invoice_id)
|
||||
{
|
||||
@@ -171,16 +300,14 @@ class AdminInvoiceController extends Controller
|
||||
'amount' => 'required|numeric|min:1',
|
||||
]);
|
||||
|
||||
$invoice = Invoice::findOrFail($invoice_id);
|
||||
|
||||
$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.'
|
||||
'status' => 'error',
|
||||
'message' => 'Installment amount exceeds remaining balance.',
|
||||
], 422);
|
||||
}
|
||||
|
||||
@@ -194,52 +321,96 @@ class AdminInvoiceController extends Controller
|
||||
|
||||
$newPaid = $paidTotal + $request->amount;
|
||||
|
||||
// Mark as 'paid' if GST-inclusive total is cleared
|
||||
if ($newPaid >= $invoice->final_amount_with_gst) {
|
||||
$invoice->update(['status' => 'paid']);
|
||||
|
||||
$this->generateInvoicePDF($invoice);
|
||||
}
|
||||
|
||||
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
|
||||
'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,
|
||||
]);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// INSTALLMENTS (DELETE)
|
||||
// -------------------------------------------------------------
|
||||
public function deleteInstallment($id)
|
||||
{
|
||||
$installment = InvoiceInstallment::findOrFail($id);
|
||||
$invoice = $installment->invoice;
|
||||
|
||||
$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']);
|
||||
|
||||
$this->generateInvoicePDF($invoice);
|
||||
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
|
||||
'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,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// CHARGE GROUP SAVE (NEW)
|
||||
// -------------------------------------------------------------
|
||||
public function storeChargeGroup(Request $request, $invoiceId)
|
||||
{
|
||||
$invoice = Invoice::with('items')->findOrFail($invoiceId);
|
||||
|
||||
$data = $request->validate([
|
||||
'group_name' => 'nullable|string|max:255',
|
||||
'basis_type' => 'required|in:ttl_qty,amount,ttl_cbm,ttl_kg',
|
||||
'basis_value' => 'required|numeric',
|
||||
'rate' => 'required|numeric|min:0.0001',
|
||||
'auto_total' => 'required|numeric|min:0.01',
|
||||
'item_ids' => 'required|array',
|
||||
'item_ids.*' => 'integer|exists:invoice_items,id',
|
||||
]);
|
||||
|
||||
$group = InvoiceChargeGroup::create([
|
||||
'invoice_id' => $invoice->id,
|
||||
'group_name' => $data['group_name'] ?? null,
|
||||
'basis_type' => $data['basis_type'],
|
||||
'basis_value' => $data['basis_value'],
|
||||
'rate' => $data['rate'],
|
||||
'total_charge' => $data['auto_total'],
|
||||
]);
|
||||
|
||||
foreach ($data['item_ids'] as $itemId) {
|
||||
InvoiceChargeGroupItem::create([
|
||||
'group_id' => $group->id,
|
||||
'invoice_item_id' => $itemId,
|
||||
]);
|
||||
}
|
||||
|
||||
return redirect()
|
||||
->back()
|
||||
->with('success', 'Charge group saved successfully.');
|
||||
}
|
||||
|
||||
public function download(Invoice $invoice)
|
||||
{
|
||||
if (!$invoice->pdf_path || !Storage::exists($invoice->pdf_path)) {
|
||||
return back()->with('error', 'PDF not found.');
|
||||
}
|
||||
|
||||
return Storage::download($invoice->pdf_path, $invoice->invoice_number . '.pdf');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user