dolibarr  20.0.0-beta
api_tickets.class.php
1 <?php
2 /* Copyright (C) 2016 Jean-François Ferry <hello@librethic.io>
3  * Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <https://www.gnu.org/licenses/>.
17  */
18 
19  use Luracast\Restler\RestException;
20 
21 require_once DOL_DOCUMENT_ROOT.'/ticket/class/ticket.class.php';
22 require_once DOL_DOCUMENT_ROOT.'/core/lib/ticket.lib.php';
23 
24 
31 class Tickets extends DolibarrApi
32 {
36  public static $FIELDS = array(
37  'subject',
38  'message'
39  );
40 
44  public static $FIELDS_MESSAGES = array(
45  'track_id',
46  'message'
47  );
48 
52  public $ticket;
53 
57  public function __construct()
58  {
59  global $db;
60  $this->db = $db;
61  $this->ticket = new Ticket($this->db);
62  }
63 
76  public function get($id)
77  {
78  return $this->getCommon($id, '', '');
79  }
80 
95  public function getByTrackId($track_id)
96  {
97  return $this->getCommon(0, $track_id, '');
98  }
99 
114  public function getByRef($ref)
115  {
116  return $this->getCommon(0, '', $ref);
117  }
118 
128  private function getCommon($id = 0, $track_id = '', $ref = '')
129  {
130  if (!DolibarrApiAccess::$user->hasRight('ticket', 'read')) {
131  throw new RestException(403);
132  }
133 
134  // Check parameters
135  if (($id < 0) && !$track_id && !$ref) {
136  throw new RestException(400, 'Wrong parameters');
137  }
138  if ($id == 0) {
139  $result = $this->ticket->initAsSpecimen();
140  } else {
141  $result = $this->ticket->fetch($id, $ref, $track_id);
142  }
143  if (!$result) {
144  throw new RestException(404, 'Ticket not found');
145  }
146 
147  // String for user assigned
148  if ($this->ticket->fk_user_assign > 0) {
149  $userStatic = new User($this->db);
150  $userStatic->fetch($this->ticket->fk_user_assign);
151  $this->ticket->fk_user_assign_string = $userStatic->firstname.' '.$userStatic->lastname;
152  }
153 
154  // Messages of ticket
155  $messages = array();
156  $this->ticket->loadCacheMsgsTicket();
157  if (is_array($this->ticket->cache_msgs_ticket) && count($this->ticket->cache_msgs_ticket) > 0) {
158  $num = count($this->ticket->cache_msgs_ticket);
159  $i = 0;
160  while ($i < $num) {
161  if ($this->ticket->cache_msgs_ticket[$i]['fk_user_author'] > 0) {
162  $user_action = new User($this->db);
163  $user_action->fetch($this->ticket->cache_msgs_ticket[$i]['fk_user_author']);
164  }
165 
166  // Now define messages
167  $messages[] = array(
168  'id' => $this->ticket->cache_msgs_ticket[$i]['id'],
169  'fk_user_action' => $this->ticket->cache_msgs_ticket[$i]['fk_user_author'],
170  'fk_user_action_socid' => $user_action->socid,
171  'fk_user_action_string' => dolGetFirstLastname($user_action->firstname, $user_action->lastname),
172  'message' => $this->ticket->cache_msgs_ticket[$i]['message'],
173  'datec' => $this->ticket->cache_msgs_ticket[$i]['datec'],
174  'private' => $this->ticket->cache_msgs_ticket[$i]['private']
175  );
176  $i++;
177  }
178  $this->ticket->messages = $messages;
179  }
180 
181  if (!DolibarrApi::_checkAccessToResource('ticket', $this->ticket->id)) {
182  throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
183  }
184  return $this->_cleanObjectDatas($this->ticket);
185  }
186 
203  public function index($socid = 0, $sortfield = "t.rowid", $sortorder = "ASC", $limit = 100, $page = 0, $sqlfilters = '', $properties = '')
204  {
205  if (!DolibarrApiAccess::$user->hasRight('ticket', 'read')) {
206  throw new RestException(403);
207  }
208 
209  $obj_ret = array();
210 
211  $socid = DolibarrApiAccess::$user->socid ? DolibarrApiAccess::$user->socid : $socid;
212 
213  $search_sale = null;
214  // If the internal user must only see his customers, force searching by him
215  $search_sale = 0;
216  if (!DolibarrApiAccess::$user->hasRight('societe', 'client', 'voir') && !$socid) {
217  $search_sale = DolibarrApiAccess::$user->id;
218  }
219 
220  $sql = "SELECT t.rowid";
221  $sql .= " FROM ".MAIN_DB_PREFIX."ticket AS t";
222  $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
223  $sql .= ' WHERE t.entity IN ('.getEntity('ticket', 1).')';
224  if ($socid > 0) {
225  $sql .= " AND t.fk_soc = ".((int) $socid);
226  }
227  // Search on sale representative
228  if ($search_sale && $search_sale != '-1') {
229  if ($search_sale == -2) {
230  $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = t.fk_soc)";
231  } elseif ($search_sale > 0) {
232  $sql .= " AND EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = t.fk_soc AND sc.fk_user = ".((int) $search_sale).")";
233  }
234  }
235  // Add sql filters
236  if ($sqlfilters) {
237  $errormessage = '';
238  $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
239  if ($errormessage) {
240  throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage);
241  }
242  }
243 
244  $sql .= $this->db->order($sortfield, $sortorder);
245 
246  if ($limit) {
247  if ($page < 0) {
248  $page = 0;
249  }
250  $offset = $limit * $page;
251 
252  $sql .= $this->db->plimit($limit, $offset);
253  }
254 
255  $result = $this->db->query($sql);
256  if ($result) {
257  $num = $this->db->num_rows($result);
258  $i = 0;
259  while ($i < $num) {
260  $obj = $this->db->fetch_object($result);
261  $ticket_static = new Ticket($this->db);
262  if ($ticket_static->fetch($obj->rowid)) {
263  if ($ticket_static->fk_user_assign > 0) {
264  $userStatic = new User($this->db);
265  $userStatic->fetch($ticket_static->fk_user_assign);
266  $ticket_static->fk_user_assign_string = $userStatic->firstname.' '.$userStatic->lastname;
267  }
268  $obj_ret[] = $this->_filterObjectProperties($this->_cleanObjectDatas($ticket_static), $properties);
269  }
270  $i++;
271  }
272  } else {
273  throw new RestException(503, 'Error when retrieve ticket list');
274  }
275 
276  return $obj_ret;
277  }
278 
285  public function post($request_data = null)
286  {
287  $ticketstatic = new Ticket($this->db);
288  if (!DolibarrApiAccess::$user->hasRight('ticket', 'write')) {
289  throw new RestException(403);
290  }
291  // Check mandatory fields
292  $result = $this->_validate($request_data);
293 
294  foreach ($request_data as $field => $value) {
295  if ($field === 'caller') {
296  // 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
297  $this->ticket->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
298  continue;
299  }
300 
301  $this->ticket->$field = $this->_checkValForAPI($field, $value, $this->ticket);
302  }
303  if (empty($this->ticket->ref)) {
304  $this->ticket->ref = $ticketstatic->getDefaultRef();
305  }
306  if (empty($this->ticket->track_id)) {
307  $this->ticket->track_id = generate_random_id(16);
308  }
309 
310  if ($this->ticket->create(DolibarrApiAccess::$user) < 0) {
311  throw new RestException(500, "Error creating ticket", array_merge(array($this->ticket->error), $this->ticket->errors));
312  }
313 
314  return $this->ticket->id;
315  }
316 
324  public function postNewMessage($request_data = null)
325  {
326  $ticketstatic = new Ticket($this->db);
327  if (!DolibarrApiAccess::$user->hasRight('ticket', 'write')) {
328  throw new RestException(403);
329  }
330  // Check mandatory fields
331  $result = $this->_validateMessage($request_data);
332 
333  foreach ($request_data as $field => $value) {
334  if ($field === 'caller') {
335  // 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
336  $this->ticket->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
337  continue;
338  }
339 
340  $this->ticket->$field = $this->_checkValForAPI($field, $value, $this->ticket);
341  }
342  $ticketMessageText = $this->ticket->message;
343  $result = $this->ticket->fetch('', '', $this->ticket->track_id);
344  if (!$result) {
345  throw new RestException(404, 'Ticket not found');
346  }
347  $this->ticket->message = $ticketMessageText;
348  if (!$this->ticket->createTicketMessage(DolibarrApiAccess::$user)) {
349  throw new RestException(500, 'Error when creating ticket');
350  }
351  return $this->ticket->id;
352  }
353 
361  public function put($id, $request_data = null)
362  {
363  if (!DolibarrApiAccess::$user->hasRight('ticket', 'write')) {
364  throw new RestException(403);
365  }
366 
367  $result = $this->ticket->fetch($id);
368  if (!$result) {
369  throw new RestException(404, 'Ticket not found');
370  }
371 
372  if (!DolibarrApi::_checkAccessToResource('ticket', $this->ticket->id)) {
373  throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
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 
386  if ($this->ticket->update(DolibarrApiAccess::$user) > 0) {
387  return $this->get($id);
388  } else {
389  throw new RestException(500, $this->ticket->error);
390  }
391  }
392 
400  public function delete($id)
401  {
402  if (!DolibarrApiAccess::$user->hasRight('ticket', 'delete')) {
403  throw new RestException(403);
404  }
405  $result = $this->ticket->fetch($id);
406  if (!$result) {
407  throw new RestException(404, 'Ticket not found');
408  }
409 
410  if (!DolibarrApi::_checkAccessToResource('ticket', $this->ticket->id)) {
411  throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
412  }
413 
414  if (!$this->ticket->delete(DolibarrApiAccess::$user)) {
415  throw new RestException(500, 'Error when deleting ticket');
416  }
417 
418  return array(
419  'success' => array(
420  'code' => 200,
421  'message' => 'Ticket deleted'
422  )
423  );
424  }
425 
434  private function _validate($data)
435  {
436  $ticket = array();
437  foreach (Tickets::$FIELDS as $field) {
438  if (!isset($data[$field])) {
439  throw new RestException(400, "$field field missing");
440  }
441  $ticket[$field] = $data[$field];
442  }
443  return $ticket;
444  }
445 
454  private function _validateMessage($data)
455  {
456  $ticket = array();
457  foreach (Tickets::$FIELDS_MESSAGES as $field) {
458  if (!isset($data[$field])) {
459  throw new RestException(400, "$field field missing");
460  }
461  $ticket[$field] = $data[$field];
462  }
463  return $ticket;
464  }
465 
466  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
476  protected function _cleanObjectDatas($object)
477  {
478  // phpcs:enable
479  $object = parent::_cleanObjectDatas($object);
480 
481  // Other attributes to clean
482  $attr2clean = array(
483  "contact",
484  "contact_id",
485  "ref_previous",
486  "ref_next",
487  "ref_ext",
488  "table_element_line",
489  "statut",
490  "country",
491  "country_id",
492  "country_code",
493  "barcode_type",
494  "barcode_type_code",
495  "barcode_type_label",
496  "barcode_type_coder",
497  "mode_reglement_id",
498  "cond_reglement_id",
499  "cond_reglement",
500  "fk_delivery_address",
501  "shipping_method_id",
502  "modelpdf",
503  "fk_account",
504  "note_public",
505  "note_private",
506  "note",
507  "total_ht",
508  "total_tva",
509  "total_localtax1",
510  "total_localtax2",
511  "total_ttc",
512  "fk_incoterms",
513  "label_incoterms",
514  "location_incoterms",
515  "name",
516  "lastname",
517  "firstname",
518  "civility_id",
519  "canvas",
520  "cache_msgs_ticket",
521  "cache_logs_ticket",
522  "cache_types_tickets",
523  "cache_category_tickets",
524  "regeximgext",
525  "labelStatus",
526  "labelStatusShort",
527  "multicurrency_code",
528  "multicurrency_tx",
529  "multicurrency_total_ht",
530  "multicurrency_total_ttc",
531  "multicurrency_total_tva",
532  "multicurrency_total_localtax1",
533  "multicurrency_total_localtax2"
534  );
535  foreach ($attr2clean as $toclean) {
536  unset($object->$toclean);
537  }
538 
539  // If object has lines, remove $db property
540  if (isset($object->lines) && count($object->lines) > 0) {
541  $nboflines = count($object->lines);
542  for ($i = 0; $i < $nboflines; $i++) {
543  $this->_cleanObjectDatas($object->lines[$i]);
544  }
545  }
546 
547  // If object has linked objects, remove $db property
548  if (isset($object->linkedObjects) && count($object->linkedObjects) > 0) {
549  foreach ($object->linkedObjects as $type_object => $linked_object) {
550  foreach ($linked_object as $object2clean) {
551  $this->_cleanObjectDatas($object2clean);
552  }
553  }
554  }
555  return $object;
556  }
557 }
if($user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition: card.php:58
Class for API REST v1.
Definition: api.class.php:30
_filterObjectProperties($object, $properties)
Filter properties that will be returned on object.
Definition: api.class.php:136
static _checkAccessToResource($resource, $resource_id=0, $dbtablename='', $feature2='', $dbt_keyfield='fk_soc', $dbt_select='rowid')
Check access by user to a given resource.
Definition: api.class.php:369
_checkValForAPI($field, $value, $object)
Check and convert a string depending on its type/name.
Definition: api.class.php:82
getCommon($id=0, $track_id='', $ref='')
Get properties of a Ticket object Return an array with ticket information.
getByRef($ref)
Get properties of a Ticket object from ref.
index($socid=0, $sortfield="t.rowid", $sortorder="ASC", $limit=100, $page=0, $sqlfilters='', $properties='')
List tickets.
__construct()
Constructor.
_cleanObjectDatas($object)
Clean sensible object datas.
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.
getByTrackId($track_id)
Get properties of a Ticket object from track id.
_validate($data)
Validate fields before create or update object.
Class to manage Dolibarr users.
Definition: user.class.php:50
if(isModEnabled('invoice') && $user->hasRight('facture', 'lire')) if((isModEnabled('fournisseur') &&!getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD') && $user->hasRight("fournisseur", "facture", "lire"))||(isModEnabled('supplier_invoice') && $user->hasRight("supplier_invoice", "lire"))) if(isModEnabled('don') && $user->hasRight('don', 'lire')) if(isModEnabled('tax') && $user->hasRight('tax', 'charges', 'lire')) if(isModEnabled('invoice') &&isModEnabled('order') && $user->hasRight("commande", "lire") &&!getDolGlobalString('WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER')) $sql
Social contributions to pay.
Definition: index.php:745
forgeSQLFromUniversalSearchCriteria($filter, &$errorstr='', $noand=0, $nopar=0, $noerror=0)
forgeSQLFromUniversalSearchCriteria
dolGetFirstLastname($firstname, $lastname, $nameorder=-1)
Return firstname and lastname in correct order.
sanitizeVal($out='', $check='alphanohtml', $filter=null, $options=null)
Return a sanitized or empty value after checking value against a rule.
Class to generate the form for creating a new ticket.
generate_random_id($car=16)
Generate a random id.
Definition: ticket.lib.php:205