Skip to content

Issue 301 CSAT Delay Was Never Applied

Ed Mozley edited this page Nov 11, 2026 · 4 revisions

πŸ” #301 CSAT "Delay before sending" was saved but never applied

GitHub #301, reported by Abdullah31H Β· Tickets, CSAT Β· fixed in 5.0.0 (#2783)

What you saw

Tickets β†’ Settings β†’ CSAT has a Delay before sending (minutes), and its help says what it is for - give the requester time to see the fix held before asking them to rate it. Set it to 30, close a ticket, and the survey arrived straight away.

What was wrong

The setting was saved and shown back, and read by nothing. The close path called sendCsatSurvey() directly whenever CSAT was on Auto. There was no queue and no background job - the "or after csat_delay_minutes" in Database Verify's own comment had never been built.

The fix

Queue on close csatAutoOnClose() (called by TicketsService when a ticket closes): no delay β†’ send now, exactly as before; a delay β†’ a row in csat_queue, one per ticket, due after the delay.
A background job cron/csat_delayed.php - Delayed satisfaction surveys on Reporting β†’ System logs β†’ Background jobs - runs every 5 minutes through the built-in scheduler, so there is nothing to set up. It is "needed" only while a delay is set or a survey is waiting, so an install without a delay never sees it overdue.
Only if still closed Due surveys are sent only if the ticket is still closed and not in the trash. Reopened in the meantime β†’ no survey. Closed again β†’ queued afresh, and the wait starts over (one row per ticket, so the second close moves it).
Never twice The job claims each row by deleting it before sending; a second overlapping run finds nothing to take.
A working link The survey link used the web request's host name - from a background job, localhost or a bare path. It now uses the public web address when one is configured, and otherwise the address the ticket was closed from, saved with the queued survey.

Nothing changes for an install with no delay (the default), and a survey can never be lost to the queue: if the queue cannot be written (before Database Verify, say) the survey is sent at once.

πŸ“ Files

File Change
includes/csat.php csatAutoOnClose(), csatSendDue(), csatUseLinkBase(); the link through publicAbsoluteUrl(); a resettable settings cache
includes/services/tickets.php the close path calls csatAutoOnClose()
cron/csat_delayed.php new background job
includes/job_registry.php the job registered, tied to the CSAT switch
database/freeitsm.sql, includes/db_verify_schema.php, includes/db_verify_indexes.php csat_queue (unique per ticket)
includes/analyst_lifecycle.php, includes/db_verify_modules.php the new table's analyst column and its DB Verify group
lang/{en,de,es}/tickets.php, reporting.php the delay's help says what a reopen does; the job's name

How it was proved

tests/csat-delay.php, 15 checks on a throwaway install. It sends no mail: every CSAT email template is switched off for the run, so the send stops at its "no template" check - proof the job reached the send, with nothing leaving the building. A 30-minute delay queues the survey due in 30 minutes and sends nothing; the job leaves it alone until due, then takes it; a ticket reopened before then gets no survey; a second close keeps one row and moves it; no delay queues nothing; the job is needed only while a delay is set; the link uses the address the ticket was closed from, and a configured address always wins. The background-job health (35) and scheduler (38) suites still pass.

What this means for you

  • Using a delay? It works from 5.0.0 - run Database Verify after upgrading. The built-in scheduler starts the new job; if you run jobs from your own scheduled tasks instead, add cron/csat_delayed.php every 5 minutes.
  • Not using one? Nothing changes.
  • Set your public web address (System β†’ Settings) so every emailed link - surveys included - uses it.

And the reminder the issue suggested (#2784)

The report also suggested an optional reminder for surveys nobody answers. Built as suggested, under Tickets β†’ Settings β†’ CSAT:

  • Send one reminder if the survey is not answered, with Remind after (hours) - 1 to 336 (two weeks), 48 by default. Off by default, so an upgrade changes nothing.
  • At most one per survey: ticket_csat_responses.reminder_sent_datetime is set by an update only one run can win, before the email goes.
  • The same link (the same token), so the survey and the reminder both work and only one rating is stored.
  • Email only - a survey asked in a chat has its own seven-day reply window.
  • Not if the ticket has been reopened or trashed, or CSAT switched off.
  • Switching it on never mails old surveys: only those whose reminder falls due from the moment it is switched on (csat_reminder_since, changelog #2798 - at first it allowed any whose reminder had fallen due in the previous 48 hours, which could send a burst on the day it was ticked). A job that was down for days still catches up with no more than 48 hours (CSAT_REMINDER_CATCHUP_HOURS).
  • The email is the new CSAT reminder template event (Tickets β†’ Settings β†’ Email templates) when there is one, otherwise the survey template again. With neither, nothing is claimed - a reminder marked sent when nothing could go would be lost.

It is sent by the same background job, now Delayed satisfaction surveys and reminders, which the built-in scheduler already runs every 5 minutes. The reminder's link uses the address the survey was sent from (ticket_csat_responses.link_base) when no public address is configured.

Proved by tests/csat-delay.php (now 24 checks) with a stand-in sender, so nothing is mailed: off by default; no template, nothing claimed; on, exactly one reminder - for the unanswered emailed survey just due - and not for one too young, one long overdue, an answered one or a chat one; the reminder template and the same link; never a second; none after a reopen.

Related

FreeITSM

Getting Started

Modules

Multi-tenancy (planned)

Responses to feedback

Blue sky thinking

Bugs resolved

Pull requests

Links

Clone this wiki locally