43 public $fk_product_parent;
49 public $fk_product_child;
55 public $variation_price;
61 public $variation_price_percentage =
false;
67 public $variation_weight;
79 public $combination_price_levels;
85 public $variation_ref_ext =
'';
97 $this->entity = $conf->entity;
110 $sql =
"SELECT rowid, fk_product_parent, fk_product_child, variation_price, variation_price_percentage, variation_weight, variation_ref_ext FROM ".MAIN_DB_PREFIX.
"product_attribute_combination WHERE rowid = ".((int) $rowid).
" AND entity IN (".
getEntity(
'product').
")";
112 $query = $this->
db->query($sql);
118 if (!$this->
db->num_rows($query)) {
122 $obj = $this->
db->fetch_object($query);
124 $this->
id = $obj->rowid;
125 $this->fk_product_parent = $obj->fk_product_parent;
126 $this->fk_product_child = $obj->fk_product_child;
127 $this->variation_price = $obj->variation_price;
128 $this->variation_price_percentage = $obj->variation_price_percentage;
129 $this->variation_weight = $obj->variation_weight;
130 $this->variation_ref_ext = $obj->variation_ref_ext;
132 if (!empty($conf->global->PRODUIT_MULTIPRICES)) {
152 if (!empty($this->combination_price_levels) && $useCache) {
153 if ((!empty($fk_price_level) && isset($this->combination_price_levels[$fk_price_level])) || empty($fk_price_level)) {
158 if (!is_array($this->combination_price_levels)
159 || empty($fk_price_level)
161 $this->combination_price_levels = array();
165 $combination_price_levels = $staticProductCombinationLevel->fetchAll($this->
id, $fk_price_level);
167 if (!is_array($combination_price_levels)) {
171 if (empty($combination_price_levels)) {
175 if ($fk_price_level > 0) {
178 for ($i = 1; $i <= $conf->global->PRODUIT_MULTIPRICES_LIMIT; $i++) {
184 $this->combination_price_levels = $combination_price_levels;
204 if (empty($this->combination_price_levels)) {
205 return $staticProductCombinationLevel->deleteAllForCombination($this->
id);
210 $res = $staticProductCombinationLevel->clean($this->
id);
212 $this->errors[] =
'Fail to clean not needed price levels';
217 foreach ($this->combination_price_levels as $fk_price_level => $combination_price_level) {
218 $res = $combination_price_level->save();
220 $this->error =
'Error saving combination price level '.$fk_price_level.
' : '.$combination_price_level->error;
221 $this->errors[] = $this->error;
245 $sql =
"SELECT rowid, fk_product_parent, fk_product_child, variation_price, variation_price_percentage, variation_weight";
246 $sql .=
" FROM ".MAIN_DB_PREFIX.
"product_attribute_combination WHERE fk_product_child = ".((int) $productid).
" AND entity IN (".
getEntity(
'product').
")";
248 $query = $this->
db->query($sql);
254 if (!$this->
db->num_rows($query)) {
258 $result = $this->
db->fetch_object($query);
260 $this->
id = $result->rowid;
261 $this->fk_product_parent = $result->fk_product_parent;
262 $this->fk_product_child = $result->fk_product_child;
263 $this->variation_price = $result->variation_price;
264 $this->variation_price_percentage = $result->variation_price_percentage;
265 $this->variation_weight = $result->variation_weight;
267 if (empty($donotloadpricelevel) && !empty($conf->global->PRODUIT_MULTIPRICES)) {
271 return (
int) $this->fk_product_parent;
284 $sql =
"SELECT rowid, fk_product_parent, fk_product_child, variation_price, variation_price_percentage, variation_weight FROM ".MAIN_DB_PREFIX.
"product_attribute_combination WHERE fk_product_parent = ".((int) $fk_product_parent).
" AND entity IN (".
getEntity(
'product').
")";
286 $query = $this->
db->query($sql);
294 while ($result = $this->
db->fetch_object($query)) {
296 $tmp->id = $result->rowid;
297 $tmp->fk_product_parent = $result->fk_product_parent;
298 $tmp->fk_product_child = $result->fk_product_child;
299 $tmp->variation_price = $result->variation_price;
300 $tmp->variation_price_percentage = $result->variation_price_percentage;
301 $tmp->variation_weight = $result->variation_weight;
302 $tmp->variation_ref_ext = $result->variation_ref_ext;
304 if (!empty($conf->global->PRODUIT_MULTIPRICES)) {
305 $tmp->fetchCombinationPriceLevels();
323 $sql =
"SELECT count(rowid) as nb FROM ".MAIN_DB_PREFIX.
"product_attribute_combination WHERE fk_product_parent = ".((int) $fk_product_parent).
" AND entity IN (".
getEntity(
'product').
")";
327 $obj = $this->
db->fetch_object(
$resql);
348 $sql =
"INSERT INTO ".MAIN_DB_PREFIX.
"product_attribute_combination";
349 $sql .=
" (fk_product_parent, fk_product_child, variation_price, variation_price_percentage, variation_weight, variation_ref_ext, entity)";
350 $sql .=
" VALUES (".((int) $this->fk_product_parent).
", ".((int) $this->fk_product_child).
",";
351 $sql .= (
float) $this->variation_price.
", ".(
int) $this->variation_price_percentage.
",";
352 $sql .= (
float) $this->variation_weight.
", '".$this->db->escape($this->variation_ref_ext).
"', ".(int) $this->entity.
")";
354 $resql = $this->db->query($sql);
356 $this->
id = $this->
db->last_insert_id(MAIN_DB_PREFIX.
'product_attribute_combination');
358 $this->error = $this->
db->lasterror();
362 if (!empty($conf->global->PRODUIT_MULTIPRICES)) {
382 $sql =
"UPDATE ".MAIN_DB_PREFIX.
"product_attribute_combination";
383 $sql .=
" SET fk_product_parent = ".(int) $this->fk_product_parent.
", fk_product_child = ".(
int) $this->fk_product_child.
",";
384 $sql .=
" variation_price = ".(float) $this->variation_price.
", variation_price_percentage = ".(
int) $this->variation_price_percentage.
",";
385 $sql .=
" variation_ref_ext = '".$this->db->escape($this->variation_ref_ext).
"',";
386 $sql .=
" variation_weight = ".(float) $this->variation_weight.
" WHERE rowid = ".((
int) $this->id);
393 if (!empty($conf->global->PRODUIT_MULTIPRICES)) {
401 $parent->fetch($this->fk_product_parent);
414 public function delete(
User $user)
419 $comb2val->deleteByFkCombination($this->
id);
422 if (!$this->
db->query(
"DELETE FROM ".MAIN_DB_PREFIX.
"product_attribute_combination_price_level WHERE fk_product_attribute_combination = ".(
int) $this->id)) {
423 $this->
db->rollback();
427 $sql =
"DELETE FROM ".MAIN_DB_PREFIX.
"product_attribute_combination WHERE rowid = ".(int) $this->
id;
429 if ($this->
db->query($sql)) {
434 $this->
db->rollback();
452 $res = $prodstatic->fetch($prodcomb->fk_product_child);
455 $res = $prodcomb->delete($user);
458 if ($res > 0 && !$prodstatic->isObjectUsed($prodstatic->id)) {
459 $res = $prodstatic->delete($user);
463 $this->
db->rollback();
487 $child->fetch($this->fk_product_child);
489 $child->price_autogen = $parent->price_autogen;
490 $child->weight = $parent->weight;
492 if ($parent->oldcopy && ($parent->status != $parent->oldcopy->status)) {
493 $child->status = $parent->status;
495 if ($parent->oldcopy && ($parent->status_buy != $parent->oldcopy->status_buy)) {
496 $child->status_buy = $parent->status_buy;
499 if ($this->variation_weight) {
500 $child->weight = ($child->weight ? $child->weight : 0) + $this->variation_weight;
502 $child->weight_units = $parent->weight_units;
505 if ($child->label == $parent->label) {
508 $child->label = $parent->label.$varlabel; ;
512 if ($child->update($child->id, $user) > 0) {
513 $new_vat = $parent->tva_tx;
514 $new_npr = $parent->tva_npr;
517 if (!empty($conf->global->PRODUIT_MULTIPRICES)) {
518 for ($i = 1; $i <= $conf->global->PRODUIT_MULTIPRICES_LIMIT; $i++) {
519 if ($parent->multiprices[$i] !=
'' || isset($this->combination_price_levels[$i]->variation_price)) {
520 $new_type = empty($parent->multiprices_base_type[$i]) ?
'HT' : $parent->multiprices_base_type[$i];
521 $new_min_price = $parent->multiprices_min[$i];
522 $variation_price = floatval(!isset($this->combination_price_levels[$i]->variation_price) ? $this->variation_price : $this->combination_price_levels[$i]->variation_price);
523 $variation_price_percentage = floatval(!isset($this->combination_price_levels[$i]->variation_price_percentage) ? $this->variation_price_percentage : $this->combination_price_levels[$i]->variation_price_percentage);
525 if ($parent->prices_by_qty_list[$i]) {
531 if ($new_type ==
'TTC') {
532 $new_price = $parent->multiprices_ttc[$i];
534 $new_price = $parent->multiprices[$i];
537 if ($variation_price_percentage) {
538 if ($new_price != 0) {
539 $new_price *= 1 + ($variation_price / 100);
542 $new_price += $variation_price;
545 $ret = $child->updatePrice($new_price, $new_type, $user, $new_vat, $new_min_price, $i, $new_npr, $new_psq, 0, array(), $parent->default_vat_code);
548 $this->
db->rollback();
549 $this->error = $child->error;
550 $this->errors = $child->errors;
556 $new_type = $parent->price_base_type;
557 $new_min_price = $parent->price_min;
558 $new_psq = $parent->price_by_qty;
560 if ($new_type ==
'TTC') {
561 $new_price = $parent->price_ttc;
563 $new_price = $parent->price;
566 if ($this->variation_price_percentage) {
567 if ($new_price != 0) {
568 $new_price *= 1 + ($this->variation_price / 100);
571 $new_price += $this->variation_price;
574 $ret = $child->updatePrice($new_price, $new_type, $user, $new_vat, $new_min_price, 1, $new_npr, $new_psq);
577 $this->
db->rollback();
578 $this->error = $child->error;
579 $this->errors = $child->errors;
589 $this->
db->rollback();
590 $this->error = $child->error;
591 $this->errors = $child->errors;
604 require_once DOL_DOCUMENT_ROOT.
'/variants/class/ProductCombination2ValuePair.class.php';
606 $actual_comp = array();
611 $features = array_filter($features,
function ($v) {
615 foreach ($features as $attr => $attr_val) {
616 $actual_comp[$attr] = $attr_val;
619 foreach ($prodcomb->fetchAllByFkProductParent($prodid) as $prc) {
622 foreach ($prodcomb2val->fetchByFkCombination($prc->id) as $value) {
623 $values[$value->fk_prod_attr] = $value->fk_prod_attr_val;
626 $check1 = count(array_diff_assoc($values, $actual_comp));
627 $check2 = count(array_diff_assoc($actual_comp, $values));
629 if (!$check1 && !$check2) {
645 require_once DOL_DOCUMENT_ROOT.
'/variants/class/ProductAttribute.class.php';
646 require_once DOL_DOCUMENT_ROOT.
'/variants/class/ProductAttributeValue.class.php';
651 $sql =
"SELECT DISTINCT fk_prod_attr, a.position";
652 $sql .=
" FROM ".MAIN_DB_PREFIX.
"product_attribute_combination2val c2v LEFT JOIN ".MAIN_DB_PREFIX.
"product_attribute_combination c ON c2v.fk_prod_combination = c.rowid";
653 $sql .=
" LEFT JOIN ".MAIN_DB_PREFIX.
"product p ON p.rowid = c.fk_product_child";
654 $sql .=
" LEFT JOIN ".MAIN_DB_PREFIX.
"product_attribute a ON a.rowid = fk_prod_attr";
655 $sql .=
" WHERE c.fk_product_parent = ".((int) $productid).
" AND p.tosell = 1";
656 $sql .= $this->
db->order(
'a.position',
'asc');
658 $query = $this->
db->query($sql);
661 while ($result = $this->
db->fetch_object($query)) {
663 $attr->fetch($result->fk_prod_attr);
665 $tmp =
new stdClass();
666 $tmp->id = $attr->id;
667 $tmp->ref = $attr->ref;
668 $tmp->label = $attr->label;
669 $tmp->values = array();
672 foreach ($res = $attrval->fetchAllByProductAttribute($attr->id,
true) as $val) {
673 $tmp->values[] = $val;
705 public function createProductCombination(
User $user,
Product $product, array $combinations, array $variations, $price_var_percent =
false, $forced_pricevar =
false, $forced_weightvar =
false, $forced_refvar =
false, $ref_ext =
'')
709 require_once DOL_DOCUMENT_ROOT.
'/variants/class/ProductAttribute.class.php';
710 require_once DOL_DOCUMENT_ROOT.
'/variants/class/ProductAttributeValue.class.php';
714 $price_impact = array(1=>0);
716 $forced_refvar = trim($forced_refvar);
718 if (!empty($forced_refvar) && $forced_refvar != $product->ref) {
719 $existingProduct =
new Product($this->
db);
720 $result = $existingProduct->fetch(
'', $forced_refvar);
722 $newproduct = $existingProduct;
724 $existingProduct =
false;
725 $newproduct = clone $product;
726 $newproduct->ref = $forced_refvar;
729 $forced_refvar =
false;
730 $existingProduct =
false;
731 $newproduct = clone $product;
735 $weight_impact = (
float) $forced_weightvar;
738 if (!is_array($forced_pricevar)) {
739 $price_impact[1] = (
float) $forced_pricevar;
741 $price_impact = $forced_pricevar;
744 if (!array($price_var_percent)) {
745 $price_var_percent[1] = (
float) $price_var_percent;
749 $existingCombination = $newcomb->fetchByProductCombination2ValuePairs($product->id, $combinations);
751 if ($existingCombination) {
752 $newcomb = $existingCombination;
754 $newcomb->fk_product_parent = $product->id;
757 $result = $newcomb->create($user);
759 $this->error = $newcomb->error;
760 $this->errors = $newcomb->errors;
761 $this->
db->rollback();
771 foreach ($combinations as $currcombattr => $currcombval) {
773 $prodattr->fetch($currcombattr);
774 $prodattrval->fetch($currcombval);
777 if (!$existingCombination) {
779 $tmp->fk_prod_attr = $currcombattr;
780 $tmp->fk_prod_attr_val = $currcombval;
781 $tmp->fk_prod_combination = $newcomb->id;
783 if ($tmp->create($user) < 0) {
784 $this->error = $tmp->error;
785 $this->errors = $tmp->errors;
786 $this->
db->rollback();
791 if ($forced_weightvar ===
false) {
792 $weight_impact += (
float)
price2num($variations[$currcombattr][$currcombval][
'weight']);
794 if ($forced_pricevar ===
false) {
795 $price_impact[1] += (
float)
price2num($variations[$currcombattr][$currcombval][
'price']);
798 if ($conf->global->PRODUIT_MULTIPRICES) {
799 for ($i = 2; $i <= $conf->global->PRODUIT_MULTIPRICES_LIMIT; $i++) {
800 $price_impact[$i] += (
float)
price2num($variations[$currcombattr][$currcombval][
'price']);
805 if ($forced_refvar ===
false) {
806 if (isset($conf->global->PRODUIT_ATTRIBUTES_SEPARATOR)) {
807 $newproduct->ref .= $conf->global->PRODUIT_ATTRIBUTES_SEPARATOR.$prodattrval->ref;
809 $newproduct->ref .=
'_'.$prodattrval->ref;
814 if ($newproduct->description) {
815 $newproduct->description .=
'<br>';
817 $newproduct->description .=
'<strong>'.$prodattr->label.
':</strong> '.$prodattrval->value;
820 $newcomb->variation_price_percentage = $price_var_percent[1];
821 $newcomb->variation_price = $price_impact[1];
822 $newcomb->variation_weight = $weight_impact;
823 $newcomb->variation_ref_ext = $this->
db->escape($ref_ext);
826 if ($conf->global->PRODUIT_MULTIPRICES) {
827 for ($i = 1; $i <= $conf->global->PRODUIT_MULTIPRICES_LIMIT; $i++) {
829 $productCombinationLevel->fk_product_attribute_combination = $newcomb->id;
830 $productCombinationLevel->fk_price_level = $i;
831 $productCombinationLevel->variation_price = $price_impact[$i];
833 if (is_array($price_var_percent)) {
834 $productCombinationLevel->variation_price_percentage = (empty($price_var_percent[$i]) ? false : $price_var_percent[$i]);
836 $productCombinationLevel->variation_price_percentage = $price_var_percent;
839 $newcomb->combination_price_levels[$i] = $productCombinationLevel;
844 $newproduct->weight += $weight_impact;
848 if ($existingProduct ===
false) {
850 $newproduct->price = 0;
851 $newproduct->price_ttc = 0;
852 $newproduct->price_min = 0;
853 $newproduct->price_min_ttc = 0;
856 $newproduct->barcode = -1;
857 $result = $newproduct->create($user);
861 if ($newproduct->error !=
'ErrorProductAlreadyExists') {
862 $this->error[] = $newproduct->error;
863 $this->errors = $newproduct->errors;
864 $this->
db->rollback();
873 if ($newcomb->fk_product_child) {
874 $res = $newproduct->fetch($existingCombination->fk_product_child);
876 $orig_prod_ref = $newproduct->ref;
880 $newproduct->ref = $orig_prod_ref.$i;
881 $res = $newproduct->create($user);
883 if ($newproduct->error !=
'ErrorProductAlreadyExists') {
884 $this->errors[] = $newproduct->error;
893 $this->
db->rollback();
898 $result = $newproduct->update($newproduct->id, $user);
900 $this->
db->rollback();
905 $newcomb->fk_product_child = $newproduct->id;
907 if ($newcomb->update($user) < 0) {
908 $this->error = $newcomb->error;
909 $this->errors = $newcomb->errors;
910 $this->
db->rollback();
915 return $newproduct->id;
928 require_once DOL_DOCUMENT_ROOT.
'/variants/class/ProductCombination2ValuePair.class.php';
931 if ($origProductId == $destProduct->id) {
940 foreach ($combinations as $combination) {
941 $variations = array();
943 foreach ($prodcomb2val->fetchByFkCombination($combination->id) as $tmp_pc2v) {
944 $variations[$tmp_pc2v->fk_prod_attr] = $tmp_pc2v->fk_prod_attr_val;
952 $combination->variation_price_percentage,
953 $combination->variation_price,
954 $combination->variation_weight
971 $sql =
'SELECT pav.value AS label';
972 $sql .=
' FROM '.MAIN_DB_PREFIX.
'product_attribute_combination pac';
973 $sql .=
' INNER JOIN '.MAIN_DB_PREFIX.
'product_attribute_combination2val pac2v ON pac2v.fk_prod_combination=pac.rowid';
974 $sql .=
' INNER JOIN '.MAIN_DB_PREFIX.
'product_attribute_value pav ON pav.rowid=pac2v.fk_prod_attr_val';
975 $sql .=
' WHERE pac.fk_product_child='.((int) $prod_child);
984 $obj = $this->
db->fetch_object(
$resql);
987 $label .=
' '.$obj->label;
1013 public $table_element =
'product_attribute_combination_price_level';
1025 public $fk_product_attribute_combination;
1031 public $fk_price_level;
1037 public $variation_price;
1043 public $variation_price_percentage =
false;
1063 $sql =
"SELECT rowid, fk_product_attribute_combination, fk_price_level, variation_price, variation_price_percentage";
1064 $sql .=
" FROM ".MAIN_DB_PREFIX.$this->table_element;
1065 $sql .=
" WHERE rowid = ".(int) $rowid;
1069 $obj = $this->
db->fetch_object(
$resql);
1086 public function fetchAll($fk_product_attribute_combination, $fk_price_level = 0)
1090 $sql =
"SELECT rowid, fk_product_attribute_combination, fk_price_level, variation_price, variation_price_percentage";
1091 $sql .=
" FROM ".MAIN_DB_PREFIX.$this->table_element;
1092 $sql .=
" WHERE fk_product_attribute_combination = ".intval($fk_product_attribute_combination);
1093 if (!empty($fk_price_level)) {
1094 $sql .=
' AND fk_price_level = '.intval($fk_price_level);
1097 $res = $this->
db->query($sql);
1099 if ($this->
db->num_rows($res) > 0) {
1100 while ($obj = $this->
db->fetch_object($res)) {
1102 $productCombinationLevel->fetchFormObj($obj);
1103 $result[$obj->fk_price_level] = $productCombinationLevel;
1125 $this->
id = $obj->rowid;
1126 $this->fk_product_attribute_combination = floatval($obj->fk_product_attribute_combination);
1127 $this->fk_price_level = intval($obj->fk_price_level);
1128 $this->variation_price = floatval($obj->variation_price);
1129 $this->variation_price_percentage = (bool) $obj->variation_price_percentage;
1142 if (($this->
id > 0 && empty($this->fk_product_attribute_combination)) || empty($this->fk_price_level)) {
1147 if ($this->fk_product_attribute_combination > 0 && empty($this->
id)) {
1148 $sql =
"SELECT rowid id";
1149 $sql .=
" FROM ".MAIN_DB_PREFIX.$this->table_element;
1150 $sql .=
" WHERE fk_product_attribute_combination = ".(int) $this->fk_product_attribute_combination;
1151 $sql .=
' AND fk_price_level = '.((int) $this->fk_price_level);
1155 $obj = $this->
db->fetch_object(
$resql);
1157 $this->
id = $obj->id;
1163 if (!empty($this->
id)) {
1164 $sql =
'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1165 $sql .=
' SET variation_price = '.floatval($this->variation_price).
' , variation_price_percentage = '.intval($this->variation_price_percentage);
1166 $sql .=
' WHERE rowid = '.((int) $this->
id);
1168 $res = $this->
db->query($sql);
1172 $this->error = $this->
db->error();
1173 $this->errors[] = $this->error;
1178 $sql =
"INSERT INTO ".MAIN_DB_PREFIX.$this->table_element.
" (";
1179 $sql .=
"fk_product_attribute_combination, fk_price_level, variation_price, variation_price_percentage";
1180 $sql .=
") VALUES (";
1181 $sql .= (int) $this->fk_product_attribute_combination;
1182 $sql .=
", ".intval($this->fk_price_level);
1183 $sql .=
", ".floatval($this->variation_price);
1184 $sql .=
", ".intval($this->variation_price_percentage);
1187 $res = $this->
db->query($sql);
1189 $this->
id = $this->
db->last_insert_id(MAIN_DB_PREFIX.$this->table_element);
1191 $this->error = $this->
db->error();
1192 $this->errors[] = $this->error;
1206 public function delete()
1208 $sql =
"DELETE FROM ".MAIN_DB_PREFIX.$this->table_element.
" WHERE rowid = ".(int) $this->
id;
1209 $res = $this->
db->query($sql);
1211 return $res ? 1 : -1;
1223 $sql =
"DELETE FROM ".MAIN_DB_PREFIX.$this->table_element.
" WHERE fk_product_attribute_combination = ".(int) $fk_product_attribute_combination;
1224 $res = $this->
db->query($sql);
1226 return $res ? 1 : -1;
1236 public function clean($fk_product_attribute_combination)
1240 $sql =
"DELETE FROM ".MAIN_DB_PREFIX.$this->table_element;
1241 $sql .=
" WHERE fk_product_attribute_combination = ".(int) $fk_product_attribute_combination;
1242 $sql .=
" AND fk_price_level > ".intval($conf->global->PRODUIT_MULTIPRICES_LIMIT);
1243 $res = $this->
db->query($sql);
1245 return $res ? 1 : -1;
1259 $productCombinationLevel =
new self($db);
1260 $productCombinationLevel->fk_price_level = $fkPriceLevel;
1261 $productCombinationLevel->fk_product_attribute_combination = $productCombination->id;
1262 $productCombinationLevel->variation_price = $productCombination->variation_price;
1263 $productCombinationLevel->variation_price_percentage = (bool) $productCombination->variation_price_percentage;
1265 return $productCombinationLevel;