dolibarr  16.0.5
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  $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 : '.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 : '.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->fetch_all('', '', 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 : '.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 $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  $attr["is_used_by_products"] = (int) $obj->nb;
1217 
1218  return $attr;
1219  }
1220 
1234  public function addAttributes($ref, $label, $ref_ext = '')
1235  {
1236  if (!DolibarrApiAccess::$user->rights->produit->creer) {
1237  throw new RestException(401);
1238  }
1239 
1240  $prodattr = new ProductAttribute($this->db);
1241  $prodattr->label = $label;
1242  $prodattr->ref = $ref;
1243  $prodattr->ref_ext = $ref_ext;
1244 
1245  $resid = $prodattr->create(DolibarrApiAccess::$user);
1246  if ($resid <= 0) {
1247  throw new RestException(500, "Error creating new attribute");
1248  }
1249  return $resid;
1250  }
1251 
1265  public function putAttributes($id, $request_data = null)
1266  {
1267  if (!DolibarrApiAccess::$user->rights->produit->creer) {
1268  throw new RestException(401);
1269  }
1270 
1271  $prodattr = new ProductAttribute($this->db);
1272 
1273  $result = $prodattr->fetch((int) $id);
1274  if ($result == 0) {
1275  throw new RestException(404, 'Attribute not found');
1276  } elseif ($result < 0) {
1277  throw new RestException(500, "Error fetching attribute");
1278  }
1279 
1280  foreach ($request_data as $field => $value) {
1281  if ($field == 'rowid') {
1282  continue;
1283  }
1284  $prodattr->$field = $value;
1285  }
1286 
1287  if ($prodattr->update(DolibarrApiAccess::$user) > 0) {
1288  $result = $prodattr->fetch((int) $id);
1289  if ($result == 0) {
1290  throw new RestException(404, 'Attribute not found');
1291  } elseif ($result < 0) {
1292  throw new RestException(500, "Error fetching attribute");
1293  } else {
1294  return $prodattr;
1295  }
1296  }
1297  throw new RestException(500, "Error updating attribute");
1298  }
1299 
1311  public function deleteAttributes($id)
1312  {
1313  if (!DolibarrApiAccess::$user->rights->produit->supprimer) {
1314  throw new RestException(401);
1315  }
1316 
1317  $prodattr = new ProductAttribute($this->db);
1318  $prodattr->id = (int) $id;
1319  $result = $prodattr->delete(DolibarrApiAccess::$user);
1320 
1321  if ($result <= 0) {
1322  throw new RestException(500, "Error deleting attribute");
1323  }
1324 
1325  return $result;
1326  }
1327 
1339  public function getAttributeValueById($id)
1340  {
1341  if (!DolibarrApiAccess::$user->rights->produit->lire) {
1342  throw new RestException(401);
1343  }
1344 
1345  $sql = "SELECT rowid, fk_product_attribute, ref, value FROM ".$this->db->prefix()."product_attribute_value WHERE rowid = ".(int) $id." AND entity IN (".getEntity('product').")";
1346 
1347  $query = $this->db->query($sql);
1348 
1349  if (!$query) {
1350  throw new RestException(401);
1351  }
1352 
1353  if (!$this->db->num_rows($query)) {
1354  throw new RestException(404, 'Attribute value not found');
1355  }
1356 
1357  $result = $this->db->fetch_object($query);
1358 
1359  $attrval = array();
1360  $attrval['id'] = $result->rowid;
1361  $attrval['fk_product_attribute'] = $result->fk_product_attribute;
1362  $attrval['ref'] = $result->ref;
1363  $attrval['value'] = $result->value;
1364 
1365  return $attrval;
1366  }
1367 
1380  public function getAttributeValueByRef($id, $ref)
1381  {
1382  if (!DolibarrApiAccess::$user->rights->produit->lire) {
1383  throw new RestException(401);
1384  }
1385 
1386  $ref = trim($ref);
1387 
1388  $sql = "SELECT rowid, fk_product_attribute, ref, value FROM ".$this->db->prefix()."product_attribute_value";
1389  $sql .= " WHERE ref LIKE '".$this->db->escape($ref)."' AND fk_product_attribute = ".((int) $id)." AND entity IN (".getEntity('product').")";
1390 
1391  $query = $this->db->query($sql);
1392 
1393  if (!$query) {
1394  throw new RestException(401);
1395  }
1396 
1397  if (!$this->db->num_rows($query)) {
1398  throw new RestException(404, 'Attribute value not found');
1399  }
1400 
1401  $result = $this->db->fetch_object($query);
1402 
1403  $attrval = array();
1404  $attrval['id'] = $result->rowid;
1405  $attrval['fk_product_attribute'] = $result->fk_product_attribute;
1406  $attrval['ref'] = $result->ref;
1407  $attrval['value'] = $result->value;
1408 
1409  return $attrval;
1410  }
1411 
1423  public function deleteAttributeValueByRef($id, $ref)
1424  {
1425  if (!DolibarrApiAccess::$user->rights->produit->supprimer) {
1426  throw new RestException(401);
1427  }
1428 
1429  $ref = trim($ref);
1430 
1431  $sql = "SELECT rowid FROM ".$this->db->prefix()."product_attribute_value";
1432  $sql .= " WHERE ref LIKE '".$this->db->escape($ref)."' AND fk_product_attribute = ".((int) $id)." AND entity IN (".getEntity('product').")";
1433  $query = $this->db->query($sql);
1434 
1435  if (!$query) {
1436  throw new RestException(401);
1437  }
1438 
1439  if (!$this->db->num_rows($query)) {
1440  throw new RestException(404, 'Attribute value not found');
1441  }
1442 
1443  $result = $this->db->fetch_object($query);
1444 
1445  $attrval = new ProductAttributeValue($this->db);
1446  $attrval->id = $result->rowid;
1447  $result = $attrval->delete(DolibarrApiAccess::$user);
1448  if ($result > 0) {
1449  return 1;
1450  }
1451 
1452  throw new RestException(500, "Error deleting attribute value");
1453  }
1454 
1466  public function getAttributeValues($id)
1467  {
1468  if (!DolibarrApiAccess::$user->rights->produit->lire) {
1469  throw new RestException(401);
1470  }
1471 
1472  $objectval = new ProductAttributeValue($this->db);
1473 
1474  $return = $objectval->fetchAllByProductAttribute((int) $id);
1475 
1476  if (count($return) == 0) {
1477  throw new RestException(404, 'Attribute values not found');
1478  }
1479 
1480  foreach ($return as $key => $val) {
1481  $return[$key] = $this->_cleanObjectDatas($return[$key]);
1482  }
1483 
1484  return $return;
1485  }
1486 
1497  public function getAttributeValuesByRef($ref)
1498  {
1499  if (!DolibarrApiAccess::$user->rights->produit->lire) {
1500  throw new RestException(401);
1501  }
1502 
1503  $ref = trim($ref);
1504 
1505  $return = array();
1506 
1507  $sql = "SELECT ";
1508  $sql .= "v.fk_product_attribute, v.rowid, v.ref, v.value FROM ".$this->db->prefix()."product_attribute_value as v";
1509  $sql .= " WHERE v.fk_product_attribute IN (SELECT rowid FROM ".$this->db->prefix()."product_attribute WHERE ref LIKE '".$this->db->escape($ref)."')";
1510 
1511  $resql = $this->db->query($sql);
1512 
1513  while ($result = $this->db->fetch_object($resql)) {
1514  $tmp = new ProductAttributeValue($this->db);
1515  $tmp->fk_product_attribute = $result->fk_product_attribute;
1516  $tmp->id = $result->rowid;
1517  $tmp->ref = $result->ref;
1518  $tmp->value = $result->value;
1519 
1520  $return[] = $this->_cleanObjectDatas($tmp);
1521  }
1522 
1523  return $return;
1524  }
1525 
1539  public function addAttributeValue($id, $ref, $value)
1540  {
1541  if (!DolibarrApiAccess::$user->rights->produit->creer) {
1542  throw new RestException(401);
1543  }
1544 
1545  if (empty($ref) || empty($value)) {
1546  throw new RestException(401);
1547  }
1548 
1549  $objectval = new ProductAttributeValue($this->db);
1550  $objectval->fk_product_attribute = ((int) $id);
1551  $objectval->ref = $ref;
1552  $objectval->value = $value;
1553 
1554  if ($objectval->create(DolibarrApiAccess::$user) > 0) {
1555  return $objectval->id;
1556  }
1557  throw new RestException(500, "Error creating new attribute value");
1558  }
1559 
1572  public function putAttributeValue($id, $request_data)
1573  {
1574  if (!DolibarrApiAccess::$user->rights->produit->creer) {
1575  throw new RestException(401);
1576  }
1577 
1578  $objectval = new ProductAttributeValue($this->db);
1579  $result = $objectval->fetch((int) $id);
1580 
1581  if ($result == 0) {
1582  throw new RestException(404, 'Attribute value not found');
1583  } elseif ($result < 0) {
1584  throw new RestException(500, "Error fetching attribute value");
1585  }
1586 
1587  foreach ($request_data as $field => $value) {
1588  if ($field == 'rowid') {
1589  continue;
1590  }
1591  $objectval->$field = $value;
1592  }
1593 
1594  if ($objectval->update(DolibarrApiAccess::$user) > 0) {
1595  $result = $objectval->fetch((int) $id);
1596  if ($result == 0) {
1597  throw new RestException(404, 'Attribute not found');
1598  } elseif ($result < 0) {
1599  throw new RestException(500, "Error fetching attribute");
1600  } else {
1601  return $objectval;
1602  }
1603  }
1604  throw new RestException(500, "Error updating attribute");
1605  }
1606 
1618  public function deleteAttributeValueById($id)
1619  {
1620  if (!DolibarrApiAccess::$user->rights->produit->supprimer) {
1621  throw new RestException(401);
1622  }
1623 
1624  $objectval = new ProductAttributeValue($this->db);
1625  $objectval->id = (int) $id;
1626 
1627  if ($objectval->delete(DolibarrApiAccess::$user) > 0) {
1628  return 1;
1629  }
1630  throw new RestException(500, "Error deleting attribute value");
1631  }
1632 
1645  public function getVariants($id, $includestock = 0)
1646  {
1647  if (!DolibarrApiAccess::$user->rights->produit->lire) {
1648  throw new RestException(401);
1649  }
1650 
1651  $prodcomb = new ProductCombination($this->db);
1652  $combinations = $prodcomb->fetchAllByFkProductParent((int) $id);
1653 
1654  foreach ($combinations as $key => $combination) {
1655  $prodc2vp = new ProductCombination2ValuePair($this->db);
1656  $combinations[$key]->attributes = $prodc2vp->fetchByFkCombination((int) $combination->id);
1657  $combinations[$key] = $this->_cleanObjectDatas($combinations[$key]);
1658 
1659  if ($includestock==1 && DolibarrApiAccess::$user->rights->stock->lire) {
1660  $productModel = new Product($this->db);
1661  $productModel->fetch((int) $combination->fk_product_child);
1662  $productModel->load_stock();
1663  $combinations[$key]->stock_warehouse = $this->_cleanObjectDatas($productModel)->stock_warehouse;
1664  }
1665  }
1666 
1667  return $combinations;
1668  }
1669 
1681  public function getVariantsByProdRef($ref)
1682  {
1683  if (!DolibarrApiAccess::$user->rights->produit->lire) {
1684  throw new RestException(401);
1685  }
1686 
1687  $result = $this->product->fetch('', $ref);
1688  if (!$result) {
1689  throw new RestException(404, 'Product not found');
1690  }
1691 
1692  $prodcomb = new ProductCombination($this->db);
1693  $combinations = $prodcomb->fetchAllByFkProductParent((int) $this->product->id);
1694 
1695  foreach ($combinations as $key => $combination) {
1696  $prodc2vp = new ProductCombination2ValuePair($this->db);
1697  $combinations[$key]->attributes = $prodc2vp->fetchByFkCombination((int) $combination->id);
1698  $combinations[$key] = $this->_cleanObjectDatas($combinations[$key]);
1699  }
1700 
1701  return $combinations;
1702  }
1703 
1724  public function addVariant($id, $weight_impact, $price_impact, $price_impact_is_percent, $features, $reference = '', $ref_ext = '')
1725  {
1726  if (!DolibarrApiAccess::$user->rights->produit->creer) {
1727  throw new RestException(401);
1728  }
1729 
1730  if (empty($id) || empty($features) || !is_array($features)) {
1731  throw new RestException(401);
1732  }
1733 
1734  $weight_impact = price2num($weight_impact);
1735  $price_impact = price2num($price_impact);
1736 
1737  $prodattr = new ProductAttribute($this->db);
1738  $prodattr_val = new ProductAttributeValue($this->db);
1739  foreach ($features as $id_attr => $id_value) {
1740  if ($prodattr->fetch((int) $id_attr) < 0) {
1741  throw new RestException(401);
1742  }
1743  if ($prodattr_val->fetch((int) $id_value) < 0) {
1744  throw new RestException(401);
1745  }
1746  }
1747 
1748  $result = $this->product->fetch((int) $id);
1749  if (!$result) {
1750  throw new RestException(404, 'Product not found');
1751  }
1752 
1753  $prodcomb = new ProductCombination($this->db);
1754 
1755  $result = $prodcomb->createProductCombination(DolibarrApiAccess::$user, $this->product, $features, array(), $price_impact_is_percent, $price_impact, $weight_impact, $reference, $ref_ext);
1756  if ($result > 0) {
1757  return $result;
1758  } else {
1759  throw new RestException(500, "Error creating new product variant");
1760  }
1761  }
1762 
1781  public function addVariantByProductRef($ref, $weight_impact, $price_impact, $price_impact_is_percent, $features)
1782  {
1783  if (!DolibarrApiAccess::$user->rights->produit->creer) {
1784  throw new RestException(401);
1785  }
1786 
1787  if (empty($ref) || empty($features) || !is_array($features)) {
1788  throw new RestException(401);
1789  }
1790 
1791  $weight_impact = price2num($weight_impact);
1792  $price_impact = price2num($price_impact);
1793 
1794  $prodattr = new ProductAttribute($this->db);
1795  $prodattr_val = new ProductAttributeValue($this->db);
1796  foreach ($features as $id_attr => $id_value) {
1797  if ($prodattr->fetch((int) $id_attr) < 0) {
1798  throw new RestException(404);
1799  }
1800  if ($prodattr_val->fetch((int) $id_value) < 0) {
1801  throw new RestException(404);
1802  }
1803  }
1804 
1805  $result = $this->product->fetch('', trim($ref));
1806  if (!$result) {
1807  throw new RestException(404, 'Product not found');
1808  }
1809 
1810  $prodcomb = new ProductCombination($this->db);
1811  if (!$prodcomb->fetchByProductCombination2ValuePairs($this->product->id, $features)) {
1812  $result = $prodcomb->createProductCombination(DolibarrApiAccess::$user, $this->product, $features, array(), $price_impact_is_percent, $price_impact, $weight_impact);
1813  if ($result > 0) {
1814  return $result;
1815  } else {
1816  throw new RestException(500, "Error creating new product variant");
1817  }
1818  } else {
1819  return $prodcomb->id;
1820  }
1821  }
1822 
1835  public function putVariant($id, $request_data = null)
1836  {
1837  if (!DolibarrApiAccess::$user->rights->produit->creer) {
1838  throw new RestException(401);
1839  }
1840 
1841  $prodcomb = new ProductCombination($this->db);
1842  $prodcomb->fetch((int) $id);
1843 
1844  foreach ($request_data as $field => $value) {
1845  if ($field == 'rowid') {
1846  continue;
1847  }
1848  $prodcomb->$field = $value;
1849  }
1850 
1851  $result = $prodcomb->update(DolibarrApiAccess::$user);
1852  if ($result > 0) {
1853  return 1;
1854  }
1855  throw new RestException(500, "Error editing variant");
1856  }
1857 
1869  public function deleteVariant($id)
1870  {
1871  if (!DolibarrApiAccess::$user->rights->produit->supprimer) {
1872  throw new RestException(401);
1873  }
1874 
1875  $prodcomb = new ProductCombination($this->db);
1876  $prodcomb->id = (int) $id;
1877  $result = $prodcomb->delete(DolibarrApiAccess::$user);
1878  if ($result <= 0) {
1879  throw new RestException(500, "Error deleting variant");
1880  }
1881  return $result;
1882  }
1883 
1898  public function getStock($id, $selected_warehouse_id = null)
1899  {
1900 
1901  if (!DolibarrApiAccess::$user->rights->produit->lire || !DolibarrApiAccess::$user->rights->stock->lire) {
1902  throw new RestException(401);
1903  }
1904 
1905  if (!DolibarrApi::_checkAccessToResource('product', $id)) {
1906  throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1907  }
1908 
1909  $product_model = new Product($this->db);
1910  $product_model->fetch($id);
1911  $product_model->load_stock();
1912 
1913  $stockData = $this->_cleanObjectDatas($product_model)->stock_warehouse;
1914  if ($selected_warehouse_id) {
1915  foreach ($stockData as $warehouse_id => $warehouse) {
1916  if ($warehouse_id != $selected_warehouse_id) {
1917  unset($stockData[$warehouse_id]);
1918  }
1919  }
1920  }
1921 
1922  if (empty($stockData)) {
1923  throw new RestException(404, 'No stock found');
1924  }
1925 
1926  return ['stock_warehouses'=>$stockData];
1927  }
1928 
1929  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
1936  protected function _cleanObjectDatas($object)
1937  {
1938  // phpcs:enable
1939  $object = parent::_cleanObjectDatas($object);
1940 
1941  unset($object->statut);
1942 
1943  unset($object->regeximgext);
1944  unset($object->price_by_qty);
1945  unset($object->prices_by_qty_id);
1946  unset($object->libelle);
1947  unset($object->product_id_already_linked);
1948  unset($object->reputations);
1949  unset($object->db);
1950  unset($object->name);
1951  unset($object->firstname);
1952  unset($object->lastname);
1953  unset($object->civility_id);
1954  unset($object->contact);
1955  unset($object->contact_id);
1956  unset($object->thirdparty);
1957  unset($object->user);
1958  unset($object->origin);
1959  unset($object->origin_id);
1960  unset($object->fourn_pu);
1961  unset($object->fourn_price_base_type);
1962  unset($object->fourn_socid);
1963  unset($object->ref_fourn);
1964  unset($object->ref_supplier);
1965  unset($object->product_fourn_id);
1966  unset($object->fk_project);
1967 
1968  unset($object->mode_reglement_id);
1969  unset($object->cond_reglement_id);
1970  unset($object->demand_reason_id);
1971  unset($object->transport_mode_id);
1972  unset($object->cond_reglement);
1973  unset($object->shipping_method_id);
1974  unset($object->model_pdf);
1975  unset($object->note);
1976 
1977  unset($object->nbphoto);
1978  unset($object->recuperableonly);
1979  unset($object->multiprices_recuperableonly);
1980  unset($object->tva_npr);
1981  unset($object->lines);
1982  unset($object->fk_bank);
1983  unset($object->fk_account);
1984 
1985  unset($object->supplierprices); // Mut use another API to get them
1986 
1987  if (empty(DolibarrApiAccess::$user->rights->stock->lire)) {
1988  unset($object->stock_reel);
1989  unset($object->stock_theorique);
1990  unset($object->stock_warehouse);
1991  }
1992 
1993  return $object;
1994  }
1995 
2003  private function _validate($data)
2004  {
2005  $product = array();
2006  foreach (Products::$FIELDS as $field) {
2007  if (!isset($data[$field])) {
2008  throw new RestException(400, "$field field missing");
2009  }
2010  $product[$field] = $data[$field];
2011  }
2012  return $product;
2013  }
2014 
2034  private function _fetch($id, $ref = '', $ref_ext = '', $barcode = '', $includestockdata = 0, $includesubproducts = false, $includeparentid = false, $includeifobjectisused = false, $includetrans = false)
2035  {
2036  if (empty($id) && empty($ref) && empty($ref_ext) && empty($barcode)) {
2037  throw new RestException(400, 'bad value for parameter id, ref, ref_ext or barcode');
2038  }
2039 
2040  $id = (empty($id) ? 0 : $id);
2041 
2042  if (!DolibarrApiAccess::$user->rights->produit->lire) {
2043  throw new RestException(403);
2044  }
2045 
2046  $result = $this->product->fetch($id, $ref, $ref_ext, $barcode, 0, 0, ($includetrans ? 0 : 1));
2047  if (!$result) {
2048  throw new RestException(404, 'Product not found');
2049  }
2050 
2051  if (!DolibarrApi::_checkAccessToResource('product', $this->product->id)) {
2052  throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
2053  }
2054 
2055  if ($includestockdata && DolibarrApiAccess::$user->rights->stock->lire) {
2056  $this->product->load_stock();
2057 
2058  if (is_array($this->product->stock_warehouse)) {
2059  foreach ($this->product->stock_warehouse as $keytmp => $valtmp) {
2060  if (isset($this->product->stock_warehouse[$keytmp]->detail_batch) && is_array($this->product->stock_warehouse[$keytmp]->detail_batch)) {
2061  foreach ($this->product->stock_warehouse[$keytmp]->detail_batch as $keytmp2 => $valtmp2) {
2062  unset($this->product->stock_warehouse[$keytmp]->detail_batch[$keytmp2]->db);
2063  }
2064  }
2065  }
2066  }
2067  }
2068 
2069  if ($includesubproducts) {
2070  $childsArbo = $this->product->getChildsArbo($id, 1);
2071 
2072  $keys = array('rowid', 'qty', 'fk_product_type', 'label', 'incdec', 'ref', 'fk_association', 'rang');
2073  $childs = array();
2074  foreach ($childsArbo as $values) {
2075  $childs[] = array_combine($keys, $values);
2076  }
2077 
2078  $this->product->sousprods = $childs;
2079  }
2080 
2081  if ($includeparentid) {
2082  $prodcomb = new ProductCombination($this->db);
2083  $this->product->fk_product_parent = null;
2084  if (($fk_product_parent = $prodcomb->fetchByFkProductChild($this->product->id)) > 0) {
2085  $this->product->fk_product_parent = $fk_product_parent;
2086  }
2087  }
2088 
2089  if ($includeifobjectisused) {
2090  $this->product->is_object_used = ($this->product->isObjectUsed() > 0);
2091  }
2092 
2093  return $this->_cleanObjectDatas($this->product);
2094  }
2095 }
Products\getByRefExt
getByRefExt($ref_ext, $includestockdata=0, $includesubproducts=false, $includeparentid=false, $includetrans=false)
Get properties of a product object by ref_ext.
Definition: api_products.class.php:131
Products\addAttributes
addAttributes($ref, $label, $ref_ext='')
Add attributes.
Definition: api_products.class.php:1234
Products\delSubproducts
delSubproducts($id, $subproduct_id)
Remove subproduct.
Definition: api_products.class.php:552
db
$conf db
API class for accounts.
Definition: inc.php:41
Products\getCategories
getCategories($id, $sortfield="s.rowid", $sortorder='ASC', $limit=0, $page=0)
Get categories for a product.
Definition: api_products.class.php:583
Products\putAttributes
putAttributes($id, $request_data=null)
Update attributes by id.
Definition: api_products.class.php:1265
ProductCombination
Class ProductCombination Used to represent a product combination.
Definition: ProductCombination.class.php:25
Productcustomerprice
File of class to manage predefined price products or services by customer.
Definition: productcustomerprice.class.php:29
ProductFournisseur
Class to manage predefined suppliers products.
Definition: fournisseur.product.class.php:41
Products\getAttributeValuesByRef
getAttributeValuesByRef($ref)
Get all values for an attribute ref.
Definition: api_products.class.php:1497
Products\deletePurchasePrice
deletePurchasePrice($id, $priceid)
Delete purchase price for a product.
Definition: api_products.class.php:822
Products\getAttributesByRef
getAttributesByRef($ref)
Get attributes by ref.
Definition: api_products.class.php:1133
Products\getVariantsByProdRef
getVariantsByProdRef($ref)
Get product variants by Product ref.
Definition: api_products.class.php:1681
Products\getAttributesByRefExt
getAttributesByRefExt($ref_ext)
Get attributes by ref_ext.
Definition: api_products.class.php:1183
ProductAttributeValue
Class ProductAttributeValue Used to represent a product attribute value.
Definition: ProductAttributeValue.class.php:25
Products\getByBarcode
getByBarcode($barcode, $includestockdata=0, $includesubproducts=false, $includeparentid=false, $includetrans=false)
Get properties of a product object by barcode.
Definition: api_products.class.php:155
Products\getSubproducts
getSubproducts($id)
Get the list of subproducts of the product.
Definition: api_products.class.php:484
sanitizeVal
sanitizeVal($out='', $check='alphanohtml', $filter=null, $options=null)
Return a sanitized or empty value after checking value against a rule.
Definition: functions.lib.php:825
DolibarrApi\_checkAccessToResource
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
Categorie
Class to manage categories.
Definition: categorie.class.php:47
dol_clone
dol_clone($object, $native=0)
Create a clone of instance of object (new instance with same value for properties) With native = 0: P...
Definition: functions.lib.php:1157
Products\getByRef
getByRef($ref, $includestockdata=0, $includesubproducts=false, $includeparentid=false, $includetrans=false)
Get properties of a product object by ref.
Definition: api_products.class.php:107
Products\getCustomerPricesPerSegment
getCustomerPricesPerSegment($id)
Get prices per segment for a product.
Definition: api_products.class.php:613
Products\_validate
_validate($data)
Validate fields before create or update object.
Definition: api_products.class.php:2003
Products\addVariantByProductRef
addVariantByProductRef($ref, $weight_impact, $price_impact, $price_impact_is_percent, $features)
Add variant by product ref.
Definition: api_products.class.php:1781
price2num
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
Definition: functions.lib.php:5647
Products\addPurchasePrice
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.
Definition: api_products.class.php:764
Products\getAttributeValues
getAttributeValues($id)
Get all values for an attribute id.
Definition: api_products.class.php:1466
DolibarrApi
Class for API REST v1.
Definition: api.class.php:30
Products\_fetch
_fetch($id, $ref='', $ref_ext='', $barcode='', $includestockdata=0, $includesubproducts=false, $includeparentid=false, $includeifobjectisused=false, $includetrans=false)
Get properties of 1 product object.
Definition: api_products.class.php:2034
Products\post
post($request_data=null)
Create product object.
Definition: api_products.class.php:311
ProductAttribute
Class ProductAttribute Used to represent a product attribute.
Definition: ProductAttribute.class.php:25
Products\deleteAttributeValueById
deleteAttributeValueById($id)
Delete attribute value by id.
Definition: api_products.class.php:1618
Products\getAttributes
getAttributes($sortfield="t.ref", $sortorder='ASC', $limit=100, $page=0, $sqlfilters='')
Get attributes.
Definition: api_products.class.php:1023
Products\addVariant
addVariant($id, $weight_impact, $price_impact, $price_impact_is_percent, $features, $reference='', $ref_ext='')
Add variant.
Definition: api_products.class.php:1724
Products\addAttributeValue
addAttributeValue($id, $ref, $value)
Add attribute value.
Definition: api_products.class.php:1539
Products\getAttributeValueByRef
getAttributeValueByRef($id, $ref)
Get attribute value by ref.
Definition: api_products.class.php:1380
getEntity
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
Definition: functions.lib.php:148
Products\deleteAttributes
deleteAttributes($id)
Delete attributes by id.
Definition: api_products.class.php:1311
Fournisseur
Class to manage suppliers.
Definition: fournisseur.class.php:34
Products\getAttributeById
getAttributeById($id)
Get attribute by ID.
Definition: api_products.class.php:1090
DolibarrApi\_checkFilters
_checkFilters($sqlfilters, &$error='')
Return if a $sqlfilters parameter is valid.
Definition: api.class.php:310
Products\getPurchasePrices
getPurchasePrices($id, $ref='', $ref_ext='', $barcode='')
Get purchase prices for a product.
Definition: api_products.class.php:970
Products\getVariants
getVariants($id, $includestock=0)
Get product variants.
Definition: api_products.class.php:1645
Products\_cleanObjectDatas
_cleanObjectDatas($object)
Clean sensible object datas.
Definition: api_products.class.php:1936
Products\__construct
__construct()
Constructor.
Definition: api_products.class.php:58
Products\deleteAttributeValueByRef
deleteAttributeValueByRef($id, $ref)
Delete attribute value by ref.
Definition: api_products.class.php:1423
Products\addSubproducts
addSubproducts($id, $subproduct_id, $qty, $incdec=1)
Add subproduct.
Definition: api_products.class.php:522
Products\put
put($id, $request_data=null)
Update product.
Definition: api_products.class.php:340
Product
Class to manage products or services.
Definition: product.class.php:46
Products\getSupplierProducts
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.
Definition: api_products.class.php:859
Products\getCustomerPricesPerQuantity
getCustomerPricesPerQuantity($id)
Get prices per quantity for a product.
Definition: api_products.class.php:704
Products\putVariant
putVariant($id, $request_data=null)
Put product variants.
Definition: api_products.class.php:1835
Products\getCustomerPricesPerCustomer
getCustomerPricesPerCustomer($id, $thirdparty_id='')
Get prices per customer for a product.
Definition: api_products.class.php:655
Products\index
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.
Definition: api_products.class.php:178
$resql
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
Products\getAttributeValueById
getAttributeValueById($id)
Get attribute value by id.
Definition: api_products.class.php:1339
ProductCombination2ValuePair
Class ProductCombination2ValuePair Used to represent the relation between a product combination,...
Definition: ProductCombination2ValuePair.class.php:24
Products
Definition: api_products.class.php:35
Products\deleteVariant
deleteVariant($id)
Delete product variants.
Definition: api_products.class.php:1869
Products\putAttributeValue
putAttributeValue($id, $request_data)
Update attribute value.
Definition: api_products.class.php:1572
Products\getStock
getStock($id, $selected_warehouse_id=null)
Get stock data for the product id given.
Definition: api_products.class.php:1898