filled('search')) { $search = $request->search; $query->where(function ($q) use ($search) { $q->where('invoice_number', 'like', "%{$search}%") ->orWhere('customer_name', 'like', "%{$search}%"); }); } if ($request->filled('status') && $request->status !== 'all') { $query->where('status', $request->status); } if ($request->filled('start_date')) { $query->whereDate('invoice_date', '>=', $request->start_date); } if ($request->filled('end_date')) { $query->whereDate('invoice_date', '<=', $request->end_date); } $invoices = $query->latest()->get(); return view('admin.invoice', compact('invoices')); } // ------------------------------------------------------------- // POPUP VIEW // ------------------------------------------------------------- public function popup($id) { $invoice = Invoice::with([ 'items', 'chargeGroups.items', ])->findOrFail($id); $shipment = null; $groupedItemIds = $invoice->chargeGroups ->flatMap(fn($group) => $group->items->pluck('invoice_item_id')) ->unique() ->values() ->toArray(); return view('admin.popup_invoice', compact('invoice', 'shipment', 'groupedItemIds')); } // ------------------------------------------------------------- // EDIT INVOICE PAGE // ------------------------------------------------------------- public function edit($id) { $invoice = Invoice::with([ 'items', 'customer', 'container', 'chargeGroups.items', 'installments', ])->findOrFail($id); // ✅ 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'); }) ->unique() ->values() ->toArray(); return view('admin.invoice_edit', compact('invoice', 'shipment', 'groupedItemIds')); } // ------------------------------------------------------------- // UPDATE INVOICE (HEADER ONLY) // ------------------------------------------------------------- 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', 'status' => 'required|in:pending,paying,paid,overdue', 'notes' => 'nullable|string', ]); Log::info('✅ Validated Invoice Header Update Data', $data); $invoice->update($data); $invoice->refresh(); Log::info('🔍 Invoice AFTER HEADER UPDATE', [ '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.'); } // ------------------------------------------------------------- // UPDATE INVOICE ITEMS (फक्त items save) // ------------------------------------------------------------- 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'], ]); foreach ($data['items'] 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(); } Log::info('✅ Invoice items updated (no totals recalculation)', [ 'invoice_id' => $invoice->id, ]); return back()->with('success', 'Invoice items updated successfully.'); } // ------------------------------------------------------------- // PDF GENERATION (EXISTING - केवळ chargeGroups load ला confirm कर) // ------------------------------------------------------------- public function generateInvoicePDF($invoice) { // ✅ यामध्ये chargeGroups आणि installments load कर $invoice->load(['items', 'customer', 'container', 'chargeGroups.items', 'installments']); $shipment = null; $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) // ------------------------------------------------------------- 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); $grandTotal = $invoice->grand_total_with_charges ?? 0; $paidTotal = $invoice->installments()->sum('amount'); $remaining = $grandTotal - $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; $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'; } $invoice->update([ 'payment_method' => $request->payment_method, 'reference_no' => $request->reference_no, 'status' => $newStatus, ]); return response()->json([ '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, 'newStatus' => $newStatus, 'isCompleted' => $remaining <= 0, 'isZero' => $newPaid == 0, ]); } // ------------------------------------------------------------- // INSTALLMENTS (DELETE) // ------------------------------------------------------------- public function deleteInstallment($id) { $installment = InvoiceInstallment::findOrFail($id); $invoice = $installment->invoice; $installment->delete(); $invoice->refresh(); $grandTotal = $invoice->grand_total_with_charges ?? 0; $paidTotal = $invoice->installments()->sum('amount'); $remaining = max(0, $grandTotal - $paidTotal); $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.', 'chargeGroupsTotal' => $invoice->charge_groups_total ?? 0, 'gstAmount' => $invoice->gst_amount ?? 0, 'grandTotal' => $grandTotal, 'totalPaid' => $paidTotal, 'remaining' => $remaining, 'newStatus' => $newStatus, 'isZero' => $paidTotal == 0, ]); } // ------------------------------------------------------------- // CHARGE GROUP SAVE // ------------------------------------------------------------- public function storeChargeGroup(Request $request, $invoiceId) { Log::info('🟡 storeChargeGroup HIT', [ 'invoice_id' => $invoiceId, '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', 'total_with_gst' => 'nullable|numeric|min:0', ]); Log::info('✅ storeChargeGroup VALIDATED', $data); $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'; $gstPercent = $data['gst_percent'] ?? 0; $baseTotal = $data['autototal']; $totalWithGst = $data['total_with_gst'] ?? $baseTotal; if ($totalWithGst == 0 && $gstPercent > 0) { $gstAmount = ($baseTotal * $gstPercent) / 100; $totalWithGst = $baseTotal + $gstAmount; } $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, 'total_with_gst' => $totalWithGst, ]); foreach ($data['itemids'] as $itemId) { InvoiceChargeGroupItem::create([ 'group_id' => $group->id, 'invoice_item_id' => $itemId, ]); } $invoice->load('chargeGroups'); $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'; $cgstPercent = 0; $sgstPercent = 0; $igstPercent = 0; if ($invoiceTaxType === 'gst') { $cgstPercent = $invoiceGstPercent / 2; $sgstPercent = $invoiceGstPercent / 2; } elseif ($invoiceTaxType === 'igst') { $igstPercent = $invoiceGstPercent; } $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, '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, 'grand_total_with_charges'=> $invoice->grand_total_with_charges, ]); 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)); } }