From 6af0634f47e4a6eb64a303511de5d26af38ce688 Mon Sep 17 00:00:00 2001 From: johnnyq Date: Sat, 15 Aug 2026 19:55:40 -0400 Subject: [PATCH] Ticket SLA: Fix on hold to pause only after first reply, show resolved closed and onhold as fixed on paused sla in the list --- admin/database_updates/2.7.0.php | 39 +++++++++++++++++++ .../ticket_status/ticket_status_edit.php | 22 ++++++++--- admin/post/ticket_status.php | 8 +++- admin/ticket_statuses.php | 3 ++ agent/post/ticket.php | 3 +- agent/ticket_kanban.php | 5 ++- agent/ticket_list.php | 4 +- agent/tickets.php | 3 +- cron/ticket_sla.php | 29 ++++++++++++++ functions/app.php | 29 ++++++++++++++ setup/seed_data.php | 12 +++--- 11 files changed, 142 insertions(+), 15 deletions(-) create mode 100644 admin/database_updates/2.7.0.php diff --git a/admin/database_updates/2.7.0.php b/admin/database_updates/2.7.0.php new file mode 100644 index 000000000..a1951ab59 --- /dev/null +++ b/admin/database_updates/2.7.0.php @@ -0,0 +1,39 @@ + Ticket Statuses no + // longer offers the choice on these five - only on custom statuses. + // + // On Hold shipped with the column defaulting to 0, so a stock install breached + // the resolution SLA on tickets parked waiting for a client or a vendor. Every + // install had to find the toggle themselves; this sets it for them. + // + // Resolved and Closed are set for consistency across the SLA surfaces, not to + // change behaviour - both already stopped the clocks via ticket_resolved_at and + // ticket_closed_at, which is what the cron and syncTicketSlaClock() actually read. + mysqli_query($mysqli, "UPDATE ticket_statuses SET ticket_status_pauses_sla = 1 WHERE ticket_status_id IN (3, 4, 5)"); + mysqli_query($mysqli, "UPDATE ticket_statuses SET ticket_status_pauses_sla = 0 WHERE ticket_status_id IN (1, 2)"); + + /* + * Tickets already parked in On Hold are still holding an open sla_history + * interval, so their paused time would keep counting as consumed. Closing those + * intervals needs business-hours maths in the app timezone, and this file also + * runs from scripts/update_cli.php, which sets neither - so the first + * cron/ticket_sla.php run after the upgrade reconciles them instead, where the + * timezone is set. Nothing is lost by waiting a minute: the same cron no longer + * warns or breaches a paused ticket the moment the flag above lands. + * + * Breach verdicts already recorded against on-hold tickets are deliberately left + * alone. They were recorded by a clock that really was running under the old + * rules, and rewriting a stored miss is exactly what the reopen-integrity rules + * in functions/sla.php exist to prevent. Re-stamp an individual ticket (change + * its priority or its SLA) if you want it re-judged. + */ diff --git a/admin/modals/ticket_status/ticket_status_edit.php b/admin/modals/ticket_status/ticket_status_edit.php index f573579ae..a34aad885 100644 --- a/admin/modals/ticket_status/ticket_status_edit.php +++ b/admin/modals/ticket_status/ticket_status_edit.php @@ -12,6 +12,8 @@ $ticket_status_color = escapeHtml($row['ticket_status_color']); $ticket_status_order = intval($row['ticket_status_order']); $ticket_status_active = intval($row['ticket_status_active']); $ticket_status_pauses_sla = intval($row['ticket_status_pauses_sla']); +// Null for a custom status (the admin picks), 1 or 0 for the five built-in ones +$ticket_status_sla_lock = getTicketStatusSlaLock($ticket_status_id); // Generate the HTML form content using output buffering. ob_start(); @@ -66,12 +68,22 @@ ob_start();
- + + + + +
- Tickets sitting in a paused status never warn or breach on resolution. Time already spent is kept and the deadline moves out when the ticket comes back. + + Tickets sitting in a paused status never warn or breach on resolution. Time already spent is kept and the deadline moves out when the ticket comes back. + + Fixed for built-in statuses. Tickets here never warn or breach on resolution - time already spent is kept and the deadline moves out when the ticket comes back. + + Fixed for built-in statuses. Tickets here are being worked, so their resolution clock runs. + diff --git a/admin/post/ticket_status.php b/admin/post/ticket_status.php index b653e0897..8a0190ca1 100644 --- a/admin/post/ticket_status.php +++ b/admin/post/ticket_status.php @@ -33,7 +33,13 @@ if (isset($_POST['edit_ticket_status'])) { $order = intval($_POST['order']); $status = intval($_POST['status']); - $pauses_sla = intval($_POST['pauses_sla'] ?? 0); + // Built-in statuses have fixed SLA behaviour. The modal renders it read-only, and a + // read-only field submits nothing - so the value is decided here rather than taken + // from the POST, which also means a hand-made request cannot change it. + $pauses_sla = getTicketStatusSlaLock($ticket_status_id); + if (is_null($pauses_sla)) { + $pauses_sla = intval($_POST['pauses_sla'] ?? 0); + } mysqli_query($mysqli, "UPDATE ticket_statuses SET ticket_status_name = '$name', ticket_status_color = '$color', ticket_status_order = $order, ticket_status_active = $status, ticket_status_pauses_sla = $pauses_sla WHERE ticket_status_id = $ticket_status_id"); diff --git a/admin/ticket_statuses.php b/admin/ticket_statuses.php index 8c9363422..c8755fc2f 100644 --- a/admin/ticket_statuses.php +++ b/admin/ticket_statuses.php @@ -99,6 +99,9 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); Running + + (fixed) +