dolibarr  16.0.1
api_products.class.php
1 <?php
2 /* Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
3  * Copyright (C) 2019 Cedric Ancelin <icedo.anc@gmail.com>
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.'/product/class/product.class.php';
22 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
23 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
24 require_once DOL_DOCUMENT_ROOT.'/variants/class/ProductAttribute.class.php';
25 require_once DOL_DOCUMENT_ROOT.'/variants/class/ProductAttributeValue.class.php';
26 require_once DOL_DOCUMENT_ROOT.'/variants/class/ProductCombination.class.php';
27 require_once DOL_DOCUMENT_ROOT.'/variants/class/ProductCombination2ValuePair.class.php';
28 
35 class Products extends DolibarrApi
36 {
40  public static $FIELDS = array(
41  'ref',
42  'label'
43  );
44 
48  public $product;
49 
53  public $productsupplier;
54 
58  public function __construct()
59  {
60  global $db, $conf;
61 
62  $this->db = $db;
63  $this->product = new Product($this->db);
64  $this->productsupplier = new ProductFournisseur($this->db);
65  }
66 
83  public function get($id, $includestockdata = 0, $includesubproducts = false, $includeparentid = false, $includetrans = false)
84  {
85  return $this->_fetch($id, '', '', '', $includestockdata, $includesubproducts, $includeparentid, false, $includetrans);
86  }
87 
107  public function getByRef($ref, $includestockdata = 0, $includesubproducts = false, $includeparentid = false, $includetrans = false)
108  {
109  return $this->_fetch('', $ref, '', '', $includestockdata, $includesubproducts, $includeparentid, false, $includetrans);
110  }
111 
131  public function getByRefExt($ref_ext, $includestockdata = 0, $includesubproducts = false, $includeparentid = false, $includetrans = false)
132  {
133  return $this->_fetch('', '', $ref_ext, '', $includestockdata, $includesubproducts, $includeparentid, false, $includetrans);
134  }
135 
155  public function getByBarcode($barcode, $includestockdata = 0, $includesubproducts = false, $includeparentid = false, $includetrans = false)
156  {
157  return $this->_fetch('', '', '', $barcode, $includestockdata, $includesubproducts, $includeparentid, false, $includetrans);
158  }
159 
178  public function index($sortfield = "t.ref", $sortorder = 'ASC', $limit = 100, $page = 0, $mode = 0, $category = 0, $sqlfilters = '', $ids_only = false, $variant_filter = 0, $pagination_data = false, $includestockdata = 0)
179  {
180  global $db, $conf;
181 
182  if (!DolibarrApiAccess::$user->rights->produit->lire) {
183  throw new RestException(403);
184  }
185 
186  $obj_ret = array();
187 
188  $socid = DolibarrApiAccess::$user->socid ? DolibarrApiAccess::$user->socid : '';
189 
190  $sql = "SELECT t.rowid, t.ref, t.ref_ext";
191  $sql .= " FROM ".$this->db->prefix()."product as t";
192  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_extrafields AS ef ON ef.fk_object = t.rowid"; // So we will be able to filter on extrafields
193  if ($category > 0) {
194  $sql .= ", ".$this->db->prefix()."categorie_product as c";
195  }
196  $sql .= ' WHERE t.entity IN ('.getEntity('product').')';
197 
198  if ($variant_filter == 1) {
199  $sql .= ' AND t.rowid not in (select distinct fk_product_parent from '.$this->db->prefix().'product_attribute_combination)';
200  $sql .= ' AND t.rowid not in (select distinct fk_product_child from '.$this->db->prefix().'product_attribute_combination)';
201  }
202  if ($variant_filter == 2) {
203  $sql .= ' AND t.rowid in (select distinct fk_product_parent from '.$this->db->prefix().'product_attribute_combination)';
204  }
205  if ($variant_filter == 3) {
206  $sql .= ' AND t.rowid in (select distinct fk_product_child from '.$this->db->prefix().'product_attribute_combination)';
207  }
208 
209  // Select products of given category
210  if ($category > 0) {
211  $sql .= " AND c.fk_categorie = ".((int) $category);
212  $sql .= " AND c.fk_product = t.rowid";
213  }
214  if ($mode == 1) {
215  // Show only products
216  $sql .= " AND t.fk_product_type = 0";
217  } elseif ($mode == 2) {
218  // Show only services
219  $sql .= " AND t.fk_product_type = 1";
220  }
221 
222  // Add sql filters
223  if ($sqlfilters) {
224  $errormessage = '';
225  if (!DolibarrApi::_checkFilters($sqlfilters, $errormessage)) {
226  throw new RestException(503, 'Error when validating parameter sqlfilters -> '.$errormessage);
227  }
228  //var_dump($sqlfilters);exit;
229  $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^\(\)]+)\)'; // We must accept datc:<:2020-01-01 10:10:10
230  $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")";
231  }
232 
233  //this query will return total products with the filters given
234  $sqlTotals = str_replace('SELECT t.rowid, t.ref, t.ref_ext', 'SELECT count(t.rowid) as total', $sql);
235 
236  $sql .= $this->db->order($sortfield, $sortorder);
237  if ($limit) {
238  if ($page < 0) {
239  $page = 0;
240  }
241  $offset = $limit * $page;
242 
243  $sql .= $this->db->plimit($limit + 1, $offset);
244  }
245 
246  $result = $this->db->query($sql);
247  if ($result) {
248  $num = $this->db->num_rows($result);
249  $min = min($num, ($limit <= 0 ? $num : $limit));
250  $i = 0;
251  while ($i < $min) {
252  $obj = $this->db->fetch_object($result);
253  if (!$ids_only) {
254  $product_static = new Product($this->db);
255  if ($product_static->fetch($obj->rowid)) {
256  if ($includestockdata && DolibarrApiAccess::$user->rights->stock->lire) {
257  $product_static->load_stock();
258 
259  if (is_array($product_static->stock_warehouse)) {
260  foreach ($product_static->stock_warehouse as $keytmp => $valtmp) {
261  if (isset($product_static->stock_warehouse[$keytmp]->detail_batch) && is_array($product_static->stock_warehouse[$keytmp]->detail_batch)) {
262  foreach ($product_static->stock_warehouse[$keytmp]->detail_batch as $keytmp2 => $valtmp2) {
263  unset($product_static->stock_warehouse[$keytmp]->detail_batch[$keytmp2]->db);
264  }
265  }
266  }
267  }
268  }
269 
270 
271  $obj_ret[] = $this->_cleanObjectDatas($product_static);
272  }
273  } else {
274  $obj_ret[] = $obj->rowid;
275  }
276  $i++;
277  }
278  } else {
279  throw new RestException(503, 'Error when retrieve product list : '.$this->db->lasterror());
280  }
281  if (!count($obj_ret)) {
282  throw new RestException(404, 'No product found');
283  }
284 
285  //if $pagination_data is true the response will contain element data with all values and element pagination with pagination data(total,page,limit)
286  if ($pagination_data) {
287  $totalsResult = $this->db->query($sqlTotals);
288  $total = $this->db->fetch_object($totalsResult)->total;
289 
290  $tmp = $obj_ret;
291  $obj_ret = array();
292 
293  $obj_ret['data'] = $tmp;
294  $obj_ret['pagination'] = array(
295  'total' => (int) $total,
296  'page' => $page, //count starts from 0
297  'page_count' => ceil((int) $total/$limit),
298  'limit' => $limit
299  );
300  }
301 
302  return $obj_ret;
303  }
304 
311  public function post($request_data = null)
312  {
313  if (!DolibarrApiAccess::$user->rights->produit->creer) {
314  throw new RestException(401);
315  }
316  // Check mandatory fields
317  $result = $this->_validate($request_data);
318 
319  foreach ($request_data as $field => $value) {
320  $this->product->$field = $value;
321  }
322  if ($this->product->create(DolibarrApiAccess::$user) < 0) {
323  throw new RestException(500, "Error creating product", array_merge(array($this->product->error), $this->product->errors));
324  }
325 
326  return $this->product->id;
327  }
328 
340  public function put($id, $request_data = null)
341  {
342  global $conf;
343 
344  if (!DolibarrApiAccess::$user->rights->produit->creer) {
345  throw new RestException(401);
346  }
347 
348  $result = $this->product->fetch($id);
349  if (!$result) {
350  throw new RestException(404, 'Product not found');
351  }
352 
353  if (!DolibarrApi::_checkAccessToResource('product', $this->product->id)) {
354  throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
355  }
356 
357  $oldproduct = dol_clone($this->product, 0);
358 
359  foreach ($request_data as $field => $value) {
360  if ($field == 'id') {
361  continue;
362  }
363  if ($field == 'stock_reel') {
364  throw new RestException(400, 'Stock reel cannot be updated here. Use the /stockmovements endpoint instead');
365  }
366  $this->product->$field = $value;
367  }
368 
369  $updatetype = false;
370  if ($this->product->type != $oldproduct->type && ($this->product->isProduct() || $this->product->isService())) {
371  $updatetype = true;
372  }
373 
374  $result = $this->product->update($id, DolibarrApiAccess::$user, 1, 'update', $updatetype);
375 
376  // If price mode is 1 price per product
377  if ($result > 0 && !empty($conf->global->PRODUCT_PRICE_UNIQ)) {
378  // We update price only if it was changed
379  $pricemodified = false;
380  if ($this->product->price_base_type != $oldproduct->price_base_type) {
381  $pricemodified = true;
382  } else {
383  if ($this->product->tva_tx != $oldproduct->tva_tx) {
384  $pricemodified = true;
385  }
386  if ($this->product->tva_npr != $oldproduct->tva_npr) {
387  $pricemodified = true;
388  }
389  if ($this->product->default_vat_code != $oldproduct->default_vat_code) {
390  $pricemodified = true;
391  }
392 
393  if ($this->product->price_base_type == 'TTC') {
394  if ($this->product->price_ttc != $oldproduct->price_ttc) {
395  $pricemodified = true;
396  }
397  if ($this->product->price_min_ttc != $oldproduct->price_min_ttc) {
398  $pricemodified = true;
399  }
400  } else {
401  if ($this->product->price != $oldproduct->price) {
402  $pricemodified = true;
403  }
404  if ($this->product->price_min != $oldproduct->price_min) {
405  $pricemodified = true;
406  }
407  }
408  }
409 
410  if ($pricemodified) {
411  $newvat = $this->product->tva_tx;
412  $newnpr = $this->product->tva_npr;
413  $newvatsrccode = $this->product->default_vat_code;
414 
415  $newprice = $this->product->price;
416  $newpricemin = $this->product->price_min;
417  if ($this->product->price_base_type == 'TTC') {
418  $newprice = $this->product->price_ttc;
419  $newpricemin = $this->product->price_min_ttc;
420  }
421 
422  $result = $this->product->updatePrice($newprice, $this->product->price_base_type, DolibarrApiAccess::$user, $newvat, $newpricemin, 0, $newnpr, 0, 0, array(), $newvatsrccode);
423  }
424  }
425 
426  if ($result <= 0) {
427  throw new RestException(500, "Error updating product", array_merge(array($this->product->error), $this->product->errors));
428  }
429 
430  return $this->get($id);
431  }
432 
439  public function delete($id)
440  {
441  if (!DolibarrApiAccess::$user->rights->produit->supprimer) {
442  throw new RestException(401);
443  }
444  $result = $this->product->fetch($id);
445  if (!$result) {
446  throw new RestException(404, 'Product not found');
447  }
448 
449  if (!DolibarrApi::_checkAccessToResource('product', $this->product->id)) {
450  throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
451  }
452 
453  // The Product::delete() method uses the global variable $user.
454  global $user;
455  $user = DolibarrApiAccess::$user;
456 
457  return $this->product->delete(DolibarrApiAccess::$user);
458  }
459 
472  public function getSubproducts($id)
473  {
474  if (!DolibarrApiAccess::$user->rights->produit->lire) {
475  throw new RestException(401);
476  }
477 
478  if (!DolibarrApi::_checkAccessToResource('product', $id)) {
479  throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
480  }
481 
482  $childsArbo = $this->product->getChildsArbo($id, 1);
483 
484  $keys = array('rowid', 'qty', 'fk_product_type', 'label', 'incdec', 'ref', 'fk_association', 'rang');
485  $childs = array();
486  foreach ($childsArbo as $values) {
487  $childs[] = array_combine($keys, $values);
488  }
489 
490  return $childs;
491  }
492 
510  public function addSubproducts($id, $subproduct_id, $qty, $incdec = 1)
511  {
512  if (!DolibarrApiAccess::$user->rights->produit->creer) {
513  throw new RestException(401);
514  }
515 
516  if (!DolibarrApi::_checkAccessToResource('product', $id)) {
517  throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
518  }
519 
520  $result = $this->product->add_sousproduit($id, $subproduct_id, $qty, $incdec);
521  if ($result <= 0) {
522  throw new RestException(500, "Error adding product child");
523  }
524  return $result;
525  }
526 
540  public function delSubproducts($id, $subproduct_id)
541  {
542  if (!DolibarrApiAccess::$user->rights->produit->creer) {
543  throw new RestException(401);
544  }
545 
546  if (!DolibarrApi::_checkAccessToResource('product', $id)) {
547  throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
548  }
549 
550  $result = $this->product->del_sousproduit($id, $subproduct_id);
551  if ($result <= 0) {
552  throw new RestException(500, "Error while removing product child");
553  }
554  return $result;
555  }
556 
557 
571  public function getCategories($id, $sortfield = "s.rowid", $sortorder = 'ASC', $limit = 0, $page = 0)
572  {
573  if (!DolibarrApiAccess::$user->rights->categorie->lire) {
574  throw new RestException(401);
575  }
576 
577  $categories = new Categorie($this->db);
578 
579  $result = $categories->getListForItem($id, 'product', $sortfield, $sortorder, $limit, $page);
580 
581  if (empty($result)) {
582  throw new RestException(404, 'No category found');
583  }
584 
585  if ($result < 0) {
586  throw new RestException(503, 'Error when retrieve category list : '.array_merge(array($categories->error), $categories->errors));
587  }
588 
589  return $result;
590  }
591 
601  public function getCustomerPricesPerSegment($id)
602  {
603  global $conf;
604 
605  if (!DolibarrApiAccess::$user->rights->produit->lire) {
606  throw new RestException(401);
607  }
608 
609  if (empty($conf->global->PRODUIT_MULTIPRICES)) {
610  throw new RestException(400, 'API not available: this mode of pricing is not enabled by setup');
611  }
612 
613  $result = $this->product->fetch($id);
614  if (!$result) {
615  throw new RestException(404, 'Product not found');
616  }
617 
618  if ($result < 0) {
619  throw new RestException(503, 'Error when retrieve prices list : '.array_merge(array($this->product->error), $this->product->errors));
620  }
621 
622  return array(
623  'multiprices'=>$this->product->multiprices,
624  'multiprices_inc_tax'=>$this->product->multiprices_ttc,
625  'multiprices_min'=>$this->product->multiprices_min,
626  'multiprices_min_inc_tax'=>$this->product->multiprices_min_ttc,
627  'multiprices_vat'=>$this->product->multiprices_tva_tx,
628  'multiprices_base_type'=>$this->product->multiprices_base_type,
629  //'multiprices_default_vat_code'=>$this->product->multiprices_default_vat_code
630  );
631  }
632 
643  public function getCustomerPricesPerCustomer($id, $thirdparty_id = '')
644  {
645  global $conf;
646 
647  if (!DolibarrApiAccess::$user->rights->produit->lire) {
648  throw new RestException(401);
649  }
650 
651  if (empty($conf->global->PRODUIT_CUSTOMER_PRICES)) {
652  throw new RestException(400, 'API not available: this mode of pricing is not enabled by setup');
653  }
654 
655  $socid = DolibarrApiAccess::$user->socid ? DolibarrApiAccess::$user->socid : '';
656  if ($socid > 0 && $socid != $thirdparty_id) {
657  throw new RestException(401, 'Getting prices for all customers or for the customer ID '.$thirdparty_id.' is not allowed for login '.DolibarrApiAccess::$user->login);
658  }
659 
660  $result = $this->product->fetch($id);
661  if (!$result) {
662  throw new RestException(404, 'Product not found');
663  }
664 
665  if ($result > 0) {
666  require_once DOL_DOCUMENT_ROOT.'/product/class/productcustomerprice.class.php';
667  $prodcustprice = new Productcustomerprice($this->db);
668  $filter = array();
669  $filter['t.fk_product'] .= $id;
670  if ($thirdparty_id) {
671  $filter['t.fk_soc'] .= $thirdparty_id;
672  }
673  $result = $prodcustprice->fetch_all('', '', 0, 0, $filter);
674  }
675 
676  if (empty($prodcustprice->lines)) {
677  throw new RestException(404, 'Prices not found');
678  }
679 
680  return $prodcustprice->lines;
681  }
682 
692  public function getCustomerPricesPerQuantity($id)
693  {
694  global $conf;
695 
696  if (!DolibarrApiAccess::$user->rights->produit->lire) {
697  throw new RestException(401);
698  }
699 
700  if (empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY)) {
701  throw new RestException(400, 'API not available: this mode of pricing is not enabled by setup');
702  }
703 
704  $result = $this->product->fetch($id);
705  if (!$result) {
706  throw new RestException(404, 'Product not found');
707  }
708 
709  if ($result < 0) {
710  throw new RestException(503, 'Error when retrieve prices list : '.array_merge(array($this->product->error), $this->product->errors));
711  }
712 
713  return array(
714  'prices_by_qty'=>$this->product->prices_by_qty[0], // 1 if price by quantity was activated for the product
715  'prices_by_qty_list'=>$this->product->prices_by_qty_list[0]
716  );
717  }
718 
752  public function addPurchasePrice($id, $qty, $buyprice, $price_base_type, $fourn_id, $availability, $ref_fourn, $tva_tx, $charges = 0, $remise_percent = 0, $remise = 0, $newnpr = 0, $delivery_time_days = 0, $supplier_reputation = '', $localtaxes_array = array(), $newdefaultvatcode = '', $multicurrency_buyprice = 0, $multicurrency_price_base_type = 'HT', $multicurrency_tx = 1, $multicurrency_code = '', $desc_fourn = '', $barcode = '', $fk_barcode_type = null)
753  {
754  if (!DolibarrApiAccess::$user->rights->produit->creer) {
755  throw new RestException(401);
756  }
757 
758  $result = $this->productsupplier->fetch($id);
759  if (!$result) {
760  throw new RestException(404, 'Product not found');
761  }
762 
763  if (!DolibarrApi::_checkAccessToResource('product', $this->productsupplier->id)) {
764  throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
765  }
766 
767  $socid = DolibarrApiAccess::$user->socid ? DolibarrApiAccess::$user->socid : '';
768  if ($socid > 0 && $socid != $fourn_id) {
769  throw new RestException(401, 'Adding purchase price for the supplier ID '.$fourn_id.' is not allowed for login '.DolibarrApiAccess::$user->login);
770  }
771 
772  $result = $this->productsupplier->add_fournisseur(DolibarrApiAccess::$user, $fourn_id, $ref_fourn, $qty);
773  if ($result < 0) {
774  throw new RestException(500, "Error adding supplier to product : ".$this->db->lasterror());
775  }
776 
777  $fourn = new Fournisseur($this->db);
778  $result = $fourn->fetch($fourn_id);
779  if ($result <= 0) {
780  throw new RestException(404, 'Supplier not found');
781  }
782 
783  // Clean data
784  $ref_fourn = sanitizeVal($ref_fourn, 'alphanohtml');
785  $desc_fourn = sanitizeVal($desc_fourn, 'restricthtml');
786  $barcode = sanitizeVal($barcode, 'alphanohtml');
787 
788  $result = $this->productsupplier->update_buyprice($qty, $buyprice, DolibarrApiAccess::$user, $price_base_type, $fourn, $availability, $ref_fourn, $tva_tx, $charges, $remise_percent, $remise, $newnpr, $delivery_time_days, $supplier_reputation, $localtaxes_array, $newdefaultvatcode, $multicurrency_buyprice, $multicurrency_price_base_type, $multicurrency_tx, $multicurrency_code, $desc_fourn, $barcode, $fk_barcode_type);
789 
790  if ($result <= 0) {
791  throw new RestException(500, "Error updating buy price : ".$this->db->lasterror());
792  }
793  return (int) $this->productsupplier->product_fourn_price_id;
794  }
795 
810  public function deletePurchasePrice($id, $priceid)
811  {
812  if (!DolibarrApiAccess::$user->rights->produit->supprimer) {
813  throw new RestException(401);
814  }
815  $result = $this->productsupplier->fetch($id);
816  if (!$result) {
817  throw new RestException(404, 'Product not found');
818  }
819 
820  if (!DolibarrApi::_checkAccessToResource('product', $this->productsupplier->id)) {
821  throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
822  }
823 
824  $resultsupplier = 0;
825  if ($result > 0) {
826  $resultsupplier = $this->productsupplier->remove_product_fournisseur_price($priceid);
827  }
828 
829  return $resultsupplier;
830  }
831 
847  public function getSupplierProducts($sortfield = "t.ref", $sortorder = 'ASC', $limit = 100, $page = 0, $mode = 0, $category = 0, $supplier = 0, $sqlfilters = '')
848  {
849  global $db, $conf;
850 
851  if (!DolibarrApiAccess::$user->rights->produit->lire) {
852  throw new RestException(401);
853  }
854 
855  $obj_ret = array();
856 
857  // Force id of company for external users
858  $socid = DolibarrApiAccess::$user->socid ? DolibarrApiAccess::$user->socid : '';
859  if ($socid > 0) {
860  if ($supplier != $socid || empty($supplier)) {
861  throw new RestException(401, 'As an external user, you can request only for your supplier id = '.$socid);
862  }
863  }
864 
865  $sql = "SELECT t.rowid, t.ref, t.ref_ext";
866  $sql .= " FROM ".$this->db->prefix()."product as t";
867  if ($category > 0) {
868  $sql .= ", ".$this->db->prefix()."categorie_product as c";
869  }
870  $sql .= ", ".$this->db->prefix()."product_fournisseur_price as s";
871 
872  $sql .= ' WHERE t.entity IN ('.getEntity('product').')';
873 
874  if ($supplier > 0) {
875  $sql .= " AND s.fk_soc = ".((int) $supplier);
876  }
877  if ($socid > 0) { // if external user
878  $sql .= " AND s.fk_soc = ".((int) $socid);
879  }
880  $sql .= " AND s.fk_product = t.rowid";
881  // Select products of given category
882  if ($category > 0) {
883  $sql .= " AND c.fk_categorie = ".((int) $category);
884  $sql .= " AND c.fk_product = t.rowid";
885  }
886  if ($mode == 1) {
887  // Show only products
888  $sql .= " AND t.fk_product_type = 0";
889  } elseif ($mode == 2) {
890  // Show only services
891  $sql .= " AND t.fk_product_type = 1";
892  }
893  // Add sql filters
894  if ($sqlfilters) {
895  $errormessage = '';
896  if (!DolibarrApi::_checkFilters($sqlfilters, $errormessage)) {
897  throw new RestException(503, 'Error when validating parameter sqlfilters -> '.$errormessage);
898  }
899  $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^\(\)]+)\)';
900  $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")";
901  }
902 
903  $sql .= $this->db->order($sortfield, $sortorder);
904  if ($limit) {
905  if ($page < 0) {
906  $page = 0;
907  }
908  $offset = $limit * $page;
909  $sql .= $this->db->plimit($limit + 1, $offset);
910  }
911  $result = $this->db->query($sql);
912  if ($result) {
913  $num = $this->db->num_rows($result);
914  $min = min($num, ($limit <= 0 ? $num : $limit));
915  $i = 0;
916  while ($i < $min) {
917  $obj = $this->db->fetch_object($result);
918 
919  $product_fourn = new ProductFournisseur($this->db);
920  $product_fourn_list = $product_fourn->list_product_fournisseur_price($obj->rowid, '', '', 0, 0);
921  foreach ($product_fourn_list as $tmpobj) {
922  $this->_cleanObjectDatas($tmpobj);
923  }
924 
925  //var_dump($product_fourn_list->db);exit;
926  $obj_ret[$obj->rowid] = $product_fourn_list;
927 
928  $i++;
929  }
930  } else {
931  throw new RestException(503, 'Error when retrieve product list : '.$this->db->lasterror());
932  }
933  if (!count($obj_ret)) {
934  throw new RestException(404, 'No product found');
935  }
936  return $obj_ret;
937  }
938 
958  public function getPurchasePrices($id, $ref = '', $ref_ext = '', $barcode = '')
959  {
960  if (empty($id) && empty($ref) && empty($ref_ext) && empty($barcode)) {
961  throw new RestException(400, 'bad value for parameter id, ref, ref_ext or barcode');
962  }
963 
964  $id = (empty($id) ? 0 : $id);
965 
966  if (!DolibarrApiAccess::$user->rights->produit->lire) {
967  throw new RestException(403);
968  }
969 
970  $socid = DolibarrApiAccess::$user->socid ? DolibarrApiAccess::$user->socid : '';
971 
972  $result = $this->product->fetch($id, $ref, $ref_ext, $barcode);
973  if (!$result) {
974  throw new RestException(404, 'Product not found');
975  }
976 
977  if (!DolibarrApi::_checkAccessToResource('product', $this->product->id)) {
978  throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
979  }
980 
981  $product_fourn_list = array();
982 
983  if ($result) {
984  $product_fourn = new ProductFournisseur($this->db);
985  $product_fourn_list = $product_fourn->list_product_fournisseur_price($this->product->id, '', '', 0, 0, ($socid > 0 ? $socid : 0));
986  }
987 
988  foreach ($product_fourn_list as $tmpobj) {
989  $this->_cleanObjectDatas($tmpobj);
990  }
991 
992  return $this->_cleanObjectDatas($product_fourn_list);
993  }
994 
1011  public function getAttributes($sortfield = "t.ref", $sortorder = 'ASC', $limit = 100, $page = 0, $sqlfilters = '')
1012  {
1013  if (!DolibarrApiAccess::$user->rights->produit->lire) {
1014  throw new RestException(401);
1015  }
1016 
1017  $sql = "SELECT t.rowid, t.ref, t.ref_ext, t.label, t.rang, t.entity";
1018  $sql .= " FROM ".$this->db->prefix()."product_attribute as t";
1019  $sql .= ' WHERE t.entity IN ('.getEntity('product').')';
1020 
1021  // Add sql filters
1022  if ($sqlfilters) {
1023  $errormessage = '';
1024  if (!DolibarrApi::_checkFilters($sqlfilters, $errormessage)) {
1025  throw new RestException(503, 'Error when validating parameter sqlfilters -> '.$errormessage);
1026  }
1027  $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^\(\)]+)\)';
1028  $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")";
1029  }
1030 
1031  $sql .= $this->db->order($sortfield, $sortorder);
1032  if ($limit) {
1033  if ($page < 0) {
1034  $page = 0;
1035  }
1036  $offset = $limit * $page;
1037 
1038  $sql .= $this->db->plimit($limit, $offset);
1039  }
1040 
1041  $result = $this->db->query($sql);
1042 
1043  if (!$result) {
1044  throw new RestException(503, 'Error when retrieve product attribute list : '.$this->db->lasterror());
1045  }
1046 
1047  $return = array();
1048  while ($result = $this->db->fetch_object($query)) {
1049  $tmp = new ProductAttribute($this->db);
1050  $tmp->id = $result->rowid;
1051  $tmp->ref = $result->ref;
1052  $tmp->ref_ext = $result->ref_ext;
1053  $tmp->label = $result->label;
1054  $tmp->rang = $result->rang;
1055  $tmp->entity = $result->entity;
1056 
1057  $return[] = $this->_cleanObjectDatas($tmp);
1058  }
1059 
1060  if (!count($return)) {
1061  throw new RestException(404, 'No product attribute found');
1062  }
1063 
1064  return $return;
1065  }
1066 
1078  public function getAttributeById($id)
1079  {
1080  if (!DolibarrApiAccess::$user->rights->produit->lire) {
1081  throw new RestException(401);
1082  }
1083 
1084  $prodattr = new ProductAttribute($this->db);
1085  $result = $prodattr->fetch((int) $id);
1086 
1087  if ($result < 0) {
1088  throw new RestException(404, "Product attribute not found");
1089  }
1090 
1091  $fields = ["id", "ref", "ref_ext", "label", "rang", "entity"];
1092 
1093  foreach ($prodattr as $field => $value) {
1094  if (!in_array($field, $fields)) {
1095  unset($prodattr->{$field});
1096  }
1097  }
1098 
1099  $sql = "SELECT COUNT(*) as nb FROM ".$this->db->prefix()."product_attribute_combination2val as pac2v";
1100  $sql .= " JOIN ".$this->db->prefix()."product_attribute_combination as pac ON pac2v.fk_prod_combination = pac.rowid";
1101  $sql .= " WHERE pac2v.fk_prod_attr = ".((int) $prodattr->id)." AND pac.entity IN (".getEntity('product').")";
1102 
1103  $resql = $this->db->query($sql);
1104  $obj = $this->db->fetch_object($resql);
1105  $prodattr->is_used_by_products = (int) $obj->nb;
1106 
1107  return $prodattr;
1108  }
1109 
1121  public function getAttributesByRef($ref)
1122  {
1123  if (!DolibarrApiAccess::$user->rights->produit->lire) {
1124  throw new RestException(401);
1125  }
1126 
1127  $ref = trim($ref);
1128 
1129  $sql = "SELECT rowid, ref, ref_ext, label, position, entity FROM ".$this->db->prefix()."product_attribute WHERE ref LIKE '".$this->db->escape($ref)."' AND entity IN (".getEntity('product').")";
1130 
1131  $query = $this->db->query($sql);
1132 
1133  if (!$this->db->num_rows($query)) {
1134  throw new RestException(404);
1135  }
1136 
1137  $result = $this->db->fetch_object($query);
1138 
1139  $attr = array();
1140  $attr['id'] = $result->rowid;
1141  $attr['ref'] = $result->ref;
1142  $attr['ref_ext'] = $result->ref_ext;
1143  $attr['label'] = $result->label;
1144  $attr['rang'] = $result->position;
1145  $attr['position'] = $result->position;
1146  $attr['entity'] = $result->entity;
1147 
1148  $sql = "SELECT COUNT(*) as nb FROM ".$this->db->prefix()."product_attribute_combination2val as pac2v";
1149  $sql .= " JOIN ".$this->db->prefix()."product_attribute_combination as pac ON pac2v.fk_prod_combination = pac.rowid";
1150  $sql .= " WHERE pac2v.fk_prod_attr = ".((int) $result->rowid)." AND pac.entity IN (".getEntity('product').")";
1151 
1152  $resql = $this->db->query($sql);
1153  $obj = $this->db->fetch_object($resql);
1154 
1155  $attr["is_used_by_products"] = (int) $obj->nb;
1156 
1157  return $attr;
1158  }
1159 
1171  public function getAttributesByRefExt($ref_ext)
1172  {
1173  if (!DolibarrApiAccess::$user->rights->produit->lire) {
1174  throw new RestException(401);
1175  }
1176 
1177  $ref_ext = trim($ref_ext);
1178 
1179  $sql = "SELECT rowid, ref, ref_ext, label, position, entity FROM ".$this->db->prefix()."product_attribute WHERE ref_ext LIKE '".$this->db->escape($ref_ext)."' AND entity IN (".getEntity('product').")";
1180 
1181  $query = $this->db->query($sql);
1182 
1183  if (!$this->db->num_rows($query)) {
1184  throw new RestException(404);
1185  }
1186 
1187  $result = $this->db->fetch_object($query);
1188 
1189  $attr = array();
1190  $attr['id'] = $result->rowid;
1191  $attr['ref'] = $result->ref;
1192  $attr['ref_ext'] = $result->ref_ext;
1193  $attr['label'] = $result->label;
1194  $attr['rang'] = $result->position;
1195  $attr['position'] = $result->position;
1196  $attr['entity'] = $result->entity;
1197 
1198  $sql = "SELECT COUNT(*) as nb FROM ".$this->db->prefix()."product_attribute_combination2val as pac2v";
1199  $sql .= " JOIN ".$this->db->prefix()."product_attribute_combination as pac ON pac2v.fk_prod_combination = pac.rowid";
1200  $sql .= " WHERE pac2v.fk_prod_attr = ".((int) $result->rowid)." AND pac.entity IN (".getEntity('product').")";
1201 
1202  $resql = $this->db->query($sql);
1203  $obj = $this->db->fetch_object($resql);
1204  $attr["is_used_by_products"] = (int) $obj->nb;
1205 
1206  return $attr;
1207  }
1208 
1222  public function addAttributes($ref, $label, $ref_ext = '')
1223  {
1224  if (!DolibarrApiAccess::$user->rights->produit->creer) {
1225  throw new RestException(401);
1226  }
1227 
1228  $prodattr = new ProductAttribute($this->db);
1229  $prodattr->label = $label;
1230  $prodattr->ref = $ref;
1231  $prodattr->ref_ext = $ref_ext;
1232 
1233  $resid = $prodattr->create(DolibarrApiAccess::$user);
1234  if ($resid <= 0) {
1235  throw new RestException(500, "Error creating new attribute");
1236  }
1237  return $resid;
1238  }
1239 
1253  public function putAttributes($id, $request_data = null)
1254  {
1255  if (!DolibarrApiAccess::$user->rights->produit->creer) {
1256  throw new RestException(401);
1257  }
1258 
1259  $prodattr = new ProductAttribute($this->db);
1260 
1261  $result = $prodattr->fetch((int) $id);
1262  if ($result == 0) {
1263  throw new RestException(404, 'Attribute not found');
1264  } elseif ($result < 0) {
1265  throw new RestException(500, "Error fetching attribute");
1266  }
1267 
1268  foreach ($request_data as $field => $value) {
1269  if ($field == 'rowid') {
1270  continue;
1271  }
1272  $prodattr->$field = $value;
1273  }
1274 
1275  if ($prodattr->update(DolibarrApiAccess::$user) > 0) {
1276  $result = $prodattr->fetch((int) $id);
1277  if ($result == 0) {
1278  throw new RestException(404, 'Attribute not found');
1279  } elseif ($result < 0) {
1280  throw new RestException(500, "Error fetching attribute");
1281  } else {
1282  return $prodattr;
1283  }
1284  }
1285  throw new RestException(500, "Error updating attribute");
1286  }
1287 
1299  public function deleteAttributes($id)
1300  {
1301  if (!DolibarrApiAccess::$user->rights->produit->supprimer) {
1302  throw new RestException(401);
1303  }
1304 
1305  $prodattr = new ProductAttribute($this->db);
1306  $prodattr->id = (int) $id;
1307  $result = $prodattr->delete(DolibarrApiAccess::$user);
1308 
1309  if ($result <= 0) {
1310  throw new RestException(500, "Error deleting attribute");
1311  }
1312 
1313  return $result;
1314  }
1315 
1327  public function getAttributeValueById($id)
1328  {
1329  if (!DolibarrApiAccess::$user->rights->produit->lire) {
1330  throw new RestException(401);
1331  }
1332 
1333  $sql = "SELECT rowid, fk_product_attribute, ref, value FROM ".$this->db->prefix()."product_attribute_value WHERE rowid = ".(int) $id." AND entity IN (".getEntity('product').")";
1334 
1335  $query = $this->db->query($sql);
1336 
1337  if (!$query) {
1338  throw new RestException(401);
1339  }
1340 
1341  if (!$this->db->num_rows($query)) {
1342  throw new RestException(404, 'Attribute value not found');
1343  }
1344 
1345  $result = $this->db->fetch_object($query);
1346 
1347  $attrval = array();
1348  $attrval['id'] = $result->rowid;
1349  $attrval['fk_product_attribute'] = $result->fk_product_attribute;
1350  $attrval['ref'] = $result->ref;
1351  $attrval['value'] = $result->value;
1352 
1353  return $attrval;
1354  }
1355 
1368  public function getAttributeValueByRef($id, $ref)
1369  {
1370  if (!DolibarrApiAccess::$user->rights->produit->lire) {
1371  throw new RestException(401);
1372  }
1373 
1374  $ref = trim($ref);
1375 
1376  $sql = "SELECT rowid, fk_product_attribute, ref, value FROM ".$this->db->prefix()."product_attribute_value";
1377  $sql .= " WHERE ref LIKE '".$this->db->escape($ref)."' AND fk_product_attribute = ".((int) $id)." AND entity IN (".getEntity('product').")";
1378 
1379  $query = $this->db->query($sql);
1380 
1381  if (!$query) {
1382  throw new RestException(401);
1383  }
1384 
1385  if (!$this->db->num_rows($query)) {
1386  throw new RestException(404, 'Attribute value not found');
1387  }
1388 
1389  $result = $this->db->fetch_object($query);
1390 
1391  $attrval = array();
1392  $attrval['id'] = $result->rowid;
1393  $attrval['fk_product_attribute'] = $result->fk_product_attribute;
1394  $attrval['ref'] = $result->ref;
1395  $attrval['value'] = $result->value;
1396 
1397  return $attrval;
1398  }
1399 
1411  public function deleteAttributeValueByRef($id, $ref)
1412  {
1413  if (!DolibarrApiAccess::$user->rights->produit->supprimer) {
1414  throw new RestException(401);
1415  }
1416 
1417  $ref = trim($ref);
1418 
1419  $sql = "SELECT rowid FROM ".$this->db->prefix()."product_attribute_value";
1420  $sql .= " WHERE ref LIKE '".$this->db->escape($ref)."' AND fk_product_attribute = ".((int) $id)." AND entity IN (".getEntity('product').")";
1421  $query = $this->db->query($sql);
1422 
1423  if (!$query) {
1424  throw new RestException(401);
1425  }
1426 
1427  if (!$this->db->num_rows($query)) {
1428  throw new RestException(404, 'Attribute value not found');
1429  }
1430 
1431  $result = $this->db->fetch_object($query);
1432 
1433  $attrval = new ProductAttributeValue($this->db);
1434  $attrval->id = $result->rowid;
1435  $result = $attrval->delete(DolibarrApiAccess::$user);
1436  if ($result > 0) {
1437  return 1;
1438  }
1439 
1440  throw new RestException(500, "Error deleting attribute value");
1441  }
1442 
1454  public function getAttributeValues($id)
1455  {
1456  if (!DolibarrApiAccess::$user->rights->produit->lire) {
1457  throw new RestException(401);
1458  }
1459 
1460  $objectval = new ProductAttributeValue($this->db);
1461 
1462  $return = $objectval->fetchAllByProductAttribute((int) $id);
1463 
1464  if (count($return) == 0) {
1465  throw new RestException(404, 'Attribute values not found');
1466  }
1467 
1468  foreach ($return as $key => $val) {
1469  $return[$key] = $this->_cleanObjectDatas($return[$key]);
1470  }
1471 
1472  return $return;
1473  }
1474 
1485  public function getAttributeValuesByRef($ref)
1486  {
1487  if (!DolibarrApiAccess::$user->rights->produit->lire) {
1488  throw new RestException(401);
1489  }
1490 
1491  $ref = trim($ref);
1492 
1493  $return = array();
1494 
1495  $sql = "SELECT ";
1496  $sql .= "v.fk_product_attribute, v.rowid, v.ref, v.value FROM ".$this->db->prefix()."product_attribute_value as v";
1497  $sql .= " WHERE v.fk_product_attribute IN (SELECT rowid FROM ".$this->db->prefix()."product_attribute WHERE ref LIKE '".$this->db->escape($ref)."')";
1498 
1499  $resql = $this->db->query($sql);
1500 
1501  while ($result = $this->db->fetch_object($resql)) {
1502  $tmp = new ProductAttributeValue($this->db);
1503  $tmp->fk_product_attribute = $result->fk_product_attribute;
1504  $tmp->id = $result->rowid;
1505  $tmp->ref = $result->ref;
1506  $tmp->value = $result->value;
1507 
1508  $return[] = $this->_cleanObjectDatas($tmp);
1509  }
1510 
1511  return $return;
1512  }
1513 
1527  public function addAttributeValue($id, $ref, $value)
1528  {
1529  if (!DolibarrApiAccess::$user->rights->produit->creer) {
1530  throw new RestException(401);
1531  }
1532 
1533  if (empty($ref) || empty($value)) {
1534  throw new RestException(401);
1535  }
1536 
1537  $objectval = new ProductAttributeValue($this->db);
1538  $objectval->fk_product_attribute = ((int) $id);
1539  $objectval->ref = $ref;
1540  $objectval->value = $value;
1541 
1542  if ($objectval->create(DolibarrApiAccess::$user) > 0) {
1543  return $objectval->id;
1544  }
1545  throw new RestException(500, "Error creating new attribute value");
1546  }
1547 
1560  public function putAttributeValue($id, $request_data)
1561  {
1562  if (!DolibarrApiAccess::$user->rights->produit->creer) {
1563  throw new RestException(401);
1564  }
1565 
1566  $objectval = new ProductAttributeValue($this->db);
1567  $result = $objectval->fetch((int) $id);
1568 
1569  if ($result == 0) {
1570  throw new RestException(404, 'Attribute value not found');
1571  } elseif ($result < 0) {
1572  throw new RestException(500, "Error fetching attribute value");
1573  }
1574 
1575  foreach ($request_data as $field => $value) {
1576  if ($field == 'rowid') {
1577  continue;
1578  }
1579  $objectval->$field = $value;
1580  }
1581 
1582  if ($objectval->update(DolibarrApiAccess::$user) > 0) {
1583  $result = $objectval->fetch((int) $id);
1584  if ($result == 0) {
1585  throw new RestException(404, 'Attribute not found');
1586  } elseif ($result < 0) {
1587  throw new RestException(500, "Error fetching attribute");
1588  } else {
1589  return $objectval;
1590  }
1591  }
1592  throw new RestException(500, "Error updating attribute");
1593  }
1594 
1606  public function deleteAttributeValueById($id)
1607  {
1608  if (!DolibarrApiAccess::$user->rights->produit->supprimer) {
1609  throw new RestException(401);
1610  }
1611 
1612  $objectval = new ProductAttributeValue($this->db);
1613  $objectval->id = (int) $id;
1614 
1615  if ($objectval->delete(DolibarrApiAccess::$user) > 0) {
1616  return 1;
1617  }
1618  throw new RestException(500, "Error deleting attribute value");
1619  }
1620 
1633  public function getVariants($id, $includestock = 0)
1634  {
1635  if (!DolibarrApiAccess::$user->rights->produit->lire) {
1636  throw new RestException(401);
1637  }
1638 
1639  $prodcomb = new ProductCombination($this->db);
1640  $combinations = $prodcomb->fetchAllByFkProductParent((int) $id);
1641 
1642  foreach ($combinations as $key => $combination) {
1643  $prodc2vp = new ProductCombination2ValuePair($this->db);
1644  $combinations[$key]->attributes = $prodc2vp->fetchByFkCombination((int) $combination->id);
1645  $combinations[$key] = $this->_cleanObjectDatas($combinations[$key]);
1646 
1647  if ($includestock==1 && DolibarrApiAccess::$user->rights->stock->lire) {
1648  $productModel = new Product($this->db);
1649  $productModel->fetch((int) $combination->fk_product_child);
1650  $productModel->load_stock();
1651  $combinations[$key]->stock_warehouse = $this->_cleanObjectDatas($productModel)->stock_warehouse;
1652  }
1653  }
1654 
1655  return $combinations;
1656  }
1657 
1669  public function getVariantsByProdRef($ref)
1670  {
1671  if (!DolibarrApiAccess::$user->rights->produit->lire) {
1672  throw new RestException(401);
1673  }
1674 
1675  $result = $this->product->fetch('', $ref);
1676  if (!$result) {
1677  throw new RestException(404, 'Product not found');
1678  }
1679 
1680  $prodcomb = new ProductCombination($this->db);
1681  $combinations = $prodcomb->fetchAllByFkProductParent((int) $this->product->id);
1682 
1683  foreach ($combinations as $key => $combination) {
1684  $prodc2vp = new ProductCombination2ValuePair($this->db);
1685  $combinations[$key]->attributes = $prodc2vp->fetchByFkCombination((int) $combination->id);
1686  $combinations[$key] = $this->_cleanObjectDatas($combinations[$key]);
1687  }
1688 
1689  return $combinations;
1690  }
1691 
1712  public function addVariant($id, $weight_impact, $price_impact, $price_impact_is_percent, $features, $reference = '', $ref_ext = '')
1713  {
1714  if (!DolibarrApiAccess::$user->rights->produit->creer) {
1715  throw new RestException(401);
1716  }
1717 
1718  if (empty($id) || empty($features) || !is_array($features)) {
1719  throw new RestException(401);
1720  }
1721 
1722  $weight_impact = price2num($weight_impact);
1723  $price_impact = price2num($price_impact);
1724 
1725  $prodattr = new ProductAttribute($this->db);
1726  $prodattr_val = new ProductAttributeValue($this->db);
1727  foreach ($features as $id_attr => $id_value) {
1728  if ($prodattr->fetch((int) $id_attr) < 0) {
1729  throw new RestException(401);
1730  }
1731  if ($prodattr_val->fetch((int) $id_value) < 0) {
1732  throw new RestException(401);
1733  }
1734  }
1735 
1736  $result = $this->product->fetch((int) $id);
1737  if (!$result) {
1738  throw new RestException(404, 'Product not found');
1739  }
1740 
1741  $prodcomb = new ProductCombination($this->db);
1742 
1743  $result = $prodcomb->createProductCombination(DolibarrApiAccess::$user, $this->product, $features, array(), $price_impact_is_percent, $price_impact, $weight_impact, $reference, $ref_ext);
1744  if ($result > 0) {
1745  return $result;
1746  } else {
1747  throw new RestException(500, "Error creating new product variant");
1748  }
1749  }
1750 
1769  public function addVariantByProductRef($ref, $weight_impact, $price_impact, $price_impact_is_percent, $features)
1770  {
1771  if (!DolibarrApiAccess::$user->rights->produit->creer) {
1772  throw new RestException(401);
1773  }
1774 
1775  if (empty($ref) || empty($features) || !is_array($features)) {
1776  throw new RestException(401);
1777  }
1778 
1779  $weight_impact = price2num($weight_impact);
1780  $price_impact = price2num($price_impact);
1781 
1782  $prodattr = new ProductAttribute($this->db);
1783  $prodattr_val = new ProductAttributeValue($this->db);
1784  foreach ($features as $id_attr => $id_value) {
1785  if ($prodattr->fetch((int) $id_attr) < 0) {
1786  throw new RestException(404);
1787  }
1788  if ($prodattr_val->fetch((int) $id_value) < 0) {
1789  throw new RestException(404);
1790  }
1791  }
1792 
1793  $result = $this->product->fetch('', trim($ref));
1794  if (!$result) {
1795  throw new RestException(404, 'Product not found');
1796  }
1797 
1798  $prodcomb = new ProductCombination($this->db);
1799  if (!$prodcomb->fetchByProductCombination2ValuePairs($this->product->id, $features)) {
1800  $result = $prodcomb->createProductCombination(DolibarrApiAccess::$user, $this->product, $features, array(), $price_impact_is_percent, $price_impact, $weight_impact);
1801  if ($result > 0) {
1802  return $result;
1803  } else {
1804  throw new RestException(500, "Error creating new product variant");
1805  }
1806  } else {
1807  return $prodcomb->id;
1808  }
1809  }
1810 
1823  public function putVariant($id, $request_data = null)
1824  {
1825  if (!DolibarrApiAccess::$user->rights->produit->creer) {
1826  throw new RestException(401);
1827  }
1828 
1829  $prodcomb = new ProductCombination($this->db);
1830  $prodcomb->fetch((int) $id);
1831 
1832  foreach ($request_data as $field => $value) {
1833  if ($field == 'rowid') {
1834  continue;
1835  }
1836  $prodcomb->$field = $value;
1837  }
1838 
1839  $result = $prodcomb->update(DolibarrApiAccess::$user);
1840  if ($result > 0) {
1841  return 1;
1842  }
1843  throw new RestException(500, "Error editing variant");
1844  }
1845 
1857  public function deleteVariant($id)
1858  {
1859  if (!DolibarrApiAccess::$user->rights->produit->supprimer) {
1860  throw new RestException(401);
1861  }
1862 
1863  $prodcomb = new ProductCombination($this->db);
1864  $prodcomb->id = (int) $id;
1865  $result = $prodcomb->delete(DolibarrApiAccess::$user);
1866  if ($result <= 0) {
1867  throw new RestException(500, "Error deleting variant");
1868  }
1869  return $result;
1870  }
1871 
1886  public function getStock($id, $selected_warehouse_id = null)
1887  {
1888 
1889  if (!DolibarrApiAccess::$user->rights->produit->lire || !DolibarrApiAccess::$user->rights->stock->lire) {
1890  throw new RestException(401);
1891  }
1892 
1893  if (!DolibarrApi::_checkAccessToResource('product', $id)) {
1894  throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1895  }
1896 
1897  $product_model = new Product($this->db);
1898  $product_model->fetch($id);
1899  $product_model->load_stock();
1900 
1901  $stockData = $this->_cleanObjectDatas($product_model)->stock_warehouse;
1902  if ($selected_warehouse_id) {
1903  foreach ($stockData as $warehouse_id => $warehouse) {
1904  if ($warehouse_id != $selected_warehouse_id) {
1905  unset($stockData[$warehouse_id]);
1906  }
1907  }
1908  }
1909 
1910  if (empty($stockData)) {
1911  throw new RestException(404, 'No stock found');
1912  }
1913 
1914  return ['stock_warehouses'=>$stockData];
1915  }
1916 
1917  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
1924  protected function _cleanObjectDatas($object)
1925  {
1926  // phpcs:enable
1927  $object = parent::_cleanObjectDatas($object);
1928 
1929  unset($object->statut);
1930 
1931  unset($object->regeximgext);
1932  unset($object->price_by_qty);
1933  unset($object->prices_by_qty_id);
1934  unset($object->libelle);
1935  unset($object->product_id_already_linked);
1936  unset($object->reputations);
1937  unset($object->db);
1938  unset($object->name);
1939  unset($object->firstname);
1940  unset($object->lastname);
1941  unset($object->civility_id);
1942  unset($object->contact);
1943  unset($object->contact_id);
1944  unset($object->thirdparty);
1945  unset($object->user);
1946  unset($object->origin);
1947  unset($object->origin_id);
1948  unset($object->fourn_pu);
1949  unset($object->fourn_price_base_type);
1950  unset($object->fourn_socid);
1951  unset($object->ref_fourn);
1952  unset($object->ref_supplier);
1953  unset($object->product_fourn_id);
1954  unset($object->fk_project);
1955 
1956  unset($object->mode_reglement_id);
1957  unset($object->cond_reglement_id);
1958  unset($object->demand_reason_id);
1959  unset($object->transport_mode_id);
1960  unset($object->cond_reglement);
1961  unset($object->shipping_method_id);
1962  unset($object->model_pdf);
1963  unset($object->note);
1964 
1965  unset($object->nbphoto);
1966  unset($object->recuperableonly);
1967  unset($object->multiprices_recuperableonly);
1968  unset($object->tva_npr);
1969  unset($object->lines);
1970  unset($object->fk_bank);
1971  unset($object->fk_account);
1972 
1973  unset($object->supplierprices); // Mut use another API to get them
1974 
1975  if (empty(DolibarrApiAccess::$user->rights->stock->lire)) {
1976  unset($object->stock_reel);
1977  unset($object->stock_theorique);
1978  unset($object->stock_warehouse);
1979  }
1980 
1981  return $object;
1982  }
1983 
1991  private function _validate($data)
1992  {
1993  $product = array();
1994  foreach (Products::$FIELDS as $field) {
1995  if (!isset($data[$field])) {
1996  throw new RestException(400, "$field field missing");
1997  }
1998  $product[$field] = $data[$field];
1999  }
2000  return $product;
2001  }
2002 
2022  private function _fetch($id, $ref = '', $ref_ext = '', $barcode = '', $includestockdata = 0, $includesubproducts = false, $includeparentid = false, $includeifobjectisused = false, $includetrans = false)
2023  {
2024  if (empty($id) && empty($ref) && empty($ref_ext) && empty($barcode)) {
2025  throw new RestException(400, 'bad value for parameter id, ref, ref_ext or barcode');
2026  }
2027 
2028  $id = (empty($id) ? 0 : $id);
2029 
2030  if (!DolibarrApiAccess::$user->rights->produit->lire) {
2031  throw new RestException(403);
2032  }
2033 
2034  $result = $this->product->fetch($id, $ref, $ref_ext, $barcode, 0, 0, ($includetrans ? 0 : 1));
2035  if (!$result) {
2036  throw new RestException(404, 'Product not found');
2037  }
2038 
2039  if (!DolibarrApi::_checkAccessToResource('product', $this->product->id)) {
2040  throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
2041  }
2042 
2043  if ($includestockdata && DolibarrApiAccess::$user->rights->stock->lire) {
2044  $this->product->load_stock();
2045 
2046  if (is_array($this->product->stock_warehouse)) {
2047  foreach ($this->product->stock_warehouse as $keytmp => $valtmp) {
2048  if (isset($this->product->stock_warehouse[$keytmp]->detail_batch) && is_array($this->product->stock_warehouse[$keytmp]->detail_batch)) {
2049  foreach ($this->product->stock_warehouse[$keytmp]->detail_batch as $keytmp2 => $valtmp2) {
2050  unset($this->product->stock_warehouse[$keytmp]->detail_batch[$keytmp2]->db);
2051  }
2052  }
2053  }
2054  }
2055  }
2056 
2057  if ($includesubproducts) {
2058  $childsArbo = $this->product->getChildsArbo($id, 1);
2059 
2060  $keys = array('rowid', 'qty', 'fk_product_type', 'label', 'incdec', 'ref', 'fk_association', 'rang');
2061  $childs = array();
2062  foreach ($childsArbo as $values) {
2063  $childs[] = array_combine($keys, $values);
2064  }
2065 
2066  $this->product->sousprods = $childs;
2067  }
2068 
2069  if ($includeparentid) {
2070  $prodcomb = new ProductCombination($this->db);
2071  $this->product->fk_product_parent = null;
2072  if (($fk_product_parent = $prodcomb->fetchByFkProductChild($this->product->id)) > 0) {
2073  $this->product->fk_product_parent = $fk_product_parent;
2074  }
2075  }
2076 
2077  if ($includeifobjectisused) {
2078  $this->product->is_object_used = ($this->product->isObjectUsed() > 0);
2079  }
2080 
2081  return $this->_cleanObjectDatas($this->product);
2082  }
2083 }
File of class to manage predefined price products or services by customer.
getAttributeById($id)
Get attribute by ID.
putVariant($id, $request_data=null)
Put product variants.
getAttributeValuesByRef($ref)
Get all values for an attribute ref.
deleteVariant($id)
Delete product variants.
getCategories($id, $sortfield="s.rowid", $sortorder= 'ASC', $limit=0, $page=0)
Get categories for a product.
getAttributesByRef($ref)
Get attributes by ref.
_cleanObjectDatas($object)
Clean sensible object datas.
getAttributes($sortfield="t.ref", $sortorder= 'ASC', $limit=100, $page=0, $sqlfilters= '')
Get attributes.
$conf db
API class for accounts.
Definition: inc.php:41
Class to manage products or services.
getByRef($ref, $includestockdata=0, $includesubproducts=false, $includeparentid=false, $includetrans=false)
Get properties of a product object by ref.
getPurchasePrices($id, $ref= '', $ref_ext= '', $barcode= '')
Get purchase prices for a product.
getCustomerPricesPerQuantity($id)
Get prices per quantity for a product.
putAttributeValue($id, $request_data)
Update attribute value.
dol_clone($object, $native=0)
Create a clone of instance of object (new instance with same value for properties) With native = 0: P...
getStock($id, $selected_warehouse_id=null)
Get stock data for the product id given.
_fetch($id, $ref= '', $ref_ext= '', $barcode= '', $includestockdata=0, $includesubproducts=false, $includeparentid=false, $includeifobjectisused=false, $includetrans=false)
Get properties of 1 product object.
Class ProductAttributeValue Used to represent a product attribute value.
__construct()
Constructor.
addVariant($id, $weight_impact, $price_impact, $price_impact_is_percent, $features, $reference= '', $ref_ext= '')
Add variant.
Class to manage suppliers.
Class ProductAttribute Used to represent a product attribute.
getAttributeValueById($id)
Get attribute value by id.
putAttributes($id, $request_data=null)
Update attributes by id.
_checkFilters($sqlfilters, &$error= '')
Return if a $sqlfilters parameter is valid.
Definition: api.class.php:310
deletePurchasePrice($id, $priceid)
Delete purchase price for a product.
Class for API REST v1.
Definition: api.class.php:30
put($id, $request_data=null)
Update product.
getAttributesByRefExt($ref_ext)
Get attributes by ref_ext.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
getByRefExt($ref_ext, $includestockdata=0, $includesubproducts=false, $includeparentid=false, $includetrans=false)
Get properties of a product object by ref_ext.
Class to manage categories.
price2num($amount, $rounding= '', $option=0)
Function that return a number with universal decimal format (decimal separator is &#39;...
getAttributeValueByRef($id, $ref)
Get attribute value by ref.
deleteAttributes($id)
Delete attributes by id.
getSubproducts($id)
Get the list of subproducts of the product.
addVariantByProductRef($ref, $weight_impact, $price_impact, $price_impact_is_percent, $features)
Add variant by product ref.
addPurchasePrice($id, $qty, $buyprice, $price_base_type, $fourn_id, $availability, $ref_fourn, $tva_tx, $charges=0, $remise_percent=0, $remise=0, $newnpr=0, $delivery_time_days=0, $supplier_reputation= '', $localtaxes_array=array(), $newdefaultvatcode= '', $multicurrency_buyprice=0, $multicurrency_price_base_type= 'HT', $multicurrency_tx=1, $multicurrency_code= '', $desc_fourn= '', $barcode= '', $fk_barcode_type=null)
Add/Update purchase prices for a product.
post($request_data=null)
Create product object.
Class ProductCombination Used to represent a product combination.
getCustomerPricesPerCustomer($id, $thirdparty_id= '')
Get prices per customer for a product.
if(isModEnabled('facture')&&!empty($user->rights->facture->lire)) if((isModEnabled('fournisseur')&&empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD)&&$user->rights->fournisseur->facture->lire)||(isModEnabled('supplier_invoice')&&$user->rights->supplier_invoice->lire)) if(isModEnabled('don')&&!empty($user->rights->don->lire)) if(isModEnabled('tax')&&!empty($user->rights->tax->charges->lire)) if(isModEnabled('facture')&&isModEnabled('commande')&&$user->rights->commande->lire &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) $resql
Social contributions to pay.
Definition: index.php:742
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:283
Class ProductCombination2ValuePair Used to represent the relation between a product combination...
addSubproducts($id, $subproduct_id, $qty, $incdec=1)
Add subproduct.
delSubproducts($id, $subproduct_id)
Remove subproduct.
_validate($data)
Validate fields before create or update object.
deleteAttributeValueByRef($id, $ref)
Delete attribute value by ref.
getCustomerPricesPerSegment($id)
Get prices per segment for a product.
getAttributeValues($id)
Get all values for an attribute id.
getVariantsByProdRef($ref)
Get product variants by Product ref.
addAttributeValue($id, $ref, $value)
Add attribute value.
getVariants($id, $includestock=0)
Get product variants.
deleteAttributeValueById($id)
Delete attribute value by id.
addAttributes($ref, $label, $ref_ext= '')
Add attributes.
getByBarcode($barcode, $includestockdata=0, $includesubproducts=false, $includeparentid=false, $includetrans=false)
Get properties of a product object by barcode.
sanitizeVal($out= '', $check= 'alphanohtml', $filter=null, $options=null)
Return a sanitized or empty value after checking value against a rule.
Class to manage predefined suppliers products.
index($sortfield="t.ref", $sortorder= 'ASC', $limit=100, $page=0, $mode=0, $category=0, $sqlfilters= '', $ids_only=false, $variant_filter=0, $pagination_data=false, $includestockdata=0)
List products.
getSupplierProducts($sortfield="t.ref", $sortorder= 'ASC', $limit=100, $page=0, $mode=0, $category=0, $supplier=0, $sqlfilters= '')
Get a list of all purchase prices of products.