dolibarr 25.0.0-alpha
api_tickets.class.php
1<?php
2/* Copyright (C) 2016 Jean-François Ferry <hello@librethic.io>
3 * Copyright (C) 2024-2026 Frédéric France <frederic.france@free.fr>
4 * Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
5 * Copyright (C) 2026 Jose Martinez <jose.martinez@pichinov.com>
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
21use Luracast\Restler\RestException;
22
23require_once DOL_DOCUMENT_ROOT.'/ticket/class/ticket.class.php';
24require_once DOL_DOCUMENT_ROOT.'/core/lib/ticket.lib.php';
25require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
26
27
34class Tickets extends DolibarrApi
35{
39 public static $FIELDS = array(
40 'subject',
41 'message'
42 );
43
47 public static $FIELDS_MESSAGES = array(
48 'track_id',
49 'message'
50 );
51
55 public $ticket;
56
60 public function __construct()
61 {
62 global $db;
63 $this->db = $db;
64 $this->ticket = new Ticket($this->db);
65 }
66
80 public function get($id, $contact_list = 1)
81 {
82 return $this->getCommon($id, '', '', $contact_list);
83 }
84
100 public function getByTrackId($track_id, $contact_list = 1)
101 {
102 return $this->getCommon(0, $track_id, '', $contact_list);
103 }
104
120 public function getByRef($ref, $contact_list = 1)
121 {
122 return $this->getCommon(0, '', $ref, $contact_list);
123 }
124
135 private function getCommon($id = 0, $track_id = '', $ref = '', $contact_list = 1)
136 {
137 global $conf;
138
139 if (!DolibarrApiAccess::$user->hasRight('ticket', 'read')) {
140 throw new RestException(403);
141 }
142
143 // Check parameters
144 if (($id < 0) && !$track_id && !$ref) {
145 throw new RestException(400, 'Wrong parameters');
146 }
147 if (empty($id) && empty($ref) && empty($track_id)) {
148 $result = $this->ticket->initAsSpecimen();
149 } else {
150 $result = $this->ticket->fetch($id, $ref, $track_id);
151 }
152 if (!$result) {
153 throw new RestException(404, 'Ticket not found');
154 }
155
156 // Messages of ticket
157 $messages = array();
158
159 $this->ticket->loadCacheMsgsTicket();
160
161 if (is_array($this->ticket->cache_msgs_ticket) && count($this->ticket->cache_msgs_ticket) > 0) {
162 $num = count($this->ticket->cache_msgs_ticket);
163 $i = 0;
164 while ($i < $num) {
165 $iduseraction = $this->ticket->cache_msgs_ticket[$i]['fk_user_action'];
166 if ($iduseraction > 0) {
167 if (empty($conf->cache['user'][$iduseraction])) {
168 $user_action = new User($this->db);
169 $user_action->fetch($iduseraction);
170
171 $conf->cache['user'][$iduseraction] = $user_action;
172 } else {
173 $user_action = $conf->cache['user'][$iduseraction];
174 }
175 } else {
176 $user_action = null;
177 }
178
179 // Now define messages
180 $messages[] = array(
181 'id' => $this->ticket->cache_msgs_ticket[$i]['id'],
182 'fk_user_action' => $this->ticket->cache_msgs_ticket[$i]['fk_user_action'], // Id of user owning the event
183 'fk_user_action_socid' => $user_action === null ? '' : $user_action->socid,
184 'fk_user_action_string' => $user_action === null ? '' : dolGetFirstLastname($user_action->firstname, $user_action->lastname),
185 'datec' => $this->ticket->cache_msgs_ticket[$i]['datec'],
186 'datep' => $this->ticket->cache_msgs_ticket[$i]['datep'],
187 'subject' => $this->ticket->cache_msgs_ticket[$i]['subject'],
188 'message' => $this->ticket->cache_msgs_ticket[$i]['message'],
189 'private' => $this->ticket->cache_msgs_ticket[$i]['private']
190 );
191 $i++;
192 }
193 $this->ticket->messages = $messages;
194 }
195
196 if (!DolibarrApi::_checkAccessToResource('ticket', $this->ticket->id)) {
197 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
198 }
199
200 if ($contact_list > -1) {
201 // Add external contacts ids
202 $tmparray = $this->ticket->liste_contact(-1, 'external', $contact_list);
203 if (is_array($tmparray)) {
204 $this->ticket->contacts_ids = $tmparray;
205 }
206 $tmparray = $this->ticket->liste_contact(-1, 'internal', $contact_list);
207 if (is_array($tmparray)) {
208 $this->ticket->contacts_ids_internal = $tmparray;
209 }
210 }
211
212 return $this->_cleanObjectDatas($this->ticket);
213 }
214
234 public function index($socid = 0, $sortfield = "t.rowid", $sortorder = "ASC", $limit = 100, $page = 0, $sqlfilters = '', $properties = '', $loadcontacts = 0, $pagination_data = false)
235 {
236 if (!DolibarrApiAccess::$user->hasRight('ticket', 'read')) {
237 throw new RestException(403);
238 }
239
240 $obj_ret = array();
241
242 $socid = DolibarrApiAccess::$user->socid ?: $socid;
243
244 $search_sale = null;
245 // If the internal user must only see his customers, force searching by him
246 $search_sale = 0;
247 if (!DolibarrApiAccess::$user->hasRight('societe', 'client', 'voir') && !$socid) {
248 $search_sale = DolibarrApiAccess::$user->id;
249 }
250
251 $sql = "SELECT t.rowid";
252 $sql .= " FROM ".MAIN_DB_PREFIX."ticket AS t";
253 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe AS s ON (s.rowid = t.fk_soc)";
254 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."ticket_extrafields AS ef ON (ef.fk_object = t.rowid)"; // Modification VMR Global Solutions to include extrafields as search parameters in the API GET call, so we will be able to filter on extrafields
255 $sql .= ' WHERE t.entity IN ('.getEntity('ticket', 1).')';
256 if ($socid > 0) {
257 $sql .= " AND t.fk_soc = ".((int) $socid);
258 }
259 // Search on sale representative
260 if ($search_sale && $search_sale != '-1') {
261 if ($search_sale == -2) {
262 $sql .= " AND ".getSalesRepresentativeSqlFilter('t.fk_soc', 0, 1);
263 } elseif ($search_sale > 0) {
264 $sql .= " AND ".getSalesRepresentativeSqlFilter('t.fk_soc', (int) $search_sale);
265 }
266 }
267 // Add sql filters
268 if ($sqlfilters) {
269 $errormessage = '';
270 $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
271 if ($errormessage) {
272 throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage);
273 }
274 }
275
276 //this query will return total orders with the filters given
277 $sqlTotals = str_replace('SELECT t.rowid', 'SELECT count(t.rowid) as total', $sql);
278
279 $sql .= $this->db->order($sortfield, $sortorder);
280
281 if ($limit) {
282 if ($page < 0) {
283 $page = 0;
284 }
285 $offset = $limit * $page;
286
287 $sql .= $this->db->plimit($limit, $offset);
288 }
289
290 $result = $this->db->query($sql);
291 if ($result) {
292 $i = 0;
293 $num = $this->db->num_rows($result);
294 $min = min($num, ($limit <= 0 ? $num : $limit));
295 while ($i < $min) {
296 $obj = $this->db->fetch_object($result);
297 $ticket_static = new Ticket($this->db);
298 if ($ticket_static->fetch($obj->rowid)) {
299 if ($ticket_static->fk_user_assign > 0) {
300 $userStatic = new User($this->db);
301 $userStatic->fetch($ticket_static->fk_user_assign);
302 //$ticket_static->fk_user_assign_string = dolGetFirstLastname($userStatic->firstname, $userStatic->lastname);
303 }
304
305 if ($loadcontacts) {
306 // Add external contacts ids
307 $tmparray = $ticket_static->liste_contact(-1, 'external', 1);
308 if (is_array($tmparray)) {
309 $ticket_static->contacts_ids = $tmparray;
310 }
311 $tmparray = $ticket_static->liste_contact(-1, 'internal', 1);
312 if (is_array($tmparray)) {
313 $ticket_static->contacts_ids_internal = $tmparray;
314 }
315 }
316
317 $obj_ret[] = $this->_filterObjectProperties($this->_cleanObjectDatas($ticket_static), $properties);
318 }
319 $i++;
320 }
321 } else {
322 throw new RestException(503, 'Error when retrieve ticket list');
323 }
324
325 //if $pagination_data is true the response will contain element data with all values and element pagination with pagination data(total,page,limit)
326 if ($pagination_data) {
327 $totalsResult = $this->db->query($sqlTotals);
328 $total = $this->db->fetch_object($totalsResult)->total;
329
330 $tmp = $obj_ret;
331 $obj_ret = [];
332
333 $obj_ret['data'] = $tmp;
334 $obj_ret['pagination'] = [
335 'total' => (int) $total,
336 'page' => $page, //count starts from 0
337 'page_count' => ceil((int) $total / $limit),
338 'limit' => $limit
339 ];
340 }
341
342 return $obj_ret;
343 }
344
353 public function post($request_data = null)
354 {
355 $ticketstatic = new Ticket($this->db);
356 if (!DolibarrApiAccess::$user->hasRight('ticket', 'write')) {
357 throw new RestException(403);
358 }
359
360 // Check mandatory fields
361 $this->_validate($request_data);
362
363 // Check thirdparty validity
364 $socid = (int) $request_data['socid'];
365 if ($socid > 0) {
366 $thirdpartytmp = new Societe($this->db);
367 $thirdparty_result = $thirdpartytmp->fetch($socid);
368 if ($thirdparty_result < 1) {
369 throw new RestException(404, 'Thirdparty with id='.$socid.' not found or not allowed');
370 }
371 if (!DolibarrApi::_checkAccessToResource('societe', $thirdpartytmp->id)) {
372 throw new RestException(404, 'Thirdparty with id='.$thirdpartytmp->id.' not found or not allowed');
373 }
374 }
375
376 foreach ($request_data as $field => $value) {
377 if ($field === 'caller') {
378 // Add a mention of caller so on trigger called after action, we can filter to avoid a loop if we try to sync back again with the caller
379 $this->ticket->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
380 continue;
381 }
382
383 $this->ticket->$field = $this->_checkValForAPI($field, $value, $this->ticket);
384 }
385 if (empty($this->ticket->ref)) {
386 $this->ticket->ref = $ticketstatic->getDefaultRef();
387 }
388 if (empty($this->ticket->track_id)) {
389 $this->ticket->track_id = generate_random_id(16);
390 }
391
392 if ($this->ticket->create(DolibarrApiAccess::$user) < 0) {
393 throw new RestException(500, "Error creating ticket", array_merge(array($this->ticket->error), $this->ticket->errors));
394 }
395
396 return $this->ticket->id;
397 }
398
409 public function postNewMessage($request_data = null)
410 {
411 if (!DolibarrApiAccess::$user->hasRight('ticket', 'write')) {
412 throw new RestException(403);
413 }
414
415 // Check mandatory fields
416 $result = $this->_validateMessage($request_data);
417
418 $return_action_id = false;
419 if (isset($request_data['return_action_id'])) {
420 $return_action_id = !empty($request_data['return_action_id']);
421 unset($request_data['return_action_id']);
422 }
423
424 $attachments = array();
425 if (isset($request_data['attachments']) && is_array($request_data['attachments'])) {
426 $attachments = $request_data['attachments'];
427 unset($request_data['attachments']);
428 }
429
430 foreach ($request_data as $field => $value) {
431 if ($field === 'caller') {
432 // Add a mention of caller so on trigger called after action, we can filter to avoid a loop if we try to sync back again with the caller
433 $this->ticket->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
434 continue;
435 }
436
437 $this->ticket->$field = $this->_checkValForAPI($field, $value, $this->ticket);
438 }
439 $ticketMessageText = $this->ticket->message;
440 // Allow targeting the ticket by id or ref, not only track_id
441 if (!empty($this->ticket->id)) {
442 $result = $this->ticket->fetch($this->ticket->id);
443 } elseif (!empty($this->ticket->ref)) {
444 $result = $this->ticket->fetch(0, $this->ticket->ref);
445 } else {
446 $result = $this->ticket->fetch(0, '', $this->ticket->track_id);
447 }
448 if (!$result) {
449 throw new RestException(404, 'Ticket not found');
450 }
451
452 if (!DolibarrApi::_checkAccessToResource('ticket', $this->ticket->id)) {
453 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
454 }
455
456 $this->ticket->message = $ticketMessageText;
457
458 $filename_list = array();
459 $mimetype_list = array();
460 $mimefilename_list = array();
461 if (!empty($attachments)) {
462 global $conf;
463 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
464
465 $destdir = $conf->ticket->dir_output.'/'.$this->ticket->ref;
466 if (!dol_is_dir($destdir) && dol_mkdir($destdir) < 0) {
467 throw new RestException(500, 'Error while trying to create directory '.$destdir);
468 }
469
470 foreach ($attachments as $attachment) {
471 if (!is_array($attachment) || empty($attachment['filename'])) {
472 continue;
473 }
474
475 $filename = dol_sanitizeFileName($attachment['filename']);
476 if (empty($filename)) {
477 continue;
478 }
479
480 $filecontent = isset($attachment['filecontent']) ? $attachment['filecontent'] : '';
481 $fileencoding = isset($attachment['fileencoding']) ? $attachment['fileencoding'] : '';
482 $content = ($fileencoding === 'base64') ? base64_decode($filecontent) : $filecontent;
483 if ($content === false) {
484 throw new RestException(400, 'Failed to decode attachment '.$filename);
485 }
486
487 $destfile = $destdir.'/'.$filename;
488 if (is_file($destfile)) {
489 $pathinfo = pathinfo($filename);
490 $suffix = ' - '.dol_print_date(dol_now(), 'dayhourlog');
491 $extension = !empty($pathinfo['extension']) ? '.'.$pathinfo['extension'] : '';
492 $basename = !empty($pathinfo['filename']) ? $pathinfo['filename'] : preg_replace('/\.[^.]+$/', '', $filename);
493 $destfile = $destdir.'/'.$basename.$suffix.$extension;
494 }
495
496 $destfiletmp = DOL_DATA_ROOT.'/admin/temp/'.basename($destfile);
497 if (!dol_is_dir(dirname($destfiletmp))) {
498 dol_mkdir(dirname($destfiletmp));
499 }
500
501 $fhandle = @fopen($destfiletmp, 'w');
502 if (!$fhandle) {
503 throw new RestException(500, "Failed to open file '".$destfiletmp."' for write");
504 }
505 $nbofbyteswrote = fwrite($fhandle, $content);
506 fclose($fhandle);
507 if ($nbofbyteswrote === false) {
508 throw new RestException(500, "Failed to write file '".$destfiletmp."'");
509 }
510
511 $moreinfo = array(
512 'description' => 'File uploaded using ticket API',
513 'src_object_type' => $this->ticket->element,
514 'src_object_id' => $this->ticket->id,
515 'gen_or_uploaded' => 'uploaded'
516 );
517 $resultmove = dol_move($destfiletmp, $destfile, '0', 1, 0, 1, $moreinfo);
518 if (!$resultmove) {
519 throw new RestException(500, "Failed to move file into '".$destfile."'");
520 }
521
522 $filename_list[] = $destfile;
523 $mimefilename_list[] = basename($destfile);
524 $mimetype_list[] = !empty($attachment['mimetype']) ? $attachment['mimetype'] : '';
525 }
526 }
527
528 $actionid = $this->ticket->createTicketMessage(DolibarrApiAccess::$user, 0, $filename_list, $mimetype_list, $mimefilename_list);
529 if ($actionid <= 0) {
530 throw new RestException(500, 'Error when creating ticket');
531 }
532 if ($return_action_id) {
533 return array('ticket_id' => $this->ticket->id, 'action_id' => $actionid);
534 }
535 return $this->ticket->id;
536 }
537
547 public function put($id, $request_data = null)
548 {
549 if (!DolibarrApiAccess::$user->hasRight('ticket', 'write')) {
550 throw new RestException(403);
551 }
552
553 $result = $this->ticket->fetch($id);
554 if (!$result) {
555 throw new RestException(404, 'Ticket not found');
556 }
557
558 if (!DolibarrApi::_checkAccessToResource('ticket', $this->ticket->id)) {
559 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
560 }
561
562 // Check thirdparty validity
563 $socid = (int) $request_data['socid'];
564 if ($socid > 0) {
565 $thirdpartytmp = new Societe($this->db);
566 $thirdparty_result = $thirdpartytmp->fetch($socid);
567 if ($thirdparty_result < 1) {
568 throw new RestException(404, 'Thirdparty with id='.$socid.' not found or not allowed');
569 }
570 if (!DolibarrApi::_checkAccessToResource('societe', $thirdpartytmp->id)) {
571 throw new RestException(404, 'Thirdparty with id='.$thirdpartytmp->id.' not found or not allowed');
572 }
573 }
574
575 foreach ($request_data as $field => $value) {
576 if ($field === 'caller') {
577 // Add a mention of caller so on trigger called after action, we can filter to avoid a loop if we try to sync back again with the caller
578 $this->ticket->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
579 continue;
580 }
581
582 if ($field == 'id') {
583 continue;
584 }
585 if ($field == 'array_options' && is_array($value)) {
586 foreach ($value as $index => $val) {
587 $this->ticket->array_options[$index] = $this->_checkValExtrafieldsForAPI($index, $val, $this->ticket);
588 }
589 continue;
590 }
591
592 $this->ticket->$field = $this->_checkValForAPI($field, $value, $this->ticket);
593 }
594
595 if ($this->ticket->update(DolibarrApiAccess::$user) > 0) {
596 return $this->get($id);
597 } else {
598 throw new RestException(500, $this->ticket->error);
599 }
600 }
601
610 public function delete($id)
611 {
612 if (!DolibarrApiAccess::$user->hasRight('ticket', 'delete')) {
613 throw new RestException(403);
614 }
615 $result = $this->ticket->fetch($id);
616 if (!$result) {
617 throw new RestException(404, 'Ticket not found');
618 }
619
620 if (!DolibarrApi::_checkAccessToResource('ticket', $this->ticket->id)) {
621 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
622 }
623
624 if (!$this->ticket->delete(DolibarrApiAccess::$user)) {
625 throw new RestException(500, 'Error when deleting ticket');
626 }
627
628 return array(
629 'success' => array(
630 'code' => 200,
631 'message' => 'Ticket deleted'
632 )
633 );
634 }
635
644 private function _validate($data)
645 {
646 if ($data === null) {
647 $data = array();
648 }
649 $ticket = array();
650 foreach (Tickets::$FIELDS as $field) {
651 if (!isset($data[$field])) {
652 throw new RestException(400, "$field field missing");
653 }
654 $ticket[$field] = $data[$field];
655 }
656 return $ticket;
657 }
658
667 private function _validateMessage($data)
668 {
669 if ($data === null) {
670 $data = array();
671 }
672 $ticket = array();
673 foreach (Tickets::$FIELDS_MESSAGES as $field) {
674 if (!isset($data[$field])) {
675 throw new RestException(400, "$field field missing");
676 }
677 $ticket[$field] = $data[$field];
678 }
679 return $ticket;
680 }
681
682 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
694 protected function _cleanObjectDatas($object)
695 {
696 // phpcs:enable
697 $object = parent::_cleanObjectDatas($object);
698
699 // Other attributes to clean
700 $attr2clean = array(
701 "contact",
702 "contact_id",
703 "ref_previous",
704 "ref_next",
705 "ref_ext",
706 "table_element_line",
707 "statut",
708 "country",
709 "country_id",
710 "country_code",
711 "barcode_type",
712 "barcode_type_code",
713 "barcode_type_label",
714 "barcode_type_coder",
715 "mode_reglement_id",
716 "cond_reglement_id",
717 "cond_reglement",
718 "fk_delivery_address",
719 "shipping_method_id",
720 "modelpdf",
721 "fk_account",
722 "note_public",
723 "note_private",
724 "note",
725 "total_ht",
726 "total_tva",
727 "total_localtax1",
728 "total_localtax2",
729 "total_ttc",
730 "fk_incoterms",
731 "label_incoterms",
732 "location_incoterms",
733 "name",
734 "lastname",
735 "firstname",
736 "civility_id",
737 "canvas",
738 "cache_msgs_ticket",
739 "cache_logs_ticket",
740 "cache_types_tickets",
741 "regeximgext",
742 "labelStatus",
743 "labelStatusShort",
744 "multicurrency_code",
745 "multicurrency_tx",
746 "multicurrency_total_ht",
747 "multicurrency_total_ttc",
748 "multicurrency_total_tva",
749 "multicurrency_total_localtax1",
750 "multicurrency_total_localtax2"
751 );
752 foreach ($attr2clean as $toclean) {
753 unset($object->$toclean);
754 }
755
756 // If object has lines, remove $db property
757 if (isset($object->lines) && count($object->lines) > 0) {
758 $nboflines = count($object->lines);
759 for ($i = 0; $i < $nboflines; $i++) {
760 $this->_cleanObjectDatas($object->lines[$i]);
761 }
762 }
763
764 // If object has linked objects, remove $db property
765 if (isset($object->linkedObjects) && count($object->linkedObjects) > 0) {
766 foreach ($object->linkedObjects as $type_object => $linked_object) {
767 foreach ($linked_object as $object2clean) {
768 $this->_cleanObjectDatas($object2clean);
769 }
770 }
771 }
772 return $object;
773 }
774
795 public function postContact(int $id, int $contactid, string $type, string $source = "external", int $notrigger = 0): array
796 {
797 // Check permissions
798 if (!DolibarrApiAccess::$user->hasRight('ticket', 'write')) {
799 throw new RestException(403);
800 }
801
802 // test source
803 if (empty($source)) {
804 throw new RestException(400, 'Source can not be empty');
805 }
806 // test type
807 if (empty($type)) {
808 throw new RestException(400, 'type can not be empty');
809 }
810
811 // Check type/source contact exists
812 $sqlCheckTypeSource = "SELECT tc.rowid";
813 $sqlCheckTypeSource .= " FROM ".$this->db->prefix()."c_type_contact as tc";
814 $sqlCheckTypeSource .= " WHERE tc.element LIKE 'ticket'";
815 $sqlCheckTypeSource .= " AND tc.source = '".$this->db->escape($source)."'";
816 $sqlCheckTypeSource .= " AND tc.code = '".$this->db->escape($type)."'";
817 $sqlCheckTypeSource .= " AND tc.active = 1";
818 $result = $this->db->query($sqlCheckTypeSource);
819
820 if ($result && $this->db->num_rows($result) == 0) {
821 throw new RestException(400, 'Contact type not found');
822 }
823
824 // Check contact exists
825 if ($source == "external") {
826 // Check external contact exists
827 $sqlCheckExternalContact = "SELECT 1 as exist";
828 $sqlCheckExternalContact .= " FROM ".MAIN_DB_PREFIX."socpeople";
829 $sqlCheckExternalContact .= " WHERE rowid = " . intval($contactid);
830 $result = $this->db->query($sqlCheckExternalContact);
831
832 if ($result && $this->db->num_rows($result) == 0) {
833 throw new RestException(404, 'External contact not found');
834 }
835 } else {
836 // Check internal contact exists
837 $sqlCheckInternalContact = "SELECT 1 as exist";
838 $sqlCheckInternalContact .= " FROM ".MAIN_DB_PREFIX."user";
839 $sqlCheckInternalContact .= " WHERE rowid = " . intval($contactid);
840 $result = $this->db->query($sqlCheckInternalContact);
841
842 if ($result && $this->db->num_rows($result) == 0) {
843 throw new RestException(404, 'Internal contact not found');
844 }
845 }
846
847 // tests done, let's get it
848 $result = $this->ticket->fetch($id);
849 if (!$result) {
850 throw new RestException(404, 'Ticket not found');
851 }
852 if (!DolibarrApi::_checkAccessToResource('ticket', $this->ticket->id)) {
853 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
854 }
855
856 $result = $this->ticket->add_contact($contactid, $type, $source, $notrigger);
857
858 if ($result == 0) {
859 throw new RestException(400, 'Already exists: Contact='.$contactid.' is already linked to the ticket='.$id.' as source='.$source.' and type='.$type);
860 } elseif ($result == -1) {
861 throw new RestException(400, 'Wrong contact='.$contactid);
862 } elseif ($result == -2) {
863 throw new RestException(400, 'Wrong type='.$type);
864 } elseif ($result == -3) {
865 throw new RestException(400, 'Not allowed contacts');
866 } elseif ($result == -4) {
867 throw new RestException(400, 'ErrorCommercialNotAllowedForThirdparty');
868 } elseif ($result == -5) {
869 throw new RestException(400, 'Trigger failed');
870 } elseif ($result == -6) {
871 throw new RestException(400, 'DB_ERROR_RECORD_ALREADY_EXISTS');
872 } elseif ($result == -7) {
873 throw new RestException(400, 'Some other error');
874 } elseif ($result <= -8) {
875 throw new RestException(400, 'Unknown error occurred');
876 }
877
878 return array(
879 'success' => array(
880 'code' => 200,
881 'message' => 'Contact='.$contactid.' linked to the ticket='.$id.' as '.$source.' '.$type
882 )
883 );
884 }
885
905 public function deleteContact(int $id, int $contactid, string $type, string $source = "external"): array
906 {
907 // Check permissions
908 if (!DolibarrApiAccess::$user->hasRight('ticket', 'write')) {
909 throw new RestException(403);
910 }
911
912 // test source
913 if (empty($source)) {
914 throw new RestException(400, 'Source can not be empty');
915 }
916 // test type
917 if (empty($type)) {
918 throw new RestException(400, 'type can not be empty');
919 }
920
921 $result = $this->ticket->fetch($id);
922 if (!$result) {
923 throw new RestException(404, 'Ticket not found');
924 }
925
926 if (!DolibarrApi::_checkAccessToResource('ticket', $this->ticket->id)) {
927 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
928 }
929
930 $contacts = $this->ticket->liste_contact(-1, $source);
931 foreach ($contacts as $contact) {
932 if ($contact['id'] == $contactid && $contact['code'] == $type) {
933 $result = $this->ticket->delete_contact($contact['rowid']);
934
935 if (!$result) {
936 throw new RestException(500, 'Error when deleting the contact '.$contact['rowid']);
937 }
938 }
939 }
940
941 return array(
942 'success' => array(
943 'code' => 200,
944 'message' => 'Contact unlinked from ticket'
945 )
946 );
947 }
948}
$id
Support class for third parties, contacts, members, users or resources.
Definition account.php:47
if(! $sortfield) if(! $sortorder) $object
Definition account.php:100
Class for API REST v1.
Definition api.class.php:35
_checkValExtrafieldsForAPI($field, $value, $object)
Check and convert a string depending on its type/name.
_filterObjectProperties($object, $properties)
Filter properties that will be returned on object.
_checkValForAPI($field, $value, $object)
Check and convert a string depending on its type/name.
static _checkAccessToResource($resource, $resource_id=0, $dbtablename='', $feature2='', $dbt_keyfield='fk_soc', $dbt_select='rowid', $parenttableforentity='')
Check access by user to a given resource.
Class to manage third parties objects (customers, suppliers, prospects...)
getByTrackId($track_id, $contact_list=1)
Get properties of a Ticket object from track id.
deleteContact(int $id, int $contactid, string $type, string $source="external")
Unlink a contact type of given ticket.
getCommon($id=0, $track_id='', $ref='', $contact_list=1)
Get properties of a Ticket object Return an array with ticket information.
__construct()
Constructor.
index($socid=0, $sortfield="t.rowid", $sortorder="ASC", $limit=100, $page=0, $sqlfilters='', $properties='', $loadcontacts=0, $pagination_data=false)
List tickets.
_cleanObjectDatas($object)
Clean sensible object datas @phpstan-template T.
postNewMessage($request_data=null)
Add a new message to an existing ticket identified by property ->track_id into request.
post($request_data=null)
Create ticket object.
put($id, $request_data=null)
Update ticket.
_validateMessage($data)
Validate fields before create or update object message.
postContact(int $id, int $contactid, string $type, string $source="external", int $notrigger=0)
Add a contact type of given ticket.
getByRef($ref, $contact_list=1)
Get properties of a Ticket object from ref.
_validate($data)
Validate fields before create or update object.
Class to manage Dolibarr users.
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $db
API class for accounts.
dol_move($srcfile, $destfile, $newmask='0', $overwriteifexists=1, $testvirus=0, $indexdatabase=1, $moreinfo=array(), $entity=null)
Move a file into another name.
dol_is_dir($folder)
Test if filename is a directory.
dol_now($mode='gmt')
Return date for now.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1, $includequotes=0, $allowdash=0)
Clean a string to use it as a file name.
dolGetFirstLastname($firstname, $lastname, $nameorder=-1)
Return firstname and lastname in correct order.
forgeSQLFromUniversalSearchCriteria($filter, &$errorstr='', $noand=0, $nopar=0, $noerror=0, $forbiddenfields=array())
forgeSQLFromUniversalSearchCriteria
sanitizeVal($out='', $check='alphanohtml', $filter=null, $options=null)
Return a sanitized or empty value after checking value against a rule.
dol_mkdir($dir, $dataroot='', $newmask='')
Creation of a directory (this can create recursive subdir)
Class to generate the form for creating a new ticket.
generate_random_id($car=16)
Generate a random id.