Gst Updates
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
APP_NAME=Laravel
|
||||
APP_ENV=local
|
||||
APP_KEY=
|
||||
APP_KEY=base64:/ulhRgiCOFjZV6xUDkXLfiR9X8iFRZ4QIiX3UJbdwY4=
|
||||
APP_DEBUG=true
|
||||
APP_URL=http://localhost
|
||||
|
||||
|
||||
@@ -8,10 +8,8 @@ use App\Models\InvoiceItem;
|
||||
use App\Models\InvoiceInstallment;
|
||||
use App\Models\InvoiceChargeGroup;
|
||||
use App\Models\InvoiceChargeGroupItem;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Mpdf\Mpdf;
|
||||
|
||||
class AdminInvoiceController extends Controller
|
||||
@@ -21,9 +19,8 @@ class AdminInvoiceController extends Controller
|
||||
// -------------------------------------------------------------
|
||||
public function index(Request $request)
|
||||
{
|
||||
// Container relation सह invoices load करतो
|
||||
$query = Invoice::with(['items', 'customer', 'container']);
|
||||
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$search = $request->search;
|
||||
$query->where(function ($q) use ($search) {
|
||||
@@ -31,26 +28,26 @@ class AdminInvoiceController extends Controller
|
||||
->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 + CUSTOMER DATA SYNC
|
||||
// POPUP VIEW
|
||||
// -------------------------------------------------------------
|
||||
public function popup($id)
|
||||
{
|
||||
@@ -59,7 +56,7 @@ class AdminInvoiceController extends Controller
|
||||
'chargeGroups.items',
|
||||
])->findOrFail($id);
|
||||
|
||||
// demo update असेल तर ठेव/काढ
|
||||
// demo update असेल तर
|
||||
$invoice->update([
|
||||
'customer_email' => 'test@demo.com',
|
||||
'customer_address' => 'TEST ADDRESS',
|
||||
@@ -68,7 +65,6 @@ class AdminInvoiceController extends Controller
|
||||
|
||||
$shipment = null;
|
||||
|
||||
// आधीच group मध्ये असलेले item ids
|
||||
$groupedItemIds = $invoice->chargeGroups
|
||||
->flatMap(fn($group) => $group->items->pluck('invoice_item_id'))
|
||||
->unique()
|
||||
@@ -88,6 +84,7 @@ class AdminInvoiceController extends Controller
|
||||
'customer',
|
||||
'container',
|
||||
'chargeGroups.items',
|
||||
'installments',
|
||||
])->findOrFail($id);
|
||||
|
||||
$shipment = null;
|
||||
@@ -104,7 +101,7 @@ class AdminInvoiceController extends Controller
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// UPDATE INVOICE (HEADER LEVEL)
|
||||
// UPDATE INVOICE (HEADER ONLY)
|
||||
// -------------------------------------------------------------
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
@@ -118,65 +115,20 @@ class AdminInvoiceController extends Controller
|
||||
$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 = (float) $data['final_amount'];
|
||||
$taxPercent = (float) $data['tax_percent'];
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
$gstAmount = ($finalAmount * $taxPercent) / 100;
|
||||
$data['gst_amount'] = $gstAmount;
|
||||
$data['final_amount_with_gst'] = $finalAmount + $gstAmount;
|
||||
$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('✅ Validated Invoice Header Update Data', $data);
|
||||
|
||||
$invoice->update($data);
|
||||
|
||||
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,
|
||||
|
||||
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);
|
||||
@@ -187,7 +139,7 @@ class AdminInvoiceController extends Controller
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// UPDATE INVOICE ITEMS
|
||||
// UPDATE INVOICE ITEMS (फक्त items save)
|
||||
// -------------------------------------------------------------
|
||||
public function updateItems(Request $request, Invoice $invoice)
|
||||
{
|
||||
@@ -202,9 +154,7 @@ class AdminInvoiceController extends Controller
|
||||
'items.*.ttl_amount' => ['required', 'numeric', 'min:0'],
|
||||
]);
|
||||
|
||||
$itemsInput = $data['items'];
|
||||
|
||||
foreach ($itemsInput as $itemId => $itemData) {
|
||||
foreach ($data['items'] as $itemId => $itemData) {
|
||||
$item = InvoiceItem::where('id', $itemId)
|
||||
->where('invoice_id', $invoice->id)
|
||||
->first();
|
||||
@@ -222,47 +172,8 @@ class AdminInvoiceController extends Controller
|
||||
$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();
|
||||
|
||||
// ⭐ Total Charges (groups समावेत) पुन्हा कॅलक्युलेट
|
||||
$chargeGroupsTotal = $invoice->chargeGroups()->sum('total_charge');
|
||||
$invoice->charge_groups_total = $chargeGroupsTotal;
|
||||
$invoice->grand_total_with_charges = $invoice->final_amount_with_gst + $chargeGroupsTotal;
|
||||
$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,
|
||||
'charge_groups_total' => $invoice->charge_groups_total,
|
||||
'grand_total_with_charges' => $invoice->grand_total_with_charges,
|
||||
Log::info('✅ Invoice items updated (no totals recalculation)', [
|
||||
'invoice_id' => $invoice->id,
|
||||
]);
|
||||
|
||||
return back()->with('success', 'Invoice items updated successfully.');
|
||||
@@ -318,12 +229,12 @@ class AdminInvoiceController extends Controller
|
||||
'amount' => 'required|numeric|min:1',
|
||||
]);
|
||||
|
||||
$invoice = Invoice::findOrFail($invoice_id);
|
||||
$invoice = Invoice::findOrFail($invoice_id);
|
||||
|
||||
$grandTotal = $invoice->grand_total_with_charges ?? 0;
|
||||
|
||||
$paidTotal = $invoice->installments()->sum('amount');
|
||||
|
||||
// 👇 Total Charges (grand_total_with_charges) वरून remaining
|
||||
$grandTotal = $invoice->grand_total_with_charges;
|
||||
$remaining = $grandTotal - $paidTotal;
|
||||
$remaining = $grandTotal - $paidTotal;
|
||||
|
||||
if ($request->amount > $remaining) {
|
||||
return response()->json([
|
||||
@@ -340,24 +251,25 @@ class AdminInvoiceController extends Controller
|
||||
'amount' => $request->amount,
|
||||
]);
|
||||
|
||||
$newPaid = $paidTotal + $request->amount;
|
||||
$newPaid = $paidTotal + $request->amount;
|
||||
$remaining = max(0, $grandTotal - $newPaid);
|
||||
|
||||
if ($newPaid >= $invoice->final_amount_with_gst) {
|
||||
if ($newPaid >= $grandTotal && $grandTotal > 0) {
|
||||
$invoice->update(['status' => 'paid']);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'status' => 'success',
|
||||
'message' => 'Installment added successfully.',
|
||||
'installment' => $installment,
|
||||
'totalPaid' => $newPaid,
|
||||
'gstAmount' => $invoice->gst_amount,
|
||||
'finalAmountWithGst' => $grandTotal, // इथे grand total पाठव
|
||||
'baseAmount' => $invoice->final_amount,
|
||||
'remaining' => max(0, $grandTotal - $newPaid),
|
||||
'isCompleted' => $newPaid >= $grandTotal,
|
||||
'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,
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
@@ -369,87 +281,156 @@ class AdminInvoiceController extends Controller
|
||||
$invoice = $installment->invoice;
|
||||
|
||||
$installment->delete();
|
||||
$invoice->refresh();
|
||||
|
||||
$grandTotal = $invoice->grand_total_with_charges ?? 0;
|
||||
|
||||
$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']);
|
||||
}
|
||||
|
||||
$paidTotal = $invoice->installments()->sum('amount');
|
||||
$grandTotal = $invoice->grand_total_with_charges;
|
||||
$remaining = $grandTotal - $paidTotal;
|
||||
|
||||
return response()->json([
|
||||
'status' => 'success',
|
||||
'message' => 'Installment deleted.',
|
||||
'totalPaid' => $paidTotal,
|
||||
'gstAmount' => $invoice->gst_amount,
|
||||
'finalAmountWithGst' => $grandTotal, // इथेही
|
||||
'baseAmount' => $invoice->final_amount,
|
||||
'remaining' => $remaining,
|
||||
'isZero' => $paidTotal == 0,
|
||||
'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,
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// CHARGE GROUP SAVE (no AJAX branch)
|
||||
// CHARGE GROUP SAVE
|
||||
// -------------------------------------------------------------
|
||||
public function storeChargeGroup(Request $request, $invoiceId)
|
||||
{
|
||||
$invoice = Invoice::with('items')->findOrFail($invoiceId);
|
||||
|
||||
$data = $request->validate([
|
||||
'group_name' => 'required|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',
|
||||
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);
|
||||
|
||||
// duplicate name check
|
||||
$exists = InvoiceChargeGroup::where('invoice_id', $invoice->id)
|
||||
->where('group_name', $data['group_name'])
|
||||
->where('group_name', $data['groupname'])
|
||||
->exists();
|
||||
|
||||
|
||||
if ($exists) {
|
||||
return back()
|
||||
->withErrors(['group_name' => 'This group name is already used for this invoice.'])
|
||||
->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;
|
||||
}
|
||||
|
||||
// 1) Group create
|
||||
$group = InvoiceChargeGroup::create([
|
||||
'invoice_id' => $invoice->id,
|
||||
'group_name' => $data['group_name'],
|
||||
'basis_type' => $data['basis_type'],
|
||||
'basis_value' => $data['basis_value'],
|
||||
'rate' => $data['rate'],
|
||||
'total_charge' => $data['auto_total'],
|
||||
'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['item_ids'] as $itemId) {
|
||||
|
||||
// 2) Items link
|
||||
foreach ($data['itemids'] as $itemId) {
|
||||
InvoiceChargeGroupItem::create([
|
||||
'group_id' => $group->id,
|
||||
'invoice_item_id' => $itemId,
|
||||
]);
|
||||
}
|
||||
|
||||
// ⭐ Charge groups नुसार Total Charges सेट करा
|
||||
$chargeGroupsTotal = $invoice->chargeGroups()->sum('total_charge');
|
||||
$grandTotal = $invoice->final_amount_with_gst + $chargeGroupsTotal;
|
||||
|
||||
|
||||
// 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
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
// 🔴 इथे 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' => $chargeGroupsTotal,
|
||||
'grand_total_with_charges' => $grandTotal,
|
||||
'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,
|
||||
'grand_total_with_charges'=> $invoice->grand_total_with_charges,
|
||||
]);
|
||||
|
||||
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');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,13 @@ class Invoice extends Model
|
||||
'pincode',
|
||||
'pdf_path',
|
||||
'notes',
|
||||
// totals from charge groups
|
||||
'charge_groups_total',
|
||||
'grand_total_with_charges',
|
||||
'tax_type',
|
||||
'cgst_percent',
|
||||
'sgst_percent',
|
||||
'igst_percent',
|
||||
];
|
||||
|
||||
/****************************
|
||||
@@ -57,16 +64,16 @@ class Invoice extends Model
|
||||
return $this->hasMany(InvoiceInstallment::class);
|
||||
}
|
||||
|
||||
// ✅ SINGLE, correct relation
|
||||
public function chargeGroups()
|
||||
{
|
||||
return $this->hasMany(\App\Models\InvoiceChargeGroup::class, 'invoice_id');
|
||||
return $this->hasMany(InvoiceChargeGroup::class, 'invoice_id');
|
||||
}
|
||||
|
||||
/****************************
|
||||
* Helper Functions
|
||||
****************************/
|
||||
|
||||
// (Items based calculateTotals वापरणार नाहीस तरी ठेवू शकतोस)
|
||||
public function calculateTotals()
|
||||
{
|
||||
$gst = ($this->final_amount * $this->gst_percent) / 100;
|
||||
@@ -84,29 +91,32 @@ class Invoice extends Model
|
||||
return null;
|
||||
}
|
||||
|
||||
// ✅ Charge groups total accessor
|
||||
// ✅ Charge groups base total (WITHOUT GST)
|
||||
public function getChargeGroupsTotalAttribute()
|
||||
{
|
||||
// relation already loaded असेल तर collection वरून sum होईल
|
||||
// base = total_charge sum
|
||||
return (float) $this->chargeGroups->sum('total_charge');
|
||||
}
|
||||
|
||||
// ✅ Grand total accessor (items + GST + charge groups)
|
||||
// ✅ Grand total: Charge groups base + GST (items ignore)
|
||||
public function getGrandTotalWithChargesAttribute()
|
||||
{
|
||||
return (float) ($this->final_amount_with_gst ?? 0) + $this->charge_groups_total;
|
||||
$base = (float) ($this->charge_groups_total ?? 0);
|
||||
$gst = (float) ($this->gst_amount ?? 0);
|
||||
|
||||
return $base + $gst;
|
||||
}
|
||||
|
||||
public function totalPaid()
|
||||
{
|
||||
return $this->installments->sum('amount');
|
||||
}
|
||||
|
||||
|
||||
public function remainingAmount()
|
||||
{
|
||||
return $this->grand_total_with_charges - $this->totalPaid();
|
||||
}
|
||||
|
||||
|
||||
public function totalPaid(): float
|
||||
{
|
||||
return (float) $this->installments()->sum('amount');
|
||||
}
|
||||
|
||||
public function remainingAmount(): float
|
||||
{
|
||||
$grand = (float) $this->grand_total_with_charges;
|
||||
$paid = (float) $this->totalPaid();
|
||||
|
||||
return max(0, $grand - $paid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,10 @@ class InvoiceChargeGroup extends Model
|
||||
'basis_value',
|
||||
'rate',
|
||||
'total_charge',
|
||||
|
||||
'tax_type',
|
||||
'gst_percent',
|
||||
'total_with_gst',
|
||||
];
|
||||
|
||||
public function invoice()
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('invoice_charge_groups', function (Blueprint $table) {
|
||||
$table->string('tax_type')->nullable()->after('total_charge');
|
||||
$table->decimal('gst_percent', 5, 2)->default(0)->after('tax_type');
|
||||
$table->decimal('total_with_gst', 15, 2)->default(0)->after('gst_percent');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('invoice_charge_groups', function (Blueprint $table) {
|
||||
$table->dropColumn(['tax_type', 'gst_percent', 'total_with_gst']);
|
||||
});
|
||||
}
|
||||
};
|
||||
BIN
public/invoices/invoice-INV-2026-000183.pdf
Normal file
BIN
public/invoices/invoice-INV-2026-000183.pdf
Normal file
Binary file not shown.
BIN
public/invoices/invoice-INV-2026-000184.pdf
Normal file
BIN
public/invoices/invoice-INV-2026-000184.pdf
Normal file
Binary file not shown.
BIN
public/invoices/invoice-INV-2026-000185.pdf
Normal file
BIN
public/invoices/invoice-INV-2026-000185.pdf
Normal file
Binary file not shown.
@@ -1,9 +1,7 @@
|
||||
@extends('admin.layouts.app')
|
||||
|
||||
|
||||
@section('page-title', 'Edit Invoice')
|
||||
|
||||
|
||||
@section('content')
|
||||
<style>
|
||||
:root {
|
||||
@@ -314,7 +312,7 @@
|
||||
</style>
|
||||
|
||||
<div class="container-fluid py-3">
|
||||
{{-- Invoice Preview / Overview --}}
|
||||
{{-- Invoice Overview --}}
|
||||
<div class="glass-card">
|
||||
<div class="card-header-compact">
|
||||
<h4>
|
||||
@@ -323,12 +321,11 @@
|
||||
</h4>
|
||||
</div>
|
||||
<div class="card-body-compact">
|
||||
{{-- Read-only popup: items price/total cannot be edited here --}}
|
||||
@include('admin.popup_invoice', ['invoice' => $invoice, 'shipment' => $shipment])
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Edit Invoice Header Details (POST) --}}
|
||||
|
||||
{{-- Edit Invoice Header --}}
|
||||
<div class="glass-card">
|
||||
<div class="card-header-compact">
|
||||
<h4>
|
||||
@@ -365,61 +362,16 @@
|
||||
required>
|
||||
</div>
|
||||
|
||||
{{-- Final Amount (Base) --}}
|
||||
{{-- Final Amount (With GST) – Charge Groups Total --}}
|
||||
<div class="form-group-compact">
|
||||
<label class="form-label-compact">
|
||||
<i class="fas fa-money-bill-wave"></i> Final Amount (Before GST)
|
||||
<i class="fas fa-money-bill-wave"></i> Final Amount (With GST)
|
||||
</label>
|
||||
<input type="number"
|
||||
step="0.01"
|
||||
name="final_amount"
|
||||
class="form-control-compact"
|
||||
value="{{ old('final_amount', $invoice->final_amount) }}"
|
||||
required>
|
||||
</div>
|
||||
|
||||
{{-- Tax Type --}}
|
||||
<div class="form-group-compact">
|
||||
<label class="form-label-compact">
|
||||
<i class="fas fa-receipt"></i> Tax Type
|
||||
</label>
|
||||
<div class="d-flex gap-3 mt-1">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input"
|
||||
type="radio"
|
||||
name="tax_type"
|
||||
value="gst"
|
||||
{{ old('tax_type', $invoice->tax_type) === 'gst' ? 'checked' : '' }}>
|
||||
<label class="form-check-label fw-semibold">GST (CGST + SGST)</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input"
|
||||
type="radio"
|
||||
name="tax_type"
|
||||
value="igst"
|
||||
{{ old('tax_type', $invoice->tax_type) === 'igst' ? 'checked' : '' }}>
|
||||
<label class="form-check-label fw-semibold">IGST</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Tax Percentage --}}
|
||||
<div class="form-group-compact">
|
||||
<label class="form-label-compact">
|
||||
<i class="fas fa-percentage"></i> Tax Percentage
|
||||
</label>
|
||||
<input type="number"
|
||||
step="0.01"
|
||||
min="0"
|
||||
max="28"
|
||||
name="tax_percent"
|
||||
class="form-control-compact"
|
||||
value="{{ old('tax_percent',
|
||||
$invoice->tax_type === 'gst'
|
||||
? ($invoice->cgst_percent + $invoice->sgst_percent)
|
||||
: $invoice->igst_percent
|
||||
) }}"
|
||||
required>
|
||||
value="{{ $invoice->grand_total_with_charges }}"
|
||||
readonly>
|
||||
</div>
|
||||
|
||||
{{-- Status --}}
|
||||
@@ -428,15 +380,9 @@
|
||||
<i class="fas fa-tasks"></i> Status
|
||||
</label>
|
||||
<select name="status" class="form-select-compact" required>
|
||||
<option value="pending" {{ old('status', $invoice->status) === 'pending' ? 'selected' : '' }}>
|
||||
Pending
|
||||
</option>
|
||||
<option value="paid" {{ old('status', $invoice->status) === 'paid' ? 'selected' : '' }}>
|
||||
Paid
|
||||
</option>
|
||||
<option value="overdue" {{ old('status', $invoice->status) === 'overdue' ? 'selected' : '' }}>
|
||||
Overdue
|
||||
</option>
|
||||
<option value="pending" {{ old('status', $invoice->status) === 'pending' ? 'selected' : '' }}>Pending</option>
|
||||
<option value="paid" {{ old('status', $invoice->status) === 'paid' ? 'selected' : '' }}>Paid</option>
|
||||
<option value="overdue" {{ old('status', $invoice->status) === 'overdue' ? 'selected' : '' }}>Overdue</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@@ -462,43 +408,46 @@
|
||||
</div>
|
||||
|
||||
@php
|
||||
// आता helpers वापरू: totalPaid() आणि remainingAmount()
|
||||
$totalPaid = $invoice->totalPaid();
|
||||
$remaining = $invoice->remainingAmount();
|
||||
$totalPaid = $invoice->totalPaid();
|
||||
$remaining = $invoice->remainingAmount();
|
||||
|
||||
// Mixed tax type label from charge groups
|
||||
$taxTypes = $invoice->chargeGroups
|
||||
? $invoice->chargeGroups->pluck('tax_type')->filter()->unique()->values()
|
||||
: collect([]);
|
||||
|
||||
$taxTypeLabel = 'None';
|
||||
|
||||
if ($taxTypes->count() === 1) {
|
||||
if ($taxTypes[0] === 'gst') {
|
||||
$taxTypeLabel = 'GST (CGST + SGST)';
|
||||
} elseif ($taxTypes[0] === 'igst') {
|
||||
$taxTypeLabel = 'IGST';
|
||||
} else {
|
||||
$taxTypeLabel = strtoupper($taxTypes[0]);
|
||||
}
|
||||
} elseif ($taxTypes->count() > 1) {
|
||||
$parts = [];
|
||||
if ($taxTypes->contains('gst')) {
|
||||
$parts[] = 'GST (CGST + SGST)';
|
||||
}
|
||||
if ($taxTypes->contains('igst')) {
|
||||
$parts[] = 'IGST';
|
||||
}
|
||||
$taxTypeLabel = implode(' + ', $parts);
|
||||
}
|
||||
@endphp
|
||||
|
||||
{{-- Amount Breakdown (items + GST + groups) --}}
|
||||
{{-- Amount Breakdown --}}
|
||||
<div class="amount-breakdown-compact">
|
||||
<h6 class="fw-bold mb-3 text-dark">
|
||||
<i class="fas fa-calculator me-2"></i>Amount Breakdown
|
||||
</h6>
|
||||
|
||||
<div class="breakdown-row">
|
||||
<span class="breakdown-label">Total Amount Before Tax</span>
|
||||
<span class="breakdown-value" id="baseAmount">
|
||||
₹{{ number_format($invoice->final_amount, 2) }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="breakdown-row">
|
||||
<span class="breakdown-label">Tax Type</span>
|
||||
<span class="breakdown-value text-primary">
|
||||
@if($invoice->tax_type === 'gst')
|
||||
GST (CGST + SGST)
|
||||
@else
|
||||
IGST
|
||||
@endif
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="breakdown-row">
|
||||
<span class="breakdown-label">Tax Percentage</span>
|
||||
<span class="breakdown-value text-primary">
|
||||
@if($invoice->tax_type === 'gst')
|
||||
{{ $invoice->cgst_percent + $invoice->sgst_percent }}%
|
||||
@else
|
||||
{{ $invoice->igst_percent }}%
|
||||
@endif
|
||||
{{ $taxTypeLabel }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -509,15 +458,8 @@
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="breakdown-row">
|
||||
<span class="breakdown-label">Charge Groups Total</span>
|
||||
<span class="breakdown-value text-info" id="chargeGroupsTotal">
|
||||
₹{{ number_format($invoice->charge_groups_total, 2) }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="breakdown-row" style="border-top: 2px solid #e2e8f0; padding-top: 0.75rem;">
|
||||
<span class="breakdown-label fw-bold">Grand Total (Items + GST + Groups)</span>
|
||||
<span class="breakdown-label fw-bold">Grand Total (Charges + GST)</span>
|
||||
<span class="breakdown-value fw-bold text-dark" id="totalInvoiceWithGst">
|
||||
₹{{ number_format($invoice->grand_total_with_charges, 2) }}
|
||||
</span>
|
||||
@@ -538,13 +480,13 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Installment Summary (top cards) --}}
|
||||
{{-- Summary cards --}}
|
||||
<div class="summary-grid-compact">
|
||||
<div class="summary-card-compact total">
|
||||
<div class="summary-value-compact text-success" id="totalInvoiceWithGstCard">
|
||||
₹{{ number_format($invoice->grand_total_with_charges, 2) }}
|
||||
</div>
|
||||
<div class="summary-label-compact">Grand Total (Items + GST + Groups)</div>
|
||||
<div class="summary-label-compact">Grand Total (Charges + GST)</div>
|
||||
</div>
|
||||
<div class="summary-card-compact paid">
|
||||
<div class="summary-value-compact text-primary">
|
||||
@@ -789,15 +731,19 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
document.getElementById("remainingAmount").textContent = "₹" + formatINR(data.remaining);
|
||||
}
|
||||
if (document.getElementById("baseAmount")) {
|
||||
document.getElementById("baseAmount").textContent = "₹" + formatINR(data.baseAmount);
|
||||
document.getElementById("baseAmount").textContent = "₹" + formatINR(data.chargeGroupsTotal);
|
||||
}
|
||||
if (document.getElementById("gstAmount")) {
|
||||
document.getElementById("gstAmount").textContent = "₹" + formatINR(data.gstAmount);
|
||||
}
|
||||
// grand total आता finalAmountWithGst नाही, पण API अजून तेच key देत आहे,
|
||||
// त्यामुळे इथे फक्त card आणि breakdown value update करतो:
|
||||
|
||||
if (document.getElementById("totalInvoiceWithGst")) {
|
||||
document.getElementById("totalInvoiceWithGst").textContent = "₹" + formatINR(data.finalAmountWithGst);
|
||||
document.getElementById("totalInvoiceWithGst").textContent =
|
||||
"₹" + formatINR(data.grandTotal);
|
||||
}
|
||||
const totalCard = document.getElementById("totalInvoiceWithGstCard");
|
||||
if (totalCard) {
|
||||
totalCard.textContent = "₹" + formatINR(data.grandTotal);
|
||||
}
|
||||
|
||||
const paidCard = document.querySelector(".summary-card-compact.paid .summary-value-compact");
|
||||
@@ -858,13 +804,18 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
document.getElementById("remainingAmount").textContent = "₹" + formatINR(data.remaining);
|
||||
}
|
||||
if (document.getElementById("baseAmount")) {
|
||||
document.getElementById("baseAmount").textContent = "₹" + formatINR(data.baseAmount);
|
||||
document.getElementById("baseAmount").textContent = "₹" + formatINR(data.chargeGroupsTotal);
|
||||
}
|
||||
if (document.getElementById("gstAmount")) {
|
||||
document.getElementById("gstAmount").textContent = "₹" + formatINR(data.gstAmount);
|
||||
}
|
||||
if (document.getElementById("totalInvoiceWithGst")) {
|
||||
document.getElementById("totalInvoiceWithGst").textContent = "₹" + formatINR(data.finalAmountWithGst);
|
||||
document.getElementById("totalInvoiceWithGst").textContent =
|
||||
"₹" + formatINR(data.grandTotal);
|
||||
}
|
||||
const totalCard = document.getElementById("totalInvoiceWithGstCard");
|
||||
if (totalCard) {
|
||||
totalCard.textContent = "₹" + formatINR(data.grandTotal);
|
||||
}
|
||||
|
||||
const paidCard = document.querySelector(".summary-card-compact.paid .summary-value-compact");
|
||||
@@ -886,16 +837,15 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
alert("Something went wrong. Please try again.");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Auto due date = invoice date + 10 days
|
||||
const invoiceDateInput = document.querySelector('input[name="invoice_date"]');
|
||||
const dueDateInput = document.querySelector('input[name="due_date"]');
|
||||
|
||||
if (invoiceDateInput && dueDateInput) {
|
||||
invoiceDateInput.addEventListener('change', function() {
|
||||
const selectedDate = new Date(this.value);
|
||||
|
||||
|
||||
if (!isNaN(selectedDate.getTime())) {
|
||||
selectedDate.setDate(selectedDate.getDate() + 10);
|
||||
const year = selectedDate.getFullYear();
|
||||
|
||||
@@ -1024,156 +1024,252 @@
|
||||
</div>
|
||||
|
||||
<!-- ═══════════════════════════ CHARGE GROUP FORM ═══════════════════════════ -->
|
||||
<div id="chargeGroupBox" class="cg-panel d-none">
|
||||
<div class="cg-panel-header">
|
||||
<i class="fas fa-layer-group"></i> Create Charge Group for Selected Items
|
||||
</div>
|
||||
<div class="cg-body">
|
||||
<form method="POST" id="chargeGroupForm"
|
||||
action="{{ route('admin.invoices.charge-group.store', $invoice->id) }}">
|
||||
@csrf
|
||||
|
||||
<div class="row g-3 mb-3">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label-custom">Group Name</label>
|
||||
<input type="text" class="form-control-custom" id="cgGroupName"
|
||||
name="group_name" placeholder="e.g. Group #1">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label-custom">Price based on</label>
|
||||
<select class="form-select-custom" id="cgBasis">
|
||||
<option value="" selected disabled>Select basis</option>
|
||||
<option value="ttl_qty">TTL/QTY</option>
|
||||
<option value="amount">AMOUNT</option>
|
||||
<option value="ttl_cbm">TTL CBM</option>
|
||||
<option value="ttl_kg">TTL KG</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label-custom">Rate per selected basis</label>
|
||||
<input type="number" step="0.0001" class="form-control-custom"
|
||||
id="cgRate" placeholder="Enter rate">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="basis_type" id="cgBasisTypeInput">
|
||||
<input type="hidden" name="basis_value" id="cgBasisValueInput">
|
||||
<input type="hidden" name="rate" id="cgRateHidden">
|
||||
<input type="hidden" name="auto_total" id="cgAutoTotal">
|
||||
|
||||
<div class="row g-3 mb-3">
|
||||
<!-- LEFT: Total basis value -->
|
||||
<div class="col-md-6">
|
||||
<div class="basis-box">
|
||||
<div class="basis-box-label">Total basis value</div>
|
||||
<div style="display:flex;align-items:baseline;gap:0.4rem;">
|
||||
<span class="basis-value-display" id="cgBasisValue">0</span>
|
||||
<span class="basis-label-display" id="cgBasisLabel"></span>
|
||||
</div>
|
||||
<div class="basis-hint" id="cgBasisHint">
|
||||
Select basis to see total TTLQTY, CBM, KG or Amount.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- RIGHT: Total charges (admin input) -->
|
||||
<div class="col-md-6">
|
||||
<div class="basis-box">
|
||||
<div class="basis-box-label">Total charges (admin input)</div>
|
||||
<input type="number"
|
||||
step="0.01"
|
||||
class="form-control-custom"
|
||||
id="cgTotalChargeInput"
|
||||
name="totalcharge"
|
||||
placeholder="Enter total charges"
|
||||
readonly>
|
||||
<div class="basis-hint" style="margin-top:0.5rem">
|
||||
Suggested Rate × basis:
|
||||
<span class="suggested-total" id="cgSuggestedTotal">0</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label-custom" style="margin-bottom:0.5rem;">Selected items in this group</label>
|
||||
<div class="table-responsive" style="border:1px solid var(--border);border-radius:9px;overflow:hidden;">
|
||||
<table class="invoice-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Description</th>
|
||||
<th class="text-center">QTY</th>
|
||||
<th class="text-center">TTL QTY</th>
|
||||
<th class="text-center">CBM</th>
|
||||
<th class="text-center">TTL CBM</th>
|
||||
<th class="text-center">KG</th>
|
||||
<th class="text-center">TTL KG</th>
|
||||
<th class="text-center">TTL Amount</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="cgItemsTableBody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-end gap-2">
|
||||
<button type="button" id="cgCancelBtn"
|
||||
style="padding:0.5rem 1.1rem;border:1.5px solid var(--border);border-radius:8px;background:transparent;font-size:0.85rem;font-weight:600;cursor:pointer;color:var(--text-secondary);">
|
||||
Cancel
|
||||
</button>
|
||||
<button type="submit" id="cgSaveBtn"
|
||||
style="display:inline-flex;align-items:center;gap:0.4rem;padding:0.5rem 1.25rem;border:none;border-radius:8px;background:#10b981;color:white;font-size:0.85rem;font-weight:700;cursor:pointer;">
|
||||
<i class="fas fa-save"></i> Save Charge Group
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div id="chargeGroupBox" class="cg-panel d-none">
|
||||
<div class="cg-panel-header">
|
||||
<i class="fas fa-layer-group"></i> Create Charge Group for Selected Items
|
||||
</div>
|
||||
<div class="cg-body">
|
||||
<form method="POST" id="chargeGroupForm"
|
||||
action="{{ route('admin.invoices.charge-group.store', $invoice->id) }}">
|
||||
@csrf
|
||||
|
||||
<!-- ═══════════════════════════ CHARGE GROUPS LIST ═══════════════════════════ -->
|
||||
@if($invoice->chargeGroups->isNotEmpty())
|
||||
<div class="cg-groups-panel">
|
||||
<div class="cg-groups-header">
|
||||
<i class="fas fa-layer-group"></i> Charge Groups
|
||||
<div class="row g-3 mb-3">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label-custom">Group Name</label>
|
||||
<input type="text" class="form-control-custom" id="cgGroupName"
|
||||
name="groupname" placeholder="e.g. Group #1">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label-custom">Price based on</label>
|
||||
<select class="form-select-custom" id="cgBasis">
|
||||
<option value="" selected disabled>Select basis</option>
|
||||
<option value="ttl_qty">TTL/QTY</option>
|
||||
<option value="amount">AMOUNT</option>
|
||||
<option value="ttl_cbm">TTL CBM</option>
|
||||
<option value="ttl_kg">TTL KG</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label-custom">Rate per selected basis</label>
|
||||
<input type="number" step="0.0001" class="form-control-custom"
|
||||
id="cgRate" placeholder="Enter rate">
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="invoice-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Group Name</th>
|
||||
<th>Basis</th>
|
||||
<th class="text-end">Basis Value</th>
|
||||
<th class="text-end">Rate</th>
|
||||
<th class="text-end">Total Charge</th>
|
||||
<th class="text-center">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($invoice->chargeGroups as $index => $group)
|
||||
|
||||
<div class="row g-3 mb-3">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label-custom">Tax Type</label>
|
||||
<select class="form-select-custom" id="cgTaxType" name="tax_type">
|
||||
<option value="" selected disabled>Select tax type</option>
|
||||
<!-- <option value="none">No Tax</option> -->
|
||||
<option value="gst">GST (CGST+SGST)</option>
|
||||
<option value="igst">IGST</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<label class="form-label-custom">GST %</label>
|
||||
<input type="number" step="0.01"
|
||||
class="form-control-custom"
|
||||
id="cgGstPercent"
|
||||
name="gst_percent"
|
||||
placeholder="e.g. 18">
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<label class="form-label-custom">Total charges with GST</label>
|
||||
<input type="number" step="0.01"
|
||||
class="form-control-custom"
|
||||
id="cgTotalWithGst"
|
||||
name="total_with_gst"
|
||||
placeholder="Auto calculated"
|
||||
readonly>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Hidden fields (controller validate साठी names match केलेत) --}}
|
||||
<input type="hidden" name="basistype" id="cgBasisTypeInput">
|
||||
<input type="hidden" name="basisvalue" id="cgBasisValueInput">
|
||||
<input type="hidden" name="rate" id="cgRateHidden">
|
||||
<input type="hidden" name="autototal" id="cgAutoTotal">
|
||||
|
||||
{{-- itemids[] हे JS मधून append होतात (checkbox selected items) --}}
|
||||
|
||||
<div class="row g-3 mb-3">
|
||||
<!-- LEFT: Total basis value -->
|
||||
<div class="col-md-6">
|
||||
<div class="basis-box">
|
||||
<div class="basis-box-label">Total basis value</div>
|
||||
<div style="display:flex;align-items:baseline;gap:0.4rem;">
|
||||
<span class="basis-value-display" id="cgBasisValue">0</span>
|
||||
<span class="basis-label-display" id="cgBasisLabel"></span>
|
||||
</div>
|
||||
<div class="basis-hint" id="cgBasisHint">
|
||||
Select basis to see total TTLQTY, CBM, KG or Amount.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- RIGHT: Total charges (admin input) -->
|
||||
<div class="col-md-6">
|
||||
<div class="basis-box">
|
||||
<div class="basis-box-label">Total charges (admin input)</div>
|
||||
<input type="number"
|
||||
step="0.01"
|
||||
class="form-control-custom"
|
||||
id="cgTotalChargeInput"
|
||||
placeholder="Enter total charges"
|
||||
readonly>
|
||||
<div class="basis-hint" style="margin-top:0.5rem">
|
||||
Suggested Rate × basis:
|
||||
<span class="suggested-total" id="cgSuggestedTotal">0</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label-custom" style="margin-bottom:0.5rem;">Selected items in this group</label>
|
||||
<div class="table-responsive" style="border:1px solid var(--border);border-radius:9px;overflow:hidden;">
|
||||
<table class="invoice-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>{{ $index + 1 }}</td>
|
||||
<td style="font-weight:600;">{{ $group->group_name ?? 'Group '.$group->id }}</td>
|
||||
<td><span style="text-transform:uppercase;font-size:0.75rem;font-weight:700;color:var(--text-muted);">{{ $group->basis_type }}</span></td>
|
||||
<td class="text-end" style="font-family:'JetBrains Mono',monospace;font-size:0.82rem;">{{ number_format($group->basis_value, 3) }}</td>
|
||||
<td class="text-end" style="font-family:'JetBrains Mono',monospace;font-size:0.82rem;">{{ number_format($group->rate, 2) }}</td>
|
||||
<td class="text-end price-blue">₹{{ number_format($group->total_charge, 2) }}</td>
|
||||
<td class="text-center">
|
||||
<button type="button" class="cg-toggle-btn cg-toggle-items"
|
||||
data-group-id="{{ $group->id }}">
|
||||
<i class="fas fa-eye"></i> View
|
||||
</button>
|
||||
</td>
|
||||
<th>#</th>
|
||||
<th>Description</th>
|
||||
<th class="text-center">QTY</th>
|
||||
<th class="text-center">TTL QTY</th>
|
||||
<th class="text-center">CBM</th>
|
||||
<th class="text-center">TTL CBM</th>
|
||||
<th class="text-center">KG</th>
|
||||
<th class="text-center">TTL KG</th>
|
||||
<th class="text-center">TTL Amount</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="cgItemsTableBody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-end gap-2">
|
||||
<button type="button" id="cgCancelBtn"
|
||||
style="padding:0.5rem 1.1rem;border:1.5px solid var(--border);border-radius:8px;background:transparent;font-size:0.85rem;font-weight:600;cursor:pointer;color:var(--text-secondary);">
|
||||
Cancel
|
||||
</button>
|
||||
<button type="submit" id="cgSaveBtn"
|
||||
style="display:inline-flex;align-items:center;gap:0.4rem;padding:0.5rem 1.25rem;border:none;border-radius:8px;background:#10b981;color:white;font-size:0.85rem;font-weight:700;cursor:pointer;">
|
||||
<i class="fas fa-save"></i> Save Charge Group
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- ═══════════════════════════ CHARGE GROUPS LIST ═══════════════════════════ --}}
|
||||
@if($invoice->chargeGroups->isNotEmpty())
|
||||
<div class="cg-groups-panel">
|
||||
<div class="cg-groups-header">
|
||||
<i class="fas fa-layer-group"></i> Charge Groups
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="invoice-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Group Name</th>
|
||||
<th>Basis</th>
|
||||
<th class="text-end">Basis Value</th>
|
||||
<th class="text-end">Rate</th>
|
||||
<th class="text-end">GST %</th>
|
||||
<th class="text-end">Total Charge</th>
|
||||
<th class="text-end">Total With GST</th>
|
||||
<th class="text-center">Tax Type</th>
|
||||
<th class="text-center">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($invoice->chargeGroups as $index => $group)
|
||||
@php
|
||||
// Base total आणि GST calculation
|
||||
$groupBaseTotal = $group->total_charge ?? 0;
|
||||
$groupTotalWithGst = $group->total_with_gst ?? $groupBaseTotal;
|
||||
$groupGstAmount = max(0, $groupTotalWithGst - $groupBaseTotal);
|
||||
$groupGstPercent = $group->gst_percent ?? 0;
|
||||
@endphp
|
||||
|
||||
{{-- Main group row --}}
|
||||
<tr>
|
||||
<td>{{ $index + 1 }}</td>
|
||||
|
||||
<td style="font-weight:600;">
|
||||
{{ $group->group_name ?? 'Group '.$group->id }}
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<span style="text-transform:uppercase;
|
||||
font-size:0.75rem;
|
||||
font-weight:700;
|
||||
color:var(--text-muted);">
|
||||
{{ $group->basis_type }}
|
||||
</span>
|
||||
</td>
|
||||
|
||||
<td class="text-end"
|
||||
style="font-family:'JetBrains Mono',monospace;font-size:0.82rem;">
|
||||
{{ number_format($group->basis_value, 3) }}
|
||||
</td>
|
||||
|
||||
<td class="text-end"
|
||||
style="font-family:'JetBrains Mono',monospace;font-size:0.82rem;">
|
||||
{{ number_format($group->rate, 2) }}
|
||||
</td>
|
||||
|
||||
{{-- GST % --}}
|
||||
<td class="text-end"
|
||||
style="font-family:'JetBrains Mono',monospace;font-size:0.82rem;">
|
||||
{{ number_format($groupGstPercent, 2) }}%
|
||||
</td>
|
||||
|
||||
{{-- Base total without GST --}}
|
||||
<td class="text-end price-blue">
|
||||
₹{{ number_format($groupBaseTotal, 2) }}
|
||||
</td>
|
||||
|
||||
{{-- Group total with GST --}}
|
||||
<td class="text-end" style="font-weight:700;color:#16a34a;">
|
||||
₹{{ number_format($groupTotalWithGst, 2) }}
|
||||
</td>
|
||||
|
||||
{{-- Group tax type --}}
|
||||
<td class="text-center">
|
||||
{{ $group->tax_type ?? '-' }}
|
||||
</td>
|
||||
|
||||
<td class="text-center">
|
||||
<button type="button"
|
||||
class="cg-toggle-btn cg-toggle-items"
|
||||
data-group-id="{{ $group->id }}">
|
||||
<i class="fas fa-eye"></i> View
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
{{-- Hidden items row --}}
|
||||
<tr class="cg-items-row d-none" data-group-id="{{ $group->id }}">
|
||||
<td colspan="7">
|
||||
<td colspan="10">
|
||||
<div style="padding:1rem;background:#f8faff;border-radius:8px;margin:0.25rem 0;">
|
||||
<div style="font-weight:700;font-size:0.8rem;margin-bottom:0.6rem;color:var(--primary);">Items in this group:</div>
|
||||
<div style="font-weight:700;font-size:0.8rem;margin-bottom:0.6rem;color:var(--primary);">
|
||||
Items in this group
|
||||
<span style="font-weight:500;color:#64748b;margin-left:8px;">
|
||||
(GST: {{ number_format($groupGstPercent ?? 0, 2) }}%)
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@if($group->items->isEmpty())
|
||||
<div style="color:var(--text-muted);font-size:0.82rem;">No items linked.</div>
|
||||
<div style="color:var(--text-muted);font-size:0.82rem;">
|
||||
No items linked.
|
||||
</div>
|
||||
@else
|
||||
<div class="table-responsive">
|
||||
<table class="invoice-table">
|
||||
@@ -1190,6 +1286,10 @@
|
||||
<th class="text-end">TTL Amount</th>
|
||||
<th class="text-end">Rate</th>
|
||||
<th class="text-end">Total Charge</th>
|
||||
{{-- नवीन GST % कॉलम --}}
|
||||
<th class="text-end">GST %</th>
|
||||
<th class="text-end">Item GST</th>
|
||||
<th class="text-end">Item Total (with GST)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -1198,14 +1298,37 @@
|
||||
$it = $gi->item;
|
||||
$rate = $group->rate;
|
||||
$itemBasis = 0;
|
||||
|
||||
switch ($group->basis_type) {
|
||||
case 'ttl_qty': $itemBasis = $it->ttl_qty ?? 0; break;
|
||||
case 'amount': $itemBasis = $it->ttl_amount ?? 0; break;
|
||||
case 'ttl_cbm': $itemBasis = $it->ttl_cbm ?? 0; break;
|
||||
case 'ttl_kg': $itemBasis = $it->ttl_kg ?? 0; break;
|
||||
case 'ttl_qty':
|
||||
$itemBasis = $it->ttl_qty ?? 0;
|
||||
break;
|
||||
case 'amount':
|
||||
$itemBasis = $it->ttl_amount ?? 0;
|
||||
break;
|
||||
case 'ttl_cbm':
|
||||
$itemBasis = $it->ttl_cbm ?? 0;
|
||||
break;
|
||||
case 'ttl_kg':
|
||||
$itemBasis = $it->ttl_kg ?? 0;
|
||||
break;
|
||||
}
|
||||
|
||||
$itemTotal = $itemBasis * $rate;
|
||||
|
||||
// Per‑item GST share (proportional)
|
||||
$itemGst = 0;
|
||||
$itemTotalWithGst = $itemTotal;
|
||||
|
||||
if ($groupBaseTotal > 0 && $groupGstAmount > 0) {
|
||||
$share = $itemTotal / $groupBaseTotal;
|
||||
$itemGst = $groupGstAmount * $share;
|
||||
$itemTotalWithGst = $itemTotal + $itemGst;
|
||||
}
|
||||
|
||||
$itemGstPercent = $groupGstPercent ?? 0; // same as group
|
||||
@endphp
|
||||
|
||||
<tr>
|
||||
<td>{{ $giIndex + 1 }}</td>
|
||||
<td>{{ $it->description }}</td>
|
||||
@@ -1215,9 +1338,27 @@
|
||||
<td class="text-center">{{ $it->ttl_cbm }}</td>
|
||||
<td class="text-center">{{ $it->kg }}</td>
|
||||
<td class="text-center">{{ $it->ttl_kg }}</td>
|
||||
<td class="text-end">{{ number_format($it->ttl_amount, 2) }}</td>
|
||||
<td class="text-end">{{ number_format($rate, 2) }}</td>
|
||||
<td class="text-end" style="color:#06b6d4;font-weight:700;">{{ number_format($itemTotal, 2) }}</td>
|
||||
<td class="text-end">
|
||||
{{ number_format($it->ttl_amount, 2) }}
|
||||
</td>
|
||||
<td class="text-end">
|
||||
{{ number_format($rate, 2) }}
|
||||
</td>
|
||||
<td class="text-end" style="color:#06b6d4;font-weight:700;">
|
||||
{{ number_format($itemTotal, 2) }}
|
||||
</td>
|
||||
|
||||
{{-- GST % (per item = group GST %) --}}
|
||||
<td class="text-end">
|
||||
{{ number_format($itemGstPercent, 2) }}%
|
||||
</td>
|
||||
|
||||
<td class="text-end" style="color:#ef4444;">
|
||||
{{ $itemGst > 0 ? number_format($itemGst, 2) : '0.00' }}
|
||||
</td>
|
||||
<td class="text-end" style="font-weight:700;color:#16a34a;">
|
||||
{{ number_format($itemTotalWithGst, 2) }}
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
@@ -1227,12 +1368,14 @@
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
|
||||
<!-- ═══════════════════════════ SUMMARY ═══════════════════════════ -->
|
||||
<!-- <div class="summary-wrap">
|
||||
@@ -1286,61 +1429,52 @@
|
||||
</div>
|
||||
|
||||
<div class="summary-body-compact">
|
||||
@if($invoice->tax_type === 'gst')
|
||||
<div class="summary-row-compact">
|
||||
<span class="label">
|
||||
CGST {{ $invoice->cgst_percent ?? $invoice->gst_percent / 2 }}%
|
||||
</span>
|
||||
<span class="value red">
|
||||
₹{{ number_format($invoice->gst_amount / 2, 2) }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="summary-row-compact">
|
||||
<span class="label">
|
||||
SGST {{ $invoice->sgst_percent ?? $invoice->gst_percent / 2 }}%
|
||||
</span>
|
||||
<span class="value red">
|
||||
₹{{ number_format($invoice->gst_amount / 2, 2) }}
|
||||
</span>
|
||||
</div>
|
||||
@elseif($invoice->tax_type === 'igst')
|
||||
<div class="summary-row-compact">
|
||||
<span class="label">
|
||||
IGST {{ $invoice->igst_percent ?? $invoice->gst_percent }}%
|
||||
</span>
|
||||
<span class="value red">
|
||||
₹{{ number_format($invoice->gst_amount, 2) }}
|
||||
</span>
|
||||
</div>
|
||||
@else
|
||||
<div class="summary-row-compact">
|
||||
<span class="label">
|
||||
GST {{ $invoice->gst_percent }}%
|
||||
</span>
|
||||
<span class="value red">
|
||||
₹{{ number_format($invoice->gst_amount, 2) }}
|
||||
</span>
|
||||
</div>
|
||||
@endif
|
||||
@php
|
||||
// Base amount (without GST) – invoice level
|
||||
$baseAmount = $invoice->final_amount; // items + groups base, जर तू तसे ठेवले असेल
|
||||
$gstAmount = $invoice->gst_amount; // total GST
|
||||
$groupsWithGst = $invoice->chargeGroups->sum('total_with_gst'); // सर्व charge groups with GST
|
||||
@endphp
|
||||
|
||||
{{-- Total Charge (Before GST) --}}
|
||||
<div class="summary-row-compact">
|
||||
<span class="label">Charge Groups Total</span>
|
||||
<span class="label">Total Charge (Before GST)</span>
|
||||
<span class="value">
|
||||
₹{{ number_format($invoice->charge_groups_total, 2) }}
|
||||
₹{{ number_format($baseAmount, 2) }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- <div class="summary-row-compact total">
|
||||
<span class="label">Grand Total (Items + GST + Groups)</span>
|
||||
<span class="value">
|
||||
₹{{ number_format($invoice->grand_total_with_charges, 2) }}
|
||||
{{-- GST Amount (total) --}}
|
||||
<div class="summary-row-compact">
|
||||
<span class="label">GST Amount</span>
|
||||
<span class="value red">
|
||||
₹{{ number_format($gstAmount, 2) }}
|
||||
</span>
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
{{-- Charge Groups Total (with GST) --}}
|
||||
<div class="summary-row-compact">
|
||||
<span class="label">Charge Groups Total (with GST)</span>
|
||||
<span class="value">
|
||||
₹{{ number_format($groupsWithGst, 2) }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
{{--
|
||||
<div class="summary-row-compact total">
|
||||
<span class="label">Total Charge With GST</span>
|
||||
<span class="value">
|
||||
₹{{ number_format($invoice->final_amount_with_gst, 2) }}
|
||||
</span>
|
||||
</div>
|
||||
--}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- ═══════════════════════════ FOOTER ═══════════════════════════ -->
|
||||
<div class="invoice-footer">
|
||||
@if($invoice->pdf_path && $showActions)
|
||||
@@ -1419,7 +1553,12 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
const cgRateHidden = document.getElementById('cgRateHidden');
|
||||
const cgForm = document.getElementById('chargeGroupForm');
|
||||
|
||||
const cgGroupName = document.getElementById('cgGroupName');
|
||||
const cgGroupName = document.getElementById('cgGroupName');
|
||||
|
||||
// Tax related fields
|
||||
const cgTaxType = document.getElementById('cgTaxType');
|
||||
const cgGstPercent = document.getElementById('cgGstPercent');
|
||||
const cgTotalWithGst = document.getElementById('cgTotalWithGst');
|
||||
|
||||
function updateSelectionState() {
|
||||
let selectedCount = 0;
|
||||
@@ -1463,6 +1602,23 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
return isNaN(val) ? 0 : val;
|
||||
}
|
||||
|
||||
// GST calculation based on total charge and selected tax type
|
||||
function refreshChargeGroupGst() {
|
||||
if (!cgTotalChargeInput || !cgTotalWithGst) return;
|
||||
|
||||
const base = parseNumber(cgTotalChargeInput.value);
|
||||
let gstPercent = 0;
|
||||
|
||||
if (cgTaxType && cgGstPercent && cgTaxType.value && cgTaxType.value !== 'none') {
|
||||
gstPercent = parseNumber(cgGstPercent.value);
|
||||
}
|
||||
|
||||
const gstAmount = base * gstPercent / 100;
|
||||
const totalWithGst = base + gstAmount;
|
||||
|
||||
cgTotalWithGst.value = totalWithGst ? totalWithGst.toFixed(2) : 0;
|
||||
}
|
||||
|
||||
function fillChargeGroupItemsTable() {
|
||||
if (!cgItemsTableBody) return;
|
||||
const items = getSelectedItemsDataBasic();
|
||||
@@ -1486,10 +1642,12 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
|
||||
function refreshBasisSummaryAndSuggestion() {
|
||||
if (!chargeGroupBox || chargeGroupBox.classList.contains('d-none')) return;
|
||||
|
||||
const items = getSelectedItemsDataBasic();
|
||||
const basis = cgBasisSelect ? cgBasisSelect.value : '';
|
||||
let totalBasis = 0;
|
||||
let label = '';
|
||||
|
||||
if (basis === 'ttl_qty') {
|
||||
totalBasis = items.reduce((sum, it) => sum + parseNumber(it.ttlqty), 0);
|
||||
label = 'Total TTL/QTY';
|
||||
@@ -1503,11 +1661,15 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
totalBasis = items.reduce((sum, it) => sum + parseNumber(it.ttlkg), 0);
|
||||
label = 'Total TTL KG';
|
||||
}
|
||||
|
||||
if (cgBasisValueSpan) cgBasisValueSpan.textContent = totalBasis ? totalBasis.toFixed(3) : '0';
|
||||
if (cgBasisLabelSpan) cgBasisLabelSpan.textContent = label || '–';
|
||||
|
||||
const rate = parseNumber(cgRateInput ? cgRateInput.value : '0');
|
||||
const suggested = rate * totalBasis;
|
||||
|
||||
if (cgSuggestedTotalSpan) cgSuggestedTotalSpan.textContent = suggested ? suggested.toFixed(2) : '0';
|
||||
|
||||
if (cgBasisTypeInput) cgBasisTypeInput.value = basis || '';
|
||||
if (cgBasisValueInput) cgBasisValueInput.value = totalBasis || 0;
|
||||
if (cgRateHidden && cgRateInput) cgRateHidden.value = cgRateInput.value || 0;
|
||||
@@ -1515,6 +1677,9 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
if (cgTotalChargeInput) {
|
||||
cgTotalChargeInput.value = suggested ? suggested.toFixed(2) : '0';
|
||||
}
|
||||
|
||||
// base बदलला की लगेच GST सह total पण refresh
|
||||
refreshChargeGroupGst();
|
||||
}
|
||||
|
||||
itemCheckboxes.forEach(cb => {
|
||||
@@ -1547,6 +1712,17 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
if (cgBasisSelect) cgBasisSelect.addEventListener('change', refreshBasisSummaryAndSuggestion);
|
||||
if (cgRateInput) cgRateInput.addEventListener('input', refreshBasisSummaryAndSuggestion);
|
||||
|
||||
// GST fields event listeners
|
||||
if (cgGstPercent) {
|
||||
cgGstPercent.addEventListener('input', refreshChargeGroupGst);
|
||||
}
|
||||
if (cgTaxType) {
|
||||
cgTaxType.addEventListener('change', refreshChargeGroupGst);
|
||||
}
|
||||
if (cgTotalChargeInput) {
|
||||
cgTotalChargeInput.addEventListener('input', refreshChargeGroupGst);
|
||||
}
|
||||
|
||||
if (btnCreate && chargeGroupBox) {
|
||||
btnCreate.addEventListener('click', function () {
|
||||
const hasSelection = Array.from(itemCheckboxes).some(cb => cb.checked && !cb.disabled);
|
||||
@@ -1567,7 +1743,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
});
|
||||
}
|
||||
|
||||
// MAIN CHANGE: normal form submit, फक्त hidden item_ids तयार करतो
|
||||
// normal form submit, फक्त hidden item_ids तयार करतो
|
||||
if (cgForm) {
|
||||
cgForm.addEventListener('submit', function () {
|
||||
const selectedIds = [];
|
||||
@@ -1579,7 +1755,6 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
|
||||
if (selectedIds.length === 0) {
|
||||
alert('Please select at least one item for this charge group.');
|
||||
// default submit रोखण्यासाठी return false
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1596,19 +1771,21 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
return false;
|
||||
}
|
||||
|
||||
const oldHidden = cgForm.querySelectorAll('input[name="item_ids[]"]');
|
||||
// submit आधी latest GST total कॅलक्युलेट करणे सुरक्षित
|
||||
refreshChargeGroupGst();
|
||||
|
||||
const oldHidden = cgForm.querySelectorAll('input[name="itemids[]"]');
|
||||
oldHidden.forEach(el => el.remove());
|
||||
|
||||
selectedIds.forEach(id => {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'hidden';
|
||||
input.name = 'item_ids[]';
|
||||
input.name = 'itemids[]';
|
||||
input.value = id;
|
||||
cgForm.appendChild(input);
|
||||
});
|
||||
|
||||
// इथे e.preventDefault नाही; normal submit होऊ दे
|
||||
return true;
|
||||
return true; // normal submit
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1634,9 +1811,9 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
});
|
||||
});
|
||||
|
||||
// simple select all (duplicate राहिला तरी harmless; गरज असल्यास काढू शकतो)
|
||||
// simple select all (duplicate राहिला तरी harmless)
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const selectAll = document.getElementById('selectAllItems');
|
||||
const selectAll = document.getElementById('selectAllItems');
|
||||
const checkboxes = document.querySelectorAll('.item-select-checkbox');
|
||||
|
||||
if (selectAll) {
|
||||
|
||||
Reference in New Issue
Block a user