0"; $opened_in = reportRangeSql('ticket_created_at', $period); // Every current template, and archived ones with tickets in the period $templates = []; $sql = mysqli_query($mysqli, "SELECT ticket_template_id, ticket_template_name, ticket_template_archived_at, ticket_template_portal FROM ticket_templates WHERE ticket_template_archived_at IS NULL OR ticket_template_id IN (SELECT tickets.ticket_template_id FROM tickets WHERE $filter AND $opened_in)"); while ($row = mysqli_fetch_assoc($sql)) { $templates[intval($row['ticket_template_id'])] = ['id' => intval($row['ticket_template_id']), 'template' => $row['ticket_template_name'], 'archived' => !empty($row['ticket_template_archived_at']), 'portal_form' => intval($row['ticket_template_portal']) === 1, 'raised' => 0, 'agent' => 0, 'portal' => 0, 'recurring' => 0, 'project' => 0, 'resolved' => 0, 'open_now' => 0, 'seconds' => 0, 'resolve' => null, 'prev' => 0, 'last' => null]; } // Raised in the period, by how $sql = mysqli_query($mysqli, "SELECT ticket_template_id AS id, ticket_source AS source, COUNT(ticket_id) AS n FROM tickets WHERE $filter AND $opened_in GROUP BY ticket_template_id, ticket_source"); while ($row = mysqli_fetch_assoc($sql)) { $id = intval($row['id']); if (!isset($templates[$id])) { continue; } $how = ['Portal' => 'portal', 'Recurring' => 'recurring', 'Project' => 'project'][$row['source']] ?? 'agent'; $templates[$id][$how] += intval($row['n']); $templates[$id]['raised'] += intval($row['n']); } $counts = function ($sql_where, $key) use ($mysqli, $filter, &$templates) { $sql = mysqli_query($mysqli, "SELECT ticket_template_id AS id, COUNT(ticket_id) AS n FROM tickets WHERE $filter AND $sql_where GROUP BY ticket_template_id"); while ($row = mysqli_fetch_assoc($sql)) { if (isset($templates[intval($row['id'])])) { $templates[intval($row['id'])][$key] = intval($row['n']); } } }; $counts(reportRangeSql('ticket_resolved_at', $period), 'resolved'); $counts(TICKET_OPEN_SQL, 'open_now'); $counts(reportRangeSql('ticket_created_at', $period, true), 'prev'); // Time logged on them in the period, and when each was last used $sql = mysqli_query($mysqli, "SELECT tickets.ticket_template_id AS id, SUM(TIME_TO_SEC(ticket_reply_time_worked)) AS s FROM ticket_replies JOIN tickets ON ticket_reply_ticket_id = ticket_id WHERE $filter AND ticket_reply_archived_at IS NULL AND " . reportRangeSql('ticket_reply_created_at', $period) . " GROUP BY tickets.ticket_template_id"); while ($row = mysqli_fetch_assoc($sql)) { if (isset($templates[intval($row['id'])])) { $templates[intval($row['id'])]['seconds'] = intval($row['s']); } } $sql = mysqli_query($mysqli, "SELECT ticket_template_id AS id, MAX(ticket_created_at) AS last FROM tickets WHERE $filter GROUP BY ticket_template_id"); while ($row = mysqli_fetch_assoc($sql)) { if (isset($templates[intval($row['id'])])) { $templates[intval($row['id'])]['last'] = $row['last']; } } // The typical (middle) time to resolve $times = []; $sql = mysqli_query($mysqli, "SELECT ticket_template_id AS id, TIMESTAMPDIFF(SECOND, ticket_created_at, ticket_resolved_at) AS s FROM tickets WHERE $filter AND " . reportRangeSql('ticket_resolved_at', $period)); while ($row = mysqli_fetch_assoc($sql)) { $times[intval($row['id'])][] = floatval($row['s']); } foreach ($templates as $id => &$row) { $row['resolve'] = reportMedian($times[$id] ?? []); $row['change'] = reportChange($row['raised'], $row['prev']); } unset($row); uasort($templates, function ($a, $b) { return $b['raised'] <=> $a['raised'] ?: $b['open_now'] <=> $a['open_now'] ?: strcasecmp($a['template'], $b['template']); }); $templates = array_values($templates); $admin = !empty($session_is_admin); $columns = [ 'template' => ['label' => 'Template', 'html' => function ($row) use ($admin) { $name = escapeHtml($row['template']); return ($admin ? '' . $name . '' : $name) . ($row['archived'] ? ' Archived' : '') . ($row['portal_form'] ? ' ' : ''); }], 'raised' => ['label' => 'Raised', 'type' => 'count', 'class' => 'fw-semibold'], 'change' => ['label' => 'Change', 'type' => 'percent', 'csv' => false, 'html' => function ($row) { return $row['change'] === null ? '–' : '' . reportChangeHtml($row['raised'], $row['prev'], 'up') . ''; }], 'agent' => ['label' => 'By agents', 'type' => 'count'], 'portal' => ['label' => 'From the portal', 'type' => 'count'], 'recurring' => ['label' => 'Recurring', 'type' => 'count'], 'project' => ['label' => 'Projects', 'type' => 'count'], 'resolved' => ['label' => 'Resolved', 'type' => 'count'], 'open_now' => ['label' => 'Open now', 'type' => 'count'], 'resolve' => ['label' => 'To resolve', 'type' => 'duration', 'title' => 'Typical (middle) time to resolve'], 'seconds' => ['label' => 'Time logged', 'type' => 'hours'], 'last' => ['label' => 'Last used', 'type' => 'date', 'html' => function ($row) { return $row['last'] ? '' . timeAgo($row['last']) . '' : 'Never'; }], ]; $totals = ['template' => 'Total']; foreach (['raised', 'agent', 'portal', 'recurring', 'project', 'resolved', 'open_now', 'seconds'] as $key) { $totals[$key] = array_sum(array_column($templates, $key)); } $totals['resolve'] = reportMedian($times ? array_merge(...array_values($times)) : []); if (reportExporting()) { reportCsv('Ticket Templates ' . $period['start'] . ' to ' . $period['end'], $columns, $templates, $totals); } require_once "includes/inc_report_layout.php"; // All the tickets raised in the period - from templates or not - for the share $all_raised = intval(mysqli_fetch_row(mysqli_query($mysqli, "SELECT COUNT(ticket_id) FROM tickets WHERE " . reportTicketSql($client_id, $agent_id) . " AND $opened_in"))[0]); $current = array_filter($templates, function ($t) { return !$t['archived']; }); $used = count(array_filter($current, function ($t) { return $t['raised'] > 0; })); $unused = count($current) - $used; $top = array_slice(array_filter($templates, function ($t) { return $t['raised'] > 0; }), 0, 12); ?>
$period, 'years' => reportPastYears("SELECT DISTINCT YEAR(ticket_created_at) AS y FROM tickets WHERE ticket_template_id > 0"), 'client' => $client_id, 'agent' => $agent_id, 'print_meta' => $client_id ? reportClientName($client_id) : '', ]); ?> 'Raised from templates', 'value' => reportCount($totals['raised']), 'sub' => $all_raised ? reportPercent(reportShare($totals['raised'], $all_raised)) . ' of all tickets raised' : ''], ['label' => 'From the client portal', 'value' => reportCount($totals['portal']), 'sub' => 'Asked for on its request forms'], ['label' => 'Templates used', 'value' => $used . ' of ' . count($current), 'sub' => $unused ? $unused . ' not used in this period' : 'Every one'], ['label' => 'Most used', 'value' => $top ? $top[0]['template'] : '-', 'sub' => $top ? $top[0]['raised'] . ' tickets · ' . reportPercent(reportShare($top[0]['raised'], $totals['raised'])) . ' of them' : ''], ]) ?>
['By agents', 'accent'], 'portal' => ['From the portal', 'success'], 'recurring' => ['Recurring', 'info'], 'project' => ['Projects', 'purple']] as $key => [$label, $tone]) { $series[] = ['label' => $label, 'tone' => $tone, 'stack' => 'how', 'data' => array_column($top, $key)]; } echo reportChart(['type' => 'bar', 'horizontal' => true, 'stacked' => true, 'labels' => array_column($top, 'template'), 'series' => $series, 'format' => 'count'], '', 'Tickets raised per template, by how'); ?>
'There are no ticket templates yet.', 'totals' => $totals]) ?>