diff --git a/htdocs/attributes/admin/admin.php b/htdocs/attributes/admin/admin.php
new file mode 100644
index 00000000000..eedacb086b3
--- /dev/null
+++ b/htdocs/attributes/admin/admin.php
@@ -0,0 +1,65 @@
+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+require '../../main.inc.php';
+require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
+require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
+
+$langs->load("admin");
+$langs->load("products");
+
+// Security check
+if (! $user->admin || (empty($conf->product->enabled) && empty($conf->service->enabled)))
+ accessforbidden();
+
+if ($_POST) {
+
+ $value = GETPOST('PRODUIT_ATTRIBUTES_HIDECHILD');
+
+ if (dolibarr_set_const($db, 'PRODUIT_ATTRIBUTES_HIDECHILD', $value, 'chaine', 0, '', $conf->entity)) {
+ setEventMessage($langs->trans('RecordSaved'));
+ } else {
+ setEventMessage($langs->trans('CoreErrorMessage'), 'errors');
+ }
+
+}
+
+$title = $langs->trans('ModuleSetup').' '.$langs->trans('ProductAttributes');
+llxHeader('', $title);
+
+$linkback=''.$langs->trans("BackToModuleList").'';
+print load_fiche_titre($title,$linkback,'title_setup');
+
+dol_fiche_head(array(), 'general', $tab, 0, 'product');
+
+print '
';
+
+llxFooter();
+
+$db->close();
+
diff --git a/htdocs/attributes/admin/index.html b/htdocs/attributes/admin/index.html
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/htdocs/attributes/ajax/getCombinations.php b/htdocs/attributes/ajax/getCombinations.php
new file mode 100644
index 00000000000..5ed3e959ea3
--- /dev/null
+++ b/htdocs/attributes/ajax/getCombinations.php
@@ -0,0 +1,50 @@
+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+define('NOTOKENRENEWAL','1');
+define('NOREQUIREMENU','1');
+define('NOREQUIREHTML','1');
+define('NOREQUIREAJAX','1');
+define('NOREQUIRESOC','1');
+
+require '../../main.inc.php';
+require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
+require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductCombination.class.php';
+
+header('Content-Type: application/json');
+
+$id = GETPOST('id');
+
+if (!$id) {
+ print json_encode(array(
+ 'error' => 'ID not set'
+ ));
+ die;
+}
+
+$product = new Product($db);
+
+if ($product->fetch($id) < 0) {
+ print json_encode(array(
+ 'error' => 'Product not found'
+ ));
+}
+
+$prodcomb = new ProductCombination($db);
+
+echo json_encode($prodcomb->getUniqueAttributesAndValuesByFkProductParent($product->id));
diff --git a/htdocs/attributes/ajax/get_attribute_values.php b/htdocs/attributes/ajax/get_attribute_values.php
new file mode 100644
index 00000000000..a08126d997a
--- /dev/null
+++ b/htdocs/attributes/ajax/get_attribute_values.php
@@ -0,0 +1,61 @@
+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+define('NOTOKENRENEWAL','1');
+define('NOREQUIREMENU','1');
+define('NOREQUIREHTML','1');
+define('NOREQUIREAJAX','1');
+define('NOREQUIRESOC','1');
+
+require '../../main.inc.php';
+require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
+require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductAttribute.class.php';
+require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductAttributeValue.class.php';
+
+header('Content-Type: application/json');
+
+$id = GETPOST('id');
+
+if (!$id) {
+ print json_encode(array(
+ 'error' => 'ID not set'
+ ));
+ die;
+}
+
+$prodattr = new ProductAttribute($db);
+
+if ($prodattr->fetch($id) < 0) {
+ print json_encode(array(
+ 'error' => 'Attribute not found'
+ ));
+ die;
+}
+
+$prodattrval = new ProductAttributeValue($db);
+
+$res = $prodattrval->fetchAllByProductAttribute($id);
+
+if ($res == -1) {
+ print json_encode(array(
+ 'error' => 'Internal error'
+ ));
+ die;
+}
+
+print json_encode($res);
\ No newline at end of file
diff --git a/htdocs/attributes/ajax/index.html b/htdocs/attributes/ajax/index.html
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/htdocs/attributes/ajax/orderAttribute.php b/htdocs/attributes/ajax/orderAttribute.php
new file mode 100644
index 00000000000..6c7eecc9453
--- /dev/null
+++ b/htdocs/attributes/ajax/orderAttribute.php
@@ -0,0 +1,53 @@
+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+if (! defined('NOTOKENRENEWAL')) define('NOTOKENRENEWAL','1'); // Disable token renewal
+if (! defined('NOREQUIREMENU')) define('NOREQUIREMENU','1');
+if (! defined('NOREQUIREHTML')) define('NOREQUIREHTML','1');
+if (! defined('NOREQUIREAJAX')) define('NOREQUIREAJAX','1');
+if (! defined('NOREQUIRESOC')) define('NOREQUIRESOC','1');
+if (! defined('NOREQUIRETRAN')) define('NOREQUIRETRAN','1');
+if (! defined('NOREQUIREHOOK')) define('NOREQUIREHOOK','1'); // Disable "main.inc.php" hooks
+
+require '../../main.inc.php';
+
+/*
+ * View
+ */
+
+top_httphead();
+
+// Registering the location of boxes
+if (isset($_POST['roworder'])) {
+ $roworder=GETPOST('roworder','alpha',2);
+
+ dol_syslog("AjaxOrderAttribute roworder=".$roworder, LOG_DEBUG);
+
+ $rowordertab = explode(',', $roworder);
+
+ foreach ($rowordertab as $value) {
+ if (!empty($value)) {
+ $newrowordertab[] = $value;
+ }
+ }
+
+ require DOL_DOCUMENT_ROOT.'/attributes/class/ProductAttribute.class.php';
+
+ ProductAttribute::bulkUpdateOrder($db, $newrowordertab);
+}
+
diff --git a/htdocs/attributes/card.php b/htdocs/attributes/card.php
new file mode 100644
index 00000000000..3339a169c89
--- /dev/null
+++ b/htdocs/attributes/card.php
@@ -0,0 +1,245 @@
+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+require '../main.inc.php';
+require 'class/ProductAttribute.class.php';
+require 'class/ProductAttributeValue.class.php';
+
+$id = GETPOST('id');
+$valueid = GETPOST('valueid');
+$action = GETPOST('action');
+$label = GETPOST('label');
+$ref = GETPOST('ref');
+$confirm = GETPOST('confirm');
+
+$prodattr = new ProductAttribute($db);
+$prodattrval = new ProductAttributeValue($db);
+
+if ($prodattr->fetch($id) < 1) {
+ dol_print_error($db, $langs->trans('ErrorRecordNotFound'));
+ die;
+}
+
+if ($_POST) {
+
+ if ($action == 'edit') {
+
+ $prodattr->label = $label;
+ $prodattr->ref = $ref;
+
+ if ($prodattr->update() < 1) {
+ setEventMessage($langs->trans('CoreErrorMessage'), 'errors');
+ } else {
+ setEventMessage($langs->trans('RecordSaved'));
+ header('Location: '.dol_buildpath('/attributes/card.php?id='.$id, 2));
+ die;
+ }
+ } elseif ($action == 'edit_value') {
+
+ if ($prodattrval->fetch($valueid) > 0) {
+
+ $prodattrval->ref = $ref;
+ $prodattrval->value = GETPOST('value');
+
+ if ($prodattrval->update() > 0) {
+ setEventMessage($langs->trans('RecordSaved'));
+ } else {
+ setEventMessage($langs->trans('CoreErrorMessage'), 'errors');
+ }
+ }
+
+ header('Location: '.dol_buildpath('/attributes/card.php?id='.$prodattr->id, 2));
+ die;
+ }
+
+}
+
+if ($confirm == 'yes') {
+ if ($action == 'confirm_delete') {
+
+ $db->begin();
+
+ $res = $prodattrval->deleteByFkAttribute($prodattr->id);
+
+ if ($res < 1 || ($prodattr->delete() < 1)) {
+ $db->rollback();
+ setEventMessage($langs->trans('CoreErrorMessage'), 'errors');
+ header('Location: '.dol_buildpath('/attributes/card.php?id='.$prodattr->id, 2));
+ } else {
+ $db->commit();
+ setEventMessage($langs->trans('RecordSaved'));
+ header('Location: '.dol_buildpath('/attributes/list.php', 2));
+ }
+
+ die;
+ } elseif ($action == 'confirm_deletevalue') {
+
+ if ($prodattrval->fetch($valueid) > 0) {
+
+ if ($prodattrval->delete() < 1) {
+ setEventMessage($langs->trans('CoreErrorMessage'), 'errors');
+ } else {
+ setEventMessage($langs->trans('RecordSaved'));
+ }
+
+ header('Location: '.dol_buildpath('/attributes/card.php?id='.$prodattr->id, 2));
+ die;
+ }
+ }
+}
+
+$langs->load('products');
+
+$title = $langs->trans('ProductAttributeName', dol_htmlentities($prodattr->label));
+$var = false;
+
+llxHeader('', $title);
+
+print_fiche_titre($title);
+
+dol_fiche_head();
+
+if ($action == 'edit') {
+ print '
+formconfirm(
+ "card.php?id=".$prodattr->id,
+ $langs->trans('Delete'),
+ $langs->trans('ProductAttributeDeleteDialog'),
+ "confirm_delete",
+ '',
+ 0,
+ 1
+ );
+ } elseif ($action == 'delete_value') {
+
+ if ($prodattrval->fetch($valueid) > 0) {
+
+ $form = new Form($db);
+
+ print $form->formconfirm(
+ "card.php?id=".$prodattr->id."&valueid=".$prodattrval->id,
+ $langs->trans('Delete'),
+ $langs->trans('ProductAttributeValueDeleteDialog', dol_htmlentities($prodattrval->value), dol_htmlentities($prodattrval->ref)),
+ "confirm_deletevalue",
+ '',
+ 0,
+ 1
+ );
+ }
+ }
+
+ ?>
+
+
';
+ print '';
+
+ }
+}
+
+llxFooter();
diff --git a/htdocs/attributes/create.php b/htdocs/attributes/create.php
new file mode 100644
index 00000000000..5b68ed28907
--- /dev/null
+++ b/htdocs/attributes/create.php
@@ -0,0 +1,73 @@
+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+require '../main.inc.php';
+require 'class/ProductAttribute.class.php';
+
+$ref = GETPOST('ref');
+$label = GETPOST('label');
+
+if ($_POST) {
+
+ if (empty($ref) || empty($label)) {
+ setEventMessage($langs->trans('ErrorFieldsRequired'), 'errors');
+ } else {
+
+ $prodattr = new ProductAttribute($db);
+ $prodattr->label = $label;
+ $prodattr->ref = $ref;
+
+ if ($prodattr->create()) {
+ setEventMessage($langs->trans('RecordSaved'));
+ header('Location: '.dol_buildpath('/attributes/list.php', 2));
+ } else {
+ setEventMessage($langs->trans('ErrorRecordAlreadyExists'), 'errors');
+ }
+ }
+}
+
+$langs->load('products');
+
+$title = $langs->trans('NewProductAttribute');
+
+llxHeader('', $title);
+
+print_fiche_titre($title);
+
+dol_fiche_head();
+
+?>
+';
+
+llxFooter();
\ No newline at end of file
diff --git a/htdocs/attributes/create_val.php b/htdocs/attributes/create_val.php
new file mode 100644
index 00000000000..b25e24fa64a
--- /dev/null
+++ b/htdocs/attributes/create_val.php
@@ -0,0 +1,102 @@
+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+require '../main.inc.php';
+require 'class/ProductAttribute.class.php';
+require 'class/ProductAttributeValue.class.php';
+
+$id = GETPOST('id');
+$ref = GETPOST('ref');
+$value = GETPOST('value');
+
+$prodattr = new ProductAttribute($db);
+$prodattrval = new ProductAttributeValue($db);
+
+if ($prodattr->fetch($id) < 1) {
+ dol_print_error($db, $langs->trans('ErrorRecordNotFound'));
+ die;
+}
+
+if ($_POST) {
+
+ if (empty($ref) || empty($value)) {
+ setEventMessage($langs->trans('ErrorFieldsRequired'), 'errors');
+ } else {
+
+ $prodattrval->fk_product_attribute = $prodattr->id;
+ $prodattrval->ref = $ref;
+ $prodattrval->value = $value;
+
+ if ($prodattrval->create() > 0) {
+ setEventMessage($langs->trans('RecordSaved'));
+ header('Location: '.dol_buildpath('/attributes/card.php?id='.$prodattr->id, 2));
+ die;
+ } else {
+ setEventMessage($langs->trans('ErrorCreatingProductAttributeValue'), 'errors');
+ }
+ }
+
+}
+
+$langs->load('products');
+
+$title = $langs->trans('ProductAttributeName', dol_htmlentities($prodattr->label));
+
+llxHeader('', $title);
+
+print_fiche_titre($title);
+
+dol_fiche_head();
+
+?>
+
+
+
trans('Ref') ?>
+
ref) ?>
+
+
+
trans('Label') ?>
+
label) ?>
+
+
+
+trans('NewProductAttributeValue'));
+
+dol_fiche_head();
+?>
+';
+
+llxFooter();
\ No newline at end of file
diff --git a/htdocs/attributes/generator.php b/htdocs/attributes/generator.php
new file mode 100644
index 00000000000..37f2175cc49
--- /dev/null
+++ b/htdocs/attributes/generator.php
@@ -0,0 +1,393 @@
+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+require '../main.inc.php';
+require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
+require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
+require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductAttribute.class.php';
+require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductAttributeValue.class.php';
+require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductCombination.class.php';
+require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductCombination2ValuePair.class.php';
+
+$langs->load("products");
+$langs->load('other');
+
+$id = GETPOST('id', 'int');
+$ref = GETPOST('ref');
+$form = new Form($db);
+
+// Security check
+$fieldvalue = (! empty($id) ? $id : $ref);
+$fieldtype = (! empty($ref) ? 'ref' : 'rowid');
+$result=restrictedArea($user,'produit|service',$fieldvalue,'product&product','','',$fieldtype);
+
+$prodattr = new ProductAttribute($db);
+$prodattrval = new ProductAttributeValue($db);
+$product = new Product($db);
+
+$product->fetch($id);
+
+if (!$product->isProduct()) {
+ header('Location: '.dol_buildpath('/product/card.php?id='.$product->id, 2));
+ die;
+}
+
+/**
+ * Posible combinations. Format.
+ * attrval => array(
+ * valueid => array(
+ * price => ''
+ * weight => ''
+ * )
+ * )
+ */
+$combinations = GETPOST('combinations', 'array');
+$price_var_percent = (bool) GETPOST('price_var_percent');
+$donotremove = true;
+
+if ($_POST) {
+
+ $donotremove = (bool) GETPOST('donotremove');
+
+ //We must check if all those given combinations actually exist
+ $sanitized_values = array();
+
+ foreach ($combinations as $attr => $val) {
+ if ($prodattr->fetch($attr) > 0) {
+ foreach ($val as $valueid => $content) {
+ if ($prodattrval->fetch($valueid) > 0) {
+ $sanitized_values[$attr][$valueid] = $content;
+ }
+ }
+ }
+ }
+
+ if ($sanitized_values) {
+
+ require DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
+
+ $adapted_values = array();
+
+ //Adapt the array to the cartesian function
+ foreach ($sanitized_values as $attr => $val) {
+ $adapted_values[$attr] = array_keys($val);
+ }
+
+ $db->begin();
+
+ $combination = new ProductCombination($db);
+
+ $delete_prev_comb_res = 1;
+
+ if (!$donotremove) {
+ $delete_prev_comb_res = $combination->deleteByFkProductParent($id);
+ }
+
+ //Current combinations will be deleted
+ if ($delete_prev_comb_res > 0) {
+
+ $res = 1;
+
+ foreach (cartesianArray($adapted_values) as $currcomb) {
+
+ $res = ProductCombination::createProductCombination($product, $currcomb, $sanitized_values, $price_var_percent);
+
+ if ($res < 0) {
+ break;
+ }
+ }
+
+ if ($res > 0) {
+ $db->commit();
+ setEventMessage($langs->trans('RecordSaved'));
+ header('Location: '.dol_buildpath('/attributes/combinations.php?id='.$id, 2));
+ die;
+ } else {
+ setEventMessage($langs->trans('CoreErrorMessage'), 'errors');
+ }
+ } else {
+ setEventMessage($langs->trans('ErrorDeletingGeneratedProducts'), 'errors');
+ }
+
+ $db->rollback();
+
+ } else {
+ setEventMessage($langs->trans('ErrorFieldsRequired'), 'errors');
+ }
+}
+
+/*
+ * View
+ */
+
+if (! empty($id) || ! empty($ref)) {
+ $object = new Product($db);
+ $result = $object->fetch($id, $ref);
+
+ llxHeader("", "", $langs->trans("CardProduct".$object->type));
+
+ if ($result) {
+ $head = product_prepare_head($object);
+ $titre = $langs->trans("CardProduct".$object->type);
+ $picto = ($object->type == Product::TYPE_SERVICE ? 'service' : 'product');
+
+ dol_fiche_head($head, 'combinations', $titre, 0, $picto);
+
+ print '
+
+
+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+/**
+ * Returns the entity of the products.
+ * @param DoliDB $db Database handler
+ * @return mixed
+ */
+function getProductEntities(DoliDB $db)
+{
+ require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
+
+ $product = new Product($db);
+
+ return getEntity($product->element, 1);
+}
\ No newline at end of file
diff --git a/htdocs/attributes/list.php b/htdocs/attributes/list.php
new file mode 100644
index 00000000000..3c4cf945524
--- /dev/null
+++ b/htdocs/attributes/list.php
@@ -0,0 +1,140 @@
+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+require '../main.inc.php';
+require 'class/ProductAttribute.class.php';
+
+$rowid = GETPOST('rowid');
+$action = GETPOST('action');
+$object = new ProductAttribute($db);
+
+if ($action == 'up') {
+ $object->fetch($rowid);
+ $object->moveUp();
+
+ header('Location: '.$_SERVER['PHP_SELF']);
+ die;
+} elseif ($action == 'down') {
+ $object->fetch($rowid);
+ $object->moveDown();
+
+ header('Location: '.$_SERVER['PHP_SELF']);
+ die;
+}
+
+$langs->load('products');
+
+$var = false;
+$title = $langs->trans($langs->trans('ProductAttributes'));
+
+$attributes = $object->fetchAll();
+
+llxHeader('', $title);
+
+print_fiche_titre($title);
+
+$forcereloadpage=empty($conf->global->MAIN_FORCE_RELOAD_PAGE)?0:1;
+?>
+
+
+
\n";
print "";
-
+
print "\n";
dol_fiche_end();
diff --git a/htdocs/contrat/card.php b/htdocs/contrat/card.php
index 3c88eb64e26..bea5af7e795 100644
--- a/htdocs/contrat/card.php
+++ b/htdocs/contrat/card.php
@@ -7,7 +7,7 @@
* Copyright (C) 2013 Christophe Battarel
* Copyright (C) 2013-2014 Florian Henry
* Copyright (C) 2014-2016 Ferran Marcet
- * Copyright (C) 2014 Marcos García
+ * Copyright (C) 2014-2016 Marcos García
* Copyright (C) 2015 Jean-François Ferry
*
* This program is free software; you can redistribute it and/or modify
@@ -446,7 +446,7 @@ if (empty($reshook))
unset($_POST["options_" . $key]);
}
}
-
+
if (! $error)
{
// Clean parameters
diff --git a/htdocs/core/class/html.form.class.php b/htdocs/core/class/html.form.class.php
index 6a3f3825c7c..cec7aa2aede 100644
--- a/htdocs/core/class/html.form.class.php
+++ b/htdocs/core/class/html.form.class.php
@@ -12,7 +12,7 @@
* Copyright (C) 2010 Juanjo Menent
* Copyright (C) 2010-2014 Philippe Grand
* Copyright (C) 2011 Herve Prot
- * Copyright (C) 2012-2014 Marcos García
+ * Copyright (C) 2012-2016 Marcos García
* Copyright (C) 2012 Cedric Salvador
* Copyright (C) 2012-2015 Raphaël Doursenaud
* Copyright (C) 2014 Alexandre Spangaro
@@ -80,7 +80,7 @@ class Form
* @param string $typeofdata Type of data ('string' by default, 'email', 'amount:99', 'numeric:99', 'text' or 'textarea:rows:cols', 'datepicker' ('day' do not work, don't know why), 'ckeditor:dolibarr_zzz:width:height:savemethod:1:rows:cols', 'select;xxx[:class]'...)
* @param string $moreparam More param to add on a href URL.
* @param int $fieldrequired 1 if we want to show field as mandatory using the "fieldrequired" CSS.
- * @param int $notabletag 1=Do not output table tags but output a ':', 2=Do not output table tags and no ':', 3=Do not output table tags but output a ' '
+ * @param int $notabletag 1=Do not output table tags but output a ':', 2=Do not output table tags and no ':', 3=Do not output table tags but output a ' '
* @return string HTML edit field
*/
function editfieldkey($text, $htmlname, $preselected, $object, $perm, $typeofdata='string', $moreparam='', $fieldrequired=0, $notabletag=0)
@@ -512,7 +512,7 @@ class Form
*
* @param string $selected Value auto selected when at least one record is selected. Not a preselected value. Use '0' by default.
* @param int $arrayofaction array('code'=>'label', ...). The code is the key stored into the GETPOST('massaction') when submitting action.
- * @param int $alwaysvisible 1=select button always visible
+ * @param int $alwaysvisible 1=select button always visible
* @return string Select list
*/
function selectMassAction($selected, $arrayofaction, $alwaysvisible=0)
@@ -586,7 +586,7 @@ class Form
';
}
-
+
return $ret;
}
@@ -1410,7 +1410,7 @@ class Form
// Build list includeUsers to have only hierarchy and current user
$includeUsers = implode(",",$user->getAllChildIds(1));
}
-
+
$out='';
// On recherche les utilisateurs
@@ -1616,7 +1616,7 @@ class Form
$i++;
}
if ($nbassignetouser) $out.='';
-
+
//$out.='';
return $out;
}
@@ -1642,11 +1642,12 @@ class Form
* @param int $hidepriceinlabel 1=Hide prices in label
* @param string $warehouseStatus warehouse status filter, following comma separated filter options can be used
* 'warehouseopen' = select products from open warehouses,
- * 'warehouseclosed' = select products from closed warehouses,
+ * 'warehouseclosed' = select products from closed warehouses,
* 'warehouseinternal' = select products from warehouses for internal correct/transfer only
+ * @param array $selected_combinations Selected combinations. Format: array([attrid] => attrval, [...])
* @return void
*/
- function select_produits($selected='', $htmlname='productid', $filtertype='', $limit=20, $price_level=0, $status=1, $finished=2, $selected_input_value='', $hidelabel=0, $ajaxoptions=array(), $socid=0, $showempty='1', $forcecombo=0, $morecss='', $hidepriceinlabel=0, $warehouseStatus='')
+ function select_produits($selected='', $htmlname='productid', $filtertype='', $limit=20, $price_level=0, $status=1, $finished=2, $selected_input_value='', $hidelabel=0, $ajaxoptions=array(), $socid=0, $showempty='1', $forcecombo=0, $morecss='', $hidepriceinlabel=0, $warehouseStatus='', $selected_combinations = array())
{
global $langs,$conf;
@@ -1671,6 +1672,80 @@ class Form
$urloption.='&socid='.$socid;
}
print ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/product/ajax/products.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 0, $ajaxoptions);
+
+ if (!empty($conf->attributes->enabled)) {
+ ?>
+
+ trans("RefOrLabel").' : ';
else if ($hidelabel > 1) {
if (! empty($conf->global->MAIN_HTML5_PLACEHOLDER)) $placeholder=' placeholder="'.$langs->trans("RefOrLabel").'"';
@@ -1709,7 +1784,7 @@ class Form
* @param int $hidepriceinlabel 1=Hide prices in label
* @param string $warehouseStatus warehouse status filter, following comma separated filter options can be used
* 'warehouseopen' = select products from open warehouses,
- * 'warehouseclosed' = select products from closed warehouses,
+ * 'warehouseclosed' = select products from closed warehouses,
* 'warehouseinternal' = select products from warehouses for internal correct/transfer only
* @return array Array of keys for json
*/
@@ -1724,19 +1799,19 @@ class Form
if (! empty($warehouseStatus))
{
require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php';
- if (preg_match('/warehouseclosed/', $warehouseStatus))
+ if (preg_match('/warehouseclosed/', $warehouseStatus))
{
$warehouseStatusArray[] = Entrepot::STATUS_CLOSED;
}
- if (preg_match('/warehouseopen/', $warehouseStatus))
+ if (preg_match('/warehouseopen/', $warehouseStatus))
{
$warehouseStatusArray[] = Entrepot::STATUS_OPEN_ALL;
}
- if (preg_match('/warehouseinternal/', $warehouseStatus))
+ if (preg_match('/warehouseinternal/', $warehouseStatus))
{
$warehouseStatusArray[] = Entrepot::STATUS_OPEN_INTERNAL;
}
- }
+ }
$selectFields = " p.rowid, p.label, p.ref, p.description, p.barcode, p.fk_product_type, p.price, p.price_ttc, p.price_base_type, p.tva_tx, p.duration, p.fk_price_expression";
(count($warehouseStatusArray)) ? $selectFieldsGrouped = ", sum(ps.reel) as stock" : $selectFieldsGrouped = ", p.stock";
@@ -1775,7 +1850,7 @@ class Form
$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."product_stock as ps on ps.fk_product = p.rowid";
$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."entrepot as e on ps.fk_entrepot = e.rowid";
}
-
+
//Price by customer
if (! empty($conf->global->PRODUIT_CUSTOMER_PRICES) && !empty($socid)) {
$sql.=" LEFT JOIN ".MAIN_DB_PREFIX."product_customer_price as pcp ON pcp.fk_soc=".$socid." AND pcp.fk_product=p.rowid";
@@ -1785,11 +1860,21 @@ class Form
{
$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."product_lang as pl ON pl.fk_product = p.rowid AND pl.lang='". $langs->getDefaultLang() ."'";
}
+
+ if (!empty($conf->global->PRODUIT_ATTRIBUTES_HIDECHILD)) {
+ $sql .= " LEFT JOIN llx_product_attribute_combination pac ON pac.fk_product_child = p.rowid";
+ }
+
$sql.= ' WHERE p.entity IN ('.getEntity('product', 1).')';
if (count($warehouseStatusArray))
{
$sql.= ' AND (p.fk_product_type = 1 OR e.statut IN ('.implode(',',$warehouseStatusArray).'))';
}
+
+ if (!empty($conf->global->PRODUIT_ATTRIBUTES_HIDECHILD)) {
+ $sql .= " AND pac.rowid IS NULL";
+ }
+
if ($finished == 0)
{
$sql.= " AND p.finished = ".$finished;
@@ -1842,7 +1927,7 @@ class Form
$num = $this->db->num_rows($result);
$events=null;
-
+
if ($conf->use_javascript_ajax && ! $forcecombo)
{
include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
@@ -1850,9 +1935,9 @@ class Form
$out.= $comboenhancement;
$nodatarole=($comboenhancement?' data-role="none"':'');
}
-
+
$out.='