dolibarr 25.0.0-alpha
api_boms.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
3 * Copyright (C) 2019 Maxime Kohlhaas <maxime@atm-consulting.fr>
4 * Copyright (C) 2020-2025 Frédéric France <frederic.france@free.fr>
5 * Copyright (C) 2022 Christian Humpel <christian.humpel@live.com>
6 * Copyright (C) 2025 MDW <mdeweerd@users.noreply.github.com>
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.'/bom/class/bom.class.php';
25require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
26
27
40class Boms extends DolibarrApi
41{
45 public $bom;
46
50 public function __construct()
51 {
52 global $db;
53
54 $this->db = $db;
55 $this->bom = new BOM($this->db);
56 }
57
73 public function get($id)
74 {
75 if (!DolibarrApiAccess::$user->hasRight('bom', 'read')) {
76 throw new RestException(403);
77 }
78
79 $result = $this->bom->fetch($id);
80 if (!$result) {
81 throw new RestException(404, 'BOM not found');
82 }
83
84 if (!DolibarrApi::_checkAccessToResource('bom', $this->bom->id, 'bom_bom')) {
85 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
86 }
87
88 return $this->_cleanObjectDatas($this->bom);
89 }
90
91
111 public function index($sortfield = "t.rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $sqlfilters = '', $properties = '')
112 {
113 if (!DolibarrApiAccess::$user->hasRight('bom', 'read')) {
114 throw new RestException(403);
115 }
116
117 $obj_ret = array();
118 $tmpobject = new BOM($this->db);
119
120 $socid = DolibarrApiAccess::$user->socid ?: '';
121
122 $restrictonsocid = 0; // Set to 1 if there is a field socid in table of object
123
124 // If the internal user must only see his customers, force searching by him
125 $search_sale = 0;
126 if ($restrictonsocid && !DolibarrApiAccess::$user->hasRight('societe', 'client', 'voir') && !$socid) {
127 $search_sale = DolibarrApiAccess::$user->id;
128 }
129
130 $sql = "SELECT t.rowid";
131 $sql .= " FROM ".MAIN_DB_PREFIX.$tmpobject->table_element." AS t";
132 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$tmpobject->table_element."_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
133 $sql .= " WHERE 1 = 1";
134 if ($tmpobject->ismultientitymanaged) {
135 $sql .= ' AND t.entity IN ('.getEntity($tmpobject->element).')';
136 }
137 if ($restrictonsocid && $socid) {
138 $sql .= " AND t.fk_soc = ".((int) $socid);
139 }
140 // Search on sale representative
141 if ($search_sale && $search_sale != '-1') {
142 if ($search_sale == -2) {
143 $sql .= " AND ".getSalesRepresentativeSqlFilter('t.fk_soc', 0, 1);
144 } elseif ($search_sale > 0) {
145 $sql .= " AND ".getSalesRepresentativeSqlFilter('t.fk_soc', (int) $search_sale);
146 }
147 }
148 if ($sqlfilters) {
149 $errormessage = '';
150 $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
151 if ($errormessage) {
152 throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage);
153 }
154 }
155
156 $sql .= $this->db->order($sortfield, $sortorder);
157 if ($limit) {
158 if ($page < 0) {
159 $page = 0;
160 }
161 $offset = $limit * $page;
162
163 $sql .= $this->db->plimit($limit + 1, $offset);
164 }
165
166 $result = $this->db->query($sql);
167 if ($result) {
168 $i = 0;
169 $num = $this->db->num_rows($result);
170 $min = min($num, ($limit <= 0 ? $num : $limit));
171 while ($i < $min) {
172 $obj = $this->db->fetch_object($result);
173 $bom_static = new BOM($this->db);
174 if ($bom_static->fetch($obj->rowid)) {
175 $obj_ret[] = $this->_filterObjectProperties($this->_cleanObjectDatas($bom_static), $properties);
176 }
177 $i++;
178 }
179 } else {
180 throw new RestException(503, 'Error when retrieve bom list');
181 }
182
183 return $obj_ret;
184 }
185
197 public function post($request_data = null)
198 {
199 if (!DolibarrApiAccess::$user->hasRight('bom', 'write')) {
200 throw new RestException(403);
201 }
202 // Check mandatory fields
203 $result = $this->_validate($request_data);
204
205 foreach ($request_data as $field => $value) {
206 if ($field === 'caller') {
207 // 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
208 $this->bom->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
209 continue;
210 }
211
212 $this->bom->$field = $this->_checkValForAPI($field, $value, $this->bom);
213 }
214
215 $this->checkRefNumbering();
216
217 if (!$this->bom->create(DolibarrApiAccess::$user)) {
218 throw new RestException(500, "Error creating BOM", array_merge(array($this->bom->error), $this->bom->errors));
219 }
220 return $this->bom->id;
221 }
222
238 public function put($id, $request_data = null)
239 {
240 if (!DolibarrApiAccess::$user->hasRight('bom', 'write')) {
241 throw new RestException(403);
242 }
243
244 $result = $this->bom->fetch($id);
245 if (!$result) {
246 throw new RestException(404, 'BOM not found');
247 }
248
249 if (!DolibarrApi::_checkAccessToResource('bom', $this->bom->id, 'bom_bom')) {
250 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
251 }
252
253 foreach ($request_data as $field => $value) {
254 if ($field == 'id') {
255 continue;
256 }
257 if ($field === 'caller') {
258 // 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
259 $this->bom->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
260 continue;
261 }
262
263 if ($field == 'array_options' && is_array($value)) {
264 foreach ($value as $index => $val) {
265 $this->bom->array_options[$index] = $this->_checkValExtrafieldsForAPI($index, $val, $this->bom);
266 }
267 continue;
268 }
269 $this->bom->$field = $this->_checkValForAPI($field, $value, $this->bom);
270 }
271
272 $this->checkRefNumbering();
273
274 if ($this->bom->update(DolibarrApiAccess::$user) > 0) {
275 return $this->get($id);
276 } else {
277 throw new RestException(500, $this->bom->error);
278 }
279 }
280
295 public function validate($id, $notrigger = 0)
296 {
297 if (!DolibarrApiAccess::$user->hasRight('bom', 'write')) {
298 throw new RestException(403);
299 }
300 $result = $this->bom->fetch($id);
301 if (!$result) {
302 throw new RestException(404, 'Bom not found');
303 }
304
305 $result = $this->bom->validate(DolibarrApiAccess::$user, $notrigger);
306 if ($result == 0) {
307 throw new RestException(304, 'Error nothing done. May be object is already validated');
308 }
309 if ($result < 0) {
310 throw new RestException(500, 'Error when validating BOM: '.$this->bom->error);
311 }
312 $result = $this->bom->fetch($id);
313
314 return $this->_cleanObjectDatas($this->bom);
315 }
316
329 public function delete($id)
330 {
331 if (!DolibarrApiAccess::$user->hasRight('bom', 'delete')) {
332 throw new RestException(403);
333 }
334 $result = $this->bom->fetch($id);
335 if (!$result) {
336 throw new RestException(404, 'BOM not found');
337 }
338
339 if (!DolibarrApi::_checkAccessToResource('bom', $this->bom->id, 'bom_bom')) {
340 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
341 }
342
343 if (!$this->bom->delete(DolibarrApiAccess::$user)) {
344 throw new RestException(500, 'Error when deleting BOM : '.$this->bom->error);
345 }
346
347 return array(
348 'success' => array(
349 'code' => 200,
350 'message' => 'BOM deleted'
351 )
352 );
353 }
354
369 public function getLines($id)
370 {
371 if (!DolibarrApiAccess::$user->hasRight('bom', 'read')) {
372 throw new RestException(403);
373 }
374
375 $result = $this->bom->fetch($id);
376 if (!$result) {
377 throw new RestException(404, 'BOM not found');
378 }
379
380 if (!DolibarrApi::_checkAccessToResource('bom_bom', $this->bom->id)) {
381 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
382 }
383 $this->bom->getLinesArray();
384 $result = array();
385 foreach ($this->bom->lines as $line) {
386 array_push($result, $this->_cleanObjectDatas($line));
387 }
388 return $result;
389 }
390
407 public function postLine($id, $request_data = null)
408 {
409 if (!DolibarrApiAccess::$user->hasRight('bom', 'write')) {
410 throw new RestException(403);
411 }
412
413 $result = $this->bom->fetch($id);
414 if (!$result) {
415 throw new RestException(404, 'BOM not found');
416 }
417
418 if (!DolibarrApi::_checkAccessToResource('bom_bom', $this->bom->id)) {
419 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
420 }
421
422 $request_data = (object) $request_data;
423
424 $updateRes = $this->bom->addLine(
425 $request_data->fk_product,
426 $request_data->qty,
427 $request_data->qty_frozen,
428 $request_data->disable_stock_change,
429 $request_data->efficiency,
430 $request_data->position,
431 $request_data->fk_bom_child,
432 $request_data->import_key,
433 $request_data->fk_unit,
434 $request_data->array_options,
435 $request_data->fk_default_workstation
436 );
437
438 if ($updateRes > 0) {
439 return $updateRes;
440 } else {
441 throw new RestException(500, $this->bom->error);
442 }
443 }
444
461 public function putLine($id, $lineid, $request_data = null)
462 {
463 if (!DolibarrApiAccess::$user->hasRight('bom', 'write')) {
464 throw new RestException(403);
465 }
466
467 $result = $this->bom->fetch($id);
468 if (!$result) {
469 throw new RestException(404, 'BOM not found');
470 }
471
472 if (!DolibarrApi::_checkAccessToResource('bom_bom', $this->bom->id)) {
473 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
474 }
475
476 $request_data = (object) $request_data;
477
478 $updateRes = $this->bom->updateLine(
479 $lineid,
480 $request_data->qty,
481 $request_data->qty_frozen,
482 $request_data->disable_stock_change,
483 $request_data->efficiency,
484 $request_data->position,
485 $request_data->import_key,
486 $request_data->fk_unit,
487 $request_data->array_options,
488 $request_data->fk_default_workstation
489 );
490
491 if ($updateRes > 0) {
492 $result = $this->get($id);
493 unset($result->line);
494 return $this->_cleanObjectDatas($result);
495 }
496 return false;
497 }
498
516 public function deleteLine($id, $lineid)
517 {
518 if (!DolibarrApiAccess::$user->hasRight('bom', 'write')) {
519 throw new RestException(403);
520 }
521
522 $result = $this->bom->fetch($id);
523 if (!$result) {
524 throw new RestException(404, 'BOM not found');
525 }
526
527 if (!DolibarrApi::_checkAccessToResource('bom_bom', $this->bom->id)) {
528 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
529 }
530
531 //Check the rowid is a line of current bom object
532 $lineIdIsFromObject = false;
533 foreach ($this->bom->lines as $bl) {
534 if ($bl->id == $lineid) {
535 $lineIdIsFromObject = true;
536 break;
537 }
538 }
539 if (!$lineIdIsFromObject) {
540 throw new RestException(500, 'Line to delete (rowid: '.$lineid.') is not a line of BOM (id: '.$this->bom->id.')');
541 }
542
543 $updateRes = $this->bom->deleteLine(DolibarrApiAccess::$user, $lineid);
544 if ($updateRes > 0) {
545 return array(
546 'success' => array(
547 'code' => 200,
548 'message' => 'line ' .$lineid. ' deleted'
549 )
550 );
551 } else {
552 throw new RestException(500, $this->bom->error);
553 }
554 }
555
556 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
566 protected function _cleanObjectDatas($object)
567 {
568 // phpcs:enable
569 $object = parent::_cleanObjectDatas($object);
570
571 unset($object->rowid);
572 unset($object->canvas);
573
574 unset($object->name);
575 unset($object->lastname);
576 unset($object->firstname);
577 unset($object->civility_id);
578 unset($object->statut);
579 unset($object->state);
580 unset($object->region_id);
581 unset($object->state_id);
582 unset($object->state_code);
583 unset($object->region);
584 unset($object->region_code);
585 unset($object->country);
586 unset($object->country_id);
587 unset($object->country_code);
588 unset($object->barcode_type);
589 unset($object->barcode_type_code);
590 unset($object->barcode_type_label);
591 unset($object->barcode_type_coder);
592 unset($object->demand_reason_id);
593 unset($object->transport_mode_id);
594 unset($object->shipping_method);
595 unset($object->civility_code);
596 unset($object->actiontypecode);
597 unset($object->product);
598
599 unset($object->total_ht);
600 unset($object->total_tva);
601 unset($object->total_localtax1);
602 unset($object->total_localtax2);
603 unset($object->total_ttc);
604
605 unset($object->user);
606
607 unset($object->totalpaid);
608 unset($object->totalpaid_multicurrency);
609 unset($object->deposit_percent);
610 unset($object->cond_reglement_supplier_id);
611
612 unset($object->fk_account);
613 unset($object->comments);
614 unset($object->note);
615 unset($object->mode_reglement_id);
616 unset($object->cond_reglement_id);
617 unset($object->cond_reglement);
618 unset($object->shipping_method_id);
619 unset($object->fk_incoterms);
620 unset($object->label_incoterms);
621 unset($object->location_incoterms);
622 unset($object->multicurrency_code);
623 unset($object->multicurrency_tx);
624 unset($object->multicurrency_total_ht);
625 unset($object->multicurrency_total_ttc);
626 unset($object->multicurrency_total_tva);
627 unset($object->multicurrency_total_localtax1);
628 unset($object->multicurrency_total_localtax2);
629
630
631 // If object has lines, remove $db property
632 if (isset($object->lines) && is_array($object->lines) && count($object->lines) > 0) {
633 $nboflines = count($object->lines);
634 for ($i = 0; $i < $nboflines; $i++) {
635 $this->_cleanObjectDatas($object->lines[$i]);
636
637 unset($object->lines[$i]->lines);
638 unset($object->lines[$i]->note);
639 }
640 }
641
642 return $object;
643 }
644
653 private function _validate($data)
654 {
655 if ($data === null) {
656 $data = array();
657 }
658 $myobject = array();
659 foreach ($this->bom->fields as $field => $propfield) {
660 if (in_array($field, array('rowid', 'entity', 'date_creation', 'tms', 'fk_user_creat')) || empty($propfield['notnull']) || $propfield['notnull'] != 1) {
661 continue; // Not a mandatory field
662 }
663 if (!isset($data[$field])) {
664 throw new RestException(400, "$field field missing");
665 }
666 $myobject[$field] = $data[$field];
667 }
668 return $myobject;
669 }
670
676 private function checkRefNumbering()
677 {
678 $ref = substr($this->bom->ref, 1, 4);
679 if ($this->bom->status > BOM::STATUS_DRAFT && $ref == 'PROV') {
680 throw new RestException(400, "Wrong naming scheme '(PROV%)' is only allowed on 'DRAFT' status. For automatic increment use 'auto' on the 'ref' field.");
681 }
682
683 if (strtolower($this->bom->ref) == 'auto') {
684 if (empty($this->bom->id) && $this->bom->status == BOM::STATUS_DRAFT) {
685 $this->bom->ref = ''; // 'ref' will auto incremented with '(PROV' + newID + ')'
686 } else {
687 $res = $this->bom->fetch_product();
688 if ($res > 0 && $this->bom->product instanceof Product) {
689 $numref = $this->bom->getNextNumRef($this->bom->product); // @phan-suppress-current-line PhanTypeMismatchArgumentNullable
690 $this->bom->ref = $numref;
691 } else {
692 throw new RestException(400, "Error when generating automatic increment on the 'ref' field.");
693 }
694 }
695 }
696 }
697}
$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 BOM.
Definition bom.class.php:42
validate($id, $notrigger=0)
Validate BOM.
checkRefNumbering()
Validate the ref field and get the next Number if it's necessary.
put($id, $request_data=null)
Update bom.
_cleanObjectDatas($object)
Clean sensible object datas @phpstan-template T.
putLine($id, $lineid, $request_data=null)
Update a line to given BOM.
post($request_data=null)
Create bom object.
getLines($id)
Get lines of an BOM.
index($sortfield="t.rowid", $sortorder='ASC', $limit=100, $page=0, $sqlfilters='', $properties='')
List boms.
__construct()
Constructor.
deleteLine($id, $lineid)
Delete a line to given BOM.
postLine($id, $request_data=null)
Add a line to given BOM.
_validate($data)
Validate fields before create or update object.
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.
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 to manage products or services.
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.