42 public $fk_product_parent;
48 public $fk_product_child;
54 public $variation_price;
60 public $variation_price_percentage =
false;
66 public $variation_weight;
78 public $combination_price_levels;
84 public $variation_ref_ext =
'';
94 public $errors = array();
106 $this->entity = $conf->entity;
119 $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').
")";
121 $query = $this->db->query(
$sql);
127 if (!$this->db->num_rows($query)) {
131 $obj = $this->db->fetch_object($query);
133 $this->
id = $obj->rowid;
134 $this->fk_product_parent = $obj->fk_product_parent;
135 $this->fk_product_child = $obj->fk_product_child;
136 $this->variation_price = $obj->variation_price;
137 $this->variation_price_percentage = $obj->variation_price_percentage;
138 $this->variation_weight = $obj->variation_weight;
139 $this->variation_ref_ext = $obj->variation_ref_ext;
141 if (!empty($conf->global->PRODUIT_MULTIPRICES)) {
161 if (!empty($this->combination_price_levels) && $useCache) {
162 if ((!empty($fk_price_level) && isset($this->combination_price_levels[$fk_price_level])) || empty($fk_price_level)) {
167 if (!is_array($this->combination_price_levels)
168 || empty($fk_price_level)
170 $this->combination_price_levels = array();
174 $combination_price_levels = $staticProductCombinationLevel->fetchAll($this->
id, $fk_price_level);
176 if (!is_array($combination_price_levels)) {
180 if (empty($combination_price_levels)) {
184 if ($fk_price_level > 0) {
187 for ($i = 1; $i <= $conf->global->PRODUIT_MULTIPRICES_LIMIT; $i++) {
193 $this->combination_price_levels = $combination_price_levels;
213 if (empty($this->combination_price_levels)) {
214 return $staticProductCombinationLevel->deleteAllForCombination($this->
id);
219 $res = $staticProductCombinationLevel->clean($this->
id);
221 $this->errors[] =
'Fail to clean not needed price levels';
226 foreach ($this->combination_price_levels as $fk_price_level => $combination_price_level) {
227 $res = $combination_price_level->save();
229 $this->error =
'Error saving combination price level '.$fk_price_level.
' : '.$combination_price_level->error;
230 $this->errors[] = $this->error;
254 $sql =
"SELECT rowid, fk_product_parent, fk_product_child, variation_price, variation_price_percentage, variation_weight";
255 $sql .=
" FROM ".MAIN_DB_PREFIX.
"product_attribute_combination WHERE fk_product_child = ".((int) $productid).
" AND entity IN (".
getEntity(
'product').
")";
257 $query = $this->db->query(
$sql);
263 if (!$this->db->num_rows($query)) {
267 $result = $this->db->fetch_object($query);
269 $this->
id = $result->rowid;
270 $this->fk_product_parent = $result->fk_product_parent;
271 $this->fk_product_child = $result->fk_product_child;
272 $this->variation_price = $result->variation_price;
273 $this->variation_price_percentage = $result->variation_price_percentage;
274 $this->variation_weight = $result->variation_weight;
276 if (empty($donotloadpricelevel) && !empty($conf->global->PRODUIT_MULTIPRICES)) {
280 return (
int) $this->fk_product_parent;
293 $sql =
"SELECT rowid, fk_product_parent, fk_product_child, variation_price, variation_price_percentage, variation_ref_ext, variation_weight";
294 $sql.=
" FROM ".MAIN_DB_PREFIX.
"product_attribute_combination";
295 $sql.=
" WHERE fk_product_parent = ".((int) $fk_product_parent).
" AND entity IN (".
getEntity(
'product').
")";
297 $query = $this->db->query(
$sql);
305 while ($result = $this->db->fetch_object($query)) {
307 $tmp->id = $result->rowid;
308 $tmp->fk_product_parent = $result->fk_product_parent;
309 $tmp->fk_product_child = $result->fk_product_child;
310 $tmp->variation_price = $result->variation_price;
311 $tmp->variation_price_percentage = $result->variation_price_percentage;
312 $tmp->variation_weight = $result->variation_weight;
313 $tmp->variation_ref_ext = $result->variation_ref_ext;
315 if (!empty($conf->global->PRODUIT_MULTIPRICES)) {
316 $tmp->fetchCombinationPriceLevels();
334 $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').
")";
336 $resql = $this->db->query(
$sql);
338 $obj = $this->db->fetch_object($resql);
359 $sql =
"INSERT INTO ".MAIN_DB_PREFIX.
"product_attribute_combination";
360 $sql .=
" (fk_product_parent, fk_product_child, variation_price, variation_price_percentage, variation_weight, variation_ref_ext, entity)";
361 $sql .=
" VALUES (".((int) $this->fk_product_parent).
", ".((int) $this->fk_product_child).
",";
362 $sql .= (
float) $this->variation_price.
", ".(
int) $this->variation_price_percentage.
",";
363 $sql .= (
float) $this->variation_weight.
", '".$this->db->escape($this->variation_ref_ext).
"', ".(int) $this->entity.
")";
365 $resql = $this->db->query(
$sql);
367 $this->
id = $this->db->last_insert_id(MAIN_DB_PREFIX.
'product_attribute_combination');
369 $this->error = $this->db->lasterror();
373 if (!empty($conf->global->PRODUIT_MULTIPRICES)) {
393 $sql =
"UPDATE ".MAIN_DB_PREFIX.
"product_attribute_combination";
394 $sql .=
" SET fk_product_parent = ".(int) $this->fk_product_parent.
", fk_product_child = ".(
int) $this->fk_product_child.
",";
395 $sql .=
" variation_price = ".(float) $this->variation_price.
", variation_price_percentage = ".(
int) $this->variation_price_percentage.
",";
396 $sql .=
" variation_ref_ext = '".$this->db->escape($this->variation_ref_ext).
"',";
397 $sql .=
" variation_weight = ".(float) $this->variation_weight.
" WHERE rowid = ".((
int) $this->id);
399 $resql = $this->db->query(
$sql);
404 if (!empty($conf->global->PRODUIT_MULTIPRICES)) {
411 $parent =
new Product($this->db);
412 $parent->fetch($this->fk_product_parent);
425 public function delete(
User $user)
430 $comb2val->deleteByFkCombination($this->
id);
433 if (!$this->db->query(
"DELETE FROM ".MAIN_DB_PREFIX.
"product_attribute_combination_price_level WHERE fk_product_attribute_combination = ".(
int) $this->id)) {
434 $this->db->rollback();
438 $sql =
"DELETE FROM ".MAIN_DB_PREFIX.
"product_attribute_combination WHERE rowid = ".(int) $this->
id;
440 if ($this->db->query(
$sql)) {
445 $this->db->rollback();
461 $prodstatic =
new Product($this->db);
463 $res = $prodstatic->fetch($prodcomb->fk_product_child);
466 $res = $prodcomb->delete($user);
469 if ($res > 0 && !$prodstatic->isObjectUsed($prodstatic->id)) {
470 $res = $prodstatic->delete($user);
474 $this->db->rollback();
497 $child =
new Product($this->db);
498 $child->fetch($this->fk_product_child);
500 $child->price_autogen = $parent->price_autogen;
501 $child->weight = $parent->weight;
503 if (!empty($parent->oldcopy) && ($parent->status != $parent->oldcopy->status)) {
504 $child->status = $parent->status;
506 if (!empty($parent->oldcopy) && ($parent->status_buy != $parent->oldcopy->status_buy)) {
507 $child->status_buy = $parent->status_buy;
510 if ($this->variation_weight) {
511 $child->weight = ($child->weight ? $child->weight : 0) + $this->variation_weight;
513 $child->weight_units = $parent->weight_units;
516 if ($child->label == $parent->label) {
519 $child->label = $parent->label.$varlabel;
523 if ($child->update($child->id, $user) > 0) {
524 $new_vat = $parent->tva_tx;
525 $new_npr = $parent->tva_npr;
528 if (!empty($conf->global->PRODUIT_MULTIPRICES)) {
529 for ($i = 1; $i <= $conf->global->PRODUIT_MULTIPRICES_LIMIT; $i++) {
530 if ($parent->multiprices[$i] !=
'' || isset($this->combination_price_levels[$i]->variation_price)) {
531 $new_type = empty($parent->multiprices_base_type[$i]) ?
'HT' : $parent->multiprices_base_type[$i];
532 $new_min_price = $parent->multiprices_min[$i];
533 $variation_price = floatval(!isset($this->combination_price_levels[$i]->variation_price) ? $this->variation_price : $this->combination_price_levels[$i]->variation_price);
534 $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);
536 if ($parent->prices_by_qty_list[$i]) {
542 if ($new_type ==
'TTC') {
543 $new_price = $parent->multiprices_ttc[$i];
545 $new_price = $parent->multiprices[$i];
548 if ($variation_price_percentage) {
549 if ($new_price != 0) {
550 $new_price *= 1 + ($variation_price / 100);
553 $new_price += $variation_price;
556 $ret = $child->updatePrice($new_price, $new_type, $user, $new_vat, $new_min_price, $i, $new_npr, $new_psq, 0, array(), $parent->default_vat_code);
559 $this->db->rollback();
560 $this->error = $child->error;
561 $this->errors = $child->errors;
567 $new_type = $parent->price_base_type;
568 $new_min_price = $parent->price_min;
569 $new_psq = $parent->price_by_qty;
571 if ($new_type ==
'TTC') {
572 $new_price = $parent->price_ttc;
574 $new_price = $parent->price;
577 if ($this->variation_price_percentage) {
578 if ($new_price != 0) {
579 $new_price *= 1 + ($this->variation_price / 100);
582 $new_price += $this->variation_price;
585 $ret = $child->updatePrice($new_price, $new_type, $user, $new_vat, $new_min_price, 1, $new_npr, $new_psq);
588 $this->db->rollback();
589 $this->error = $child->error;
590 $this->errors = $child->errors;
600 $this->db->rollback();
601 $this->error = $child->error;
602 $this->errors = $child->errors;
615 require_once DOL_DOCUMENT_ROOT.
'/variants/class/ProductCombination2ValuePair.class.php';
617 $actual_comp = array();
622 $features = array_filter($features,
function ($v) {
626 foreach ($features as $attr => $attr_val) {
627 $actual_comp[$attr] = $attr_val;
630 foreach ($prodcomb->fetchAllByFkProductParent($prodid) as $prc) {
633 foreach ($prodcomb2val->fetchByFkCombination($prc->id) as $value) {
634 $values[$value->fk_prod_attr] = $value->fk_prod_attr_val;
637 $check1 = count(array_diff_assoc($values, $actual_comp));
638 $check2 = count(array_diff_assoc($actual_comp, $values));
640 if (!$check1 && !$check2) {
656 require_once DOL_DOCUMENT_ROOT.
'/variants/class/ProductAttribute.class.php';
657 require_once DOL_DOCUMENT_ROOT.
'/variants/class/ProductAttributeValue.class.php';
662 $sql =
"SELECT DISTINCT fk_prod_attr, a.position";
663 $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";
664 $sql .=
" LEFT JOIN ".MAIN_DB_PREFIX.
"product p ON p.rowid = c.fk_product_child";
665 $sql .=
" LEFT JOIN ".MAIN_DB_PREFIX.
"product_attribute a ON a.rowid = fk_prod_attr";
666 $sql .=
" WHERE c.fk_product_parent = ".((int) $productid).
" AND p.tosell = 1";
667 $sql .= $this->db->order(
'a.position',
'asc');
669 $query = $this->db->query(
$sql);
672 while ($result = $this->db->fetch_object($query)) {
674 $attr->fetch($result->fk_prod_attr);
676 $tmp =
new stdClass();
677 $tmp->id = $attr->id;
678 $tmp->ref = $attr->ref;
679 $tmp->label = $attr->label;
680 $tmp->values = array();
683 foreach ($res = $attrval->fetchAllByProductAttribute($attr->id,
true) as $val) {
684 $tmp->values[] = $val;
716 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 =
'')
720 require_once DOL_DOCUMENT_ROOT.
'/variants/class/ProductAttribute.class.php';
721 require_once DOL_DOCUMENT_ROOT.
'/variants/class/ProductAttributeValue.class.php';
725 $price_impact = array(1=>0);
727 $forced_refvar = trim($forced_refvar);
729 if (!empty($forced_refvar) && $forced_refvar != $product->ref) {
730 $existingProduct =
new Product($this->db);
731 $result = $existingProduct->fetch(
'', $forced_refvar);
733 $newproduct = $existingProduct;
735 $existingProduct =
false;
736 $newproduct = clone $product;
737 $newproduct->ref = $forced_refvar;
740 $forced_refvar =
false;
741 $existingProduct =
false;
742 $newproduct = clone $product;
746 $weight_impact = (
float) $forced_weightvar;
749 if (!is_array($forced_pricevar)) {
750 $price_impact[1] = (
float) $forced_pricevar;
752 $price_impact = $forced_pricevar;
755 if (!array($price_var_percent)) {
756 $price_var_percent[1] = (
float) $price_var_percent;
760 $existingCombination = $newcomb->fetchByProductCombination2ValuePairs($product->id, $combinations);
762 if ($existingCombination) {
763 $newcomb = $existingCombination;
765 $newcomb->fk_product_parent = $product->id;
768 $result = $newcomb->create($user);
770 $this->error = $newcomb->error;
771 $this->errors = $newcomb->errors;
772 $this->db->rollback();
782 foreach ($combinations as $currcombattr => $currcombval) {
784 $prodattr->fetch($currcombattr);
785 $prodattrval->fetch($currcombval);
788 if (!$existingCombination) {
790 $tmp->fk_prod_attr = $currcombattr;
791 $tmp->fk_prod_attr_val = $currcombval;
792 $tmp->fk_prod_combination = $newcomb->id;
794 if ($tmp->create($user) < 0) {
795 $this->error = $tmp->error;
796 $this->errors = $tmp->errors;
797 $this->db->rollback();
802 if ($forced_weightvar ===
false) {
803 $weight_impact += (
float)
price2num($variations[$currcombattr][$currcombval][
'weight']);
805 if ($forced_pricevar ===
false) {
806 $price_impact[1] += (
float)
price2num($variations[$currcombattr][$currcombval][
'price']);
809 if ($conf->global->PRODUIT_MULTIPRICES) {
810 for ($i = 2; $i <= $conf->global->PRODUIT_MULTIPRICES_LIMIT; $i++) {
811 $price_impact[$i] += (
float)
price2num($variations[$currcombattr][$currcombval][
'price']);
816 if ($forced_refvar ===
false) {
817 if (isset($conf->global->PRODUIT_ATTRIBUTES_SEPARATOR)) {
818 $newproduct->ref .= $conf->global->PRODUIT_ATTRIBUTES_SEPARATOR.$prodattrval->ref;
820 $newproduct->ref .=
'_'.$prodattrval->ref;
825 if ($newproduct->description) {
826 $newproduct->description .=
'<br>';
828 $newproduct->description .=
'<strong>'.$prodattr->label.
':</strong> '.$prodattrval->value;
831 $newcomb->variation_price_percentage = $price_var_percent[1];
832 $newcomb->variation_price = $price_impact[1];
833 $newcomb->variation_weight = $weight_impact;
834 $newcomb->variation_ref_ext = $this->db->escape($ref_ext);
837 if ($conf->global->PRODUIT_MULTIPRICES) {
838 for ($i = 1; $i <= $conf->global->PRODUIT_MULTIPRICES_LIMIT; $i++) {
840 $productCombinationLevel->fk_product_attribute_combination = $newcomb->id;
841 $productCombinationLevel->fk_price_level = $i;
842 $productCombinationLevel->variation_price = $price_impact[$i];
844 if (is_array($price_var_percent)) {
845 $productCombinationLevel->variation_price_percentage = (empty($price_var_percent[$i]) ? false : $price_var_percent[$i]);
847 $productCombinationLevel->variation_price_percentage = $price_var_percent;
850 $newcomb->combination_price_levels[$i] = $productCombinationLevel;
855 $newproduct->weight += $weight_impact;
859 if ($existingProduct ===
false) {
861 $newproduct->price = 0;
862 $newproduct->price_ttc = 0;
863 $newproduct->price_min = 0;
864 $newproduct->price_min_ttc = 0;
867 $newproduct->barcode = -1;
868 $result = $newproduct->create($user);
872 if ($newproduct->error !=
'ErrorProductAlreadyExists') {
873 $this->error[] = $newproduct->error;
874 $this->errors = $newproduct->errors;
875 $this->db->rollback();
884 if ($newcomb->fk_product_child) {
885 $res = $newproduct->fetch($existingCombination->fk_product_child);
887 $orig_prod_ref = $newproduct->ref;
891 $newproduct->ref = $orig_prod_ref.$i;
892 $res = $newproduct->create($user);
894 if ($newproduct->error !=
'ErrorProductAlreadyExists') {
895 $this->errors[] = $newproduct->error;
904 $this->db->rollback();
909 $result = $newproduct->update($newproduct->id, $user);
911 $this->db->rollback();
916 $newcomb->fk_product_child = $newproduct->id;
918 if ($newcomb->update($user) < 0) {
919 $this->error = $newcomb->error;
920 $this->errors = $newcomb->errors;
921 $this->db->rollback();
926 return $newproduct->id;
939 require_once DOL_DOCUMENT_ROOT.
'/variants/class/ProductCombination2ValuePair.class.php';
942 if ($origProductId == $destProduct->id) {
951 foreach ($combinations as $combination) {
952 $variations = array();
954 foreach ($prodcomb2val->fetchByFkCombination($combination->id) as $tmp_pc2v) {
955 $variations[$tmp_pc2v->fk_prod_attr] = $tmp_pc2v->fk_prod_attr_val;
963 $combination->variation_price_percentage,
964 $combination->variation_price,
965 $combination->variation_weight
982 $sql =
'SELECT pav.value AS label';
983 $sql .=
' FROM '.MAIN_DB_PREFIX.
'product_attribute_combination pac';
984 $sql .=
' INNER JOIN '.MAIN_DB_PREFIX.
'product_attribute_combination2val pac2v ON pac2v.fk_prod_combination=pac.rowid';
985 $sql .=
' INNER JOIN '.MAIN_DB_PREFIX.
'product_attribute_value pav ON pav.rowid=pac2v.fk_prod_attr_val';
986 $sql .=
' WHERE pac.fk_product_child='.((int) $prod_child);
988 $resql = $this->db->query(
$sql);
990 $num = $this->db->num_rows($resql);
995 $obj = $this->db->fetch_object($resql);
998 $label .=
' '.$obj->label;
1024 public $table_element =
'product_attribute_combination_price_level';
1036 public $fk_product_attribute_combination;
1042 public $fk_price_level;
1048 public $variation_price;
1054 public $variation_price_percentage =
false;
1064 public $errors = array();
1084 $sql =
"SELECT rowid, fk_product_attribute_combination, fk_price_level, variation_price, variation_price_percentage";
1085 $sql .=
" FROM ".MAIN_DB_PREFIX.$this->table_element;
1086 $sql .=
" WHERE rowid = ".(int) $rowid;
1088 $resql = $this->db->query(
$sql);
1090 $obj = $this->db->fetch_object($resql);
1107 public function fetchAll($fk_product_attribute_combination, $fk_price_level = 0)
1111 $sql =
"SELECT rowid, fk_product_attribute_combination, fk_price_level, variation_price, variation_price_percentage";
1112 $sql .=
" FROM ".MAIN_DB_PREFIX.$this->table_element;
1113 $sql .=
" WHERE fk_product_attribute_combination = ".intval($fk_product_attribute_combination);
1114 if (!empty($fk_price_level)) {
1115 $sql .=
' AND fk_price_level = '.intval($fk_price_level);
1118 $res = $this->db->query(
$sql);
1120 if ($this->db->num_rows($res) > 0) {
1121 while ($obj = $this->db->fetch_object($res)) {
1123 $productCombinationLevel->fetchFormObj($obj);
1124 $result[$obj->fk_price_level] = $productCombinationLevel;
1146 $this->
id = $obj->rowid;
1147 $this->fk_product_attribute_combination = floatval($obj->fk_product_attribute_combination);
1148 $this->fk_price_level = intval($obj->fk_price_level);
1149 $this->variation_price = floatval($obj->variation_price);
1150 $this->variation_price_percentage = (bool) $obj->variation_price_percentage;
1163 if (($this->
id > 0 && empty($this->fk_product_attribute_combination)) || empty($this->fk_price_level)) {
1168 if ($this->fk_product_attribute_combination > 0 && empty($this->
id)) {
1169 $sql =
"SELECT rowid id";
1170 $sql .=
" FROM ".MAIN_DB_PREFIX.$this->table_element;
1171 $sql .=
" WHERE fk_product_attribute_combination = ".(int) $this->fk_product_attribute_combination;
1172 $sql .=
' AND fk_price_level = '.((int) $this->fk_price_level);
1174 $resql = $this->db->query(
$sql);
1176 $obj = $this->db->fetch_object($resql);
1178 $this->
id = $obj->id;
1184 if (!empty($this->
id)) {
1185 $sql =
'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1186 $sql .=
' SET variation_price = '.floatval($this->variation_price).
' , variation_price_percentage = '.intval($this->variation_price_percentage);
1187 $sql .=
' WHERE rowid = '.((int) $this->
id);
1189 $res = $this->db->query(
$sql);
1193 $this->error = $this->db->error();
1194 $this->errors[] = $this->error;
1199 $sql =
"INSERT INTO ".MAIN_DB_PREFIX.$this->table_element.
" (";
1200 $sql .=
"fk_product_attribute_combination, fk_price_level, variation_price, variation_price_percentage";
1201 $sql .=
") VALUES (";
1202 $sql .= (int) $this->fk_product_attribute_combination;
1203 $sql .=
", ".intval($this->fk_price_level);
1204 $sql .=
", ".floatval($this->variation_price);
1205 $sql .=
", ".intval($this->variation_price_percentage);
1208 $res = $this->db->query(
$sql);
1210 $this->
id = $this->db->last_insert_id(MAIN_DB_PREFIX.$this->table_element);
1212 $this->error = $this->db->error();
1213 $this->errors[] = $this->error;
1227 public function delete()
1229 $sql =
"DELETE FROM ".MAIN_DB_PREFIX.$this->table_element.
" WHERE rowid = ".(int) $this->
id;
1230 $res = $this->db->query(
$sql);
1232 return $res ? 1 : -1;
1244 $sql =
"DELETE FROM ".MAIN_DB_PREFIX.$this->table_element.
" WHERE fk_product_attribute_combination = ".(int) $fk_product_attribute_combination;
1245 $res = $this->db->query(
$sql);
1247 return $res ? 1 : -1;
1257 public function clean($fk_product_attribute_combination)
1261 $sql =
"DELETE FROM ".MAIN_DB_PREFIX.$this->table_element;
1262 $sql .=
" WHERE fk_product_attribute_combination = ".(int) $fk_product_attribute_combination;
1263 $sql .=
" AND fk_price_level > ".intval($conf->global->PRODUIT_MULTIPRICES_LIMIT);
1264 $res = $this->db->query(
$sql);
1266 return $res ? 1 : -1;
1280 $productCombinationLevel =
new self($db);
1281 $productCombinationLevel->fk_price_level = $fkPriceLevel;
1282 $productCombinationLevel->fk_product_attribute_combination = $productCombination->id;
1283 $productCombinationLevel->variation_price = $productCombination->variation_price;
1284 $productCombinationLevel->variation_price_percentage = (bool) $productCombination->variation_price_percentage;
1286 return $productCombinationLevel;