dolibarr 23.0.3
api_shipments.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) 2025 MDW <mdeweerd@users.noreply.github.com>
5 * Copyright (C) 2025 Charlene Benke <charlene@patas-monkey.com>
6 * Copyright (C) 2025 Frédéric France <frederic.france@free.fr>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
22use Luracast\Restler\RestException;
23
24require_once DOL_DOCUMENT_ROOT.'/expedition/class/expedition.class.php';
25
33{
37 public static $FIELDS = array(
38 'socid',
39 'origin_id',
40 'origin_type',
41 );
42
46 public $shipment;
47
51 public function __construct()
52 {
53 global $db;
54 $this->db = $db;
55 $this->shipment = new Expedition($this->db);
56 }
57
68 public function get($id)
69 {
70 if (!DolibarrApiAccess::$user->hasRight('expedition', 'lire')) {
71 throw new RestException(403);
72 }
73
74 $result = $this->shipment->fetch($id);
75 if (!$result) {
76 throw new RestException(404, 'Shipment not found');
77 }
78
79 if (!DolibarrApi::_checkAccessToResource('expedition', $this->shipment->id)) {
80 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
81 }
82
83 $this->shipment->fetchObjectLinked();
84 return $this->_cleanObjectDatas($this->shipment);
85 }
86
87
88
108 public function index($sortfield = "t.rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $thirdparty_ids = '', $sqlfilters = '', $properties = '', $pagination_data = false)
109 {
110 if (!DolibarrApiAccess::$user->hasRight('expedition', 'lire')) {
111 throw new RestException(403);
112 }
113
114 global $hookmanager;
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 ?: $thirdparty_ids;
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."expedition AS t";
129 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."societe AS s ON (s.rowid = t.fk_soc)";
130 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."expedition_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 .= ' WHERE t.entity IN ('.getEntity('expedition').')';
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 = t.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 = t.fk_soc AND sc.fk_user = ".((int) $search_sale).")";
141 }
142 }
143 // Add sql filters
144 if ($sqlfilters) {
145 $parameters = array('sqlfilters' => $sqlfilters, 'apiroute' => 'shipments', 'apimethod' => 'index');
146 $object = new stdClass();
147 $action = 'list';
148 $reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
149 if ($reshook > 0) {
150 $sql = $hookmanager->resPrint;
151 } elseif ($reshook == 0) {
152 $sql .= $hookmanager->resPrint;
153 }
154 $errormessage = '';
155 $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
156 if ($errormessage) {
157 throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage);
158 }
159 }
160
161 //this query will return total shipments with the filters given
162 $sqlTotals = str_replace('SELECT t.rowid', 'SELECT count(t.rowid) as total', $sql);
163
164 $sql .= $this->db->order($sortfield, $sortorder);
165 if ($limit) {
166 if ($page < 0) {
167 $page = 0;
168 }
169 $offset = $limit * $page;
170
171 $sql .= $this->db->plimit($limit + 1, $offset);
172 }
173
174 dol_syslog("API Rest request");
175 $result = $this->db->query($sql);
176
177 if ($result) {
178 $num = $this->db->num_rows($result);
179 $min = min($num, ($limit <= 0 ? $num : $limit));
180 $i = 0;
181 while ($i < $min) {
182 $obj = $this->db->fetch_object($result);
183 $shipment_static = new Expedition($this->db);
184 if ($shipment_static->fetch($obj->rowid)) {
185 $obj_ret[] = $this->_filterObjectProperties($this->_cleanObjectDatas($shipment_static), $properties);
186 }
187 $i++;
188 }
189 } else {
190 throw new RestException(503, 'Error when retrieve commande list : '.$this->db->lasterror());
191 }
192
193 //if $pagination_data is true the response will contain element data with all values and element pagination with pagination data(total,page,limit)
194 if ($pagination_data) {
195 $totalsResult = $this->db->query($sqlTotals);
196 $total = $this->db->fetch_object($totalsResult)->total;
197
198 $tmp = $obj_ret;
199 $obj_ret = [];
200
201 $obj_ret['data'] = $tmp;
202 $obj_ret['pagination'] = [
203 'total' => (int) $total,
204 'page' => $page, //count starts from 0
205 'page_count' => ceil((int) $total / $limit),
206 'limit' => $limit
207 ];
208 }
209
210 return $obj_ret;
211 }
212
221 public function post($request_data = null)
222 {
223 if (!DolibarrApiAccess::$user->hasRight('expedition', 'creer')) {
224 throw new RestException(403, "Insufficiant rights");
225 }
226 // Check mandatory fields
227 $result = $this->_validate($request_data);
228
229 foreach ($request_data as $field => $value) {
230 if ($field === 'caller') {
231 // 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
232 $this->shipment->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
233 continue;
234 }
235
236 $this->shipment->$field = $this->_checkValForAPI($field, $value, $this->shipment);
237 }
238 if (isset($request_data["lines"])) {
239 $lines = array();
240 foreach ($request_data["lines"] as $line) {
241 $shipmentline = new ExpeditionLigne($this->db);
242
243 $shipmentline->entrepot_id = (int) $line['entrepot_id'];
244 $shipmentline->fk_element = (int) ($line['fk_element'] ?? $line['origin_id']); // example: order id. this->origin is 'commande'
245 $shipmentline->origin_line_id = (int) ($line['fk_elementdet'] ?? $line['origin_line_id']); // example: order id
246 $shipmentline->fk_elementdet = (int) ($line['fk_elementdet'] ?? $line['origin_line_id']); // example: order line id
247 $shipmentline->origin_type = $line['element_type'] ?? $line['origin_type']; // example 'commande' or 'order'
248 $shipmentline->element_type = $line['element_type'] ?? $line['origin_type']; // example 'commande' or 'order'
249 $shipmentline->qty = (float) $line['qty'];
250 $shipmentline->rang = (int) $line['rang'];
251 $array_options = $line['array_options'];
252 if (is_array($array_options)) {
253 $shipmentline->array_options = $array_options;
254 }
255 $detail_batch = $line['detail_batch'];
256 if (is_array($detail_batch) || is_object($detail_batch)) {
257 $shipmentline->detail_batch = $detail_batch;
258 }
259 $lines[] = $shipmentline;
260 }
261 $this->shipment->lines = $lines;
262 }
263
264 if ($this->shipment->create(DolibarrApiAccess::$user) < 0) {
265 throw new RestException(500, "Error creating shipment", array_merge(array($this->shipment->error), $this->shipment->errors));
266 }
267
268 return $this->shipment->id;
269 }
270
271 // /**
272 // * Get lines of an shipment
273 // *
274 // * @param int $id Id of shipment
275 // *
276 // * @url GET {id}/lines
277 // *
278 // * @return int
279 // */
280 /*
281 public function getLines($id)
282 {
283 if(! DolibarrApiAccess::$user->hasRight('expedition', 'lire')) {
284 throw new RestException(403);
285 }
286
287 $result = $this->shipment->fetch($id);
288 if( ! $result ) {
289 throw new RestException(404, 'Shipment not found');
290 }
291
292 if( ! DolibarrApi::_checkAccessToResource('expedition',$this->shipment->id)) {
293 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
294 }
295 $this->shipment->getLinesArray();
296 $result = array();
297 foreach ($this->shipment->lines as $line) {
298 array_push($result,$this->_cleanObjectDatas($line));
299 }
300 return $result;
301 }
302 */
303
304 // /**
305 // * Add a line to given shipment
306 // *
307 // * @param int $id Id of shipment to update
308 // * @param array $request_data ShipmentLine data
309 // * @phan-param ?array<string,string> $request_data
310 // * @phpstan-param ?array<string,string> $request_data
311 // *
312 // * @url POST {id}/lines
313 // *
314 // * @return int
315 // */
316 /*
317 public function postLine($id, $request_data = null)
318 {
319 if(! DolibarrApiAccess::$user->hasRight('expedition', 'creer')) {
320 throw new RestException(403);
321 }
322
323 $result = $this->shipment->fetch($id);
324 if ( ! $result ) {
325 throw new RestException(404, 'Shipment not found');
326 }
327
328 if( ! DolibarrApi::_checkAccessToResource('expedition',$this->shipment->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 $request_data->label = sanitizeVal($request_data->label);
336
337 $updateRes = $this->shipment->addline(
338 $request_data->desc,
339 $request_data->subprice,
340 $request_data->qty,
341 $request_data->tva_tx,
342 $request_data->localtax1_tx,
343 $request_data->localtax2_tx,
344 $request_data->fk_product,
345 $request_data->remise_percent,
346 $request_data->info_bits,
347 $request_data->fk_remise_except,
348 'HT',
349 0,
350 $request_data->date_start,
351 $request_data->date_end,
352 $request_data->product_type,
353 $request_data->rang,
354 $request_data->special_code,
355 $fk_parent_line,
356 $request_data->fk_fournprice,
357 $request_data->pa_ht,
358 $request_data->label,
359 $request_data->array_options,
360 $request_data->fk_unit,
361 $request_data->origin,
362 $request_data->origin_id,
363 $request_data->multicurrency_subprice
364 );
365
366 if ($updateRes > 0) {
367 return $updateRes;
368
369 }
370 return false;
371 }*/
372
373 // /**
374 // * Update a line to given shipment
375 // *
376 // * @param int $id Id of shipment to update
377 // * @param int $lineid Id of line to update
378 // * @param array $request_data ShipmentLine data
379 // *
380 // * @url PUT {id}/lines/{lineid}
381 // *
382 // * @return object
383 // */
384 /*
385 public function putLine($id, $lineid, $request_data = null)
386 {
387 if (! DolibarrApiAccess::$user->hasRight('expedition', 'creer')) {
388 throw new RestException(403);
389 }
390
391 $result = $this->shipment->fetch($id);
392 if ( ! $result ) {
393 throw new RestException(404, 'Shipment not found');
394 }
395
396 if( ! DolibarrApi::_checkAccessToResource('expedition',$this->shipment->id)) {
397 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
398 }
399
400 $request_data = (object) $request_data;
401
402 $request_data->desc = sanitizeVal($request_data->desc, 'restricthtml');
403 $request_data->label = sanitizeVal($request_data->label);
404
405 $updateRes = $this->shipment->updateline(
406 $lineid,
407 $request_data->desc,
408 $request_data->subprice,
409 $request_data->qty,
410 $request_data->remise_percent,
411 $request_data->tva_tx,
412 $request_data->localtax1_tx,
413 $request_data->localtax2_tx,
414 'HT',
415 $request_data->info_bits,
416 $request_data->date_start,
417 $request_data->date_end,
418 $request_data->product_type,
419 $request_data->fk_parent_line,
420 0,
421 $request_data->fk_fournprice,
422 $request_data->pa_ht,
423 $request_data->label,
424 $request_data->special_code,
425 $request_data->array_options,
426 $request_data->fk_unit,
427 $request_data->multicurrency_subprice
428 );
429
430 if ($updateRes > 0) {
431 $result = $this->get($id);
432 unset($result->line);
433 return $this->_cleanObjectDatas($result);
434 }
435 return false;
436 }*/
437
454 public function deleteLine($id, $lineid)
455 {
456 if (!DolibarrApiAccess::$user->hasRight('expedition', 'creer')) {
457 throw new RestException(403);
458 }
459
460 $result = $this->shipment->fetch($id);
461 if (!$result) {
462 throw new RestException(404, 'Shipment not found');
463 }
464
465 if (!DolibarrApi::_checkAccessToResource('expedition', $this->shipment->id)) {
466 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
467 }
468
469 // TODO Check the lineid $lineid is a line of object
470
471 $updateRes = $this->shipment->deleteLine(DolibarrApiAccess::$user, $lineid);
472 if ($updateRes > 0) {
473 return array(
474 'success' => array(
475 'code' => 200,
476 'message' => 'line ' .$lineid. ' deleted'
477 )
478 );
479 } else {
480 throw new RestException(405, $this->shipment->error);
481 }
482 }
483
493 public function put($id, $request_data = null)
494 {
495 if (!DolibarrApiAccess::$user->hasRight('expedition', 'creer')) {
496 throw new RestException(403);
497 }
498
499 $result = $this->shipment->fetch($id);
500 if (!$result) {
501 throw new RestException(404, 'Shipment not found');
502 }
503
504 if (!DolibarrApi::_checkAccessToResource('expedition', $this->shipment->id)) {
505 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
506 }
507 foreach ($request_data as $field => $value) {
508 if ($field == 'id') {
509 continue;
510 }
511 if ($field === 'caller') {
512 // 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
513 $this->shipment->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
514 continue;
515 }
516
517 if ($field == 'array_options' && is_array($value)) {
518 foreach ($value as $index => $val) {
519 $this->shipment->array_options[$index] = $this->_checkValForAPI($field, $val, $this->shipment);
520 }
521 continue;
522 }
523 $this->shipment->$field = $this->_checkValForAPI($field, $value, $this->shipment);
524 }
525
526 if ($this->shipment->update(DolibarrApiAccess::$user) > 0) {
527 return $this->get($id);
528 } else {
529 throw new RestException(500, $this->shipment->error);
530 }
531 }
532
542 public function delete($id)
543 {
544 if (!DolibarrApiAccess::$user->hasRight('expedition', 'supprimer')) {
545 throw new RestException(403);
546 }
547 $result = $this->shipment->fetch($id);
548 if (!$result) {
549 throw new RestException(404, 'Shipment not found');
550 }
551
552 if (!DolibarrApi::_checkAccessToResource('expedition', $this->shipment->id)) {
553 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
554 }
555
556 if (!$this->shipment->delete(DolibarrApiAccess::$user)) {
557 throw new RestException(500, 'Error when deleting shipment : '.$this->shipment->error);
558 }
559
560 return array(
561 'success' => array(
562 'code' => 200,
563 'message' => 'Shipment deleted'
564 )
565 );
566 }
567
587 public function validate($id, $notrigger = 0)
588 {
589 if (!DolibarrApiAccess::$user->hasRight('expedition', 'creer')) {
590 throw new RestException(403);
591 }
592 $result = $this->shipment->fetch($id);
593 if (!$result) {
594 throw new RestException(404, 'Shipment not found');
595 }
596
597 if (!DolibarrApi::_checkAccessToResource('expedition', $this->shipment->id)) {
598 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
599 }
600
601 $result = $this->shipment->valid(DolibarrApiAccess::$user, $notrigger);
602 if ($result == 0) {
603 throw new RestException(304, 'Error nothing done. May be object is already validated');
604 }
605 if ($result < 0) {
606 throw new RestException(500, 'Error when validating Shipment: '.$this->shipment->error);
607 }
608
609 // Reload shipment
610 $result = $this->shipment->fetch($id);
611
612 $this->shipment->fetchObjectLinked();
613 return $this->_cleanObjectDatas($this->shipment);
614 }
615
616
617 // /**
618 // * Classify the shipment as invoiced
619 // *
620 // * @param int $id Id of the shipment
621 // *
622 // * @url POST {id}/setinvoiced
623 // *
624 // * @return int
625 // *
626 // * @throws RestException 400
627 // * @throws RestException 401
628 // * @throws RestException 404
629 // * @throws RestException 405
630 // */
631 /*
632 public function setinvoiced($id)
633 {
634
635 if(! DolibarrApiAccess::$user->hasRight('expedition', 'creer')) {
636 throw new RestException(403);
637 }
638 if(empty($id)) {
639 throw new RestException(400, 'Shipment ID is mandatory');
640 }
641 $result = $this->shipment->fetch($id);
642 if( ! $result ) {
643 throw new RestException(404, 'Shipment not found');
644 }
645
646 $result = $this->shipment->classifyBilled(DolibarrApiAccess::$user);
647 if( $result < 0) {
648 throw new RestException(400, $this->shipment->error);
649 }
650 return $result;
651 }
652 */
653
654
655 // /**
656 // * Create a shipment using an existing order.
657 // *
658 // * @param int $orderid Id of the order
659 // *
660 // * @url POST /createfromorder/{orderid}
661 // *
662 // * @return int
663 // * @throws RestException 400
664 // * @throws RestException 401
665 // * @throws RestException 404
666 // * @throws RestException 405
667 // */
668 /*
669 public function createShipmentFromOrder($orderid)
670 {
671
672 require_once DOL_DOCUMENT_ROOT . '/commande/class/commande.class.php';
673
674 if(! DolibarrApiAccess::$user->hasRight('expedition', 'lire')) {
675 throw new RestException(403);
676 }
677 if(! DolibarrApiAccess::$user->hasRight('expedition', 'creer')) {
678 throw new RestException(403);
679 }
680 if(empty($proposalid)) {
681 throw new RestException(400, 'Order ID is mandatory');
682 }
683
684 $order = new Commande($this->db);
685 $result = $order->fetch($proposalid);
686 if( ! $result ) {
687 throw new RestException(404, 'Order not found');
688 }
689
690 $result = $this->shipment->createFromOrder($order, DolibarrApiAccess::$user);
691 if( $result < 0) {
692 throw new RestException(405, $this->shipment->error);
693 }
694 $this->shipment->fetchObjectLinked();
695 return $this->_cleanObjectDatas($this->shipment);
696 }
697 */
698
709 public function close($id, $notrigger = 0)
710 {
711 if (!DolibarrApiAccess::$user->hasRight('expedition', 'creer')) {
712 throw new RestException(403);
713 }
714
715 $result = $this->shipment->fetch($id);
716 if (!$result) {
717 throw new RestException(404, 'Shipment not found');
718 }
719
720 if (!DolibarrApi::_checkAccessToResource('expedition', $this->shipment->id)) {
721 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
722 }
723
724 $result = $this->shipment->setClosed();
725 if ($result == 0) {
726 throw new RestException(304, 'Error nothing done. May be object is already closed');
727 }
728 if ($result < 0) {
729 throw new RestException(500, 'Error when closing Order: '.$this->shipment->error);
730 }
731
732 // Reload shipment
733 $result = $this->shipment->fetch($id);
734
735 $this->shipment->fetchObjectLinked();
736
737 return $this->_cleanObjectDatas($this->shipment);
738 }
739
740 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
750 protected function _cleanObjectDatas($object)
751 {
752 // phpcs:enable
753 $object = parent::_cleanObjectDatas($object);
754
755 unset($object->canvas);
756
757 unset($object->thirdparty); // id already returned
758
759 unset($object->note);
760 unset($object->address);
761 unset($object->barcode_type);
762 unset($object->barcode_type_code);
763 unset($object->barcode_type_label);
764 unset($object->barcode_type_coder);
765
766 if (!empty($object->lines) && is_array($object->lines)) {
767 foreach ($object->lines as $line) {
768 if (is_array($line->detail_batch)) {
769 foreach ($line->detail_batch as $keytmp2 => $valtmp2) {
770 unset($line->detail_batch[$keytmp2]->db);
771 }
772 }
773 unset($line->canvas);
774
775 unset($line->tva_tx);
776 unset($line->vat_src_code);
777 unset($line->total_ht);
778 unset($line->total_ttc);
779 unset($line->total_tva);
780 unset($line->total_localtax1);
781 unset($line->total_localtax2);
782 unset($line->remise_percent);
783 }
784 }
785
786 return $object;
787 }
788
796 private function _validate($data)
797 {
798 if ($data === null) {
799 $data = array();
800 }
801 $shipment = array();
802 foreach (Shipments::$FIELDS as $field) {
803 if (!isset($data[$field])) {
804 throw new RestException(400, "$field field missing");
805 }
806 $shipment[$field] = $data[$field];
807 }
808 return $shipment;
809 }
810}
$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:33
_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:98
Class to manage lines of shipment.
close($id, $notrigger=0)
Classify the shipment as invoiced.
_validate($data)
Validate fields before create or update object.
put($id, $request_data=null)
Update shipment general fields (won't touch lines of shipment)
index($sortfield="t.rowid", $sortorder='ASC', $limit=100, $page=0, $thirdparty_ids='', $sqlfilters='', $properties='', $pagination_data=false)
List shipments.
validate($id, $notrigger=0)
Validate a shipment.
__construct()
Constructor.
post($request_data=null)
Create shipment object.
_cleanObjectDatas($object)
Clean sensible object datas @phpstan-template T.
deleteLine($id, $lineid)
Get lines of an shipment.
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.