dolibarr  13.0.2
combinations.php
1 <?php
2 /* Copyright (C) 2016 Marcos García <marcosgdf@gmail.com>
3  * Copyright (C) 2017 Laurent Destailleur <eldy@users.sourceforge.net>
4  * Copyright (C) 2018-2019 Frédéric France <frederic.france@netlogic.fr>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see <https://www.gnu.org/licenses/>.
18  */
19 
20 require '../main.inc.php';
21 require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
22 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
23 require_once DOL_DOCUMENT_ROOT.'/variants/class/ProductAttribute.class.php';
24 require_once DOL_DOCUMENT_ROOT.'/variants/class/ProductAttributeValue.class.php';
25 require_once DOL_DOCUMENT_ROOT.'/variants/class/ProductCombination.class.php';
26 require_once DOL_DOCUMENT_ROOT.'/variants/class/ProductCombination2ValuePair.class.php';
27 
28 $langs->loadLangs(array("products", "other"));
29 
30 $id = GETPOST('id', 'int');
31 $valueid = GETPOST('valueid', 'int');
32 $ref = GETPOST('ref', 'alpha');
33 $weight_impact = GETPOST('weight_impact', 'alpha');
34 $price_impact = GETPOST('price_impact', 'alpha');
35 $price_impact_percent = (bool) GETPOST('price_impact_percent');
36 
37 $level_price_impact = GETPOST('level_price_impact', 'array');
38 $level_price_impact_percent = GETPOST('level_price_impact_percent', 'array');
39 
40 $reference = GETPOST('reference', 'alpha');
41 $form = new Form($db);
42 
43 $action = GETPOST('action', 'aZ09');
44 $massaction = GETPOST('massaction', 'alpha');
45 $show_files = GETPOST('show_files', 'int');
46 $confirm = GETPOST('confirm', 'alpha');
47 $toselect = GETPOST('toselect', 'array');
48 $cancel = GETPOST('cancel', 'alpha');
49 $delete_product = GETPOST('delete_product', 'alpha');
50 
51 // Security check
52 $fieldvalue = (!empty($id) ? $id : $ref);
53 $fieldtype = (!empty($ref) ? 'ref' : 'rowid');
54 $result = restrictedArea($user, 'produit|service', $fieldvalue, 'product&product', '', '', $fieldtype);
55 
56 $prodstatic = new Product($db);
57 $prodattr = new ProductAttribute($db);
58 $prodattr_val = new ProductAttributeValue($db);
59 
60 $object = new Product($db);
61 if ($id > 0 || $ref)
62 {
63  $object->fetch($id, $ref);
64 }
65 
66 $selectedvariant = $_SESSION['addvariant_'.$object->id];
67 
68 
69 /*
70  * Actions
71  */
72 
73 if ($cancel) {
74  $action = '';
75  $massaction = '';
76  unset($_SESSION['addvariant_'.$object->id]);
77 }
78 
79 if (!$object->isProduct() && !$object->isService()) {
80  header('Location: '.dol_buildpath('/product/card.php?id='.$object->id, 2));
81  exit();
82 }
83 if ($action == 'add')
84 {
85  unset($selectedvariant);
86  unset($_SESSION['addvariant_'.$object->id]);
87 }
88 if ($action == 'create' && GETPOST('selectvariant', 'alpha')) // We click on select combination
89 {
90  $action = 'add';
91  if (GETPOST('attribute') != '-1' && GETPOST('value') != '-1')
92  {
93  $selectedvariant[GETPOST('attribute').':'.GETPOST('value')] = GETPOST('attribute').':'.GETPOST('value');
94  $_SESSION['addvariant_'.$object->id] = $selectedvariant;
95  }
96 }
97 
98 
99 $prodcomb = new ProductCombination($db);
100 $prodcomb2val = new ProductCombination2ValuePair($db);
101 
102 $productCombination2ValuePairs1 = array();
103 
104 if (($action == 'add' || $action == 'create') && empty($massaction) && !GETPOST('selectvariant', 'alpha')) // We click on Create all defined combinations
105 {
106  //$features = GETPOST('features', 'array');
107  $features = $_SESSION['addvariant_'.$object->id];
108 
109  if (!$features) {
110  if ($action == 'create') {
111  setEventMessages($langs->trans('ErrorFieldsRequired'), null, 'errors');
112  }
113  } else {
114  $reference = trim($reference);
115  if (empty($reference)) {
116  $reference = false;
117  }
118  $weight_impact = price2num($weight_impact);
119  $price_impact = price2num($price_impact);
120 
121  // for conf PRODUIT_MULTIPRICES
122  if ($conf->global->PRODUIT_MULTIPRICES) {
123  $level_price_impact = array_map('price2num', $level_price_impact);
124  }
125  else {
126  $level_price_impact = array(1 => $price_impact);
127  $level_price_impact_percent = array(1 => $price_impact_percent);
128  }
129 
130  $sanit_features = array();
131 
132  //First, sanitize
133  foreach ($features as $feature) {
134  $explode = explode(':', $feature);
135 
136  if ($prodattr->fetch($explode[0]) < 0) {
137  continue;
138  }
139 
140  if ($prodattr_val->fetch($explode[1]) < 0) {
141  continue;
142  }
143 
144  // Valuepair
145  $sanit_features[$explode[0]] = $explode[1];
146 
147  $tmp = new ProductCombination2ValuePair($db);
148  $tmp->fk_prod_attr = $explode[0];
149  $tmp->fk_prod_attr_val = $explode[1];
150 
151  $productCombination2ValuePairs1[] = $tmp;
152  }
153 
154  $db->begin();
155 
156  // sanit_feature is an array with 1 (and only 1) value per attribute.
157  // For example: Color->blue, Size->Small, Option->2
158  //var_dump($sanit_features);
159  if (!$prodcomb->fetchByProductCombination2ValuePairs($id, $sanit_features))
160  {
161  $result = $prodcomb->createProductCombination($user, $object, $sanit_features, array(), $level_price_impact_percent, $level_price_impact, $weight_impact, $reference);
162  if ($result > 0)
163  {
164  setEventMessages($langs->trans('RecordSaved'), null, 'mesgs');
165  unset($_SESSION['addvariant_'.$object->id]);
166 
167  $db->commit();
168  header('Location: '.dol_buildpath('/variants/combinations.php?id='.$id, 2));
169  exit();
170  } else {
171  $langs->load("errors");
172  setEventMessages($prodcomb->error, $prodcomb->errors, 'errors');
173  }
174  } else {
175  setEventMessages($langs->trans('ErrorRecordAlreadyExists'), null, 'errors');
176  }
177 
178  $db->rollback();
179  }
180 } elseif (!empty($massaction)) {
181  $bulkaction = $massaction;
182  $error = 0;
183 
184 
185 
186  $db->begin();
187 
188  foreach ($toselect as $prodid) {
189  // need create new of Product to prevent rename dir behavior
190  $prodstatic = new Product($db);
191 
192  if ($prodstatic->fetch($prodid) < 0) {
193  continue;
194  }
195 
196  if ($bulkaction == 'on_sell') {
197  $prodstatic->status = 1;
198  $res = $prodstatic->update($prodstatic->id, $user);
199  } elseif ($bulkaction == 'on_buy') {
200  $prodstatic->status_buy = 1;
201  $res = $prodstatic->update($prodstatic->id, $user);
202  } elseif ($bulkaction == 'not_sell') {
203  $prodstatic->status = 0;
204  $res = $prodstatic->update($prodstatic->id, $user);
205  } elseif ($bulkaction == 'not_buy') {
206  $prodstatic->status_buy = 0;
207  $res = $prodstatic->update($prodstatic->id, $user);
208  } elseif ($bulkaction == 'delete') {
209  $res = $prodstatic->delete($user, $prodstatic->id);
210  } else {
211  break;
212  }
213 
214  if ($res <= 0) {
215  $error++;
216  break;
217  }
218  }
219 
220  if ($error) {
221  $db->rollback();
222 
223  if ($prodstatic->error) {
224  setEventMessages($prodstatic->error, $prodstatic->errors, 'errors');
225  } else {
226  setEventMessages($langs->trans('CoreErrorMessage'), null, 'errors');
227  }
228  } else {
229  $db->commit();
230  setEventMessages($langs->trans('RecordSaved'), null, 'mesgs');
231  }
232 } elseif ($action === 'update' && $valueid > 0) {
233  if ($prodcomb->fetch($valueid) < 0) {
234  dol_print_error($db, $langs->trans('ErrorRecordNotFound'));
235  exit();
236  }
237 
238  $prodcomb->variation_weight = $weight_impact;
239 
240  // for conf PRODUIT_MULTIPRICES
241  if ($conf->global->PRODUIT_MULTIPRICES) {
242  $level_price_impact = array_map('price2num', $level_price_impact);
243 
244  $prodcomb->variation_price = $level_price_impact[1];
245  $prodcomb->variation_price_percentage = (bool) $level_price_impact_percent[1];
246  }
247  else {
248  $level_price_impact = array(1 => $price_impact);
249  $level_price_impact_percent = array(1 => $price_impact_percent);
250 
251  $prodcomb->variation_price = $price_impact;
252  $prodcomb->variation_price_percentage = $price_impact_percent;
253  }
254 
255  if ($conf->global->PRODUIT_MULTIPRICES) {
256  $prodcomb->combination_price_levels = array();
257  for ($i = 1; $i <= $conf->global->PRODUIT_MULTIPRICES_LIMIT; $i++) {
258  $productCombinationLevel = new ProductCombinationLevel($db);
259  $productCombinationLevel->fk_product_attribute_combination = $prodcomb->id;
260  $productCombinationLevel->fk_price_level = $i;
261  $productCombinationLevel->variation_price = $level_price_impact[$i];
262  $productCombinationLevel->variation_price_percentage = (bool) $level_price_impact_percent[$i];
263  $prodcomb->combination_price_levels[$i] = $productCombinationLevel;
264  }
265  }
266 
267  if ($prodcomb->update($user) > 0) {
268  setEventMessages($langs->trans('RecordSaved'), null, 'mesgs');
269  header('Location: '.dol_buildpath('/variants/combinations.php?id='.$id, 2));
270  exit();
271  } else {
272  setEventMessages($prodcomb->error, $prodcomb->errors, 'errors');
273  }
274 }
275 
276 
277 // Reload variants
278 $productCombinations = $prodcomb->fetchAllByFkProductParent($object->id);
279 
280 if ($action === 'confirm_deletecombination') {
281  if ($prodcomb->fetch($valueid) > 0) {
282  $db->begin();
283 
284  if ($prodcomb->delete($user) > 0 && (empty($delete_product) || ($delete_product == 'on' && $prodstatic->fetch($prodcomb->fk_product_child) > 0 && $prodstatic->delete($user) > 0))) {
285  $db->commit();
286  setEventMessages($langs->trans('RecordSaved'), null, 'mesgs');
287  header('Location: '.dol_buildpath('/variants/combinations.php?id='.$object->id, 2));
288  exit();
289  }
290 
291  $db->rollback();
292  setEventMessages($langs->trans('ProductCombinationAlreadyUsed'), null, 'errors');
293  $action = '';
294  }
295 } elseif ($action === 'edit') {
296  if ($prodcomb->fetch($valueid) < 0) {
297  dol_print_error($db, $langs->trans('ErrorRecordNotFound'));
298  exit();
299  }
300 
301  $weight_impact = $prodcomb->variation_weight;
302  $price_impact = $prodcomb->variation_price;
303  $price_impact_percent = $prodcomb->variation_price_percentage;
304 
305  $productCombination2ValuePairs1 = $prodcomb2val->fetchByFkCombination($valueid);
306 } elseif ($action === 'confirm_copycombination') {
307  //Check destination product
308  $dest_product = GETPOST('dest_product');
309 
310  if ($prodstatic->fetch('', $dest_product) > 0) {
311  //To prevent from copying to the same product
312  if ($prodstatic->ref != $object->ref) {
313  if ($prodcomb->copyAll($user, $object->id, $prodstatic) > 0) {
314  header('Location: '.dol_buildpath('/variants/combinations.php?id='.$prodstatic->id, 2));
315  exit();
316  } else {
317  setEventMessages($langs->trans('ErrorCopyProductCombinations'), null, 'errors');
318  }
319  }
320  } else {
321  setEventMessages($langs->trans('ErrorDestinationProductNotFound'), null, 'errors');
322  }
323 }
324 
325 
326 
327 /*
328  * View
329  */
330 
331 $form = new Form($db);
332 
333 if (!empty($id) || !empty($ref))
334 {
335  llxHeader("", "", $langs->trans("CardProduct".$object->type));
336 
337  $showbarcode = empty($conf->barcode->enabled) ? 0 : 1;
338  if (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && empty($user->rights->barcode->lire_advance)) $showbarcode = 0;
339 
340  $head = product_prepare_head($object);
341  $titre = $langs->trans("CardProduct".$object->type);
342  $picto = ($object->type == Product::TYPE_SERVICE ? 'service' : 'product');
343 
344  print dol_get_fiche_head($head, 'combinations', $titre, -1, $picto);
345 
346  $linkback = '<a href="'.DOL_URL_ROOT.'/product/list.php?type='.$object->type.'">'.$langs->trans("BackToList").'</a>';
347  $object->next_prev_filter = " fk_product_type = ".$object->type;
348 
349  dol_banner_tab($object, 'ref', $linkback, ($user->socid ? 0 : 1), 'ref', '', '', '', 0, '', '', 1);
350 
351  print '<div class="fichecenter">';
352 
353  print '<div class="underbanner clearboth"></div>';
354  print '<table class="border tableforfield" width="100%">';
355 
356  // TVA
357  print '<tr><td class="titlefield">'.$langs->trans("DefaultTaxRate").'</td><td>';
358 
359  $positiverates = '';
360  if (price2num($object->tva_tx)) $positiverates .= ($positiverates ? '/' : '').price2num($object->tva_tx);
361  if (price2num($object->localtax1_type)) $positiverates .= ($positiverates ? '/' : '').price2num($object->localtax1_tx);
362  if (price2num($object->localtax2_type)) $positiverates .= ($positiverates ? '/' : '').price2num($object->localtax2_tx);
363  if (empty($positiverates)) $positiverates = '0';
364  echo vatrate($positiverates.($object->default_vat_code ? ' ('.$object->default_vat_code.')' : ''), '%', $object->tva_npr);
365  /*
366  if ($object->default_vat_code)
367  {
368  print vatrate($object->tva_tx, true) . ' ('.$object->default_vat_code.')';
369  }
370  else print vatrate($object->tva_tx, true, $object->tva_npr, true);*/
371  print '</td></tr>';
372 
373  // Price
374  print '<tr><td>'.$langs->trans("SellingPrice").'</td><td>';
375  if ($object->price_base_type == 'TTC') {
376  print price($object->price_ttc).' '.$langs->trans($object->price_base_type);
377  } else {
378  print price($object->price).' '.$langs->trans($object->price_base_type);
379  }
380  print '</td></tr>';
381 
382  // Price minimum
383  print '<tr><td>'.$langs->trans("MinPrice").'</td><td>';
384  if ($object->price_base_type == 'TTC') {
385  print price($object->price_min_ttc).' '.$langs->trans($object->price_base_type);
386  } else {
387  print price($object->price_min).' '.$langs->trans($object->price_base_type);
388  }
389  print '</td></tr>';
390 
391  // Weight
392  print '<tr><td>'.$langs->trans("Weight").'</td><td>';
393  if ($object->weight != '')
394  {
395  print $object->weight." ".measuringUnitString(0, "weight", $object->weight_units);
396  } else {
397  print '&nbsp;';
398  }
399  print "</td></tr>\n";
400 
401 
402 
403 
404  print "</table>\n";
405 
406  print '</div>';
407  print '<div style="clear:both"></div>';
408 
409  print dol_get_fiche_end();
410 
411  $listofvariantselected = '';
412 
413  // Create or edit a varian
414  if ($action == 'add' || ($action == 'edit')) {
415  if ($action == 'add') {
416  $title = $langs->trans('NewProductCombination');
417  // print dol_get_fiche_head();
418  $features = $_SESSION['addvariant_'.$object->id];
419  //First, sanitize
420  $listofvariantselected = '<div id="parttoaddvariant">';
421  if (!empty($features)) {
422  foreach ($features as $feature) {
423  $explode = explode(':', $feature);
424 
425  if ($prodattr->fetch($explode[0]) < 0) {
426  continue;
427  }
428 
429  if ($prodattr_val->fetch($explode[1]) < 0) {
430  continue;
431  }
432 
433  $listofvariantselected .= '<i>'.$prodattr->label.'</i>:'.$prodattr_val->value.' ';
434  }
435  }
436  $listofvariantselected .= '</div>';
437  //print dol_get_fiche_end();
438  } else {
439  $title = $langs->trans('EditProductCombination');
440  }
441 
442  if ($action == 'add') {
443  $prodattr_all = $prodattr->fetchAll();
444 
445  if (!$selected) {
446  $selected = $prodattr_all[key($prodattr_all)]->id;
447  }
448 
449  $prodattr_alljson = array();
450 
451  foreach ($prodattr_all as $each) {
452  $prodattr_alljson[$each->id] = $each;
453  }
454 
455  ?>
456 
457  <script type="text/javascript">
458 
459  variants_available = <?php echo json_encode($prodattr_alljson); ?>;
460  variants_selected = {
461  index: [],
462  info: []
463  };
464 
465  <?php
466  foreach ($productCombination2ValuePairs1 as $pc2v) {
467  $prodattr_val->fetch($pc2v->fk_prod_attr_val);
468  ?>
469  variants_selected.index.push(<?php echo $pc2v->fk_prod_attr ?>);
470  variants_selected.info[<?php echo $pc2v->fk_prod_attr ?>] = {
471  attribute: variants_available[<?php echo $pc2v->fk_prod_attr ?>],
472  value: {
473  id: <?php echo $pc2v->fk_prod_attr_val ?>,
474  label: '<?php echo $prodattr_val->value ?>'
475  }
476  };
477  <?php
478  }
479  ?>
480 
481  restoreAttributes = function() {
482  jQuery("select[name=attribute]").empty().append('<option value="-1">&nbsp;</option>');
483 
484  jQuery.each(variants_available, function (key, val) {
485  if (jQuery.inArray(val.id, variants_selected.index) == -1) {
486  jQuery("select[name=attribute]").append('<option value="' + val.id + '">' + val.label + '</option>');
487  }
488  });
489  };
490 
491 
492  jQuery(document).ready(function() {
493  jQuery("select#attribute").change(function () {
494  console.log("Change of field variant attribute");
495  var select = jQuery("select#value");
496 
497  if (!jQuery(this).val().length || jQuery(this).val() == '-1') {
498  select.empty();
499  select.append('<option value="-1">&nbsp;</option>');
500  return;
501  }
502 
503  select.empty().append('<option value="">Loading...</option>');
504 
505  jQuery.getJSON("ajax/get_attribute_values.php", {
506  id: jQuery(this).val()
507  }, function(data) {
508  if (data.error) {
509  select.empty();
510  select.append('<option value="-1">&nbsp;</option>');
511  return alert(data.error);
512  }
513 
514  select.empty();
515  select.append('<option value="-1">&nbsp;</option>');
516 
517  jQuery(data).each(function (key, val) {
518  keyforoption = val.id
519  valforoption = val.value
520  select.append('<option value="' + keyforoption + '">' + valforoption + '</option>');
521  });
522  });
523  });
524  });
525  </script>
526 
527  <?php
528  }
529 
530  print '<br>';
531 
532  print load_fiche_titre($title);
533 
534  print '<form method="post" id="combinationform" action="'.$_SERVER["PHP_SELF"].'">'."\n";
535  print '<input type="hidden" name="token" value="'.newToken().'">';
536  print '<input type="hidden" name="id" value="'.dol_escape_htmltag($id).'">'."\n";
537  print '<input type="hidden" name="action" value="'.(($valueid > 0) ? "update" : "create").'">'."\n";
538  if ($valueid > 0) {
539  print '<input type="hidden" name="valueid" value="'.$valueid.'">'."\n";
540  }
541 
542  print dol_get_fiche_head();
543 
544 
545  if ($action == 'add') {
546  print '<table class="border" style="width: 100%">';
547  print "<!-- Variant -->\n";
548  print '<tr>';
549  print '<td class="titlefieldcreate fieldrequired"><label for="attribute">'.$langs->trans('ProductAttribute').'</label></td>';
550  print '<td>';
551  if (is_array($prodattr_all)) {
552  print '<select class="flat minwidth100" id="attribute" name="attribute">';
553  print '<option value="-1">&nbsp;</option>';
554  foreach ($prodattr_all as $attr) {
555  //print '<option value="'.$attr->id.'"'.($attr->id == GETPOST('attribute', 'int') ? ' selected="selected"' : '').'>'.$attr->label.'</option>';
556  print '<option value="'.$attr->id.'">'.$attr->label.'</option>';
557  }
558  print '</select>';
559  }
560 
561  $htmltext = $langs->trans("GoOnMenuToCreateVairants", $langs->transnoentities("Product"), $langs->transnoentities("VariantAttributes"));
562  print $form->textwithpicto('', $htmltext);
563  /*print ' &nbsp; &nbsp; <a href="'.DOL_URL_ROOT.'/variants/create.php?action=create&backtopage='.urlencode($_SERVER["PHP_SELF"].'?action=add&id='.$object->id).'">';
564  print $langs->trans("Create");
565  print '</a>';*/
566 
567  print '</td>';
568  print '</tr>';
569  ?>
570  <!-- Value -->
571  <tr>
572  <td class="fieldrequired"><label for="value"><?php echo $langs->trans('Value') ?></label></td>
573  <td>
574  <select class="flat minwidth100" id="value" name="value">
575  <option value="-1">&nbsp;</option>
576  </select>
577  <?php
578  $htmltext = $langs->trans("GoOnMenuToCreateVairants", $langs->transnoentities("Product"), $langs->transnoentities("VariantAttributes"));
579  print $form->textwithpicto('', $htmltext);
580  /*
581  print ' &nbsp; &nbsp; <a href="'.DOL_URL_ROOT.'/variants/create.php?action=create&backtopage='.urlencode($_SERVER["PHP_SELF"].'?action=add&id='.$object->id).'">';
582  print $langs->trans("Create");
583  print '</a>';
584  */
585  ?>
586  </td>
587  </tr>
588  <tr>
589  <td></td><td>
590  <input type="submit" class="button" name="selectvariant" id="selectvariant" value="<?php echo dol_escape_htmltag($langs->trans("SelectCombination")); ?>">
591  </td>
592  </tr>
593  <?php
594  print '<tr><td></td><td>';
595  print $listofvariantselected;
596  print '</td>';
597  print '</tr>';
598 
599  print '</table>';
600  print '<hr>';
601  }
602 
603  if (is_array($productCombination2ValuePairs1)) {
604  print '<table class="border" style="width: 100%">';
605 
606  // When in edit mode
607  if (is_array($productCombination2ValuePairs1) && count($productCombination2ValuePairs1))
608  {
609  ?>
610  <tr>
611  <td class="titlefieldcreate tdtop"><label for="features"><?php echo $langs->trans('Combination') ?></label></td>
612  <td class="tdtop">
613  <div class="inline-block valignmiddle quatrevingtpercent">
614  <?php
615  foreach ($productCombination2ValuePairs1 as $key => $val) {
616  $result1 = $prodattr->fetch($val->fk_prod_attr);
617  $result2 = $prodattr_val->fetch($val->fk_prod_attr_val);
618  //print 'rr'.$result1.' '.$result2;
619  if ($result1 > 0 && $result2 > 0)
620  {
621  print $prodattr->label.' - '.$prodattr_val->value.'<br>';
622  // TODO Add delete link
623  }
624  }
625  ?>
626  </div>
627  <!-- <div class="inline-block valignmiddle">
628  <a href="#" class="inline-block valignmiddle button" id="delfeature"><?php echo img_edit_remove() ?></a>
629  </div>-->
630  </td>
631  <td>
632  </td>
633  </tr>
634  <?php
635  }
636  ?>
637  <tr>
638  <td><label for="reference"><?php echo $langs->trans('Reference') ?></label></td>
639  <td><input type="text" id="reference" name="reference" value="<?php echo trim($reference) ?>"></td>
640  </tr>
641  <?php
642  if (empty($conf->global->PRODUIT_MULTIPRICES)) {
643  ?>
644  <tr>
645  <td><label for="price_impact"><?php echo $langs->trans('PriceImpact') ?></label></td>
646  <td><input type="text" id="price_impact" name="price_impact" value="<?php echo price($price_impact) ?>">
647  <input type="checkbox" id="price_impact_percent" name="price_impact_percent" <?php echo $price_impact_percent ? ' checked' : '' ?>> <label for="price_impact_percent"><?php echo $langs->trans('PercentageVariation') ?></label>
648  </td>
649  </tr>
650  <?php
651  } else {
652  $prodcomb->fetchCombinationPriceLevels();
653 
654  for ($i = 1; $i <= $conf->global->PRODUIT_MULTIPRICES_LIMIT; $i++)
655  {
656  print '<tr>';
657  print '<td><label for="level_price_impact_'.$i.'">'.$langs->trans('ImpactOnPriceLevel', $i).'</label>';
658  if ($i === 1) {
659  print ' <a id="apply-price-impact-to-all-level" class="classfortooltip" href="#" title="'.$langs->trans('ApplyToAllPriceImpactLevelHelp').'">('.$langs->trans('ApplyToAllPriceImpactLevel').')</a>';
660  }
661  print '</td>';
662  print '<td><input type="text" class="level_price_impact" id="level_price_impact_'.$i.'" name="level_price_impact['.$i.']" value="'.price($prodcomb->combination_price_levels[$i]->variation_price).'">';
663  print '<input type="checkbox" class="level_price_impact_percent" id="level_price_impact_percent_'.$i.'" name="level_price_impact_percent['.$i.']" '.(!empty($prodcomb->combination_price_levels[$i]->variation_price_percentage) ? ' checked' : '').'> <label for="level_price_impact_percent_'.$i.'">'.$langs->trans('PercentageVariation').'</label>';
664 
665  print '</td>';
666  print '</tr>';
667  }
668  }
669 
670  if ($object->isProduct()) {
671  print '<tr>';
672  print '<td><label for="weight_impact">'.$langs->trans('WeightImpact').'</label></td>';
673  print '<td><input type="text" id="weight_impact" name="weight_impact" value="'.price($weight_impact).'"></td>';
674  print '</tr>';
675  }
676 
677  print '</table>';
678  }
679 
680  if (!empty($conf->global->PRODUIT_MULTIPRICES)) {
681  ?>
682  <script>
683  $(document).ready(function() {
684  // Apply level 1 impact to all prices impact levels
685  $('body').on('click', '#apply-price-impact-to-all-level', function(e) {
686  e.preventDefault();
687  let priceImpact = $( "#level_price_impact_1" ).val();
688  let priceImpactPrecent = $( "#level_price_impact_percent_1" ).prop("checked");
689 
690  var multipricelimit = <?php print intval($conf->global->PRODUIT_MULTIPRICES_LIMIT); ?>
691 
692  for (let i = 2; i <= multipricelimit; i++) {
693  $( "#level_price_impact_" + i ).val(priceImpact);
694  $( "#level_price_impact_percent_" + i ).prop("checked", priceImpactPrecent);
695  }
696  });
697  });
698  </script>
699  <?php
700  }
701 
702  print dol_get_fiche_end();
703  ?>
704 
705  <div style="text-align: center">
706  <input type="submit" name="create" <?php if (!is_array($productCombination2ValuePairs1)) print ' disabled="disabled"'; ?> value="<?php echo $action == 'add' ? $langs->trans('Create') : $langs->trans("Save") ?>" class="button button-save">
707  &nbsp;
708  <input type="submit" name="cancel" value="<?php echo $langs->trans("Cancel"); ?>" class="button button-cancel">
709  </div>
710 
711  <?php
712 
713  print '</form>';
714  } else {
715  if ($action === 'delete') {
716  if ($prodcomb->fetch($valueid) > 0) {
717  $prodstatic->fetch($prodcomb->fk_product_child);
718 
719  print $form->formconfirm(
720  "combinations.php?id=".$id."&valueid=".$valueid,
721  $langs->trans('Delete'),
722  $langs->trans('ProductCombinationDeleteDialog', $prodstatic->ref),
723  "confirm_deletecombination",
724  array(array('label'=> $langs->trans('DeleteLinkedProduct'), 'type'=> 'checkbox', 'name' => 'delete_product', 'value' => false)),
725  0,
726  1
727  );
728  }
729  } elseif ($action === 'copy') {
730  print $form->formconfirm('combinations.php?id='.$id, $langs->trans('ToClone'), $langs->trans('ConfirmCloneProductCombinations'), 'confirm_copycombination', array(array('type' => 'text', 'label' => $langs->trans('CloneDestinationReference'), 'name' => 'dest_product')), 0, 1);
731  }
732 
733  $comb2val = new ProductCombination2ValuePair($db);
734 
735  if ($productCombinations)
736  {
737  ?>
738 
739  <script type="text/javascript">
740  jQuery(document).ready(function() {
741 
742  jQuery('input[name="select_all"]').click(function() {
743 
744  if (jQuery(this).prop('checked')) {
745  var checked = true;
746  } else {
747  var checked = false;
748  }
749 
750  jQuery('table.liste input[type="checkbox"]').prop('checked', checked);
751  });
752 
753  jQuery('input[name^="select["]').click(function() {
754  jQuery('input[name="select_all"]').prop('checked', false);
755  });
756 
757  });
758  </script>
759 
760  <?php
761  }
762 
763  // Buttons
764  print '<div class="tabsAction">';
765 
766  print ' <div class="inline-block divButAction">';
767 
768  print '<a href="combinations.php?id='.$object->id.'&action=add&token='.newToken().'" class="butAction">'.$langs->trans('NewProductCombination').'</a>'; // NewVariant
769 
770  if ($productCombinations)
771  {
772  print '<a href="combinations.php?id='.$object->id.'&action=copy&token='.newToken().'" class="butAction">'.$langs->trans('PropagateVariant').'</a>';
773  }
774 
775  print ' </div>';
776 
777  print '</div>';
778 
779 
780 
781  $arrayofselected = is_array($toselect) ? $toselect : array();
782 
783 
784  // List of variants
785  print '<form method="POST" action="'.$_SERVER["PHP_SELF"].'">';
786  print '<input type="hidden" name="token" value="'.newToken().'">';
787  print '<input type="hidden" name="action" value="massaction">';
788  print '<input type="hidden" name="id" value="'.$id.'">';
789  print '<input type="hidden" name="backtopage" value="'.$backtopage.'">';
790 
791  // List of mass actions available
792  /*
793  $arrayofmassactions = array(
794  'presend'=>$langs->trans("SendByMail"),
795  'builddoc'=>$langs->trans("PDFMerge"),
796  );
797  if ($user->rights->product->supprimer) $arrayofmassactions['predelete']='<span class="fa fa-trash paddingrightonly"></span>'.$langs->trans("Delete");
798  if (in_array($massaction, array('presend','predelete'))) $arrayofmassactions=array();
799  $massactionbutton=$form->selectMassAction('', $arrayofmassactions);
800  */
801 
802  $aaa = '';
803  if (count($productCombinations))
804  {
805  $aaa = '<label for="massaction">'.$langs->trans('BulkActions').'</label>';
806  $aaa .= '<select id="bulk_action" name="massaction" class="flat">';
807  $aaa .= ' <option value="nothing">&nbsp;</option>';
808  $aaa .= ' <option value="not_buy">'.$langs->trans('ProductStatusNotOnBuy').'</option>';
809  $aaa .= ' <option value="not_sell">'.$langs->trans('ProductStatusNotOnSell').'</option>';
810  $aaa .= ' <option value="on_buy">'.$langs->trans('ProductStatusOnBuy').'</option>';
811  $aaa .= ' <option value="on_sell">'.$langs->trans('ProductStatusOnSell').'</option>';
812  $aaa .= ' <option value="delete">'.$langs->trans('Delete').'</option>';
813  $aaa .= '</select>';
814  $aaa .= '<input type="submit" value="'.dol_escape_htmltag($langs->trans("Apply")).'" class="button">';
815  }
816  $massactionbutton = $aaa;
817 
818  $title = $langs->trans("ProductCombinations");
819 
820  print_barre_liste($title, 0, $_SERVER["PHP_SELF"], '', $sortfield, $sortorder, $aaa, 0);
821 
822  print '<div class="div-table-responsive">';
823  ?>
824  <table class="liste">
825  <tr class="liste_titre">
826  <td class="liste_titre"><?php echo $langs->trans('Product') ?></td>
827  <td class="liste_titre"><?php echo $langs->trans('Combination') ?></td>
828  <td class="liste_titre right"><?php echo $langs->trans('PriceImpact') ?></td>
829  <?php if ($object->isProduct()) print'<td class="liste_titre right">'.$langs->trans('WeightImpact').'</td>'; ?>
830  <td class="liste_titre center"><?php echo $langs->trans('OnSell') ?></td>
831  <td class="liste_titre center"><?php echo $langs->trans('OnBuy') ?></td>
832  <td class="liste_titre"></td>
833  <?php
834  print '<td class="liste_titre center">';
835  $searchpicto = $form->showCheckAddButtons('checkforselect', 1);
836  print $searchpicto;
837  print '</td>';
838  ?>
839  </tr>
840  <?php
841 
842  if (count($productCombinations))
843  {
844  foreach ($productCombinations as $currcomb)
845  {
846  $prodstatic->fetch($currcomb->fk_product_child);
847  print '<tr class="oddeven">';
848  print '<td>'.$prodstatic->getNomUrl(1).'</td>';
849  print '<td>';
850 
851  $productCombination2ValuePairs = $comb2val->fetchByFkCombination($currcomb->id);
852  $iMax = count($productCombination2ValuePairs);
853 
854  for ($i = 0; $i < $iMax; $i++) {
855  echo dol_htmlentities($productCombination2ValuePairs[$i]);
856  if ($i !== ($iMax - 1)) {
857  echo ', ';
858  }
859  }
860  print '</td>';
861  print '<td class="right">'.($currcomb->variation_price >= 0 ? '+' : '').price($currcomb->variation_price).($currcomb->variation_price_percentage ? ' %' : '').'</td>';
862  if ($object->isProduct()) {
863  print '<td class="right">'.($currcomb->variation_weight >= 0 ? '+' : '').price($currcomb->variation_weight).' '.measuringUnitString(0, 'weight', $prodstatic->weight_units).'</td>';
864  }
865  print '<td class="center">'.$prodstatic->getLibStatut(2, 0).'</td>';
866  print '<td class="center">'.$prodstatic->getLibStatut(2, 1).'</td>';
867  print '<td class="right">';
868  print '<a class="paddingleft paddingright editfielda" href="'.$_SERVER["PHP_SELF"].'?id='.$id.'&action=edit&valueid='.$currcomb->id.'">'.img_edit().'</a>';
869  print '<a class="paddingleft paddingright" href="'.$_SERVER["PHP_SELF"].'?id='.$id.'&action=delete&token='.newToken().'&valueid='.$currcomb->id.'">'.img_delete().'</a>';
870  print '</td>';
871  print '<td class="nowrap center">';
872  if ($productCombinations || $massactionbutton || $massaction) // If we are in select mode (massactionbutton defined) or if we have already selected and sent an action ($massaction) defined
873  {
874  $selected = 0;
875  if (in_array($prodstatic->id, $arrayofselected)) $selected = 1;
876  print '<input id="cb'.$prodstatic->id.'" class="flat checkforselect" type="checkbox" name="toselect[]" value="'.$prodstatic->id.'"'.($selected ? ' checked="checked"' : '').'>';
877  }
878  print '</td>';
879  print '</tr>';
880  }
881  } else {
882  print '<tr><td colspan="8"><span class="opacitymedium">'.$langs->trans("None").'</span></td></tr>';
883  }
884  print '</table>';
885  print '</div>';
886  print '</form>';
887  }
888 } else {
889  llxHeader();
890  // not found
891 }
892 
893 // End of page
894 llxFooter();
895 $db->close();
GETPOST($paramname, $check= 'alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
img_edit($titlealt= 'default', $float=0, $other= '')
Show logo editer/modifier fiche.
Class to manage products or services.
dol_htmlentities($string, $flags=null, $encoding= 'UTF-8', $double_encode=false)
Replace htmlentities functions.
const TYPE_SERVICE
Service.
Class ProductAttributeValue Used to represent a product attribute value.
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition: repair.php:108
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
Class ProductAttribute Used to represent a product attribute.
price($amount, $form=0, $outlangs= '', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code= '')
Function to format a value into an amount for visual output Function used into PDF and HTML pages...
llxHeader()
Empty header.
Definition: wrapper.php:45
setEventMessages($mesg, $mesgs, $style= 'mesgs', $messagekey= '')
Set event messages in dol_events session object.
print_barre_liste($titre, $page, $file, $options= '', $sortfield= '', $sortorder= '', $morehtmlcenter= '', $num=-1, $totalnboflines= '', $picto= 'generic', $pictoisfullpath=0, $morehtmlright= '', $morecss= '', $limit=-1, $hideselectlimit=0, $hidenavigation=0, $pagenavastextinput=0, $morehtmlrightbeforearrow= '')
Print a title with navigation controls for pagination.
Class to manage generation of HTML components Only common components must be here.
load_fiche_titre($titre, $morehtmlright= '', $picto= 'generic', $pictoisfullpath=0, $id= '', $morecssontable= '', $morehtmlcenter= '')
Load a title with picto.
price2num($amount, $rounding= '', $option=0)
Function that return a number with universal decimal format (decimal separator is &#39;...
Class ProductCombinationLevel Used to represent a product combination Level.
restrictedArea($user, $features, $objectid=0, $tableandshare= '', $feature2= '', $dbt_keyfield= 'fk_soc', $dbt_select= 'rowid', $isdraft=0)
Check permissions of a user to show a page and an object.
Class ProductCombination Used to represent a product combination.
vatrate($rate, $addpercent=false, $info_bits=0, $usestarfornpr=0)
Return a string with VAT rate label formated for view output Used into pdf and HTML pages...
print $_SERVER["PHP_SELF"]
Edit parameters.
img_edit_remove($titlealt= 'default', $other= '')
Show logo -.
dol_get_fiche_head($links=array(), $active= '', $title= '', $notab=0, $picto= '', $pictoisfullpath=0, $morehtmlright= '', $morecss= '', $limittoshow=0, $moretabssuffix= '')
Show tabs of a record.
print
Draft customers invoices.
Definition: index.php:89
Class ProductCombination2ValuePair Used to represent the relation between a product combination...
dol_print_error($db= '', $error= '', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
newToken()
Return the value of token currently saved into session with name &#39;newtoken&#39;.
dol_get_fiche_end($notab=0)
Return tab footer of a card.
dol_banner_tab($object, $paramid, $morehtml= '', $shownav=1, $fieldid= 'rowid', $fieldref= 'ref', $morehtmlref= '', $moreparam= '', $nodbprefix=0, $morehtmlleft= '', $morehtmlstatus= '', $onlybanner=0, $morehtmlright= '')
Show tab footer of a card.
llxFooter()
Empty footer.
Definition: wrapper.php:59
img_delete($titlealt= 'default', $other= 'class="pictodelete"', $morecss= '')
Show delete logo.
if(preg_match('/crypted:/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
Definition: repair.php:105
product_prepare_head($object)
Prepare array with list of tabs.
Definition: product.lib.php:35
measuringUnitString($unit, $measuring_style= '', $scale= '', $use_short_label=0, $outputlangs=null)
Return translation label of a unit key.