dolibarr  x.y.z
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 (!empty($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);
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  $res = $this->product->delete(DolibarrApiAccess::$user);
458  if ($res < 0) {
459  throw new RestException(500, "Can't delete, error occurs");
460  } elseif ($res == 0) {
461  throw new RestException(409, "Can't delete, that product is probably used");
462  }
463 
464  return array(
465  'success' => array(
466  'code' => 200,
467  'message' => 'Object deleted'
468  )
469  );
470  }
471 
484  public function getSubproducts($id)
485  {
486  if (!DolibarrApiAccess::$user->rights->produit->lire) {
487  throw new RestException(401);
488  }
489 
490  if (!DolibarrApi::_checkAccessToResource('product', $id)) {
491  throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
492  }
493 
494  $childsArbo = $this->product->getChildsArbo($id, 1);
495 
496  $keys = array('rowid', 'qty', 'fk_product_type', 'label', 'incdec', 'ref', 'fk_association', 'rang');
497  $childs = array();
498  foreach ($childsArbo as $values) {
499  $childs[] = array_combine($keys, $values);
500  }
501 
502  return $childs;
503  }
504 
522  public function addSubproducts($id, $subproduct_id, $qty, $incdec = 1)
523  {
524  if (!DolibarrApiAccess::$user->rights->produit->creer) {
525  throw new RestException(401);
526  }
527 
528  if (!DolibarrApi::_checkAccessToResource('product', $id)) {
529  throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
530  }
531 
532  $result = $this->product->add_sousproduit($id, $subproduct_id, $qty, $incdec);
533  if ($result <= 0) {
534  throw new RestException(500, "Error adding product child");
535  }
536  return $result;
537  }
538 
552  public function delSubproducts($id, $subproduct_id)
553  {
554  if (!DolibarrApiAccess::$user->rights->produit->creer) {
555  throw new RestException(401);
556  }
557 
558  if (!DolibarrApi::_checkAccessToResource('product', $id)) {
559  throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
560  }
561 
562  $result = $this->product->del_sousproduit($id, $subproduct_id);
563  if ($result <= 0) {
564  throw new RestException(500, "Error while removing product child");
565  }
566  return $result;
567  }
568 
569 
583  public function getCategories($id, $sortfield = "s.rowid", $sortorder = 'ASC', $limit = 0, $page = 0)
584  {
585  if (!DolibarrApiAccess::$user->rights->categorie->lire) {
586  throw new RestException(401);
587  }
588 
589  $categories = new Categorie($this->db);
590 
591  $result = $categories->getListForItem($id, 'product', $sortfield, $sortorder, $limit, $page);
592 
593  if (empty($result)) {
594  throw new RestException(404, 'No category found');
595  }
596 
597  if ($result < 0) {
598  throw new RestException(503, 'Error when retrieve category list : '.join(',', array_merge(array($categories->error), $categories->errors)));
599  }
600 
601  return $result;
602  }
603 
613  public function getCustomerPricesPerSegment($id)
614  {
615  global $conf;
616 
617  if (!DolibarrApiAccess::$user->rights->produit->lire) {
618  throw new RestException(401);
619  }
620 
621  if (empty($conf->global->PRODUIT_MULTIPRICES)) {
622  throw new RestException(400, 'API not available: this mode of pricing is not enabled by setup');
623  }
624 
625  $result = $this->product->fetch($id);
626  if (!$result) {
627  throw new RestException(404, 'Product not found');
628  }
629 
630  if ($result < 0) {
631  throw new RestException(503, 'Error when retrieve prices list : '.join(',', array_merge(array($this->product->error), $this->product->errors)));
632  }
633 
634  return array(
635  'multiprices'=>$this->product->multiprices,
636  'multiprices_inc_tax'=>$this->product->multiprices_ttc,
637  'multiprices_min'=>$this->product->multiprices_min,
638  'multiprices_min_inc_tax'=>$this->product->multiprices_min_ttc,
639  'multiprices_vat'=>$this->product->multiprices_tva_tx,
640  'multiprices_base_type'=>$this->product->multiprices_base_type,
641  //'multiprices_default_vat_code'=>$this->product->multiprices_default_vat_code
642  );
643  }
644 
655  public function getCustomerPricesPerCustomer($id, $thirdparty_id = '')
656  {
657  global $conf;
658 
659  if (!DolibarrApiAccess::$user->rights->produit->lire) {
660  throw new RestException(401);
661  }
662 
663  if (empty($conf->global->PRODUIT_CUSTOMER_PRICES)) {
664  throw new RestException(400, 'API not available: this mode of pricing is not enabled by setup');
665  }
666 
667  $socid = DolibarrApiAccess::$user->socid ? DolibarrApiAccess::$user->socid : '';
668  if ($socid > 0 && $socid != $thirdparty_id) {
669  throw new RestException(401, 'Getting prices for all customers or for the customer ID '.$thirdparty_id.' is not allowed for login '.DolibarrApiAccess::$user->login);
670  }
671 
672  $result = $this->product->fetch($id);
673  if (!$result) {
674  throw new RestException(404, 'Product not found');
675  }
676 
677  if ($result > 0) {
678  require_once DOL_DOCUMENT_ROOT.'/product/class/productcustomerprice.class.php';
679  $prodcustprice = new Productcustomerprice($this->db);
680  $filter = array();
681  $filter['t.fk_product'] .= $id;
682  if ($thirdparty_id) {
683  $filter['t.fk_soc'] .= $thirdparty_id;
684  }
685  $result = $prodcustprice->fetchAll('', '', 0, 0, $filter);
686  }
687 
688  if (empty($prodcustprice->lines)) {
689  throw new RestException(404, 'Prices not found');
690  }
691 
692  return $prodcustprice->lines;
693  }
694 
704  public function getCustomerPricesPerQuantity($id)
705  {
706  global $conf;
707 
708  if (!DolibarrApiAccess::$user->rights->produit->lire) {
709  throw new RestException(401);
710  }
711 
712  if (empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY)) {
713  throw new RestException(400, 'API not available: this mode of pricing is not enabled by setup');
714  }
715 
716  $result = $this->product->fetch($id);
717  if (!$result) {
718  throw new RestException(404, 'Product not found');
719  }
720 
721  if ($result < 0) {
722  throw new RestException(503, 'Error when retrieve prices list : '.join(',', array_merge(array($this->product->error), $this->product->errors)));
723  }
724 
725  return array(
726  'prices_by_qty'=>$this->product->prices_by_qty[0], // 1 if price by quantity was activated for the product
727  'prices_by_qty_list'=>$this->product->prices_by_qty_list[0]
728  );
729  }
730 
764  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)
765  {
766  if (!DolibarrApiAccess::$user->rights->produit->creer) {
767  throw new RestException(401);
768  }
769 
770  $result = $this->productsupplier->fetch($id);
771  if (!$result) {
772  throw new RestException(404, 'Product not found');
773  }
774 
775  if (!DolibarrApi::_checkAccessToResource('product', $this->productsupplier->id)) {
776  throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
777  }
778 
779  $socid = DolibarrApiAccess::$user->socid ? DolibarrApiAccess::$user->socid : '';
780  if ($socid > 0 && $socid != $fourn_id) {
781  throw new RestException(401, 'Adding purchase price for the supplier ID '.$fourn_id.' is not allowed for login '.DolibarrApiAccess::$user->login);
782  }
783 
784  $result = $this->productsupplier->add_fournisseur(DolibarrApiAccess::$user, $fourn_id, $ref_fourn, $qty);
785  if ($result < 0) {
786  throw new RestException(500, "Error adding supplier to product : ".$this->db->lasterror());
787  }
788 
789  $fourn = new Fournisseur($this->db);
790  $result = $fourn->fetch($fourn_id);
791  if ($result <= 0) {
792  throw new RestException(404, 'Supplier not found');
793  }
794 
795  // Clean data
796  $ref_fourn = sanitizeVal($ref_fourn, 'alphanohtml');
797  $desc_fourn = sanitizeVal($desc_fourn, 'restricthtml');
798  $barcode = sanitizeVal($barcode, 'alphanohtml');
799 
800  $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);
801 
802  if ($result <= 0) {
803  throw new RestException(500, "Error updating buy price : ".$this->db->lasterror());
804  }
805  return (int) $this->productsupplier->product_fourn_price_id;
806  }
807 
822  public function deletePurchasePrice($id, $priceid)
823  {
824  if (!DolibarrApiAccess::$user->rights->produit->supprimer) {
825  throw new RestException(401);
826  }
827  $result = $this->productsupplier->fetch($id);
828  if (!$result) {
829  throw new RestException(404, 'Product not found');
830  }
831 
832  if (!DolibarrApi::_checkAccessToResource('product', $this->productsupplier->id)) {
833  throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
834  }
835 
836  $resultsupplier = 0;
837  if ($result > 0) {
838  $resultsupplier = $this->productsupplier->remove_product_fournisseur_price($priceid);
839  }
840 
841  return $resultsupplier;
842  }
843 
859  public function getSupplierProducts($sortfield = "t.ref", $sortorder = 'ASC', $limit = 100, $page = 0, $mode = 0, $category = 0, $supplier = 0, $sqlfilters = '')
860  {
861  global $db, $conf;
862 
863  if (!DolibarrApiAccess::$user->rights->produit->lire) {
864  throw new RestException(401);
865  }
866 
867  $obj_ret = array();
868 
869  // Force id of company for external users
870  $socid = DolibarrApiAccess::$user->socid ? DolibarrApiAccess::$user->socid : '';
871  if ($socid > 0) {
872  if ($supplier != $socid || empty($supplier)) {
873  throw new RestException(401, 'As an external user, you can request only for your supplier id = '.$socid);
874  }
875  }
876 
877  $sql = "SELECT t.rowid, t.ref, t.ref_ext";
878  $sql .= " FROM ".$this->db->prefix()."product as t";
879  if ($category > 0) {
880  $sql .= ", ".$this->db->prefix()."categorie_product as c";
881  }
882  $sql .= ", ".$this->db->prefix()."product_fournisseur_price as s";
883 
884  $sql .= ' WHERE t.entity IN ('.getEntity('product').')';
885 
886  if ($supplier > 0) {
887  $sql .= " AND s.fk_soc = ".((int) $supplier);
888  }
889  if ($socid > 0) { // if external user
890  $sql .= " AND s.fk_soc = ".((int) $socid);
891  }
892  $sql .= " AND s.fk_product = t.rowid";
893  // Select products of given category
894  if ($category > 0) {
895  $sql .= " AND c.fk_categorie = ".((int) $category);
896  $sql .= " AND c.fk_product = t.rowid";
897  }
898  if ($mode == 1) {
899  // Show only products
900  $sql .= " AND t.fk_product_type = 0";
901  } elseif ($mode == 2) {
902  // Show only services
903  $sql .= " AND t.fk_product_type = 1";
904  }
905  // Add sql filters
906  if ($sqlfilters) {
907  $errormessage = '';
908  if (!DolibarrApi::_checkFilters($sqlfilters, $errormessage)) {
909  throw new RestException(503, 'Error when validating parameter sqlfilters -> '.$errormessage);
910  }
911  $regexstring = '\‍(([^:\'\‍(\‍)]+:[^:\'\‍(\‍)]+:[^\‍(\‍)]+)\‍)';
912  $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")";
913  }
914 
915  $sql .= $this->db->order($sortfield, $sortorder);
916  if ($limit) {
917  if ($page < 0) {
918  $page = 0;
919  }
920  $offset = $limit * $page;
921  $sql .= $this->db->plimit($limit + 1, $offset);
922  }
923  $result = $this->db->query($sql);
924  if ($result) {
925  $num = $this->db->num_rows($result);
926  $min = min($num, ($limit <= 0 ? $num : $limit));
927  $i = 0;
928  while ($i < $min) {
929  $obj = $this->db->fetch_object($result);
930 
931  $product_fourn = new ProductFournisseur($this->db);
932  $product_fourn_list = $product_fourn->list_product_fournisseur_price($obj->rowid, '', '', 0, 0);
933  foreach ($product_fourn_list as $tmpobj) {
934  $this->_cleanObjectDatas($tmpobj);
935  }
936 
937  //var_dump($product_fourn_list->db);exit;
938  $obj_ret[$obj->rowid] = $product_fourn_list;
939 
940  $i++;
941  }
942  } else {
943  throw new RestException(503, 'Error when retrieve product list : '.$this->db->lasterror());
944  }
945  if (!count($obj_ret)) {
946  throw new RestException(404, 'No product found');
947  }
948  return $obj_ret;
949  }
950 
970  public function getPurchasePrices($id, $ref = '', $ref_ext = '', $barcode = '')
971  {
972  if (empty($id) && empty($ref) && empty($ref_ext) && empty($barcode)) {
973  throw new RestException(400, 'bad value for parameter id, ref, ref_ext or barcode');
974  }
975 
976  $id = (empty($id) ? 0 : $id);
977 
978  if (!DolibarrApiAccess::$user->rights->produit->lire) {
979  throw new RestException(403);
980  }
981 
982  $socid = DolibarrApiAccess::$user->socid ? DolibarrApiAccess::$user->socid : '';
983 
984  $result = $this->product->fetch($id, $ref, $ref_ext, $barcode);
985  if (!$result) {
986  throw new RestException(404, 'Product not found');
987  }
988 
989  if (!DolibarrApi::_checkAccessToResource('product', $this->product->id)) {
990  throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
991  }
992 
993  $product_fourn_list = array();
994 
995  if ($result) {
996  $product_fourn = new ProductFournisseur($this->db);
997  $product_fourn_list = $product_fourn->list_product_fournisseur_price($this->product->id, '', '', 0, 0, ($socid > 0 ? $socid : 0));
998  }
999 
1000  foreach ($product_fourn_list as $tmpobj) {
1001  $this->_cleanObjectDatas($tmpobj);
1002  }
1003 
1004  return $this->_cleanObjectDatas($product_fourn_list);
1005  }
1006 
1023  public function getAttributes($sortfield = "t.ref", $sortorder = 'ASC', $limit = 100, $page = 0, $sqlfilters = '')
1024  {
1025  if (!DolibarrApiAccess::$user->rights->produit->lire) {
1026  throw new RestException(401);
1027  }
1028 
1029  $sql = "SELECT t.rowid, t.ref, t.ref_ext, t.label, t.position, t.entity";
1030  $sql .= " FROM ".$this->db->prefix()."product_attribute as t";
1031  $sql .= ' WHERE t.entity IN ('.getEntity('product').')';
1032 
1033  // Add sql filters
1034  if ($sqlfilters) {
1035  $errormessage = '';
1036  if (!DolibarrApi::_checkFilters($sqlfilters, $errormessage)) {
1037  throw new RestException(503, 'Error when validating parameter sqlfilters -> '.$errormessage);
1038  }
1039  $regexstring = '\‍(([^:\'\‍(\‍)]+:[^:\'\‍(\‍)]+:[^\‍(\‍)]+)\‍)';
1040  $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")";
1041  }
1042 
1043  $sql .= $this->db->order($sortfield, $sortorder);
1044  if ($limit) {
1045  if ($page < 0) {
1046  $page = 0;
1047  }
1048  $offset = $limit * $page;
1049 
1050  $sql .= $this->db->plimit($limit, $offset);
1051  }
1052 
1053  $result = $this->db->query($sql);
1054 
1055  if (!$result) {
1056  throw new RestException(503, 'Error when retrieve product attribute list : '.$this->db->lasterror());
1057  }
1058 
1059  $return = array();
1060  while ($result = $this->db->fetch_object($query)) {
1061  $tmp = new ProductAttribute($this->db);
1062  $tmp->id = $result->rowid;
1063  $tmp->ref = $result->ref;
1064  $tmp->ref_ext = $result->ref_ext;
1065  $tmp->label = $result->label;
1066  $tmp->position = $result->position;
1067  $tmp->entity = $result->entity;
1068 
1069  $return[] = $this->_cleanObjectDatas($tmp);
1070  }
1071 
1072  if (!count($return)) {
1073  throw new RestException(404, 'No product attribute found');
1074  }
1075 
1076  return $return;
1077  }
1078 
1090  public function getAttributeById($id)
1091  {
1092  if (!DolibarrApiAccess::$user->rights->produit->lire) {
1093  throw new RestException(401);
1094  }
1095 
1096  $prodattr = new ProductAttribute($this->db);
1097  $result = $prodattr->fetch((int) $id);
1098 
1099  if ($result < 0) {
1100  throw new RestException(404, "Product attribute not found");
1101  }
1102 
1103  $fields = ["id", "ref", "ref_ext", "label", "position", "entity"];
1104 
1105  foreach ($prodattr as $field => $value) {
1106  if (!in_array($field, $fields)) {
1107  unset($prodattr->{$field});
1108  }
1109  }
1110 
1111  $sql = "SELECT COUNT(*) as nb FROM ".$this->db->prefix()."product_attribute_combination2val as pac2v";
1112  $sql .= " JOIN ".$this->db->prefix()."product_attribute_combination as pac ON pac2v.fk_prod_combination = pac.rowid";
1113  $sql .= " WHERE pac2v.fk_prod_attr = ".((int) $prodattr->id)." AND pac.entity IN (".getEntity('product').")";
1114 
1115  $resql = $this->db->query($sql);
1116  $obj = $this->db->fetch_object($resql);
1117  $prodattr->is_used_by_products = (int) $obj->nb;
1118 
1119  return $this->_cleanObjectDatas($prodattr);
1120  }
1121 
1133  public function getAttributesByRef($ref)
1134  {
1135  if (!DolibarrApiAccess::$user->rights->produit->lire) {
1136  throw new RestException(401);
1137  }
1138 
1139  $ref = trim($ref);
1140 
1141  $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').")";
1142 
1143  $query = $this->db->query($sql);
1144 
1145  if (!$this->db->num_rows($query)) {
1146  throw new RestException(404);
1147  }
1148 
1149  $result = $this->db->fetch_object($query);
1150 
1151  $attr = array();
1152  $attr['id'] = $result->rowid;
1153  $attr['ref'] = $result->ref;
1154  $attr['ref_ext'] = $result->ref_ext;
1155  $attr['label'] = $result->label;
1156  $attr['rang'] = $result->position;
1157  $attr['position'] = $result->position;
1158  $attr['entity'] = $result->entity;
1159 
1160  $sql = "SELECT COUNT(*) as nb FROM ".$this->db->prefix()."product_attribute_combination2val as pac2v";
1161  $sql .= " JOIN ".$this->db->prefix()."product_attribute_combination as pac ON pac2v.fk_prod_combination = pac.rowid";
1162  $sql .= " WHERE pac2v.fk_prod_attr = ".((int) $result->rowid)." AND pac.entity IN (".getEntity('product').")";
1163 
1164  $resql = $this->db->query($sql);
1165  $obj = $this->db->fetch_object($resql);
1166 
1167  $attr["is_used_by_products"] = (int) $obj->nb;
1168 
1169  return $attr;
1170  }
1171 
1183  public function getAttributesByRefExt($ref_ext)
1184  {
1185  if (!DolibarrApiAccess::$user->rights->produit->lire) {
1186  throw new RestException(401);
1187  }
1188 
1189  $ref_ext = trim($ref_ext);
1190 
1191  $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').")";
1192 
1193  $query = $this->db->query($sql);
1194 
1195  if (!$this->db->num_rows($query)) {
1196  throw new RestException(404);
1197  }
1198 
1199  $result = $this->db->fetch_object($query);
1200 
1201  $attr = array();
1202  $attr['id'] = $result->rowid;
1203  $attr['ref'] = $result->ref;
1204  $attr['ref_ext'] = $result->ref_ext;
1205  $attr['label'] = $result->label;
1206  $attr['rang'] = $result->position;
1207  $attr['position'] = $result->position;
1208  $attr['entity'] = $result->entity;
1209 
1210  $sql = "SELECT COUNT(*) as nb FROM ".$this->db->prefix()."product_attribute_combination2val as pac2v";
1211  $sql .= " JOIN ".$this->db->prefix()."product_attribute_combination as pac ON pac2v.fk_prod_combination = pac.rowid";
1212  $sql .= " WHERE pac2v.fk_prod_attr = ".((int) $result->rowid)." AND pac.entity IN (".getEntity('product').")";
1213 
1214  $resql = $this->db->query($sql);
1215  $obj = $this->db->fetch_object($resql);
1216 
1217  $attr["is_used_by_products"] = (int) $obj->nb;
1218 
1219  return $attr;
1220  }
1221 
1235  public function addAttributes($ref, $label, $ref_ext = '')
1236  {
1237  if (!DolibarrApiAccess::$user->rights->produit->creer) {
1238  throw new RestException(401);
1239  }
1240 
1241  $prodattr = new ProductAttribute($this->db);
1242  $prodattr->label = $label;
1243  $prodattr->ref = $ref;
1244  $prodattr->ref_ext = $ref_ext;
1245 
1246  $resid = $prodattr->create(DolibarrApiAccess::$user);
1247  if ($resid <= 0) {
1248  throw new RestException(500, "Error creating new attribute");
1249  }
1250 
1251  return $resid;
1252  }
1253 
1267  public function putAttributes($id, $request_data = null)
1268  {
1269  if (!DolibarrApiAccess::$user->rights->produit->creer) {
1270  throw new RestException(401);
1271  }
1272 
1273  $prodattr = new ProductAttribute($this->db);
1274 
1275  $result = $prodattr->fetch((int) $id);
1276  if ($result == 0) {
1277  throw new RestException(404, 'Attribute not found');
1278  } elseif ($result < 0) {
1279  throw new RestException(500, "Error fetching attribute");
1280  }
1281 
1282  foreach ($request_data as $field => $value) {
1283  if ($field == 'rowid') {
1284  continue;
1285  }
1286  $prodattr->$field = $value;
1287  }
1288 
1289  if ($prodattr->update(DolibarrApiAccess::$user) > 0) {
1290  $result = $prodattr->fetch((int) $id);
1291  if ($result == 0) {
1292  throw new RestException(404, 'Attribute not found');
1293  } elseif ($result < 0) {
1294  throw new RestException(500, "Error fetching attribute");
1295  } else {
1296  return $this->_cleanObjectDatas($prodattr);
1297  }
1298  }
1299  throw new RestException(500, "Error updating attribute");
1300  }
1301 
1313  public function deleteAttributes($id)
1314  {
1315  if (!DolibarrApiAccess::$user->rights->produit->supprimer) {
1316  throw new RestException(401);
1317  }
1318 
1319  $prodattr = new ProductAttribute($this->db);
1320  $prodattr->id = (int) $id;
1321  $result = $prodattr->delete(DolibarrApiAccess::$user);
1322 
1323  if ($result <= 0) {
1324  throw new RestException(500, "Error deleting attribute");
1325  }
1326 
1327  return $result;
1328  }
1329 
1341  public function getAttributeValueById($id)
1342  {
1343  if (!DolibarrApiAccess::$user->rights->produit->lire) {
1344  throw new RestException(401);
1345  }
1346 
1347  $sql = "SELECT rowid, fk_product_attribute, ref, value FROM ".$this->db->prefix()."product_attribute_value WHERE rowid = ".(int) $id." AND entity IN (".getEntity('product').")";
1348 
1349  $query = $this->db->query($sql);
1350 
1351  if (!$query) {
1352  throw new RestException(401);
1353  }
1354 
1355  if (!$this->db->num_rows($query)) {
1356  throw new RestException(404, 'Attribute value not found');
1357  }
1358 
1359  $result = $this->db->fetch_object($query);
1360 
1361  $attrval = array();
1362  $attrval['id'] = $result->rowid;
1363  $attrval['fk_product_attribute'] = $result->fk_product_attribute;
1364  $attrval['ref'] = $result->ref;
1365  $attrval['value'] = $result->value;
1366 
1367  return $attrval;
1368  }
1369 
1382  public function getAttributeValueByRef($id, $ref)
1383  {
1384  if (!DolibarrApiAccess::$user->rights->produit->lire) {
1385  throw new RestException(401);
1386  }
1387 
1388  $ref = trim($ref);
1389 
1390  $sql = "SELECT rowid, fk_product_attribute, ref, value FROM ".$this->db->prefix()."product_attribute_value";
1391  $sql .= " WHERE ref LIKE '".$this->db->escape($ref)."' AND fk_product_attribute = ".((int) $id)." AND entity IN (".getEntity('product').")";
1392 
1393  $query = $this->db->query($sql);
1394 
1395  if (!$query) {
1396  throw new RestException(401);
1397  }
1398 
1399  if (!$this->db->num_rows($query)) {
1400  throw new RestException(404, 'Attribute value not found');
1401  }
1402 
1403  $result = $this->db->fetch_object($query);
1404 
1405  $attrval = array();
1406  $attrval['id'] = $result->rowid;
1407  $attrval['fk_product_attribute'] = $result->fk_product_attribute;
1408  $attrval['ref'] = $result->ref;
1409  $attrval['value'] = $result->value;
1410 
1411  return $attrval;
1412  }
1413 
1425  public function deleteAttributeValueByRef($id, $ref)
1426  {
1427  if (!DolibarrApiAccess::$user->rights->produit->supprimer) {
1428  throw new RestException(401);
1429  }
1430 
1431  $ref = trim($ref);
1432 
1433  $sql = "SELECT rowid FROM ".$this->db->prefix()."product_attribute_value";
1434  $sql .= " WHERE ref LIKE '".$this->db->escape($ref)."' AND fk_product_attribute = ".((int) $id)." AND entity IN (".getEntity('product').")";
1435  $query = $this->db->query($sql);
1436 
1437  if (!$query) {
1438  throw new RestException(401);
1439  }
1440 
1441  if (!$this->db->num_rows($query)) {
1442  throw new RestException(404, 'Attribute value not found');
1443  }
1444 
1445  $result = $this->db->fetch_object($query);
1446 
1447  $attrval = new ProductAttributeValue($this->db);
1448  $attrval->id = $result->rowid;
1449  $result = $attrval->delete(DolibarrApiAccess::$user);
1450  if ($result > 0) {
1451  return 1;
1452  }
1453 
1454  throw new RestException(500, "Error deleting attribute value");
1455  }
1456 
1468  public function getAttributeValues($id)
1469  {
1470  if (!DolibarrApiAccess::$user->rights->produit->lire) {
1471  throw new RestException(401);
1472  }
1473 
1474  $objectval = new ProductAttributeValue($this->db);
1475 
1476  $return = $objectval->fetchAllByProductAttribute((int) $id);
1477 
1478  if (count($return) == 0) {
1479  throw new RestException(404, 'Attribute values not found');
1480  }
1481 
1482  foreach ($return as $key => $val) {
1483  $return[$key] = $this->_cleanObjectDatas($return[$key]);
1484  }
1485 
1486  return $return;
1487  }
1488 
1499  public function getAttributeValuesByRef($ref)
1500  {
1501  if (!DolibarrApiAccess::$user->rights->produit->lire) {
1502  throw new RestException(401);
1503  }
1504 
1505  $ref = trim($ref);
1506 
1507  $return = array();
1508 
1509  $sql = "SELECT ";
1510  $sql .= "v.fk_product_attribute, v.rowid, v.ref, v.value FROM ".$this->db->prefix()."product_attribute_value as v";
1511  $sql .= " WHERE v.fk_product_attribute IN (SELECT rowid FROM ".$this->db->prefix()."product_attribute WHERE ref LIKE '".$this->db->escape($ref)."')";
1512 
1513  $resql = $this->db->query($sql);
1514 
1515  while ($result = $this->db->fetch_object($resql)) {
1516  $tmp = new ProductAttributeValue($this->db);
1517  $tmp->fk_product_attribute = $result->fk_product_attribute;
1518  $tmp->id = $result->rowid;
1519  $tmp->ref = $result->ref;
1520  $tmp->value = $result->value;
1521 
1522  $return[] = $this->_cleanObjectDatas($tmp);
1523  }
1524 
1525  return $return;
1526  }
1527 
1541  public function addAttributeValue($id, $ref, $value)
1542  {
1543  if (!DolibarrApiAccess::$user->rights->produit->creer) {
1544  throw new RestException(401);
1545  }
1546 
1547  if (empty($ref) || empty($value)) {
1548  throw new RestException(401);
1549  }
1550 
1551  $objectval = new ProductAttributeValue($this->db);
1552  $objectval->fk_product_attribute = ((int) $id);
1553  $objectval->ref = $ref;
1554  $objectval->value = $value;
1555 
1556  if ($objectval->create(DolibarrApiAccess::$user) > 0) {
1557  return $objectval->id;
1558  }
1559  throw new RestException(500, "Error creating new attribute value");
1560  }
1561 
1574  public function putAttributeValue($id, $request_data)
1575  {
1576  if (!DolibarrApiAccess::$user->rights->produit->creer) {
1577  throw new RestException(401);
1578  }
1579 
1580  $objectval = new ProductAttributeValue($this->db);
1581  $result = $objectval->fetch((int) $id);
1582 
1583  if ($result == 0) {
1584  throw new RestException(404, 'Attribute value not found');
1585  } elseif ($result < 0) {
1586  throw new RestException(500, "Error fetching attribute value");
1587  }
1588 
1589  foreach ($request_data as $field => $value) {
1590  if ($field == 'rowid') {
1591  continue;
1592  }
1593  $objectval->$field = $value;
1594  }
1595 
1596  if ($objectval->update(DolibarrApiAccess::$user) > 0) {
1597  $result = $objectval->fetch((int) $id);
1598  if ($result == 0) {
1599  throw new RestException(404, 'Attribute not found');
1600  } elseif ($result < 0) {
1601  throw new RestException(500, "Error fetching attribute");
1602  } else {
1603  return $this->_cleanObjectDatas($objectval);
1604  }
1605  }
1606  throw new RestException(500, "Error updating attribute");
1607  }
1608 
1620  public function deleteAttributeValueById($id)
1621  {
1622  if (!DolibarrApiAccess::$user->rights->produit->supprimer) {
1623  throw new RestException(401);
1624  }
1625 
1626  $objectval = new ProductAttributeValue($this->db);
1627  $objectval->id = (int) $id;
1628 
1629  if ($objectval->delete(DolibarrApiAccess::$user) > 0) {
1630  return 1;
1631  }
1632  throw new RestException(500, "Error deleting attribute value");
1633  }
1634 
1647  public function getVariants($id, $includestock = 0)
1648  {
1649  if (!DolibarrApiAccess::$user->rights->produit->lire) {
1650  throw new RestException(401);
1651  }
1652 
1653  $prodcomb = new ProductCombination($this->db);
1654  $combinations = $prodcomb->fetchAllByFkProductParent((int) $id);
1655 
1656  foreach ($combinations as $key => $combination) {
1657  $prodc2vp = new ProductCombination2ValuePair($this->db);
1658  $combinations[$key]->attributes = $prodc2vp->fetchByFkCombination((int) $combination->id);
1659  $combinations[$key] = $this->_cleanObjectDatas($combinations[$key]);
1660 
1661  if (!empty($includestock) && DolibarrApiAccess::$user->rights->stock->lire) {
1662  $productModel = new Product($this->db);
1663  $productModel->fetch((int) $combination->fk_product_child);
1664  $productModel->load_stock($includestock);
1665  $combinations[$key]->stock_warehouse = $this->_cleanObjectDatas($productModel)->stock_warehouse;
1666  }
1667  }
1668 
1669  return $combinations;
1670  }
1671 
1683  public function getVariantsByProdRef($ref)
1684  {
1685  if (!DolibarrApiAccess::$user->rights->produit->lire) {
1686  throw new RestException(401);
1687  }
1688 
1689  $result = $this->product->fetch('', $ref);
1690  if (!$result) {
1691  throw new RestException(404, 'Product not found');
1692  }
1693 
1694  $prodcomb = new ProductCombination($this->db);
1695  $combinations = $prodcomb->fetchAllByFkProductParent((int) $this->product->id);
1696 
1697  foreach ($combinations as $key => $combination) {
1698  $prodc2vp = new ProductCombination2ValuePair($this->db);
1699  $combinations[$key]->attributes = $prodc2vp->fetchByFkCombination((int) $combination->id);
1700  $combinations[$key] = $this->_cleanObjectDatas($combinations[$key]);
1701  }
1702 
1703  return $combinations;
1704  }
1705 
1726  public function addVariant($id, $weight_impact, $price_impact, $price_impact_is_percent, $features, $reference = '', $ref_ext = '')
1727  {
1728  if (!DolibarrApiAccess::$user->rights->produit->creer) {
1729  throw new RestException(401);
1730  }
1731 
1732  if (empty($id) || empty($features) || !is_array($features)) {
1733  throw new RestException(401);
1734  }
1735 
1736  $weight_impact = price2num($weight_impact);
1737  $price_impact = price2num($price_impact);
1738 
1739  $prodattr = new ProductAttribute($this->db);
1740  $prodattr_val = new ProductAttributeValue($this->db);
1741  foreach ($features as $id_attr => $id_value) {
1742  if ($prodattr->fetch((int) $id_attr) < 0) {
1743  throw new RestException(401);
1744  }
1745  if ($prodattr_val->fetch((int) $id_value) < 0) {
1746  throw new RestException(401);
1747  }
1748  }
1749 
1750  $result = $this->product->fetch((int) $id);
1751  if (!$result) {
1752  throw new RestException(404, 'Product not found');
1753  }
1754 
1755  $prodcomb = new ProductCombination($this->db);
1756 
1757  $result = $prodcomb->createProductCombination(DolibarrApiAccess::$user, $this->product, $features, array(), $price_impact_is_percent, $price_impact, $weight_impact, $reference, $ref_ext);
1758  if ($result > 0) {
1759  return $result;
1760  } else {
1761  throw new RestException(500, "Error creating new product variant");
1762  }
1763  }
1764 
1783  public function addVariantByProductRef($ref, $weight_impact, $price_impact, $price_impact_is_percent, $features)
1784  {
1785  if (!DolibarrApiAccess::$user->rights->produit->creer) {
1786  throw new RestException(401);
1787  }
1788 
1789  if (empty($ref) || empty($features) || !is_array($features)) {
1790  throw new RestException(401);
1791  }
1792 
1793  $weight_impact = price2num($weight_impact);
1794  $price_impact = price2num($price_impact);
1795 
1796  $prodattr = new ProductAttribute($this->db);
1797  $prodattr_val = new ProductAttributeValue($this->db);
1798  foreach ($features as $id_attr => $id_value) {
1799  if ($prodattr->fetch((int) $id_attr) < 0) {
1800  throw new RestException(404);
1801  }
1802  if ($prodattr_val->fetch((int) $id_value) < 0) {
1803  throw new RestException(404);
1804  }
1805  }
1806 
1807  $result = $this->product->fetch('', trim($ref));
1808  if (!$result) {
1809  throw new RestException(404, 'Product not found');
1810  }
1811 
1812  $prodcomb = new ProductCombination($this->db);
1813  if (!$prodcomb->fetchByProductCombination2ValuePairs($this->product->id, $features)) {
1814  $result = $prodcomb->createProductCombination(DolibarrApiAccess::$user, $this->product, $features, array(), $price_impact_is_percent, $price_impact, $weight_impact);
1815  if ($result > 0) {
1816  return $result;
1817  } else {
1818  throw new RestException(500, "Error creating new product variant");
1819  }
1820  } else {
1821  return $prodcomb->id;
1822  }
1823  }
1824 
1837  public function putVariant($id, $request_data = null)
1838  {
1839  if (!DolibarrApiAccess::$user->rights->produit->creer) {
1840  throw new RestException(401);
1841  }
1842 
1843  $prodcomb = new ProductCombination($this->db);
1844  $prodcomb->fetch((int) $id);
1845 
1846  foreach ($request_data as $field => $value) {
1847  if ($field == 'rowid') {
1848  continue;
1849  }
1850  $prodcomb->$field = $value;
1851  }
1852 
1853  $result = $prodcomb->update(DolibarrApiAccess::$user);
1854  if ($result > 0) {
1855  return 1;
1856  }
1857  throw new RestException(500, "Error editing variant");
1858  }
1859 
1871  public function deleteVariant($id)
1872  {
1873  if (!DolibarrApiAccess::$user->rights->produit->supprimer) {
1874  throw new RestException(401);
1875  }
1876 
1877  $prodcomb = new ProductCombination($this->db);
1878  $prodcomb->id = (int) $id;
1879  $result = $prodcomb->delete(DolibarrApiAccess::$user);
1880  if ($result <= 0) {
1881  throw new RestException(500, "Error deleting variant");
1882  }
1883  return $result;
1884  }
1885 
1900  public function getStock($id, $selected_warehouse_id = null)
1901  {
1902  if (!DolibarrApiAccess::$user->rights->produit->lire || !DolibarrApiAccess::$user->rights->stock->lire) {
1903  throw new RestException(401);
1904  }
1905 
1906  if (!DolibarrApi::_checkAccessToResource('product', $id)) {
1907  throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1908  }
1909 
1910  $product_model = new Product($this->db);
1911  $product_model->fetch($id);
1912  $product_model->load_stock();
1913 
1914  $stockData = $this->_cleanObjectDatas($product_model)->stock_warehouse;
1915  if ($selected_warehouse_id) {
1916  foreach ($stockData as $warehouse_id => $warehouse) {
1917  if ($warehouse_id != $selected_warehouse_id) {
1918  unset($stockData[$warehouse_id]);
1919  }
1920  }
1921  }
1922 
1923  if (empty($stockData)) {
1924  throw new RestException(404, 'No stock found');
1925  }
1926 
1927  return array('stock_warehouses'=>$stockData);
1928  }
1929 
1930  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
1937  protected function _cleanObjectDatas($object)
1938  {
1939  // phpcs:enable
1940  $object = parent::_cleanObjectDatas($object);
1941 
1942  unset($object->statut);
1943 
1944  unset($object->regeximgext);
1945  unset($object->price_by_qty);
1946  unset($object->prices_by_qty_id);
1947  unset($object->libelle);
1948  unset($object->product_id_already_linked);
1949  unset($object->reputations);
1950  unset($object->db);
1951  unset($object->name);
1952  unset($object->firstname);
1953  unset($object->lastname);
1954  unset($object->civility_id);
1955  unset($object->contact);
1956  unset($object->contact_id);
1957  unset($object->thirdparty);
1958  unset($object->user);
1959  unset($object->origin);
1960  unset($object->origin_id);
1961  unset($object->fourn_pu);
1962  unset($object->fourn_price_base_type);
1963  unset($object->fourn_socid);
1964  unset($object->ref_fourn);
1965  unset($object->ref_supplier);
1966  unset($object->product_fourn_id);
1967  unset($object->fk_project);
1968 
1969  unset($object->mode_reglement_id);
1970  unset($object->cond_reglement_id);
1971  unset($object->demand_reason_id);
1972  unset($object->transport_mode_id);
1973  unset($object->cond_reglement);
1974  unset($object->shipping_method_id);
1975  unset($object->model_pdf);
1976  unset($object->note);
1977 
1978  unset($object->nbphoto);
1979  unset($object->recuperableonly);
1980  unset($object->multiprices_recuperableonly);
1981  unset($object->tva_npr);
1982  unset($object->lines);
1983  unset($object->fk_bank);
1984  unset($object->fk_account);
1985 
1986  unset($object->supplierprices); // Mut use another API to get them
1987 
1988  if (empty(DolibarrApiAccess::$user->rights->stock->lire)) {
1989  unset($object->stock_reel);
1990  unset($object->stock_theorique);
1991  unset($object->stock_warehouse);
1992  }
1993 
1994  return $object;
1995  }
1996 
2004  private function _validate($data)
2005  {
2006  $product = array();
2007  foreach (Products::$FIELDS as $field) {
2008  if (!isset($data[$field])) {
2009  throw new RestException(400, "$field field missing");
2010  }
2011  $product[$field] = $data[$field];
2012  }
2013  return $product;
2014  }
2015 
2035  private function _fetch($id, $ref = '', $ref_ext = '', $barcode = '', $includestockdata = 0, $includesubproducts = false, $includeparentid = false, $includeifobjectisused = false, $includetrans = false)
2036  {
2037  if (empty($id) && empty($ref) && empty($ref_ext) && empty($barcode)) {
2038  throw new RestException(400, 'bad value for parameter id, ref, ref_ext or barcode');
2039  }
2040 
2041  $id = (empty($id) ? 0 : $id);
2042 
2043  if (!DolibarrApiAccess::$user->rights->produit->lire) {
2044  throw new RestException(403);
2045  }
2046 
2047  $result = $this->product->fetch($id, $ref, $ref_ext, $barcode, 0, 0, ($includetrans ? 0 : 1));
2048  if (!$result) {
2049  throw new RestException(404, 'Product not found');
2050  }
2051 
2052  if (!DolibarrApi::_checkAccessToResource('product', $this->product->id)) {
2053  throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
2054  }
2055 
2056  if (!empty($includestockdata) && DolibarrApiAccess::$user->rights->stock->lire) {
2057  $this->product->load_stock($includestockdata);
2058 
2059  if (is_array($this->product->stock_warehouse)) {
2060  foreach ($this->product->stock_warehouse as $keytmp => $valtmp) {
2061  if (isset($this->product->stock_warehouse[$keytmp]->detail_batch) && is_array($this->product->stock_warehouse[$keytmp]->detail_batch)) {
2062  foreach ($this->product->stock_warehouse[$keytmp]->detail_batch as $keytmp2 => $valtmp2) {
2063  unset($this->product->stock_warehouse[$keytmp]->detail_batch[$keytmp2]->db);
2064  }
2065  }
2066  }
2067  }
2068  }
2069 
2070  if ($includesubproducts) {
2071  $childsArbo = $this->product->getChildsArbo($id, 1);
2072 
2073  $keys = array('rowid', 'qty', 'fk_product_type', 'label', 'incdec', 'ref', 'fk_association', 'rang');
2074  $childs = array();
2075  foreach ($childsArbo as $values) {
2076  $childs[] = array_combine($keys, $values);
2077  }
2078 
2079  $this->product->sousprods = $childs;
2080  }
2081 
2082  if ($includeparentid) {
2083  $prodcomb = new ProductCombination($this->db);
2084  $this->product->fk_product_parent = null;
2085  if (($fk_product_parent = $prodcomb->fetchByFkProductChild($this->product->id)) > 0) {
2086  $this->product->fk_product_parent = $fk_product_parent;
2087  }
2088  }
2089 
2090  if ($includeifobjectisused) {
2091  $this->product->is_object_used = ($this->product->isObjectUsed() > 0);
2092  }
2093 
2094  return $this->_cleanObjectDatas($this->product);
2095  }
2096 }
Class to manage categories.
Class for API REST v1.
Definition: api.class.php:31
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
_checkFilters($sqlfilters, &$error='')
Return if a $sqlfilters parameter is valid.
Definition: api.class.php:310
Class to manage suppliers.
Class ProductAttribute Used to represent a product attribute.
Class ProductAttributeValue Used to represent a product attribute value.
Class ProductCombination2ValuePair Used to represent the relation between a product combination,...
Class ProductCombination Used to represent a product combination.
Class to manage predefined suppliers products.
Class to manage products or services.
File of class to manage predefined price products or services by customer.
_cleanObjectDatas($object)
Clean sensible object datas.
putAttributeValue($id, $request_data)
Update attribute value.
deleteAttributes($id)
Delete attributes by id.
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.
deletePurchasePrice($id, $priceid)
Delete purchase price for a product.
getAttributeValuesByRef($ref)
Get all values for an attribute ref.
putVariant($id, $request_data=null)
Put product variants.
put($id, $request_data=null)
Update product.
addAttributeValue($id, $ref, $value)
Add attribute value.
addVariantByProductRef($ref, $weight_impact, $price_impact, $price_impact_is_percent, $features)
Add variant by product ref.
getAttributeValueById($id)
Get attribute value by id.
getVariantsByProdRef($ref)
Get product variants by Product ref.
getCustomerPricesPerQuantity($id)
Get prices per quantity for a product.
__construct()
Constructor.
getPurchasePrices($id, $ref='', $ref_ext='', $barcode='')
Get purchase prices for a product.
delSubproducts($id, $subproduct_id)
Remove subproduct.
getVariants($id, $includestock=0)
Get product variants.
putAttributes($id, $request_data=null)
Update attributes by id.
deleteAttributeValueById($id)
Delete attribute value by id.
getAttributeValues($id)
Get all values for an attribute id.
addVariant($id, $weight_impact, $price_impact, $price_impact_is_percent, $features, $reference='', $ref_ext='')
Add variant.
getSubproducts($id)
Get the list of subproducts of the product.
getAttributesByRefExt($ref_ext)
Get attributes by ref_ext.
getByRef($ref, $includestockdata=0, $includesubproducts=false, $includeparentid=false, $includetrans=false)
Get properties of a product object by ref.
deleteAttributeValueByRef($id, $ref)
Delete attribute value by ref.
post($request_data=null)
Create product object.
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.
getCategories($id, $sortfield="s.rowid", $sortorder='ASC', $limit=0, $page=0)
Get categories for a product.
getByBarcode($barcode, $includestockdata=0, $includesubproducts=false, $includeparentid=false, $includetrans=false)
Get properties of a product object by barcode.
getByRefExt($ref_ext, $includestockdata=0, $includesubproducts=false, $includeparentid=false, $includetrans=false)
Get properties of a product object by ref_ext.
getCustomerPricesPerCustomer($id, $thirdparty_id='')
Get prices per customer for a product.
deleteVariant($id)
Delete product variants.
_validate($data)
Validate fields before create or update object.
getCustomerPricesPerSegment($id)
Get prices per segment for a product.
_fetch($id, $ref='', $ref_ext='', $barcode='', $includestockdata=0, $includesubproducts=false, $includeparentid=false, $includeifobjectisused=false, $includetrans=false)
Get properties of 1 product object.
addAttributes($ref, $label, $ref_ext='')
Add attributes.
getAttributeValueByRef($id, $ref)
Get attribute value by ref.
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.
getAttributeById($id)
Get attribute by ID.
getStock($id, $selected_warehouse_id=null)
Get stock data for the product id given.
getAttributes($sortfield="t.ref", $sortorder='ASC', $limit=100, $page=0, $sqlfilters='')
Get attributes.
getAttributesByRef($ref)
Get attributes by ref.
addSubproducts($id, $subproduct_id, $qty, $incdec=1)
Add subproduct.
if(isModEnabled('facture') &&!empty($user->rights->facture->lire)) if((isModEnabled('fournisseur') &&empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD) && $user->hasRight("fournisseur", "facture", "lire"))||(isModEnabled('supplier_invoice') && $user->hasRight("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->hasRight("commande", "lire") &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) $resql
Social contributions to pay.
Definition: index.php:745
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_clone($object, $native=0)
Create a clone of instance of object (new instance with same value for each properties) With native =...
sanitizeVal($out='', $check='alphanohtml', $filter=null, $options=null)
Return a sanitized or empty value after checking value against a rule.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
$conf db
API class for accounts.
Definition: inc.php:41