1<?php 2/* Copyright (C) - 2013-2016 Jean-François FERRY <hello@librethic.io> 3 * Copyright (C) - 2019 Nicolas ZABOURI <info@inovea-conseil.com> 4 * Copyright (C) 2021 Frédéric France <frederic.france@netlogic.fr> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 3 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program. If not, see <https://www.gnu.org/licenses/>. 18 */ 19 20/** 21 * \file htdocs/ticket/agenda.php 22 * \ingroup ticket 23 */ 24 25require '../main.inc.php'; 26require_once DOL_DOCUMENT_ROOT.'/ticket/class/actions_ticket.class.php'; 27require_once DOL_DOCUMENT_ROOT.'/ticket/class/ticketstats.class.php'; 28require_once DOL_DOCUMENT_ROOT.'/core/class/dolgraph.class.php'; 29require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; 30require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php'; 31 32$hookmanager = new HookManager($db); 33 34// Initialize technical object to manage hooks. Note that conf->hooks_modules contains array 35$hookmanager->initHooks(array('ticketsindex')); 36 37// Load translation files required by the page 38$langs->loadLangs(array('companies', 'other', 'ticket')); 39 40$WIDTH = DolGraph::getDefaultGraphSizeForStats('width'); 41$HEIGHT = DolGraph::getDefaultGraphSizeForStats('height'); 42 43// Get parameters 44$id = GETPOST('id', 'int'); 45$msg_id = GETPOST('msg_id', 'int'); 46 47$action = GETPOST('action', 'aZ09'); 48 49$socid = 0; 50if ($user->socid) { 51 $socid = $user->socid; 52} 53 54// Security check 55$result = restrictedArea($user, 'ticket', 0, '', '', '', ''); 56 57$nowyear = strftime("%Y", dol_now()); 58$year = GETPOST('year') > 0 ? GETPOST('year') : $nowyear; 59$startyear = $year - (empty($conf->global->MAIN_STATS_GRAPHS_SHOW_N_YEARS) ? 2 : max(1, min(10, $conf->global->MAIN_STATS_GRAPHS_SHOW_N_YEARS))); 60$endyear = $year; 61 62$object = new Ticket($db); 63 64 65/* 66 * Actions 67 */ 68 69// None 70 71 72/* 73 * View 74 */ 75$resultboxes = FormOther::getBoxesArea($user, "11"); // Load $resultboxes (selectboxlist + boxactivated + boxlista + boxlistb) 76 77$form = new Form($db); 78$tickesupstatic = new Ticket($db); 79 80llxHeader('', $langs->trans('TicketsIndex'), ''); 81 82$linkback = ''; 83print load_fiche_titre($langs->trans('TicketsIndex'), $resultboxes['selectboxlist'], 'ticket'); 84 85 86$dir = ''; 87$filenamenb = $dir."/".$prefix."ticketinyear-".$endyear.".png"; 88$fileurlnb = DOL_URL_ROOT.'/viewimage.php?modulepart=ticket&file=ticketinyear-'.$endyear.'.png'; 89 90$stats = new TicketStats($db, $socid, $userid); 91$param_year = 'DOLUSERCOOKIE_ticket_by_status_year'; 92$param_shownb = 'DOLUSERCOOKIE_ticket_by_status_shownb'; 93$param_showtot = 'DOLUSERCOOKIE_ticket_by_status_showtot'; 94$autosetarray = preg_split("/[,;:]+/", GETPOST('DOL_AUTOSET_COOKIE')); 95if (in_array('DOLUSERCOOKIE_ticket_by_status', $autosetarray)) { 96 $endyear = GETPOST($param_year, 'int'); 97 $shownb = GETPOST($param_shownb, 'alpha'); 98 $showtot = GETPOST($param_showtot, 'alpha'); 99} else { 100 $tmparray = json_decode($_COOKIE['DOLUSERCOOKIE_ticket_by_status'], true); 101 $endyear = $tmparray['year']; 102 $shownb = $tmparray['shownb']; 103 $showtot = $tmparray['showtot']; 104} 105if (empty($shownb) && empty($showtot)) { 106 $showtot = 1; 107} 108 109$nowarray = dol_getdate(dol_now(), true); 110if (empty($endyear)) { 111 $endyear = $nowarray['year']; 112} 113 114$startyear = $endyear - 1; 115 116// Change default WIDHT and HEIGHT (we need a smaller than default for both desktop and smartphone) 117$WIDTH = (($shownb && $showtot) || !empty($conf->dol_optimize_smallscreen)) ? '100%' : '80%'; 118if (empty($conf->dol_optimize_smallscreen)) { 119 $HEIGHT = '200'; 120} else { 121 $HEIGHT = '160'; 122} 123 124print '<div class="clearboth"></div>'; 125print '<div class="fichecenter fichecenterbis">'; 126 127print '<div class="twocolumns">'; 128 129print '<div class="firstcolumn fichehalfleft boxhalfleft" id="boxhalfleft">'; 130 131/* 132 * Statistics area 133 */ 134$tick = array( 135 'unread' => 0, 136 'read' => 0, 137 'needmoreinfo' => 0, 138 'answered' => 0, 139 'assigned' => 0, 140 'inprogress' => 0, 141 'waiting' => 0, 142 'closed' => 0, 143 'canceled' => 0, 144 'deleted' => 0, 145); 146 147$sql = "SELECT t.fk_statut, COUNT(t.fk_statut) as nb"; 148$sql .= " FROM ".MAIN_DB_PREFIX."ticket as t"; 149if (!$user->rights->societe->client->voir && !$socid) { 150 $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; 151} 152$sql .= ' WHERE t.entity IN ('.getEntity('ticket').')'; 153$sql .= dolSqlDateFilter('datec', 0, 0, $endyear); 154 155if (!$user->rights->societe->client->voir && !$socid) { 156 $sql .= " AND t.fk_soc = sc.fk_soc AND sc.fk_user = ".((int) $user->id); 157} 158 159// External users restriction 160if ($user->socid > 0) { 161 $sql .= " AND t.fk_soc= ".((int) $user->socid); 162} else { 163 // For internals users, 164 if (!empty($conf->global->TICKET_LIMIT_VIEW_ASSIGNED_ONLY) && !$user->rights->ticket->manage) { 165 $sql .= " AND t.fk_user_assign = ".((int) $user->id); 166 } 167} 168$sql .= " GROUP BY t.fk_statut"; 169 170$result = $db->query($sql); 171if ($result) { 172 while ($objp = $db->fetch_object($result)) { 173 $found = 0; 174 if ($objp->fk_statut == Ticket::STATUS_NOT_READ) { 175 $tick['unread'] = $objp->nb; 176 } 177 if ($objp->fk_statut == Ticket::STATUS_READ) { 178 $tick['read'] = $objp->nb; 179 } 180 if ($objp->fk_statut == Ticket::STATUS_NEED_MORE_INFO) { 181 $tick['needmoreinfo'] = $objp->nb; 182 } 183 if ($objp->fk_statut == Ticket::STATUS_ASSIGNED) { 184 $tick['assigned'] = $objp->nb; 185 } 186 if ($objp->fk_statut == Ticket::STATUS_IN_PROGRESS) { 187 $tick['inprogress'] = $objp->nb; 188 } 189 if ($objp->fk_statut == Ticket::STATUS_WAITING) { 190 $tick['waiting'] = $objp->nb; 191 } 192 if ($objp->fk_statut == Ticket::STATUS_CLOSED) { 193 $tick['closed'] = $objp->nb; 194 } 195 if ($objp->fk_statut == Ticket::STATUS_CANCELED) { 196 $tick['canceled'] = $objp->nb; 197 } 198 } 199 200 include DOL_DOCUMENT_ROOT.'/theme/'.$conf->theme.'/theme_vars.inc.php'; 201 202 $dataseries = array(); 203 $colorseries = array(); 204 205 $dataseries[] = array('label' => $langs->transnoentitiesnoconv($tickesupstatic->statuts_short[Ticket::STATUS_NOT_READ]), 'data' => round($tick['unread'])); 206 $colorseries[Ticket::STATUS_NOT_READ] = '-'.$badgeStatus0; 207 $dataseries[] = array('label' => $langs->transnoentitiesnoconv($tickesupstatic->statuts_short[Ticket::STATUS_READ]), 'data' => round($tick['read'])); 208 $colorseries[Ticket::STATUS_READ] = $badgeStatus1; 209 $dataseries[] = array('label' => $langs->transnoentitiesnoconv($tickesupstatic->statuts_short[Ticket::STATUS_ASSIGNED]), 'data' => round($tick['assigned'])); 210 $colorseries[Ticket::STATUS_ASSIGNED] = $badgeStatus3; 211 $dataseries[] = array('label' => $langs->transnoentitiesnoconv($tickesupstatic->statuts_short[Ticket::STATUS_IN_PROGRESS]), 'data' => round($tick['inprogress'])); 212 $colorseries[Ticket::STATUS_IN_PROGRESS] = $badgeStatus4; 213 $dataseries[] = array('label' => $langs->transnoentitiesnoconv($tickesupstatic->statuts_short[Ticket::STATUS_WAITING]), 'data' => round($tick['waiting'])); 214 $colorseries[Ticket::STATUS_WAITING] = '-'.$badgeStatus4; 215 $dataseries[] = array('label' => $langs->transnoentitiesnoconv($tickesupstatic->statuts_short[Ticket::STATUS_NEED_MORE_INFO]), 'data' => round($tick['needmoreinfo'])); 216 $colorseries[Ticket::STATUS_NEED_MORE_INFO] = '-'.$badgeStatus3; 217 $dataseries[] = array('label' => $langs->transnoentitiesnoconv($tickesupstatic->statuts_short[Ticket::STATUS_CANCELED]), 'data' => round($tick['canceled'])); 218 $colorseries[Ticket::STATUS_CANCELED] = $badgeStatus9; 219 $dataseries[] = array('label' => $langs->transnoentitiesnoconv($tickesupstatic->statuts_short[Ticket::STATUS_CLOSED]), 'data' => round($tick['closed'])); 220 $colorseries[Ticket::STATUS_CLOSED] = $badgeStatus6; 221} else { 222 dol_print_error($db); 223} 224 225$stringtoshow = '<script type="text/javascript" language="javascript"> 226 jQuery(document).ready(function() { 227 jQuery("#idsubimgDOLUSERCOOKIE_ticket_by_status").click(function() { 228 jQuery("#idfilterDOLUSERCOOKIE_ticket_by_status").toggle(); 229 }); 230 }); 231 </script>'; 232$stringtoshow .= '<div class="center hideobject" id="idfilterDOLUSERCOOKIE_ticket_by_status">'; // hideobject is to start hidden 233$stringtoshow .= '<form class="flat formboxfilter" method="POST" action="'.$_SERVER["PHP_SELF"].'">'; 234$stringtoshow .= '<input type="hidden" name="token" value="'.newToken().'">'; 235$stringtoshow .= '<input type="hidden" name="action" value="refresh">'; 236$stringtoshow .= '<input type="hidden" name="DOL_AUTOSET_COOKIE" value="DOLUSERCOOKIE_ticket_by_status:year,shownb,showtot">'; 237$stringtoshow .= $langs->trans("Year").' <input class="flat" size="4" type="text" name="'.$param_year.'" value="'.$endyear.'">'; 238$stringtoshow .= '<input type="image" alt="'.$langs->trans("Refresh").'" src="'.img_picto($langs->trans("Refresh"), 'refresh.png', '', '', 1).'">'; 239$stringtoshow .= '</form>'; 240$stringtoshow .= '</div>'; 241 242print '<div class="div-table-responsive-no-min">'; 243print '<table class="noborder centpercent">'; 244print '<tr class="liste_titre"><th >'.$langs->trans("Statistics").' '.$endyear.' '.img_picto('', 'filter.png', 'id="idsubimgDOLUSERCOOKIE_ticket_by_status" class="linkobject"').'</th></tr>'; 245 246print '<tr><td class="center">'; 247print $stringtoshow; 248 249// don't display graph if no series 250if (!empty($dataseries) && count($dataseries) > 1) { 251 $totalnb = 0; 252 foreach ($dataseries as $key => $value) { 253 $totalnb += $value['data']; 254 } 255 256 $data = array(); 257 foreach ($dataseries as $key => $value) { 258 $data[] = array($value['label'], $value['data']); 259 } 260 $px1 = new DolGraph(); 261 $mesg = $px1->isGraphKo(); 262 if (!$mesg) { 263 $px1->SetData($data); 264 $px1->SetDataColor(array_values($colorseries)); 265 266 unset($data1); 267 $i = $startyear; 268 $legend = array(); 269 while ($i <= $endyear) { 270 $legend[] = $i; 271 $i++; 272 } 273 $px1->setShowLegend(2); 274 $px1->SetType(array('pie')); 275 $px1->SetLegend($legend); 276 $px1->SetMaxValue($px1->GetCeilMaxValue()); 277 //$px1->SetWidth($WIDTH); 278 $px1->SetHeight($HEIGHT); 279 $px1->SetYLabel($langs->trans("TicketStatByStatus")); 280 $px1->SetShading(3); 281 $px1->SetHorizTickIncrement(1); 282 $px1->SetCssPrefix("cssboxes"); 283 $px1->mode = 'depth'; 284 //$px1->SetTitle($langs->trans("TicketStatByStatus")); 285 286 $px1->draw($filenamenb, $fileurlnb); 287 print $px1->show($totalnb ? 0 : 1); 288 } 289} 290print '</td></tr>'; 291 292print '</table>'; 293print '</div>'; 294 295// Build graphic number of object 296$data = $stats->getNbByMonthWithPrevYear($endyear, $startyear); 297 298print '<br>'."\n"; 299 300print $resultboxes['boxlista']; 301 302print '</div>'."\n"; 303 304print '<div class="secondcolumn fichehalfright boxhalfright" id="boxhalfright">'; 305 306/* 307 * Latest unread tickets 308 */ 309 310$max = 10; 311 312$sql = "SELECT t.rowid, t.ref, t.track_id, t.datec, t.subject, t.type_code, t.category_code, t.severity_code, t.fk_statut, t.progress,"; 313$sql .= " type.code as type_code, type.label as type_label,"; 314$sql .= " category.code as category_code, category.label as category_label,"; 315$sql .= " severity.code as severity_code, severity.label as severity_label"; 316$sql .= " FROM ".MAIN_DB_PREFIX."ticket as t"; 317$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticket_type as type ON type.code=t.type_code"; 318$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticket_category as category ON category.code=t.category_code"; 319$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticket_severity as severity ON severity.code=t.severity_code"; 320if (!$user->rights->societe->client->voir && !$socid) { 321 $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; 322} 323 324$sql .= ' WHERE t.entity IN ('.getEntity('ticket').')'; 325$sql .= " AND t.fk_statut=0"; 326if (!$user->rights->societe->client->voir && !$socid) { 327 $sql .= " AND t.fk_soc = sc.fk_soc AND sc.fk_user = ".((int) $user->id); 328} 329 330if ($user->socid > 0) { 331 $sql .= " AND t.fk_soc= ".((int) $user->socid); 332} else { 333 // Restricted to assigned user only 334 if (!empty($conf->global->TICKET_LIMIT_VIEW_ASSIGNED_ONLY) && !$user->rights->ticket->manage) { 335 $sql .= " AND t.fk_user_assign = ".((int) $user->id); 336 } 337} 338$sql .= $db->order("t.datec", "DESC"); 339$sql .= $db->plimit($max, 0); 340 341//print $sql; 342$result = $db->query($sql); 343if ($result) { 344 $num = $db->num_rows($result); 345 346 $i = 0; 347 348 $transRecordedType = $langs->trans("LatestNewTickets", $max); 349 350 print '<div class="div-table-responsive-no-min">'; 351 print '<table class="noborder centpercent">'; 352 print '<tr class="liste_titre"><th colspan="5">'.$transRecordedType.'</th>'; 353 print '<th class="right" colspan="2"><a href="'.DOL_URL_ROOT.'/ticket/list.php?search_fk_statut[]='.Ticket::STATUS_NOT_READ.'">'.$langs->trans("FullList").'</th>'; 354 print '</tr>'; 355 if ($num > 0) { 356 while ($i < $num) { 357 $objp = $db->fetch_object($result); 358 359 $tickesupstatic->id = $objp->rowid; 360 $tickesupstatic->ref = $objp->ref; 361 $tickesupstatic->track_id = $objp->track_id; 362 $tickesupstatic->fk_statut = $objp->fk_statut; 363 $tickesupstatic->progress = $objp->progress; 364 $tickesupstatic->subject = $objp->subject; 365 366 print '<tr class="oddeven">'; 367 368 // Ref 369 print '<td class="nowraponall">'; 370 print $tickesupstatic->getNomUrl(1); 371 print "</td>\n"; 372 373 // Creation date 374 print '<td class="left">'; 375 print dol_print_date($db->jdate($objp->datec), 'dayhour'); 376 print "</td>"; 377 378 // Subject 379 print '<td class="nowrap">'; 380 print '<a href="card.php?track_id='.$objp->track_id.'">'.dol_trunc($objp->subject, 30).'</a>'; 381 print "</td>\n"; 382 383 // Type 384 print '<td class="nowrap tdoverflowmax100">'; 385 $s = $langs->getLabelFromKey($db, 'TicketTypeShort'.$objp->type_code, 'c_ticket_type', 'code', 'label', $objp->type_code); 386 print '<span title="'.dol_escape_htmltag($s).'">'.$s.'</span>'; 387 print '</td>'; 388 389 // Category 390 print '<td class="nowrap">'; 391 $s = $langs->getLabelFromKey($db, 'TicketCategoryShort'.$objp->category_code, 'c_ticket_category', 'code', 'label', $objp->category_code); 392 print '<span title="'.dol_escape_htmltag($s).'">'.$s.'</span>'; 393 //print $objp->category_label; 394 print "</td>"; 395 396 // Severity 397 print '<td class="nowrap">'; 398 $s = $langs->getLabelFromKey($db, 'TicketSeverityShort'.$objp->severity_code, 'c_ticket_severity', 'code', 'label', $objp->severity_code); 399 print '<span title="'.dol_escape_htmltag($s).'">'.$s.'</span>'; 400 //print $objp->severity_label; 401 print "</td>"; 402 403 print '<td class="nowraponall right">'; 404 print $tickesupstatic->getLibStatut(5); 405 print "</td>"; 406 407 print "</tr>\n"; 408 $i++; 409 } 410 411 $db->free($result); 412 } else { 413 print '<tr><td colspan="6"><span class="opacitymedium">'.$langs->trans('NoUnreadTicketsFound').'</span></td></tr>'; 414 } 415 416 print "</table>"; 417 print '</div>'; 418 419 print '<br>'; 420} else { 421 dol_print_error($db); 422} 423 424 425print $resultboxes['boxlistb']; 426 427print '</div>'; 428print '</div>'; 429print '</div>'; 430 431 432print '<div style="clear:both"></div>'; 433 434$parameters = array('user' => $user); 435$reshook = $hookmanager->executeHooks('dashboardTickets', $parameters, $object); // Note that $action and $object may have been modified by hook 436 437 438 439// End of page 440llxFooter(''); 441$db->close(); 442