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.

This commit is contained in:
johnnyq
2025-11-28 14:14:46 -05:00
parent f09d8ffe05
commit 99e2487d2b
13 changed files with 378 additions and 286 deletions

View File

@@ -34,10 +34,16 @@ if (isset($_POST['add_ticket'])) {
$priority = sanitizeInput($_POST['priority']);
}
// 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_source = 'Portal', ticket_category = $category, ticket_subject = '$subject', ticket_details = '$details', ticket_priority = '$priority', ticket_status = 1, ticket_billable = $config_ticket_default_billable, ticket_created_by = $session_user_id, ticket_contact_id = $session_contact_id, ticket_asset_id = $asset, ticket_url_key = '$url_key', ticket_client_id = $session_client_id");
$ticket_id = mysqli_insert_id($mysqli);
@@ -180,7 +186,7 @@ if (isset($_POST['add_ticket_comment'])) {
}
if (isset($_POST['add_ticket_feedback'])) {
$ticket_id = intval($_POST['ticket_id']);
$feedback = sanitizeInput($_POST['add_ticket_feedback']);
@@ -210,7 +216,7 @@ if (isset($_POST['add_ticket_feedback'])) {
}
if (isset($_GET['resolve_ticket'])) {
$ticket_id = intval($_GET['resolve_ticket']);
// Get ticket details for logging
@@ -275,7 +281,7 @@ if (isset($_GET['reopen_ticket'])) {
}
if (isset($_GET['close_ticket'])) {
$ticket_id = intval($_GET['close_ticket']);
// Get ticket details for logging
@@ -299,7 +305,7 @@ if (isset($_GET['close_ticket'])) {
customAction('ticket_close', $ticket_id);
redirect("ticket.php?id=" . $ticket_id);
} else {
// The client does not have access to this ticket - send them home
redirect("index.php");
@@ -307,7 +313,7 @@ if (isset($_GET['close_ticket'])) {
}
if (isset($_GET['logout'])) {
setcookie("PHPSESSID", '', time() - 3600, "/");
unset($_COOKIE['PHPSESSID']);
@@ -319,9 +325,9 @@ if (isset($_GET['logout'])) {
}
if (isset($_POST['edit_profile'])) {
$new_password = $_POST['new_password'];
if (!empty($new_password)) {
$password_hash = password_hash($new_password, PASSWORD_DEFAULT);
mysqli_query($mysqli, "UPDATE users SET user_password = '$password_hash' WHERE user_id = $session_user_id");
@@ -329,7 +335,7 @@ if (isset($_POST['edit_profile'])) {
// Logging
logAction("Contact", "Edit", "Client contact $session_contact_name edited their profile/password in the client portal", $session_client_id, $session_contact_id);
}
redirect('index.php');
}
@@ -362,12 +368,12 @@ if (isset($_POST['add_contact'])) {
mysqli_query($mysqli, "INSERT INTO users SET user_name = '$contact_name', user_email = '$contact_email', user_password = '$password_hash', user_auth_method = '$contact_auth_method', user_type = 2");
$contact_user_id = mysqli_insert_id($mysqli);
}
// Create contact record
mysqli_query($mysqli, "INSERT INTO contacts SET contact_name = '$contact_name', contact_email = '$contact_email', contact_billing = $contact_billing, contact_technical = $contact_technical, contact_client_id = $session_client_id, contact_user_id = $contact_user_id");
$contact_id = mysqli_insert_id($mysqli);
// Logging
@@ -613,15 +619,15 @@ if (isset($_GET['add_payment_by_provider'])) {
customAction('invoice_pay', $invoice_id);
flash_alert("The amount " . numfmt_format_currency($currency_format, $invoice_amount, $invoice_currency_code) . " paid Invoice $invoice_prefix$invoice_number");
redirect();
} else {
mysqli_query($mysqli, "INSERT INTO history SET history_status = 'Payment failed', history_description = 'Stripe pay failed due to payment error', history_invoice_id = $invoice_id");
logAction("Invoice", "Payment", "Failed online payment amount of invoice $invoice_prefix$invoice_number due to Stripe payment error", $client_id, $invoice_id);
flash_alert("Payment failed", 'error');
redirect();
}
@@ -635,9 +641,9 @@ if (isset($_POST['create_stripe_customer'])) {
// Get Stripe provider
$stripe_provider_result = mysqli_query($mysqli, "
SELECT * FROM payment_providers
WHERE payment_provider_name = 'Stripe'
AND payment_provider_active = 1
SELECT * FROM payment_providers
WHERE payment_provider_name = 'Stripe'
AND payment_provider_active = 1
LIMIT 1
");
@@ -657,10 +663,10 @@ if (isset($_POST['create_stripe_customer'])) {
// Check if client already has a Stripe customer
$existing_customer = mysqli_fetch_array(mysqli_query($mysqli, "
SELECT payment_provider_client
FROM client_payment_provider
WHERE client_id = $session_client_id
AND payment_provider_id = $stripe_provider_id
SELECT payment_provider_client
FROM client_payment_provider
WHERE client_id = $session_client_id
AND payment_provider_id = $stripe_provider_id
LIMIT 1
"));
@@ -684,10 +690,10 @@ if (isset($_POST['create_stripe_customer'])) {
// Insert customer into client_payment_provider
mysqli_query($mysqli, "
INSERT INTO client_payment_provider
SET client_id = $session_client_id,
payment_provider_id = $stripe_provider_id,
payment_provider_client = '$stripe_customer_id',
INSERT INTO client_payment_provider
SET client_id = $session_client_id,
payment_provider_id = $stripe_provider_id,
payment_provider_client = '$stripe_customer_id',
client_payment_provider_created_at = NOW()
");
@@ -697,9 +703,9 @@ if (isset($_POST['create_stripe_customer'])) {
} catch (Exception $e) {
$error = $e->getMessage();
error_log("Stripe error while creating customer for $session_client_name: $error");
logApp("Stripe", "error", "Failed to create Stripe customer for $session_client_name: $error");
flash_alert("An error occurred while creating your Stripe customer. Please try again.", 'danger');
@@ -723,9 +729,9 @@ if (isset($_GET['create_stripe_checkout'])) {
// Fetch Stripe provider info
$stripe_provider_result = mysqli_query($mysqli, "
SELECT * FROM payment_providers
WHERE payment_provider_name = 'Stripe'
AND payment_provider_active = 1
SELECT * FROM payment_providers
WHERE payment_provider_name = 'Stripe'
AND payment_provider_active = 1
LIMIT 1
");
@@ -747,9 +753,9 @@ if (isset($_GET['create_stripe_checkout'])) {
// Get client currency
$client_currency_result = mysqli_query($mysqli, "
SELECT client_currency_code
FROM clients
WHERE client_id = $session_client_id
SELECT client_currency_code
FROM clients
WHERE client_id = $session_client_id
LIMIT 1
");
$client_currency_row = mysqli_fetch_assoc($client_currency_result);
@@ -791,9 +797,9 @@ if (isset($_GET['stripe_save_card'])) {
// Get Stripe provider
$stripe_provider_result = mysqli_query($mysqli, "
SELECT * FROM payment_providers
WHERE payment_provider_name = 'Stripe'
AND payment_provider_active = 1
SELECT * FROM payment_providers
WHERE payment_provider_name = 'Stripe'
AND payment_provider_active = 1
LIMIT 1
");
@@ -813,10 +819,10 @@ if (isset($_GET['stripe_save_card'])) {
// Get client's Stripe customer ID
$client_provider_query = mysqli_query($mysqli, "
SELECT payment_provider_client
FROM client_payment_provider
WHERE client_id = $session_client_id
AND payment_provider_id = $stripe_provider_id
SELECT payment_provider_client
FROM client_payment_provider
WHERE client_id = $session_client_id
AND payment_provider_id = $stripe_provider_id
LIMIT 1
");
$client_provider = mysqli_fetch_array($client_provider_query);
@@ -854,8 +860,8 @@ if (isset($_GET['stripe_save_card'])) {
// Insert into client_saved_payment_methods
mysqli_query($mysqli, "
INSERT INTO client_saved_payment_methods
SET
INSERT INTO client_saved_payment_methods
SET
saved_payment_provider_method = '$payment_method_id',
saved_payment_description = '$saved_payment_description',
saved_payment_client_id = $session_client_id,
@@ -874,8 +880,8 @@ if (isset($_GET['stripe_save_card'])) {
// Email Confirmation
$sql_settings = mysqli_query($mysqli, "
SELECT * FROM companies, settings
WHERE companies.company_id = settings.company_id
SELECT * FROM companies, settings
WHERE companies.company_id = settings.company_id
AND companies.company_id = 1
");
$row = mysqli_fetch_array($sql_settings);
@@ -922,9 +928,9 @@ if (isset($_GET['delete_saved_payment'])) {
// Get Stripe provider info
$stripe_provider_result = mysqli_query($mysqli, "
SELECT * FROM payment_providers
WHERE payment_provider_name = 'Stripe'
AND payment_provider_active = 1
SELECT * FROM payment_providers
WHERE payment_provider_name = 'Stripe'
AND payment_provider_active = 1
LIMIT 1
");
$stripe_provider = mysqli_fetch_array($stripe_provider_result);
@@ -943,11 +949,11 @@ if (isset($_GET['delete_saved_payment'])) {
}
$saved_payment_result = mysqli_query($mysqli, "
SELECT saved_payment_id, saved_payment_description, saved_payment_provider_method
FROM client_saved_payment_methods
WHERE saved_payment_id = $saved_payment_id
AND saved_payment_client_id = $session_client_id
AND saved_payment_provider_id = $stripe_provider_id
SELECT saved_payment_id, saved_payment_description, saved_payment_provider_method
FROM client_saved_payment_methods
WHERE saved_payment_id = $saved_payment_id
AND saved_payment_client_id = $session_client_id
AND saved_payment_provider_id = $stripe_provider_id
LIMIT 1
");
@@ -973,27 +979,27 @@ if (isset($_GET['delete_saved_payment'])) {
} catch (Exception $e) {
$error = $e->getMessage();
error_log("Stripe error while removing payment method $payment_method_id: $error");
logApp("Stripe", "error", "Exception removing payment method $payment_method_id: $error");
flash_alert("An error occurred while removing your payment method.", 'danger');
redirect("saved_payment_methods.php");
}
// Remove saved payment method from local DB
mysqli_query($mysqli, "
DELETE FROM client_saved_payment_methods
DELETE FROM client_saved_payment_methods
WHERE saved_payment_id = $saved_payment_id
");
// Remove any auto-pay records using this payment method
$recurring_invoices = mysqli_query($mysqli, "
SELECT recurring_invoice_id
FROM recurring_invoices
SELECT recurring_invoice_id
FROM recurring_invoices
WHERE recurring_invoice_client_id = $session_client_id
");
@@ -1001,8 +1007,8 @@ if (isset($_GET['delete_saved_payment'])) {
$recurring_invoice_id = intval($row['recurring_invoice_id']);
mysqli_query($mysqli, "
DELETE FROM recurring_payments
WHERE recurring_payment_recurring_invoice_id = $recurring_invoice_id
DELETE FROM recurring_payments
WHERE recurring_payment_recurring_invoice_id = $recurring_invoice_id
AND recurring_payment_saved_payment_id = $saved_payment_id
");
}
@@ -1010,7 +1016,7 @@ if (isset($_GET['delete_saved_payment'])) {
logAction("Stripe", "Update", "$session_contact_name deleted Stripe payment method $saved_payment_description (PM: $payment_method_id)", $session_client_id);
flash_alert("Payment method $saved_payment_description removed.");
redirect("saved_payment_methods.php");
}
@@ -1035,7 +1041,7 @@ if (isset($_POST['set_recurring_payment'])) {
LEFT JOIN client_saved_payment_methods ON saved_payment_provider_id = payment_provider_id
WHERE saved_payment_id = $saved_payment_id
AND saved_payment_client_id = $session_client_id
AND payment_provider_active = 1
AND payment_provider_active = 1
");
$row = mysqli_fetch_array($sql);
@@ -1078,13 +1084,13 @@ if (isset($_POST['client_add_document'])) {
$document_content_raw = sanitizeInput($document_name . " " . strip_tags($_POST['document_content']));
// Create document
mysqli_query($mysqli, "INSERT INTO documents SET
document_name = '$document_name',
document_description = '$document_description',
document_content = '',
document_content_raw = '$document_content_raw',
document_client_visible = 1,
document_client_id = $session_client_id,
mysqli_query($mysqli, "INSERT INTO documents SET
document_name = '$document_name',
document_description = '$document_description',
document_content = '',
document_content_raw = '$document_content_raw',
document_client_visible = 1,
document_client_id = $session_client_id,
document_created_by = $session_contact_id");
$document_id = mysqli_insert_id($mysqli);
@@ -1131,7 +1137,7 @@ if (isset($_POST['client_upload_document'])) {
// Check if file was uploaded
if (isset($_FILES['document_file']) && $_FILES['document_file']['error'] == 0) {
// Validate and get a safe file reference name
if ($file_reference_name = checkFileUpload($_FILES['document_file'], $allowedExtensions)) {
@@ -1151,26 +1157,26 @@ if (isset($_POST['client_upload_document'])) {
$document_content = "<p>Uploaded file: <strong>$file_name</strong></p><p>$document_description</p>";
$document_content_raw = "$document_name $file_name $document_description";
mysqli_query($mysqli, "INSERT INTO documents SET
document_name = '$document_name',
document_description = '$document_description',
document_content = '$document_content',
document_content_raw = '$document_content_raw',
document_client_visible = 1,
document_client_id = $session_client_id,
mysqli_query($mysqli, "INSERT INTO documents SET
document_name = '$document_name',
document_description = '$document_description',
document_content = '$document_content',
document_content_raw = '$document_content_raw',
document_client_visible = 1,
document_client_id = $session_client_id,
document_created_by = $session_contact_id");
$document_id = mysqli_insert_id($mysqli);
// Create file entry
mysqli_query($mysqli, "INSERT INTO files SET
file_reference_name = '$file_reference_name',
file_name = '$file_name',
file_description = 'Attached to document: $document_name',
file_ext = '$file_extension',
file_mime_type = '$file_mime_type',
file_size = $file_size,
file_created_by = $session_contact_id,
mysqli_query($mysqli, "INSERT INTO files SET
file_reference_name = '$file_reference_name',
file_name = '$file_name',
file_description = 'Attached to document: $document_name',
file_ext = '$file_extension',
file_mime_type = '$file_mime_type',
file_size = $file_size,
file_created_by = $session_contact_id,
file_client_id = $session_client_id");
$file_id = mysqli_insert_id($mysqli);