pdf Updated, Invoice Updated, Report Updated
This commit is contained in:
@@ -22,7 +22,8 @@ class AdminCustomerController extends Controller
|
||||
$query = User::with([
|
||||
'marks',
|
||||
'orders',
|
||||
'invoices.installments' // 🔥 IMPORTANT
|
||||
'invoices.installments',
|
||||
'invoices.chargeGroups', // 🔥 for order total calculation
|
||||
])->orderBy('id', 'desc');
|
||||
|
||||
if (!empty($search)) {
|
||||
@@ -159,4 +160,4 @@ class AdminCustomerController extends Controller
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -2,30 +2,31 @@
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Controllers\Controller; // ⬅️ हे नक्की असू दे
|
||||
use App\Models\Invoice;
|
||||
use App\Models\InvoiceItem;
|
||||
use App\Models\InvoiceInstallment;
|
||||
use App\Models\InvoiceChargeGroup;
|
||||
use App\Models\InvoiceChargeGroupItem;
|
||||
use App\Models\InvoiceInstallment;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Mpdf\Mpdf;
|
||||
|
||||
|
||||
class AdminInvoiceController extends Controller
|
||||
{
|
||||
// -------------------------------------------------------------
|
||||
// INVOICE LIST PAGE
|
||||
// INDEX (LIST ALL INVOICES WITH FILTERS)
|
||||
// -------------------------------------------------------------
|
||||
public function index(Request $request)
|
||||
{
|
||||
$query = Invoice::with(['items', 'customer', 'container']);
|
||||
$query = Invoice::query();
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$search = $request->search;
|
||||
$query->where(function ($q) use ($search) {
|
||||
$q->where('invoice_number', 'like', "%{$search}%")
|
||||
->orWhere('customer_name', 'like', "%{$search}%");
|
||||
->orWhere('customer_name', 'like', "%{$search}%");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -50,22 +51,22 @@ class AdminInvoiceController extends Controller
|
||||
// POPUP VIEW
|
||||
// -------------------------------------------------------------
|
||||
public function popup($id)
|
||||
{
|
||||
$invoice = Invoice::with([
|
||||
'items',
|
||||
'chargeGroups.items',
|
||||
])->findOrFail($id);
|
||||
{
|
||||
$invoice = Invoice::with([
|
||||
'items',
|
||||
'chargeGroups.items',
|
||||
])->findOrFail($id);
|
||||
|
||||
$shipment = null;
|
||||
$shipment = null;
|
||||
|
||||
$groupedItemIds = $invoice->chargeGroups
|
||||
->flatMap(fn($group) => $group->items->pluck('invoice_item_id'))
|
||||
->unique()
|
||||
->values()
|
||||
->toArray();
|
||||
$groupedItemIds = $invoice->chargeGroups
|
||||
->flatMap(fn($group) => $group->items->pluck('invoice_item_id'))
|
||||
->unique()
|
||||
->values()
|
||||
->toArray();
|
||||
|
||||
return view('admin.popup_invoice', compact('invoice', 'shipment', 'groupedItemIds'));
|
||||
}
|
||||
return view('admin.popup_invoice', compact('invoice', 'shipment', 'groupedItemIds'));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// EDIT INVOICE PAGE
|
||||
@@ -79,31 +80,29 @@ class AdminInvoiceController extends Controller
|
||||
'chargeGroups.items',
|
||||
'installments',
|
||||
])->findOrFail($id);
|
||||
|
||||
// ✅ Customer details sync — जर test data आला असेल तर fix होईल
|
||||
|
||||
// ✅ Customer details sync
|
||||
if ($invoice->customer) {
|
||||
$needsUpdate = [];
|
||||
|
||||
|
||||
if (empty($invoice->customer_email) || $invoice->customer_email === 'test@demo.com') {
|
||||
$needsUpdate['customer_email'] = $invoice->customer->email;
|
||||
}
|
||||
|
||||
if (empty($invoice->customer_address) || $invoice->customer_address === 'TEST ADDRESS') {
|
||||
$needsUpdate['customer_address'] = $invoice->customer->address;
|
||||
}
|
||||
|
||||
if (empty($invoice->pincode) || $invoice->pincode === '999999') {
|
||||
$needsUpdate['pincode'] = $invoice->customer->pincode;
|
||||
}
|
||||
|
||||
|
||||
if (!empty($needsUpdate)) {
|
||||
$invoice->update($needsUpdate);
|
||||
$invoice->refresh();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$shipment = null;
|
||||
|
||||
|
||||
$groupedItemIds = $invoice->chargeGroups
|
||||
->flatMap(function ($group) {
|
||||
return $group->items->pluck('invoice_item_id');
|
||||
@@ -111,7 +110,7 @@ class AdminInvoiceController extends Controller
|
||||
->unique()
|
||||
->values()
|
||||
->toArray();
|
||||
|
||||
|
||||
return view('admin.invoice_edit', compact('invoice', 'shipment', 'groupedItemIds'));
|
||||
}
|
||||
|
||||
@@ -122,16 +121,16 @@ class AdminInvoiceController extends Controller
|
||||
{
|
||||
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',
|
||||
'due_date' => 'required|date|after_or_equal:invoice_date',
|
||||
'status' => 'required|in:pending,paying,paid,overdue',
|
||||
'notes' => 'nullable|string',
|
||||
'notes' => 'nullable|string',
|
||||
]);
|
||||
|
||||
Log::info('✅ Validated Invoice Header Update Data', $data);
|
||||
@@ -140,17 +139,17 @@ class AdminInvoiceController extends Controller
|
||||
$invoice->refresh();
|
||||
|
||||
Log::info('🔍 Invoice AFTER HEADER UPDATE', [
|
||||
'invoice_id' => $invoice->id,
|
||||
'charge_groups_total' => $invoice->charge_groups_total,
|
||||
'gst_amount' => $invoice->gst_amount,
|
||||
'invoice_id' => $invoice->id,
|
||||
'charge_groups_total' => $invoice->charge_groups_total,
|
||||
'gst_amount' => $invoice->gst_amount,
|
||||
'grand_total_with_charges'=> $invoice->grand_total_with_charges,
|
||||
]);
|
||||
|
||||
$this->generateInvoicePDF($invoice);
|
||||
|
||||
return redirect()
|
||||
->route('admin.invoices.edit', $invoice->id)
|
||||
->with('success', 'Invoice updated & PDF generated successfully.');
|
||||
->route('admin.invoices.edit', $invoice->id)
|
||||
->with('success', 'Invoice updated & PDF generated successfully.');
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
@@ -160,12 +159,12 @@ class AdminInvoiceController extends Controller
|
||||
{
|
||||
Log::info('🟡 Invoice Items Update Request', [
|
||||
'invoice_id' => $invoice->id,
|
||||
'payload' => $request->all(),
|
||||
'payload' => $request->all(),
|
||||
]);
|
||||
|
||||
$data = $request->validate([
|
||||
'items' => ['required', 'array'],
|
||||
'items.*.price' => ['required', 'numeric', 'min:0'],
|
||||
'items' => ['required', 'array'],
|
||||
'items.*.price' => ['required', 'numeric', 'min:0'],
|
||||
'items.*.ttl_amount' => ['required', 'numeric', 'min:0'],
|
||||
]);
|
||||
|
||||
@@ -177,12 +176,12 @@ class AdminInvoiceController extends Controller
|
||||
if (!$item) {
|
||||
Log::warning('Invoice item not found or mismatched invoice', [
|
||||
'invoice_id' => $invoice->id,
|
||||
'item_id' => $itemId,
|
||||
'item_id' => $itemId,
|
||||
]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$item->price = $itemData['price'];
|
||||
$item->price = $itemData['price'];
|
||||
$item->ttl_amount = $itemData['ttl_amount'];
|
||||
$item->save();
|
||||
}
|
||||
@@ -195,15 +194,16 @@ class AdminInvoiceController extends Controller
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// PDF GENERATION
|
||||
// PDF GENERATION (EXISTING - केवळ chargeGroups load ला confirm कर)
|
||||
// -------------------------------------------------------------
|
||||
public function generateInvoicePDF($invoice)
|
||||
{
|
||||
$invoice->load(['items', 'customer', 'container']);
|
||||
// ✅ यामध्ये chargeGroups आणि installments load कर
|
||||
$invoice->load(['items', 'customer', 'container', 'chargeGroups.items', 'installments']);
|
||||
$shipment = null;
|
||||
|
||||
$fileName = 'invoice-' . $invoice->invoice_number . '.pdf';
|
||||
$folder = public_path('invoices/');
|
||||
$folder = public_path('invoices/');
|
||||
|
||||
if (!file_exists($folder)) {
|
||||
mkdir($folder, 0777, true);
|
||||
@@ -216,13 +216,13 @@ class AdminInvoiceController extends Controller
|
||||
}
|
||||
|
||||
$mpdf = new Mpdf([
|
||||
'mode' => 'utf-8',
|
||||
'format' => 'A4',
|
||||
'mode' => 'utf-8',
|
||||
'format' => 'A4',
|
||||
'default_font' => 'sans-serif',
|
||||
]);
|
||||
|
||||
$html = view('admin.pdf.invoice', [
|
||||
'invoice' => $invoice,
|
||||
'invoice' => $invoice,
|
||||
'shipment' => $shipment,
|
||||
])->render();
|
||||
|
||||
@@ -239,9 +239,9 @@ class AdminInvoiceController extends Controller
|
||||
{
|
||||
$request->validate([
|
||||
'installment_date' => 'required|date',
|
||||
'payment_method' => 'required|string',
|
||||
'reference_no' => 'nullable|string',
|
||||
'amount' => 'required|numeric|min:1',
|
||||
'payment_method' => 'required|string',
|
||||
'reference_no' => 'nullable|string',
|
||||
'amount' => 'required|numeric|min:1',
|
||||
]);
|
||||
|
||||
$invoice = Invoice::findOrFail($invoice_id);
|
||||
@@ -253,51 +253,54 @@ class AdminInvoiceController extends Controller
|
||||
|
||||
if ($request->amount > $remaining) {
|
||||
return response()->json([
|
||||
'status' => 'error',
|
||||
'status' => 'error',
|
||||
'message' => 'Installment amount exceeds remaining balance.',
|
||||
], 422);
|
||||
}
|
||||
|
||||
$installment = InvoiceInstallment::create([
|
||||
'invoice_id' => $invoice_id,
|
||||
'invoice_id' => $invoice_id,
|
||||
'installment_date' => $request->installment_date,
|
||||
'payment_method' => $request->payment_method,
|
||||
'reference_no' => $request->reference_no,
|
||||
'amount' => $request->amount,
|
||||
'payment_method' => $request->payment_method,
|
||||
'reference_no' => $request->reference_no,
|
||||
'amount' => $request->amount,
|
||||
]);
|
||||
|
||||
$newPaid = $paidTotal + $request->amount;
|
||||
$newPaid = $paidTotal + $request->amount;
|
||||
$remaining = max(0, $grandTotal - $newPaid);
|
||||
|
||||
$isOverdue = now()->startOfDay()->gt(\Carbon\Carbon::parse($invoice->due_date)->startOfDay());
|
||||
|
||||
if ($grandTotal > 0 && $newPaid >= $grandTotal) {
|
||||
$newStatus = 'paid';
|
||||
} elseif ($newPaid > 0 && $isOverdue) {
|
||||
$newStatus = 'overdue';
|
||||
} elseif ($newPaid > 0 && !$isOverdue) {
|
||||
$newStatus = 'paying';
|
||||
} elseif ($newPaid <= 0 && $isOverdue) {
|
||||
$newStatus = 'overdue';
|
||||
} else {
|
||||
$newStatus = 'pending';
|
||||
}
|
||||
|
||||
// Full payment logic (जर पूर्ण भरले तर status paid करणे, नाहीतर pending राहील)
|
||||
// if ($newPaid >= $grandTotal && $grandTotal > 0) {
|
||||
// $invoice->update([
|
||||
// 'payment_method' => $request->payment_method,
|
||||
// 'reference_no' => $request->reference_no,
|
||||
// 'status' => ($newPaid >= $grandTotal && $grandTotal > 0) ? 'paid' : $invoice->status,
|
||||
// ]);
|
||||
// }
|
||||
|
||||
// Partial payment status logic:
|
||||
$invoice->update([
|
||||
'payment_method' => $request->payment_method,
|
||||
'reference_no' => $request->reference_no,
|
||||
'status' => ($newPaid >= $grandTotal && $grandTotal > 0) ? 'paid' : $invoice->status,
|
||||
'reference_no' => $request->reference_no,
|
||||
'status' => $newStatus,
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'status' => 'success',
|
||||
'message' => 'Installment added successfully.',
|
||||
'installment' => $installment,
|
||||
'status' => 'success',
|
||||
'message' => 'Installment added successfully.',
|
||||
'installment' => $installment,
|
||||
'chargeGroupsTotal' => $invoice->charge_groups_total ?? 0,
|
||||
'gstAmount' => $invoice->gst_amount ?? 0,
|
||||
'grandTotal' => $grandTotal,
|
||||
'totalPaid' => $newPaid,
|
||||
'remaining' => $remaining,
|
||||
'isCompleted' => $remaining <= 0,
|
||||
'isZero' => $newPaid == 0,
|
||||
'gstAmount' => $invoice->gst_amount ?? 0,
|
||||
'grandTotal' => $grandTotal,
|
||||
'totalPaid' => $newPaid,
|
||||
'remaining' => $remaining,
|
||||
'newStatus' => $newStatus,
|
||||
'isCompleted' => $remaining <= 0,
|
||||
'isZero' => $newPaid == 0,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -307,7 +310,7 @@ class AdminInvoiceController extends Controller
|
||||
public function deleteInstallment($id)
|
||||
{
|
||||
$installment = InvoiceInstallment::findOrFail($id);
|
||||
$invoice = $installment->invoice;
|
||||
$invoice = $installment->invoice;
|
||||
|
||||
$installment->delete();
|
||||
$invoice->refresh();
|
||||
@@ -317,21 +320,32 @@ class AdminInvoiceController extends Controller
|
||||
$paidTotal = $invoice->installments()->sum('amount');
|
||||
$remaining = max(0, $grandTotal - $paidTotal);
|
||||
|
||||
if ($paidTotal <= 0 && $grandTotal > 0) {
|
||||
$invoice->update(['status' => 'pending']);
|
||||
} elseif ($paidTotal > 0 && $paidTotal < $grandTotal) {
|
||||
$invoice->update(['status' => 'pending']);
|
||||
$isOverdue = now()->startOfDay()->gt(\Carbon\Carbon::parse($invoice->due_date)->startOfDay());
|
||||
|
||||
if ($grandTotal > 0 && $paidTotal >= $grandTotal) {
|
||||
$newStatus = 'paid';
|
||||
} elseif ($paidTotal > 0 && $isOverdue) {
|
||||
$newStatus = 'overdue';
|
||||
} elseif ($paidTotal > 0 && !$isOverdue) {
|
||||
$newStatus = 'paying';
|
||||
} elseif ($paidTotal <= 0 && $isOverdue) {
|
||||
$newStatus = 'overdue';
|
||||
} else {
|
||||
$newStatus = 'pending';
|
||||
}
|
||||
|
||||
$invoice->update(['status' => $newStatus]);
|
||||
|
||||
return response()->json([
|
||||
'status' => 'success',
|
||||
'message' => 'Installment deleted.',
|
||||
'status' => 'success',
|
||||
'message' => 'Installment deleted.',
|
||||
'chargeGroupsTotal' => $invoice->charge_groups_total ?? 0,
|
||||
'gstAmount' => $invoice->gst_amount ?? 0,
|
||||
'grandTotal' => $grandTotal,
|
||||
'totalPaid' => $paidTotal,
|
||||
'remaining' => $remaining,
|
||||
'isZero' => $paidTotal == 0,
|
||||
'gstAmount' => $invoice->gst_amount ?? 0,
|
||||
'grandTotal' => $grandTotal,
|
||||
'totalPaid' => $paidTotal,
|
||||
'remaining' => $remaining,
|
||||
'newStatus' => $newStatus,
|
||||
'isZero' => $paidTotal == 0,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -342,126 +356,149 @@ class AdminInvoiceController extends Controller
|
||||
{
|
||||
Log::info('🟡 storeChargeGroup HIT', [
|
||||
'invoice_id' => $invoiceId,
|
||||
'payload' => $request->all(),
|
||||
'payload' => $request->all(),
|
||||
]);
|
||||
|
||||
|
||||
$invoice = Invoice::with('items', 'chargeGroups')->findOrFail($invoiceId);
|
||||
|
||||
|
||||
$data = $request->validate([
|
||||
'groupname' => 'required|string|max:255',
|
||||
'basistype' => 'required|in:ttl_qty,amount,ttl_cbm,ttl_kg',
|
||||
'basisvalue' => 'required|numeric',
|
||||
'rate' => 'required|numeric|min:0.0001',
|
||||
'autototal' => 'required|numeric|min:0.01',
|
||||
'itemids' => 'required|array',
|
||||
'itemids.*' => 'integer|exists:invoice_items,id',
|
||||
|
||||
'tax_type' => 'nullable|in:none,gst,igst',
|
||||
'gst_percent' => 'nullable|numeric|min:0|max:28',
|
||||
'groupname' => 'required|string|max:255',
|
||||
'basistype' => 'required|in:ttl_qty,amount,ttl_cbm,ttl_kg',
|
||||
'basisvalue' => 'required|numeric',
|
||||
'rate' => 'required|numeric|min:0.0001',
|
||||
'autototal' => 'required|numeric|min:0.01',
|
||||
'itemids' => 'required|array',
|
||||
'itemids.*' => 'integer|exists:invoice_items,id',
|
||||
|
||||
'tax_type' => 'nullable|in:none,gst,igst',
|
||||
'gst_percent' => 'nullable|numeric|min:0|max:28',
|
||||
'total_with_gst' => 'nullable|numeric|min:0',
|
||||
]);
|
||||
|
||||
|
||||
Log::info('✅ storeChargeGroup VALIDATED', $data);
|
||||
|
||||
// duplicate name check
|
||||
|
||||
$exists = InvoiceChargeGroup::where('invoice_id', $invoice->id)
|
||||
->where('group_name', $data['groupname'])
|
||||
->exists();
|
||||
|
||||
|
||||
if ($exists) {
|
||||
return back()
|
||||
->withErrors(['groupname' => 'This group name is already used for this invoice.'])
|
||||
->withInput();
|
||||
}
|
||||
|
||||
$taxType = $data['tax_type'] ?? 'gst';
|
||||
|
||||
$taxType = $data['tax_type'] ?? 'gst';
|
||||
$gstPercent = $data['gst_percent'] ?? 0;
|
||||
$baseTotal = $data['autototal'];
|
||||
|
||||
$baseTotal = $data['autototal'];
|
||||
|
||||
$totalWithGst = $data['total_with_gst'] ?? $baseTotal;
|
||||
if ($totalWithGst == 0 && $gstPercent > 0) {
|
||||
$gstAmount = ($baseTotal * $gstPercent) / 100;
|
||||
$gstAmount = ($baseTotal * $gstPercent) / 100;
|
||||
$totalWithGst = $baseTotal + $gstAmount;
|
||||
}
|
||||
|
||||
// 1) Group create
|
||||
|
||||
$group = InvoiceChargeGroup::create([
|
||||
'invoice_id' => $invoice->id,
|
||||
'group_name' => $data['groupname'],
|
||||
'basis_type' => $data['basistype'],
|
||||
'basis_value' => $data['basisvalue'],
|
||||
'rate' => $data['rate'],
|
||||
'total_charge' => $baseTotal,
|
||||
'tax_type' => $taxType,
|
||||
'gst_percent' => $gstPercent,
|
||||
'invoice_id' => $invoice->id,
|
||||
'group_name' => $data['groupname'],
|
||||
'basis_type' => $data['basistype'],
|
||||
'basis_value' => $data['basisvalue'],
|
||||
'rate' => $data['rate'],
|
||||
'total_charge' => $baseTotal,
|
||||
'tax_type' => $taxType,
|
||||
'gst_percent' => $gstPercent,
|
||||
'total_with_gst' => $totalWithGst,
|
||||
]);
|
||||
|
||||
// 2) Items link
|
||||
|
||||
foreach ($data['itemids'] as $itemId) {
|
||||
InvoiceChargeGroupItem::create([
|
||||
'group_id' => $group->id,
|
||||
'group_id' => $group->id,
|
||||
'invoice_item_id' => $itemId,
|
||||
]);
|
||||
}
|
||||
|
||||
// 3) सर्व groups वरून invoice level totals
|
||||
|
||||
$invoice->load('chargeGroups');
|
||||
|
||||
$chargeGroupsBase = $invoice->chargeGroups->sum('total_charge'); // base
|
||||
$chargeGroupsWithG = $invoice->chargeGroups->sum('total_with_gst'); // base + gst
|
||||
$chargeGroupsGst = $chargeGroupsWithG - $chargeGroupsBase; // gst only
|
||||
|
||||
|
||||
$chargeGroupsBase = $invoice->chargeGroups->sum('total_charge');
|
||||
$chargeGroupsWithG = $invoice->chargeGroups->sum('total_with_gst');
|
||||
$chargeGroupsGst = $chargeGroupsWithG - $chargeGroupsBase;
|
||||
|
||||
$invoiceGstPercent = $group->gst_percent ?? 0;
|
||||
$invoiceTaxType = $group->tax_type ?? 'gst';
|
||||
|
||||
$invoiceTaxType = $group->tax_type ?? 'gst';
|
||||
|
||||
$cgstPercent = 0;
|
||||
$sgstPercent = 0;
|
||||
$igstPercent = 0;
|
||||
|
||||
|
||||
if ($invoiceTaxType === 'gst') {
|
||||
$cgstPercent = $invoiceGstPercent / 2;
|
||||
$sgstPercent = $invoiceGstPercent / 2;
|
||||
} elseif ($invoiceTaxType === 'igst') {
|
||||
$igstPercent = $invoiceGstPercent;
|
||||
}
|
||||
|
||||
// 🔴 इथे main fix:
|
||||
// final_amount = base (total_charge sum)
|
||||
// final_amount_with_gst = base + gst (total_with_gst sum)
|
||||
// grand_total_with_charges = final_amount_with_gst (same)
|
||||
|
||||
$invoice->update([
|
||||
'charge_groups_total' => $chargeGroupsBase,
|
||||
'gst_amount' => $chargeGroupsGst,
|
||||
'gst_percent' => $invoiceGstPercent,
|
||||
'tax_type' => $invoiceTaxType,
|
||||
'cgst_percent' => $cgstPercent,
|
||||
'sgst_percent' => $sgstPercent,
|
||||
'igst_percent' => $igstPercent,
|
||||
|
||||
'final_amount' => $chargeGroupsBase,
|
||||
'final_amount_with_gst' => $chargeGroupsWithG,
|
||||
'charge_groups_total' => $chargeGroupsBase,
|
||||
'gst_amount' => $chargeGroupsGst,
|
||||
'gst_percent' => $invoiceGstPercent,
|
||||
'tax_type' => $invoiceTaxType,
|
||||
'cgst_percent' => $cgstPercent,
|
||||
'sgst_percent' => $sgstPercent,
|
||||
'igst_percent' => $igstPercent,
|
||||
|
||||
'final_amount' => $chargeGroupsBase,
|
||||
'final_amount_with_gst' => $chargeGroupsWithG,
|
||||
'grand_total_with_charges' => $chargeGroupsWithG,
|
||||
]);
|
||||
|
||||
|
||||
Log::info('✅ Invoice updated from Charge Group (CG total + GST)', [
|
||||
'invoice_id' => $invoice->id,
|
||||
'charge_groups_total' => $chargeGroupsBase,
|
||||
'gst_amount' => $chargeGroupsGst,
|
||||
'gst_percent' => $invoiceGstPercent,
|
||||
'tax_type' => $invoiceTaxType,
|
||||
'cgst_percent' => $cgstPercent,
|
||||
'sgst_percent' => $sgstPercent,
|
||||
'igst_percent' => $igstPercent,
|
||||
'final_amount' => $invoice->final_amount,
|
||||
'final_amount_with_gst' => $invoice->final_amount_with_gst,
|
||||
'invoice_id' => $invoice->id,
|
||||
'charge_groups_total' => $chargeGroupsBase,
|
||||
'gst_amount' => $chargeGroupsGst,
|
||||
'grand_total_with_charges'=> $invoice->grand_total_with_charges,
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Charge group saved successfully.',
|
||||
'group_id' => $group->id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🆕 PDF DOWNLOAD (Direct browser download)
|
||||
// ============================================
|
||||
public function downloadPdf($id)
|
||||
{
|
||||
$invoice = Invoice::with(['items', 'customer', 'container', 'chargeGroups.items', 'installments'])
|
||||
->findOrFail($id);
|
||||
|
||||
$fileName = 'invoice-' . $invoice->invoice_number . '.pdf';
|
||||
$folder = public_path('invoices/');
|
||||
$filePath = $folder . $fileName;
|
||||
|
||||
// जर PDF exist नसेल तर generate कर
|
||||
if (!file_exists($filePath)) {
|
||||
$this->generateInvoicePDF($invoice);
|
||||
}
|
||||
|
||||
return response()->download($filePath, $fileName);
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🆕 EXCEL DOWNLOAD (CSV format - simple)
|
||||
// ============================================
|
||||
|
||||
|
||||
public function share($id)
|
||||
{
|
||||
$invoice = Invoice::findOrFail($id);
|
||||
|
||||
// इथे तुला जसं share करायचंय तसं logic टाक:
|
||||
// उदा. public link generate करून redirect कर, किंवा WhatsApp deeplink, इ.
|
||||
|
||||
$url = route('admin.invoices.popup', $invoice->id); // example: popup link share
|
||||
|
||||
return redirect()->away('https://wa.me/?text=' . urlencode($url));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,104 +5,197 @@ namespace App\Http\Controllers\Admin;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Maatwebsite\Excel\Facades\Excel;
|
||||
use Mpdf\Mpdf;
|
||||
|
||||
class AdminReportController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display the reports page with joined data
|
||||
*/
|
||||
// public function index(Request $request)
|
||||
// {
|
||||
/*********************************************************
|
||||
* OLD FLOW (Order + Shipment + Invoice)
|
||||
* फक्त reference साठी ठेवलेला, वापरत नाही.
|
||||
*********************************************************/
|
||||
|
||||
/*
|
||||
$reports = DB::table('orders')
|
||||
->join('shipment_items', 'shipment_items.order_id', '=', 'orders.id')
|
||||
->join('shipments', 'shipments.id', '=', 'shipment_items.shipment_id')
|
||||
->join('invoices', 'invoices.order_id', '=', 'orders.id')
|
||||
->leftJoin('mark_list', 'mark_list.mark_no', '=', 'orders.mark_no')
|
||||
->leftJoin('users', 'users.customer_id', '=', 'mark_list.customer_id')
|
||||
->select(...)
|
||||
->orderBy('shipments.shipment_date', 'desc')
|
||||
->get();
|
||||
*/
|
||||
|
||||
/*********************************************************
|
||||
* NEW FLOW (Container + Invoice + MarkList)
|
||||
*********************************************************/
|
||||
|
||||
// $reports = DB::table('invoices')
|
||||
// ->join('containers', 'containers.id', '=', 'invoices.containerid')
|
||||
// ->leftJoin('mark_list', 'mark_list.markno', '=', 'invoices.markno')
|
||||
// ->select(
|
||||
// 'invoices.id as invoicepk',
|
||||
// 'invoices.invoicenumber',
|
||||
// 'invoices.invoicedate',
|
||||
// 'invoices.finalamount',
|
||||
// 'invoices.finalamountwithgst',
|
||||
// 'invoices.gstpercent',
|
||||
// 'invoices.gstamount',
|
||||
// 'invoices.status as invoicestatus',
|
||||
// 'invoices.markno',
|
||||
|
||||
// 'containers.id as containerpk',
|
||||
// 'containers.containernumber',
|
||||
// 'containers.containerdate',
|
||||
// 'containers.containername',
|
||||
|
||||
// 'mark_list.companyname',
|
||||
// 'mark_list.customername'
|
||||
// )
|
||||
// ->orderBy('containers.containerdate', 'desc')
|
||||
// ->get();
|
||||
|
||||
// return view('admin.reports', compact('reports'));
|
||||
// }
|
||||
|
||||
|
||||
|
||||
public function index(Request $request)
|
||||
// UI साठी main action
|
||||
public function containerReport(Request $request)
|
||||
{
|
||||
$reports = DB::table('invoices')
|
||||
$reports = $this->buildContainerReportQuery($request)->get();
|
||||
|
||||
return view('admin.reports', compact('reports'));
|
||||
}
|
||||
|
||||
// ही common query — filters accept करते
|
||||
protected function buildContainerReportQuery(Request $request = null)
|
||||
{
|
||||
$query = DB::table('invoices')
|
||||
->join('containers', 'containers.id', '=', 'invoices.container_id')
|
||||
->leftJoin('mark_list', 'mark_list.mark_no', '=', 'invoices.mark_no')
|
||||
->leftJoinSub(
|
||||
DB::table('invoice_installments')
|
||||
->select('invoice_id', DB::raw('COALESCE(SUM(amount), 0) as total_paid'))
|
||||
->groupBy('invoice_id'),
|
||||
'inst',
|
||||
'inst.invoice_id',
|
||||
'=',
|
||||
'invoices.id'
|
||||
)
|
||||
->select(
|
||||
// INVOICE
|
||||
'invoices.id as invoicepk',
|
||||
'invoices.invoice_number',
|
||||
'invoices.invoice_date',
|
||||
'invoices.final_amount',
|
||||
'invoices.final_amount_with_gst',
|
||||
'invoices.gst_percent',
|
||||
'invoices.gst_amount',
|
||||
'invoices.status as invoicestatus',
|
||||
'invoices.mark_no',
|
||||
|
||||
// CONTAINER
|
||||
'containers.id as containerpk',
|
||||
'containers.id as container_id',
|
||||
'containers.container_number',
|
||||
'containers.container_date',
|
||||
'containers.container_name',
|
||||
|
||||
// RAW FIELDS (for reference/debug if needed)
|
||||
'invoices.company_name as inv_company_name',
|
||||
'invoices.customer_name as inv_customer_name',
|
||||
'mark_list.company_name as ml_company_name',
|
||||
'mark_list.customer_name as ml_customer_name',
|
||||
|
||||
// FINAL FIELDS (automatically pick invoice first, else mark_list)
|
||||
DB::raw('COALESCE(invoices.company_name, mark_list.company_name) as company_name'),
|
||||
DB::raw('COALESCE(invoices.customer_name, mark_list.customer_name) as customer_name')
|
||||
)
|
||||
->orderBy('invoices.invoice_date', 'desc')
|
||||
->orderBy('invoices.id', 'desc')
|
||||
|
||||
->get();
|
||||
|
||||
return view('admin.reports', compact('reports'));
|
||||
DB::raw('COUNT(DISTINCT invoices.mark_no) as total_mark_nos'),
|
||||
DB::raw('COUNT(DISTINCT invoices.customer_id) as total_customers'),
|
||||
DB::raw('COUNT(invoices.id) as total_invoices'),
|
||||
|
||||
DB::raw('COALESCE(SUM(invoices.charge_groups_total), 0) as total_invoice_amount'),
|
||||
DB::raw('COALESCE(SUM(invoices.gst_amount), 0) as total_gst_amount'),
|
||||
DB::raw('COALESCE(SUM(invoices.grand_total_with_charges), 0) as total_payable'),
|
||||
DB::raw('COALESCE(SUM(inst.total_paid), 0) as total_paid'),
|
||||
DB::raw('GREATEST(0, COALESCE(SUM(invoices.grand_total_with_charges), 0) - COALESCE(SUM(inst.total_paid), 0)) as total_remaining'),
|
||||
|
||||
DB::raw("
|
||||
CASE
|
||||
WHEN COUNT(invoices.id) > 0
|
||||
AND SUM(CASE WHEN invoices.status != 'paid' THEN 1 ELSE 0 END) = 0
|
||||
THEN 'paid'
|
||||
WHEN SUM(CASE WHEN invoices.status = 'overdue' THEN 1 ELSE 0 END) > 0
|
||||
THEN 'overdue'
|
||||
WHEN SUM(CASE WHEN invoices.status = 'paying' THEN 1 ELSE 0 END) > 0
|
||||
THEN 'paying'
|
||||
ELSE 'pending'
|
||||
END as container_status
|
||||
")
|
||||
)
|
||||
->groupBy(
|
||||
'containers.id',
|
||||
'containers.container_number',
|
||||
'containers.container_date',
|
||||
'containers.container_name'
|
||||
)
|
||||
->orderBy('containers.container_date', 'desc')
|
||||
->orderBy('containers.id', 'desc');
|
||||
|
||||
// ── Filters ──────────────────────────────────────────────────────
|
||||
if ($request) {
|
||||
if ($request->filled('from_date')) {
|
||||
$query->whereDate('containers.container_date', '>=', $request->from_date);
|
||||
}
|
||||
|
||||
if ($request->filled('to_date')) {
|
||||
$query->whereDate('containers.container_date', '<=', $request->to_date);
|
||||
}
|
||||
|
||||
if ($request->filled('status')) {
|
||||
// container_status हे aggregate expression आहे,
|
||||
// त्यामुळे HAVING clause वापरतो
|
||||
$query->havingRaw("
|
||||
CASE
|
||||
WHEN COUNT(invoices.id) > 0
|
||||
AND SUM(CASE WHEN invoices.status != 'paid' THEN 1 ELSE 0 END) = 0
|
||||
THEN 'paid'
|
||||
WHEN SUM(CASE WHEN invoices.status = 'overdue' THEN 1 ELSE 0 END) > 0
|
||||
THEN 'overdue'
|
||||
WHEN SUM(CASE WHEN invoices.status = 'paying' THEN 1 ELSE 0 END) > 0
|
||||
THEN 'paying'
|
||||
ELSE 'pending'
|
||||
END = ?
|
||||
", [$request->status]);
|
||||
}
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ---- Excel export ----
|
||||
public function containerReportExcel(Request $request)
|
||||
{
|
||||
$reports = $this->buildContainerReportQuery($request)->get();
|
||||
|
||||
$headings = [
|
||||
'Container No',
|
||||
'Container Date',
|
||||
'Total Mark Nos',
|
||||
'Total Customers',
|
||||
'Total Invoices',
|
||||
'Invoice Amount (Before GST)',
|
||||
'GST Amount',
|
||||
'Payable Amount (Incl. GST)',
|
||||
'Paid Amount',
|
||||
'Remaining Amount',
|
||||
'Container Status',
|
||||
];
|
||||
|
||||
$rows = $reports->map(function ($r) {
|
||||
return [
|
||||
$r->container_number,
|
||||
$r->container_date,
|
||||
$r->total_mark_nos,
|
||||
$r->total_customers,
|
||||
$r->total_invoices,
|
||||
$r->total_invoice_amount,
|
||||
$r->total_gst_amount,
|
||||
$r->total_payable,
|
||||
$r->total_paid,
|
||||
$r->total_remaining,
|
||||
$r->container_status,
|
||||
];
|
||||
})->toArray();
|
||||
|
||||
$export = new class($headings, $rows) implements
|
||||
\Maatwebsite\Excel\Concerns\FromArray,
|
||||
\Maatwebsite\Excel\Concerns\WithHeadings
|
||||
{
|
||||
private $headings;
|
||||
private $rows;
|
||||
|
||||
public function __construct($headings, $rows)
|
||||
{
|
||||
$this->headings = $headings;
|
||||
$this->rows = $rows;
|
||||
}
|
||||
|
||||
public function array(): array
|
||||
{
|
||||
return $this->rows;
|
||||
}
|
||||
|
||||
public function headings(): array
|
||||
{
|
||||
return $this->headings;
|
||||
}
|
||||
};
|
||||
|
||||
return Excel::download(
|
||||
$export,
|
||||
'container-report-' . now()->format('Ymd-His') . '.xlsx'
|
||||
);
|
||||
}
|
||||
|
||||
// ---- PDF export ----
|
||||
public function containerReportPdf(Request $request)
|
||||
{
|
||||
$reports = $this->buildContainerReportQuery($request)->get();
|
||||
|
||||
$html = view('admin.reports', [
|
||||
'reports' => $reports,
|
||||
'isPdf' => true,
|
||||
])->render();
|
||||
|
||||
$mpdf = new \Mpdf\Mpdf([
|
||||
'mode' => 'utf-8',
|
||||
'format' => 'A4-L',
|
||||
'default_font' => 'dejavusans',
|
||||
'margin_top' => 8,
|
||||
'margin_right' => 8,
|
||||
'margin_bottom' => 10,
|
||||
'margin_left' => 8,
|
||||
]);
|
||||
|
||||
$mpdf->SetHTMLHeader('');
|
||||
$mpdf->SetHTMLFooter('');
|
||||
|
||||
$mpdf->WriteHTML($html);
|
||||
|
||||
$fileName = 'container-report-' . now()->format('Ymd-His') . '.pdf';
|
||||
|
||||
return response($mpdf->Output($fileName, 'S'), 200, [
|
||||
'Content-Type' => 'application/pdf',
|
||||
'Content-Disposition' => 'attachment; filename="' . $fileName . '"',
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -565,8 +565,6 @@ class ContainerController extends Controller
|
||||
$invoice->save();
|
||||
$invoiceCount++;
|
||||
|
||||
$totalAmount = 0;
|
||||
|
||||
foreach ($rowsForCustomer as $item) {
|
||||
$row = $item['row'];
|
||||
$offset = $item['offset'];
|
||||
@@ -606,16 +604,7 @@ class ContainerController extends Controller
|
||||
'shop_no' => $shopNo,
|
||||
'mark_no' => $mark, // ✅ save mark_no from Excel
|
||||
]);
|
||||
|
||||
$totalAmount += $ttlAmount;
|
||||
}
|
||||
|
||||
$invoice->final_amount = $totalAmount;
|
||||
$invoice->gst_percent = 0;
|
||||
$invoice->gst_amount = 0;
|
||||
$invoice->final_amount_with_gst = $totalAmount;
|
||||
|
||||
$invoice->save();
|
||||
}
|
||||
|
||||
$msg = "Container '{$container->container_number}' created with {$savedCount} rows and {$invoiceCount} customer invoice(s).";
|
||||
@@ -972,4 +961,4 @@ class ContainerController extends Controller
|
||||
'summary' => $summary,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user