dolibarr 24.0.1
api_holidays.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) 2020-2025 Frédéric France <frederic.france@free.fr>
5 * Copyright (C) 2025 MDW <mdeweerd@users.noreply.github.com>
6 * Copyright (C) 2025 William Mead <william@m34d.com>
7 * Copyright (C) 2025-2026 Charlene Benke <charlene@patas-monkey.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 */
22
23use Luracast\Restler\RestException;
24
25require_once DOL_DOCUMENT_ROOT.'/holiday/class/holiday.class.php';
26
27
36class Holidays extends DolibarrApi
37{
41 public static $FIELDS = array(
42 'fk_user',
43 'date_debut',
44 'date_fin',
45 );
46
53 public static $FIELDS_FORBIDDEN_FOR_API = array(
54 'status',
55 'statut',
56 'fk_validator',
57 'date_valid',
58 'fk_user_valid',
59 'date_approval',
60 'fk_user_approve',
61 'date_refuse',
62 'fk_user_refuse',
63 'detail_refuse',
64 );
65
69 public $holiday;
70
71
75 public function __construct()
76 {
77 global $db;
78
79 $this->db = $db;
80 $this->holiday = new Holiday($this->db);
81 }
82
95 public function get($id)
96 {
97 if (!DolibarrApiAccess::$user->hasRight('holiday', 'read')) {
98 throw new RestException(403);
99 }
100
101 $result = $this->holiday->fetch($id);
102 if (!$result) {
103 throw new RestException(404, 'Leave not found');
104 }
105
106 if (!DolibarrApi::_checkAccessToResource('holiday', $this->holiday)) {
107 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
108 }
109
110 $this->holiday->fetchObjectLinked();
111 return $this->_cleanObjectDatas($this->holiday);
112 }
113
133 public function index($sortfield = "t.rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $user_ids = '', $sqlfilters = '', $properties = '', $pagination_data = false)
134 {
135 if (!DolibarrApiAccess::$user->hasRight('holiday', 'read') && !DolibarrApiAccess::$user->hasRight('holiday', 'readall')) {
136 throw new RestException(403);
137 }
138
139 $obj_ret = array();
140
141 // case of external user, $societe param is ignored and replaced by user's socid
142 //$socid = DolibarrApiAccess::$user->socid ?: $societe;
143
144 $sql = "SELECT t.rowid";
145 $sql .= " FROM ".MAIN_DB_PREFIX."holiday AS t LEFT JOIN ".MAIN_DB_PREFIX."holiday_extrafields AS ef ON (ef.fk_object = t.rowid)"; // Link to extrafields is to allow to search parameters in the API GET call, so we will be able to filter on extrafields
146 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."user AS u ON t.fk_user = u.rowid";
147 $sql .= ' WHERE t.entity IN ('.getEntity('holiday').')';
148 if ($user_ids) {
149 $sql .= " AND t.fk_user IN (".$this->db->sanitize($user_ids).")";
150 }
151 if (!DolibarrApiAccess::$user->hasRight('holiday', 'readall')) {
152 $childids = DolibarrApiAccess::$user->getAllChildIds(1);
153 $sql .= " AND t.fk_user IN (".$this->db->sanitize(implode(',', $childids)).")";
154 }
155
156 // Add sql filters
157 if ($sqlfilters) {
158 $errormessage = '';
159 $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
160 if ($errormessage) {
161 throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage);
162 }
163 }
164
165 //this query will return total orders with the filters given
166 $sqlTotals = str_replace('SELECT t.rowid', 'SELECT count(t.rowid) as total', $sql);
167
168 $sql .= $this->db->order($sortfield, $sortorder);
169 if ($limit) {
170 if ($page < 0) {
171 $page = 0;
172 }
173 $offset = $limit * $page;
174
175 $sql .= $this->db->plimit($limit + 1, $offset);
176 }
177
178 $result = $this->db->query($sql);
179
180 if ($result) {
181 $num = $this->db->num_rows($result);
182 $min = min($num, ($limit <= 0 ? $num : $limit));
183 $i = 0;
184 while ($i < $min) {
185 $obj = $this->db->fetch_object($result);
186 $holiday_static = new Holiday($this->db);
187 if ($holiday_static->fetch($obj->rowid)) {
188 $obj_ret[] = $this->_filterObjectProperties($this->_cleanObjectDatas($holiday_static), $properties);
189 }
190 $i++;
191 }
192 } else {
193 throw new RestException(503, 'Error when retrieve Leave list : '.$this->db->lasterror());
194 }
195
196 //if $pagination_data is true the response will contain element data with all values and element pagination with pagination data(total,page,limit)
197 if ($pagination_data) {
198 $totalsResult = $this->db->query($sqlTotals);
199 $total = $this->db->fetch_object($totalsResult)->total;
200
201 $tmp = $obj_ret;
202 $obj_ret = [];
203
204 $obj_ret['data'] = $tmp;
205 $obj_ret['pagination'] = [
206 'total' => (int) $total,
207 'page' => $page, //count starts from 0
208 'page_count' => ceil((int) $total / $limit),
209 'limit' => $limit
210 ];
211 }
212
213 return $obj_ret;
214 }
215
228 public function post($request_data = null)
229 {
230 if (!DolibarrApiAccess::$user->hasRight('holiday', 'write')) {
231 throw new RestException(403, "Insufficiant rights");
232 }
233
234 // Check mandatory fields
235 $result = $this->_validate($request_data);
236
237 foreach ($request_data as $field => $value) {
238 if ($field === 'caller') {
239 // 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
240 $this->holiday->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
241 continue;
242 }
243 if (in_array($field, self::$FIELDS_FORBIDDEN_FOR_API) && $field !== 'fk_validator') {
244 throw new RestException(400, "Field '".$field."' is not allowed in create endpoint. Use dedicated routes (validate, approve, refuse, cancel, reopen) to change the workflow status.");
245 }
246
247 $this->holiday->$field = $this->_checkValForAPI($field, $value, $this->holiday);
248 }
249 /*if (isset($request_data["lines"])) {
250 $lines = array();
251 foreach ($request_data["lines"] as $line) {
252 array_push($lines, (object) $line);
253 }
254 $this->holiday->lines = $lines;
255 }*/
256 if ($this->holiday->create(DolibarrApiAccess::$user) < 0) {
257 throw new RestException(500, "Error creating holiday", array_merge(array($this->holiday->error), $this->holiday->errors));
258 }
259
260 return $this->holiday->id;
261 }
262
263
281 public function put($id, $request_data = null)
282 {
283 if (!DolibarrApiAccess::$user->hasRight('holiday', 'write')) {
284 throw new RestException(403);
285 }
286
287 $result = $this->holiday->fetch($id);
288 if (!$result) {
289 throw new RestException(404, 'Leave not found');
290 }
291
292 if (!DolibarrApi::_checkAccessToResource('holiday', $this->holiday)) {
293 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
294 }
295
296 if (!is_array($request_data)) {
297 $request_data = array();
298 }
299
300 foreach ($request_data as $field => $value) {
301 if ($field == 'id') {
302 continue;
303 }
304 if ($field === 'caller') {
305 // 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
306 $this->holiday->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
307 continue;
308 }
309 if (in_array($field, self::$FIELDS_FORBIDDEN_FOR_API)) {
310 throw new RestException(400, "Field '".$field."' is not allowed in update endpoint. Use dedicated routes (validate, approve, refuse, cancel, reopen) to change the workflow status.");
311 }
312
313 if ($field == 'array_options' && is_array($value)) {
314 foreach ($value as $index => $val) {
315 $this->holiday->array_options[$index] = $this->_checkValExtrafieldsForAPI($index, $val, $this->holiday);
316 }
317 continue;
318 }
319
320 $this->holiday->$field = $this->_checkValForAPI($field, $value, $this->holiday);
321 }
322
323 if ($this->holiday->update(DolibarrApiAccess::$user) > 0) {
324 return $this->get($id);
325 } else {
326 throw new RestException(500, $this->holiday->error);
327 }
328 }
329
342 public function delete($id)
343 {
344 if (!DolibarrApiAccess::$user->hasRight('holiday', 'delete')) {
345 throw new RestException(403);
346 }
347
348 $result = $this->holiday->fetch($id);
349 if (!$result) {
350 throw new RestException(404, 'Leave not found');
351 }
352
353 if (!DolibarrApi::_checkAccessToResource('holiday', $this->holiday)) {
354 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
355 }
356
357 if (!$this->holiday->delete(DolibarrApiAccess::$user)) {
358 throw new RestException(500, 'Error when deleting Leave : '.$this->holiday->error);
359 }
360
361 return array(
362 'success' => array(
363 'code' => 200,
364 'message' => 'Leave deleted'
365 )
366 );
367 }
368
388 public function validate($id, $notrigger = 0)
389 {
390 if (!DolibarrApiAccess::$user->hasRight('holiday', 'write')) {
391 throw new RestException(403, "Insufficiant rights");
392 }
393 $result = $this->holiday->fetch($id);
394 if (!$result) {
395 throw new RestException(404, 'Leave not found');
396 }
397
398 if (!DolibarrApi::_checkAccessToResource('holiday', $this->holiday)) {
399 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
400 }
401
402 $this->holiday->status = Holiday::STATUS_VALIDATED;
403 $result = $this->holiday->validate(DolibarrApiAccess::$user, $notrigger);
404 if ($result == 0) {
405 throw new RestException(304, 'Error nothing done. May be object is already validated');
406 }
407 if ($result < 0) {
408 throw new RestException(500, 'Error when validating leave: '.$this->holiday->error);
409 }
410
411 return $this->_cleanObjectDatas($this->holiday);
412 }
413
414
434 public function approve($id, $notrigger = 0)
435 {
436 if (!DolibarrApiAccess::$user->hasRight('holiday', 'approve')) {
437 throw new RestException(403, "Insufficiant rights");
438 }
439 $result = $this->holiday->fetch($id);
440 if (!$result) {
441 throw new RestException(404, 'Leave not found');
442 }
443
444 if (!DolibarrApi::_checkAccessToResource('holiday', $this->holiday)) {
445 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
446 }
447
448 $this->holiday->status = Holiday::STATUS_APPROVED;
449 $result = $this->holiday->approve(DolibarrApiAccess::$user, $notrigger);
450 if ($result == 0) {
451 throw new RestException(304, 'Error nothing done. May be object is already approved');
452 }
453 if ($result < 0) {
454 throw new RestException(500, 'Error when approving holiday: '.$this->holiday->error);
455 }
456
457 return $this->_cleanObjectDatas($this->holiday);
458 }
459
479 public function cancel($id, $notrigger = 0)
480 {
481 if (!DolibarrApiAccess::$user->hasRight('holiday', 'write')) {
482 throw new RestException(403, "Insufficient rights");
483 }
484
485 $result = $this->holiday->fetch($id);
486 if (!$result) {
487 throw new RestException(404, 'Leave not found');
488 }
489
490 if (!DolibarrApi::_checkAccessToResource('holiday', $this->holiday)) {
491 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
492 }
493
494 $this->holiday->status = Holiday::STATUS_CANCELED;
495 $result = $this->holiday->update(DolibarrApiAccess::$user, $notrigger);
496 if ($result == 0) {
497 throw new RestException(304, 'Error nothing done. May be object is already canceled');
498 }
499 if ($result < 0) {
500 throw new RestException(500, 'Error when canceling holiday: '.$this->holiday->error);
501 }
502
503 return $this->_cleanObjectDatas($this->holiday);
504 }
505
526 public function refuse($id, $detail_refuse, $notrigger = 0)
527 {
528 if (!DolibarrApiAccess::$user->hasRight('holiday', 'approve')) {
529 throw new RestException(403, "Insufficient rights");
530 }
531
532 $result = $this->holiday->fetch($id);
533 if (!$result) {
534 throw new RestException(404, 'Leave not found');
535 }
536
537 if (!DolibarrApi::_checkAccessToResource('holiday', $this->holiday)) {
538 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
539 }
540
541 $this->holiday->status = Holiday::STATUS_REFUSED;
542 $this->holiday->detail_refuse = $detail_refuse;
543 $result = $this->holiday->update(DolibarrApiAccess::$user, $notrigger);
544 if ($result == 0) {
545 throw new RestException(304, 'Error nothing done. May be object is already refused');
546 }
547 if ($result < 0) {
548 throw new RestException(500, 'Error when refusing holiday: '.$this->holiday->error);
549 }
550
551 return $this->_cleanObjectDatas($this->holiday);
552 }
553
576 public function reopen($id, $notrigger = 0)
577 {
578 if (!DolibarrApiAccess::$user->hasRight('holiday', 'write')) {
579 throw new RestException(403, "Insufficient rights");
580 }
581
582 $result = $this->holiday->fetch($id);
583 if (!$result) {
584 throw new RestException(404, 'Leave not found');
585 }
586
587 if (!DolibarrApi::_checkAccessToResource('holiday', $this->holiday)) {
588 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
589 }
590
591 // Check if the holiday is actually canceled
592 if ($this->holiday->statut != Holiday::STATUS_CANCELED) {
593 throw new RestException(400, 'Holiday is not canceled. Only canceled holidays can be reopened.');
594 }
595 $this->holiday->status = Holiday::STATUS_VALIDATED;
596 $result = $this->holiday->validate(DolibarrApiAccess::$user, $notrigger);
597 if ($result < 0) {
598 throw new RestException(500, 'Error when canceling holiday: '.$this->holiday->error);
599 }
600
601 return $this->_cleanObjectDatas($this->holiday);
602 }
603
604 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
614 protected function _cleanObjectDatas($object)
615 {
616 // phpcs:enable
617 $object = parent::_cleanObjectDatas($object);
621 unset($object->statut);
622 unset($object->user);
623 unset($object->thirdparty);
624
625 unset($object->cond_reglement);
626 unset($object->shipping_method_id);
627
628 unset($object->barcode_type);
629 unset($object->barcode_type_code);
630 unset($object->barcode_type_label);
631 unset($object->barcode_type_coder);
632
633 unset($object->mode_reglement_id);
634 unset($object->cond_reglement_id);
635
636 unset($object->name);
637 unset($object->lastname);
638 unset($object->firstname);
639 unset($object->civility_id);
640 unset($object->cond_reglement_id);
641 unset($object->contact);
642 unset($object->contact_id);
643
644 unset($object->state);
645 unset($object->state_id);
646 unset($object->state_code);
647 unset($object->country);
648 unset($object->country_id);
649 unset($object->country_code);
650
651 unset($object->logs);
652 unset($object->events);
653 unset($object->holiday);
654 unset($object->canvas);
655 unset($object->lines);
656
657 unset($object->totalpaid);
658 unset($object->totalpaid_multicurrency);
659
660 unset($object->note); // We already use note_public and note_pricate
661
662 return $object;
663 }
664
672 private function _validate($data)
673 {
674 if ($data === null) {
675 $data = array();
676 }
677 $holiday = array();
678 foreach (self::$FIELDS as $field) {
679 if (!isset($data[$field])) {
680 throw new RestException(400, "$field field missing");
681 }
682 $holiday[$field] = $data[$field];
683 }
684 return $holiday;
685 }
686}
$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.
_cleanObjectDatas($object)
Clean sensitive object data @phpstan-template T.
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 of the module paid holiday.
const STATUS_VALIDATED
Validated status.
const STATUS_REFUSED
Refused.
const STATUS_CANCELED
Canceled.
const STATUS_APPROVED
Approved.
_validate($data)
Validate fields before create or update object.
index($sortfield="t.rowid", $sortorder='ASC', $limit=100, $page=0, $user_ids='', $sqlfilters='', $properties='', $pagination_data=false)
List leaves.
put($id, $request_data=null)
Update holiday general fields.
validate($id, $notrigger=0)
Validate a holiday.
refuse($id, $detail_refuse, $notrigger=0)
Refuse a holiday.
__construct()
Constructor.
cancel($id, $notrigger=0)
Cancel a holiday.
reopen($id, $notrigger=0)
Reopen a canceled holiday.
approve($id, $notrigger=0)
Approve a leave.
post($request_data=null)
Create a leave.
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $db
API class for accounts.
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.