From 99e2487d2b199a3daa30a8530938be5e2d7cd8ef Mon Sep 17 00:00:00 2001 From: johnnyq Date: Fri, 28 Nov 2025 14:14:46 -0500 Subject: [PATCH] Fix dupe race condition with ticket, invoice, quote, project, recurring ticket numbering when being created in parallel Atomically update and get the next ticket number in one SQL query everywhere. --- agent/modals/quote/quote_to_invoice.php | 9 +- agent/post/client.php | 118 ++++++++------- agent/post/invoice.php | 64 +++++---- agent/post/project.php | 31 ++-- agent/post/quote.php | 63 +++++--- agent/post/recurring_invoice.php | 58 +++++--- agent/post/recurring_ticket.php | 29 ++-- agent/post/ticket.php | 37 ++--- agent/ticket.php | 12 +- api/v1/tickets/create.php | 14 +- client/post.php | 184 ++++++++++++------------ cron/cron.php | 31 ++-- cron/ticket_email_parser.php | 14 +- 13 files changed, 378 insertions(+), 286 deletions(-) diff --git a/agent/modals/quote/quote_to_invoice.php b/agent/modals/quote/quote_to_invoice.php index d943cf57..77a5acc5 100644 --- a/agent/modals/quote/quote_to_invoice.php +++ b/agent/modals/quote/quote_to_invoice.php @@ -9,10 +9,9 @@
- - +
- \ No newline at end of file + diff --git a/agent/post/client.php b/agent/post/client.php index a5ef8ee1..fdde8742 100644 --- a/agent/post/client.php +++ b/agent/post/client.php @@ -237,7 +237,7 @@ if (isset($_POST['add_client'])) { $client_id ); mysqli_stmt_execute($query); - + $extended_log_description .= ", SSL certificate $website added"; } } @@ -245,7 +245,7 @@ if (isset($_POST['add_client'])) { logAction("Client", "Create", "$session_name created client $name$extended_log_description", $client_id, $client_id); flash_alert("Client $name created"); - + redirect(); } @@ -774,10 +774,16 @@ if (isset($_POST['bulk_add_client_ticket'])) { $client_name = sanitizeInput($row['client_name']); - // Get the next Ticket Number and update the config - $sql_ticket_number = mysqli_query($mysqli, "SELECT config_ticket_next_number FROM settings WHERE company_id = 1"); - $ticket_number_row = mysqli_fetch_array($sql_ticket_number); - $ticket_number = intval($ticket_number_row['config_ticket_next_number']); + // Atomically increment and get the new ticket number + mysqli_query($mysqli, " + UPDATE settings + SET + config_ticket_next_number = LAST_INSERT_ID(config_ticket_next_number), + config_ticket_next_number = config_ticket_next_number + 1 + WHERE company_id = 1 + "); + + $ticket_number = mysqli_insert_id($mysqli); // Sanitize Config Vars from get_settings.php and Session Vars from check_login.php $config_ticket_prefix = sanitizeInput($config_ticket_prefix); @@ -788,18 +794,10 @@ if (isset($_POST['bulk_add_client_ticket'])) { //Generate a unique URL key for clients to access $url_key = randomString(156); - // Increment the config ticket next number - $new_config_ticket_next_number = $ticket_number + 1; - - mysqli_query($mysqli, "UPDATE settings SET config_ticket_next_number = $new_config_ticket_next_number WHERE company_id = 1"); - mysqli_query($mysqli, "INSERT INTO tickets SET ticket_prefix = '$config_ticket_prefix', ticket_number = $ticket_number, ticket_category = $category_id, ticket_subject = '$subject', ticket_details = '$details', ticket_priority = '$priority', ticket_billable = $billable, ticket_status = $ticket_status, ticket_created_by = $session_user_id, ticket_assigned_to = $assigned_to, ticket_url_key = '$url_key', ticket_client_id = $client_id, ticket_project_id = $project_id"); $ticket_id = mysqli_insert_id($mysqli); - // Update the next ticket number in the database - mysqli_query($mysqli, "UPDATE settings SET config_ticket_next_number = $new_config_ticket_next_number WHERE company_id = 1"); - // Add Tasks if (!empty($_POST['tasks'])) { foreach ($_POST['tasks'] as $task) { @@ -1021,8 +1019,8 @@ if (isset($_POST['bulk_send_client_email']) && isset($_POST['client_ids'])) { $client_ids_str = implode(',', $client_ids); // SQL to fetch matching contacts - $sql = "SELECT * FROM contacts - WHERE contact_client_id IN ($client_ids_str) + $sql = "SELECT * FROM contacts + WHERE contact_client_id IN ($client_ids_str) $contact_filter_query"; $result = mysqli_query($mysqli, $sql); @@ -1181,7 +1179,7 @@ if (isset($_POST["export_client_pdf"])) { logAction("Client", "Export", "$session_name exported client data to a PDF file", $client_id, $client_id); // Get client record (joining primary contact and primary location) - $sql = mysqli_query($mysqli, "SELECT * FROM clients + $sql = mysqli_query($mysqli, "SELECT * FROM clients LEFT JOIN contacts ON clients.client_id = contacts.contact_client_id AND contact_primary = 1 LEFT JOIN locations ON clients.client_id = locations.location_client_id AND location_primary = 1 WHERE client_id = $client_id @@ -1208,53 +1206,53 @@ if (isset($_POST["export_client_pdf"])) { $sql_locations = mysqli_query($mysqli, "SELECT * FROM locations WHERE location_client_id = $client_id AND location_archived_at IS NULL ORDER BY location_name ASC"); $sql_vendors = mysqli_query($mysqli, "SELECT * FROM vendors WHERE vendor_client_id = $client_id AND vendor_archived_at IS NULL ORDER BY vendor_name ASC"); $sql_credentials = mysqli_query($mysqli, "SELECT * FROM credentials WHERE credential_client_id = $client_id ORDER BY credential_name ASC"); - $sql_assets = mysqli_query($mysqli, "SELECT * FROM assets - LEFT JOIN contacts ON asset_contact_id = contact_id + $sql_assets = mysqli_query($mysqli, "SELECT * FROM assets + LEFT JOIN contacts ON asset_contact_id = contact_id LEFT JOIN locations ON asset_location_id = location_id LEFT JOIN asset_interfaces ON interface_asset_id = asset_id AND interface_primary = 1 WHERE asset_client_id = $client_id AND asset_archived_at IS NULL ORDER BY asset_type ASC" ); - $sql_asset_workstations = mysqli_query($mysqli, "SELECT * FROM assets - LEFT JOIN contacts ON asset_contact_id = contact_id - LEFT JOIN locations ON asset_location_id = location_id - LEFT JOIN asset_interfaces ON interface_asset_id = asset_id AND interface_primary = 1 - WHERE asset_client_id = $client_id - AND (asset_type = 'desktop' OR asset_type = 'laptop') - AND asset_archived_at IS NULL + $sql_asset_workstations = mysqli_query($mysqli, "SELECT * FROM assets + LEFT JOIN contacts ON asset_contact_id = contact_id + LEFT JOIN locations ON asset_location_id = location_id + LEFT JOIN asset_interfaces ON interface_asset_id = asset_id AND interface_primary = 1 + WHERE asset_client_id = $client_id + AND (asset_type = 'desktop' OR asset_type = 'laptop') + AND asset_archived_at IS NULL ORDER BY asset_name ASC" ); - $sql_asset_servers = mysqli_query($mysqli, "SELECT * FROM assets - LEFT JOIN locations ON asset_location_id = location_id - LEFT JOIN asset_interfaces ON interface_asset_id = asset_id AND interface_primary = 1 - WHERE asset_client_id = $client_id - AND asset_type = 'server' - AND asset_archived_at IS NULL + $sql_asset_servers = mysqli_query($mysqli, "SELECT * FROM assets + LEFT JOIN locations ON asset_location_id = location_id + LEFT JOIN asset_interfaces ON interface_asset_id = asset_id AND interface_primary = 1 + WHERE asset_client_id = $client_id + AND asset_type = 'server' + AND asset_archived_at IS NULL ORDER BY asset_name ASC" ); - $sql_asset_vms = mysqli_query($mysqli, "SELECT * FROM assets - LEFT JOIN asset_interfaces ON interface_asset_id = asset_id AND interface_primary = 1 - WHERE asset_client_id = $client_id - AND asset_type = 'virtual machine' - AND asset_archived_at IS NULL + $sql_asset_vms = mysqli_query($mysqli, "SELECT * FROM assets + LEFT JOIN asset_interfaces ON interface_asset_id = asset_id AND interface_primary = 1 + WHERE asset_client_id = $client_id + AND asset_type = 'virtual machine' + AND asset_archived_at IS NULL ORDER BY asset_name ASC" ); - $sql_asset_network = mysqli_query($mysqli, "SELECT * FROM assets - LEFT JOIN locations ON asset_location_id = location_id - LEFT JOIN asset_interfaces ON interface_asset_id = asset_id AND interface_primary = 1 - WHERE asset_client_id = $client_id - AND (asset_type = 'Firewall/Router' OR asset_type = 'Switch' OR asset_type = 'Access Point') - AND asset_archived_at IS NULL + $sql_asset_network = mysqli_query($mysqli, "SELECT * FROM assets + LEFT JOIN locations ON asset_location_id = location_id + LEFT JOIN asset_interfaces ON interface_asset_id = asset_id AND interface_primary = 1 + WHERE asset_client_id = $client_id + AND (asset_type = 'Firewall/Router' OR asset_type = 'Switch' OR asset_type = 'Access Point') + AND asset_archived_at IS NULL ORDER BY asset_type ASC" ); - $sql_asset_other = mysqli_query($mysqli, "SELECT * FROM assets - LEFT JOIN contacts ON asset_contact_id = contact_id - LEFT JOIN locations ON asset_location_id = location_id - LEFT JOIN asset_interfaces ON interface_asset_id = asset_id AND interface_primary = 1 - WHERE asset_client_id = $client_id - AND (asset_type NOT LIKE 'laptop' AND asset_type NOT LIKE 'desktop' AND asset_type NOT LIKE 'server' AND asset_type NOT LIKE 'virtual machine' AND asset_type NOT LIKE 'firewall/router' AND asset_type NOT LIKE 'switch' AND asset_type NOT LIKE 'access point') - AND asset_archived_at IS NULL + $sql_asset_other = mysqli_query($mysqli, "SELECT * FROM assets + LEFT JOIN contacts ON asset_contact_id = contact_id + LEFT JOIN locations ON asset_location_id = location_id + LEFT JOIN asset_interfaces ON interface_asset_id = asset_id AND interface_primary = 1 + WHERE asset_client_id = $client_id + AND (asset_type NOT LIKE 'laptop' AND asset_type NOT LIKE 'desktop' AND asset_type NOT LIKE 'server' AND asset_type NOT LIKE 'virtual machine' AND asset_type NOT LIKE 'firewall/router' AND asset_type NOT LIKE 'switch' AND asset_type NOT LIKE 'access point') + AND asset_archived_at IS NULL ORDER BY asset_type ASC" ); $sql_networks = mysqli_query($mysqli, "SELECT * FROM networks WHERE network_client_id = $client_id AND network_archived_at IS NULL ORDER BY network_name ASC"); @@ -1263,38 +1261,38 @@ if (isset($_POST["export_client_pdf"])) { $sql_software = mysqli_query($mysqli, "SELECT * FROM software WHERE software_client_id = $client_id AND software_archived_at IS NULL ORDER BY software_name ASC"); $sql_user_licenses = mysqli_query($mysqli, " - SELECT + SELECT contact_name, software_name - FROM + FROM software_contacts - JOIN + JOIN contacts ON software_contacts.contact_id = contacts.contact_id - JOIN + JOIN software ON software_contacts.software_id = software.software_id WHERE software_archived_at IS NULL AND contact_archived_at IS NULL AND software_client_id = $client_id AND contact_client_id = $client_id - ORDER BY + ORDER BY contact_name, software_name;" ); $sql_asset_licenses = mysqli_query($mysqli, " - SELECT + SELECT asset_name, software_name - FROM + FROM software_assets - JOIN + JOIN assets ON software_assets.asset_id = assets.asset_id - JOIN + JOIN software ON software_assets.software_id = software.software_id WHERE software_archived_at IS NULL AND asset_archived_at IS NULL AND software_client_id = $client_id AND asset_client_id = $client_id - ORDER BY + ORDER BY asset_name, software_name;" ); diff --git a/agent/post/invoice.php b/agent/post/invoice.php index affc1945..4d2b4716 100644 --- a/agent/post/invoice.php +++ b/agent/post/invoice.php @@ -17,16 +17,22 @@ if (isset($_POST['add_invoice'])) { // Get Net Terms $client_net_terms = intval(getFieldById('clients', $client_id, 'client_net_terms')); - //Get the last Invoice Number and add 1 for the new invoice number - $invoice_number = $config_invoice_next_number; - $new_config_invoice_next_number = $config_invoice_next_number + 1; - mysqli_query($mysqli,"UPDATE settings SET config_invoice_next_number = $new_config_invoice_next_number WHERE company_id = 1"); + // Atomically increment and get the new invoice number + mysqli_query($mysqli, " + UPDATE settings + SET + config_invoice_next_number = LAST_INSERT_ID(config_invoice_next_number), + config_invoice_next_number = config_invoice_next_number + 1 + WHERE company_id = 1 + "); + + $invoice_number = mysqli_insert_id($mysqli); //Generate a unique URL key for clients to access $url_key = randomString(156); mysqli_query($mysqli,"INSERT INTO invoices SET invoice_prefix = '$config_invoice_prefix', invoice_number = $invoice_number, invoice_scope = '$scope', invoice_date = '$date', invoice_due = DATE_ADD('$date', INTERVAL $client_net_terms day), invoice_discount_amount = '$invoice_discount', invoice_amount = '$invoice_amount', invoice_currency_code = '$session_company_currency', invoice_category_id = $category, invoice_status = 'Draft', invoice_url_key = '$url_key', invoice_client_id = $client_id"); - + $invoice_id = mysqli_insert_id($mysqli); mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Draft', history_description = 'Invoice created', history_invoice_id = $invoice_id"); @@ -81,16 +87,9 @@ if (isset($_POST['add_invoice_copy'])) { $date = sanitizeInput($_POST['date']); //Get Net Terms - $sql = mysqli_query($mysqli,"SELECT client_net_terms FROM clients, invoices WHERE client_id = invoice_client_id AND invoice_id = $invoice_id"); + $sql = mysqli_query($mysqli,"SELECT * FROM clients, invoices WHERE client_id = invoice_client_id AND invoice_id = $invoice_id"); $row = mysqli_fetch_array($sql); $client_net_terms = intval($row['client_net_terms']); - - $new_invoice_number = $config_invoice_next_number; - $new_config_invoice_next_number = $config_invoice_next_number + 1; - mysqli_query($mysqli,"UPDATE settings SET config_invoice_next_number = $new_config_invoice_next_number WHERE company_id = 1"); - - $sql = mysqli_query($mysqli,"SELECT * FROM invoices WHERE invoice_id = $invoice_id"); - $row = mysqli_fetch_array($sql); $invoice_scope = sanitizeInput($row['invoice_scope']); $invoice_discount_amount = floatval($row['invoice_discount_amount']); $invoice_amount = floatval($row['invoice_amount']); @@ -101,6 +100,17 @@ if (isset($_POST['add_invoice_copy'])) { $old_invoice_prefix = sanitizeInput($row['invoice_prefix']); $old_invoice_number = intval($row['invoice_number']); + // Atomically increment and get the new invoice number + mysqli_query($mysqli, " + UPDATE settings + SET + config_invoice_next_number = LAST_INSERT_ID(config_invoice_next_number), + config_invoice_next_number = config_invoice_next_number + 1 + WHERE company_id = 1 + "); + + $new_invoice_number = mysqli_insert_id($mysqli); + //Generate a unique URL key for clients to access $url_key = randomString(156); @@ -206,7 +216,7 @@ if (isset($_GET['cancel_invoice'])) { } if (isset($_GET['delete_invoice'])) { - + $invoice_id = intval($_GET['delete_invoice']); // Get Invoice Number and Prefix and Client ID for Logging @@ -251,7 +261,7 @@ if (isset($_GET['delete_invoice'])) { } if (isset($_POST['add_invoice_item'])) { - + enforceUserPermission('module_sales', 2); $invoice_id = intval($_POST['invoice_id']); @@ -264,7 +274,7 @@ if (isset($_POST['add_invoice_item'])) { $product_id = intval($_POST['product_id']); $subtotal = $price * $qty; - + // Update Product Inventory if ($product_id) { // Only enforce stock for tangible products @@ -334,7 +344,7 @@ if (isset($_POST['add_invoice_item'])) { } if (isset($_POST['invoice_note'])) { - + enforceUserPermission('module_sales', 2); $invoice_id = intval($_POST['invoice_id']); @@ -358,7 +368,7 @@ if (isset($_POST['invoice_note'])) { } if (isset($_POST['edit_item'])) { - + enforceUserPermission('module_sales', 2); $item_id = intval($_POST['item_id']); @@ -405,7 +415,7 @@ if (isset($_POST['edit_item'])) { $row = mysqli_fetch_array($sql_invoice_total); $new_invoice_amount = floatval($row['invoice_total']) - $invoice_discount; - + mysqli_query($mysqli,"UPDATE invoices SET invoice_amount = $new_invoice_amount WHERE invoice_id = $invoice_id"); @@ -458,7 +468,7 @@ if (isset($_POST['edit_item'])) { } if (isset($_GET['delete_invoice_item'])) { - + enforceUserPermission('module_sales', 2); $item_id = intval($_GET['delete_invoice_item']); @@ -499,7 +509,7 @@ if (isset($_GET['delete_invoice_item'])) { } if (isset($_GET['email_invoice'])) { - + $invoice_id = intval($_GET['email_invoice']); $sql = mysqli_query($mysqli,"SELECT * FROM invoices @@ -577,7 +587,7 @@ if (isset($_GET['email_invoice'])) { $email_id = mysqli_insert_id($mysqli); flash_alert("Invoice sent!"); - + mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Sent', history_description = 'Invoice sent to the mail queue ID: $email_id', history_invoice_id = $invoice_id"); // Don't change the status to sent if the status is anything but draft @@ -627,7 +637,7 @@ if (isset($_GET['email_invoice'])) { if (isset($_POST['export_invoices_csv'])) { enforceUserPermission('module_sales'); - + if (isset($_POST['client_id'])) { $client_id = intval($_POST['client_id']); $client_query = "AND invoice_client_id = $client_id"; @@ -690,7 +700,7 @@ if (isset($_POST['export_invoices_csv'])) { } if (isset($_POST['link_invoice_to_ticket'])) { - + $invoice_id = intval($_POST['invoice_id']); $ticket_id = intval($_POST['ticket_id']); @@ -703,7 +713,7 @@ if (isset($_POST['link_invoice_to_ticket'])) { } if (isset($_POST['add_ticket_to_invoice'])) { - + $invoice_id = intval($_POST['invoice_id']); $ticket_id = intval($_POST['ticket_id']); @@ -874,7 +884,7 @@ if (isset($_GET['export_invoice_pdf'])) { // Load items $sub_total = 0; $total_tax = 0; - + $sql_items = mysqli_query($mysqli, "SELECT * FROM invoice_items WHERE item_invoice_id = $invoice_id ORDER BY item_order ASC"); while ($item = mysqli_fetch_array($sql_items)) { $name = $item['item_name']; @@ -933,7 +943,7 @@ if (isset($_GET['export_invoice_pdf'])) { $filename = preg_replace('/[^A-Za-z0-9_\-]/', '_', "{$invoice_date}_{$company_name}_{$client_name}_Invoice_{$invoice_prefix}{$invoice_number}"); $pdf->Output("$filename.pdf", 'I'); - + exit; } diff --git a/agent/post/project.php b/agent/post/project.php index c1c529e5..80ad0dbe 100644 --- a/agent/post/project.php +++ b/agent/post/project.php @@ -20,11 +20,16 @@ if (isset($_POST['add_project'])) { // Sanitize Project Prefix $config_project_prefix = sanitizeInput($config_project_prefix); - // Get the next Project Number and add 1 for the new Project number - $project_number = $config_project_next_number; - $new_config_project_next_number = $config_project_next_number + 1; + // Atomically increment and get the new project number + mysqli_query($mysqli, " + UPDATE settings + SET + config_project_next_number = LAST_INSERT_ID(config_project_next_number), + config_project_next_number = config_project_next_number + 1 + WHERE company_id = 1 + "); - mysqli_query($mysqli, "UPDATE settings SET config_project_next_number = $new_config_project_next_number WHERE company_id = 1"); + $project_number = mysqli_insert_id($mysqli); mysqli_query($mysqli, "INSERT INTO projects SET project_prefix = '$config_project_prefix', project_number = $project_number, project_name = '$project_name', project_description = '$project_description', project_due = '$due_date', project_manager = $project_manager, project_client_id = $client_id"); @@ -44,15 +49,19 @@ if (isset($_POST['add_project'])) { $ticket_template_subject = sanitizeInput($row['ticket_template_subject']); $ticket_template_details = mysqli_escape_string($mysqli, $row['ticket_template_details']); - // Get the next Ticket Number and add 1 for the new ticket number - $ticket_number = $config_ticket_next_number; - $new_config_ticket_next_number = $config_ticket_next_number + 1; - mysqli_query($mysqli, "UPDATE settings SET config_ticket_next_number = $new_config_ticket_next_number WHERE company_id = 1"); + // Atomically increment and get the new ticket number + mysqli_query($mysqli, " + UPDATE settings + SET + config_ticket_next_number = LAST_INSERT_ID(config_ticket_next_number), + config_ticket_next_number = config_ticket_next_number + 1 + WHERE company_id = 1 + "); + + $ticket_number = mysqli_insert_id($mysqli); mysqli_query($mysqli, "INSERT INTO tickets SET ticket_prefix = '$config_ticket_prefix', ticket_number = $ticket_number, ticket_subject = '$ticket_template_subject', ticket_details = '$ticket_template_details', ticket_priority = 'Low', ticket_status = 1, ticket_created_by = $session_user_id, ticket_client_id = $client_id, ticket_project_id = $project_id"); - $config_ticket_next_number = $config_ticket_next_number + 1; - $ticket_id = mysqli_insert_id($mysqli); // Task Templates for Ticket template and add the to the ticket @@ -263,7 +272,7 @@ if (isset($_POST['link_closed_ticket_to_project'])) { logAction("Project", "Edit", "$session_name added ticket $ticket_prefix$ticket_number - $ticket_subject to project $project_name", $client_id, $project_id); flash_alert("Ticket added to $project_name"); - + redirect(); } diff --git a/agent/post/quote.php b/agent/post/quote.php index 6a8695a0..cc306f16 100644 --- a/agent/post/quote.php +++ b/agent/post/quote.php @@ -14,10 +14,16 @@ if (isset($_POST['add_quote'])) { $client_id = intval($_POST['client']); - //Get the last Quote Number and add 1 for the new Quote number - $quote_number = $config_quote_next_number; - $new_config_quote_next_number = $config_quote_next_number + 1; - mysqli_query($mysqli,"UPDATE settings SET config_quote_next_number = $new_config_quote_next_number WHERE company_id = 1"); + // Atomically increment and get the new quote number + mysqli_query($mysqli, " + UPDATE settings + SET + config_quote_next_number = LAST_INSERT_ID(config_quote_next_number), + config_quote_next_number = config_quote_next_number + 1 + WHERE company_id = 1 + "); + + $quote_number = mysqli_insert_id($mysqli); //Generate a unique URL key for clients to access $quote_url_key = randomString(156); @@ -49,10 +55,16 @@ if (isset($_POST['add_quote_copy'])) { $config_quote_prefix = sanitizeInput($config_quote_prefix); - //Get the last Invoice Number and add 1 for the new invoice number - $quote_number = $config_quote_next_number; - $new_config_quote_next_number = $config_quote_next_number + 1; - mysqli_query($mysqli,"UPDATE settings SET config_quote_next_number = $new_config_quote_next_number WHERE company_id = 1"); + // Atomically increment and get the new quote number + mysqli_query($mysqli, " + UPDATE settings + SET + config_quote_next_number = LAST_INSERT_ID(config_quote_next_number), + config_quote_next_number = config_quote_next_number + 1 + WHERE company_id = 1 + "); + + $quote_number = mysqli_insert_id($mysqli); $sql = mysqli_query($mysqli,"SELECT * FROM quotes WHERE quote_id = $quote_id"); $row = mysqli_fetch_array($sql); @@ -106,16 +118,10 @@ if (isset($_POST['add_quote_to_invoice'])) { $quote_id = intval($_POST['quote_id']); $date = sanitizeInput($_POST['date']); - $client_net_terms = intval($_POST['client_net_terms']); - $config_invoice_prefix = sanitizeInput($config_invoice_prefix); - - $invoice_number = $config_invoice_next_number; - $new_config_invoice_next_number = $config_invoice_next_number + 1; - mysqli_query($mysqli,"UPDATE settings SET config_invoice_next_number = $new_config_invoice_next_number WHERE company_id = 1"); - - $sql = mysqli_query($mysqli,"SELECT * FROM quotes WHERE quote_id = $quote_id"); + $sql = mysqli_query($mysqli,"SELECT * FROM clients, quotes WHERE client_id = quote_client_id AND quote_id = $quote_id"); $row = mysqli_fetch_array($sql); + $client_net_terms = intval($row['client_net_terms']); $quote_prefix = sanitizeInput($row['quote_prefix']); $quote_number = sanitizeInput($row['quote_number']); $quote_discount_amount = floatval($row['quote_discount_amount']); @@ -127,6 +133,19 @@ if (isset($_POST['add_quote_to_invoice'])) { $client_id = intval($row['quote_client_id']); $category_id = intval($row['quote_category_id']); + $config_invoice_prefix = sanitizeInput($config_invoice_prefix); + + // Atomically increment and get the new invoice number + mysqli_query($mysqli, " + UPDATE settings + SET + config_invoice_next_number = LAST_INSERT_ID(config_invoice_next_number), + config_invoice_next_number = config_invoice_next_number + 1 + WHERE company_id = 1 + "); + + $invoice_number = mysqli_insert_id($mysqli); + //Generate a unique URL key for clients to access $url_key = randomString(156); @@ -153,7 +172,7 @@ if (isset($_POST['add_quote_to_invoice'])) { } mysqli_query($mysqli,"UPDATE quotes SET quote_status = 'Invoiced' WHERE quote_id = $quote_id"); - + mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Invoiced', history_description = 'Quote invoiced as $config_invoice_prefix$invoice_number', history_quote_id = $quote_id"); logAction("Invoice", "Create", "$session_name created invoice $config_invoice_prefix$invoice_number from quote $config_quote_prefix$quote_number", $client_id, $new_invoice_id); @@ -339,7 +358,7 @@ if (isset($_GET['delete_quote_item'])) { $quote_prefix = sanitizeInput($row['quote_prefix']); $quote_number = sanitizeInput($row['quote_number']); $client_id = intval($row['quote_client_id']); - + $new_quote_amount = floatval($row['quote_amount']) - $item_total; mysqli_query($mysqli,"UPDATE quotes SET quote_amount = $new_quote_amount WHERE quote_id = $quote_id"); @@ -494,7 +513,7 @@ if (isset($_GET['email_quote'])) { // Update History mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Sent', history_description = 'Emailed Quote', history_quote_id = $quote_id"); - + logAction("Quote", "Email", "$session_name emailed quote $quote_prefix$quote_number to $contact_email", $client_id, $quote_id); flash_alert("Quote sent!"); @@ -549,7 +568,7 @@ if(isset($_POST['export_quotes_csv'])){ } $sql = mysqli_query($mysqli,"SELECT * FROM quotes $client_query ORDER BY quote_number ASC"); - + $num_rows = mysqli_num_rows($sql); if($num_rows > 0){ @@ -581,7 +600,7 @@ if(isset($_POST['export_quotes_csv'])){ //output all remaining data on a file pointer fpassthru($f); } - + logAction("Quote", "Export", "$session_name exported $num_rows quote(s) to a CSV file"); flash_alert("Exported $num_rows quote(s)"); @@ -796,7 +815,7 @@ if (isset($_GET['export_quote_pdf'])) { $filename = preg_replace('/[^A-Za-z0-9_\-]/', '_', "{$quote_date}_{$company_name}_{$client_name}_Quote_{$quote_prefix}{$quote_number}"); $pdf->Output("$filename.pdf", 'I'); - + exit; } diff --git a/agent/post/recurring_invoice.php b/agent/post/recurring_invoice.php index a2334a31..12f783f9 100644 --- a/agent/post/recurring_invoice.php +++ b/agent/post/recurring_invoice.php @@ -23,10 +23,16 @@ if (isset($_POST['add_invoice_recurring'])) { $client_id = intval($row['invoice_client_id']); $category_id = intval($row['invoice_category_id']); - //Get the last Recurring Invoice Number and add 1 for the new Recurring Invoice number - $recurring_invoice_number = $config_recurring_invoice_next_number; - $new_config_recurring_invoice_next_number = $config_recurring_invoice_next_number + 1; - mysqli_query($mysqli,"UPDATE settings SET config_recurring_invoice_next_number = $new_config_recurring_invoice_next_number WHERE company_id = 1"); + // Atomically increment and get the new recurring_invoice number + mysqli_query($mysqli, " + UPDATE settings + SET + config_recurring_invoice_next_number = LAST_INSERT_ID(config_recurring_invoice_next_number), + config_recurring_invoice_next_number = config_recurring_invoice_next_number + 1 + WHERE company_id = 1 + "); + + $recurring_invoice_number = mysqli_insert_id($mysqli); mysqli_query($mysqli,"INSERT INTO recurring_invoices SET recurring_invoice_prefix = '$config_recurring_invoice_prefix', recurring_invoice_number = $recurring_invoice_number, recurring_invoice_scope = '$invoice_scope', recurring_invoice_frequency = '$recurring_invoice_frequency', recurring_invoice_next_date = DATE_ADD('$invoice_date', INTERVAL 1 $recurring_invoice_frequency), recurring_invoice_status = 1, recurring_invoice_amount = $invoice_amount, recurring_invoice_currency_code = '$invoice_currency_code', recurring_invoice_note = '$invoice_note', recurring_invoice_category_id = $category_id, recurring_invoice_client_id = $client_id"); @@ -66,10 +72,16 @@ if (isset($_POST['add_recurring_invoice'])) { $category = intval($_POST['category']); $scope = sanitizeInput($_POST['scope']); - //Get the last Recurring Number and add 1 for the new Recurring number - $recurring_invoice_number = $config_recurring_invoice_next_number; - $new_config_recurring_invoice_next_number = $config_recurring_invoice_next_number + 1; - mysqli_query($mysqli,"UPDATE settings SET config_recurring_invoice_next_number = $new_config_recurring_invoice_next_number WHERE company_id = 1"); + // Atomically increment and get the new recurring_invoice number + mysqli_query($mysqli, " + UPDATE settings + SET + config_recurring_invoice_next_number = LAST_INSERT_ID(config_recurring_invoice_next_number), + config_recurring_invoice_next_number = config_recurring_invoice_next_number + 1 + WHERE company_id = 1 + "); + + $recurring_invoice_number = mysqli_insert_id($mysqli); mysqli_query($mysqli,"INSERT INTO recurring_invoices SET recurring_invoice_prefix = '$config_recurring_invoice_prefix', recurring_invoice_number = $recurring_invoice_number, recurring_invoice_scope = '$scope', recurring_invoice_frequency = '$frequency', recurring_invoice_next_date = '$start_date', recurring_invoice_category_id = $category, recurring_invoice_status = 1, recurring_invoice_currency_code = '$session_company_currency', recurring_invoice_client_id = $client_id"); @@ -124,7 +136,7 @@ if (isset($_POST['edit_recurring_invoice'])) { } if (isset($_GET['delete_recurring_invoice'])) { - + $recurring_invoice_id = intval($_GET['delete_recurring_invoice']); // Get Recurring Invoice Details and Client ID for Logging @@ -234,7 +246,7 @@ if (isset($_POST['recurring_invoice_note'])) { } if (isset($_GET['delete_recurring_invoice_item'])) { - + $item_id = intval($_GET['delete_recurring_invoice_item']); $sql = mysqli_query($mysqli,"SELECT * FROM invoice_items WHERE item_id = $item_id"); @@ -250,7 +262,7 @@ if (isset($_GET['delete_recurring_invoice_item'])) { $recurring_invoice_prefix = sanitizeInput($row['recurring_invoice_prefix']); $recurring_invoice_number = intval($row['recurring_invoice_number']); $client_id = intval($row['recurring_invoice_client_id']); - + $new_recurring_invoice_amount = floatval($row['recurring_invoice_amount']) - $item_total; mysqli_query($mysqli,"UPDATE recurring_invoices SET recurring_invoice_amount = $new_recurring_invoice_amount WHERE recurring_invoice_id = $recurring_invoice_id"); @@ -266,7 +278,7 @@ if (isset($_GET['delete_recurring_invoice_item'])) { } if (isset($_GET['force_recurring'])) { - + $recurring_invoice_id = intval($_GET['force_recurring']); $sql_recurring_invoices = mysqli_query($mysqli,"SELECT * FROM recurring_invoices, clients WHERE client_id = recurring_invoice_client_id AND recurring_invoice_id = $recurring_invoice_id"); @@ -286,10 +298,16 @@ if (isset($_GET['force_recurring'])) { $client_id = intval($row['recurring_invoice_client_id']); $client_net_terms = intval($row['client_net_terms']); - //Get the last Invoice Number and add 1 for the new invoice number - $new_invoice_number = $config_invoice_next_number; - $new_config_invoice_next_number = $config_invoice_next_number + 1; - mysqli_query($mysqli,"UPDATE settings SET config_invoice_next_number = $new_config_invoice_next_number WHERE company_id = 1"); + // Atomically increment and get the new invoice number + mysqli_query($mysqli, " + UPDATE settings + SET + config_invoice_next_number = LAST_INSERT_ID(config_invoice_next_number), + config_invoice_next_number = config_invoice_next_number + 1 + WHERE company_id = 1 + "); + + $new_invoice_number = mysqli_insert_id($mysqli); //Generate a unique URL key for clients to access $url_key = randomString(156); @@ -472,7 +490,7 @@ if (isset($_POST['set_recurring_payment'])) { } if (isset($_POST['export_client_recurring_invoice_csv'])) { - + $client_id = intval($_POST['client_id']); //get records from database @@ -482,7 +500,7 @@ if (isset($_POST['export_client_recurring_invoice_csv'])) { $client_name = $row['client_name']; $sql = mysqli_query($mysqli,"SELECT * FROM recurring_invoices WHERE recurring_invoice_client_id = $client_id ORDER BY recurring_invoice_number ASC"); - + $num_rows = mysqli_num_rows($sql); if ($num_rows > 0) { @@ -520,7 +538,7 @@ if (isset($_POST['export_client_recurring_invoice_csv'])) { } if (isset($_GET['recurring_invoice_email_notify'])) { - + $recurring_invoice_email_notify = intval($_GET['recurring_invoice_email_notify']); $recurring_invoice_id = intval($_GET['recurring_invoice_id']); @@ -535,7 +553,7 @@ if (isset($_GET['recurring_invoice_email_notify'])) { // Wording if ($recurring_invoice_email_notify) { $notify_wording = "On"; - } else { + } else { $notify_wording = "Off"; } diff --git a/agent/post/recurring_ticket.php b/agent/post/recurring_ticket.php index 40204310..0e410ad7 100644 --- a/agent/post/recurring_ticket.php +++ b/agent/post/recurring_ticket.php @@ -103,11 +103,16 @@ if (isset($_POST['bulk_force_recurring_tickets'])) { $config_ticket_from_email = sanitizeInput($config_ticket_from_email); $config_base_url = sanitizeInput($config_base_url); - // Assign this new ticket the next ticket number & increment config_ticket_next_number by 1 (for the next ticket) - $ticket_number_sql = mysqli_fetch_array(mysqli_query($mysqli, "SELECT config_ticket_next_number FROM settings WHERE company_id = 1")); - $ticket_number = intval($ticket_number_sql['config_ticket_next_number']); - $new_config_ticket_next_number = $ticket_number + 1; - mysqli_query($mysqli, "UPDATE settings SET config_ticket_next_number = $new_config_ticket_next_number WHERE company_id = 1"); + // Atomically increment and get the new ticket number + mysqli_query($mysqli, " + UPDATE settings + SET + config_ticket_next_number = LAST_INSERT_ID(config_ticket_next_number), + config_ticket_next_number = config_ticket_next_number + 1 + WHERE company_id = 1 + "); + + $ticket_number = mysqli_insert_id($mysqli); // Raise the ticket mysqli_query($mysqli, "INSERT INTO tickets SET ticket_prefix = '$config_ticket_prefix', ticket_number = $ticket_number, ticket_source = 'Recurring', ticket_subject = '$subject', ticket_details = '$details', ticket_priority = '$priority', ticket_status = '$ticket_status', ticket_billable = $billable, ticket_url_key = '$url_key', ticket_created_by = $created_id, ticket_assigned_to = $assigned_id, ticket_contact_id = $contact_id, ticket_client_id = $client_id, ticket_asset_id = $asset_id, ticket_category = $category, ticket_recurring_ticket_id = $recurring_ticket_id"); @@ -236,10 +241,16 @@ if (isset($_GET['force_recurring_ticket'])) { $config_ticket_from_email = sanitizeInput($config_ticket_from_email); $config_base_url = sanitizeInput($config_base_url); - // Assign this new ticket the next ticket number & increment config_ticket_next_number by 1 (for the next ticket) - $ticket_number = $config_ticket_next_number; - $new_config_ticket_next_number = $config_ticket_next_number + 1; - mysqli_query($mysqli, "UPDATE settings SET config_ticket_next_number = $new_config_ticket_next_number WHERE company_id = 1"); + // Atomically increment and get the new ticket number + mysqli_query($mysqli, " + UPDATE settings + SET + config_ticket_next_number = LAST_INSERT_ID(config_ticket_next_number), + config_ticket_next_number = config_ticket_next_number + 1 + WHERE company_id = 1 + "); + + $ticket_number = mysqli_insert_id($mysqli); // Raise the ticket mysqli_query($mysqli, "INSERT INTO tickets SET ticket_prefix = '$config_ticket_prefix', ticket_number = $ticket_number, ticket_source = 'Recurring', ticket_subject = '$subject', ticket_details = '$details', ticket_priority = '$priority', ticket_status = '$ticket_status', ticket_billable = $billable, ticket_url_key = '$url_key', ticket_created_by = $created_id, ticket_assigned_to = $assigned_id, ticket_contact_id = $contact_id, ticket_client_id = $client_id, ticket_asset_id = $asset_id, ticket_category = $category, ticket_recurring_ticket_id = $recurring_ticket_id"); diff --git a/agent/post/ticket.php b/agent/post/ticket.php index 5d4a3aff..a2f7a3f6 100644 --- a/agent/post/ticket.php +++ b/agent/post/ticket.php @@ -1503,10 +1503,16 @@ if (isset($_POST['bulk_add_asset_ticket'])) { $subject_asset_prepended = "$asset_name - $subject"; - // Get the next Ticket Number and update the config - $sql_ticket_number = mysqli_query($mysqli, "SELECT config_ticket_next_number FROM settings WHERE company_id = 1"); - $ticket_number_row = mysqli_fetch_array($sql_ticket_number); - $ticket_number = intval($ticket_number_row['config_ticket_next_number']); + // Atomically increment and get the new ticket number + mysqli_query($mysqli, " + UPDATE settings + SET + config_ticket_next_number = LAST_INSERT_ID(config_ticket_next_number), + config_ticket_next_number = config_ticket_next_number + 1 + WHERE company_id = 1 + "); + + $ticket_number = mysqli_insert_id($mysqli); // Sanitize Config Vars from get_settings.php and Session Vars from check_login.php $config_ticket_prefix = sanitizeInput($config_ticket_prefix); @@ -1517,18 +1523,10 @@ if (isset($_POST['bulk_add_asset_ticket'])) { //Generate a unique URL key for clients to access $url_key = randomString(156); - // Increment the config ticket next number - $new_config_ticket_next_number = $ticket_number + 1; - - mysqli_query($mysqli, "UPDATE settings SET config_ticket_next_number = $new_config_ticket_next_number WHERE company_id = 1"); - mysqli_query($mysqli, "INSERT INTO tickets SET ticket_prefix = '$config_ticket_prefix', ticket_number = $ticket_number, ticket_category = $category_id, ticket_subject = '$subject_asset_prepended', ticket_details = '$details', ticket_priority = '$priority', ticket_billable = $billable, ticket_status = $ticket_status, ticket_asset_id = $asset_id, ticket_created_by = $session_user_id, ticket_assigned_to = $assigned_to, ticket_url_key = '$url_key', ticket_client_id = $client_id, ticket_project_id = $project_id"); $ticket_id = mysqli_insert_id($mysqli); - // Update the next ticket number in the database - mysqli_query($mysqli, "UPDATE settings SET config_ticket_next_number = $new_config_ticket_next_number WHERE company_id = 1"); - // Add Tasks if (!empty($_POST['tasks'])) { foreach ($_POST['tasks'] as $task) { @@ -2155,11 +2153,18 @@ if (isset($_POST['add_invoice_from_ticket'])) { if ($invoice_id == 0) { - //Get the last Invoice Number and add 1 for the new invoice number - $invoice_number = $config_invoice_next_number; $invoice_prefix = sanitizeInput($config_invoice_prefix); - $new_config_invoice_next_number = $config_invoice_next_number + 1; - mysqli_query($mysqli, "UPDATE settings SET config_invoice_next_number = $new_config_invoice_next_number WHERE company_id = 1"); + + // Atomically increment and get the new invoice number + mysqli_query($mysqli, " + UPDATE settings + SET + config_invoice_next_number = LAST_INSERT_ID(config_invoice_next_number), + config_invoice_next_number = config_invoice_next_number + 1 + WHERE company_id = 1 + "); + + $invoice_number = mysqli_insert_id($mysqli); //Generate a unique URL key for clients to access $url_key = randomString(156); diff --git a/agent/ticket.php b/agent/ticket.php index 7564ddf9..266f01db 100644 --- a/agent/ticket.php +++ b/agent/ticket.php @@ -274,7 +274,7 @@ if (isset($_GET['ticket_id'])) { // Get ticket replies - $sql_ticket_replies = mysqli_query($mysqli, "SELECT * FROM ticket_replies + $sql_ticket_replies = mysqli_query($mysqli, "SELECT * FROM ticket_replies LEFT JOIN users ON ticket_reply_by = user_id LEFT JOIN contacts ON ticket_reply_by = contact_id WHERE ticket_reply_ticket_id = $ticket_id @@ -340,7 +340,7 @@ if (isset($_GET['ticket_id'])) { $sql_ticket_collaborators = mysqli_query($mysqli, " SELECT GROUP_CONCAT(DISTINCT user_name SEPARATOR ', ') AS user_names FROM users - LEFT JOIN ticket_replies ON user_id = ticket_reply_by + LEFT JOIN ticket_replies ON user_id = ticket_reply_by WHERE ticket_reply_archived_at IS NULL AND ticket_reply_ticket_id = $ticket_id "); @@ -461,7 +461,7 @@ if (isset($_GET['ticket_id'])) {
- +
Updated:
@@ -587,7 +587,7 @@ if (isset($_GET['ticket_id'])) { = 2 && empty($ticket_resolved_at) && empty($ticket_closed_at)) { ?> - +
@@ -677,7 +677,7 @@ if (isset($_GET['ticket_id'])) {
- + @@ -813,7 +813,7 @@ if (isset($_GET['ticket_id'])) {
">
Ticket Details
- +