dolibarr  21.0.0-alpha
api_tasks.class.php
1 <?php
2 /* Copyright (C) 2015 Jean-Fran├žois Ferry <jfefe@aternatik.fr>
3  * Copyright (C) 2016 Laurent Destailleur <eldy@users.sourceforge.net>
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.'/projet/class/task.class.php';
22 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
23 
24 
31 class Tasks extends DolibarrApi
32 {
36  public static $FIELDS = array(
37  'ref',
38  'label',
39  'fk_project'
40  );
41 
45  public $task;
46 
50  public function __construct()
51  {
52  global $db, $conf;
53  $this->db = $db;
54  $this->task = new Task($this->db);
55  }
56 
68  public function get($id, $includetimespent = 0)
69  {
70  if (!DolibarrApiAccess::$user->hasRight('projet', 'lire')) {
71  throw new RestException(403);
72  }
73 
74  $result = $this->task->fetch($id);
75  if (!$result) {
76  throw new RestException(404, 'Task not found');
77  }
78 
79  if (!DolibarrApi::_checkAccessToResource('task', $this->task->id)) {
80  throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
81  }
82 
83  if ($includetimespent == 1) {
84  $timespent = $this->task->getSummaryOfTimeSpent(0);
85  }
86  if ($includetimespent == 2) {
87  $timespent = $this->task->fetchTimeSpentOnTask();
88  }
89 
90  return $this->_cleanObjectDatas($this->task);
91  }
92 
93 
94 
108  public function index($sortfield = "t.rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $sqlfilters = '', $properties = '')
109  {
110  global $db, $conf;
111 
112  if (!DolibarrApiAccess::$user->hasRight('projet', 'lire')) {
113  throw new RestException(403);
114  }
115 
116  $obj_ret = array();
117 
118  // case of external user, $thirdparty_ids param is ignored and replaced by user's socid
119  $socids = DolibarrApiAccess::$user->socid ? DolibarrApiAccess::$user->socid : 0;
120 
121  // If the internal user must only see his customers, force searching by him
122  $search_sale = 0;
123  if (!DolibarrApiAccess::$user->hasRight('societe', 'client', 'voir') && !$socids) {
124  $search_sale = DolibarrApiAccess::$user->id;
125  }
126 
127  $sql = "SELECT t.rowid";
128  $sql .= " FROM ".MAIN_DB_PREFIX."projet_task AS t";
129  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."projet_task_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
130  $sql .= " INNER JOIN ".MAIN_DB_PREFIX."projet AS p ON p.rowid = t.fk_projet";
131  $sql .= ' WHERE t.entity IN ('.getEntity('project').')';
132  if ($socids) {
133  $sql .= " AND t.fk_soc IN (".$this->db->sanitize($socids).")";
134  }
135  // Search on sale representative
136  if ($search_sale && $search_sale != '-1') {
137  if ($search_sale == -2) {
138  $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc)";
139  } elseif ($search_sale > 0) {
140  $sql .= " AND EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc AND sc.fk_user = ".((int) $search_sale).")";
141  }
142  }
143  // Add sql filters
144  if ($sqlfilters) {
145  $errormessage = '';
146  $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
147  if ($errormessage) {
148  throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage);
149  }
150  }
151 
152  $sql .= $this->db->order($sortfield, $sortorder);
153  if ($limit) {
154  if ($page < 0) {
155  $page = 0;
156  }
157  $offset = $limit * $page;
158 
159  $sql .= $this->db->plimit($limit + 1, $offset);
160  }
161 
162  dol_syslog("API Rest request");
163  $result = $this->db->query($sql);
164 
165  if ($result) {
166  $num = $this->db->num_rows($result);
167  $min = min($num, ($limit <= 0 ? $num : $limit));
168  $i = 0;
169  while ($i < $min) {
170  $obj = $this->db->fetch_object($result);
171  $task_static = new Task($this->db);
172  if ($task_static->fetch($obj->rowid)) {
173  $obj_ret[] = $this->_filterObjectProperties($this->_cleanObjectDatas($task_static), $properties);
174  }
175  $i++;
176  }
177  } else {
178  throw new RestException(503, 'Error when retrieve task list : '.$this->db->lasterror());
179  }
180 
181  return $obj_ret;
182  }
183 
190  public function post($request_data = null)
191  {
192  if (!DolibarrApiAccess::$user->hasRight('projet', 'creer')) {
193  throw new RestException(403, "Insuffisant rights");
194  }
195  // Check mandatory fields
196  $result = $this->_validate($request_data);
197 
198  foreach ($request_data as $field => $value) {
199  if ($field === 'caller') {
200  // 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
201  $this->task->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
202  continue;
203  }
204 
205  $this->task->$field = $this->_checkValForAPI($field, $value, $this->task);
206  }
207  /*if (isset($request_data["lines"])) {
208  $lines = array();
209  foreach ($request_data["lines"] as $line) {
210  array_push($lines, (object) $line);
211  }
212  $this->project->lines = $lines;
213  }*/
214  if ($this->task->create(DolibarrApiAccess::$user) < 0) {
215  throw new RestException(500, "Error creating task", array_merge(array($this->task->error), $this->task->errors));
216  }
217 
218  return $this->task->id;
219  }
220 
221  // /**
222  // * Get time spent of a task
223  // *
224  // * @param int $id Id of task
225  // * @return int
226  // *
227  // * @url GET {id}/tasks
228  // */
229  /*
230  public function getLines($id, $includetimespent=0)
231  {
232  if(! DolibarrApiAccess::$user->hasRight('projet', 'lire')) {
233  throw new RestException(403);
234  }
235 
236  $result = $this->project->fetch($id);
237  if( ! $result ) {
238  throw new RestException(404, 'Project not found');
239  }
240 
241  if( ! DolibarrApi::_checkAccessToResource('project',$this->project->id)) {
242  throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
243  }
244  $this->project->getLinesArray(DolibarrApiAccess::$user);
245  $result = array();
246  foreach ($this->project->lines as $line) // $line is a task
247  {
248  if ($includetimespent == 1)
249  {
250  $timespent = $line->getSummaryOfTimeSpent(0);
251  }
252  if ($includetimespent == 1)
253  {
254  // TODO
255  // Add class for timespent records and loop and fill $line->lines with records of timespent
256  }
257  array_push($result,$this->_cleanObjectDatas($line));
258  }
259  return $result;
260  }
261  */
262 
273  public function getRoles($id, $userid = 0)
274  {
275  global $db;
276 
277  if (!DolibarrApiAccess::$user->hasRight('projet', 'lire')) {
278  throw new RestException(403);
279  }
280 
281  $result = $this->task->fetch($id);
282  if (!$result) {
283  throw new RestException(404, 'Task not found');
284  }
285 
286  if (!DolibarrApi::_checkAccessToResource('tasks', $this->task->id)) {
287  throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
288  }
289 
290  $usert = DolibarrApiAccess::$user;
291  if ($userid > 0) {
292  $usert = new User($this->db);
293  $usert->fetch($userid);
294  }
295  $this->task->roles = $this->task->getUserRolesForProjectsOrTasks(null, $usert, 0, $id);
296  $result = array();
297  foreach ($this->task->roles as $line) {
298  array_push($result, $this->_cleanObjectDatas($line));
299  }
300 
301  return $result;
302  }
303 
304 
305  // /**
306  // * Add a task to given project
307  // *
308  // * @param int $id Id of project to update
309  // * @param array $request_data Projectline data
310  // *
311  // * @url POST {id}/tasks
312  // *
313  // * @return int
314  // */
315  /*
316  public function postLine($id, $request_data = null)
317  {
318  if(! DolibarrApiAccess::$user->hasRight('projet', 'creer')) {
319  throw new RestException(403);
320  }
321 
322  $result = $this->project->fetch($id);
323  if( ! $result ) {
324  throw new RestException(404, 'Project not found');
325  }
326 
327  if( ! DolibarrApi::_checkAccessToResource('project',$this->project->id)) {
328  throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
329  }
330 
331  $request_data = (object) $request_data;
332 
333  $request_data->desc = sanitizeVal($request_data->desc, 'restricthtml');
334 
335  $updateRes = $this->project->addline(
336  $request_data->desc,
337  $request_data->subprice,
338  $request_data->qty,
339  $request_data->tva_tx,
340  $request_data->localtax1_tx,
341  $request_data->localtax2_tx,
342  $request_data->fk_product,
343  $request_data->remise_percent,
344  $request_data->info_bits,
345  $request_data->fk_remise_except,
346  'HT',
347  0,
348  $request_data->date_start,
349  $request_data->date_end,
350  $request_data->product_type,
351  $request_data->rang,
352  $request_data->special_code,
353  $fk_parent_line,
354  $request_data->fk_fournprice,
355  $request_data->pa_ht,
356  $request_data->label,
357  $request_data->array_options,
358  $request_data->fk_unit,
359  $this->element,
360  $request_data->id
361  );
362 
363  if ($updateRes > 0) {
364  return $updateRes;
365 
366  }
367  return false;
368  }
369  */
370 
371  // /**
372  // * Update a task of a given project
373  // *
374  // * @param int $id Id of project to update
375  // * @param int $taskid Id of task to update
376  // * @param array $request_data Projectline data
377  // *
378  // * @url PUT {id}/tasks/{taskid}
379  // *
380  // * @return object
381  // */
382  /*
383  public function putLine($id, $lineid, $request_data = null)
384  {
385  if(! DolibarrApiAccess::$user->hasRight('projet', 'creer')) {
386  throw new RestException(403);
387  }
388 
389  $result = $this->project->fetch($id);
390  if( ! $result ) {
391  throw new RestException(404, 'Project not found');
392  }
393 
394  if( ! DolibarrApi::_checkAccessToResource('project',$this->project->id)) {
395  throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
396  }
397 
398  $request_data = (object) $request_data;
399 
400  $request_data->desc = sanitizeVal($request_data->desc, 'restricthtml');
401 
402  $updateRes = $this->project->updateline(
403  $lineid,
404  $request_data->desc,
405  $request_data->subprice,
406  $request_data->qty,
407  $request_data->remise_percent,
408  $request_data->tva_tx,
409  $request_data->localtax1_tx,
410  $request_data->localtax2_tx,
411  'HT',
412  $request_data->info_bits,
413  $request_data->date_start,
414  $request_data->date_end,
415  $request_data->product_type,
416  $request_data->fk_parent_line,
417  0,
418  $request_data->fk_fournprice,
419  $request_data->pa_ht,
420  $request_data->label,
421  $request_data->special_code,
422  $request_data->array_options,
423  $request_data->fk_unit
424  );
425 
426  if ($updateRes > 0) {
427  $result = $this->get($id);
428  unset($result->line);
429  return $this->_cleanObjectDatas($result);
430  }
431  return false;
432  }*/
433 
434 
442  public function put($id, $request_data = null)
443  {
444  if (!DolibarrApiAccess::$user->hasRight('projet', 'creer')) {
445  throw new RestException(403);
446  }
447 
448  $result = $this->task->fetch($id);
449  if (!$result) {
450  throw new RestException(404, 'Task not found');
451  }
452 
453  if (!DolibarrApi::_checkAccessToResource('task', $this->task->id)) {
454  throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
455  }
456  foreach ($request_data as $field => $value) {
457  if ($field == 'id') {
458  continue;
459  }
460  if ($field === 'caller') {
461  // 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
462  $this->task->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
463  continue;
464  }
465  if ($field == 'array_options' && is_array($value)) {
466  foreach ($value as $index => $val) {
467  $this->task->array_options[$index] = $this->_checkValForAPI($field, $val, $this->task);;
468  }
469  continue;
470  }
471 
472  $this->task->$field = $this->_checkValForAPI($field, $value, $this->task);
473  }
474 
475  if ($this->task->update(DolibarrApiAccess::$user) > 0) {
476  return $this->get($id);
477  } else {
478  throw new RestException(500, $this->task->error);
479  }
480  }
481 
489  public function delete($id)
490  {
491  if (!DolibarrApiAccess::$user->hasRight('projet', 'supprimer')) {
492  throw new RestException(403);
493  }
494  $result = $this->task->fetch($id);
495  if (!$result) {
496  throw new RestException(404, 'Task not found');
497  }
498 
499  if (!DolibarrApi::_checkAccessToResource('task', $this->task->id)) {
500  throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
501  }
502 
503  if (!$this->task->delete(DolibarrApiAccess::$user)) {
504  throw new RestException(500, 'Error when delete task : '.$this->task->error);
505  }
506 
507  return array(
508  'success' => array(
509  'code' => 200,
510  'message' => 'Task deleted'
511  )
512  );
513  }
514 
515 
532  public function addTimeSpent($id, $date, $duration, $user_id = 0, $note = '')
533  {
534  if (!DolibarrApiAccess::$user->hasRight('projet', 'creer')) {
535  throw new RestException(403);
536  }
537  $result = $this->task->fetch($id);
538  if ($result <= 0) {
539  throw new RestException(404, 'Task not found');
540  }
541 
542  if (!DolibarrApi::_checkAccessToResource('project', $this->task->fk_project)) {
543  throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
544  }
545 
546  $uid = $user_id;
547  if (empty($uid)) {
548  $uid = DolibarrApiAccess::$user->id;
549  }
550 
551  $newdate = dol_stringtotime($date, 1);
552  $this->task->timespent_date = $newdate;
553  $this->task->timespent_datehour = $newdate;
554  $this->task->timespent_withhour = 1;
555  $this->task->timespent_duration = $duration;
556  $this->task->timespent_fk_user = $uid;
557  $this->task->timespent_note = $note;
558 
559  $result = $this->task->addTimeSpent(DolibarrApiAccess::$user, 0);
560  if ($result == 0) {
561  throw new RestException(304, 'Error nothing done. May be object is already validated');
562  }
563  if ($result < 0) {
564  throw new RestException(500, 'Error when adding time: '.$this->task->error);
565  }
566 
567  return array(
568  'success' => array(
569  'code' => 200,
570  'message' => 'Time spent added'
571  )
572  );
573  }
574 
591  public function putTimeSpent($id, $timespent_id, $date, $duration, $user_id = 0, $note = '')
592  {
593  if (!DolibarrApiAccess::$user->hasRight('projet', 'creer')) {
594  throw new RestException(403);
595  }
596  $this->timespentRecordChecks($id, $timespent_id);
597 
598  if (!DolibarrApi::_checkAccessToResource('task', $this->task->id)) {
599  throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
600  }
601 
602  $newdate = dol_stringtotime($date, 1);
603  $this->task->timespent_date = $newdate;
604  $this->task->timespent_datehour = $newdate;
605  $this->task->timespent_withhour = 1;
606  $this->task->timespent_duration = $duration;
607  $this->task->timespent_fk_user = $user_id ?? DolibarrApiAccess::$user->id;
608  $this->task->timespent_note = $note;
609 
610  $result = $this->task->updateTimeSpent(DolibarrApiAccess::$user, 0);
611  if ($result == 0) {
612  throw new RestException(304, 'Error nothing done.');
613  }
614  if ($result < 0) {
615  throw new RestException(500, 'Error when updating time spent: '.$this->task->error);
616  }
617 
618  return array(
619  'success' => array(
620  'code' => 200,
621  'message' => 'Time spent updated'
622  )
623  );
624  }
625 
636  public function deleteTimeSpent($id, $timespent_id)
637  {
638  if (!DolibarrApiAccess::$user->hasRight('projet', 'supprimer')) {
639  throw new RestException(403);
640  }
641  $this->timespentRecordChecks($id, $timespent_id);
642 
643  if (!DolibarrApi::_checkAccessToResource('task', $this->task->id)) {
644  throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
645  }
646 
647  if ($this->task->delTimeSpent(DolibarrApiAccess::$user, 0) < 0) {
648  throw new RestException(500, 'Error when deleting time spent: '.$this->task->error);
649  }
650 
651  return array(
652  'success' => array(
653  'code' => 200,
654  'message' => 'Time spent deleted'
655  )
656  );
657  }
658 
668  protected function timespentRecordChecks($id, $timespent_id)
669  {
670  if ($this->task->fetch($id) <= 0) {
671  throw new RestException(404, 'Task not found');
672  }
673  if ($this->task->fetchTimeSpent($timespent_id) <= 0) {
674  throw new RestException(404, 'Timespent not found');
675  } elseif ($this->task->id != $id) {
676  throw new RestException(404, 'Timespent not found in selected task');
677  }
678  }
679 
680  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
687  protected function _cleanObjectDatas($object)
688  {
689  // phpcs:enable
690  $object = parent::_cleanObjectDatas($object);
691 
692  unset($object->barcode_type);
693  unset($object->barcode_type_code);
694  unset($object->barcode_type_label);
695  unset($object->barcode_type_coder);
696  unset($object->cond_reglement_id);
697  unset($object->cond_reglement);
698  unset($object->fk_delivery_address);
699  unset($object->shipping_method_id);
700  unset($object->fk_account);
701  unset($object->note);
702  unset($object->fk_incoterms);
703  unset($object->label_incoterms);
704  unset($object->location_incoterms);
705  unset($object->name);
706  unset($object->lastname);
707  unset($object->firstname);
708  unset($object->civility_id);
709  unset($object->mode_reglement_id);
710  unset($object->country);
711  unset($object->country_id);
712  unset($object->country_code);
713 
714  unset($object->weekWorkLoad);
715  unset($object->weekWorkLoad);
716 
717  //unset($object->lines); // for task we use timespent_lines, but for project we use lines
718 
719  unset($object->total_ht);
720  unset($object->total_tva);
721  unset($object->total_localtax1);
722  unset($object->total_localtax2);
723  unset($object->total_ttc);
724 
725  unset($object->comments);
726 
727  return $object;
728  }
729 
737  private function _validate($data)
738  {
739  $object = array();
740  foreach (self::$FIELDS as $field) {
741  if (!isset($data[$field])) {
742  throw new RestException(400, "$field field missing");
743  }
744  $object[$field] = $data[$field];
745  }
746  return $object;
747  }
748 
749 
750  // \todo
751  // getSummaryOfTimeSpent
752 }
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
Class to manage tasks.
Definition: task.class.php:41
_cleanObjectDatas($object)
Clean sensible object datas.
putTimeSpent($id, $timespent_id, $date, $duration, $user_id=0, $note='')
Update time spent for a task of a project.
_validate($data)
Validate fields before create or update object.
post($request_data=null)
Create task object.
put($id, $request_data=null)
Update task general fields (won't touch time spent of task)
getRoles($id, $userid=0)
Get roles a user is assigned to a task with.
__construct()
Constructor.
addTimeSpent($id, $date, $duration, $user_id=0, $note='')
Add time spent to a task of a project.
timespentRecordChecks($id, $timespent_id)
Validate task & timespent IDs for timespent API methods.
index($sortfield="t.rowid", $sortorder='ASC', $limit=100, $page=0, $sqlfilters='', $properties='')
List tasks.
deleteTimeSpent($id, $timespent_id)
Delete time spent for a task of a project.
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:751
dol_stringtotime($string, $gm=1)
Convert a string date into a GM Timestamps date Warning: YYYY-MM-DDTHH:MM:SS+02:00 (RFC3339) is not s...
Definition: date.lib.php:426
forgeSQLFromUniversalSearchCriteria($filter, &$errorstr='', $noand=0, $nopar=0, $noerror=0)
forgeSQLFromUniversalSearchCriteria
sanitizeVal($out='', $check='alphanohtml', $filter=null, $options=null)
Return a sanitized or empty value after checking value against a rule.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.