diff --git a/app/Http/Controllers/Admin/AdminOrderController.php b/app/Http/Controllers/Admin/AdminOrderController.php index f116b96..8d887ff 100644 --- a/app/Http/Controllers/Admin/AdminOrderController.php +++ b/app/Http/Controllers/Admin/AdminOrderController.php @@ -68,33 +68,42 @@ class AdminOrderController extends Controller * ORDER ITEM MANAGEMENT (existing orders) * ---------------------------*/ public function addItem(Request $request, $orderId) - { - $order = Order::findOrFail($orderId); +{ + $order = Order::findOrFail($orderId); - $data = $request->validate([ - 'description' => 'required|string', - 'ctn' => 'nullable|numeric', - 'qty' => 'nullable|numeric', - 'ttl_qty' => 'nullable|numeric', - 'unit' => 'nullable|string', - 'price' => 'nullable|numeric', - 'ttl_amount' => 'nullable|numeric', - 'cbm' => 'nullable|numeric', - 'ttl_cbm' => 'nullable|numeric', - 'kg' => 'nullable|numeric', - 'ttl_kg' => 'nullable|numeric', - 'shop_no' => 'nullable|string', - ]); + $data = $request->validate([ + 'description' => 'required|string', + 'ctn' => 'nullable|numeric', + 'qty' => 'nullable|numeric', + 'unit' => 'nullable|string', + 'price' => 'nullable|numeric', + 'cbm' => 'nullable|numeric', + 'kg' => 'nullable|numeric', + 'shop_no' => 'nullable|string', + ]); - $data['order_id'] = $order->id; + // ✅ BACKEND CALCULATION + $ctn = (float) ($data['ctn'] ?? 0); + $qty = (float) ($data['qty'] ?? 0); + $price = (float) ($data['price'] ?? 0); + $cbm = (float) ($data['cbm'] ?? 0); + $kg = (float) ($data['kg'] ?? 0); - OrderItem::create($data); + $data['ttl_qty'] = $ctn * $qty; + $data['ttl_amount'] = $data['ttl_qty'] * $price; + $data['ttl_cbm'] = $cbm * $ctn; + $data['ttl_kg'] = $ctn * $kg; - $this->recalcTotals($order); - $this->updateInvoiceFromOrder($order); + $data['order_id'] = $order->id; + + OrderItem::create($data); + + $this->recalcTotals($order); + $this->updateInvoiceFromOrder($order); + + return redirect()->back()->with('success', 'Item added and totals updated.'); +} - return redirect()->back()->with('success', 'Item added and totals updated.'); - } public function deleteItem($id) { @@ -500,14 +509,14 @@ class AdminOrderController extends Controller 'items.*.description' => 'required|string', 'items.*.ctn' => 'nullable|numeric', 'items.*.qty' => 'nullable|numeric', - 'items.*.ttl_qty' => 'nullable|numeric', + 'items.*.unit' => 'nullable|string', 'items.*.price' => 'nullable|numeric', - 'items.*.ttl_amount' => 'nullable|numeric', + 'items.*.cbm' => 'nullable|numeric', - 'items.*.ttl_cbm' => 'nullable|numeric', + 'items.*.kg' => 'nullable|numeric', - 'items.*.ttl_kg' => 'nullable|numeric', + 'items.*.shop_no' => 'nullable|string', ])['items']; @@ -520,6 +529,24 @@ class AdminOrderController extends Controller return back()->with('error', 'Add at least one item.'); } + // ✅ BACKEND CALCULATION (DO NOT TRUST FRONTEND) + foreach ($items as &$item) { + + $ctn = (float) ($item['ctn'] ?? 0); + $qty = (float) ($item['qty'] ?? 0); + $price = (float) ($item['price'] ?? 0); + $cbm = (float) ($item['cbm'] ?? 0); + $kg = (float) ($item['kg'] ?? 0); + + // Calculated fields + $item['ttl_qty'] = $ctn * $qty; + $item['ttl_amount'] = $item['ttl_qty'] * $price; + $item['ttl_cbm'] = $cbm * $ctn; + $item['ttl_kg'] = $ctn * $kg; + } + unset($item); // VERY IMPORTANT + + // 3) totals $total_ctn = array_sum(array_column($items, 'ctn')); $total_qty = array_sum(array_column($items, 'qty')); @@ -631,30 +658,49 @@ class AdminOrderController extends Controller * UPDATE ORDER ITEM (existing orders) * ---------------------------*/ public function updateItem(Request $request, $id) - { - $item = OrderItem::findOrFail($id); - $order = $item->order; +{ + $item = OrderItem::findOrFail($id); + $order = $item->order; - $item->update([ - 'description' => $request->description, - 'ctn' => $request->ctn, - 'qty' => $request->qty, - 'ttl_qty' => $request->ttl_qty, - 'unit' => $request->unit, - 'price' => $request->price, - 'ttl_amount' => $request->ttl_amount, - 'cbm' => $request->cbm, - 'ttl_cbm' => $request->ttl_cbm, - 'kg' => $request->kg, - 'ttl_kg' => $request->ttl_kg, - 'shop_no' => $request->shop_no, - ]); + $request->validate([ + 'description' => 'required|string', + 'ctn' => 'nullable|numeric', + 'qty' => 'nullable|numeric', + 'unit' => 'nullable|string', + 'price' => 'nullable|numeric', + 'cbm' => 'nullable|numeric', + 'kg' => 'nullable|numeric', + 'shop_no' => 'nullable|string', + ]); - $this->recalcTotals($order); - $this->updateInvoiceFromOrder($order); + // ✅ BACKEND CALCULATION + $ctn = (float) ($request->ctn ?? 0); + $qty = (float) ($request->qty ?? 0); + $price = (float) ($request->price ?? 0); + $cbm = (float) ($request->cbm ?? 0); + $kg = (float) ($request->kg ?? 0); + + $item->update([ + 'description' => $request->description, + 'ctn' => $ctn, + 'qty' => $qty, + 'ttl_qty' => $ctn * $qty, + 'unit' => $request->unit, + 'price' => $price, + 'ttl_amount' => ($ctn * $qty) * $price, + 'cbm' => $cbm, + 'ttl_cbm' => $cbm * $ctn, + 'kg' => $kg, + 'ttl_kg' => $ctn * $kg, + 'shop_no' => $request->shop_no, + ]); + + $this->recalcTotals($order); + $this->updateInvoiceFromOrder($order); + + return back()->with('success', 'Item updated successfully'); +} - return back()->with('success', 'Item updated successfully'); - } private function updateInvoiceFromOrder(Order $order) { diff --git a/public/invoices/invoice-INV-2025-000048.pdf b/public/invoices/invoice-INV-2025-000048.pdf new file mode 100644 index 0000000..6bce3a0 Binary files /dev/null and b/public/invoices/invoice-INV-2025-000048.pdf differ diff --git a/resources/views/admin/dashboard.blade.php b/resources/views/admin/dashboard.blade.php index 2f8e362..b3a7eed 100644 --- a/resources/views/admin/dashboard.blade.php +++ b/resources/views/admin/dashboard.blade.php @@ -1569,24 +1569,98 @@ document.addEventListener('DOMContentLoaded', function() { function addRow(index) { const tr = document.createElement('tr'); tr.innerHTML = ` - ${index + 1} - - - - - - - - - - - - - - `; + ${index + 1} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + `; + itemsTableBody.appendChild(tr); } + function calculateRow(row) { + const ctn = parseFloat(row.querySelector('[data-field="ctn"]')?.value) || 0; + const qty = parseFloat(row.querySelector('[data-field="qty"]')?.value) || 0; + const price = parseFloat(row.querySelector('[data-field="price"]')?.value) || 0; + const cbm = parseFloat(row.querySelector('[data-field="cbm"]')?.value) || 0; + const kg = parseFloat(row.querySelector('[data-field="kg"]')?.value) || 0; + + const ttlQty = ctn * qty; + const ttlAmount = ttlQty * price; + const ttlCbm = cbm * ctn; + const ttlKg = ctn * kg; + + row.querySelector('[data-field="ttl_qty"]').value = ttlQty.toFixed(2); + row.querySelector('[data-field="ttl_amount"]').value = ttlAmount.toFixed(2); + row.querySelector('[data-field="ttl_cbm"]').value = ttlCbm.toFixed(3); + row.querySelector('[data-field="ttl_kg"]').value = ttlKg.toFixed(3); + } + + itemsTableBody.addEventListener('input', function (e) { + const row = e.target.closest('tr'); + if (!row) return; + + const calcFields = ['ctn', 'qty', 'price', 'cbm', 'kg']; + if (calcFields.includes(e.target.dataset.field)) { + calculateRow(row); + } + }); + + + function generateDefaultRows() { itemsTableBody.innerHTML = ''; addRow(0); @@ -1971,29 +2045,29 @@ if (uploadExcelBtn && excelInput) { } -function populateItemsTable(items) { - itemsTableBody.innerHTML = ''; + function populateItemsTable(items) { + itemsTableBody.innerHTML = ''; - items.forEach((item, index) => { - addRow(index); - const row = itemsTableBody.children[index]; + items.forEach((item, index) => { + addRow(index); + const row = itemsTableBody.children[index]; - row.querySelector('[data-field="description"]').value = item.description ?? ''; - row.querySelector('[data-field="ctn"]').value = item.ctn ?? ''; - row.querySelector('[data-field="qty"]').value = item.qty ?? ''; - row.querySelector('[data-field="ttl_qty"]').value = item.ttl_qty ?? ''; - row.querySelector('[data-field="unit"]').value = item.unit ?? ''; - row.querySelector('[data-field="price"]').value = item.price ?? ''; - row.querySelector('[data-field="ttl_amount"]').value = item.ttl_amount ?? ''; - row.querySelector('[data-field="cbm"]').value = item.cbm ?? ''; - row.querySelector('[data-field="ttl_cbm"]').value = item.ttl_cbm ?? ''; - row.querySelector('[data-field="kg"]').value = item.kg ?? ''; - row.querySelector('[data-field="ttl_kg"]').value = item.ttl_kg ?? ''; - row.querySelector('[data-field="shop_no"]').value = item.shop_no ?? ''; - }); + row.querySelector('[data-field="description"]').value = item.description ?? ''; + row.querySelector('[data-field="ctn"]').value = item.ctn ?? 0; + row.querySelector('[data-field="qty"]').value = item.qty ?? 0; + row.querySelector('[data-field="unit"]').value = item.unit ?? ''; + row.querySelector('[data-field="price"]').value= item.price ?? 0; + row.querySelector('[data-field="cbm"]').value = item.cbm ?? 0; + row.querySelector('[data-field="kg"]').value = item.kg ?? 0; + row.querySelector('[data-field="shop_no"]').value = item.shop_no ?? ''; + + // 🔥 ALWAYS RECALCULATE + calculateRow(row); + }); + + reindexRows(); + } - reindexRows(); -} });