:: **Zploit** v1.0 | Current Path: **/home/kreativepixelz/www/crm/quatation/**
:: Editing File: update_vendor_invoice.php
<?php include_once("session.php"); include("db.php"); // $conn (mysqli) // Only accept POST if ($_SERVER['REQUEST_METHOD'] !== 'POST') { header("Location: vendor_invoice_list.php"); exit; } try { // --- 1) Read & sanitize POST --- $user_id = $_SESSION['user_id'] ?? 0; $vendor_invoice_id = isset($_POST['vendor_invoice_id']) ? (int)$_POST['vendor_invoice_id'] : 0; $invoice_date = trim($_POST['invoice_date'] ?? ''); $invoice_no = trim($_POST['invoice_no'] ?? ''); $product_name = trim($_POST['product_name'] ?? ''); $vehicle_number = trim($_POST['vehicle_number'] ?? ''); $terms_of_delivery = trim($_POST['terms_of_delivery'] ?? ''); $vendor_id = isset($_POST['vendor_id']) ? (int)$_POST['vendor_id'] : 0; $billing_id = isset($_POST['billing_id']) ? (int)($_POST['billing_id'] ?? 0) : 0; $tax_cgst_id = isset($_POST['tax_cgst']) && $_POST['tax_cgst'] !== '' ? (int)$_POST['tax_cgst'] : null; $tax_sgst_id = isset($_POST['tax_sgst']) && $_POST['tax_sgst'] !== '' ? (int)$_POST['tax_sgst'] : null; // Item arrays $item_ids = $_POST['item_id'] ?? []; $desc_arr = $_POST['description']?? []; $pack_arr = $_POST['pack_type'] ?? []; $qty_arr = $_POST['qty'] ?? []; $wt_arr = $_POST['net_weight'] ?? []; $rate_arr = $_POST['rate'] ?? []; if ($vendor_invoice_id <= 0) { throw new Exception("Invalid invoice ID."); } if (empty($invoice_date) || empty($invoice_no) || $vendor_id <= 0 || $billing_id <= 0) { throw new Exception("Please provide invoice date, invoice number, vendor and billing company."); } // --- 2) Handle vendor_receipt upload (optional) --- $new_receipt_path = null; $delete_old_receipt = false; // fetch current receipt filename (to delete if replaced) $old_receipt = null; $stmt_old = mysqli_prepare($conn, "SELECT vendor_receipt FROM vendor_invoice WHERE id = ? LIMIT 1"); if ($stmt_old) { mysqli_stmt_bind_param($stmt_old, "i", $vendor_invoice_id); mysqli_stmt_execute($stmt_old); mysqli_stmt_bind_result($stmt_old, $old_receipt); mysqli_stmt_fetch($stmt_old); mysqli_stmt_close($stmt_old); } if (isset($_FILES['vendor_receipt']) && $_FILES['vendor_receipt']['error'] !== UPLOAD_ERR_NO_FILE) { if ($_FILES['vendor_receipt']['error'] !== UPLOAD_ERR_OK) { throw new Exception("Error uploading vendor receipt."); } $file_name = $_FILES['vendor_receipt']['name']; $file_tmp = $_FILES['vendor_receipt']['tmp_name']; $file_ext = strtolower(pathinfo($file_name, PATHINFO_EXTENSION)); $allowed_ext = ['pdf','jpg','jpeg','png']; if (!in_array($file_ext, $allowed_ext)) { throw new Exception("Invalid vendor receipt file type. Allowed: PDF, JPG, PNG."); } $upload_dir = __DIR__ . '/uploads/vendor_receipts/'; if (!is_dir($upload_dir)) mkdir($upload_dir, 0777, true); $new_file_name = uniqid('receipt_') . '.' . $file_ext; $destination = $upload_dir . $new_file_name; if (!move_uploaded_file($file_tmp, $destination)) { throw new Exception("Failed to move uploaded vendor receipt."); } // store web-relative path $new_receipt_path = 'uploads/vendor_receipts/' . $new_file_name; $delete_old_receipt = !empty($old_receipt); } // --- 3) Build items to insert/update & compute amounts server-side --- $items_to_process = []; // each item: ['id'=>int,'desc'=>,'pack'=>,'qty'=>, 'net_weight'=>, 'rate'=>, 'amount'=>] $sub_total = 0.0; $countItems = max(count($desc_arr), count($item_ids)); // guard for ($i = 0; $i < $countItems; $i++) { $raw_desc = trim($desc_arr[$i] ?? ''); $raw_pack = trim($pack_arr[$i] ?? ''); $raw_qty = is_numeric($qty_arr[$i] ?? null) ? (float)$qty_arr[$i] : 0.0; $raw_wt = is_numeric($wt_arr[$i] ?? null) ? (float)$wt_arr[$i] : 0.0; $raw_rate = is_numeric($rate_arr[$i] ?? null) ? (float)$rate_arr[$i] : 0.0; $amt = round($raw_wt * $raw_rate, 2); // server rule: net_weight * rate // Skip completely empty rows if ($raw_desc === '' && $amt <= 0) continue; $item_id_val = isset($item_ids[$i]) ? (int)$item_ids[$i] : 0; $items_to_process[] = [ 'id' => $item_id_val, 'description' => $raw_desc, 'pack_type' => $raw_pack, 'qty' => $raw_qty, 'net_weight' => $raw_wt, 'rate' => $raw_rate, 'amount' => $amt ]; $sub_total += $amt; } if (count($items_to_process) === 0) { throw new Exception("Please add at least one invoice item."); } // --- 4) Lookup tax percentages (if provided) and compute tax amounts --- $cgst_percent = 0.0; $sgst_percent = 0.0; if ($tax_cgst_id) { $tstmt = mysqli_prepare($conn, "SELECT tax_percentage FROM taxes WHERE id = ? LIMIT 1"); if ($tstmt) { mysqli_stmt_bind_param($tstmt, "i", $tax_cgst_id); mysqli_stmt_execute($tstmt); mysqli_stmt_bind_result($tstmt, $tp); if (mysqli_stmt_fetch($tstmt)) $cgst_percent = (float)$tp; mysqli_stmt_close($tstmt); } } if ($tax_sgst_id) { $tstmt = mysqli_prepare($conn, "SELECT tax_percentage FROM taxes WHERE id = ? LIMIT 1"); if ($tstmt) { mysqli_stmt_bind_param($tstmt, "i", $tax_sgst_id); mysqli_stmt_execute($tstmt); mysqli_stmt_bind_result($tstmt, $tp2); if (mysqli_stmt_fetch($tstmt)) $sgst_percent = (float)$tp2; mysqli_stmt_close($tstmt); } } $cgst_amount = round(($sub_total * $cgst_percent) / 100, 2); $sgst_amount = round(($sub_total * $sgst_percent) / 100, 2); $grand_total = round($sub_total + $cgst_amount + $sgst_amount, 2); // --- 5) Start DB transaction --- if (!mysqli_begin_transaction($conn)) { throw new Exception("Failed to start DB transaction: " . mysqli_error($conn)); } // --- 6) Update vendor_invoice master row --- // We'll bind many numeric values as strings (safe for DECIMAL) $sql_update_invoice = "UPDATE vendor_invoice SET vendor_id = ?, billing_id = ?, invoice_date = ?, invoice_no = ?, product_name = ?, vehicle_number = ?, terms_of_delivery = ?, sub_total = ?, tax_cgst_id = ?, tax_sgst_id = ?, cgst_amount = ?, sgst_amount = ?, grand_total = ?, vendor_receipt = ? WHERE id = ? LIMIT 1"; $stmt_up = mysqli_prepare($conn, $sql_update_invoice); if (!$stmt_up) throw new Exception("Prepare failed (invoice update): " . mysqli_error($conn)); // Convert decimals to formatted strings $sub_total_str = number_format($sub_total, 2, '.', ''); $cgst_amount_str = number_format($cgst_amount, 2, '.', ''); $sgst_amount_str = number_format($sgst_amount, 2, '.', ''); $grand_total_str = number_format($grand_total, 2, '.', ''); // vendor_receipt path: decide whether to use new or keep old $vendor_receipt_to_save = $new_receipt_path ?? $old_receipt; // if new uploaded, use new; else keep old // bind: vendor_id (i), billing_id (i), then 9 strings, then id (i) $bind_types = "iisssssssssssi"; // 2 ints, 11 strings, 1 int -> total params 14 // Build params array in same order as SQL $bind_params = [ $vendor_id, $billing_id, $invoice_date, $invoice_no, $product_name, $vehicle_number, $terms_of_delivery, $sub_total_str, $tax_cgst_id ?? '', $tax_sgst_id ?? '', $cgst_amount_str, $sgst_amount_str, $grand_total_str, $vendor_receipt_to_save, $vendor_invoice_id ]; // Note: mysqli_stmt_bind_param requires variables, not array directly mysqli_stmt_bind_param($stmt_up, $bind_types, $bind_params[0], $bind_params[1], $bind_params[2], $bind_params[3], $bind_params[4], $bind_params[5], $bind_params[6], $bind_params[7], $bind_params[8], $bind_params[9], $bind_params[10], $bind_params[11], $bind_params[12], $bind_params[13], $bind_params[14] ); if (!mysqli_stmt_execute($stmt_up)) { $err = mysqli_stmt_error($stmt_up); mysqli_stmt_close($stmt_up); throw new Exception("Error updating invoice: " . $err); } mysqli_stmt_close($stmt_up); // --- 7) Process invoice items --- // Fetch existing item IDs for this invoice so we can delete removed ones $existing_ids = []; $q_exist = mysqli_prepare($conn, "SELECT id FROM vendor_invoice_items WHERE vendor_invoice_id = ?"); if ($q_exist) { mysqli_stmt_bind_param($q_exist, "i", $vendor_invoice_id); mysqli_stmt_execute($q_exist); $res_exist = mysqli_stmt_get_result($q_exist); while ($r = mysqli_fetch_assoc($res_exist)) $existing_ids[] = (int)$r['id']; mysqli_stmt_close($q_exist); } $processed_ids = []; // keep track of item ids we updated/inserted // Prepare statements for update and insert $stmt_item_update = mysqli_prepare($conn, "UPDATE vendor_invoice_items SET description = ?, pack_type = ?, qty = ?, net_weight = ?, rate = ?, amount = ? WHERE id = ? LIMIT 1"); if (!$stmt_item_update) throw new Exception("Prepare failed (item update): " . mysqli_error($conn)); $stmt_item_insert = mysqli_prepare($conn, "INSERT INTO vendor_invoice_items (vendor_invoice_id, description, pack_type, qty, net_weight, rate, amount) VALUES (?, ?, ?, ?, ?, ?, ?)"); if (!$stmt_item_insert) throw new Exception("Prepare failed (item insert): " . mysqli_error($conn)); $stmt_item_delete = mysqli_prepare($conn, "DELETE FROM vendor_invoice_items WHERE id = ? LIMIT 1"); if (!$stmt_item_delete) throw new Exception("Prepare failed (item delete): " . mysqli_error($conn)); foreach ($items_to_process as $it) { $it_id = (int)$it['id']; $desc = $it['description']; $pack = $it['pack_type']; $qty = number_format((float)$it['qty'], 2, '.', ''); $nwt = number_format((float)$it['net_weight'], 2, '.', ''); $ratef = number_format((float)$it['rate'], 2, '.', ''); $amtf = number_format((float)$it['amount'], 2, '.', ''); if ($it_id > 0) { // Update existing row mysqli_stmt_bind_param($stmt_item_update, "ssdddis", $desc, $pack, $qty, $nwt, $ratef, $amtf, $it_id); // Note: "ssdddis" means two strings then 4 doubles/strings and one integer - but mysqli bind types must be correct. // However to stay safe (DECIMAL can be passed as string), we'll bind as: "ssssssi" (6 strings + id int) // Re-bind properly: mysqli_stmt_bind_param($stmt_item_update, "ssssssi", $desc, $pack, $qty, $nwt, $ratef, $amtf, $it_id); if (!mysqli_stmt_execute($stmt_item_update)) { $err = mysqli_stmt_error($stmt_item_update); mysqli_stmt_close($stmt_item_update); throw new Exception("Error updating item (ID {$it_id}): " . $err); } $processed_ids[] = $it_id; } else { // Insert new row mysqli_stmt_bind_param($stmt_item_insert, "issssss", $vendor_invoice_id, $desc, $pack, $qty, $nwt, $ratef, $amtf); if (!mysqli_stmt_execute($stmt_item_insert)) { $err = mysqli_stmt_error($stmt_item_insert); mysqli_stmt_close($stmt_item_insert); throw new Exception("Error inserting item: " . $err); } $new_item_id = mysqli_insert_id($conn); $processed_ids[] = (int)$new_item_id; } } // Close item statements mysqli_stmt_close($stmt_item_update); mysqli_stmt_close($stmt_item_insert); // Delete items that existed earlier but weren't in processed_ids (i.e., were removed by user) $to_delete = array_diff($existing_ids, $processed_ids); if (count($to_delete) > 0) { foreach ($to_delete as $del_id) { mysqli_stmt_bind_param($stmt_item_delete, "i", $del_id); if (!mysqli_stmt_execute($stmt_item_delete)) { $err = mysqli_stmt_error($stmt_item_delete); mysqli_stmt_close($stmt_item_delete); throw new Exception("Error deleting item ID {$del_id}: " . $err); } } } mysqli_stmt_close($stmt_item_delete); // --- 8) Commit transaction --- mysqli_commit($conn); // Delete old receipt file if replaced if ($delete_old_receipt && !empty($old_receipt) && $new_receipt_path) { $old_path = __DIR__ . '/' . $old_receipt; if (file_exists($old_path)) @unlink($old_path); } // Optional: Insert notification $notif_stmt = mysqli_prepare($conn, "INSERT INTO notifications (user_id, message, type) VALUES (?, ?, ?)"); if ($notif_stmt) { $msg = "✅ Vendor Invoice updated (ID: {$vendor_invoice_id}, No: {$invoice_no})"; $type = "info"; mysqli_stmt_bind_param($notif_stmt, "iss", $user_id, $msg, $type); mysqli_stmt_execute($notif_stmt); mysqli_stmt_close($notif_stmt); } $_SESSION['success'] = "Vendor invoice updated successfully."; header("Location: vendor_invoice_list.php"); exit; } catch (Exception $e) { // Rollback and cleanup if (isset($conn) && $conn) mysqli_rollback($conn); // If we uploaded a new receipt but the process failed, delete it if (!empty($new_receipt_path) && file_exists(__DIR__ . '/' . $new_receipt_path)) { @unlink(__DIR__ . '/' . $new_receipt_path); } $_SESSION['error'] = "Error updating invoice: " . $e->getMessage(); header("Location: edit_vendor_invoice.php?id=" . urlencode($vendor_invoice_id)); exit; }