dolibarr 18.0.6
ticket.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2013-2018 Jean-François Ferry <hello@librethic.io>
3 * Copyright (C) 2016 Christophe Battarel <christophe@altairis.fr>
4 * Copyright (C) 2019-2023 Frédéric France <frederic.france@netlogic.fr>
5 * Copyright (C) 2020 Laurent Destailleur <eldy@users.sourceforge.net>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
27// Put here all includes required by your class file
28require_once DOL_DOCUMENT_ROOT."/core/class/commonobject.class.php";
29require_once DOL_DOCUMENT_ROOT.'/fichinter/class/fichinter.class.php';
30require_once DOL_DOCUMENT_ROOT.'/core/lib/ticket.lib.php';
31
32
36class Ticket extends CommonObject
37{
41 public $element = 'ticket';
42
46 public $table_element = 'ticket';
47
51 public $fk_element = 'fk_ticket';
52
56 public $ismultientitymanaged = 1;
57
61 public $isextrafieldmanaged = 1;
62
66 public $picto = 'ticket';
67
68
72 public $track_id;
73
77 public $fk_soc;
78
82 public $fk_project;
83
87 public $origin_email;
88
92 public $fk_user_create;
93
97 public $fk_user_assign;
98
102 public $subject;
103
107 public $message;
108
114 public $fk_statut;
115
119 public $status;
120
124 public $resolution;
125
129 public $progress;
130
134 public $timing;
135
139 public $type_code;
140
144 public $category_code;
145
149 public $severity_code;
150
155
160
165
170
174 public $datec = '';
175
179 public $date_read = '';
180
184 public $date_last_msg_sent = '';
185
189 public $date_close = '';
190
194 public $cache_types_tickets;
195
199 public $cache_category_tickets;
200
204 public $cache_severity_tickets;
205
209 public $cache_msgs_ticket;
210
214 public $statuts;
215
219 public $statuts_short;
220
224 public $notify_tiers_at_create;
225
229 public $email_msgid;
230
234 public $email_date;
235
239 public $ip;
240
244 public $oldcopy;
245
249 public $lines;
250
251
255 public $regeximgext = '\.gif|\.jpg|\.jpeg|\.png|\.bmp|\.webp|\.xpm|\.xbm'; // See also into images.lib.php
256
261 const STATUS_READ = 1;
262 const STATUS_ASSIGNED = 2;
263 const STATUS_IN_PROGRESS = 3;
264 const STATUS_NEED_MORE_INFO = 5; // waiting requester feedback
265 const STATUS_WAITING = 7; // on hold
266 const STATUS_CLOSED = 8; // Closed - Solved
267 const STATUS_CANCELED = 9; // Closed - Not solved
268
269
296 // BEGIN MODULEBUILDER PROPERTIES
297 public $fields = array(
298 'rowid' => array('type'=>'integer', 'label'=>'TechnicalID', 'visible'=>-2, 'enabled'=>1, 'position'=>1, 'notnull'=>1, 'index'=>1, 'comment'=>"Id"),
299 'entity' => array('type'=>'integer', 'label'=>'Entity', 'visible'=>0, 'enabled'=>1, 'position'=>5, 'notnull'=>1, 'index'=>1),
300 'ref' => array('type'=>'varchar(128)', 'label'=>'Ref', 'visible'=>1, 'enabled'=>1, 'position'=>10, 'notnull'=>1, 'index'=>1, 'searchall'=>1, 'comment'=>"Reference of object", 'css'=>'', 'showoncombobox'=>1),
301 'track_id' => array('type'=>'varchar(255)', 'label'=>'TicketTrackId', 'visible'=>-2, 'enabled'=>1, 'position'=>11, 'notnull'=>-1, 'searchall'=>1, 'help'=>"Help text"),
302 'fk_user_create' => array('type'=>'integer:User:user/class/user.class.php', 'label'=>'Author', 'visible'=>1, 'enabled'=>1, 'position'=>15, 'notnull'=>1, 'csslist'=>'tdoverflowmax100 maxwidth150onsmartphone'),
303 'origin_email' => array('type'=>'mail', 'label'=>'OriginEmail', 'visible'=>-2, 'enabled'=>1, 'position'=>16, 'notnull'=>1, 'index'=>1, 'searchall'=>1, 'comment'=>"Reference of object", 'csslist'=>'tdoverflowmax150'),
304 'subject' => array('type'=>'varchar(255)', 'label'=>'Subject', 'visible'=>1, 'enabled'=>1, 'position'=>18, 'notnull'=>-1, 'searchall'=>1, 'help'=>"", 'css'=>'maxwidth200 tdoverflowmax200', 'autofocusoncreate'=>1),
305 'type_code' => array('type'=>'varchar(32)', 'label'=>'Type', 'visible'=>1, 'enabled'=>1, 'position'=>20, 'notnull'=>-1, 'help'=>"", 'csslist'=>'maxwidth125 tdoverflowmax50'),
306 'category_code' => array('type'=>'varchar(32)', 'label'=>'TicketCategory', 'visible'=>-1, 'enabled'=>1, 'position'=>21, 'notnull'=>-1, 'help'=>"", 'css'=>'maxwidth100 tdoverflowmax200'),
307 'severity_code' => array('type'=>'varchar(32)', 'label'=>'Severity', 'visible'=>1, 'enabled'=>1, 'position'=>22, 'notnull'=>-1, 'help'=>"", 'css'=>'maxwidth100'),
308 'fk_soc' => array('type'=>'integer:Societe:societe/class/societe.class.php', 'label'=>'ThirdParty', 'visible'=>1, 'enabled'=>'isModEnabled("societe")', 'position'=>50, 'notnull'=>-1, 'index'=>1, 'searchall'=>1, 'help'=>"OrganizationEventLinkToThirdParty", 'css'=>'tdoverflowmax150 maxwidth150onsmartphone'),
309 'notify_tiers_at_create' => array('type'=>'integer', 'label'=>'NotifyThirdparty', 'visible'=>-1, 'enabled'=>0, 'position'=>51, 'notnull'=>1, 'index'=>1),
310 'fk_project' => array('type'=>'integer:Project:projet/class/project.class.php', 'label'=>'Project', 'visible'=>-1, 'enabled'=>'$conf->project->enabled', 'position'=>52, 'notnull'=>-1, 'index'=>1, 'help'=>"LinkToProject"),
311 //'timing' => array('type'=>'varchar(20)', 'label'=>'Timing', 'visible'=>-1, 'enabled'=>1, 'position'=>42, 'notnull'=>-1, 'help'=>""), // what is this ?
312 'datec' => array('type'=>'datetime', 'label'=>'DateCreation', 'visible'=>1, 'enabled'=>1, 'position'=>500, 'notnull'=>1, 'csslist'=>'nowraponall'),
313 'date_read' => array('type'=>'datetime', 'label'=>'TicketReadOn', 'visible'=>-1, 'enabled'=>1, 'position'=>501, 'notnull'=>1),
314 'date_last_msg_sent' => array('type'=>'datetime', 'label'=>'TicketLastMessageDate', 'visible'=>0, 'enabled'=>1, 'position'=>502, 'notnull'=>-1),
315 'fk_user_assign' => array('type'=>'integer:User:user/class/user.class.php', 'label'=>'AssignedTo', 'visible'=>1, 'enabled'=>1, 'position'=>505, 'notnull'=>1, 'csslist'=>'tdoverflowmax100 maxwidth150onsmartphone'),
316 'date_close' => array('type'=>'datetime', 'label'=>'TicketCloseOn', 'visible'=>-1, 'enabled'=>1, 'position'=>510, 'notnull'=>1),
317 'tms' => array('type'=>'timestamp', 'label'=>'DateModification', 'visible'=>-1, 'enabled'=>1, 'position'=>520, 'notnull'=>1),
318 'message' => array('type'=>'html', 'label'=>'Message', 'visible'=>-2, 'enabled'=>1, 'position'=>540, 'notnull'=>-1,),
319 'email_msgid' => array('type'=>'varchar(255)', 'label'=>'EmailMsgID', 'visible'=>-2, 'enabled'=>1, 'position'=>540, 'notnull'=>-1, 'help'=>'EmailMsgIDDesc', 'csslist'=>'tdoverflowmax100'),
320 'email_date' => array('type'=>'datetime', 'label'=>'EmailDate', 'visible'=>-2, 'enabled'=>1, 'position'=>541),
321 'progress' => array('type'=>'integer', 'label'=>'Progression', 'visible'=>-1, 'enabled'=>1, 'position'=>540, 'notnull'=>-1, 'css'=>'right', 'help'=>"", 'isameasure'=>2, 'csslist'=>'width50'),
322 'resolution' => array('type'=>'integer', 'label'=>'Resolution', 'visible'=>-1, 'enabled'=>'getDolGlobalString("TICKET_ENABLE_RESOLUTION")', 'position'=>550, 'notnull'=>1),
323 'fk_statut' => array('type'=>'integer', 'label'=>'Status', 'visible'=>1, 'enabled'=>1, 'position'=>600, 'notnull'=>1, 'index'=>1, 'arrayofkeyval'=>array(0 => 'Unread', 1 => 'Read', 3 => 'Answered', 4 => 'Assigned', 5 => 'InProgress', 6 => 'Waiting', 8 => 'SolvedClosed', 9 => 'Deleted')),
324 'import_key' =>array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-2, 'position'=>900),
325 );
326 // END MODULEBUILDER PROPERTIES
327
328
334 public function __construct($db)
335 {
336 global $conf;
337
338 $this->db = $db;
339
340 $this->statuts_short = array(
341 self::STATUS_NOT_READ => 'Unread',
342 self::STATUS_READ => 'Read',
343 self::STATUS_ASSIGNED => 'Assigned',
344 self::STATUS_IN_PROGRESS => 'InProgress',
345 self::STATUS_WAITING => 'OnHold',
346 self::STATUS_NEED_MORE_INFO => 'NeedMoreInformationShort',
347 self::STATUS_CLOSED => 'SolvedClosed',
348 self::STATUS_CANCELED => 'Canceled'
349 );
350 $this->statuts = array(
351 self::STATUS_NOT_READ => 'Unread',
352 self::STATUS_READ => 'Read',
353 self::STATUS_ASSIGNED => 'Assigned',
354 self::STATUS_IN_PROGRESS => 'InProgress',
355 self::STATUS_WAITING => 'OnHold',
356 self::STATUS_NEED_MORE_INFO => 'NeedMoreInformation',
357 self::STATUS_CLOSED => 'SolvedClosed',
358 self::STATUS_CANCELED => 'Canceled'
359 );
360 }
361
368 private function verify()
369 {
370 $this->errors = array();
371
372 $result = 0;
373
374 // Clean parameters
375 if (isset($this->ref)) {
376 $this->ref = trim($this->ref);
377 }
378
379 if (isset($this->track_id)) {
380 $this->track_id = trim($this->track_id);
381 }
382
383 if (isset($this->fk_soc)) {
384 $this->fk_soc = (int) $this->fk_soc;
385 }
386
387 if (isset($this->fk_project)) {
388 $this->fk_project = (int) $this->fk_project;
389 }
390
391 if (isset($this->origin_email)) {
392 $this->origin_email = trim($this->origin_email);
393 }
394
395 if (isset($this->fk_user_create)) {
396 $this->fk_user_create = (int) $this->fk_user_create;
397 }
398
399 if (isset($this->fk_user_assign)) {
400 $this->fk_user_assign = (int) $this->fk_user_assign;
401 }
402
403 if (isset($this->subject)) {
404 $this->subject = trim($this->subject);
405 }
406
407 if (isset($this->message)) {
408 $this->message = trim($this->message);
409 if (dol_strlen($this->message) > 65000) {
410 $this->errors[] = 'ErrorFieldTooLong';
411 dol_syslog(get_class($this).'::create error -1 message too long', LOG_ERR);
412 $result = -1;
413 }
414 }
415
416 if (isset($this->fk_statut)) {
417 $this->fk_statut = (int) $this->fk_statut;
418 }
419
420 if (isset($this->resolution)) {
421 $this->resolution = trim($this->resolution);
422 }
423
424 if (isset($this->progress)) {
425 $this->progress = trim($this->progress);
426 }
427
428 if (isset($this->timing)) {
429 $this->timing = trim($this->timing);
430 }
431
432 if (isset($this->type_code)) {
433 $this->type_code = trim($this->type_code);
434 }
435
436 if (isset($this->category_code)) {
437 $this->category_code = trim($this->category_code);
438 }
439
440 if (isset($this->severity_code)) {
441 $this->severity_code = trim($this->severity_code);
442 }
443
444 if (empty($this->ref)) {
445 $this->errors[] = 'ErrorTicketRefRequired';
446 dol_syslog(get_class($this)."::create error -1 ref null", LOG_ERR);
447 $result = -1;
448 }
449
450 return $result;
451 }
452
460 public function create($user, $notrigger = 0)
461 {
462 global $conf;
463
464 $error = 0;
465
466 // Clean parameters
467 $this->datec = dol_now();
468 if (empty($this->track_id)) {
469 $this->track_id = generate_random_id(16);
470 }
471
472 // Check more parameters
473 // If error, this->errors[] is filled
474 $result = $this->verify();
475
476 if ($result >= 0) {
477 $this->entity = ((isset($this->entity) && is_numeric($this->entity)) ? $this->entity : $conf->entity);
478
479 // Insert request
480 $sql = "INSERT INTO ".MAIN_DB_PREFIX."ticket(";
481 $sql .= "ref,";
482 $sql .= "track_id,";
483 $sql .= "fk_soc,";
484 $sql .= "fk_project,";
485 $sql .= "origin_email,";
486 $sql .= "fk_user_create,";
487 $sql .= "fk_user_assign,";
488 $sql .= "email_msgid,";
489 $sql .= "email_date,";
490 $sql .= "subject,";
491 $sql .= "message,";
492 $sql .= "fk_statut,";
493 $sql .= "resolution,";
494 $sql .= "progress,";
495 $sql .= "timing,";
496 $sql .= "type_code,";
497 $sql .= "category_code,";
498 $sql .= "severity_code,";
499 $sql .= "datec,";
500 $sql .= "date_read,";
501 $sql .= "date_close,";
502 $sql .= "entity,";
503 $sql .= "notify_tiers_at_create,";
504 $sql .= "ip";
505 $sql .= ") VALUES (";
506 $sql .= " ".(!isset($this->ref) ? '' : "'".$this->db->escape($this->ref)."'").",";
507 $sql .= " ".(!isset($this->track_id) ? 'NULL' : "'".$this->db->escape($this->track_id)."'").",";
508 $sql .= " ".($this->fk_soc > 0 ? $this->db->escape($this->fk_soc) : "null").",";
509 $sql .= " ".($this->fk_project > 0 ? $this->db->escape($this->fk_project) : "null").",";
510 $sql .= " ".(!isset($this->origin_email) ? 'NULL' : "'".$this->db->escape($this->origin_email)."'").",";
511 $sql .= " ".(!isset($this->fk_user_create) ? ($user->id > 0 ? $user->id : 'NULL') : ($this->fk_user_create > 0 ? $this->fk_user_create : 'NULL')).",";
512 $sql .= " ".($this->fk_user_assign > 0 ? $this->fk_user_assign : 'NULL').",";
513 $sql .= " ".(empty($this->email_msgid) ? 'NULL' : "'".$this->db->escape($this->email_msgid)."'").",";
514 $sql .= " ".(empty($this->email_date) ? 'NULL' : "'".$this->db->idate($this->email_date)."'").",";
515 $sql .= " ".(!isset($this->subject) ? 'NULL' : "'".$this->db->escape($this->subject)."'").",";
516 $sql .= " ".(!isset($this->message) ? 'NULL' : "'".$this->db->escape($this->message)."'").",";
517 $sql .= " ".(!isset($this->fk_statut) ? '0' : "'".$this->db->escape($this->fk_statut)."'").",";
518 $sql .= " ".(!isset($this->resolution) ? 'NULL' : "'".$this->db->escape($this->resolution)."'").",";
519 $sql .= " ".(!isset($this->progress) ? '0' : "'".$this->db->escape($this->progress)."'").",";
520 $sql .= " ".(!isset($this->timing) ? 'NULL' : "'".$this->db->escape($this->timing)."'").",";
521 $sql .= " ".(!isset($this->type_code) ? 'NULL' : "'".$this->db->escape($this->type_code)."'").",";
522 $sql .= " ".(empty($this->category_code) || $this->category_code == '-1' ? 'NULL' : "'".$this->db->escape($this->category_code)."'").",";
523 $sql .= " ".(!isset($this->severity_code) ? 'NULL' : "'".$this->db->escape($this->severity_code)."'").",";
524 $sql .= " ".(!isset($this->datec) || dol_strlen($this->datec) == 0 ? 'NULL' : "'".$this->db->idate($this->datec)."'").",";
525 $sql .= " ".(!isset($this->date_read) || dol_strlen($this->date_read) == 0 ? 'NULL' : "'".$this->db->idate($this->date_read)."'").",";
526 $sql .= " ".(!isset($this->date_close) || dol_strlen($this->date_close) == 0 ? 'NULL' : "'".$this->db->idate($this->date_close)."'");
527 $sql .= ", ".((int) $this->entity);
528 $sql .= ", ".(!isset($this->notify_tiers_at_create) ? '1' : "'".$this->db->escape($this->notify_tiers_at_create)."'");
529 $sql .= ", ".(!isset($this->ip) ? 'NULL' : "'".$this->db->escape($this->ip)."'");
530 $sql .= ")";
531
532 $this->db->begin();
533
534 dol_syslog(get_class($this)."::create", LOG_DEBUG);
535 $resql = $this->db->query($sql);
536 if (!$resql) {
537 $error++;
538 $this->errors[] = "Error ".$this->db->lasterror();
539 }
540
541 if (!$error) {
542 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."ticket");
543 }
544
545 if (!$error && getDolGlobalString('TICKET_ADD_AUTHOR_AS_CONTACT')) {
546 // add creator as contributor
547 if ($this->add_contact($user->id, 'CONTRIBUTOR', 'internal') < 0) {
548 $error++;
549 }
550 }
551
552 if (!$error && $this->fk_user_assign > 0) {
553 if ($this->add_contact($this->fk_user_assign, 'SUPPORTTEC', 'internal') < 0) {
554 $error++;
555 }
556 }
557
558
559 //Update extrafield
560 if (!$error) {
561 $result = $this->insertExtraFields();
562 if ($result < 0) {
563 $error++;
564 }
565 }
566
567 if (!$error && !$notrigger) {
568 // Call trigger
569 $result = $this->call_trigger('TICKET_CREATE', $user);
570 if ($result < 0) {
571 $error++;
572 }
573 // End call triggers
574 }
575
576 // Commit or rollback
577 if ($error) {
578 foreach ($this->errors as $errmsg) {
579 dol_syslog(get_class($this)."::create ".$errmsg, LOG_ERR);
580 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
581 }
582 $this->db->rollback();
583 return -1 * $error;
584 } else {
585 $this->db->commit();
586 return $this->id;
587 }
588 } else {
589 $this->db->rollback();
590 dol_syslog(get_class($this)."::Create fails verify ".join(',', $this->errors), LOG_WARNING);
591 return -3;
592 }
593 }
594
604 public function fetch($id = '', $ref = '', $track_id = '', $email_msgid = '')
605 {
606 global $langs;
607
608 // Check parameters
609 if (empty($id) && empty($ref) && empty($track_id) && empty($email_msgid)) {
610 $this->error = 'ErrorWrongParameters';
611 dol_print_error('', get_class($this)."::fetch ".$this->error);
612 return -1;
613 }
614
615 $sql = "SELECT";
616 $sql .= " t.rowid,";
617 $sql .= " t.entity,";
618 $sql .= " t.ref,";
619 $sql .= " t.track_id,";
620 $sql .= " t.fk_soc,";
621 $sql .= " t.fk_project,";
622 $sql .= " t.origin_email,";
623 $sql .= " t.fk_user_create,";
624 $sql .= " t.fk_user_assign,";
625 $sql .= " t.email_msgid,";
626 $sql .= " t.email_date,";
627 $sql .= " t.subject,";
628 $sql .= " t.message,";
629 $sql .= " t.fk_statut as status,";
630 $sql .= " t.resolution,";
631 $sql .= " t.progress,";
632 $sql .= " t.timing,";
633 $sql .= " t.type_code,";
634 $sql .= " t.category_code,";
635 $sql .= " t.severity_code,";
636 $sql .= " t.datec,";
637 $sql .= " t.date_read,";
638 $sql .= " t.date_last_msg_sent,";
639 $sql .= " t.date_close,";
640 $sql .= " t.tms,";
641 $sql .= " t.ip,";
642 $sql .= " type.label as type_label, category.label as category_label, severity.label as severity_label";
643 $sql .= " FROM ".MAIN_DB_PREFIX."ticket as t";
644 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticket_type as type ON type.code=t.type_code";
645 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticket_category as category ON category.code=t.category_code";
646 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticket_severity as severity ON severity.code=t.severity_code";
647
648 if ($id) {
649 $sql .= " WHERE t.rowid = ".((int) $id);
650 } else {
651 $sql .= " WHERE t.entity IN (".getEntity($this->element, 1).")";
652 if (!empty($ref)) {
653 $sql .= " AND t.ref = '".$this->db->escape($ref)."'";
654 } elseif ($track_id) {
655 $sql .= " AND t.track_id = '".$this->db->escape($track_id)."'";
656 } else {
657 $sql .= " AND t.email_msgid = '".$this->db->escape($email_msgid)."'";
658 }
659 }
660
661 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
662 $resql = $this->db->query($sql);
663 if ($resql) {
664 if ($this->db->num_rows($resql)) {
665 $obj = $this->db->fetch_object($resql);
666
667 $this->id = $obj->rowid;
668 $this->entity = $obj->entity;
669 $this->ref = $obj->ref;
670 $this->track_id = $obj->track_id;
671 $this->fk_soc = $obj->fk_soc;
672 $this->socid = $obj->fk_soc; // for fetch_thirdparty() method
673 $this->fk_project = $obj->fk_project;
674 $this->origin_email = $obj->origin_email;
675 $this->fk_user_create = $obj->fk_user_create;
676 $this->fk_user_assign = $obj->fk_user_assign;
677 $this->email_msgid = $obj->email_msgid;
678 $this->email_date = $this->db->jdate($obj->email_date);
679 $this->subject = $obj->subject;
680 $this->message = $obj->message;
681 $this->ip = $obj->ip;
682
683 $this->status = $obj->status;
684 $this->fk_statut = $this->status; // For backward compatibility
685
686 $this->resolution = $obj->resolution;
687 $this->progress = $obj->progress;
688 $this->timing = $obj->timing;
689
690 $this->type_code = $obj->type_code;
691 $label_type = ($langs->trans("TicketTypeShort".$obj->type_code) != ("TicketTypeShort".$obj->type_code) ? $langs->trans("TicketTypeShort".$obj->type_code) : ($obj->type_label != '-' ? $obj->type_label : ''));
692 $this->type_label = $label_type;
693
694 $this->category_code = $obj->category_code;
695 $label_category = ($langs->trans("TicketCategoryShort".$obj->category_code) != ("TicketCategoryShort".$obj->category_code) ? $langs->trans("TicketCategoryShort".$obj->category_code) : ($obj->category_label != '-' ? $obj->category_label : ''));
696 $this->category_label = $label_category;
697
698 $this->severity_code = $obj->severity_code;
699 $label_severity = ($langs->trans("TicketSeverityShort".$obj->severity_code) != ("TicketSeverityShort".$obj->severity_code) ? $langs->trans("TicketSeverityShort".$obj->severity_code) : ($obj->severity_label != '-' ? $obj->severity_label : ''));
700 $this->severity_label = $label_severity;
701
702 $this->datec = $this->db->jdate($obj->datec);
703 $this->date_creation = $this->db->jdate($obj->datec);
704 $this->date_read = $this->db->jdate($obj->date_read);
705 $this->date_validation = $this->db->jdate($obj->date_read);
706 $this->date_last_msg_sent = $this->db->jdate($obj->date_last_msg_sent);
707 $this->date_close = $this->db->jdate($obj->date_close);
708 $this->tms = $this->db->jdate($obj->tms);
709 $this->date_modification = $this->db->jdate($obj->tms);
710
711 $this->fetch_optionals();
712
713 $this->db->free($resql);
714 return 1;
715 } else {
716 return 0;
717 }
718 } else {
719 $this->error = "Error ".$this->db->lasterror();
720 dol_syslog(get_class($this)."::fetch ".$this->error, LOG_ERR);
721 return -1;
722 }
723 }
724
738 public function fetchAll($user, $sortorder = 'ASC', $sortfield = 't.datec', $limit = '', $offset = 0, $arch = '', $filter = '')
739 {
740 global $langs;
741
742 $extrafields = new ExtraFields($this->db);
743
744 // fetch optionals attributes and labels
745 $extrafields->fetch_name_optionals_label($this->table_element);
746
747 $sql = "SELECT";
748 $sql .= " t.rowid,";
749 $sql .= " t.ref,";
750 $sql .= " t.track_id,";
751 $sql .= " t.fk_soc,";
752 $sql .= " t.fk_project,";
753 $sql .= " t.origin_email,";
754 $sql .= " t.fk_user_create, uc.lastname as user_create_lastname, uc.firstname as user_create_firstname,";
755 $sql .= " t.fk_user_assign, ua.lastname as user_assign_lastname, ua.firstname as user_assign_firstname,";
756 $sql .= " t.subject,";
757 $sql .= " t.message,";
758 $sql .= " t.fk_statut,";
759 $sql .= " t.resolution,";
760 $sql .= " t.progress,";
761 $sql .= " t.timing,";
762 $sql .= " t.type_code,";
763 $sql .= " t.category_code,";
764 $sql .= " t.severity_code,";
765 $sql .= " t.datec,";
766 $sql .= " t.date_read,";
767 $sql .= " t.date_last_msg_sent,";
768 $sql .= " t.date_close,";
769 $sql .= " t.tms";
770 $sql .= ", type.label as type_label, category.label as category_label, severity.label as severity_label";
771 // Add fields for extrafields
772 foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {
773 $sql .= ($extrafields->attributes[$this->table_element]['type'][$key] != 'separate' ? ",ef.".$key." as options_".$key : '');
774 }
775 $sql .= " FROM ".MAIN_DB_PREFIX."ticket as t";
776 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticket_type as type ON type.code=t.type_code";
777 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticket_category as category ON category.code=t.category_code";
778 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticket_severity as severity ON severity.code=t.severity_code";
779 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON s.rowid=t.fk_soc";
780 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user as uc ON uc.rowid=t.fk_user_create";
781 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user as ua ON ua.rowid=t.fk_user_assign";
782 if (is_array($extrafields->attributes[$this->table_element]['label']) && count($extrafields->attributes[$this->table_element]['label'])) {
783 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."ticket_extrafields as ef on (t.rowid = ef.fk_object)";
784 }
785 if (empty($user->rights->societe->client->voir) && !$user->socid) {
786 $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
787 }
788
789 $sql .= " WHERE t.entity IN (".getEntity('ticket').")";
790
791 // Manage filter
792 if (!empty($filter)) {
793 foreach ($filter as $key => $value) {
794 if (strpos($key, 'date')) { // To allow $filter['YEAR(s.dated)']=>$year
795 $sql .= " AND ".$key." = '".$this->db->escape($value)."'";
796 } elseif (($key == 't.fk_user_assign') || ($key == 't.type_code') || ($key == 't.category_code') || ($key == 't.severity_code') || ($key == 't.fk_soc')) {
797 $sql .= " AND ".$key." = '".$this->db->escape($value)."'";
798 } elseif ($key == 't.fk_statut') {
799 if (is_array($value) && count($value) > 0) {
800 $sql .= " AND ".$key." IN (".$this->db->sanitize(implode(',', $value)).")";
801 } else {
802 $sql .= " AND ".$key.' = '.((int) $value);
803 }
804 } else {
805 $sql .= " AND ".$key." LIKE '%".$this->db->escape($value)."%'";
806 }
807 }
808 }
809 if (empty($user->rights->societe->client->voir) && !$user->socid) {
810 $sql .= " AND t.fk_soc = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
811 } elseif ($user->socid) {
812 $sql .= " AND t.fk_soc = ".((int) $user->socid);
813 }
814
815 $sql .= $this->db->order($sortfield, $sortorder);
816 if (!empty($limit)) {
817 $sql .= $this->db->plimit($limit + 1, $offset);
818 }
819
820 dol_syslog(get_class($this)."::fetchAll", LOG_DEBUG);
821 $resql = $this->db->query($sql);
822
823 if ($resql) {
824 $this->lines = array();
825
826 $num = $this->db->num_rows($resql);
827 $i = 0;
828
829 if ($num) {
830 while ($i < $num) {
831 $obj = $this->db->fetch_object($resql);
832
833 $line = new TicketsLine();
834
835 $line->id = $obj->rowid;
836 $line->rowid = $obj->rowid;
837 $line->ref = $obj->ref;
838 $line->track_id = $obj->track_id;
839 $line->fk_soc = $obj->fk_soc;
840 $line->fk_project = $obj->fk_project;
841 $line->origin_email = $obj->origin_email;
842
843 $line->fk_user_create = $obj->fk_user_create;
844 $line->user_create_lastname = $obj->user_create_lastname;
845 $line->user_create_firstname = $obj->user_create_firstname;
846
847 $line->fk_user_assign = $obj->fk_user_assign;
848 $line->user_assign_lastname = $obj->user_assign_lastname;
849 $line->user_assign_firstname = $obj->user_assign_firstname;
850
851 $line->subject = $obj->subject;
852 $line->message = $obj->message;
853 $line->fk_statut = $obj->fk_statut;
854 $line->resolution = $obj->resolution;
855 $line->progress = $obj->progress;
856 $line->timing = $obj->timing;
857
858 $label_type = ($langs->trans("TicketTypeShort".$obj->type_code) != ("TicketTypeShort".$obj->type_code) ? $langs->trans("TicketTypeShort".$obj->type_code) : ($obj->type_label != '-' ? $obj->type_label : ''));
859 $line->type_label = $label_type;
860
861 $this->category_code = $obj->category_code;
862 $label_category = ($langs->trans("TicketCategoryShort".$obj->category_code) != ("TicketCategoryShort".$obj->category_code) ? $langs->trans("TicketCategoryShort".$obj->category_code) : ($obj->category_label != '-' ? $obj->category_label : ''));
863 $line->category_label = $label_category;
864
865 $this->severity_code = $obj->severity_code;
866 $label_severity = ($langs->trans("TicketSeverityShort".$obj->severity_code) != ("TicketSeverityShort".$obj->severity_code) ? $langs->trans("TicketSeverityShort".$obj->severity_code) : ($obj->severity_label != '-' ? $obj->severity_label : ''));
867 $line->severity_label = $label_severity;
868
869 $line->datec = $this->db->jdate($obj->datec);
870 $line->date_read = $this->db->jdate($obj->date_read);
871 $line->date_last_msg_sent = $this->db->jdate($obj->date_last_msg_sent);
872 $line->date_close = $this->db->jdate($obj->date_close);
873
874 // Extra fields
875 if (is_array($extrafields->attributes[$this->table_element]['label']) && count($extrafields->attributes[$this->table_element]['label'])) {
876 foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {
877 $tmpkey = 'options_'.$key;
878 $line->{$tmpkey} = $obj->$tmpkey;
879 }
880 }
881
882 $this->lines[$i] = $line;
883 $i++;
884 }
885 }
886 $this->db->free($resql);
887 return $num;
888 } else {
889 $this->error = "Error ".$this->db->lasterror();
890 dol_syslog(get_class($this)."::fetchAll ".$this->error, LOG_ERR);
891 return -1;
892 }
893 }
894
902 public function update($user = 0, $notrigger = 0)
903 {
904 global $conf, $langs, $hookmanager;
905 $error = 0;
906
907 // $this->oldcopy should have been set by the caller of update (here properties were already modified)
908 //if (empty($this->oldcopy)) {
909 // $this->oldcopy = dol_clone($this);
910 //}
911
912 // Clean parameters
913 if (isset($this->ref)) {
914 $this->ref = trim($this->ref);
915 }
916
917 if (isset($this->track_id)) {
918 $this->track_id = trim($this->track_id);
919 }
920
921 if (isset($this->fk_soc)) {
922 $this->fk_soc = (int) $this->fk_soc;
923 }
924
925 if (isset($this->fk_project)) {
926 $this->fk_project = (int) $this->fk_project;
927 }
928
929 if (isset($this->origin_email)) {
930 $this->origin_email = trim($this->origin_email);
931 }
932
933 if (isset($this->fk_user_create)) {
934 $this->fk_user_create = (int) $this->fk_user_create;
935 }
936
937 if (isset($this->fk_user_assign)) {
938 $this->fk_user_assign = (int) $this->fk_user_assign;
939 }
940
941 if (isset($this->subject)) {
942 $this->subject = trim($this->subject);
943 }
944
945 if (isset($this->message)) {
946 $this->message = trim($this->message);
947 if (dol_strlen($this->message) > 65000) {
948 $this->errors[] = 'ErrorFieldTooLong';
949 dol_syslog(get_class($this).'::update error -1 message too long', LOG_ERR);
950 return -1;
951 }
952 }
953
954 if (isset($this->fk_statut)) {
955 $this->fk_statut = (int) $this->fk_statut;
956 }
957
958 if (isset($this->resolution)) {
959 $this->resolution = trim($this->resolution);
960 }
961
962 if (isset($this->progress)) {
963 $this->progress = trim($this->progress);
964 }
965
966 if (isset($this->timing)) {
967 $this->timing = trim($this->timing);
968 }
969
970 if (isset($this->type_code)) {
971 $this->timing = trim($this->type_code);
972 }
973
974 if (isset($this->category_code)) {
975 $this->timing = trim($this->category_code);
976 }
977
978 if (isset($this->severity_code)) {
979 $this->timing = trim($this->severity_code);
980 }
981
982 // Check parameters
983 // Put here code to add a control on parameters values
984 // Update request
985 $sql = "UPDATE ".MAIN_DB_PREFIX."ticket SET";
986 $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "").",";
987 $sql .= " track_id=".(isset($this->track_id) ? "'".$this->db->escape($this->track_id)."'" : "null").",";
988 $sql .= " fk_soc=".(isset($this->fk_soc) ? "'".$this->db->escape($this->fk_soc)."'" : "null").",";
989 $sql .= " fk_project=".(isset($this->fk_project) ? "'".$this->db->escape($this->fk_project)."'" : "null").",";
990 $sql .= " origin_email=".(isset($this->origin_email) ? "'".$this->db->escape($this->origin_email)."'" : "null").",";
991 $sql .= " fk_user_create=".(isset($this->fk_user_create) ? $this->fk_user_create : "null").",";
992 $sql .= " fk_user_assign=".(isset($this->fk_user_assign) ? $this->fk_user_assign : "null").",";
993 $sql .= " subject=".(isset($this->subject) ? "'".$this->db->escape($this->subject)."'" : "null").",";
994 $sql .= " message=".(isset($this->message) ? "'".$this->db->escape($this->message)."'" : "null").",";
995 $sql .= " fk_statut=".(isset($this->fk_statut) ? $this->fk_statut : "0").",";
996 $sql .= " resolution=".(isset($this->resolution) ? $this->resolution : "null").",";
997 $sql .= " progress=".(isset($this->progress) ? "'".$this->db->escape($this->progress)."'" : "null").",";
998 $sql .= " timing=".(isset($this->timing) ? "'".$this->db->escape($this->timing)."'" : "null").",";
999 $sql .= " type_code=".(isset($this->type_code) ? "'".$this->db->escape($this->type_code)."'" : "null").",";
1000 $sql .= " category_code=".(isset($this->category_code) ? "'".$this->db->escape($this->category_code)."'" : "null").",";
1001 $sql .= " severity_code=".(isset($this->severity_code) ? "'".$this->db->escape($this->severity_code)."'" : "null").",";
1002 $sql .= " datec=".(dol_strlen($this->datec) != 0 ? "'".$this->db->idate($this->datec)."'" : 'null').",";
1003 $sql .= " date_read=".(dol_strlen($this->date_read) != 0 ? "'".$this->db->idate($this->date_read)."'" : 'null').",";
1004 $sql .= " date_last_msg_sent=".(dol_strlen($this->date_last_msg_sent) != 0 ? "'".$this->db->idate($this->date_last_msg_sent)."'" : 'null').",";
1005 $sql .= " date_close=".(dol_strlen($this->date_close) != 0 ? "'".$this->db->idate($this->date_close)."'" : 'null');
1006 $sql .= " WHERE rowid=".((int) $this->id);
1007
1008 $this->db->begin();
1009
1010 $resql = $this->db->query($sql);
1011 if (!$resql) {
1012 $error++;
1013 $this->errors[] = "Error ".$this->db->lasterror();
1014 }
1015
1016 if (!$error) {
1017 // Update extrafields
1018 $result = $this->insertExtraFields();
1019 if ($result < 0) {
1020 $error++;
1021 }
1022 }
1023
1024 if (!$error && !$notrigger) {
1025 // Call trigger
1026 $result = $this->call_trigger('TICKET_MODIFY', $user);
1027 if ($result < 0) {
1028 $error++;
1029 }
1030 // End call triggers
1031 }
1032
1033 // Commit or rollback
1034 if ($error) {
1035 foreach ($this->errors as $errmsg) {
1036 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1037 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1038 }
1039 $this->db->rollback();
1040 return -1 * $error;
1041 } else {
1042 $this->db->commit();
1043 return 1;
1044 }
1045 }
1046
1054 public function delete($user, $notrigger = 0)
1055 {
1056 global $conf, $langs;
1057 $error = 0;
1058
1059 $this->db->begin();
1060
1061 if (!$error) {
1062 if (!$notrigger) {
1063 // Call trigger
1064 $result = $this->call_trigger('TICKET_DELETE', $user);
1065 if ($result < 0) {
1066 $error++;
1067 }
1068 // End call triggers
1069 }
1070 }
1071
1072 if (!$error) {
1073 // Delete linked contacts
1074 $res = $this->delete_linked_contact();
1075 if ($res < 0) {
1076 dol_syslog(get_class($this)."::delete error", LOG_ERR);
1077 $error++;
1078 }
1079 }
1080
1081 if (!$error) {
1082 // Delete linked object
1083 $res = $this->deleteObjectLinked();
1084 if ($res < 0) {
1085 $error++;
1086 }
1087 }
1088
1089 // Removed extrafields
1090 if (!$error) {
1091 $result = $this->deleteExtraFields();
1092 if ($result < 0) {
1093 $error++;
1094 dol_syslog(get_class($this)."::delete error -3 ".$this->error, LOG_ERR);
1095 }
1096 }
1097
1098 // Delete all child tables
1099
1100 if (!$error) {
1101 $sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie_ticket";
1102 $sql .= " WHERE fk_ticket = ".(int) $this->id;
1103
1104 $result = $this->db->query($sql);
1105 if (!$result) {
1106 $error++;
1107 $this->errors[] = $this->db->lasterror();
1108 }
1109 }
1110
1111 if (!$error) {
1112 $sql = "DELETE FROM ".MAIN_DB_PREFIX."ticket";
1113 $sql .= " WHERE rowid=".((int) $this->id);
1114
1115 dol_syslog(get_class($this)."::delete sql=".$sql);
1116 $resql = $this->db->query($sql);
1117 if (!$resql) {
1118 $error++;
1119 $this->errors[] = "Error ".$this->db->lasterror();
1120 }
1121 }
1122
1123 // Commit or rollback
1124 if ($error) {
1125 foreach ($this->errors as $errmsg) {
1126 dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
1127 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1128 }
1129 $this->db->rollback();
1130 return -1 * $error;
1131 } else {
1132 $this->db->commit();
1133 return 1;
1134 }
1135 }
1136
1144 public function createFromClone(User $user, $fromid)
1145 {
1146 $error = 0;
1147
1148 $object = new Ticket($this->db);
1149
1150 $this->db->begin();
1151
1152 // Load source object
1153 $object->fetch($fromid);
1154 $object->id = 0;
1155 $object->statut = 0;
1156
1157 // Clear fields
1158 // ...
1159 // Create clone
1160 $object->context['createfromclone'] = 'createfromclone';
1161 $result = $object->create($user);
1162
1163 // Other options
1164 if ($result < 0) {
1165 $this->error = $object->error;
1166 $error++;
1167 }
1168
1169 if (!$error) {
1170 }
1171
1172 unset($object->context['createfromclone']);
1173
1174 // End
1175 if (!$error) {
1176 $this->db->commit();
1177 return $object->id;
1178 } else {
1179 $this->db->rollback();
1180 return -1;
1181 }
1182 }
1183
1190 public function initAsSpecimen()
1191 {
1192 $this->id = 0;
1193 $this->entity = 1;
1194 $this->ref = 'TI0501-001';
1195 $this->track_id = 'XXXXaaaa';
1196 $this->origin_email = 'email@email.com';
1197 $this->fk_project = 1;
1198 $this->fk_user_create = 1;
1199 $this->fk_user_assign = 1;
1200 $this->subject = 'Subject of ticket';
1201 $this->message = 'Message of ticket';
1202 $this->status = 0;
1203 $this->resolution = '1';
1204 $this->progress = '10';
1205 //$this->timing = '30';
1206 $this->type_code = 'TYPECODE';
1207 $this->category_code = 'CATEGORYCODE';
1208 $this->severity_code = 'SEVERITYCODE';
1209 $this->datec = '';
1210 $this->date_read = '';
1211 $this->date_last_msg_sent = '';
1212 $this->date_close = '';
1213 $this->tms = '';
1214 return 1;
1215 }
1216
1223 public function printSelectStatus($selected = "")
1224 {
1225 print Form::selectarray('search_fk_statut', $this->statuts_short, $selected, $show_empty = 1, $key_in_label = 0, $value_as_key = 0, $option = '', $translate = 1, $maxlen = 0, $disabled = 0, $sort = '', $morecss = '');
1226 }
1227
1228
1234 public function loadCacheTypesTickets()
1235 {
1236 global $langs;
1237
1238 if (!empty($this->cache_types_tickets) && count($this->cache_types_tickets)) {
1239 return 0;
1240 }
1241 // Cache deja charge
1242
1243 $sql = "SELECT rowid, code, label, use_default, pos, description";
1244 $sql .= " FROM ".MAIN_DB_PREFIX."c_ticket_type";
1245 $sql .= " WHERE entity IN (".getEntity('c_ticket_type').")";
1246 $sql .= " AND active > 0";
1247 $sql .= " ORDER BY pos";
1248 dol_syslog(get_class($this)."::load_cache_type_tickets", LOG_DEBUG);
1249 $resql = $this->db->query($sql);
1250 if ($resql) {
1251 $num = $this->db->num_rows($resql);
1252 $i = 0;
1253 while ($i < $num) {
1254 $obj = $this->db->fetch_object($resql);
1255 $label = ($langs->trans("TicketTypeShort".$obj->code) != ("TicketTypeShort".$obj->code) ? $langs->trans("TicketTypeShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
1256 $this->cache_types_tickets[$obj->rowid]['code'] = $obj->code;
1257 $this->cache_types_tickets[$obj->rowid]['label'] = $label;
1258 $this->cache_types_tickets[$obj->rowid]['use_default'] = $obj->use_default;
1259 $this->cache_types_tickets[$obj->rowid]['pos'] = $obj->pos;
1260 $i++;
1261 }
1262 return $num;
1263 } else {
1264 dol_print_error($this->db);
1265 return -1;
1266 }
1267 }
1268
1275 public function loadCacheCategoriesTickets($publicgroup = -1)
1276 {
1277 global $conf, $langs;
1278
1279 if ($publicgroup == -1 && !empty($this->cache_category_ticket) && count($this->cache_category_tickets)) {
1280 // Cache already loaded
1281 return 0;
1282 }
1283
1284 $sql = "SELECT rowid, code, label, use_default, pos, description, public, active, force_severity, fk_parent";
1285 $sql .= " FROM ".MAIN_DB_PREFIX."c_ticket_category";
1286 $sql .= " WHERE entity IN (".getEntity('c_ticket_category').")";
1287 $sql .= " AND active > 0";
1288 if ($publicgroup > -1) {
1289 $sql .= " AND public = ".((int) $publicgroup);
1290 }
1291 $sql .= " ORDER BY pos";
1292
1293 dol_syslog(get_class($this)."::load_cache_categories_tickets", LOG_DEBUG);
1294
1295 $resql = $this->db->query($sql);
1296 if ($resql) {
1297 $num = $this->db->num_rows($resql);
1298 $i = 0;
1299 while ($i < $num) {
1300 $obj = $this->db->fetch_object($resql);
1301 $this->cache_category_tickets[$obj->rowid]['code'] = $obj->code;
1302 $this->cache_category_tickets[$obj->rowid]['use_default'] = $obj->use_default;
1303 $this->cache_category_tickets[$obj->rowid]['pos'] = $obj->pos;
1304 $this->cache_category_tickets[$obj->rowid]['public'] = $obj->public;
1305 $this->cache_category_tickets[$obj->rowid]['active'] = $obj->active;
1306 $this->cache_category_tickets[$obj->rowid]['force_severity'] = $obj->force_severity;
1307 $this->cache_category_tickets[$obj->rowid]['fk_parent'] = $obj->fk_parent;
1308
1309 // If translation exists, we use it to store already translated string.
1310 // Warning: You should not use this and recompute the translated string into caller code to get the value into expected language
1311 $label = ($langs->trans("TicketCategoryShort".$obj->code) != ("TicketCategoryShort".$obj->code) ? $langs->trans("TicketCategoryShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
1312 $this->cache_category_tickets[$obj->rowid]['label'] = $label;
1313
1314 $i++;
1315 }
1316 return $num;
1317 } else {
1318 dol_print_error($this->db);
1319 return -1;
1320 }
1321 }
1322
1329 {
1330 global $langs;
1331
1332 if (!empty($this->cache_severity_tickets) && count($this->cache_severity_tickets)) {
1333 return 0;
1334 }
1335 // Cache deja charge
1336
1337 $sql = "SELECT rowid, code, label, use_default, pos, description";
1338 $sql .= " FROM ".MAIN_DB_PREFIX."c_ticket_severity";
1339 $sql .= " WHERE entity IN (".getEntity('c_ticket_severity').")";
1340 $sql .= " AND active > 0";
1341 $sql .= " ORDER BY pos";
1342 dol_syslog(get_class($this)."::loadCacheSeveritiesTickets", LOG_DEBUG);
1343 $resql = $this->db->query($sql);
1344 if ($resql) {
1345 $num = $this->db->num_rows($resql);
1346 $i = 0;
1347 while ($i < $num) {
1348 $obj = $this->db->fetch_object($resql);
1349
1350 $this->cache_severity_tickets[$obj->rowid]['code'] = $obj->code;
1351 $label = ($langs->trans("TicketSeverityShort".$obj->code) != ("TicketSeverityShort".$obj->code) ? $langs->trans("TicketSeverityShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
1352 $this->cache_severity_tickets[$obj->rowid]['label'] = $label;
1353 $this->cache_severity_tickets[$obj->rowid]['use_default'] = $obj->use_default;
1354 $this->cache_severity_tickets[$obj->rowid]['pos'] = $obj->pos;
1355 $i++;
1356 }
1357 return $num;
1358 } else {
1359 dol_print_error($this->db);
1360 return -1;
1361 }
1362 }
1363
1364
1371 public function getLibStatut($mode = 0)
1372 {
1373 return $this->libStatut($this->fk_statut, $mode, 0, $this->progress);
1374 }
1375
1376
1377 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1387 public function LibStatut($status, $mode = 0, $notooltip = 0, $progress = 0)
1388 {
1389 // phpcs:enable
1390 global $langs, $hookmanager;
1391
1392 $labelStatus = $this->statuts[$status];
1393 $labelStatusShort = $this->statuts_short[$status];
1394
1395 if ($status == self::STATUS_NOT_READ) {
1396 $statusType = 'status0';
1397 } elseif ($status == self::STATUS_READ) {
1398 $statusType = 'status1';
1399 } elseif ($status == self::STATUS_ASSIGNED) {
1400 $statusType = 'status2';
1401 } elseif ($status == self::STATUS_IN_PROGRESS) {
1402 $statusType = 'status4';
1403 } elseif ($status == self::STATUS_WAITING) {
1404 $statusType = 'status7';
1405 } elseif ($status == self::STATUS_NEED_MORE_INFO) {
1406 $statusType = 'status3';
1407 } elseif ($status == self::STATUS_CANCELED) {
1408 $statusType = 'status9';
1409 } elseif ($status == self::STATUS_CLOSED) {
1410 $statusType = 'status6';
1411 } else {
1412 $labelStatus = 'Unknown';
1413 $labelStatusShort = 'Unknown';
1414 $statusType = 'status0';
1415 $mode = 0;
1416 }
1417
1418 $parameters = array(
1419 'status' => $status,
1420 'mode' => $mode,
1421 );
1422
1423 // Note that $action and $object may have been modified by hook
1424 $reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this);
1425
1426 if ($reshook > 0) {
1427 return $hookmanager->resPrint;
1428 }
1429
1430 $params = array();
1431 if ($notooltip) {
1432 $params = array('tooltip' => 'no');
1433 }
1434
1435 $labelStatus = $langs->transnoentitiesnoconv($labelStatus);
1436 $labelStatusShort = $langs->transnoentitiesnoconv($labelStatusShort);
1437
1438 if ($status == self::STATUS_IN_PROGRESS && $progress > 0) {
1439 $labelStatus .= ' ('.round($progress).'%)';
1440 $labelStatusShort .= ' ('.round($progress).'%)';
1441 }
1442
1443 return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode, '', $params);
1444 }
1445
1453 public function getTooltipContentArray($params)
1454 {
1455 global $langs;
1456
1457 $langs->load('ticket');
1458 $nofetch = !empty($params['nofetch']);
1459
1460 $datas = array();
1461 $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("Ticket").'</u>';
1462 $datas['picto'] .= ' '.$this->getLibStatut(4);
1463 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
1464 $datas['track_id'] = '<br><b>'.$langs->trans('TicketTrackId').':</b> '.$this->track_id;
1465 $datas['subject'] = '<br><b>'.$langs->trans('Subject').':</b> '.$this->subject;
1466 if ($this->date_creation) {
1467 $datas['date_creation'] = '<br><b>'.$langs->trans('DateCreation').':</b> '.dol_print_date($this->date_creation, 'dayhour');
1468 }
1469 if ($this->date_modification) {
1470 $datas['date_modification'] = '<br><b>'.$langs->trans('DateModification').':</b> '.dol_print_date($this->date_modification, 'dayhour');
1471 }
1472 // show categories for this record only in ajax to not overload lists
1473 if (isModEnabled('categorie') && !$nofetch) {
1474 require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
1475 $form = new Form($this->db);
1476 $datas['categories'] = '<br>' . $form->showCategories($this->id, Categorie::TYPE_TICKET, 1);
1477 }
1478
1479 return $datas;
1480 }
1481
1492 public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
1493 {
1494 global $conf, $langs;
1495
1496 if (!empty($conf->dol_no_mouse_hover)) {
1497 $notooltip = 1; // Force disable tooltips
1498 }
1499
1500 $result = '';
1501
1502 $params = [
1503 'id' => $this->id,
1504 'objecttype' => $this->element,
1505 'option' => $option,
1506 'nofetch' => 1,
1507 ];
1508 $classfortooltip = 'classfortooltip';
1509 $dataparams = '';
1510 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
1511 $classfortooltip = 'classforajaxtooltip';
1512 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
1513 $label = '';
1514 } else {
1515 $label = implode($this->getTooltipContentArray($params));
1516 }
1517
1518 $url = DOL_URL_ROOT.'/ticket/card.php?id='.$this->id;
1519
1520 if ($option != 'nolink') {
1521 // Add param to save lastsearch_values or not
1522 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1523 if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1524 $add_save_lastsearch_values = 1;
1525 }
1526 if ($add_save_lastsearch_values) {
1527 $url .= '&save_lastsearch_values=1';
1528 }
1529 }
1530
1531 $linkclose = '';
1532 if (empty($notooltip)) {
1533 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1534 $label = $langs->trans("ShowTicket");
1535 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
1536 }
1537 $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
1538 $linkclose .= $dataparams.' class="'.$classfortooltip.($morecss ? ' '.$morecss : '').'"';
1539 } else {
1540 $linkclose = ($morecss ? ' class="'.$morecss.'"' : '');
1541 }
1542
1543 $linkstart = '<a href="'.$url.'"';
1544 $linkstart .= $linkclose.'>';
1545 $linkend = '</a>';
1546
1547 $result .= $linkstart;
1548 if ($withpicto) {
1549 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
1550 }
1551 if ($withpicto != 2) {
1552 $result .= $this->ref;
1553 }
1554 $result .= $linkend;
1555 //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
1556
1557 return $result;
1558 }
1559
1560
1568 public function markAsRead($user, $notrigger = 0)
1569 {
1570 global $langs;
1571
1572 $error = 0;
1573
1574 if ($this->statut != self::STATUS_CANCELED) { // no closed
1575 $this->oldcopy = dol_clone($this);
1576
1577 $this->db->begin();
1578
1579 $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
1580 $sql .= " SET fk_statut = ".Ticket::STATUS_READ.", date_read = '".$this->db->idate(dol_now())."'";
1581 $sql .= " WHERE rowid = ".((int) $this->id);
1582
1583 dol_syslog(get_class($this)."::markAsRead");
1584 $resql = $this->db->query($sql);
1585 if ($resql) {
1586 $this->actionmsg = $langs->trans('TicketLogMesgReadBy', $this->ref, $user->getFullName($langs));
1587 $this->actionmsg2 = $langs->trans('TicketLogMesgReadBy', $this->ref, $user->getFullName($langs));
1588
1589 if (!$error && !$notrigger) {
1590 // Call trigger
1591 $result = $this->call_trigger('TICKET_MODIFY', $user);
1592 if ($result < 0) {
1593 $error++;
1594 }
1595 // End call triggers
1596 }
1597
1598 if (!$error) {
1599 $this->db->commit();
1600 return 1;
1601 } else {
1602 $this->db->rollback();
1603 $this->error = join(',', $this->errors);
1604 dol_syslog(get_class($this)."::markAsRead ".$this->error, LOG_ERR);
1605 return -1;
1606 }
1607 } else {
1608 $this->db->rollback();
1609 $this->error = $this->db->lasterror();
1610 dol_syslog(get_class($this)."::markAsRead ".$this->error, LOG_ERR);
1611 return -1;
1612 }
1613 }
1614
1615 return 0;
1616 }
1617
1626 public function assignUser($user, $id_assign_user, $notrigger = 0)
1627 {
1628 global $conf, $langs;
1629
1630 $error = 0;
1631
1632 $this->oldcopy = dol_clone($this);
1633
1634 $this->db->begin();
1635
1636 $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
1637 if ($id_assign_user > 0) {
1638 $sql .= " SET fk_user_assign=".((int) $id_assign_user).", fk_statut = ".Ticket::STATUS_ASSIGNED;
1639 } else {
1640 $sql .= " SET fk_user_assign=null, fk_statut = ".Ticket::STATUS_READ;
1641 }
1642 $sql .= " WHERE rowid = ".((int) $this->id);
1643
1644 dol_syslog(get_class($this)."::assignUser sql=".$sql);
1645 $resql = $this->db->query($sql);
1646 if ($resql) {
1647 $this->fk_user_assign = $id_assign_user; // May be used by trigger
1648
1649 if (!$notrigger) {
1650 // Call trigger
1651 $result = $this->call_trigger('TICKET_ASSIGNED', $user);
1652 if ($result < 0) {
1653 $error++;
1654 }
1655 // End call triggers
1656 }
1657
1658 if (!$error) {
1659 $this->db->commit();
1660 return 1;
1661 } else {
1662 $this->db->rollback();
1663 $this->error = join(',', $this->errors);
1664 dol_syslog(get_class($this)."::assignUser ".$this->error, LOG_ERR);
1665 return -1;
1666 }
1667 } else {
1668 $this->db->rollback();
1669 $this->error = $this->db->lasterror();
1670 dol_syslog(get_class($this)."::assignUser ".$this->error, LOG_ERR);
1671 return -1;
1672 }
1673 }
1674
1686 public function createTicketMessage($user, $notrigger = 0, $filename_list = array(), $mimetype_list = array(), $mimefilename_list = array(), $send_email = false)
1687 {
1688 global $conf, $langs;
1689 $error = 0;
1690
1691 $now = dol_now();
1692
1693 // Clean parameters
1694 if (isset($this->fk_track_id)) {
1695 $this->fk_track_id = trim($this->fk_track_id);
1696 }
1697
1698 if (isset($this->message)) {
1699 $this->message = trim($this->message);
1700 }
1701
1702 $this->db->begin();
1703
1704 // Insert entry into agenda with code 'TICKET_MSG'
1705 include_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
1706 $actioncomm = new ActionComm($this->db);
1707 $actioncomm->type_code = 'AC_OTH_AUTO'; // This is not an entry that must appears into manual calendar but only into CRM calendar
1708 $actioncomm->code = 'TICKET_MSG';
1709 if ($this->private) {
1710 $actioncomm->code = 'TICKET_MSG_PRIVATE';
1711 }
1712 if ($send_email) {
1713 $actioncomm->code .= '_SENTBYMAIL';
1714 }
1715 if ((empty($user->id) || $user->id == 0) && isset($_SESSION['email_customer'])) {
1716 $actioncomm->email_from = $_SESSION['email_customer'];
1717 }
1718 $actioncomm->socid = $this->socid;
1719 $actioncomm->label = $this->subject;
1720 $actioncomm->note_private = $this->message;
1721 $actioncomm->userassigned = array($user->id);
1722 $actioncomm->userownerid = $user->id;
1723 $actioncomm->datep = $now;
1724 $actioncomm->percentage = -1; // percentage is not relevant for punctual events
1725 $actioncomm->elementtype = 'ticket';
1726 $actioncomm->fk_element = $this->id;
1727 $actioncomm->fk_project = $this->fk_project;
1728
1729 $attachedfiles = array();
1730 $attachedfiles['paths'] = $filename_list;
1731 $attachedfiles['names'] = $mimefilename_list;
1732 $attachedfiles['mimes'] = $mimetype_list;
1733 if (is_array($attachedfiles) && count($attachedfiles) > 0) {
1734 $actioncomm->attachedfiles = $attachedfiles;
1735 }
1736
1737 if (!empty($mimefilename_list) && is_array($mimefilename_list)) {
1738 $actioncomm->note_private = dol_concatdesc($actioncomm->note_private, "\n".$langs->transnoentities("AttachedFiles").': '.join(';', $mimefilename_list));
1739 }
1740
1741 $actionid = $actioncomm->create($user);
1742 if ($actionid <= 0) {
1743 $error++;
1744 $this->error = $actioncomm->error;
1745 $this->errors = $actioncomm->errors;
1746 }
1747
1748 // Commit or rollback
1749 if ($error) {
1750 $this->db->rollback();
1751 return -1 * $error;
1752 } else {
1753 $this->db->commit();
1754 return 1;
1755 }
1756 }
1757
1763 public function loadCacheMsgsTicket()
1764 {
1765 if (!empty($this->cache_msgs_ticket) && is_array($this->cache_msgs_ticket) && count($this->cache_msgs_ticket)) {
1766 return 0;
1767 }
1768
1769 // Cache already loaded
1770
1771 $sql = "SELECT id as rowid, fk_user_author, email_from, datec, datep, label, note as message, code";
1772 $sql .= " FROM ".MAIN_DB_PREFIX."actioncomm";
1773 $sql .= " WHERE fk_element = ".(int) $this->id;
1774 $sql .= " AND elementtype = 'ticket'";
1775 $sql .= " ORDER BY datep DESC";
1776
1777 dol_syslog(get_class($this)."::load_cache_actions_ticket", LOG_DEBUG);
1778 $resql = $this->db->query($sql);
1779 if ($resql) {
1780 $num = $this->db->num_rows($resql);
1781 $i = 0;
1782 while ($i < $num) {
1783 $obj = $this->db->fetch_object($resql);
1784 $this->cache_msgs_ticket[$i]['id'] = $obj->rowid;
1785 $this->cache_msgs_ticket[$i]['fk_user_author'] = $obj->fk_user_author;
1786 if ($obj->code == 'TICKET_MSG' && empty($obj->fk_user_author)) {
1787 $this->cache_msgs_ticket[$i]['fk_contact_author'] = $obj->email_from;
1788 }
1789 $this->cache_msgs_ticket[$i]['datec'] = $this->db->jdate($obj->datec);
1790 $this->cache_msgs_ticket[$i]['datep'] = $this->db->jdate($obj->datep);
1791 $this->cache_msgs_ticket[$i]['subject'] = $obj->label;
1792 $this->cache_msgs_ticket[$i]['message'] = $obj->message;
1793 $this->cache_msgs_ticket[$i]['private'] = (preg_match('/^TICKET_MSG_PRIVATE/', $obj->code) ? 1 : 0);
1794 $i++;
1795 }
1796 return $num;
1797 } else {
1798 $this->error = "Error ".$this->db->lasterror();
1799 dol_syslog(get_class($this)."::load_cache_actions_ticket ".$this->error, LOG_ERR);
1800 return -1;
1801 }
1802 }
1803
1811 public function close(User $user, $mode = 0)
1812 {
1813 global $conf;
1814
1815 if ($this->status != Ticket::STATUS_CLOSED && $this->status != Ticket::STATUS_CANCELED) { // not closed
1816 $this->db->begin();
1817
1818 $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
1819 $sql .= " SET fk_statut=".($mode ? Ticket::STATUS_CANCELED : Ticket::STATUS_CLOSED).", progress=100, date_close='".$this->db->idate(dol_now())."'";
1820 $sql .= " WHERE rowid = ".((int) $this->id);
1821
1822 dol_syslog(get_class($this)."::close mode=".$mode);
1823 $resql = $this->db->query($sql);
1824 if ($resql) {
1825 $error = 0;
1826
1827 // Valid and close fichinter linked
1828 if (isModEnabled('ficheinter') && getDolGlobalString('WORKFLOW_TICKET_CLOSE_INTERVENTION')) {
1829 dol_syslog("We have closed the ticket, so we close all linked interventions");
1830 $this->fetchObjectLinked($this->id, $this->element, null, 'fichinter');
1831 if ($this->linkedObjectsIds) {
1832 foreach ($this->linkedObjectsIds['fichinter'] as $fichinter_id) {
1833 $fichinter = new Fichinter($this->db);
1834 $fichinter->fetch($fichinter_id);
1835 if ($fichinter->statut == 0) {
1836 $result = $fichinter->setValid($user);
1837 if (!$result) {
1838 $this->errors[] = $fichinter->error;
1839 $error++;
1840 }
1841 }
1842 if ($fichinter->statut < 3) {
1843 $result = $fichinter->setStatut(3);
1844 if (!$result) {
1845 $this->errors[] = $fichinter->error;
1846 $error++;
1847 }
1848 }
1849 }
1850 }
1851 }
1852
1853 // Call trigger
1854 $result = $this->call_trigger('TICKET_CLOSE', $user);
1855 if ($result < 0) {
1856 $error++;
1857 }
1858 // End call triggers
1859
1860 if (!$error) {
1861 $this->db->commit();
1862 return 1;
1863 } else {
1864 $this->db->rollback();
1865 $this->error = join(',', $this->errors);
1866 dol_syslog(get_class($this)."::close ".$this->error, LOG_ERR);
1867 return -1;
1868 }
1869 } else {
1870 $this->db->rollback();
1871 $this->error = $this->db->lasterror();
1872 dol_syslog(get_class($this)."::close ".$this->error, LOG_ERR);
1873 return -1;
1874 }
1875 }
1876
1877 return 0;
1878 }
1879
1889 public function searchSocidByEmail($email, $type = '0', $filters = array(), $clause = 'AND')
1890 {
1891 $thirdparties = array();
1892 $exact = 0;
1893
1894 // Generation requete recherche
1895 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."societe";
1896 $sql .= " WHERE entity IN (".getEntity('ticket', 1).")";
1897 if (!empty($type)) {
1898 if ($type == 1 || $type == 2) {
1899 $sql .= " AND client = ".((int) $type);
1900 } elseif ($type == 3) {
1901 $sql .= " AND fournisseur = 1";
1902 }
1903 }
1904 if (!empty($email)) {
1905 if (empty($exact)) {
1906 $regs = array();
1907 if (preg_match('/^([\*])?[^*]+([\*])?$/', $email, $regs) && count($regs) > 1) {
1908 $email = str_replace('*', '%', $email);
1909 } else {
1910 $email = '%'.$email.'%';
1911 }
1912 }
1913 $sql .= " AND ";
1914 if (is_array($filters) && !empty($filters)) {
1915 $sql .= "(";
1916 }
1917
1918 $sql .= "email LIKE '".$this->db->escape($email)."'";
1919 }
1920 if (is_array($filters) && !empty($filters)) {
1921 foreach ($filters as $field => $value) {
1922 $sql .= " ".$clause." ".$field." LIKE '".$this->db->escape($value)."'";
1923 }
1924 if (!empty($email)) {
1925 $sql .= ")";
1926 }
1927 }
1928
1929 $res = $this->db->query($sql);
1930 if ($res) {
1931 while ($rec = $this->db->fetch_array($res)) {
1932 $soc = new Societe($this->db);
1933 $soc->fetch($rec['rowid']);
1934 $thirdparties[] = $soc;
1935 }
1936
1937 return $thirdparties;
1938 } else {
1939 $this->error = $this->db->error().' sql='.$sql;
1940 dol_syslog(get_class($this)."::searchSocidByEmail ".$this->error, LOG_ERR);
1941 return -1;
1942 }
1943 }
1944
1953 public function searchContactByEmail($email, $socid = '', $case = '')
1954 {
1955 $contacts = array();
1956
1957 // Forge the search SQL
1958 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."socpeople";
1959 $sql .= " WHERE entity IN (".getEntity('contact').")";
1960 if (!empty($socid)) {
1961 $sql .= " AND fk_soc = ".((int) $socid);
1962 }
1963 if (!empty($email)) {
1964 $sql .= " AND ";
1965 if (!$case) {
1966 $sql .= "email = '".$this->db->escape($email)."'";
1967 } else {
1968 $sql .= "email LIKE BINARY '".$this->db->escape($this->db->escapeforlike($email))."'";
1969 }
1970 }
1971
1972 $res = $this->db->query($sql);
1973 if ($res) {
1974 while ($rec = $this->db->fetch_object($res)) {
1975 include_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
1976 $contactstatic = new Contact($this->db);
1977 $contactstatic->fetch($rec->rowid);
1978 $contacts[] = $contactstatic;
1979 }
1980
1981 return $contacts;
1982 } else {
1983 $this->error = $this->db->error().' sql='.$sql;
1984 dol_syslog(get_class($this)."::searchContactByEmail ".$this->error, LOG_ERR);
1985 return -1;
1986 }
1987 }
1988
1995 public function setCustomer($id)
1996 {
1997 if ($this->id) {
1998 $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
1999 $sql .= " SET fk_soc = ".($id > 0 ? $id : "null");
2000 $sql .= " WHERE rowid = ".((int) $this->id);
2001 dol_syslog(get_class($this).'::setCustomer sql='.$sql);
2002 $resql = $this->db->query($sql);
2003 if ($resql) {
2004 return 1;
2005 } else {
2006 return -1;
2007 }
2008 } else {
2009 return -1;
2010 }
2011 }
2012
2019 public function setProgression($percent)
2020 {
2021 if ($this->id) {
2022 $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
2023 $sql .= " SET progress = ".($percent > 0 ? $percent : "null");
2024 $sql .= " WHERE rowid = ".((int) $this->id);
2025 dol_syslog(get_class($this).'::set_progression sql='.$sql);
2026 $resql = $this->db->query($sql);
2027 if ($resql) {
2028 return 1;
2029 } else {
2030 return -1;
2031 }
2032 } else {
2033 return -1;
2034 }
2035 }
2036
2043 public function setContract($contractid)
2044 {
2045 if (!$this->table_element) {
2046 dol_syslog(get_class($this)."::setContract was called on objet with property table_element not defined", LOG_ERR);
2047 return -1;
2048 }
2049
2050 $result = $this->add_object_linked('contrat', $contractid);
2051 if ($result) {
2052 $this->fk_contract = $contractid;
2053 return 1;
2054 } else {
2055 dol_print_error($this->db);
2056 return -1;
2057 }
2058 }
2059
2060 /* gestion des contacts d'un ticket */
2061
2068 {
2069 return $this->getIdContact('internal', 'SUPPORTTEC');
2070 }
2071
2078 public function getInfosTicketInternalContact($status = -1)
2079 {
2080 return $this->listeContact(-1, 'internal', 0, '', $status);
2081 }
2082
2089 {
2090 return $this->getIdContact('external', 'SUPPORTCLI');
2091 }
2092
2099 public function getInfosTicketExternalContact($status = -1)
2100 {
2101 return $this->listeContact(-1, 'external', 0, '', $status);
2102 }
2103
2110 {
2111 return $this->getIdContact('internal', 'CONTRIBUTOR');
2112 }
2113
2120 {
2121 return $this->getIdContact('external', 'CONTRIBUTOR');
2122 }
2123
2129 public function getTicketAllContacts()
2130 {
2131 $array_contact = array();
2132
2133 $array_contact = $this->getIdTicketInternalContact();
2134
2135 $array_contact = array_merge($array_contact, $this->getIdTicketCustomerContact());
2136
2137 $array_contact = array_merge($array_contact, $this->getIdTicketInternalInvolvedContact());
2138
2139 $array_contact = array_merge($array_contact, $this->getIdTicketCustomerInvolvedContact());
2140
2141 return $array_contact;
2142 }
2143
2150 {
2151 $array_contact = array();
2152
2153 $array_contact = array_merge($array_contact, $this->getIdTicketCustomerContact());
2154
2155 $array_contact = array_merge($array_contact, $this->getIdTicketCustomerInvolvedContact());
2156
2157 return $array_contact;
2158 }
2159
2160
2172 public function listeContact($statusoflink = -1, $source = 'external', $list = 0, $code = '', $status = -1)
2173 {
2174 global $langs;
2175
2176 $tab = array();
2177
2178 $sql = "SELECT ec.rowid, ec.statut as statuslink, ec.fk_socpeople as id, ec.fk_c_type_contact"; // This field contains id of llx_socpeople or id of llx_user
2179 if ($source == 'internal') {
2180 $sql .= ", '-1' as socid, t.statut as statuscontact";
2181 }
2182
2183 if ($source == 'external' || $source == 'thirdparty') {
2184 $sql .= ", t.fk_soc as socid, t.statut as statuscontact";
2185 }
2186
2187 $sql .= ", t.civility, t.lastname as lastname, t.firstname, t.email";
2188 if ($source == 'internal') {
2189 $sql .= ", t.office_phone as phone, t.user_mobile as phone_mobile";
2190 }
2191
2192 if ($source == 'external') {
2193 $sql .= ", t.phone as phone, t.phone_mobile as phone_mobile, t.phone_perso as phone_perso";
2194 }
2195
2196 $sql .= ", tc.source, tc.element, tc.code, tc.libelle as type_contact_label";
2197 $sql .= " FROM ".MAIN_DB_PREFIX."c_type_contact tc";
2198 $sql .= ", ".MAIN_DB_PREFIX."element_contact ec";
2199 if ($source == 'internal') {
2200 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user t on ec.fk_socpeople = t.rowid";
2201 }
2202
2203 if ($source == 'external' || $source == 'thirdparty') {
2204 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."socpeople t on ec.fk_socpeople = t.rowid";
2205 }
2206
2207 $sql .= " WHERE ec.element_id = ".((int) $this->id);
2208 $sql .= " AND ec.fk_c_type_contact=tc.rowid";
2209 $sql .= " AND tc.element='".$this->db->escape($this->element)."'";
2210 if ($source == 'internal') {
2211 $sql .= " AND tc.source = 'internal'";
2212 if ($status >= 0) {
2213 $sql .= " AND t.statut = ".((int) $status);
2214 }
2215 }
2216
2217 if ($source == 'external' || $source == 'thirdparty') {
2218 $sql .= " AND tc.source = 'external'";
2219 if ($status >= 0) {
2220 $sql .= " AND t.statut = ".((int) $status);
2221 }
2222 }
2223
2224 if (!empty($code)) {
2225 $sql .= " AND tc.code = '".$this->db->escape($code)."'";
2226 }
2227
2228 $sql .= " AND tc.active=1";
2229 if ($statusoflink >= 0) {
2230 $sql .= " AND ec.statut = ".((int) $statusoflink);
2231 }
2232
2233 $sql .= " ORDER BY t.lastname ASC";
2234
2235 $resql = $this->db->query($sql);
2236 if ($resql) {
2237 $num = $this->db->num_rows($resql);
2238 $i = 0;
2239 while ($i < $num) {
2240 $obj = $this->db->fetch_object($resql);
2241
2242 if (!$list) {
2243 $transkey = "TypeContact_".$obj->element."_".$obj->source."_".$obj->code;
2244 $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->type_contact_label);
2245 $tab[$i] = array(
2246 'source' => $obj->source,
2247 'socid' => $obj->socid,
2248 'id' => $obj->id,
2249 'nom' => $obj->lastname, // For backward compatibility
2250 'civility' => $obj->civility,
2251 'lastname' => $obj->lastname,
2252 'firstname' => $obj->firstname,
2253 'email' => $obj->email,
2254 'rowid' => $obj->rowid,
2255 'code' => $obj->code,
2256 'libelle' => $libelle_type,
2257 'status' => $obj->statuslink,
2258 'statuscontact'=>$obj->statuscontact,
2259 'fk_c_type_contact' => $obj->fk_c_type_contact,
2260 'phone' => $obj->phone,
2261 'phone_mobile' => $obj->phone_mobile);
2262 } else {
2263 $tab[$i] = $obj->id;
2264 }
2265
2266 $i++;
2267 }
2268
2269 return $tab;
2270 } else {
2271 $this->error = $this->db->error();
2272 dol_print_error($this->db);
2273 return -1;
2274 }
2275 }
2276
2283 public function getDefaultRef($thirdparty = '')
2284 {
2285 global $conf;
2286
2287 $defaultref = '';
2288 $modele = getDolGlobalString('TICKET_ADDON', 'mod_ticket_simple');
2289
2290 // Search template files
2291 $file = '';
2292 $classname = '';
2293 $filefound = 0;
2294 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2295 foreach ($dirmodels as $reldir) {
2296 $file = dol_buildpath($reldir."core/modules/ticket/".$modele.'.php', 0);
2297 if (file_exists($file)) {
2298 $filefound = 1;
2299 $classname = $modele;
2300 break;
2301 }
2302 }
2303
2304 if ($filefound) {
2305 $result = dol_include_once($reldir."core/modules/ticket/".$modele.'.php');
2306 $modTicket = new $classname;
2307
2308 $defaultref = $modTicket->getNextValue($thirdparty, $this);
2309 }
2310
2311 if (is_numeric($defaultref) && $defaultref <= 0) {
2312 $defaultref = '';
2313 }
2314
2315 return $defaultref;
2316 }
2317
2318
2319 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2326 public function is_photo_available($sdir)
2327 {
2328 // phpcs:enable
2329 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2330
2331 global $conf;
2332
2333 $dir = $sdir.'/';
2334 $nbphoto = 0;
2335
2336 $dir_osencoded = dol_osencode($dir);
2337 if (file_exists($dir_osencoded)) {
2338 $handle = opendir($dir_osencoded);
2339 if (is_resource($handle)) {
2340 while (($file = readdir($handle)) !== false) {
2341 if (!utf8_check($file)) {
2342 $file = utf8_encode($file); // To be sure data is stored in UTF8 in memory
2343 }
2344 if (dol_is_file($dir.$file)) {
2345 return true;
2346 }
2347 }
2348 }
2349 }
2350 return false;
2351 }
2352
2353
2362 public function copyFilesForTicket($forcetrackid = null)
2363 {
2364 global $conf;
2365
2366 // Create form object
2367 include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
2368 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2369 include_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
2370
2371 $maxwidthsmall = 270;
2372 $maxheightsmall = 150;
2373 $maxwidthmini = 128;
2374 $maxheightmini = 72;
2375
2376 $formmail = new FormMail($this->db);
2377 $formmail->trackid = (is_null($forcetrackid) ? 'tic'.$this->id : '');
2378 $attachedfiles = $formmail->get_attached_files();
2379
2380 $filepath = $attachedfiles['paths']; // path is for example user->dir_temp.'/'.$user->id.'/'...
2381 $filename = $attachedfiles['names'];
2382 $mimetype = $attachedfiles['mimes'];
2383
2384 // Copy files into ticket directory
2385 $destdir = $conf->ticket->dir_output.'/'.$this->ref;
2386
2387 if (!dol_is_dir($destdir)) {
2388 dol_mkdir($destdir);
2389 }
2390
2391 $listofpaths = array();
2392 $listofnames = array();
2393 foreach ($filename as $i => $val) {
2394 $destfile = $destdir.'/'.$filename[$i];
2395 // If destination file already exists, we add a suffix to avoid to overwrite
2396 if (is_file($destfile)) {
2397 $pathinfo = pathinfo($filename[$i]);
2398 $now = dol_now();
2399 $destfile = $destdir.'/'.$pathinfo['filename'].' - '.dol_print_date($now, 'dayhourlog').'.'.$pathinfo['extension'];
2400 }
2401
2402 $res = dol_move($filepath[$i], $destfile, 0, 1, 0, 1);
2403 if (!$res) {
2404 // Move has failed
2405 $this->error = "Failed to move file ".dirbasename($filepath[$i])." into ".dirbasename($destfile);
2406 return -1;
2407 } else {
2408 // If file is an image, we create thumbs
2409 if (image_format_supported($destfile) == 1) {
2410 // Create small thumbs for image (Ratio is near 16/9)
2411 // Used on logon for example
2412 $imgThumbSmall = vignette($destfile, $maxwidthsmall, $maxheightsmall, '_small', 50, "thumbs");
2413 // Create mini thumbs for image (Ratio is near 16/9)
2414 // Used on menu or for setup page for example
2415 $imgThumbMini = vignette($destfile, $maxwidthmini, $maxheightmini, '_mini', 50, "thumbs");
2416 }
2417 }
2418
2419 // Clear variables into session
2420 $formmail->remove_attached_files($i);
2421
2422 // Fill array with new names
2423 $listofpaths[$i] = $destfile;
2424 $listofnames[$i] = basename($destfile);
2425 }
2426
2427 return array('listofpaths'=>$listofpaths, 'listofnames'=>$listofnames, 'listofmimes'=>$mimetype);
2428 }
2429
2440 public function setCategories($categories)
2441 {
2442 // Handle single category
2443 if (!is_array($categories)) {
2444 $categories = array($categories);
2445 }
2446
2447 // Get current categories
2448 include_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
2449 $c = new Categorie($this->db);
2450 $existing = $c->containing($this->id, Categorie::TYPE_TICKET, 'id');
2451
2452 // Diff
2453 if (is_array($existing)) {
2454 $to_del = array_diff($existing, $categories);
2455 $to_add = array_diff($categories, $existing);
2456 } else {
2457 $to_del = array(); // Nothing to delete
2458 $to_add = $categories;
2459 }
2460
2461 // Process
2462 foreach ($to_del as $del) {
2463 if ($c->fetch($del) > 0) {
2464 $c->del_type($this, Categorie::TYPE_TICKET);
2465 }
2466 }
2467 foreach ($to_add as $add) {
2468 if ($c->fetch($add) > 0) {
2469 $c->add_type($this, Categorie::TYPE_TICKET);
2470 }
2471 }
2472
2473 return 1;
2474 }
2475
2486 public function newMessage($user, &$action, $private = 1, $public_area = 0)
2487 {
2488 global $mysoc, $conf, $langs;
2489
2490 $error = 0;
2491
2492 $object = new Ticket($this->db);
2493
2494 $ret = $object->fetch('', '', GETPOST('track_id', 'alpha'));
2495
2496 $object->socid = $object->fk_soc;
2497 $object->fetch_thirdparty();
2498 $object->fetch_project();
2499
2500 if ($ret < 0) {
2501 $error++;
2502 array_push($this->errors, $langs->trans("ErrorTicketIsNotValid"));
2503 $action = '';
2504 }
2505
2506 if (!GETPOST("message")) {
2507 $error++;
2508 array_push($this->errors, $langs->trans("ErrorFieldRequired", $langs->transnoentities("message")));
2509 $action = 'add_message';
2510 }
2511
2512 if (!$error) {
2513 $object->subject = GETPOST('subject', 'alphanohtml');
2514 $object->message = GETPOST("message", "restricthtml");
2515 $object->private = GETPOST("private_message", "alpha");
2516
2517 $send_email = GETPOST('send_email', 'int');
2518
2519 // Copy attached files (saved into $_SESSION) as linked files to ticket. Return array with final name used.
2520 $resarray = $object->copyFilesForTicket();
2521 if (is_numeric($resarray) && $resarray == -1) {
2522 setEventMessages($object->error, $object->errors, 'errors');
2523 return -1;
2524 }
2525
2526 $listofpaths = $resarray['listofpaths'];
2527 $listofnames = $resarray['listofnames'];
2528 $listofmimes = $resarray['listofmimes'];
2529
2530 $id = $object->createTicketMessage($user, 0, $listofpaths, $listofmimes, $listofnames, $send_email);
2531 if ($id <= 0) {
2532 $error++;
2533 $this->error = $object->error;
2534 $this->errors = $object->errors;
2535 $action = 'add_message';
2536 }
2537
2538 if (!$error && $id > 0) {
2539 setEventMessages($langs->trans('TicketMessageSuccessfullyAdded'), null, 'mesgs');
2540
2541 //var_dump($_SESSION);
2542 //var_dump($listofpaths);exit;
2543
2544 if (!empty($public_area)) {
2545 /*
2546 * Message created from the Public interface
2547 *
2548 * Send emails to assigned users (public area notification)
2549 */
2550 if (getDolGlobalString('TICKET_PUBLIC_NOTIFICATION_NEW_MESSAGE_ENABLED')) {
2551 // Retrieve internal contact datas
2552 $internal_contacts = $object->getInfosTicketInternalContact(1);
2553
2554 $assigned_user_dont_have_email = '';
2555
2556 $sendto = array();
2557
2558 if ($this->fk_user_assign > 0) {
2559 $assigned_user = new User($this->db);
2560 $assigned_user->fetch($this->fk_user_assign);
2561 if (!empty($assigned_user->email)) {
2562 $sendto[$assigned_user->email] = $assigned_user->getFullName($langs)." <".$assigned_user->email.">";
2563 } else {
2564 $assigned_user_dont_have_email = $assigned_user->getFullName($langs);
2565 }
2566 }
2567
2568 // Build array to display recipient list
2569 foreach ($internal_contacts as $key => $info_sendto) {
2570 // Avoid duplicate notifications
2571 if ($info_sendto['id'] == $user->id) {
2572 continue;
2573 }
2574
2575 // We check if the email address is not the assignee's address to prevent notification from being sent twice
2576 if (!empty($info_sendto['email']) && $assigned_user->email != $info_sendto['email']) {
2577 $sendto[] = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'])." <".$info_sendto['email'].">";
2578 }
2579 }
2580
2581 if (empty($sendto)) {
2582 if (getDolGlobalString('TICKET_PUBLIC_NOTIFICATION_NEW_MESSAGE_DEFAULT_EMAIL')) {
2583 $sendto[getDolGlobalString('TICKET_PUBLIC_NOTIFICATION_NEW_MESSAGE_DEFAULT_EMAIL')] = getDolGlobalString('TICKET_PUBLIC_NOTIFICATION_NEW_MESSAGE_DEFAULT_EMAIL');
2584 } elseif (getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')) {
2585 $sendto[getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')] = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO');
2586 }
2587 }
2588
2589 // Add global email address recipient
2590 if (getDolGlobalString('TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS') &&
2591 getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO') && !array_key_exists(getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO'), $sendto)
2592 ) {
2593 $sendto[getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')] = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO');
2594 }
2595
2596 if (!empty($sendto)) {
2597 $label_title = getDolGlobalString('MAIN_APPLICATION_TITLE', $mysoc->name);
2598 $subject = '['.$label_title.'- ticket #'.$object->track_id.'] '.$langs->trans('TicketNewMessage');
2599
2600 // Message send
2601 $message = getDolGlobalString('TICKET_MESSAGE_MAIL_INTRO', $langs->trans('TicketMessageMailIntroText'));
2602 $message .= '<br><br>';
2603 $messagePost = GETPOST('message', 'restricthtml');
2604 if (!dol_textishtml($messagePost)) {
2605 $messagePost = dol_nl2br($messagePost);
2606 }
2607 $message .= $messagePost;
2608
2609 // Customer company infos
2610 $message .= '<br><br>';
2611 $message .= "==============================================";
2612 $message .= !empty($object->thirdparty->name) ? '<br>'.$langs->trans('Thirdparty')." : ".$object->thirdparty->name : '';
2613 $message .= !empty($object->thirdparty->town) ? '<br>'.$langs->trans('Town')." : ".$object->thirdparty->town : '';
2614 $message .= !empty($object->thirdparty->phone) ? '<br>'.$langs->trans('Phone')." : ".$object->thirdparty->phone : '';
2615
2616 // Email send to
2617 $message .= '<br><br>';
2618 if (!empty($assigned_user_dont_have_email)) {
2619 $message .= '<br>'.$langs->trans('NoEMail').' : '.$assigned_user_dont_have_email;
2620 }
2621 foreach ($sendto as $val) {
2622 $message .= '<br>'.$langs->trans('TicketNotificationRecipient').' : '.$val;
2623 }
2624
2625 // URL ticket
2626 $url_internal_ticket = dol_buildpath('/ticket/card.php', 2).'?track_id='.$object->track_id;
2627 $message .= '<br><br>';
2628 $message .= $langs->trans('TicketNotificationEmailBodyInfosTrackUrlinternal').' : <a href="'.$url_internal_ticket.'">'.$object->track_id.'</a>';
2629
2630 $this->sendTicketMessageByEmail($subject, $message, '', $sendto, $listofpaths, $listofmimes, $listofnames);
2631 }
2632 }
2633 } else {
2634 /*
2635 * Message send from the Backoffice / Private area
2636 *
2637 * Send emails to internal users (linked contacts) then, if private is not set, to external users (linked contacts or thirdparty email if no contact set)
2638 */
2639 if ($send_email > 0) {
2640 // Retrieve internal contact datas
2641 $internal_contacts = $object->getInfosTicketInternalContact(1);
2642
2643 $sendto = array();
2644 if (is_array($internal_contacts) && count($internal_contacts) > 0) {
2645 // Set default subject
2646 $label_title = getDolGlobalString('MAIN_APPLICATION_TITLE', $mysoc->name);
2647 $appli = $label_title;
2648 $subject = GETPOST('subject', 'alphanohtml') ? GETPOST('subject', 'alphanohtml') : '['.$appli.' - '.$langs->trans("Ticket").' #'.$object->track_id.'] '.$langs->trans('TicketNewMessage');
2649
2650 $message_intro = $langs->trans('TicketNotificationEmailBody', "#".$object->id);
2651 $message_signature = GETPOST('mail_signature') ? GETPOST('mail_signature') : getDolGlobalString('TICKET_MESSAGE_MAIL_SIGNATURE');
2652
2653 $message = getDolGlobalString('TICKET_MESSAGE_MAIL_INTRO', $langs->trans('TicketMessageMailIntroText'));
2654 $message .= '<br><br>';
2655 $messagePost = GETPOST('message', 'restricthtml');
2656 if (!dol_textishtml($messagePost)) {
2657 $messagePost = dol_nl2br($messagePost);
2658 }
2659 $message .= $messagePost;
2660
2661 // Data about customer
2662 $message .= '<br><br>';
2663 $message .= "==============================================<br>";
2664 $message .= !empty($object->thirdparty->name) ? $langs->trans('Thirdparty')." : ".$object->thirdparty->name : '';
2665 $message .= !empty($object->thirdparty->town) ? '<br>'.$langs->trans('Town')." : ".$object->thirdparty->town : '';
2666 $message .= !empty($object->thirdparty->phone) ? '<br>'.$langs->trans('Phone')." : ".$object->thirdparty->phone : '';
2667
2668 // Build array to display recipient list
2669 foreach ($internal_contacts as $key => $info_sendto) {
2670 // Avoid duplicate notifications
2671 if ($info_sendto['id'] == $user->id) {
2672 continue;
2673 }
2674
2675 if ($info_sendto['email'] != '') {
2676 if (!empty($info_sendto['email'])) {
2677 $sendto[$info_sendto['email']] = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'])." <".$info_sendto['email'].">";
2678 }
2679
2680 // Contact type
2681 $recipient = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'], '-1').' ('.strtolower($info_sendto['libelle']).')';
2682 $message .= (!empty($recipient) ? $langs->trans('TicketNotificationRecipient').' : '.$recipient.'<br>' : '');
2683 }
2684 }
2685 $message .= '<br>';
2686 // URL ticket
2687 $url_internal_ticket = dol_buildpath('/ticket/card.php', 2).'?track_id='.$object->track_id;
2688
2689 // Add html link on url
2690 $message .= '<br>'.$langs->trans('TicketNotificationEmailBodyInfosTrackUrlinternal').' : <a href="'.$url_internal_ticket.'">'.$object->track_id.'</a><br>';
2691
2692 // Add global email address recipient
2693 if (getDolGlobalString('TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS') && !array_key_exists(getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO'), $sendto)) {
2694 if (getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')) {
2695 $sendto[getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')] = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO');
2696 }
2697 }
2698
2699 // dont try to send email if no recipient
2700 if (!empty($sendto)) {
2701 $this->sendTicketMessageByEmail($subject, $message, '', $sendto, $listofpaths, $listofmimes, $listofnames);
2702 }
2703 }
2704
2705 /*
2706 * Send emails for externals users if not private (linked contacts)
2707 */
2708 if (empty($object->private)) {
2709 // Retrieve email of all contacts (external)
2710 $external_contacts = $object->getInfosTicketExternalContact(1);
2711
2712 // If no contact, get email from thirdparty
2713 if (is_array($external_contacts) && count($external_contacts) === 0) {
2714 if (!empty($object->fk_soc)) {
2715 $object->fetch_thirdparty($object->fk_soc);
2716 $array_company = array(array('firstname' => '', 'lastname' => $object->thirdparty->name, 'email' => $object->thirdparty->email, 'libelle' => $langs->transnoentities('Customer'), 'socid' => $object->thirdparty->id));
2717 $external_contacts = array_merge($external_contacts, $array_company);
2718 } elseif (empty($object->fk_soc) && !empty($object->origin_email)) {
2719 $array_external = array(array('firstname' => '', 'lastname' => $object->origin_email, 'email' => $object->thirdparty->email, 'libelle' => $langs->transnoentities('Customer'), 'socid' => $object->thirdparty->id));
2720 $external_contacts = array_merge($external_contacts, $array_external);
2721 }
2722 }
2723
2724 $sendto = array();
2725 if (is_array($external_contacts) && count($external_contacts) > 0) {
2726 // Get default subject for email to external contacts
2727 $label_title = getDolGlobalString('MAIN_APPLICATION_TITLE', $mysoc->name);
2728 $appli = $mysoc->name;
2729 $subject = GETPOST('subject') ? GETPOST('subject') : '['.$appli.' - '.$langs->trans("Ticket").' #'.$object->track_id.'] '.$langs->trans('TicketNewMessage');
2730
2731 $message_intro = GETPOST('mail_intro') ? GETPOST('mail_intro', 'restricthtml') : getDolGlobalString('TICKET_MESSAGE_MAIL_INTRO');
2732 $message_signature = GETPOST('mail_signature') ? GETPOST('mail_signature', 'restricthtml') : getDolGlobalString('TICKET_MESSAGE_MAIL_SIGNATURE');
2733 if (!dol_textishtml($message_intro)) {
2734 $message_intro = dol_nl2br($message_intro);
2735 }
2736 if (!dol_textishtml($message_signature)) {
2737 $message_signature = dol_nl2br($message_signature);
2738 }
2739
2740 // We put intro after
2741 $messagePost = GETPOST('message', 'restricthtml');
2742 if (!dol_textishtml($messagePost)) {
2743 $messagePost = dol_nl2br($messagePost);
2744 }
2745 $message = $messagePost;
2746 $message .= '<br><br>';
2747
2748 foreach ($external_contacts as $key => $info_sendto) {
2749 // altairis: avoid duplicate emails to external contacts
2750 if ($info_sendto['id'] == $user->contact_id) {
2751 continue;
2752 }
2753
2754 if ($info_sendto['email'] != '' && $info_sendto['email'] != $object->origin_email) {
2755 if (!empty($info_sendto['email'])) {
2756 $sendto[$info_sendto['email']] = trim($info_sendto['firstname']." ".$info_sendto['lastname'])." <".$info_sendto['email'].">";
2757 }
2758
2759 $recipient = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'], '-1').' ('.strtolower($info_sendto['libelle']).')';
2760 $message .= (!empty($recipient) ? $langs->trans('TicketNotificationRecipient').' : '.$recipient.'<br>' : '');
2761 }
2762 }
2763
2764 // If public interface is not enable, use link to internal page into mail
2765 $url_public_ticket = getDolGlobalString('TICKET_URL_PUBLIC_INTERFACE', dol_buildpath('/public/ticket/view.php', 2)) . '/view.php?track_id='.$object->track_id;
2766 $message .= '<br>'.$langs->trans('TicketNewEmailBodyInfosTrackUrlCustomer').' : <a href="'.$url_public_ticket.'">'.$object->track_id.'</a><br>';
2767
2768 // Build final message
2769 $message = $message_intro.'<br><br>'.$message;
2770
2771 // Add signature
2772 $message .= '<br>'.$message_signature;
2773
2774 if (!empty($object->origin_email)) {
2775 $sendto[$object->origin_email] = $object->origin_email;
2776 }
2777
2778 if ($object->fk_soc > 0 && !array_key_exists($object->origin_email, $sendto)) {
2779 $object->socid = $object->fk_soc;
2780 $object->fetch_thirdparty();
2781 if (!empty($object->thirdparty->email)) {
2782 $sendto[$object->thirdparty->email] = $object->thirdparty->email;
2783 }
2784 }
2785
2786 // Add global email address recipient
2787 if (getDolGlobalString('TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS') && !array_key_exists(getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO'), $sendto)) {
2788 if (getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')) {
2789 $sendto[getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')] = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO');
2790 }
2791 }
2792
2793 // Dont try to send email when no recipient
2794 if (!empty($sendto)) {
2795 $result = $this->sendTicketMessageByEmail($subject, $message, '', $sendto, $listofpaths, $listofmimes, $listofnames);
2796 if ($result) {
2797 // update last_msg_sent date (for last message sent to external users)
2798 $this->date_last_msg_sent = dol_now();
2799 $this->update($user, 1); // disable trigger when updating date_last_msg_sent. sendTicketMessageByEmail already create an event in actioncomm table.
2800 }
2801 }
2802 }
2803 }
2804 }
2805 }
2806
2807 // Set status to "answered" if not set yet, but only if internal user and not private message
2808 // Or set status to "answered" if the client has answered and if the ticket has started
2809 if (($object->status < self::STATUS_IN_PROGRESS && !$user->socid && !$private) ||
2810 ($object->status > self::STATUS_IN_PROGRESS && $public_area)
2811 ) {
2812 $object->setStatut(3);
2813 }
2814 return 1;
2815 } else {
2816 setEventMessages($object->error, $object->errors, 'errors');
2817 return -1;
2818 }
2819 } else {
2820 setEventMessages($this->error, $this->errors, 'errors');
2821 return -1;
2822 }
2823 }
2824
2825
2838 public function sendTicketMessageByEmail($subject, $message, $send_internal_cc = 0, $array_receiver = array(), $filename_list = array(), $mimetype_list = array(), $mimefilename_list = array())
2839 {
2840 global $conf, $langs;
2841
2842 if (getDolGlobalString('TICKET_DISABLE_ALL_MAILS')) {
2843 dol_syslog(get_class($this).'::sendTicketMessageByEmail: Emails are disable into ticket setup by option TICKET_DISABLE_ALL_MAILS', LOG_WARNING);
2844 return false;
2845 }
2846
2847 $langs->load("mails");
2848
2849 include_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
2850 //$contactstatic = new Contact($this->db);
2851
2852 // If no receiver defined, load all ticket linked contacts
2853 if (!is_array($array_receiver) || !count($array_receiver) > 0) {
2854 $array_receiver = $this->getInfosTicketInternalContact(1);
2855 $array_receiver = array_merge($array_receiver, $this->getInfosTicketExternalContact(1));
2856 }
2857
2858 $sendtocc = "";
2859 if ($send_internal_cc) {
2860 $sendtocc = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_FROM');
2861 }
2862
2863 $from = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_FROM');
2864 $is_sent = false;
2865 if (is_array($array_receiver) && count($array_receiver) > 0) {
2866 foreach ($array_receiver as $key => $receiver) {
2867 $deliveryreceipt = 0;
2868 $filepath = $filename_list;
2869 $filename = $mimefilename_list;
2870 $mimetype = $mimetype_list;
2871
2872 // Send email
2873
2874 $old_MAIN_MAIL_AUTOCOPY_TO = getDolGlobalString('MAIN_MAIL_AUTOCOPY_TO');
2875
2876 if (getDolGlobalString('TICKET_DISABLE_MAIL_AUTOCOPY_TO')) {
2877 $conf->global->MAIN_MAIL_AUTOCOPY_TO = '';
2878 }
2879
2880 $upload_dir_tmp = $conf->user->dir_output."/".$user->id.'/temp';
2881
2882 include_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
2883 $trackid = "tic".$this->id;
2884
2885 $moreinheader = 'X-Dolibarr-Info: sendTicketMessageByEmail'."\r\n";
2886 if (!empty($this->email_msgid)) {
2887 // We must also add 1 entry In-Reply-To: <$this->email_msgid> with Message-ID we respond from (See RFC5322).
2888 $moreinheader .= 'In-Reply-To: <'.$this->email_msgid.'>'."\r\n";
2889 }
2890
2891 $mailfile = new CMailFile($subject, $receiver, $from, $message, $filepath, $mimetype, $filename, $sendtocc, '', $deliveryreceipt, -1, '', '', $trackid, $moreinheader, 'ticket', '', $upload_dir_tmp);
2892
2893 if ($mailfile->error) {
2894 setEventMessages($mailfile->error, null, 'errors');
2895 } else {
2896 $result = $mailfile->sendfile();
2897 if ($result) {
2898 setEventMessages($langs->trans('MailSuccessfulySent', $mailfile->getValidAddress($from, 2), $mailfile->getValidAddress($receiver, 2)), null, 'mesgs');
2899 $is_sent = true;
2900 } else {
2901 $langs->load("other");
2902 if ($mailfile->error) {
2903 setEventMessages($langs->trans('ErrorFailedToSendMail', $from, $receiver), null, 'errors');
2904 dol_syslog($langs->trans('ErrorFailedToSendMail', $from, $receiver).' : '.$mailfile->error);
2905 } else {
2906 setEventMessages('No mail sent. Feature is disabled by option MAIN_DISABLE_ALL_MAILS', null, 'errors');
2907 }
2908 }
2909 }
2910
2911 if (getDolGlobalString('TICKET_DISABLE_MAIL_AUTOCOPY_TO')) {
2912 $conf->global->MAIN_MAIL_AUTOCOPY_TO = $old_MAIN_MAIL_AUTOCOPY_TO;
2913 }
2914 }
2915 } else {
2916 $langs->load("other");
2917 setEventMessages($langs->trans('ErrorMailRecipientIsEmptyForSendTicketMessage'), null, 'warnings');
2918 }
2919
2920 return $is_sent;
2921 }
2922
2923 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2931 public function load_board($user, $mode)
2932 {
2933 // phpcs:enable
2934 global $conf, $user, $langs;
2935
2936 $now = dol_now();
2937 $delay_warning = 0;
2938
2939 $this->nbtodo = $this->nbtodolate = 0;
2940 $clause = " WHERE";
2941
2942 $sql = "SELECT p.rowid, p.ref, p.datec as datec";
2943 $sql .= " FROM ".MAIN_DB_PREFIX."ticket as p";
2944 if (isModEnabled('societe') && empty($user->rights->societe->client->voir) && !$user->socid) {
2945 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON p.fk_soc = sc.fk_soc";
2946 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2947 $clause = " AND";
2948 }
2949 $sql .= $clause." p.entity IN (".getEntity('ticket').")";
2950 if ($mode == 'opened') {
2951 $sql .= " AND p.fk_statut NOT IN (".Ticket::STATUS_CLOSED.", ".Ticket::STATUS_CANCELED.")";
2952 }
2953 if ($user->socid) {
2954 $sql .= " AND p.fk_soc = ".((int) $user->socid);
2955 }
2956
2957 $resql = $this->db->query($sql);
2958 if ($resql) {
2959 $label = $labelShort = '';
2960 $status = '';
2961 if ($mode == 'opened') {
2962 $status = 'openall';
2963 //$delay_warning = $conf->ticket->warning_delay;
2964 $delay_warning = 0;
2965 $label = $langs->trans("MenuListNonClosed");
2966 $labelShort = $langs->trans("MenuListNonClosed");
2967 }
2968
2969 $response = new WorkboardResponse();
2970 //$response->warning_delay = $delay_warning / 60 / 60 / 24;
2971 $response->label = $label;
2972 $response->labelShort = $labelShort;
2973 $response->url = DOL_URL_ROOT.'/ticket/list.php?search_fk_statut[]='.$status;
2974 $response->img = img_object('', "ticket");
2975
2976 // This assignment in condition is not a bug. It allows walking the results.
2977 while ($obj = $this->db->fetch_object($resql)) {
2978 $response->nbtodo++;
2979 if ($mode == 'opened') {
2980 $datelimit = $this->db->jdate($obj->datec) + $delay_warning;
2981 if ($datelimit < $now) {
2982 //$response->nbtodolate++;
2983 }
2984 }
2985 }
2986 return $response;
2987 } else {
2988 $this->error = $this->db->lasterror();
2989 return -1;
2990 }
2991 }
2992
2993 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2999 public function load_state_board()
3000 {
3001 // phpcs:enable
3002 global $conf, $user;
3003
3004 $this->nb = array();
3005 $clause = "WHERE";
3006
3007 $sql = "SELECT count(p.rowid) as nb";
3008 $sql .= " FROM ".MAIN_DB_PREFIX."ticket as p";
3009 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
3010 if (empty($user->rights->societe->client->voir) && !$user->socid) {
3011 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3012 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3013 $clause = "AND";
3014 }
3015 $sql .= " ".$clause." p.entity IN (".getEntity('ticket').")";
3016
3017 $resql = $this->db->query($sql);
3018 if ($resql) {
3019 // This assignment in condition is not a bug. It allows walking the results.
3020 while ($obj = $this->db->fetch_object($resql)) {
3021 $this->nb["ticket"] = $obj->nb;
3022 }
3023 $this->db->free($resql);
3024 return 1;
3025 } else {
3026 dol_print_error($this->db);
3027 $this->error = $this->db->lasterror();
3028 return -1;
3029 }
3030 }
3031
3040 public static function replaceThirdparty($db, $origin_id, $dest_id)
3041 {
3042 $tables = array('ticket');
3043
3044 return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
3045 }
3046
3054 public function getKanbanView($option = '', $arraydata = null)
3055 {
3056 global $langs;
3057
3058 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3059
3060 $return = '<div class="box-flex-item box-flex-grow-zero">';
3061 $return .= '<div class="info-box info-box-sm">';
3062 $return .= '<span class="info-box-icon bg-infobox-action">';
3063 $return .= img_picto('', $this->picto);
3064 $return .= '</span>';
3065 $return .= '<div class="info-box-content">';
3066 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl(1) : $this->ref).'</span>';
3067 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
3068 if (!empty($arraydata['user_assignment'])) {
3069 $return .= '<br><span class="info-box-label" title="'.dol_escape_htmltag($langs->trans("AssignedTo")).'">'.$arraydata['user_assignment'].'</span>';
3070 }
3071 if (property_exists($this, 'type_code') && !empty($this->type_code)) {
3072 $return .= '<br>';
3073 $return .= '<div class="tdoverflowmax125 inline-block">'.$langs->getLabelFromKey($this->db, 'TicketTypeShort'.$this->type_code, 'c_ticket_type', 'code', 'label', $this->type_code).'</div>';
3074 }
3075 if (method_exists($this, 'getLibStatut')) {
3076 $return .= '<br><div class="info-box-status margintoponly">'.$this->getLibStatut(3).'</div>';
3077 }
3078 $return .= '</div>';
3079 $return .= '</div>';
3080 $return .= '</div>';
3081 return $return;
3082 }
3083}
3084
3085
3090{
3095 public $rowid;
3096
3100 public $id;
3101
3105 public $ref;
3106
3111
3115 public $fk_soc;
3116
3121
3126
3131
3136
3140 public $subject;
3141
3145 public $message;
3146
3151
3156
3161
3165 public $timing;
3166
3171
3176
3181
3186
3191
3196
3200 public $datec = '';
3201
3205 public $date_read = '';
3206
3210 public $date_last_msg_sent = '';
3211
3215 public $date_close = '';
3216}
$object ref
Definition info.php:78
Class to manage agenda events (actions)
Class to send emails (with attachments or not) Usage: $mailfile = new CMailFile($subject,...
Class to manage categories.
Parent class of all other business classes (invoices, contracts, proposals, orders,...
fetch_optionals($rowid=null, $optionsArray=null)
Function to get extra fields of an object into $this->array_options This method is in most cases call...
static commonReplaceThirdparty(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a thirdparty id with another one.
insertExtraFields($trigger='', $userused=null)
Add/Update all extra fields values for the current object.
call_trigger($triggerName, $user)
Call trigger based on this instance.
add_contact($fk_socpeople, $type_contact, $source='external', $notrigger=0)
Add a link between element $this->element and a contact.
Class to manage contact/addresses.
Class to manage standard extra fields.
Class to manage interventions.
Class to manage generation of HTML components Only common components must be here.
static selectarray($htmlname, $array, $id='', $show_empty=0, $key_in_label=0, $value_as_key=0, $moreparam='', $translate=0, $maxlen=0, $disabled=0, $sort='', $morecss='minwidth75', $addjscombo=1, $moreparamonempty='', $disablebademail=0, $nohtmlescape=0)
Return a HTML select string, built from an array of key+value.
Classe permettant la generation du formulaire html d'envoi de mail unitaire Usage: $formail = new For...
Class to manage third parties objects (customers, suppliers, prospects...)
update($user=0, $notrigger=0)
Update object into database.
$email_from
Email from user.
static replaceThirdparty($db, $origin_id, $dest_id)
Function used to replace a thirdparty id with another one.
fetch($id='', $ref='', $track_id='', $email_msgid='')
Load object in memory from the database.
listeContact($statusoflink=-1, $source='external', $list=0, $code='', $status=-1)
Get array of all contacts for a ticket Override method of file commonobject.class....
loadCacheMsgsTicket()
Load the list of event on ticket into ->cache_msgs_ticket.
setProgression($percent)
Define progression of current ticket.
loadCacheSeveritiesTickets()
Charge dans cache la liste des sévérité de tickets (paramétrable dans dictionnaire)
$fields
'type' field format ('integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter]]',...
getInfosTicketExternalContact($status=-1)
Retrieve informations about external contacts.
searchContactByEmail($email, $socid='', $case='')
Search and fetch contacts by email.
getIdTicketCustomerInvolvedContact()
Return id des contacts clients des intervenants.
LibStatut($status, $mode=0, $notooltip=0, $progress=0)
Return status label of object.
$type_label
Type label.
setContract($contractid)
Link element with a contract.
getTicketAllContacts()
Return id of all contacts for ticket.
createFromClone(User $user, $fromid)
Load an object from its id and create a new one in database.
assignUser($user, $id_assign_user, $notrigger=0)
Mark a message as read.
getTooltipContentArray($params)
getTooltipContentArray
load_state_board()
Load indicator this->nb of global stats widget.
getKanbanView($option='', $arraydata=null)
Return clicable link of object (with eventually picto)
printSelectStatus($selected="")
Print selected status.
is_photo_available($sdir)
Return if at least one photo is available.
getIdTicketInternalContact()
Return id des contacts interne de suivi.
fetchAll($user, $sortorder='ASC', $sortfield='t.datec', $limit='', $offset=0, $arch='', $filter='')
Load all objects in memory from database.
loadCacheCategoriesTickets($publicgroup=-1)
Load into a cache array, the list of ticket categories (setup done into dictionary)
setCustomer($id)
Define parent commany of current ticket.
markAsRead($user, $notrigger=0)
Mark a message as read.
getDefaultRef($thirdparty='')
Get a default reference.
sendTicketMessageByEmail($subject, $message, $send_internal_cc=0, $array_receiver=array(), $filename_list=array(), $mimetype_list=array(), $mimefilename_list=array())
Send ticket by email to linked contacts.
const STATUS_NOT_READ
Status.
__construct($db)
Constructor.
getTicketAllCustomerContacts()
Return id of all contacts for ticket.
$category_label
Category label.
searchSocidByEmail($email, $type='0', $filters=array(), $clause='AND')
Search and fetch thirparties by email.
$severity_label
Severity label.
verify()
Check properties of ticket are ok (like ref, track_id, ...).
setCategories($categories)
Sets object to supplied categories.
initAsSpecimen()
Initialise object with example values Id must be 0 if object instance is a specimen.
newMessage($user, &$action, $private=1, $public_area=0)
Add new message on a ticket (private/public area).
$subject
var string Ticket subject
getIdTicketCustomerContact()
Return id des contacts clients pour le suivi ticket.
loadCacheTypesTickets()
Load into a cache the types of tickets (setup done into dictionaries)
getLibStatut($mode=0)
Return status label of object.
close(User $user, $mode=0)
Close a ticket.
getIdTicketInternalInvolvedContact()
Return id des contacts clients des intervenants.
copyFilesForTicket($forcetrackid=null)
Copy files defined into $_SESSION array into the ticket directory of attached files.
getNomUrl($withpicto=0, $option='', $notooltip=0, $morecss='', $save_lastsearch_value=-1)
Return a link to the object card (with optionaly the picto)
load_board($user, $mode)
Load indicators for dashboard (this->nbtodo and this->nbtodolate)
getInfosTicketInternalContact($status=-1)
Retrieve informations about internal contacts.
createTicketMessage($user, $notrigger=0, $filename_list=array(), $mimetype_list=array(), $mimefilename_list=array(), $send_email=false)
Add message into database.
create($user, $notrigger=0)
Create object into database.
Ticket line Class.
$message
Ticket message.
$category_code
Category code.
$fk_user_assign
User id who have ticket assigned.
$date_read
Read date.
$type_label
Type label.
$fk_project
Project ID.
$timing
Duration for ticket.
$category_label
Category label.
$subject
Ticket subject.
$severity_code
Severity code.
$track_id
Hash to identify ticket.
$fk_statut
Ticket statut.
$datec
Creation date.
$progress
Progress in percent.
$date_close
Close ticket date.
$type_code
Type code.
$severity_label
Severity label.
$origin_email
Person email who have create ticket.
$resolution
State resolution.
$fk_user_create
User id who have create ticket.
Class to manage Dolibarr users.
print $langs trans("Ref").' m m m statut
Definition index.php:152
dirbasename($pathfile)
Return the relative dirname (relative to DOL_DATA_ROOT) of a full path string.
dol_move($srcfile, $destfile, $newmask=0, $overwriteifexists=1, $testvirus=0, $indexdatabase=1, $moreinfo=array())
Move a file into another name.
dol_is_file($pathoffile)
Return if path is a file.
dol_is_dir($folder)
Test if filename is a directory.
dol_osencode($str)
Return a string encoded into OS filesystem encoding.
dol_print_error($db='', $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
dol_nl2br($stringtoencode, $nl2brmode=0, $forxml=false)
Replace CRLF in string with a HTML BR tag.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs='', $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return dolibarr global constant int value.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
if(!function_exists( 'dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
dol_clone($object, $native=0)
Create a clone of instance of object (new instance with same value for each properties) With native =...
dolGetFirstLastname($firstname, $lastname, $nameorder=-1)
Return firstname and lastname in correct order.
dol_concatdesc($text1, $text2, $forxml=false, $invert=false)
Concat 2 descriptions with a new line between them (second operand after first one with appropriate n...
dol_textishtml($msg, $option=0)
Return if a text is a html content.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0)
Set event messages in dol_events session object.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
utf8_check($str)
Check if a string is in UTF8.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
dol_mkdir($dir, $dataroot='', $newmask='')
Creation of a directory (this can create recursive subdir)
vignette($file, $maxWidth=160, $maxHeight=120, $extName='_small', $quality=50, $outdir='thumbs', $targetformat=0)
Create a thumbnail from an image file (Supported extensions are gif, jpg, png and bmp).
image_format_supported($file, $acceptsvg=0)
Return if a filename is file name of a supported image format.
Class to generate the form for creating a new ticket.
generate_random_id($car=16)
Generate a random id.