diff --git a/include/class.report.php b/include/class.report.php index d65fd1cc5a63fac47ff221763c104f148fc71c5d..c9bc7c64e5d8bb90bf9af5edcf45618f4b5b06ea 100644 --- a/include/class.report.php +++ b/include/class.report.php @@ -140,56 +140,68 @@ class OverviewReport { global $thisstaff; list($start, $stop) = $this->getDateRange(); - $times = Ticket::objects() + $times = ThreadEvent::objects() ->constrain(array( 'thread__entries' => array( - 'thread__entries__type' => 'R' - ), - )) - ->aggregate(array( - 'ServiceTime' => SqlAggregate::AVG(SqlFunction::DATEDIFF( - new SqlField('closed'), new SqlField('created') - )), - 'ResponseTime' => SqlAggregate::AVG(SqlFunction::DATEDIFF( - new SqlField('thread__entries__created'), new SqlField('thread__entries__parent__created') - )), - )); - - $stats = Ticket::objects() + 'thread__entries__type' => 'R', + ), + )) ->constrain(array( 'thread__events' => array( - 'thread__events__annulled' => 0, - 'thread__events__timestamp__range' => array($start, $stop), - ), - )) + 'thread__events__state' => 'created', + 'state' => 'closed', + 'annulled' => 0, + ), + )) ->aggregate(array( - 'Opened' => SqlAggregate::COUNT( - SqlCase::N() - ->when(new Q(array('thread__events__state' => 'created')), 1) - ), - 'Assigned' => SqlAggregate::COUNT( - SqlCase::N() - ->when(new Q(array('thread__events__state' => 'assigned')), 1) - ), - 'Overdue' => SqlAggregate::COUNT( - SqlCase::N() - ->when(new Q(array('thread__events__state' => 'overdue')), 1) - ), - 'Closed' => SqlAggregate::COUNT( - SqlCase::N() - ->when(new Q(array('thread__events__state' => 'closed')), 1) - ), - 'Reopened' => SqlAggregate::COUNT( - SqlCase::N() - ->when(new Q(array('thread__events__state' => 'reopened')), 1) + 'ServiceTime' => SqlAggregate::AVG(SqlFunction::timestampdiff( + new SqlCode('HOUR'), new SqlField('thread__events__timestamp'), new SqlField('timestamp')) ), + 'ResponseTime' => SqlAggregate::AVG(SqlFunction::timestampdiff( + new SqlCode('HOUR'),new SqlField('thread__entries__parent__created'), new SqlField('thread__entries__created') + )), )); + $stats = ThreadEvent::objects() + ->filter(array( + 'annulled' => 0, + 'timestamp__range' => array($start, $stop), + 'thread__object_type' => 'T', + )) + ->aggregate(array( + 'Opened' => SqlAggregate::COUNT( + SqlCase::N() + ->when(new Q(array('state' => 'created')), 1) + ), + 'Assigned' => SqlAggregate::COUNT( + SqlCase::N() + ->when(new Q(array('state' => 'assigned')), 1) + ), + 'Overdue' => SqlAggregate::COUNT( + SqlCase::N() + ->when(new Q(array('state' => 'overdue')), 1) + ), + 'Closed' => SqlAggregate::COUNT( + SqlCase::N() + ->when(new Q(array('state' => 'closed')), 1) + ), + 'Reopened' => SqlAggregate::COUNT( + SqlCase::N() + ->when(new Q(array('state' => 'reopened')), 1) + ), + 'Deleted' => SqlAggregate::COUNT( + SqlCase::N() + ->when(new Q(array('state' => 'deleted')), 1) + ), + )); + switch ($group) { case 'dept': $headers = array(__('Department')); $header = function($row) { return Dept::getLocalNameById($row['dept_id'], $row['dept__name']); }; - $pk = 'dept_id'; + $pk = 'dept__id'; + //adriane + // $pk = 'dept_id'; $stats = $stats ->filter(array('dept_id__in' => $thisstaff->getDepts())) ->values('dept__id', 'dept__name'); @@ -203,7 +215,7 @@ class OverviewReport { $pk = 'topic_id'; $stats = $stats ->values('topic_id', 'topic__topic') - ->filter(array('topic_id__gt' => 0)); + ->filter(array('dept_id__in' => $thisstaff->getDepts(), 'topic_id__gt' => 0)); $times = $times ->values('topic_id') ->filter(array('topic_id__gt' => 0)); @@ -234,18 +246,17 @@ class OverviewReport { foreach ($times as $T) { $timings[$T[$pk]] = $T; } - $rows = array(); foreach ($stats as $R) { $T = $timings[$R[$pk]]; $rows[] = array($header($R), $R['Opened'], $R['Assigned'], - $R['Overdue'], $R['Closed'], $R['Reopened'], + $R['Overdue'], $R['Closed'], $R['Reopened'], $R['Deleted'], number_format($T['ServiceTime'], 1), number_format($T['ResponseTime'], 1)); } return array("columns" => array_merge($headers, array(__('Opened'),__('Assigned'),__('Overdue'),__('Closed'),__('Reopened'), - __('Service Time'),__('Response Time'))), + __('Deleted'),__('Service Time'),__('Response Time'))), "data" => $rows); } } diff --git a/include/class.task.php b/include/class.task.php index 4b98ff1d4a861ff9d3cab3ed906fc3ae12db34d6..7c334c41cadd9314d5fae6d838ebf35d27d440cc 100644 --- a/include/class.task.php +++ b/include/class.task.php @@ -1330,7 +1330,7 @@ class Task extends TaskModel implements RestrictedAccess, Threadable { if (!parent::delete()) return false; - $thread->delete(); + $this->logEvent('deleted'); Draft::deleteForNamespace('task.%.' . $this->getId()); diff --git a/include/class.thread.php b/include/class.thread.php index 3911f714b672fd98287a9ac5578fbbc3d1567473..19ce5da15bd25bc8dac6ad5aad2eccdd646b3e57 100644 --- a/include/class.thread.php +++ b/include/class.thread.php @@ -1557,6 +1557,12 @@ class ThreadEvent extends VerySimpleModel { ), 'null' => true, ), + 'topic' => array( + 'constraint' => array( + 'topic_id' => 'Topic.topic_id', + ), + 'null' => true, + ), ), ); @@ -1721,8 +1727,15 @@ class ThreadEvent extends VerySimpleModel { } static function forTicket($ticket, $state, $user=false) { + global $thisstaff; + + if($thisstaff && !$ticket->getStaffId()) + $staff = $thisstaff->getId(); + else + $staff = $ticket->getStaffId(); + $inst = self::create(array( - 'staff_id' => $ticket->getStaffId(), + 'staff_id' => $staff, 'team_id' => $ticket->getTeamId(), 'dept_id' => $ticket->getDeptId(), 'topic_id' => $ticket->getTopicId(), @@ -1730,6 +1743,15 @@ class ThreadEvent extends VerySimpleModel { return $inst; } + static function forTask($task, $state, $user=false) { + $inst = self::create(array( + 'staff_id' => $task->getStaffId(), + 'team_id' => $task->getTeamId(), + 'dept_id' => $task->getDeptId(), + ), $user); + return $inst; + } + function getTypedEvent() { static $subclasses; @@ -1773,8 +1795,10 @@ class ThreadEvents extends InstrumentedList { if ($object instanceof Ticket) // TODO: Use $object->createEvent() (nolint) $event = ThreadEvent::forTicket($object, $state, $user); - else - $event = ThreadEvent::create(false, $user); + elseif ($object instanceof Task) + $event = ThreadEvent::forTask($object, $state, $user); + // else + // $event = ThreadEvent::create(false, $user); # Annul previous entries if requested (for instance, reopening a # ticket will annul an 'closed' entry). This will be useful to @@ -2528,6 +2552,7 @@ implements TemplateVariable { $vars['threadId'] = $this->getId(); $vars['userId'] = 0; + $vars['pid'] = $this->getLastMessage()->id; if (!($resp = ResponseThreadEntry::add($vars, $errors))) return $resp; diff --git a/include/class.ticket.php b/include/class.ticket.php index 7cc661f9595c6a68e4dc8ee8d47b04b7fd13ddda..a9b37ec5d19034b248d356e9f003f0a70ea3abfb 100644 --- a/include/class.ticket.php +++ b/include/class.ticket.php @@ -2704,7 +2704,7 @@ implements RestrictedAccess, Threadable { if (!parent::delete()) return false; - $t->delete(); + $this->logEvent('deleted'); foreach (DynamicFormEntry::forTicket($this->getId()) as $form) $form->delete(); diff --git a/include/i18n/en_US/help/tips/dashboard.dashboard.yaml b/include/i18n/en_US/help/tips/dashboard.dashboard.yaml index 5ce715401b076ef151a2fa5a7cac3bf4aa115a09..72d947b4b8c0aa1cbc52b6cc6f197d32013c6e8e 100644 --- a/include/i18n/en_US/help/tips/dashboard.dashboard.yaml +++ b/include/i18n/en_US/help/tips/dashboard.dashboard.yaml @@ -44,49 +44,56 @@ statistics: opened: title: Opened content: > - This represents the tickets opened by Agents (i.e., internally opened) and - not by Clients. + Tickets that were originally opened having the Department or Help Topic + on the ticket, or the number of tickets an Agent has opened on behalf of a + User. assigned: title: Assigned content: > - The system tracks every event whereby a ticket is automatically or manually - assigned to a particular Department or Agent. Automatic assignments will - depend on established settings for <span class="doc-desc-title">Help - Topics</span> and <span class="doc-desc-title">Email Filters</span> in the - Admin Panel. + Tickets that have been assigned to either an Agent or a Team while having the + Department, Help Topic, or Agent on the ticket. The number reflects tickets that + are manually assigned to agents or teams, claimed tickets, and tickets assigned + from ticket filters/other auto-assignment rules. overdue: title: Overdue content: > - This is the amount of tickets that have violated the SLA Plan to which they - belonged. + Tickets that have been marked ‘Overdue’ by the system while having the Department, + Help Topic, or Agent listed on the ticket. Tickets are marked Overdue when they + have violated the SLA Plan to which they belonged, causing them to have a status of + ‘Open’ past their Due Date. closed: title: Closed content: > - This is the amount of tickets that were closed. + The number of Tickets that are currently in the Closed status + while also having the Department, Help Topic, or Agent on the ticket. reopened: title: Reopened content: > - This is the amount of tickets that were reopened either by an Agent or by a - Client when he/she responded while the ticket was in a Closed status. + The total number of times a ticket was Reopened while also having the Department, + Help Topic, or Agent listed on the ticket within the timeframe chosen. Tickets + are reopened whenever their status is changed from Closed to Open. + +deleted: + title: Deleted + content: > + The amount of tickets that have been deleted while having the + Department, Help Topic, or Agent listed on the ticket within the timeframe + chosen. service_time: title: Service Time content: > - <span class="doc-desc-title">Service time</span> is the duration of time - that begins at the opening of a ticket and ends when the ticket is closed. - The <span class="doc-desc-title">Service Time</span> column here measures - the average Service Time per ticket, in hours, within the specified date + Refers to the duration of time that begins at the opening of a ticket and ends + when the ticket is closed without being reopened again. The Service Time column + measures the average Service Time per ticket, in hours, within the specified date span. response_time: title: Response Time content: > - <span class="doc-desc-title">Response Time</span> is a duration of time - that begins with any Client’s correspondence and ends when an Agent makes a - response. This measurement of time is not exclusive to a Client’s - correspondence of the initial Ticket opening. This refers to every act of - discourse originating with a Client. + Shows an average of the number of hours between when a user posted a message on a + ticket and when an agent responded/replied to the customer. diff --git a/include/staff/dashboard.inc.php b/include/staff/dashboard.inc.php index d7b35af5ee0485a9b352ad8e2224d2b3c7c24a07..40311ac1d51d459e98d4e890856290dee7494bf9 100644 --- a/include/staff/dashboard.inc.php +++ b/include/staff/dashboard.inc.php @@ -87,8 +87,36 @@ foreach ($groups as $g=>$desc) { <div class="tab_content <?php echo (!$first) ? 'hidden' : ''; ?>" id="<?php echo Format::slugify($g); ?>"> <table class="dashboard-stats table"><tbody><tr> <?php - foreach ($data['columns'] as $j=>$c) { ?> - <th <?php if ($j === 0) echo 'width="30%" class="flush-left"'; ?>><?php echo Format::htmlchars($c); ?></th> + foreach ($data['columns'] as $j=>$c) { + ?> + <th <?php if ($j === 0) echo 'width="30%" class="flush-left"'; ?>><?php echo Format::htmlchars($c); + switch ($c) { + case 'Opened': + ?><i class="help-tip icon-question-sign" href="#opened"></i><?php + break; + case 'Assigned': + ?><i class="help-tip icon-question-sign" href="#assigned"></i><?php + break; + case 'Overdue': + ?><i class="help-tip icon-question-sign" href="#overdue"></i><?php + break; + case 'Closed': + ?><i class="help-tip icon-question-sign" href="#closed"></i><?php + break; + case 'Reopened': + ?><i class="help-tip icon-question-sign" href="#reopened"></i><?php + break; + case 'Deleted': + ?><i class="help-tip icon-question-sign" href="#deleted"></i><?php + break; + case 'Service Time': + ?><i class="help-tip icon-question-sign" href="#service_time"></i><?php + break; + case 'Response Time': + ?><i class="help-tip icon-question-sign" href="#response_time"></i><?php + break; + } + ?></th> <?php } ?> </tr></tbody>