diff --git a/README.md b/README.md
index 564a4171116..b8a764309d1 100644
--- a/README.md
+++ b/README.md
@@ -3,13 +3,13 @@


-Dolibarr ERP & CRM is a modern software package to manage your organization's activity (contacts, suppliers, invoices, orders, stocks, agenda…).
+Dolibarr ERP & CRM is a modern software package that helps manage your organization's activity (contacts, suppliers, invoices, orders, stocks, agenda…).
-It's an Open Source Software (written in PHP with optional JavaScript enhancements) designed for small, medium or large companies, foundations and freelancers.
+It's an Open Source Software suite (written in PHP with optional JavaScript enhancements) designed for small, medium or large companies, foundations and freelancers.
-You can freely use, study, modify or distribute it according to its Free Software licence.
+You can freely use, study, modify or distribute it according to its licence.
-You can use it as a standalone application or as a web application to be able to access it from the Internet or a LAN.
+You can use it as a standalone application or as a web application to access it from the Internet or a LAN.

@@ -35,11 +35,15 @@ Releases can be downloaded from [official website](https://www.dolibarr.org/).
### Advanced setup
-You can use a Web server and a supported database (MariaDB, MySQL or PostgreSQL) to install the standard version.
+You can use a web server and a supported database (MariaDB, MySQL or PostgreSQL) to install the standard version.
+
+On GNU/Linux, first check if your distribution has already packaged Dolibarr.
+
+#### Generic install steps:
- Check that your installed PHP version is supported [see PHP support](https://wiki.dolibarr.org/index.php/Versions).
-- Uncompress the downloaded .zip archive to copy the "dolibarr/htdocs" directory and all its files inside your web server root or get the files directly from GitHub (recommanded if you known git):
+- Uncompress the downloaded .zip archive to copy the "dolibarr/htdocs" directory and all its files inside your web server root or get the files directly from GitHub (recommanded if you know git as it makes it easier if you want to upgrade later):
`git clone https://github.com/dolibarr/dolibarr -b x.y` (where x.y is main version like 3.6, 9.0, ...)
@@ -70,11 +74,11 @@ If you don't have time to install it yourself, you can try some commercial 'read
## UPGRADING
-- At first make a backup of your Dolibarr files & than see https://wiki.dolibarr.org/index.php/Installation_-_Upgrade#Upgrade_Dolibarr
+- At first make a backup of your Dolibarr files & then read https://wiki.dolibarr.org/index.php/Installation_-_Upgrade#Upgrade_Dolibarr
- Check that your installed PHP version is supported by the new version [see PHP support](./doc/phpmatrix.md).
- Overwrite all old files from 'dolibarr' directory with files provided into the new version's package.
-- At first next access, Dolibarr will redirect your to the "install/" page to follow the upgrade process.
- If an `install.lock` file exists to lock any other upgrade process, the application will ask you to remove the file manually (you should find the `install.lock` file into the directory used to store generated and uploaded documents, in most cases, it is the directory called "*documents*").
+- At first next access, Dolibarr will redirect you to the "install/" page to follow the upgrade process.
+ If an `install.lock` file exists to lock any other upgrade process, the application will ask you to remove the file manually (you should find the `install.lock` file in the directory used to store generated and uploaded documents, in most cases, it is the directory called "*documents*").
*Note: migration process can be safely done multiple times by calling the `/install/index.php` page*
@@ -139,7 +143,7 @@ See the [ChangeLog](https://github.com/Dolibarr/dolibarr/blob/develop/ChangeLog)
- Highly customizable: enable only the modules you need, add user personalized fields, choose your skin, several menu managers (can be used by internal users as a back-office with a particular menu, or by external users as a front-office with another one)
- APIs
-- An easy to understand, maintain and develop code (PHP with no heavy framework; trigger and hook architecture)
+- Code that is easy to understand, maintain and develop (PHP with no heavy framework; trigger and hook architecture)
- Support a lot of country specific features:
- Spanish Tax RE and ISPF
- French NPR VAT rate (VAT called "Non Perçue Récupérable" for DOM-TOM)
@@ -149,7 +153,7 @@ See the [ChangeLog](https://github.com/Dolibarr/dolibarr/blob/develop/ChangeLog)
- Compatible with [European directives](http://europa.eu/legislation_summaries/taxation/l31057_en.htm) (2006/112/CE ... 2010/45/UE)
- Compatible with European GDPR rules
- ...
-- PDF or ODT generation for invoice, proposals, orders...
+- Flexible PDF & ODT generation for invoices, proposals, orders...
- …
### System Environment / Requirements
@@ -167,12 +171,12 @@ These are features that Dolibarr does **not** yet fully support:
- Tasks dependencies in projects
- Payroll module
-- No native embedded Webmail
+- No native embedded Webmail, but you can send email to contacts in Dolibarr with e.g. offers, invoices, etc.
- Dolibarr can't do coffee (yet)
## DOCUMENTATION
-Administrator, user, developer and translator's documentations are available along with other community resources on the [Wiki](https://wiki.dolibarr.org).
+Administrator, user, developer and translator's documentations are available along with other community resources in the [Wiki](https://wiki.dolibarr.org).
## CONTRIBUTING
@@ -182,7 +186,7 @@ This project exists thanks to all the people who contribute. [[Contribute](https
## CREDITS
-Dolibarr is the work of many contributors over the years and uses some fine libraries.
+Dolibarr is the work of many contributors over the years and uses some fine PHP libraries.
See [COPYRIGHT](https://github.com/Dolibarr/dolibarr/blob/develop/COPYRIGHT) file.
diff --git a/build/debian/control b/build/debian/control
index 970f192f094..0ef5367072f 100755
--- a/build/debian/control
+++ b/build/debian/control
@@ -14,7 +14,7 @@ Architecture: all
Depends: libapache2-mod-php5 | libapache2-mod-php5filter | php5-cgi | php5-fpm | php5 | libapache2-mod-php | libapache2-mod-phpfilter | php-cgi | php-fpm | php,
php5-cli | php-cli,
# Required PHP extensions
- php5-mysql | php5-mysqli | php-mysql | php-mysqli, php5-curl | php-curl, php5-gd | php-gd, php5-ldap | php-gd,
+ php5-mysql | php5-mysqli | php-mysql | php-mysqli, php5-curl | php-curl, php5-gd | php-gd, php5-ldap | php-gd, php5-zip | php-zip,
# Required PHP libraries
php-pear, php-mail-mime,
# php-tcpdf,
diff --git a/htdocs/accountancy/admin/fiscalyear.php b/htdocs/accountancy/admin/fiscalyear.php
index b5cc46a3d0d..89aad6e2d0d 100644
--- a/htdocs/accountancy/admin/fiscalyear.php
+++ b/htdocs/accountancy/admin/fiscalyear.php
@@ -79,6 +79,7 @@ $object = new Fiscalyear($db);
$max = 100;
$form = new Form($db);
+$fiscalyearstatic = new Fiscalyear($db);
$title = $langs->trans('AccountingPeriods');
$helpurl = "";
@@ -132,13 +133,15 @@ if ($result)
print '';
if ($num) {
- $fiscalyearstatic = new Fiscalyear($db);
-
while ($i < $num && $i < $max) {
$obj = $db->fetch_object($result);
+
$fiscalyearstatic->id = $obj->rowid;
+
print '
';
- print ''.img_object($langs->trans("ShowFiscalYear"), "technic").' '.$obj->rowid.' ';
+ print '';
+ print $fiscalyearstatic->getNomUrl(1);
+ print ' ';
print ''.$obj->label.' ';
print ''.dol_print_date($db->jdate($obj->date_start), 'day').' ';
print ''.dol_print_date($db->jdate($obj->date_end), 'day').' ';
diff --git a/htdocs/admin/modules.php b/htdocs/admin/modules.php
index bd30d863bdc..3c525200442 100644
--- a/htdocs/admin/modules.php
+++ b/htdocs/admin/modules.php
@@ -38,7 +38,7 @@ require_once DOL_DOCUMENT_ROOT.'/admin/dolistore/class/dolistore.class.php';
// Load translation files required by the page
$langs->loadLangs(array("errors", "admin", "modulebuilder"));
-$mode = GETPOSTISSET('mode') ? GETPOST('mode', 'alpha') : 'commonkanban';
+$mode = GETPOSTISSET('mode') ? GETPOST('mode', 'alpha') : (empty($conf->global->MAIN_MODULE_SETUP_ON_LIST_BY_DEFAULT) ? 'commonkanban' : 'common');
if (empty($mode)) $mode = 'common';
$action = GETPOST('action', 'alpha');
//var_dump($_POST);exit;
diff --git a/htdocs/bom/bom_card.php b/htdocs/bom/bom_card.php
index 202373dd302..6826204804f 100644
--- a/htdocs/bom/bom_card.php
+++ b/htdocs/bom/bom_card.php
@@ -192,10 +192,10 @@ if (empty($reshook))
$error = 0;
// Set if we used free entry or predefined product
- $qty = GETPOST('qty', 'int');
+ $qty = price2num(GETPOST('qty', 'int'));
$qty_frozen = GETPOST('qty_frozen', 'int');
$disable_stock_change = GETPOST('disable_stock_change', 'int');
- $efficiency = GETPOST('efficiency', 'int');
+ $efficiency = price2num(GETPOST('efficiency', 'int'));
if ($qty == '') {
setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Qty')), null, 'errors');
@@ -219,6 +219,8 @@ if (empty($reshook))
unset($_POST['qty']);
unset($_POST['qty_frozen']);
unset($_POST['disable_stock_change']);
+
+ $object->fetchLines();
}
}
}
@@ -541,7 +543,7 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea
if (!empty($object->table_element_line))
{
- print ' \n";
diff --git a/htdocs/takepos/admin/receipt.php b/htdocs/takepos/admin/receipt.php
index 59e0f3da96c..252781ec635 100644
--- a/htdocs/takepos/admin/receipt.php
+++ b/htdocs/takepos/admin/receipt.php
@@ -87,6 +87,7 @@ print ' ';
print load_fiche_titre($langs->trans("PrintMethod"), '', '');
+print '';
print '
';
print '';
print ''.$langs->trans("Name").' '.$langs->trans("Description").' '.$langs->trans("Status").' ';
@@ -143,14 +144,25 @@ if ($conf->global->TAKEPOS_PRINT_METHOD == "takeposconnector")
}
print " \n";
print '
';
+print '
';
+
print load_fiche_titre($langs->trans("Setup"), '', '');
+print '';
print '
';
print '';
print ''.$langs->trans("Parameters").' '.$langs->trans("Value").' ';
print " \n";
+// VAT Grouped on ticket
+print '';
+print $langs->trans('TicketVatGrouped');
+print ' ';
+print ajax_constantonoff("TAKEPOS_TICKET_VAT_GROUPPED", array(), $conf->entity, 0, 0, 1, 0);
+//print $form->selectyesno("TAKEPOS_TICKET_VAT_GROUPPED", $conf->global->TAKEPOS_TICKET_VAT_GROUPPED, 1);
+print " \n";
+
if ($conf->global->TAKEPOS_PRINT_METHOD == "takeposconnector") {
print '';
print $langs->trans("URL")." / ".$langs->trans("IPAddress").' ('.$langs->trans("TakeposConnectorNecesary").' )';
@@ -214,6 +226,7 @@ print $form->selectyesno("TAKEPOS_AUTO_PRINT_TICKETS", $conf->global->TAKEPOS_AU
print " \n";
print '
';
+print '
';
print ' ';
diff --git a/htdocs/takepos/admin/setup.php b/htdocs/takepos/admin/setup.php
index 22355c70d9d..ab3b200df35 100644
--- a/htdocs/takepos/admin/setup.php
+++ b/htdocs/takepos/admin/setup.php
@@ -274,14 +274,6 @@ print img_object('', 'category', 'class="paddingright"').$form->select_all_categ
print ajax_combobox('TAKEPOS_ROOT_CATEGORY_ID');
print " \n";
-// VAT Grouped on ticket
-print '';
-print $langs->trans('TicketVatGrouped');
-print ' ';
-print ajax_constantonoff("TAKEPOS_TICKET_VAT_GROUPPED", array(), $conf->entity, 0, 0, 1, 0);
-//print $form->selectyesno("TAKEPOS_TICKET_VAT_GROUPPED", $conf->global->TAKEPOS_TICKET_VAT_GROUPPED, 1);
-print " \n";
-
// Sort product
print '';
print $langs->trans("SortProductField");
@@ -429,14 +421,6 @@ print " \n";
//print $form->selectarray('TAKEPOS_ADDON', $array, (empty($conf->global->TAKEPOS_ADDON) ? '0' : $conf->global->TAKEPOS_ADDON), 0);
//print "\n";
-print '';
-print $langs->trans("EnableBarOrRestaurantFeatures");
-print ' ';
-print '';
-print ajax_constantonoff("TAKEPOS_BAR_RESTAURANT", array(), $conf->entity, 0, 0, 1, 0);
-//print $form->selectyesno("TAKEPOS_BAR_RESTAURANT", $conf->global->TAKEPOS_BAR_RESTAURANT, 1);
-print " \n";
-
print '';
print '';
diff --git a/htdocs/takepos/invoice.php b/htdocs/takepos/invoice.php
index cfde72c9fd3..350299814ec 100644
--- a/htdocs/takepos/invoice.php
+++ b/htdocs/takepos/invoice.php
@@ -398,6 +398,7 @@ if ($action == "deleteline") {
$invoice->deleteline($deletelineid);
$invoice->fetch($placeid);
}
+ if (count($invoice->lines)==0) $invoice->delete($user);
}
if ($action == "delete") {
diff --git a/htdocs/theme/eldy/info-box.inc.php b/htdocs/theme/eldy/info-box.inc.php
index b009d7ef95b..45be80d026b 100644
--- a/htdocs/theme/eldy/info-box.inc.php
+++ b/htdocs/theme/eldy/info-box.inc.php
@@ -77,7 +77,12 @@ if (!defined('ISLOADEDBYSTEELSHEET')) die('Must be call by steelsheet'); ?>
text-align: center;
font-size: 45px;
line-height: 90px;
- background: rgba(0, 0, 0, 0.08) !important
+ background: rgba(0, 0, 0, 0.08) !important;
+}
+
+.info-box-module .info-box-icon {
+ padding-top: 5px;
+ padding-bottom: 5px;
}
.info-box-sm .info-box-icon {
height: 80px;
@@ -436,7 +441,7 @@ if (GETPOSTISSET('THEME_SATURATE_RATIO')) $conf->global->THEME_SATURATE_RATIO =
.info-box-module .info-box-content {
- height: 6.4em;
+ height: 98px;
}
/* Disabled. This break the responsive on smartphone
.box{
diff --git a/htdocs/theme/eldy/theme_vars.inc.php b/htdocs/theme/eldy/theme_vars.inc.php
index cd88654e309..c28b9a21e0c 100644
--- a/htdocs/theme/eldy/theme_vars.inc.php
+++ b/htdocs/theme/eldy/theme_vars.inc.php
@@ -71,7 +71,7 @@ $colortexttitle = '0,0,0';
$colortexttitlelink = '10, 20, 100';
$colortext = '0,0,0';
$colortextlink = '10, 20, 100';
-$fontsize = '0.86em';
+$fontsize = '0.95em';
$fontsizesmaller = '0.75em';
$topMenuFontSize = '1.1em';
$toolTipBgColor = 'rgba(255, 255, 255, 0.96)';
diff --git a/htdocs/variants/card.php b/htdocs/variants/card.php
index 0669964ba57..6d077e85ad1 100644
--- a/htdocs/variants/card.php
+++ b/htdocs/variants/card.php
@@ -90,9 +90,9 @@ if ($confirm == 'yes') {
if ($action == 'confirm_delete') {
$db->begin();
- $res = $objectval->deleteByFkAttribute($object->id);
+ $res = $objectval->deleteByFkAttribute($object->id, $user);
- if ($res < 1 || ($object->delete() < 1)) {
+ if ($res < 1 || ($object->delete($user) < 1)) {
$db->rollback();
setEventMessages($langs->trans('CoreErrorMessage'), $object->errors, 'errors');
header('Location: '.dol_buildpath('/variants/card.php?id='.$object->id, 2));
@@ -105,7 +105,7 @@ if ($confirm == 'yes') {
} elseif ($action == 'confirm_deletevalue')
{
if ($objectval->fetch($valueid) > 0) {
- if ($objectval->delete() < 1) {
+ if ($objectval->delete($user) < 1) {
setEventMessages($langs->trans('CoreErrorMessage'), $objectval->errors, 'errors');
} else {
setEventMessages($langs->trans('RecordSaved'), null, 'mesgs');
diff --git a/htdocs/variants/class/ProductAttribute.class.php b/htdocs/variants/class/ProductAttribute.class.php
index 36b4823b51b..6e2335c2f99 100644
--- a/htdocs/variants/class/ProductAttribute.class.php
+++ b/htdocs/variants/class/ProductAttribute.class.php
@@ -16,17 +16,18 @@
* along with this program. If not, see .
*/
+require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
/**
* Class ProductAttribute
* Used to represent a product attribute
*/
-class ProductAttribute
+class ProductAttribute extends CommonObject
{
/**
* Database handler
* @var DoliDB
*/
- private $db;
+ public $db;
/**
* Id of the product attribute
@@ -119,7 +120,8 @@ class ProductAttribute
$return[] = $tmp;
}
- } else dol_print_error($this->db);
+ }
+ else dol_print_error($this->db);
return $return;
}
@@ -127,11 +129,21 @@ class ProductAttribute
/**
* Creates a product attribute
*
- * @param User $user Object user that create
+ * @param User $user Object user
+ * @param int $notrigger Do not execute trigger
* @return int <0 KO, Id of new variant if OK
*/
- public function create(User $user)
+ public function create(User $user, $notrigger = 0)
{
+ if (empty($notrigger)) {
+ // Call trigger
+ $result = $this->call_trigger('PRODUCT_ATTRIBUTE_CREATE', $user);
+ if ($result < 0) {
+ return -1;
+ }
+ // End call triggers
+ }
+
//Ref must be uppercase
$this->ref = strtoupper($this->ref);
@@ -152,11 +164,21 @@ class ProductAttribute
/**
* Updates a product attribute
*
- * @param User $user Object user
+ * @param User $user Object user
+ * @param int $notrigger Do not execute trigger
* @return int <0 KO, >0 OK
*/
- public function update(User $user)
+ public function update(User $user, $notrigger = 0)
{
+ if (empty($notrigger)) {
+ // Call trigger
+ $result = $this->call_trigger('PRODUCT_ATTRIBUTE_MODIFY', $user);
+ if ($result < 0) {
+ return -1;
+ }
+ // End call triggers
+ }
+
//Ref must be uppercase
$this->ref = trim(strtoupper($this->ref));
$this->label = trim($this->label);
@@ -173,11 +195,21 @@ class ProductAttribute
/**
* Deletes a product attribute
*
- * @param User $user Object user
- * @return int <0 KO, >0 OK
+ * @param User $user Object user
+ * @param int $notrigger Do not execute trigger
+ * @return int <0 KO, >0 OK
*/
- public function delete($user = null)
+ public function delete(User $user, $notrigger = 0)
{
+ if (empty($notrigger)) {
+ // Call trigger
+ $result = $this->call_trigger('PRODUCT_ATTRIBUTE_DELETE', $user);
+ if ($result < 0) {
+ return -1;
+ }
+ // End call triggers
+ }
+
$sql = "DELETE FROM ".MAIN_DB_PREFIX."product_attribute WHERE rowid = ".(int) $this->id;
if ($this->db->query($sql)) {
diff --git a/htdocs/variants/class/ProductAttributeValue.class.php b/htdocs/variants/class/ProductAttributeValue.class.php
index 0a26b3eace2..ab998c6752b 100644
--- a/htdocs/variants/class/ProductAttributeValue.class.php
+++ b/htdocs/variants/class/ProductAttributeValue.class.php
@@ -16,17 +16,18 @@
* along with this program. If not, see .
*/
+require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
/**
* Class ProductAttributeValue
* Used to represent a product attribute value
*/
-class ProductAttributeValue
+class ProductAttributeValue extends CommonObject
{
/**
* Database handler
* @var DoliDB
*/
- private $db;
+ public $db;
/**
* Attribute value id
@@ -144,10 +145,11 @@ class ProductAttributeValue
/**
* Creates a value for a product attribute
*
- * @param User $user Object user
- * @return int <0 KO >0 OK
+ * @param User $user Object user
+ * @param int $notrigger Do not execute trigger
+ * @return int <0 KO >0 OK
*/
- public function create(User $user)
+ public function create(User $user, $notrigger = 0)
{
if (!$this->fk_product_attribute) {
return -1;
@@ -155,15 +157,25 @@ class ProductAttributeValue
// Ref must be uppercase
$this->ref = strtoupper($this->ref);
+ $this->value = $this->db->escape($this->value);
$sql = "INSERT INTO ".MAIN_DB_PREFIX."product_attribute_value (fk_product_attribute, ref, value, entity)
VALUES ('".(int) $this->fk_product_attribute."', '".$this->db->escape($this->ref)."',
- '".$this->db->escape($this->value)."', ".(int) $this->entity.")";
+ '".$this->value."', ".(int) $this->entity.")";
$query = $this->db->query($sql);
if ($query) {
$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'product_attribute_value');
+ if (empty($notrigger)) {
+ // Call trigger
+ $result = $this->call_trigger('PRODUCT_ATTRIBUTE_VALUE_CREATE', $user);
+ if ($result < 0) {
+ return -1;
+ }
+ // End call triggers
+ }
+
return 1;
}
@@ -173,11 +185,21 @@ class ProductAttributeValue
/**
* Updates a product attribute value
*
- * @param User $user Object user
- * @return int <0 if KO, >0 if OK
+ * @param User $user Object user
+ * @param int $notrigger Do not execute trigger
+ * @return int <0 if KO, >0 if OK
*/
- public function update(User $user)
+ public function update(User $user, $notrigger = 0)
{
+ if (empty($notrigger)) {
+ // Call trigger
+ $result = $this->call_trigger('PRODUCT_ATTRIBUTE_VALUE_MODIFY', $user);
+ if ($result < 0) {
+ return -1;
+ }
+ // End call triggers
+ }
+
//Ref must be uppercase
$this->ref = trim(strtoupper($this->ref));
$this->value = trim($this->value);
@@ -196,33 +218,62 @@ class ProductAttributeValue
/**
* Deletes a product attribute value
*
+ * @param User $user Object user
+ * @param int $notrigger Do not execute trigger
* @return int <0 KO, >0 OK
*/
- public function delete()
+ public function delete(User $user, $notrigger = 0)
{
- $sql = "DELETE FROM ".MAIN_DB_PREFIX."product_attribute_value WHERE rowid = ".(int) $this->id;
- if ($this->db->query($sql)) {
- return 1;
- }
+ if (empty($notrigger)) {
+ // Call trigger
+ $result = $this->call_trigger('PRODUCT_ATTRIBUTE_VALUE_DELETE', $user);
+ if ($result < 0) {
+ return -1;
+ }
+ // End call triggers
+ }
+ $sql = "DELETE FROM ".MAIN_DB_PREFIX."product_attribute_value WHERE rowid = ".(int) $this->id;
+ if ($this->db->query($sql)) {
+ return 1;
+ }
- return -1;
+ return -1;
}
/**
* Deletes all product attribute values by a product attribute id
*
- * @param int $fk_attribute Product attribute id
+ * @param int $fk_attribute Product attribute id
+ * @param User $user Object user
* @return int <0 KO, >0 OK
*/
- public function deleteByFkAttribute($fk_attribute)
+ public function deleteByFkAttribute($fk_attribute, User $user)
{
- $sql = "DELETE FROM ".MAIN_DB_PREFIX."product_attribute_value WHERE fk_product_attribute = ".(int) $fk_attribute;
+ $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."product_attribute_value WHERE fk_product_attribute = ".(int) $fk_attribute;
- if ($this->db->query($sql)) {
- return 1;
- }
+ $query = $this->db->query($sql);
- return -1;
+ if (!$query) {
+ return -1;
+ }
+
+ if (!$this->db->num_rows($query)) {
+ return 1;
+ }
+
+ while ($obj = $this->db->fetch_object($query)) {
+ $tmp = new ProductAttributeValue($this->db);
+ if ($tmp->fetch($obj->rowid) > 0) {
+ $result = $tmp->delete($user);
+ if ($result < 0) {
+ return -1;
+ }
+ } else {
+ return -1;
+ }
+ }
+
+ return 1;
}
}
diff --git a/htdocs/variants/class/ProductCombination.class.php b/htdocs/variants/class/ProductCombination.class.php
index 3bb4f7312ee..7d6a07d736d 100644
--- a/htdocs/variants/class/ProductCombination.class.php
+++ b/htdocs/variants/class/ProductCombination.class.php
@@ -71,6 +71,12 @@ class ProductCombination
*/
public $entity;
+ /**
+ * Combination price level
+ * @var ProductCombinationLevel[]
+ */
+ public $combination_price_levels;
+
/**
* Constructor
*
@@ -92,6 +98,8 @@ class ProductCombination
*/
public function fetch($rowid)
{
+ global $conf;
+
$sql = "SELECT rowid, fk_product_parent, fk_product_child, variation_price, variation_price_percentage, variation_weight FROM ".MAIN_DB_PREFIX."product_attribute_combination WHERE rowid = ".(int) $rowid." AND entity IN (".getEntity('product').")";
$query = $this->db->query($sql);
@@ -113,9 +121,118 @@ class ProductCombination
$this->variation_price_percentage = $obj->variation_price_percentage;
$this->variation_weight = $obj->variation_weight;
+ if (!empty($conf->global->PRODUIT_MULTIPRICES)) {
+ $this->fetchCombinationPriceLevels();
+ }
+
return 1;
}
+
+ /**
+ * Retrieves combination price levels
+ *
+ * @param int $fk_price_level the price level to fetch, use 0 for all
+ * @param bool $useCache to use cache or not
+ * @return int <0 KO, >0 OK
+ */
+ public function fetchCombinationPriceLevels($fk_price_level = 0, $useCache = true)
+ {
+ global $conf;
+
+ // Check cache
+ if (!empty($this->combination_price_levels) && $useCache){
+ if ((!empty($fk_price_level) && isset($this->combination_price_levels[$fk_price_level])) || empty($fk_price_level)){
+ return 1;
+ }
+ }
+
+ if (!is_array($this->combination_price_levels)
+ || empty($fk_price_level) // if fetch an unique level dont erase all already fetched
+ ){
+ $this->combination_price_levels = array();
+ }
+
+ $staticProductCombinationLevel = new ProductCombinationLevel($this->db);
+ $combination_price_levels = $staticProductCombinationLevel->fetchAll($this->id, $fk_price_level);
+
+ if (!is_array($combination_price_levels)) {
+ return -1;
+ }
+
+ if (empty($combination_price_levels)){
+
+ /**
+ * for auto retrocompatibility with last behavior
+ */
+ $productCombinationLevel = new ProductCombinationLevel($this->db);
+ $productCombinationLevel->fk_price_level = intval($fk_price_level);
+ $productCombinationLevel->fk_product_attribute_combination = $this->id;
+ $productCombinationLevel->variation_price = $this->variation_price;
+ $productCombinationLevel->variation_price_percentage = $this->variation_price_percentage;
+
+ if ($fk_price_level>0){
+ $combination_price_levels[$fk_price_level] = $productCombinationLevel;
+ }
+ else {
+ for ($i = 1; $i <= $conf->global->PRODUIT_MULTIPRICES_LIMIT; $i++){
+ $combination_price_levels[$i] = $productCombinationLevel;
+ }
+ }
+ }
+
+ $this->combination_price_levels = $combination_price_levels;
+
+ return 1;
+ }
+
+ /**
+ * Retrieves combination price levels
+ *
+ * @param int $clean levels off PRODUIT_MULTIPRICES_LIMIT
+ * @return int <0 KO, >0 OK
+ */
+ public function saveCombinationPriceLevels($clean = 1)
+ {
+ global $conf;
+
+ $errors = 0;
+
+ $staticProductCombinationLevel = new ProductCombinationLevel($this->db);
+
+ // Delete all
+ if (empty($this->combination_price_levels)){
+ return $staticProductCombinationLevel->deleteAllForCombination($this->id);
+ }
+
+ // Clean not needed price levels
+ if ($clean){
+ $res = $staticProductCombinationLevel->clean($this->id);
+
+ if ($res<0){
+ $this->errors[] = 'Fail to clean not needed price levels';
+ return -1;
+ }
+ }
+
+ foreach ($this->combination_price_levels as $fk_price_level => $combination_price_level){
+ $res = $combination_price_level->save();
+ if ($res<1){
+ $this->error = 'save combination price level '.$fk_price_level . ' '.$combination_price_level->error;
+ $this->errors[] = $this->error;
+ $errors ++;
+ }
+ }
+
+ if ($errors > 0){
+ return $errors*-1;
+ }
+ else {
+ return 1;
+ }
+ }
+
+
/**
* Retrieves a product combination by a child product row id
*
@@ -124,6 +241,8 @@ class ProductCombination
*/
public function fetchByFkProductChild($fk_child)
{
+ global $conf;
+
$sql = "SELECT rowid, fk_product_parent, fk_product_child, variation_price, variation_price_percentage, variation_weight FROM ".MAIN_DB_PREFIX."product_attribute_combination WHERE fk_product_child = ".(int) $fk_child." AND entity IN (".getEntity('product').")";
$query = $this->db->query($sql);
@@ -145,6 +264,10 @@ class ProductCombination
$this->variation_price_percentage = $result->variation_price_percentage;
$this->variation_weight = $result->variation_weight;
+ if (!empty($conf->global->PRODUIT_MULTIPRICES)) {
+ $this->fetchCombinationPriceLevels();
+ }
+
return 1;
}
@@ -156,6 +279,8 @@ class ProductCombination
*/
public function fetchAllByFkProductParent($fk_product_parent)
{
+ global $conf;
+
$sql = "SELECT rowid, fk_product_parent, fk_product_child, variation_price, variation_price_percentage, variation_weight FROM ".MAIN_DB_PREFIX."product_attribute_combination WHERE fk_product_parent = ".(int) $fk_product_parent." AND entity IN (".getEntity('product').")";
$query = $this->db->query($sql);
@@ -175,6 +300,10 @@ class ProductCombination
$tmp->variation_price_percentage = $result->variation_price_percentage;
$tmp->variation_weight = $result->variation_weight;
+ if (!empty($conf->global->PRODUIT_MULTIPRICES)) {
+ $tmp->fetchCombinationPriceLevels();
+ }
+
$return[] = $tmp;
}
@@ -209,6 +338,8 @@ class ProductCombination
*/
public function create($user)
{
+ global $conf;
+
$sql = "INSERT INTO ".MAIN_DB_PREFIX."product_attribute_combination
(fk_product_parent, fk_product_child, variation_price, variation_price_percentage, variation_weight, entity)
VALUES (".(int) $this->fk_product_parent.", ".(int) $this->fk_product_child.",
@@ -221,6 +352,13 @@ class ProductCombination
return -1;
}
+ if (!empty($conf->global->PRODUIT_MULTIPRICES)) {
+ $res = $this->saveCombinationPriceLevels();
+ if ($res<0){
+ return -2;
+ }
+ }
+
$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'product_attribute_combination');
return 1;
@@ -234,6 +372,8 @@ class ProductCombination
*/
public function update(User $user)
{
+ global $conf;
+
$sql = "UPDATE ".MAIN_DB_PREFIX."product_attribute_combination
SET fk_product_parent = ".(int) $this->fk_product_parent.", fk_product_child = ".(int) $this->fk_product_child.",
variation_price = ".(float) $this->variation_price.", variation_price_percentage = ".(int) $this->variation_price_percentage.",
@@ -245,6 +385,14 @@ class ProductCombination
return -1;
}
+
+ if (!empty($conf->global->PRODUIT_MULTIPRICES)) {
+ $res = $this->saveCombinationPriceLevels();
+ if ($res<0){
+ return -2;
+ }
+ }
+
$parent = new Product($this->db);
$parent->fetch($this->fk_product_parent);
@@ -266,6 +414,12 @@ class ProductCombination
$comb2val = new ProductCombination2ValuePair($this->db);
$comb2val->deleteByFkCombination($this->id);
+ // remove combination price levels
+ if (!$this->db->query("DELETE FROM ".MAIN_DB_PREFIX."product_attribute_combination_price_level WHERE fk_product_attribute_combination = ".(int) $this->id)) {
+ $this->db->rollback();
+ return -1;
+ }
+
$sql = "DELETE FROM ".MAIN_DB_PREFIX."product_attribute_combination WHERE rowid = ".(int) $this->id;
if ($this->db->query($sql)) {
@@ -341,6 +495,7 @@ class ProductCombination
$child->label = $parent->label.$varlabel;;
}
+
if ($child->update($child->id, $user) > 0) {
$new_vat = $parent->tva_tx;
$new_npr = $parent->tva_npr;
@@ -349,9 +504,12 @@ class ProductCombination
if (!empty($conf->global->PRODUIT_MULTIPRICES)) {
for ($i = 1; $i <= $conf->global->PRODUIT_MULTIPRICES_LIMIT; $i++)
{
- if ($parent->multiprices[$i] != '') {
+ if ($parent->multiprices[$i] != '' || isset($this->combination_price_levels[$i]->variation_price)) {
$new_type = $parent->multiprices_base_type[$i];
$new_min_price = $parent->multiprices_min[$i];
+ $variation_price = doubleval(!isset($this->combination_price_levels[$i]->variation_price) ? $this->variation_price : $this->combination_price_levels[$i]->variation_price);
+ $variation_price_percentage = doubleval(!isset($this->combination_price_levels[$i]->variation_price_percentage) ? $this->variation_price_percentage : $this->combination_price_levels[$i]->variation_price_percentage);
+
if ($parent->prices_by_qty_list[$i]) {
$new_psq = 1;
} else {
@@ -364,12 +522,12 @@ class ProductCombination
$new_price = $parent->multiprices[$i];
}
- if ($this->variation_price_percentage) {
+ if ($variation_price_percentage) {
if ($new_price != 0) {
- $new_price *= 1 + ($this->variation_price / 100);
+ $new_price *= 1 + ($variation_price / 100);
}
} else {
- $new_price += $this->variation_price;
+ $new_price += $variation_price;
}
$child->updatePrice($new_price, $new_type, $user, $new_vat, $new_min_price, $i, $new_npr, $new_psq);
@@ -508,7 +666,7 @@ WHERE c.fk_product_parent = ".(int) $productid." AND p.tosell = 1";
* @param Product $product Parent product
* @param array $combinations Attribute and value combinations.
* @param array $variations Price and weight variations
- * @param bool $price_var_percent Is the price variation a relative variation?
+ * @param bool|array $price_var_percent Is the price variation a relative variation?
* @param bool|float $forced_pricevar If the price variation is forced
* @param bool|float $forced_weightvar If the weight variation is forced
* @param bool|string $forced_refvar If the reference is forced
@@ -523,6 +681,8 @@ WHERE c.fk_product_parent = ".(int) $productid." AND p.tosell = 1";
$db->begin();
+ $price_impact = array(1=>0); // init level price impact
+
$forced_refvar = trim($forced_refvar);
if (!empty($forced_refvar) && $forced_refvar != $product->ref) {
@@ -545,7 +705,12 @@ WHERE c.fk_product_parent = ".(int) $productid." AND p.tosell = 1";
$weight_impact = (float) $forced_weightvar; // If false, return 0
//Final price impact
- $price_impact = (float) $forced_pricevar; // If false, return 0
+ if (!is_array($forced_pricevar)){
+ $price_impact[1] = (float) $forced_pricevar; // If false, return 0
+ }
+ else {
+ $price_impact = $forced_pricevar;
+ }
$newcomb = new ProductCombination($db);
$existingCombination = $newcomb->fetchByProductCombination2ValuePairs($product->id, $combinations);
@@ -587,7 +752,15 @@ WHERE c.fk_product_parent = ".(int) $productid." AND p.tosell = 1";
$weight_impact += (float) price2num($variations[$currcombattr][$currcombval]['weight']);
}
if ($forced_pricevar === false) {
- $price_impact += (float) price2num($variations[$currcombattr][$currcombval]['price']);
+ $price_impact[1] += (float) price2num($variations[$currcombattr][$currcombval]['price']);
+
+ // Manage Price levels
+ if ($conf->global->PRODUIT_MULTIPRICES){
+ for ($i = 2; $i <= $conf->global->PRODUIT_MULTIPRICES_LIMIT; $i++)
+ {
+ $price_impact[$i] += (float) price2num($variations[$currcombattr][$currcombval]['price']);
+ }
+ }
}
if ($forced_refvar === false) {
@@ -606,9 +779,27 @@ WHERE c.fk_product_parent = ".(int) $productid." AND p.tosell = 1";
}
$newcomb->variation_price_percentage = $price_var_percent;
- $newcomb->variation_price = $price_impact;
+ $newcomb->variation_price = $price_impact[1];
$newcomb->variation_weight = $weight_impact;
+ // Init price level
+ if ($conf->global->PRODUIT_MULTIPRICES){
+ for ($i = 1; $i <= $conf->global->PRODUIT_MULTIPRICES_LIMIT; $i++){
+ $productCombinationLevel = new ProductCombinationLevel($this->db);
+ $productCombinationLevel->fk_product_attribute_combination = 0;
+ $productCombinationLevel->fk_price_level = $i;
+ $productCombinationLevel->variation_price = $price_impact[$i];
+
+ if (is_array($price_var_percent)){
+ $productCombinationLevel->variation_price_percentage = !empty($price_var_percent[$i]) ? $price_var_percent[$i] : 0;
+ }else {
+ $productCombinationLevel->variation_price_percentage = $price_var_percent;
+ }
+
+ $newcomb->combination_price_levels[$i] = $productCombinationLevel;
+ }
+ }
+
$newproduct->weight += $weight_impact;
// Now create the product
@@ -764,3 +955,260 @@ WHERE c.fk_product_parent = ".(int) $productid." AND p.tosell = 1";
return $label;
}
}
+
+
+
+/**
+ * Class ProductCombinationLevel
+ * Used to represent a product combination Level
+ */
+class ProductCombinationLevel
+{
+ /**
+ * Database handler
+ * @var DoliDB
+ */
+ private $db;
+
+ /**
+ * @var string Name of table without prefix where object is stored
+ */
+ public $table_element = 'product_attribute_combination_price_level';
+
+ /**
+ * Rowid of combination
+ * @var int
+ */
+ public $id;
+
+ /**
+ * Rowid of parent product combination
+ * @var int
+ */
+ public $fk_product_attribute_combination;
+
+ /**
+ * Combination price level
+ * @var int
+ */
+ public $fk_price_level;
+
+ /**
+ * Price variation
+ * @var float
+ */
+ public $variation_price;
+
+ /**
+ * Is the price variation a relative variation?
+ * @var bool
+ */
+ public $variation_price_percentage = false;
+
+ /**
+ * Constructor
+ *
+ * @param DoliDB $db Database handler
+ */
+ public function __construct(DoliDB $db)
+ {
+ $this->db = $db;
+ }
+
+ /**
+ * Retrieves a combination level by its rowid
+ *
+ * @param int $rowid Row id
+ * @return int <0 KO, >0 OK
+ */
+ public function fetch($rowid)
+ {
+ $sql = "SELECT rowid, fk_product_attribute_combination, fk_price_level, variation_price, variation_price_percentage FROM " . MAIN_DB_PREFIX . $this->table_element." WHERE rowid = " . (int) $rowid;
+
+ $obj = $this->db->getRow($sql);
+
+ if ($obj){
+ return $this->fetchFormObj($obj);
+ }
+
+ return -1;
+ }
+
+
+ /**
+ * Retrieves combination price levels
+ *
+ * @param int $fk_product_attribute_combination
+ * @param int $fk_price_level the price level to fetch, use 0 for all
+ * @return self[] | -1 on KO
+ */
+ public function fetchAll($fk_product_attribute_combination, $fk_price_level = 0)
+ {
+
+ $sql = "SELECT rowid, fk_product_attribute_combination, fk_price_level, variation_price, variation_price_percentage"
+ ." FROM ".MAIN_DB_PREFIX.$this->table_element
+ ." WHERE fk_product_attribute_combination = ".intval($fk_product_attribute_combination);
+
+ if (!empty($fk_price_level)){
+ $sql.= ' AND fk_price_level = '.intval($fk_price_level);
+ }
+
+ $combination_price_levels = $this->db->getRows($sql);
+
+ if (!is_array($combination_price_levels)) {
+ return -1;
+ }
+
+ $result = array();
+
+ if (!empty($combination_price_levels)) {
+ // For more simple usage set level as array key
+ foreach ($combination_price_levels as $k => $row){
+ $productCombinationLevel = new ProductCombinationLevel($this->db);
+ $productCombinationLevel->fetchFormObj($row);
+ $result[$row->fk_price_level] = $productCombinationLevel;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * assign vars form an stdclass like sql obj
+ *
+ * @param int $rowid Row id
+ * @return int <0 KO, >0 OK
+ */
+ public function fetchFormObj($obj)
+ {
+ if (!$obj) {
+ return -1;
+ }
+
+ $this->id = $obj->rowid;
+ $this->fk_product_attribute_combination = doubleval($obj->fk_product_attribute_combination);
+ $this->fk_price_level = intval($obj->fk_price_level);
+ $this->variation_price = doubleval($obj->variation_price);
+ $this->variation_price_percentage = (bool) $obj->variation_price_percentage;
+
+ return 1;
+ }
+
+
+ /**
+ * save
+ *
+ * @return int <0 KO, >0 OK
+ */
+ public function save()
+ {
+ $errors = 0;
+
+
+ if (empty($this->fk_product_attribute_combination) || empty($this->fk_price_level)){
+ return -1;
+ }
+
+ // check if level exist in DB before add
+ if (empty($this->id)){
+ $sql = "SELECT rowid id"
+ ." FROM ".MAIN_DB_PREFIX . $this->table_element
+ ." WHERE fk_product_attribute_combination = ".(int) $this->fk_product_attribute_combination
+ .' AND fk_price_level = '.intval($this->fk_price_level);
+
+ $existObj = $this->db->getRow($sql);
+ if ($existObj){
+ $this->id = $existObj->id;
+ }
+ }
+
+ // Update
+ if (!empty($this->id)) {
+ $sql = 'UPDATE ' . MAIN_DB_PREFIX . $this->table_element . ' SET '
+ . ' variation_price = '.doubleval($this->variation_price)
+ . ' , variation_price_percentage = '.intval($this->variation_price_percentage)
+ . ' WHERE rowid = '.intval($this->id);
+
+ $res = $this->db->query($sql);
+ if ($res>0){
+ return $this->id;
+ }
+ else {
+ $this->error = $this->db->error();
+ $this->errors[] = $this->error;
+ return -1;
+ }
+ }
+ else {
+ // ADD
+ $sql = "INSERT INTO " . MAIN_DB_PREFIX . $this->table_element . " ("
+ . " fk_product_attribute_combination, fk_price_level, variation_price, variation_price_percentage"
+ . " ) VALUES ( "
+ . intval($this->fk_product_attribute_combination)
+ . ' , '.intval($this->fk_price_level)
+ . ' , '.doubleval($this->variation_price)
+ . ' , '.intval($this->variation_price_percentage)
+ . " )";
+
+ $res = $this->db->query($sql);
+ if ($res){
+ $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . $this->table_element);
+ }
+ else {
+ $this->error = $this->db->error();
+ $this->errors[] = $this->error;
+ return -1;
+ }
+ }
+
+ return $this->id;
+ }
+
+
+ /**
+ * delete
+ *
+ * @return int <0 KO, >0 OK
+ */
+ public function delete()
+ {
+ $res = $this->db->query("DELETE FROM ".MAIN_DB_PREFIX.$this->table_element
+ ." WHERE rowid = ".(int) $this->id);
+
+ return $res ? 1 : -1;
+ }
+
+
+ /**
+ * delete all for a combination
+ *
+ * @param $fk_product_attribute_combination
+ * @return int <0 KO, >0 OK
+ */
+ public function deleteAllForCombination($fk_product_attribute_combination)
+ {
+ $res = $this->db->query("DELETE FROM ".MAIN_DB_PREFIX.$this->table_element
+ ." WHERE fk_product_attribute_combination = ".(int) $fk_product_attribute_combination);
+
+ return $res ? 1 : -1;
+ }
+
+
+ /**
+ * Clean not needed price levels for a combination
+ *
+ * @param $fk_product_attribute_combination
+ * @return int <0 KO, >0 OK
+ */
+ public function clean($fk_product_attribute_combination)
+ {
+ global $conf;
+
+ $res = $this->db->query("DELETE FROM ".MAIN_DB_PREFIX.$this->table_element
+ . " WHERE fk_product_attribute_combination = ".(int) $fk_product_attribute_combination
+ . " AND fk_price_level > ".intval($conf->global->PRODUIT_MULTIPRICES_LIMIT) );
+
+
+ return $res ? 1 : -1;
+ }
+}
diff --git a/htdocs/variants/combinations.php b/htdocs/variants/combinations.php
index 3886a8fcdce..b9f62bdcf73 100644
--- a/htdocs/variants/combinations.php
+++ b/htdocs/variants/combinations.php
@@ -33,6 +33,10 @@ $ref = GETPOST('ref', 'alpha');
$weight_impact = GETPOST('weight_impact', 'alpha');
$price_impact = GETPOST('price_impact', 'alpha');
$price_impact_percent = (bool) GETPOST('price_impact_percent');
+
+$level_price_impact = GETPOST('level_price_impact', 'array');
+$level_price_impact_percent = GETPOST('level_price_impact_percent', 'array');
+
$reference = GETPOST('reference', 'alpha');
$form = new Form($db);
@@ -112,6 +116,18 @@ if ($_POST) {
}
$weight_impact = price2num($weight_impact);
$price_impact = price2num($price_impact);
+
+ // for conf PRODUIT_MULTIPRICES
+ if ($conf->global->PRODUIT_MULTIPRICES) {
+ $level_price_impact = array_map('price2num', $level_price_impact);
+ $level_price_impact_percent = array_map('price2num', $level_price_impact_percent);
+ }
+ else {
+ $level_price_impact = array(1 => $weight_impact);
+ $level_price_impact_percent = array(1 => $price_impact_percent);
+ }
+
+
$sanit_features = array();
//First, sanitize
@@ -141,11 +157,10 @@ if ($_POST) {
// sanit_feature is an array with 1 (and only 1) value per attribute.
// For example: Color->blue, Size->Small, Option->2
//var_dump($sanit_features);
- //var_dump($productCombination2ValuePairs1); exit;
if (!$prodcomb->fetchByProductCombination2ValuePairs($id, $sanit_features))
{
- $result = $prodcomb->createProductCombination($user, $object, $sanit_features, array(), $price_impact_percent, $price_impact, $weight_impact, $reference);
+ $result = $prodcomb->createProductCombination($user, $object, $sanit_features, array(), $level_price_impact_percent, $level_price_impact, $weight_impact, $reference);
if ($result > 0)
{
setEventMessages($langs->trans('RecordSaved'), null, 'mesgs');
@@ -227,6 +242,32 @@ if ($_POST) {
$prodcomb->variation_price = $price_impact;
$prodcomb->variation_weight = $weight_impact;
+ // for conf PRODUIT_MULTIPRICES
+ if ($conf->global->PRODUIT_MULTIPRICES) {
+ $level_price_impact = array_map('price2num', $level_price_impact);
+ $level_price_impact_percent = array_map(function ($a) {
+ return !empty($a);}, $level_price_impact_percent);
+
+ $prodcomb->variation_price = $level_price_impact[1];
+ $prodcomb->variation_price_percentage = (bool) $level_price_impact_percent[1];
+ }
+ else {
+ $level_price_impact = array(1 => $weight_impact);
+ $level_price_impact_percent = array(1 => $price_impact_percent);
+ }
+
+ if ($conf->global->PRODUIT_MULTIPRICES){
+ $prodcomb->combination_price_levels = array();
+ for ($i = 1; $i <= $conf->global->PRODUIT_MULTIPRICES_LIMIT; $i++){
+ $productCombinationLevel = new ProductCombinationLevel($db);
+ $productCombinationLevel->fk_product_attribute_combination = $prodcomb->id;
+ $productCombinationLevel->fk_price_level = $i;
+ $productCombinationLevel->variation_price = $level_price_impact[$i];
+ $productCombinationLevel->variation_price_percentage = $level_price_impact_percent[$i];
+ $prodcomb->combination_price_levels[$i] = $productCombinationLevel;
+ }
+ }
+
if ($prodcomb->update($user) > 0) {
setEventMessages($langs->trans('RecordSaved'), null, 'mesgs');
header('Location: '.dol_buildpath('/variants/combinations.php?id='.$id, 2));
@@ -594,12 +635,33 @@ if (!empty($id) || !empty($ref))
trans('Reference') ?>
+ global->PRODUIT_MULTIPRICES)){ ?>
trans('PriceImpact') ?>
- > trans('PercentageVariation') ?>
+ > trans('PercentageVariation') ?>
+
- fetchCombinationPriceLevels();
+
+ for ($i = 1; $i <= $conf->global->PRODUIT_MULTIPRICES_LIMIT; $i++)
+ {
+ print '';
+ print ''.$langs->trans('ImpactOnPriceLevel', $i).' ';
+ if ($i===1){
+ print ' ('.$langs->trans('ApplyToAllPriceImpactLevel').') ';
+ }
+ print ' ';
+ print ' ';
+ print ' combination_price_levels[$i]->variation_price_percentage) ? ' checked' : '' ).'> '.$langs->trans('PercentageVariation').' ';
+
+ print ' ';
+ print ' ';
+ }
+ }
+
if ($object->isProduct()) {
print '';
print ''.$langs->trans('WeightImpact').' ';
@@ -609,9 +671,30 @@ if (!empty($id) || !empty($ref))
print '';
}
- dol_fiche_end();
- ?>
+ if (!empty($conf->global->PRODUIT_MULTIPRICES)){
+ ?>
+
+
value="trans('Create') : $langs->trans('Save') ?>" class="button">