';
print "\n";
diff --git a/htdocs/compta/facture/class/api_invoices.class.php b/htdocs/compta/facture/class/api_invoices.class.php
index 316f43bc011..15a50346fab 100644
--- a/htdocs/compta/facture/class/api_invoices.class.php
+++ b/htdocs/compta/facture/class/api_invoices.class.php
@@ -1,6 +1,6 @@
-/* Copyright (C) 2020 Thibault FOUCART
+ * Copyright (C) 2020 Thibault FOUCART
*
* 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
diff --git a/htdocs/compta/facture/class/facture.class.php b/htdocs/compta/facture/class/facture.class.php
index e302c83e2d8..4ae9fe197fe 100644
--- a/htdocs/compta/facture/class/facture.class.php
+++ b/htdocs/compta/facture/class/facture.class.php
@@ -5171,11 +5171,11 @@ class FactureLigne extends CommonInvoiceLine
// Check parameters
if ($this->product_type < 0) return -1;
- // if buy price not defined, define buyprice as configured in margin admin
- if ($this->pa_ht == 0 && $pa_ht_isemptystring)
- {
- if (($result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product)) < 0)
- {
+ // if buy price not provided, define buyprice as configured in margin admin
+ if ($this->pa_ht == 0 && $pa_ht_isemptystring) {
+ // We call defineBuyPrice only if data was not provided (if input was '0', we will not go here and value will remaine '0')
+ $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
+ if ($result < 0) {
return $result;
} else {
$this->pa_ht = $result;
@@ -5214,7 +5214,7 @@ class FactureLigne extends CommonInvoiceLine
$sql .= ", total_localtax2=".price2num($this->total_localtax2);
}
$sql .= ", fk_product_fournisseur_price=".(!empty($this->fk_fournprice) ? "'".$this->db->escape($this->fk_fournprice)."'" : "null");
- $sql .= ", buy_price_ht='".price2num($this->pa_ht)."'";
+ $sql .= ", buy_price_ht=".(($this->pa_ht || $this->pa_ht === 0 || $this->pa_ht === '0') ? price2num($this->pa_ht) : "null"); // $this->pa_ht should always be defined (set to 0 or to sell price depending on option)
$sql .= ", fk_parent_line=".($this->fk_parent_line > 0 ? $this->fk_parent_line : "null");
if (!empty($this->rang)) $sql .= ", rang=".$this->rang;
$sql .= ", situation_percent=".$this->situation_percent;
diff --git a/htdocs/core/class/commonobject.class.php b/htdocs/core/class/commonobject.class.php
index af2c902aacb..8a3f50c02a4 100644
--- a/htdocs/core/class/commonobject.class.php
+++ b/htdocs/core/class/commonobject.class.php
@@ -7122,7 +7122,7 @@ abstract class CommonObject
$buyPrice = 0;
- if (($unitPrice > 0) && (isset($conf->global->ForceBuyingPriceIfNull) && $conf->global->ForceBuyingPriceIfNull == 1)) // In most cases, test here is false
+ if (($unitPrice > 0) && (isset($conf->global->ForceBuyingPriceIfNull) && $conf->global->ForceBuyingPriceIfNull > 0)) // In most cases, test here is false
{
$buyPrice = $unitPrice * (1 - $discountPercent / 100);
} else {
diff --git a/htdocs/core/class/html.formmail.class.php b/htdocs/core/class/html.formmail.class.php
index 14e0d9c02d1..c4723eacc57 100644
--- a/htdocs/core/class/html.formmail.class.php
+++ b/htdocs/core/class/html.formmail.class.php
@@ -382,8 +382,7 @@ class FormMail extends Form
$listofmimes = array();
$keytoavoidconflict = empty($this->trackid) ? '' : '-'.$this->trackid; // this->trackid must be defined
- if (GETPOST('mode', 'alpha') == 'init' || (GETPOST('modelmailselected', 'alpha') && GETPOST('modelmailselected', 'alpha') != '-1'))
- {
+ if (GETPOST('mode', 'alpha') == 'init' || (GETPOST('modelselected') && GETPOST('modelmailselected', 'alpha') && GETPOST('modelmailselected', 'alpha') != '-1')) {
if (!empty($arraydefaultmessage->joinfiles) && is_array($this->param['fileinit']))
{
foreach ($this->param['fileinit'] as $file)
diff --git a/htdocs/core/class/html.formmargin.class.php b/htdocs/core/class/html.formmargin.class.php
index 233f98df662..608698cf1b1 100644
--- a/htdocs/core/class/html.formmargin.class.php
+++ b/htdocs/core/class/html.formmargin.class.php
@@ -91,8 +91,10 @@ class FormMargin
if ($product->fetch_product_fournisseur_price($line->fk_fournprice))
$line->pa_ht = $product->fourn_unitprice * (1 - $product->fourn_remise_percent / 100);
}
- // si prix d'achat non renseigné et devrait l'être, alors prix achat = prix vente
- if ((!isset($line->pa_ht) || $line->pa_ht == 0) && $line->subprice > 0 && (isset($conf->global->ForceBuyingPriceIfNull) && $conf->global->ForceBuyingPriceIfNull == 1)) {
+
+ // If buy price is not defined (null), we will use the sell price. If defined to 0 (it means it was forced to 0 during insert, for example for a free to get product), we must still use 0.
+ //if ((!isset($line->pa_ht) || $line->pa_ht == 0) && $line->subprice > 0 && (isset($conf->global->ForceBuyingPriceIfNull) && $conf->global->ForceBuyingPriceIfNull > 0)) {
+ if ((!isset($line->pa_ht)) && $line->subprice > 0 && (isset($conf->global->ForceBuyingPriceIfNull) && $conf->global->ForceBuyingPriceIfNull > 0)) {
$line->pa_ht = $line->subprice * (1 - ($line->remise_percent / 100));
}
diff --git a/htdocs/core/class/html.formother.class.php b/htdocs/core/class/html.formother.class.php
index c71029769f2..b6ae7cdbfc2 100644
--- a/htdocs/core/class/html.formother.class.php
+++ b/htdocs/core/class/html.formother.class.php
@@ -474,7 +474,7 @@ class FormOther
//Add hook to filter on user (for exemple on usergroup define in custom modules)
if (!empty($reshook)) $sql_usr .= $hookmanager->resArray[1];
}
- $sql_usr .= " ORDER BY statut DESC, lastname ASC"; // Do not use 'ORDER BY u.statut' here, not compatible with the UNION.
+ $sql_usr .= " ORDER BY status DESC, lastname ASC"; // Do not use 'ORDER BY u.statut' here, not compatible with the UNION.
//print $sql_usr;exit;
$resql_usr = $this->db->query($sql_usr);
diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php
index b63f9d72f26..2a57a983496 100644
--- a/htdocs/core/lib/functions.lib.php
+++ b/htdocs/core/lib/functions.lib.php
@@ -3337,7 +3337,7 @@ function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $
'shipment'=>'dolly', 'stock'=>'box-open', 'stats' => 'chart-bar', 'split'=>'code-branch', 'supplier_invoice'=>'file-invoice-dollar', 'technic'=>'cogs', 'ticket'=>'ticket-alt',
'title_setup'=>'tools', 'title_accountancy'=>'money-check-alt', 'title_bank'=>'university', 'title_hrm'=>'umbrella-beach',
'title_agenda'=>'calendar-alt',
- 'uparrow'=>'mail-forward', 'vcard'=>'address-card',
+ 'uparrow'=>'share', 'vcard'=>'address-card',
'jabber'=>'comment-o',
'website'=>'globe-americas'
);
diff --git a/htdocs/install/mysql/migration/12.0.0-13.0.0.sql b/htdocs/install/mysql/migration/12.0.0-13.0.0.sql
index cc0386621a8..bfa8bf01b39 100644
--- a/htdocs/install/mysql/migration/12.0.0-13.0.0.sql
+++ b/htdocs/install/mysql/migration/12.0.0-13.0.0.sql
@@ -575,7 +575,7 @@ insert into llx_c_action_trigger (code,label,description,elementtype,rang) value
insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('EXPENSE_REPORT_DELETE','Expense report deleted','Executed when an expense report is deleted','expensereport',205);
-- Removed no more used function
--- VPGSQL8.2 DROP FUNCTION IF EXISTS update_modified_column_date_m CASCADE;
+-- VPGSQL8.2 DROP FUNCTION IF EXISTS update_modified_column_date_m() CASCADE;
insert into llx_c_actioncomm (id, code, type, libelle, module, active, position) values ( 6,'AC_EMAIL_IN','system','reception Email',NULL, 1, 4);
diff --git a/htdocs/langs/en_US/banks.lang b/htdocs/langs/en_US/banks.lang
index b6782b57ef6..91d64b1fc27 100644
--- a/htdocs/langs/en_US/banks.lang
+++ b/htdocs/langs/en_US/banks.lang
@@ -174,7 +174,7 @@ YourSEPAMandate=Your SEPA mandate
FindYourSEPAMandate=This is your SEPA mandate to authorize our company to make direct debit order to your bank. Return it signed (scan of the signed document) or send it by mail to
AutoReportLastAccountStatement=Automatically fill the field 'number of bank statement' with last statement number when making reconciliation
CashControl=POS cash desk control
-NewCashFence=New cash desk closing
+NewCashFence=New cash desk opening or closing
BankColorizeMovement=Colorize movements
BankColorizeMovementDesc=If this function is enable, you can choose specific background color for debit or credit movements
BankColorizeMovementName1=Background color for debit movement
diff --git a/htdocs/langs/en_US/margins.lang b/htdocs/langs/en_US/margins.lang
index 76ea8ad5c4d..ad5406409b4 100644
--- a/htdocs/langs/en_US/margins.lang
+++ b/htdocs/langs/en_US/margins.lang
@@ -22,7 +22,7 @@ ProductService=Product or Service
AllProducts=All products and services
ChooseProduct/Service=Choose product or service
ForceBuyingPriceIfNull=Force buying/cost price to selling price if not defined
-ForceBuyingPriceIfNullDetails=If buying/cost price not defined, and this option "ON", margin will be zero on line (buying/cost price = selling price), otherwise ("OFF"), marge will be equal to suggested default.
+ForceBuyingPriceIfNullDetails=If buying/cost price not provided when we add a new line, and this option is "ON", the margin will be 0 on the new line (buying/cost price = selling price). If this option is "OFF" (recommended), margin will be equal to the value suggested by default (and may be 100% if no default value can be found).
MARGIN_METHODE_FOR_DISCOUNT=Margin method for global discounts
UseDiscountAsProduct=As a product
UseDiscountAsService=As a service
diff --git a/htdocs/margin/agentMargins.php b/htdocs/margin/agentMargins.php
index e7229abd2d3..d2a19e87149 100644
--- a/htdocs/margin/agentMargins.php
+++ b/htdocs/margin/agentMargins.php
@@ -170,7 +170,11 @@ if (!empty($startdate))
if (!empty($enddate))
$sql .= " AND f.datef <= '".$db->idate($enddate)."'";
$sql .= " AND d.buy_price_ht IS NOT NULL";
-if (isset($conf->global->ForceBuyingPriceIfNull) && $conf->global->ForceBuyingPriceIfNull == 1) $sql .= " AND d.buy_price_ht <> 0";
+// We should not use this here. Option ForceBuyingPriceIfNull should have effect only when inserting data. Once data is recorded, it must be used as it is for report.
+// We keep it with value ForceBuyingPriceIfNull = 2 for retroactive effect but results are unpredicable.
+if (isset($conf->global->ForceBuyingPriceIfNull) && $conf->global->ForceBuyingPriceIfNull == 2) {
+ $sql .= " AND d.buy_price_ht <> 0";
+}
//if ($agentid > 0) $sql.= " GROUP BY s.rowid, s.nom, s.code_client, s.client, u.rowid, u.login, u.lastname, u.firstname";
//else $sql.= " GROUP BY u.rowid, u.login, u.lastname, u.firstname";
$sql .= " GROUP BY s.rowid, s.nom, s.code_client, s.client, u.rowid, u.login, u.lastname, u.firstname";
diff --git a/htdocs/margin/customerMargins.php b/htdocs/margin/customerMargins.php
index 468d633875d..feb5cd34c8e 100644
--- a/htdocs/margin/customerMargins.php
+++ b/htdocs/margin/customerMargins.php
@@ -234,8 +234,11 @@ $sql .= " AND f.datef >= '".$db->idate($startdate)."'";
if (!empty($enddate))
$sql .= " AND f.datef <= '".$db->idate($enddate)."'";
$sql .= " AND d.buy_price_ht IS NOT NULL";
-if (isset($conf->global->ForceBuyingPriceIfNull) && $conf->global->ForceBuyingPriceIfNull == 1)
-$sql .= " AND d.buy_price_ht <> 0";
+// We should not use this here. Option ForceBuyingPriceIfNull should have effect only when inserting data. Once data is recorded, it must be used as it is for report.
+// We keep it with value ForceBuyingPriceIfNull = 2 for retroactive effect but results are unpredicable.
+if (isset($conf->global->ForceBuyingPriceIfNull) && $conf->global->ForceBuyingPriceIfNull == 2) {
+ $sql .= " AND d.buy_price_ht <> 0";
+}
if ($client) $sql .= " GROUP BY s.rowid, s.nom, s.code_client, s.client, f.rowid, f.ref, f.total, f.datef, f.paye, f.fk_statut";
else $sql .= " GROUP BY s.rowid, s.nom, s.code_client, s.client";
$sql .= $db->order($sortfield, $sortorder);
diff --git a/htdocs/margin/productMargins.php b/htdocs/margin/productMargins.php
index ebe8c9c1cac..b7695c9b2ea 100644
--- a/htdocs/margin/productMargins.php
+++ b/htdocs/margin/productMargins.php
@@ -197,8 +197,11 @@ if (!empty($startdate))
if (!empty($enddate))
$sql .= " AND f.datef <= '".$db->idate($enddate)."'";
$sql .= " AND d.buy_price_ht IS NOT NULL";
-if (isset($conf->global->ForceBuyingPriceIfNull) && $conf->global->ForceBuyingPriceIfNull == 1)
+// We should not use this here. Option ForceBuyingPriceIfNull should have effect only when inserting data. Once data is recorded, it must be used as it is for report.
+// We keep it with value ForceBuyingPriceIfNull = 2 for retroactive effect but results are unpredicable.
+if (isset($conf->global->ForceBuyingPriceIfNull) && $conf->global->ForceBuyingPriceIfNull == 2) {
$sql .= " AND d.buy_price_ht <> 0";
+}
if ($id > 0) $sql .= " GROUP BY p.label, p.rowid, p.fk_product_type, p.ref, p.entity, d.fk_product, f.rowid, f.ref, f.total, f.datef, f.paye, f.fk_statut";
else $sql .= " GROUP BY p.label, p.rowid, p.fk_product_type, p.ref, p.entity";
$sql .= $db->order($sortfield, $sortorder);
diff --git a/htdocs/margin/tabs/productMargins.php b/htdocs/margin/tabs/productMargins.php
index 5fcec11c93b..eb82f121295 100644
--- a/htdocs/margin/tabs/productMargins.php
+++ b/htdocs/margin/tabs/productMargins.php
@@ -150,7 +150,11 @@ if ($id > 0 || !empty($ref))
if (!$user->rights->societe->client->voir && !$socid) $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = ".$user->id;
if (!empty($socid)) $sql .= " AND f.fk_soc = $socid";
$sql .= " AND d.buy_price_ht IS NOT NULL";
- if (isset($conf->global->ForceBuyingPriceIfNull) && $conf->global->ForceBuyingPriceIfNull == 1) $sql .= " AND d.buy_price_ht <> 0";
+ // We should not use this here. Option ForceBuyingPriceIfNull should have effect only when inserting data. Once data is recorded, it must be used as it is for report.
+ // We keep it with value ForceBuyingPriceIfNull = 2 for retroactive effect but results are unpredicable.
+ if (isset($conf->global->ForceBuyingPriceIfNull) && $conf->global->ForceBuyingPriceIfNull == 2) {
+ $sql .= " AND d.buy_price_ht <> 0";
+ }
$sql .= " GROUP BY s.nom, s.rowid, s.code_client, f.rowid, f.ref, f.total, f.datef, f.paye, f.fk_statut, f.type";
if (!$user->rights->societe->client->voir && !$socid) $sql .= ", sc.fk_soc, sc.fk_user";
$sql .= $db->order($sortfield, $sortorder);
diff --git a/htdocs/margin/tabs/thirdpartyMargins.php b/htdocs/margin/tabs/thirdpartyMargins.php
index bfa5ac15811..0b79ec62b90 100644
--- a/htdocs/margin/tabs/thirdpartyMargins.php
+++ b/htdocs/margin/tabs/thirdpartyMargins.php
@@ -163,7 +163,11 @@ if ($socid > 0)
$sql .= " AND d.fk_facture = f.rowid";
$sql .= " AND f.fk_soc = $socid";
$sql .= " AND d.buy_price_ht IS NOT NULL";
- if (isset($conf->global->ForceBuyingPriceIfNull) && $conf->global->ForceBuyingPriceIfNull == 1) $sql .= " AND d.buy_price_ht <> 0";
+ // We should not use this here. Option ForceBuyingPriceIfNull should have effect only when inserting data. Once data is recorded, it must be used as it is for report.
+ // We keep it with value ForceBuyingPriceIfNull = 2 for retroactive effect but results are unpredicable.
+ if (isset($conf->global->ForceBuyingPriceIfNull) && $conf->global->ForceBuyingPriceIfNull == 2) {
+ $sql .= " AND d.buy_price_ht <> 0";
+ }
$sql .= " GROUP BY s.nom, s.rowid, s.code_client, f.rowid, f.ref, f.total, f.datef, f.paye, f.fk_statut, f.type";
$sql .= $db->order($sortfield, $sortorder);
// TODO: calculate total to display then restore pagination
diff --git a/htdocs/projet/card.php b/htdocs/projet/card.php
index e986fc4c8fa..7a12f797913 100644
--- a/htdocs/projet/card.php
+++ b/htdocs/projet/card.php
@@ -532,49 +532,51 @@ if ($action == 'create' && $user->rights->projet->creer)
print '