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