diff --git a/include/class.report.php b/include/class.report.php index d65fd1cc5a63fac47ff221763c104f148fc71c5d..fb284fc6290733e3dd2cfb6b540ccaf0b312fbe7 100644 --- a/include/class.report.php +++ b/include/class.report.php @@ -140,56 +140,66 @@ 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'; $stats = $stats ->filter(array('dept_id__in' => $thisstaff->getDepts())) ->values('dept__id', 'dept__name'); @@ -203,7 +213,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 +244,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 14292a2d92080a7e08865f4db2f6ca2acbf3e2a4..d3a09b9b5bdea2789b762d72fbfb8512f52efca9 100644 --- a/include/class.task.php +++ b/include/class.task.php @@ -1340,7 +1340,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 0d94b8df6ef8333453492440b1e0de363741d511..c6643ccbbb9bf96bfaf43538ac057ef245060256 100644 --- a/include/class.thread.php +++ b/include/class.thread.php @@ -1837,6 +1837,12 @@ class ThreadEvent extends VerySimpleModel { ), 'null' => true, ), + 'topic' => array( + 'constraint' => array( + 'topic_id' => 'Topic.topic_id', + ), + 'null' => true, + ), ), ); @@ -2007,8 +2013,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(), @@ -2016,6 +2029,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; @@ -2059,8 +2081,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 @@ -2833,6 +2857,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 a399e3d7d1c4b09a03eb8648abd4a003d2962f64..5b3f765a9033effb7df7577b2e8bbc444e80c7f9 100644 --- a/include/class.ticket.php +++ b/include/class.ticket.php @@ -3082,7 +3082,7 @@ implements RestrictedAccess, Threadable, Searchable { 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..1c82c88a2c7d9efb81980a40cd3f9c8f3ea937dc 100644 --- a/include/i18n/en_US/help/tips/dashboard.dashboard.yaml +++ b/include/i18n/en_US/help/tips/dashboard.dashboard.yaml @@ -44,49 +44,48 @@ 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. 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. 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. 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. Tickets + are reopened whenever their status is changed from Closed to Open. + +deleted: + title: Deleted + content: > + The amount of tickets that have been deleted. 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 - span. + Refers to the duration of time that begins at the opening of a ticket and ends + when the ticket is closed. The Service Time column measures the average Service + Time per ticket, in hours. 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 response time by an Agent, in hours, to ticket correspondence. diff --git a/include/staff/dashboard.inc.php b/include/staff/dashboard.inc.php index d7b35af5ee0485a9b352ad8e2224d2b3c7c24a07..3409726e5961358d439adddff369cde045e8ca37 100644 --- a/include/staff/dashboard.inc.php +++ b/include/staff/dashboard.inc.php @@ -67,6 +67,19 @@ $plots = $report->getPlotData(); <hr/> <h2><?php echo __('Statistics'); ?> <i class="help-tip icon-question-sign" href="#statistics"></i></h2> <p><?php echo __('Statistics of tickets organized by department, help topic, and agent.');?></p> +<p><b><?php echo __('Range: '); ?></b> + <?php + $range = array(); + foreach ($report->getDateRange() as $date) + { + $date = str_ireplace('FROM_UNIXTIME(', '',$date); + $date = str_ireplace(')', '',$date); + $date = new DateTime('@'.$date); + $date->setTimeZone(new DateTimeZone($thisstaff->getTimezone())); + $range[] = $date->format('F j, Y'); + } + echo __($range[0] . ' - ' . $range[1]); +?> <ul class="clean tabs"> <?php @@ -87,8 +100,52 @@ 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>