From d484629ebc464180ae23e05decba73e7bea4d518 Mon Sep 17 00:00:00 2001 From: johnnyq Date: Sat, 8 Aug 2026 12:41:34 -0400 Subject: [PATCH] do not update ticket status in history if it is the same --- agent/post/ticket.php | 37 +++++++++++++++++++++++--------- api/v1/ticket_replies/create.php | 13 +++++++---- client/post.php | 9 +++++++- cron/ticket_email_parser.php | 5 ++++- 4 files changed, 48 insertions(+), 16 deletions(-) diff --git a/agent/post/ticket.php b/agent/post/ticket.php index 8950349c1..a8d9b85a7 100644 --- a/agent/post/ticket.php +++ b/agent/post/ticket.php @@ -1510,7 +1510,7 @@ if (isset($_POST['bulk_ticket_reply'])) { foreach ($_POST['ticket_ids'] as $ticket_id) { $ticket_id = intval($ticket_id); - $sql = mysqli_query($mysqli, "SELECT ticket_client_id, ticket_first_response_at, ticket_number, ticket_prefix, ticket_priority, + $sql = mysqli_query($mysqli, "SELECT ticket_client_id, ticket_status, ticket_first_response_at, ticket_number, ticket_prefix, ticket_priority, ticket_subject, ticket_url_key FROM tickets WHERE ticket_id = $ticket_id"); $row = mysqli_fetch_assoc($sql); @@ -1521,6 +1521,7 @@ if (isset($_POST['bulk_ticket_reply'])) { $url_key = escapeSql($row['ticket_url_key']); $ticket_first_response_at = escapeSql($row['ticket_first_response_at']); $client_id = intval($row['ticket_client_id']); + $original_ticket_status = intval($row['ticket_status']); // Don't Enforce Client Access if Ticket doesn't have an assigned client if ($client_id) { @@ -1547,8 +1548,12 @@ if (isset($_POST['bulk_ticket_reply'])) { mysqli_query($mysqli, "UPDATE tickets SET ticket_status = '$ticket_status' WHERE ticket_id = $ticket_id"); syncTicketSlaClock($ticket_id); - $new_status_name = escapeSql(getTicketStatusName($ticket_status)); - logTicketHistory($ticket_id, "$session_name set the status to $new_status_name"); + // Only record a status change when the status actually changed - Resolved + // is left out because the resolve block below logs it + if ($ticket_status !== $original_ticket_status && $ticket_status != 4) { + $new_status_name = escapeSql(getTicketStatusName($ticket_status)); + logTicketHistory($ticket_id, "$session_name set the status to $new_status_name"); + } logAudit("Ticket", "Reply", "$session_name replied to ticket $ticket_prefix$ticket_number - $ticket_subject and was a $ticket_reply_type reply", $client_id, $ticket_id); @@ -1559,8 +1564,9 @@ if (isset($_POST['bulk_ticket_reply'])) { triggerCustomAction('reply_reply_agent_public', $ticket_id); } - // Resolve the ticket, if set - if ($ticket_status == 4) { + // Resolve the ticket, if it is actually moving into Resolved - a bulk reply + // on an already-resolved ticket must not restamp resolved_at + if ($ticket_status == 4 && $original_ticket_status != 4) { mysqli_query($mysqli, "UPDATE tickets SET ticket_resolved_at = NOW() WHERE ticket_id = $ticket_id"); setTicketResolutionSlaMet($ticket_id); @@ -1834,7 +1840,10 @@ if (isset($_POST['add_ticket_reply'])) { $ticket_reply = $_POST['ticket_reply']; // Reply is SQL escaped below $ticket_status = intval($_POST['status']); - $client_id = intval(getFieldById('tickets', $ticket_id, 'ticket_client_id')); + // Read the ticket as it stands before the reply changes anything + $original_row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT ticket_client_id, ticket_status FROM tickets WHERE ticket_id = $ticket_id")); + $client_id = intval($original_row['ticket_client_id'] ?? 0); + $original_ticket_status = intval($original_row['ticket_status'] ?? 0); // Don't Enforce Client Access if Ticket doesn't have an assigned client if ($client_id) { @@ -1869,8 +1878,9 @@ if (isset($_POST['add_ticket_reply'])) { mysqli_query($mysqli, "UPDATE tickets SET ticket_status = $ticket_status, ticket_updated_at = NOW() WHERE ticket_id = $ticket_id"); syncTicketSlaClock($ticket_id); - // Resolve the ticket, if set - if ($ticket_status == 4) { + // Resolve the ticket, if it is actually moving into Resolved - replying on an + // already-resolved ticket must not restamp resolved_at or re-log the resolve + if ($ticket_status == 4 && $original_ticket_status != 4) { mysqli_query($mysqli, "UPDATE tickets SET ticket_resolved_at = NOW() WHERE ticket_id = $ticket_id"); setTicketResolutionSlaMet($ticket_id); @@ -2030,8 +2040,15 @@ if (isset($_POST['add_ticket_reply'])) { flashAlert("Stored on the ticket but too large to email: " . implode(', ', $skipped_names) . "", 'error'); } - $new_status_name = escapeSql(getTicketStatusName($ticket_status)); - logTicketHistory($ticket_id, "$session_name set the status to $new_status_name"); + /* + * The reply form preselects the ticket's current status, so most replies post + * it straight back - only record a status change when it actually changed. + * Resolved is left out because the resolve block above already logged it + */ + if ($ticket_status !== $original_ticket_status && $ticket_status != 4) { + $new_status_name = escapeSql(getTicketStatusName($ticket_status)); + logTicketHistory($ticket_id, "$session_name set the status to $new_status_name"); + } logAudit("Ticket", "Reply", "$session_name replied to ticket $ticket_prefix$ticket_number - $ticket_subject and was a $ticket_reply_type reply", $client_id, $ticket_id); diff --git a/api/v1/ticket_replies/create.php b/api/v1/ticket_replies/create.php index 009443acf..27a9b5e96 100644 --- a/api/v1/ticket_replies/create.php +++ b/api/v1/ticket_replies/create.php @@ -70,6 +70,7 @@ if (!empty($ticket_id) && !empty($reply)) { $ticket_url_key = escapeSql($ticket_row['ticket_url_key']); $ticket_first_response_at = escapeSql($ticket_row['ticket_first_response_at']); $client_id = intval($ticket_row['ticket_client_id']); + $original_ticket_status = intval($ticket_row['ticket_status']); // Mark first response time if required - internal notes don't count as a response if (empty($ticket_first_response_at) && $reply_type == 'Public') { @@ -87,11 +88,15 @@ if (!empty($ticket_id) && !empty($reply)) { if (!empty($reply_ticket_status)) { mysqli_query($mysqli, "UPDATE tickets SET ticket_status = $reply_ticket_status WHERE ticket_id = $ticket_id LIMIT 1"); - $new_status_name = escapeSql(getTicketStatusName($reply_ticket_status)); - logTicketHistory($ticket_id, "Status set to $new_status_name via the API ($api_key_name)"); + // Only record a status change when the status actually changed - + // Resolved is left out because the resolve block below logs it + if ($reply_ticket_status !== $original_ticket_status && $reply_ticket_status != 4) { + $new_status_name = escapeSql(getTicketStatusName($reply_ticket_status)); + logTicketHistory($ticket_id, "Status set to $new_status_name via the API ($api_key_name)"); + } - // Resolve the ticket, if set - if ($reply_ticket_status == 4) { + // Resolve the ticket, if it is actually moving into Resolved + if ($reply_ticket_status == 4 && $original_ticket_status != 4) { mysqli_query($mysqli, "UPDATE tickets SET ticket_resolved_at = NOW() WHERE ticket_id = $ticket_id AND ticket_resolved_at IS NULL LIMIT 1"); setTicketResolutionSlaMet($ticket_id); diff --git a/client/post.php b/client/post.php index 3f03302a7..d824709d6 100644 --- a/client/post.php +++ b/client/post.php @@ -105,9 +105,16 @@ if (isset($_POST['add_ticket_comment'])) { $ticket_reply_id = mysqli_insert_id($mysqli); // Update Ticket Last Response Field & set ticket to open as client has replied + $original_row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT ticket_status FROM tickets WHERE ticket_id = $ticket_id LIMIT 1")); + $original_ticket_status = intval($original_row['ticket_status'] ?? 0); + mysqli_query($mysqli, "UPDATE tickets SET ticket_status = 2 WHERE ticket_id = $ticket_id AND ticket_client_id = $session_client_id LIMIT 1"); syncTicketSlaClock($ticket_id); - logTicketHistory($ticket_id, "$session_contact_name replied from the client portal, reopening the ticket"); + + // Only record the reopen when the ticket was not already open + if ($original_ticket_status !== 2) { + logTicketHistory($ticket_id, "$session_contact_name replied from the client portal, reopening the ticket"); + } // Get ticket details & Notify the assigned tech (if any) diff --git a/cron/ticket_email_parser.php b/cron/ticket_email_parser.php index 5f63dad43..37fc7f3a2 100644 --- a/cron/ticket_email_parser.php +++ b/cron/ticket_email_parser.php @@ -356,7 +356,10 @@ function addReply($from_email, $date, $subject, $ticket_number, $message, $attac mysqli_query($mysqli, "UPDATE tickets SET ticket_status = 2, ticket_resolved_at = NULL WHERE ticket_id = $ticket_id AND ticket_client_id = $client_id LIMIT 1"); resetTicketResolutionSla($ticket_id); - logTicketHistory($ticket_id, "$from_email_esc replied by email, reopening the ticket"); + // Only record the reopen when the ticket was not already open + if (intval($ticket_status) !== 2) { + logTicketHistory($ticket_id, "$from_email_esc replied by email, reopening the ticket"); + } logAudit("Ticket", "Edit", "Email parser: Client contact $from_email_esc updated ticket $config_ticket_prefix$ticket_number_esc ($subject)", $client_id, $ticket_id); triggerCustomAction('ticket_reply_client', $ticket_id);