generator.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. <?php
  2. /* Copyright (C) 2016 Marcos García <marcosgdf@gmail.com>
  3. * Copyright (C) 2018 Frédéric France <frederic.france@netlogic.fr>
  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 <http://www.gnu.org/licenses/>.
  17. */
  18. require '../main.inc.php';
  19. require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
  20. require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
  21. require_once DOL_DOCUMENT_ROOT.'/variants/class/ProductAttribute.class.php';
  22. require_once DOL_DOCUMENT_ROOT.'/variants/class/ProductAttributeValue.class.php';
  23. require_once DOL_DOCUMENT_ROOT.'/variants/class/ProductCombination.class.php';
  24. require_once DOL_DOCUMENT_ROOT.'/variants/class/ProductCombination2ValuePair.class.php';
  25. $langs->loadLangs(array("products", "other"));
  26. $id = GETPOST('id', 'int');
  27. $ref = GETPOST('ref');
  28. $form = new Form($db);
  29. // Security check
  30. $fieldvalue = (! empty($id) ? $id : $ref);
  31. $fieldtype = (! empty($ref) ? 'ref' : 'rowid');
  32. $result=restrictedArea($user,'produit|service',$fieldvalue,'product&product','','',$fieldtype);
  33. $prodattr = new ProductAttribute($db);
  34. $prodattrval = new ProductAttributeValue($db);
  35. $product = new Product($db);
  36. $product->fetch($id);
  37. if (!$product->isProduct()) {
  38. header('Location: '.dol_buildpath('/product/card.php?id='.$product->id, 2));
  39. exit();
  40. }
  41. /**
  42. * Posible combinations. Format.
  43. * attrval => array(
  44. * valueid => array(
  45. * price => ''
  46. * weight => ''
  47. * )
  48. * )
  49. */
  50. $combinations = GETPOST('combinations', 'array');
  51. $price_var_percent = (bool) GETPOST('price_var_percent');
  52. $donotremove = true;
  53. if ($_POST) {
  54. $donotremove = (bool) GETPOST('donotremove');
  55. //We must check if all those given combinations actually exist
  56. $sanitized_values = array();
  57. foreach ($combinations as $attr => $val) {
  58. if ($prodattr->fetch($attr) > 0) {
  59. foreach ($val as $valueid => $content) {
  60. if ($prodattrval->fetch($valueid) > 0) {
  61. $sanitized_values[$attr][$valueid] = $content;
  62. }
  63. }
  64. }
  65. }
  66. if ($sanitized_values) {
  67. require DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
  68. $adapted_values = array();
  69. //Adapt the array to the cartesian function
  70. foreach ($sanitized_values as $attr => $val) {
  71. $adapted_values[$attr] = array_keys($val);
  72. }
  73. $db->begin();
  74. $combination = new ProductCombination($db);
  75. $delete_prev_comb_res = 1;
  76. if (!$donotremove) {
  77. $delete_prev_comb_res = $combination->deleteByFkProductParent($user, $id);
  78. }
  79. //Current combinations will be deleted
  80. if ($delete_prev_comb_res > 0) {
  81. $res = 1;
  82. foreach (cartesianArray($adapted_values) as $currcomb)
  83. {
  84. $res = $combination->createProductCombination($product, $currcomb, $sanitized_values, $price_var_percent);
  85. if ($res < 0) {
  86. $error++;
  87. setEventMessages($combination->error, $combination->errors, 'errors');
  88. break;
  89. }
  90. }
  91. if ($res > 0) {
  92. $db->commit();
  93. setEventMessages($langs->trans('RecordSaved'), null, 'mesgs');
  94. header('Location: '.dol_buildpath('/variants/combinations.php?id='.$id, 2));
  95. exit;
  96. }
  97. } else {
  98. setEventMessages($langs->trans('ErrorDeletingGeneratedProducts'), null, 'errors');
  99. }
  100. $db->rollback();
  101. } else {
  102. setEventMessages($langs->trans('ErrorFieldsRequired'), null, 'errors');
  103. }
  104. }
  105. /*
  106. * View
  107. */
  108. if (! empty($id) || ! empty($ref)) {
  109. $object = new Product($db);
  110. $result = $object->fetch($id, $ref);
  111. llxHeader("", "", $langs->trans("CardProduct".$object->type));
  112. if ($result > 0)
  113. {
  114. $showbarcode=empty($conf->barcode->enabled)?0:1;
  115. if (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && empty($user->rights->barcode->lire_advance)) $showbarcode=0;
  116. $head=product_prepare_head($object);
  117. $titre=$langs->trans("CardProduct".$object->type);
  118. $picto=($object->type== Product::TYPE_SERVICE?'service':'product');
  119. dol_fiche_head($head, 'combinations', $titre, 0, $picto);
  120. $linkback = '<a href="'.DOL_URL_ROOT.'/product/list.php?type='.$object->type.'">'.$langs->trans("BackToList").'</a>';
  121. $object->next_prev_filter=" fk_product_type = ".$object->type;
  122. dol_banner_tab($object, 'ref', $linkback, ($user->societe_id?0:1), 'ref', '', '', '', 0, '', '', 1);
  123. dol_fiche_end();
  124. }
  125. print load_fiche_titre($langs->trans('ProductCombinationGenerator'));
  126. $dictionary_attr = array();
  127. foreach ($prodattr->fetchAll() as $attr) {
  128. $dictionary_attr[$attr->id] = $attr;
  129. foreach ($prodattrval->fetchAllByProductAttribute($attr->id) as $attrval) {
  130. $dictionary_attr[$attr->id]->values[$attrval->id] = $attrval;
  131. }
  132. }
  133. ?>
  134. <script>
  135. dictionary_attr = <?php echo json_encode($dictionary_attr) ?>;
  136. weight_units = '<?php echo measuring_units_string($object->weight_units, 'weight') ?>';
  137. attr_selected = {};
  138. percentage_variation = jQuery('input#price_var_percent').prop('checked');
  139. function parseSelectedFeatures(attr, val, inputs) {
  140. var price = '';
  141. var weight = '';
  142. if (typeof(inputs) == 'object') {
  143. price = inputs.price;
  144. weight = inputs.weight;
  145. }
  146. if (!attr_selected.hasOwnProperty(attr)) {
  147. var label = dictionary_attr[attr].label;
  148. var table = jQuery(document.createElement('table'))
  149. .attr('id', 'combinations_'+attr)
  150. .css('width', '100%')
  151. .addClass('liste')
  152. .append(
  153. jQuery(document.createElement('thead'))
  154. .append(jQuery(document.createElement('tr'))
  155. .addClass('liste_titre')
  156. .append(
  157. jQuery(document.createElement('th'))
  158. .addClass('liste_titre')
  159. .css('width', '40%')
  160. .text(label),
  161. jQuery(document.createElement('th')).addClass('liste_titre').css('text-align', 'center').html('<?php echo $langs->trans('PriceImpact') ?>'),
  162. jQuery(document.createElement('th')).addClass('liste_titre').css('text-align', 'center').html('<?php echo $langs->trans('WeightImpact') ?>')
  163. )
  164. )
  165. )
  166. ;
  167. jQuery('form#combinationsform').prepend(table);
  168. attr_selected[attr] = [];
  169. } else {
  170. if (jQuery.inArray(val, attr_selected[attr]) != -1) {
  171. return;
  172. }
  173. }
  174. var combinations_table = jQuery('table#combinations_' + attr);
  175. var html = jQuery(document.createElement('tr'));
  176. if (combinations_table.children('tbody').children('tr').length % 2 === 0) {
  177. html.addClass('pair');
  178. } else {
  179. html.addClass('impair');
  180. }
  181. var percent_symbol_html = jQuery(document.createElement('span')).attr('id', 'percentsymbol').html(' %');
  182. if (!percentage_variation) {
  183. percent_symbol_html.hide();
  184. }
  185. html.append(
  186. jQuery(document.createElement('td')).text(dictionary_attr[attr].values[val].value),
  187. jQuery(document.createElement('td')).css('text-align', 'center').append(
  188. jQuery(document.createElement('input')).attr('type', 'text').css('width', '50px').attr('name', 'combinations[' + attr + '][' + val + '][price]').val(price),
  189. percent_symbol_html
  190. ),
  191. jQuery(document.createElement('td')).css('text-align', 'center').append(
  192. jQuery(document.createElement('input')).attr('type', 'text').css('width', '50px').attr('name', 'combinations[' + attr + '][' + val + '][weight]').val(weight),
  193. ' ' + weight_units
  194. )
  195. );
  196. combinations_table.append(html);
  197. attr_selected[attr].push(val);
  198. }
  199. function showHidePercentageSymbol(checked) {
  200. percentage_variation = checked;
  201. if (checked) {
  202. jQuery('span#percentsymbol').show();
  203. } else {
  204. jQuery('span#percentsymbol').hide();
  205. }
  206. }
  207. jQuery(document).ready(function() {
  208. var input_price_var_percent = jQuery('input#price_var_percent');
  209. jQuery.each(<?php echo json_encode($combinations) ?>, function(key, val) {
  210. jQuery.each(val, function(valkey, valcontent) {
  211. parseSelectedFeatures(key, valkey, valcontent);
  212. });
  213. });
  214. jQuery('#addfeature').click(function() {
  215. jQuery('#features option:selected').each(function(selector) {
  216. var explode = jQuery(this).val().split(':');
  217. parseSelectedFeatures(explode[0], explode[1]);
  218. });
  219. });
  220. jQuery('#delfeature').click(function() {
  221. jQuery('#features option:selected').each(function(selector) {
  222. var explode = jQuery(this).val().split(':');
  223. if (attr_selected.hasOwnProperty(explode[0])) {
  224. var tr = jQuery('input[name="combinations[' + explode[0] + '][' + explode[1] + '][price]"').parent().parent();
  225. var index_value = jQuery.inArray(explode[1], attr_selected[explode[0]]);
  226. attr_selected[explode[0]].splice(index_value, 1);
  227. if (tr.parent().children('tr').length === 1) {
  228. tr.parent().parent().detach();
  229. delete attr_selected[explode[0]]
  230. } else {
  231. tr.detach();
  232. }
  233. }
  234. });
  235. });
  236. input_price_var_percent.click(function() {
  237. showHidePercentageSymbol(jQuery(this).prop('checked'));
  238. });
  239. jQuery('input#donotremove').click(function() {
  240. if (jQuery(this).prop('checked')) {
  241. jQuery('div#info_donotremove').hide();
  242. } else {
  243. jQuery('div#info_donotremove').show();
  244. }
  245. });
  246. });
  247. </script>
  248. <div style="width: 100%;display:block; height: 300px">
  249. <div style="float:right; width: 79%; margin-left: 1%">
  250. <form method="post" id="combinationsform">
  251. <p><?php echo $langs->trans('TooMuchCombinationsWarning', $langs->trans('DoNotRemovePreviousCombinations')) ?></p>
  252. <input type="checkbox" name="price_var_percent"
  253. id="price_var_percent"<?php echo $price_var_percent ? ' checked' : '' ?>> <label
  254. for="price_var_percent"><?php echo $langs->trans('UsePercentageVariations') ?></label>
  255. <br>
  256. <input type="checkbox" name="donotremove"
  257. id="donotremove"<?php echo $donotremove ? ' checked' : '' ?>> <label for="donotremove"><?php echo $langs->trans('DoNotRemovePreviousCombinations') ?></label>
  258. <br>
  259. <div id="info_donotremove" class="info" style="<?php echo $donotremove ? 'display: none' : '' ?>">
  260. <?php echo img_warning() ?>
  261. <?php echo $langs->trans('ProductCombinationGeneratorWarning') ?>
  262. </div>
  263. <br>
  264. <div style="text-align: center">
  265. <input type="submit" value="<?php echo $langs->trans('Generate') ?>" class="button" name="submit">
  266. </div>
  267. </form>
  268. </div>
  269. <div style="float:left; width: 20%">
  270. <select id="features" multiple style="width: 100%; height: 300px; overflow: auto">
  271. <?php foreach ($dictionary_attr as $attr): ?>
  272. <optgroup label="<?php echo $attr->label ?>">
  273. <?php foreach ($attr->values as $attrval): ?>
  274. <option value="<?php echo $attr->id.':'.$attrval->id ?>"<?php
  275. if (isset($combinations[$attr->id][$attrval->id])) {
  276. echo ' selected';
  277. }
  278. ?>><?php echo dol_htmlentities($attrval->value) ?></option>
  279. <?php endforeach ?>
  280. </optgroup>
  281. <?php endforeach ?>
  282. </select>
  283. <br><br>
  284. <a class="button" id="delfeature" style="float: right"><?php echo img_edit_remove() ?></a>
  285. <a class="button" id="addfeature"><?php echo img_edit_add() ?></a>
  286. </div>
  287. </div>
  288. <?php
  289. // End of page
  290. llxFooter();
  291. }
  292. $db->close();