diff --git a/app/Http/Controllers/Admin/AdminInvoiceController.php b/app/Http/Controllers/Admin/AdminInvoiceController.php index 00cbe82..ef0820d 100644 --- a/app/Http/Controllers/Admin/AdminInvoiceController.php +++ b/app/Http/Controllers/Admin/AdminInvoiceController.php @@ -22,7 +22,7 @@ class AdminInvoiceController extends Controller { $query = Invoice::with(['items', 'customer', 'container']); - // Search (जर पुढे form मधून पाठवलंस तर) + // Search if ($request->filled('search')) { $search = $request->search; $query->where(function ($q) use ($search) { @@ -373,7 +373,7 @@ class AdminInvoiceController extends Controller public function storeChargeGroup(Request $request, $invoiceId) { $invoice = Invoice::with('items')->findOrFail($invoiceId); - + $data = $request->validate([ 'group_name' => 'nullable|string|max:255', 'basis_type' => 'required|in:ttl_qty,amount,ttl_cbm,ttl_kg', @@ -383,7 +383,7 @@ class AdminInvoiceController extends Controller 'item_ids' => 'required|array', 'item_ids.*' => 'integer|exists:invoice_items,id', ]); - + $group = InvoiceChargeGroup::create([ 'invoice_id' => $invoice->id, 'group_name' => $data['group_name'] ?? null, @@ -392,19 +392,53 @@ class AdminInvoiceController extends Controller 'rate' => $data['rate'], 'total_charge' => $data['auto_total'], ]); - + foreach ($data['item_ids'] as $itemId) { InvoiceChargeGroupItem::create([ 'group_id' => $group->id, 'invoice_item_id' => $itemId, ]); } - + + if ($request->ajax()) { + // load items with invoice item relation + $group->load(['items.item']); + + // prepare simple array for JS + $itemsPayload = $group->items->map(function ($gi) { + $it = $gi->item; + return [ + 'description' => $it->description, + 'qty' => $it->qty, + 'ttlqty' => $it->ttl_qty, + 'cbm' => $it->cbm, + 'ttlcbm' => $it->ttl_cbm, + 'kg' => $it->kg, + 'ttlkg' => $it->ttl_kg, + 'amount' => $it->ttl_amount, + ]; + }); + + return response()->json([ + 'success' => true, + 'message' => 'Charge group created successfully.', + 'group' => [ + 'id' => $group->id, + 'group_name' => $group->group_name, + 'basis_type' => $group->basis_type, + 'basis_value' => $group->basis_value, + 'rate' => $group->rate, + 'total_charge'=> $group->total_charge, + 'items' => $itemsPayload, + ], + ]); + } + return redirect() ->back() ->with('success', 'Charge group saved successfully.'); } - + public function download(Invoice $invoice) { if (!$invoice->pdf_path || !Storage::exists($invoice->pdf_path)) { diff --git a/app/Http/Controllers/ContainerController.php b/app/Http/Controllers/ContainerController.php index af3ecd8..aeb04d0 100644 --- a/app/Http/Controllers/ContainerController.php +++ b/app/Http/Controllers/ContainerController.php @@ -9,6 +9,7 @@ use App\Models\Invoice; use App\Models\InvoiceItem; use Illuminate\Http\Request; use Maatwebsite\Excel\Facades\Excel; +use Carbon\Carbon; class ContainerController extends Controller { @@ -530,7 +531,9 @@ class ContainerController extends Controller $invoice->invoice_number = $this->generateInvoiceNumber(); $invoice->invoice_date = now()->toDateString(); - $invoice->due_date = null; + // NEW (add this): + $invoice->due_date = Carbon::parse($invoice->invoice_date)->addDays(10)->format('Y-m-d'); + if ($snap) { $invoice->customer_name = $snap['customer_name'] ?? null; diff --git a/database/migrations/2026_02_28_051812_add_duedate_to_invoices_table.php b/database/migrations/2026_02_28_051812_add_duedate_to_invoices_table.php new file mode 100644 index 0000000..986a450 --- /dev/null +++ b/database/migrations/2026_02_28_051812_add_duedate_to_invoices_table.php @@ -0,0 +1,26 @@ +date('duedate')->nullable()->after('invoicedate'); + }); + } + + public function down() + { + Schema::table('invoices', function (Blueprint $table) { + $table->dropColumn('duedate'); + }); + } + +}; diff --git a/resources/views/admin/invoice.blade.php b/resources/views/admin/invoice.blade.php index 31683a7..6360b66 100644 --- a/resources/views/admin/invoice.blade.php +++ b/resources/views/admin/invoice.blade.php @@ -1683,6 +1683,11 @@ document.addEventListener('DOMContentLoaded', function() { } }); }); + + + + + @endsection \ No newline at end of file diff --git a/resources/views/admin/invoice_edit.blade.php b/resources/views/admin/invoice_edit.blade.php index 87ed307..475fb79 100644 --- a/resources/views/admin/invoice_edit.blade.php +++ b/resources/views/admin/invoice_edit.blade.php @@ -875,5 +875,29 @@ document.addEventListener("DOMContentLoaded", function () { }); }); }); + + +document.addEventListener('DOMContentLoaded', function() { + 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())) { + // १० दिवस पुढे नेण्यासाठी logic + selectedDate.setDate(selectedDate.getDate() + 10); + + // तारीख YYYY-MM-DD format मध्ये करण्यासाठी + const year = selectedDate.getFullYear(); + const month = String(selectedDate.getMonth() + 1).padStart(2, '0'); + const day = String(selectedDate.getDate()).padStart(2, '0'); + + dueDateInput.value = `${year}-${month}-${day}`; + } + }); + } +}); @endsection diff --git a/resources/views/admin/popup_invoice.blade.php b/resources/views/admin/popup_invoice.blade.php index 4913baf..83dd6da 100644 --- a/resources/views/admin/popup_invoice.blade.php +++ b/resources/views/admin/popup_invoice.blade.php @@ -1313,40 +1313,232 @@ document.addEventListener('DOMContentLoaded', function () { } if (cgForm) { - cgForm.addEventListener('submit', function (e) { - const selectedIds = []; - itemCheckboxes.forEach(cb => { if (cb.checked && !cb.disabled) selectedIds.push(cb.value); }); - if (selectedIds.length === 0) { - e.preventDefault(); - alert('Please select at least one item for this charge group.'); - return; + cgForm.addEventListener('submit', function (e) { + // Stop normal form submit (no page reload) + e.preventDefault(); + + // 1) Collect selected item IDs + const selectedIds = []; + itemCheckboxes.forEach(cb => { + if (cb.checked && !cb.disabled) { + selectedIds.push(cb.value); } - if (!cgBasisSelect || !cgBasisSelect.value) { - e.preventDefault(); - alert('Please select a basis for this charge group.'); - return; - } - if (!cgTotalChargeInput || !cgTotalChargeInput.value || parseFloat(cgTotalChargeInput.value) <= 0) { - e.preventDefault(); - alert('Please enter total charges for this group.'); - return; - } - const oldHidden = cgForm.querySelectorAll('input[name="item_ids[]"]'); - oldHidden.forEach(el => el.remove()); - selectedIds.forEach(id => { - const input = document.createElement('input'); - input.type = 'hidden'; - input.name = 'item_ids[]'; - input.value = id; - cgForm.appendChild(input); - }); - if (chargeGroupBox) chargeGroupBox.classList.add('d-none'); - itemCheckboxes.forEach(cb => { if (!cb.disabled) cb.checked = false; }); - if (selectAll) { selectAll.checked = false; selectAll.indeterminate = false; } - updateSelectionState(); }); + + // 2) Frontend validations (same as before) + if (selectedIds.length === 0) { + alert('Please select at least one item for this charge group.'); + return; + } + + if (!cgBasisSelect || !cgBasisSelect.value) { + alert('Please select a basis for this charge group.'); + return; + } + + if ( + !cgTotalChargeInput || + !cgTotalChargeInput.value || + parseFloat(cgTotalChargeInput.value) <= 0 + ) { + alert('Please enter total charges for this group.'); + return; + } + + // 3) Remove previously added hidden item_ids[] + const oldHidden = cgForm.querySelectorAll('input[name="item_ids[]"]'); + oldHidden.forEach(el => el.remove()); + + // 4) Add fresh hidden item_ids[] for current selection + selectedIds.forEach(id => { + const input = document.createElement('input'); + input.type = 'hidden'; + input.name = 'item_ids[]'; + input.value = id; + cgForm.appendChild(input); + }); + + // 5) Build AJAX request + const url = cgForm.action; + const formData = new FormData(cgForm); + + // Optional: disable save button while processing + if (cgSaveBtn) { + cgSaveBtn.disabled = true; + cgSaveBtn.innerHTML = ' Saving...'; + } + + fetch(url, { + method: 'POST', + body: formData, + headers: { + 'X-Requested-With': 'XMLHttpRequest' + } + }) + .then(res => { + if (!res.ok) { + throw new Error('Request failed with status ' + res.status); + } + return res.json(); + }) + .then(data => { + if (!data.success) { + throw new Error(data.message || 'Failed to create charge group.'); } + const group = data.group; + + // 6) Append new group main row + hidden items row + const groupsTbody = document.querySelector( + '.cg-groups-panel table.invoice-table tbody' + ); + + if (groupsTbody && group) { + const currentMainRows = groupsTbody.querySelectorAll( + 'tr:not(.cg-items-row)' + ).length; + const index = currentMainRows + 1; + + // Main summary row (same structure as Blade) + const mainRow = document.createElement('tr'); + mainRow.innerHTML = ` + ${index} + + ${group.group_name ? group.group_name : 'Group #' + group.id} + + + + ${group.basis_type} + + + + ${Number(group.basis_value).toFixed(3)} + + + ${Number(group.rate).toFixed(2)} + + + ${Number(group.total_charge).toFixed(2)} + + + + + `; + + // Hidden items row (same idea as Blade .cg-items-row) + const itemsRow = document.createElement('tr'); + itemsRow.className = 'cg-items-row d-none'; + itemsRow.setAttribute('data-group-id', group.id); + + let itemsTableHtml = ` +
+
+ Items in this group +
+ `; + + if (!group.items || group.items.length === 0) { + itemsTableHtml += ` +
+ No items linked. +
+ `; + } else { + itemsTableHtml += ` +
+ + + + + + + + + + + + + + + + `; + + group.items.forEach((it, idx) => { + itemsTableHtml += ` + + + + + + + + + + + + `; + }); + + itemsTableHtml += ` + +
#DescriptionQTYTTL QTYCBMTTL CBMKGTTL KGTTL Amount
${idx + 1}${it.description ?? ''}${it.qty ?? ''}${it.ttlqty ?? ''}${it.cbm ?? ''}${it.ttlcbm ?? ''}${it.kg ?? ''}${it.ttlkg ?? ''}${Number(it.amount ?? 0).toFixed(2)}
+
+ `; + } + + itemsTableHtml += `
`; + + itemsRow.innerHTML = ` + + ${itemsTableHtml} + + `; + + // Append main row + items row in order + groupsTbody.appendChild(mainRow); + groupsTbody.appendChild(itemsRow); + } + + // 7) Reset Charge Group UI (hide panel, uncheck items) + if (chargeGroupBox) { + chargeGroupBox.classList.add('d-none'); + } + itemCheckboxes.forEach(cb => { + if (!cb.disabled) cb.checked = false; + }); + if (selectAll) { + selectAll.checked = false; + selectAll.indeterminate = false; + } + updateSelectionState(); + + // Optional: clear form + if (cgGroupName) cgGroupName.value = ''; + if (cgBasisSelect) cgBasisSelect.value = ''; + if (cgRateInput) cgRateInput.value = ''; + if (cgBasisValueSpan) cgBasisValueSpan.textContent = '0'; + if (cgBasisLabelSpan) cgBasisLabelSpan.textContent = '–'; + if (cgSuggestedTotalSpan) cgSuggestedTotalSpan.textContent = '0'; + if (cgTotalChargeInput) cgTotalChargeInput.value = ''; +}) + + .catch(err => { + console.error(err); + alert('Error creating charge group. Please try again.'); + }) + .finally(() => { + if (cgSaveBtn) { + cgSaveBtn.disabled = false; + cgSaveBtn.innerHTML = ' Save Charge Group'; + } + }); + }); +} + updateSelectionState(); });