";
diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php
index 155b1d74a5f..24ec5645ffd 100644
--- a/htdocs/core/lib/functions.lib.php
+++ b/htdocs/core/lib/functions.lib.php
@@ -4698,14 +4698,15 @@ function price($amount, $form = 0, $outlangs = '', $trunc = 1, $rounding = -1, $
* 'MS'=Round to Max for stock quantity (MAIN_MAX_DECIMALS_STOCK)
* 'CR'=Currency rate
* Numeric = Nb of digits for rounding
- * @param int $alreadysqlnb Put 1 if you know that content is already universal format number
+ * @param int $option Put 1 if you know that content is already universal format number (so no correction on decimal will be done)
+ * Put 2 if you know that number is a user input (so we know we don't have to fix decimal separator).
* @return string Amount with universal numeric format (Example: '99.99999').
* If conversion fails, it return text unchanged if $rounding = '' or '0' if $rounding is defined.
* If amount is null or '', it returns '' if $rounding = '' or '0' if $rounding is defined..
*
* @see price() Opposite function of price2num
*/
-function price2num($amount, $rounding = '', $alreadysqlnb = 0)
+function price2num($amount, $rounding = '', $option = 0)
{
global $langs, $conf;
@@ -4720,14 +4721,16 @@ function price2num($amount, $rounding = '', $alreadysqlnb = 0)
//print "amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."' ";
// Convert value to universal number format (no thousand separator, '.' as decimal separator)
- if ($alreadysqlnb != 1) { // If not a PHP number or unknown, we change or clean format
+ if ($option != 1) { // If not a PHP number or unknown, we change or clean format
//print 'PP'.$amount.' - '.$dec.' - '.$thousand.' - '.intval($amount).' ';
- if ($thousand == '.' && preg_match('/\.(\d\d\d)$/', (string) $amount)) { // It means the . is used as a thousand separator, not as a decimal separator
- $amount = str_replace($thousand, '', $amount); // Replace of thousand before test of is_numeric to avoid pb if thousand is . and there is 3 numbers after
+ if ($option == 2 && $thousand == '.' && preg_match('/\.(\d\d\d)$/', (string) $amount)) { // It means the . is used as a thousand separator and string come frominput data, so 1.123 is 1123
+ $amount = str_replace($thousand, '', $amount);
}
+
// Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
// to format defined by LC_NUMERIC after a calculation and we want source format to be like defined by Dolibarr setup.
+ // So if number was already a good number, it is converted into local Dolibarr setup.
if (is_numeric($amount))
{
// We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
@@ -4736,7 +4739,7 @@ function price2num($amount, $rounding = '', $alreadysqlnb = 0)
$nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
$amount = number_format($amount, $nbofdec, $dec, $thousand);
}
- //print "QQ".$amount.' ';
+ //print "QQ".$amount." \n";
// Now make replace (the main goal of function)
if ($thousand != ',' && $thousand != '.') {
@@ -4960,10 +4963,10 @@ function get_localtax($vatrate, $local, $thirdparty_buyer = "", $thirdparty_sell
// By default, search value of local tax on line of common tax
$sql = "SELECT t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
$sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
- $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$thirdparty_seller->country_code."'";
+ $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdparty_seller->country_code)."'";
$sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
- if ($vatratecode) $sql .= " AND t.code ='".$vatratecode."'"; // If we have the code, we use it in priority
- else $sql .= " AND t.recuperableonly ='".$vatnpr."'";
+ if ($vatratecode) $sql .= " AND t.code ='".$db->escape($vatratecode)."'"; // If we have the code, we use it in priority
+ else $sql .= " AND t.recuperableonly ='".$db->escape($vatnpr)."'";
dol_syslog("get_localtax", LOG_DEBUG);
$resql = $db->query($sql);
@@ -5119,10 +5122,10 @@ function getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisi
}
$sql .= ", ".MAIN_DB_PREFIX."c_country as c";
- if ($mysoc->country_code == 'ES') $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$buyer->country_code."'"; // local tax in spain use the buyer country ??
- else $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$seller->country_code."'";
+ if ($mysoc->country_code == 'ES') $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($buyer->country_code)."'"; // local tax in spain use the buyer country ??
+ else $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'";
$sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
- if ($vatratecode) $sql .= " AND t.code = '".$vatratecode."'";
+ if ($vatratecode) $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
}
$resql = $db->query($sql);
diff --git a/htdocs/core/lib/price.lib.php b/htdocs/core/lib/price.lib.php
index 2cb8d8e30f6..dfb5f4ffaf1 100644
--- a/htdocs/core/lib/price.lib.php
+++ b/htdocs/core/lib/price.lib.php
@@ -103,10 +103,14 @@ function calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocalt
$seller = $mysoc; // If sell is done to a customer, $seller is not provided, we use $mysoc
//var_dump($seller->country_id);exit;
}
- if (empty($localtaxes_array) || !is_array($localtaxes_array))
- {
+ if (empty($localtaxes_array) || !is_array($localtaxes_array)) {
dol_syslog("Price.lib::calcul_price_total Warning: function is called with parameter localtaxes_array that is missing", LOG_WARNING);
}
+ if (!is_numeric($txtva)) {
+ dol_syslog("Price.lib::calcul_price_total Warning: function was called with a parameter vat rate that is not a real numeric value. There is surely a bug.", LOG_ERR);
+ } elseif ($txtva >= 1000) {
+ dol_syslog("Price.lib::calcul_price_total Warning: function was called with a bad value for vat rate (should be often < 100, always < 1000). There is surely a bug.", LOG_ERR);
+ }
// Too verbose. Enable for debug only
//dol_syslog("Price.lib::calcul_price_total qty=".$qty." pu=".$pu." remiserpercent_ligne=".$remise_percent_ligne." txtva=".$txtva." uselocaltax1_rate=".$uselocaltax1_rate." uselocaltax2_rate=".$uselocaltax2_rate.' remise_percent_global='.$remise_percent_global.' price_base_type='.$ice_base_type.' type='.$type.' progress='.$progress);
diff --git a/htdocs/core/lib/project.lib.php b/htdocs/core/lib/project.lib.php
index ac8530b4aa5..284ebb3ad37 100644
--- a/htdocs/core/lib/project.lib.php
+++ b/htdocs/core/lib/project.lib.php
@@ -2397,24 +2397,24 @@ function getTaskProgressView($task, $label = true, $progressNumber = true, $hide
// this conf is actually hidden, by default we use 10% for "be carefull or warning"
$warningRatio = !empty($conf->global->PROJECT_TIME_SPEND_WARNING_PERCENT) ? (1 + $conf->global->PROJECT_TIME_SPEND_WARNING_PERCENT / 100) : 1.10;
- $diffTitle = ' '.$langs->trans('ProgressDeclared').' : '.$task->progress.($task->progress ? '%' : '');
- $diffTitle .= ' '.$langs->trans('ProgressCalculated').' : '.$progressCalculated.($progressCalculated ? '%' : '');
+ $diffTitle = ' '.$langs->trans('ProgressDeclared').' : '.$task->progress.($task->progress ? '%' : '');
+ $diffTitle .= ' '.$langs->trans('ProgressCalculated').' : '.$progressCalculated.($progressCalculated ? '%' : '');
//var_dump($progressCalculated.' '.$warningRatio.' '.$task->progress.' '.doubleval($task->progress * $warningRatio));
if (doubleval($progressCalculated) > doubleval($task->progress * $warningRatio)) {
$progressBarClass = 'progress-bar-danger';
$title = $langs->trans('TheReportedProgressIsLessThanTheCalculatedProgressionByX', abs($task->progress - $progressCalculated).' '.$langs->trans("point"));
- $diff = ' '.($task->progress - $progressCalculated).'%';
+ $diff = ' '.($task->progress - $progressCalculated).'%';
}
elseif (doubleval($progressCalculated) > doubleval($task->progress)) { // warning if close at 10%
$progressBarClass = 'progress-bar-warning';
$title = $langs->trans('TheReportedProgressIsLessThanTheCalculatedProgressionByX', abs($task->progress - $progressCalculated).' '.$langs->trans("point"));
- $diff = ' '.($task->progress - $progressCalculated).'%';
+ $diff = ' '.($task->progress - $progressCalculated).'%';
}
else {
$progressBarClass = 'progress-bar-success';
$title = $langs->trans('TheReportedProgressIsMoreThanTheCalculatedProgressionByX', ($task->progress - $progressCalculated).' '.$langs->trans("point"));
- $diff = ' '.($task->progress - $progressCalculated).'%';
+ $diff = ' '.($task->progress - $progressCalculated).'%';
}
}
diff --git a/htdocs/expensereport/class/expensereport.class.php b/htdocs/expensereport/class/expensereport.class.php
index c56a0ecae64..3c18805362f 100644
--- a/htdocs/expensereport/class/expensereport.class.php
+++ b/htdocs/expensereport/class/expensereport.class.php
@@ -1801,6 +1801,7 @@ class ExpenseReport extends CommonObject
$localtaxes_type = getLocalTaxesFromRate($vatrate, 0, $mysoc, $this->thirdparty);
$vat_src_code = '';
+ $reg = array();
if (preg_match('/\s*\((.*)\)/', $vatrate, $reg))
{
$vat_src_code = $reg[1];
@@ -2046,6 +2047,7 @@ class ExpenseReport extends CommonObject
// Clean vat code
$vat_src_code = '';
+ $reg = array();
if (preg_match('/\((.*)\)/', $vatrate, $reg))
{
$vat_src_code = $reg[1];
@@ -2060,10 +2062,6 @@ class ExpenseReport extends CommonObject
$tx_tva = $vatrate / 100;
$tx_tva = $tx_tva + 1;
- $total_ht = price2num($total_ttc / $tx_tva, 'MT');
-
- $total_tva = price2num($total_ttc - $total_ht, 'MT');
- // fin calculs
$this->line = new ExpenseReportLine($this->db);
$this->line->comments = $comments;
diff --git a/htdocs/fichinter/class/fichinterrec.class.php b/htdocs/fichinter/class/fichinterrec.class.php
index a09547b3164..5b3dfb3a2fc 100644
--- a/htdocs/fichinter/class/fichinterrec.class.php
+++ b/htdocs/fichinter/class/fichinterrec.class.php
@@ -481,7 +481,6 @@ class FichinterRec extends Fichinter
$pu = $pu_ttc;
}
-
// Calcul du total TTC et de la TVA pour la ligne a partir de
// qty, pu, remise_percent et txtva
// TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
diff --git a/htdocs/fourn/class/fournisseur.commande.class.php b/htdocs/fourn/class/fournisseur.commande.class.php
index a95ef1bc7c7..dc3113f2554 100644
--- a/htdocs/fourn/class/fournisseur.commande.class.php
+++ b/htdocs/fourn/class/fournisseur.commande.class.php
@@ -1776,8 +1776,6 @@ class CommandeFournisseur extends CommonOrder
$localtax1_type = $localtaxes_type[0];
$localtax2_type = $localtaxes_type[2];
- $subprice = price2num($pu, 'MU');
-
$rangmax = $this->line_max();
$rang = $rangmax + 1;
@@ -2635,9 +2633,9 @@ class CommandeFournisseur extends CommonOrder
if (!$qty) $qty = 1;
$pu = price2num($pu);
$pu_ht_devise = price2num($pu_ht_devise);
- $txtva = price2num($txtva);
- $txlocaltax1 = price2num($txlocaltax1);
- $txlocaltax2 = price2num($txlocaltax2);
+ $txtva = price2num($txtva);
+ $txlocaltax1 = price2num($txlocaltax1);
+ $txlocaltax2 = price2num($txlocaltax2);
// Check parameters
if ($type < 0) return -1;
@@ -2658,6 +2656,7 @@ class CommandeFournisseur extends CommonOrder
// Clean vat code
$vat_src_code = '';
+ $reg = array();
if (preg_match('/\((.*)\)/', $txtva, $reg))
{
$vat_src_code = $reg[1];
@@ -2683,8 +2682,6 @@ class CommandeFournisseur extends CommonOrder
$localtax1_type = $localtaxes_type[0];
$localtax2_type = $localtaxes_type[2];
- $subprice = price2num($pu_ht, 'MU');
-
//Fetch current line from the database and then clone the object and set it in $oldline property
$this->line = new CommandeFournisseurLigne($this->db);
$this->line->fetch($rowid);
diff --git a/htdocs/fourn/class/fournisseur.facture.class.php b/htdocs/fourn/class/fournisseur.facture.class.php
index 952e609555d..72d040583b2 100644
--- a/htdocs/fourn/class/fournisseur.facture.class.php
+++ b/htdocs/fourn/class/fournisseur.facture.class.php
@@ -1679,11 +1679,11 @@ class FactureFournisseur extends CommonInvoice
$remise_percent = price2num($remise_percent);
$qty = price2num($qty);
$pu = price2num($pu);
- $txlocaltax1 = price2num($txlocaltax1);
- $txlocaltax2 = price2num($txlocaltax2);
if (!preg_match('/\((.*)\)/', $txtva)) {
$txtva = price2num($txtva); // $txtva can have format '5,1' or '5.1' or '5.1(XXX)', we must clean only if '5,1'
}
+ $txlocaltax1 = price2num($txlocaltax1);
+ $txlocaltax2 = price2num($txlocaltax2);
if ($date_start && $date_end && $date_start > $date_end) {
$langs->load("errors");
@@ -1939,8 +1939,6 @@ class FactureFournisseur extends CommonInvoice
$txlocaltax1 = price2num($txlocaltax1);
$txlocaltax2 = price2num($txlocaltax2);
- $localtaxes_type = array($txlocaltax1, $txlocaltax2);
-
// Calcul du total TTC et de la TVA pour la ligne a partir de
// qty, pu, remise_percent et txtva
// TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
diff --git a/htdocs/main.inc.php b/htdocs/main.inc.php
index 9b85e62880a..84c695d9978 100644
--- a/htdocs/main.inc.php
+++ b/htdocs/main.inc.php
@@ -1705,7 +1705,7 @@ function top_menu($head, $title = '', $target = '', $disablejs = 0, $disablehead
}
if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
- $text = ''.DOL_VERSION.'';
+ $text = ''.DOL_VERSION.'';
$toprightmenu .= @Form::textwithtooltip('', $appli, 2, 1, $text, 'login_block_elem', 2);
}
diff --git a/htdocs/product/price.php b/htdocs/product/price.php
index f93545ee19e..12029ea7cdc 100644
--- a/htdocs/product/price.php
+++ b/htdocs/product/price.php
@@ -110,6 +110,7 @@ if (empty($reshook))
// We must define tva_tx, npr and local taxes
$tva_tx = $tva_tx_txt;
$vatratecode = '';
+ $reg = array();
if (preg_match('/\((.*)\)/', $tva_tx_txt, $reg))
{
$vat_src_code = $reg[1];
@@ -262,8 +263,8 @@ if (empty($reshook))
}
$pricestoupdate[$i] = array(
- 'price' => price2num($newprice[$i]),
- 'price_min' => price2num($newprice_min[$i]),
+ 'price' => price2num($newprice[$i], '', 2),
+ 'price_min' => price2num($newprice_min[$i], '', 2),
'price_base_type' => $newpricebase[$i],
'default_vat_code' => $vatratecode,
'vat_tx' => $tva_tx, // default_vat_code should be used in priority in a future
@@ -279,8 +280,8 @@ if (empty($reshook))
}
elseif (!$error)
{
- $newprice = price2num(GETPOST('price', 'alpha'));
- $newprice_min = price2num(GETPOST('price_min', 'alpha'));
+ $newprice = price2num(GETPOST('price', 'alpha'), '', 2);
+ $newprice_min = price2num(GETPOST('price_min', 'alpha'), '', 2);
$newpricebase = GETPOST('price_base_type', 'alpha');
$tva_tx_txt = GETPOST('tva_tx', 'alpha'); // tva_tx can be '8.5' or '8.5*' or '8.5 (XXX)' or '8.5* (XXX)'
diff --git a/htdocs/projet/tasks/time.php b/htdocs/projet/tasks/time.php
index 4a5d484abf8..b95f285c628 100644
--- a/htdocs/projet/tasks/time.php
+++ b/htdocs/projet/tasks/time.php
@@ -1463,7 +1463,7 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0)
if (!empty($arrayfields['value']['checked']))
{
print '
';
}
else
{
diff --git a/htdocs/societe/class/societe.class.php b/htdocs/societe/class/societe.class.php
index 30a90a884bd..ee91363dba1 100644
--- a/htdocs/societe/class/societe.class.php
+++ b/htdocs/societe/class/societe.class.php
@@ -2077,7 +2077,7 @@ class Societe extends CommonObject
$discount->amount_tva = $discount->multicurrency_amount_tva = price2num($remise * $vatrate / 100, 'MT');
$discount->amount_ttc = $discount->multicurrency_amount_ttc = price2num($discount->amount_ht + $discount->amount_tva, 'MT');
- $discount->tva_tx = price2num($vatrate, 'MT');
+ $discount->tva_tx = price2num($vatrate);
$discount->vat_src_code = $vat_src_code;
$discount->description = $desc;
diff --git a/test/phpunit/FunctionsLibTest.php b/test/phpunit/FunctionsLibTest.php
index 85937147cff..a987f28c7ed 100644
--- a/test/phpunit/FunctionsLibTest.php
+++ b/test/phpunit/FunctionsLibTest.php
@@ -1262,16 +1262,24 @@ class FunctionsLibTest extends PHPUnit\Framework\TestCase
// Test with 3 chars after . or ,
// If a . is used and there is 3 digits after, it is a thousand separator
- $this->assertEquals(1234, price2num('1.234'), 'Test 1.234 give 1234 with spanish language');
+ $this->assertEquals(1234, price2num('1.234', '', 2), 'Test 1.234 give 1234 with spanish language if user input');
+ $this->assertEquals(1.234, price2num('1,234', '', 2), 'Test 1,234 give 1234 with spanish language if user input');
+ $this->assertEquals(1234, price2num('1 234', '', 2), 'Test 1 234 give 1234 with spanish language if user input');
+ $this->assertEquals(1.234, price2num('1.234'), 'Test 1.234 give 1.234 with spanish language');
+ $this->assertEquals(1.234, price2num('1,234'), 'Test 1,234 give 1234 with spanish language');
$this->assertEquals(1234, price2num('1 234'), 'Test 1 234 give 1234 with spanish language');
- $this->assertEquals(1234, price2num('1.234'), 'Test 1.234 give 1234 with spanish language');
- $this->assertEquals(1.234, price2num('1,234'), 'Test 1,234 give 1.234 with spanish language');
$this->assertEquals(21500123, price2num('21.500.123'), 'Test 21.500.123 give 21500123 with spanish language');
- $this->assertEquals(21500123, price2num('21500.123'), 'Test 21500.123 give 21500123 with spanish language');
+ $this->assertEquals(21500123, price2num('21500.123', 0, 2), 'Test 21500.123 give 21500123 with spanish language if user input');
+ $this->assertEquals(21500.123, price2num('21500.123'), 'Test 21500.123 give 21500123 with spanish language');
$this->assertEquals(21500.123, price2num('21500,123'), 'Test 21500,123 give 21500.123 with spanish language');
// Test with 2 digits
$this->assertEquals(21500.12, price2num('21500.12'), 'Test 21500.12 give 21500.12 with spanish language');
$this->assertEquals(21500.12, price2num('21500,12'), 'Test 21500,12 give 21500.12 with spanish language');
+ // Test with 3 digits
+ $this->assertEquals(12123, price2num('12.123', '', 2), 'Test 12.123 give 12123 with spanish language if user input');
+ $this->assertEquals(12.123, price2num('12,123', '', 2), 'Test 12,123 give 12.123 with spanish language if user input');
+ $this->assertEquals(12.123, price2num('12.123'), 'Test 12.123 give 12.123 with spanish language');
+ $this->assertEquals(12.123, price2num('12,123'), 'Test 12,123 give 12.123 with spanish language');
// For french language
$newlangs3 = new Translate('', $conf);
@@ -1279,9 +1287,12 @@ class FunctionsLibTest extends PHPUnit\Framework\TestCase
$newlangs3->load("main");
$langs = $newlangs3;
+ $this->assertEquals(1, price2num('1.000', '', 2), 'Test 1.000 give 1 with french language if user input');
$this->assertEquals(1, price2num('1.000'), 'Test 1.000 give 1 with french language');
$this->assertEquals(1000, price2num('1 000'), 'Test 1.000 give 1 with french language');
+ $this->assertEquals(1.234, price2num('1.234', '', 2), 'Test 1.234 give 1.234 with french language if user input');
$this->assertEquals(1.234, price2num('1.234'), 'Test 1.234 give 1.234 with french language');
+ $this->assertEquals(1.234, price2num('1,234', '', 2), 'Test 1,234 give 1.234 with french language if user input');
$this->assertEquals(1.234, price2num('1,234'), 'Test 1,234 give 1.234 with french language');
$this->assertEquals(21500000, price2num('21500 000'), 'Test 21500 000 give 21500000 with french language');
$this->assertEquals(21500000, price2num('21 500 000'), 'Test 21 500 000 give 21500000 with french language');