mirror of
https://github.com/Dolibarr/dolibarr.git
synced 2025-12-06 09:38:23 +01:00
UIUX: Interpret first token as thousands separator 21.500,00 (when French, ...). (#33832)
* Qual: Add testcase for price2num to convert '21.500,00' in FR * NEW: Accept numbers like 1.213,00 in locales with whitespace/empty thousands separator # NEW: Accept numbers like 1.213,00 in locales with whitespace/empty thousands separator - Graceful handling of comma and dot as decimal points and thousands separators in the `price2num` function. - Improved the `getNonEmptyString` function to include proper indentation and spacing (automatic). * Add testcases for price2num * en_US tests for invalid prices, add doc for price2num regex * Restore lost test to validate compatibility * Update FunctionsLibTest.php --------- Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
This commit is contained in:
@@ -403,38 +403,38 @@ function isModEnabled($module)
|
|||||||
*/
|
*/
|
||||||
function getWarningDelay($module, $parmlevel1, $parmlevel2 = '')
|
function getWarningDelay($module, $parmlevel1, $parmlevel2 = '')
|
||||||
{
|
{
|
||||||
global $conf;
|
global $conf;
|
||||||
|
|
||||||
// For compatibility with bad naming on module
|
// For compatibility with bad naming on module
|
||||||
$moduletomoduletouse = array(
|
$moduletomoduletouse = array(
|
||||||
'invoice' => 'facture',
|
'invoice' => 'facture',
|
||||||
);
|
);
|
||||||
$moduleParmsMapping = array(
|
$moduleParmsMapping = array(
|
||||||
'product' => 'produit',
|
'product' => 'produit',
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!empty($moduletomoduletouse[$module])) {
|
if (!empty($moduletomoduletouse[$module])) {
|
||||||
$module = $moduletomoduletouse[$module];
|
$module = $moduletomoduletouse[$module];
|
||||||
}
|
}
|
||||||
|
|
||||||
$warningDelayPath = $parmlevel1;
|
$warningDelayPath = $parmlevel1;
|
||||||
if (!empty($moduleParmsMapping[$warningDelayPath])) {
|
if (!empty($moduleParmsMapping[$warningDelayPath])) {
|
||||||
$warningDelayPath = $moduleParmsMapping[$warningDelayPath];
|
$warningDelayPath = $moduleParmsMapping[$warningDelayPath];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($parmlevel2) {
|
if ($parmlevel2) {
|
||||||
if (!empty($conf->$module->$warningDelayPath->warning_delay)) {
|
if (!empty($conf->$module->$warningDelayPath->warning_delay)) {
|
||||||
if (!empty($conf->$module->$warningDelayPath->$parmlevel2->warning_delay)) {
|
if (!empty($conf->$module->$warningDelayPath->$parmlevel2->warning_delay)) {
|
||||||
return (int) $conf->$module->$warningDelayPath->$parmlevel2->warning_delay;
|
return (int) $conf->$module->$warningDelayPath->$parmlevel2->warning_delay;
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!empty($conf->$module->$warningDelayPath->warning_delay)) {
|
|
||||||
return (int) $conf->$module->$warningDelayPath->$parmlevel1->warning_delay;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (!empty($conf->$module->$warningDelayPath->warning_delay)) {
|
||||||
|
return (int) $conf->$module->$warningDelayPath->$parmlevel1->warning_delay;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2595,7 +2595,7 @@ function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename =
|
|||||||
$data['ip'] .= (($j == 1) ? ' [via ' : ',').$remoteip;
|
$data['ip'] .= (($j == 1) ? ' [via ' : ',').$remoteip;
|
||||||
}
|
}
|
||||||
$data['ip'] .= (($j > 0) ? ']' : '');
|
$data['ip'] .= (($j > 0) ? ']' : '');
|
||||||
} elseif (!empty($_SERVER['HTTP_CLIENT_IP']) ) {
|
} elseif (!empty($_SERVER['HTTP_CLIENT_IP'])) {
|
||||||
$tmpips = explode(',', $_SERVER['HTTP_CLIENT_IP']);
|
$tmpips = explode(',', $_SERVER['HTTP_CLIENT_IP']);
|
||||||
$data['ip'] = '';
|
$data['ip'] = '';
|
||||||
$foundremoteip = 0;
|
$foundremoteip = 0;
|
||||||
@@ -7258,9 +7258,30 @@ function price2num($amount, $rounding = '', $option = 0)
|
|||||||
}
|
}
|
||||||
//print "QQ".$amount."<br>\n";
|
//print "QQ".$amount."<br>\n";
|
||||||
|
|
||||||
// Now make replace (the main goal of function)
|
// Now make replaceents (the main goal of function)
|
||||||
|
|
||||||
if ($thousand != ',' && $thousand != '.') {
|
if ($thousand != ',' && $thousand != '.') {
|
||||||
$amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
|
// Accept the two types of decimal points french users (i.e., using ' ' for thousands)
|
||||||
|
|
||||||
|
// REGEX: Find the integral and decimal parts.
|
||||||
|
//
|
||||||
|
// We require that the decimal point only appears once in $amount.
|
||||||
|
// The regex `/^(?<int>[^,]*,|[^.]*\.)(?<dec>[^.,]*)$/u` can be broken down as follows:
|
||||||
|
// - `(?<int>[^,]*,|[^.]*\.)` is any accepted sequence up to the last potential decimal point '.' or ',' and named `int`.
|
||||||
|
// It covers two cases:
|
||||||
|
// - `[^,]*,`: Any sequence of characters that is not ',' with ',' accepted as the decimal point (from start of string because of earlier `^`);
|
||||||
|
// - `[^.]*\.`: Any sequence of characters that is not a '.' with '.' accepted as the decimal point (from start of string.
|
||||||
|
// - `(?<dec>[^.,]*)`: The sequence after the character accepted as the decimal point, not including it.
|
||||||
|
if (preg_match('/^(?<int>[^,]*,|[^.]*\.)(?<dec>[^.,]*)$/u', $amount, $matches)) {
|
||||||
|
$intPart = $matches['int'];
|
||||||
|
$decPart = $matches['dec'];
|
||||||
|
|
||||||
|
// Remove all commas and dots from intPart
|
||||||
|
$intPart = str_replace(['.', ','], '', $intPart);
|
||||||
|
|
||||||
|
// Combine intPart and decPart with a dot
|
||||||
|
$amount = $intPart . $dec . $decPart;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$amount = str_replace(' ', '', $amount); // To avoid spaces
|
$amount = str_replace(' ', '', $amount); // To avoid spaces
|
||||||
@@ -12122,16 +12143,23 @@ function dolExplodeKeepIfQuotes($input)
|
|||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
static function ($a, $b, $c) {
|
static function ($a, $b, $c) {
|
||||||
if ($a !== '') return $a;
|
if ($a !== '') {
|
||||||
if ($b !== '') return $b;
|
return $a;
|
||||||
if ($c !== '') return $c;
|
}
|
||||||
|
if ($b !== '') {
|
||||||
|
return $b;
|
||||||
|
}
|
||||||
|
if ($c !== '') {
|
||||||
|
return $c;
|
||||||
|
}
|
||||||
return '';
|
return '';
|
||||||
},
|
},
|
||||||
$matches[1],
|
$matches[1],
|
||||||
$matches[2],
|
$matches[2],
|
||||||
$matches[3]
|
$matches[3]
|
||||||
);
|
);
|
||||||
return array_values(array_filter($result,
|
return array_values(array_filter(
|
||||||
|
$result,
|
||||||
/**
|
/**
|
||||||
* Filter out empty strings from the result array.
|
* Filter out empty strings from the result array.
|
||||||
*
|
*
|
||||||
@@ -12140,7 +12168,8 @@ function dolExplodeKeepIfQuotes($input)
|
|||||||
*/
|
*/
|
||||||
static function ($val) {
|
static function ($val) {
|
||||||
return $val !== '';
|
return $val !== '';
|
||||||
}));
|
}
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
/* Copyright (C) 2010-2014 Laurent Destailleur <eldy@users.sourceforge.net>
|
/* Copyright (C) 2010-2014 Laurent Destailleur <eldy@users.sourceforge.net>
|
||||||
* Copyright (C) 2015 Juanjo Menent <jmenent@2byte.es>
|
* Copyright (C) 2015 Juanjo Menent <jmenent@2byte.es>
|
||||||
* Copyright (C) 2023 Alexandre Janniaux <alexandre.janniaux@gmail.com>
|
* Copyright (C) 2023 Alexandre Janniaux <alexandre.janniaux@gmail.com>
|
||||||
|
* Copyright (C) 2025 MDW <mdeweerd@users.noreply.github.com>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -482,10 +483,10 @@ class FunctionsLibTest extends CommonClassTest
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* testGetBrowserInfo
|
* testGetBrowserInfo
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function testGetBrowserInfo()
|
public function testGetBrowserInfo()
|
||||||
{
|
{
|
||||||
// MSIE 5.0
|
// MSIE 5.0
|
||||||
@@ -1618,6 +1619,9 @@ class FunctionsLibTest extends CommonClassTest
|
|||||||
$this->assertEquals('12.4', price2num('12.4$'));
|
$this->assertEquals('12.4', price2num('12.4$'));
|
||||||
$this->assertEquals('12.4', price2num('12r.4$'));
|
$this->assertEquals('12.4', price2num('12r.4$'));
|
||||||
|
|
||||||
|
$this->assertEquals('1.023210.00', price2num('1.023,210.00'), 'Test invalid 1.023,210.00 with en_US');
|
||||||
|
$this->assertEquals('1023.21000', price2num('1,023.210,00'), 'Test invalid 1,023.210,00 with en_US');
|
||||||
|
|
||||||
// For spanish language SeparatorThousand=. and SeparatorDecimal=,
|
// For spanish language SeparatorThousand=. and SeparatorDecimal=,
|
||||||
$newlangs2 = new Translate('', $conf);
|
$newlangs2 = new Translate('', $conf);
|
||||||
$newlangs2->setDefaultLang('es_ES');
|
$newlangs2->setDefaultLang('es_ES');
|
||||||
@@ -1667,11 +1671,10 @@ class FunctionsLibTest extends CommonClassTest
|
|||||||
$this->assertEquals(21500000, price2num('21 500 000'), 'Test 21 500 000 give 21500000 with french language');
|
$this->assertEquals(21500000, price2num('21 500 000'), 'Test 21 500 000 give 21500000 with french language');
|
||||||
$this->assertEquals(21500, price2num('21500.00'), 'Test 21500.00 give 21500 with french language');
|
$this->assertEquals(21500, price2num('21500.00'), 'Test 21500.00 give 21500 with french language');
|
||||||
$this->assertEquals(21500, price2num('21500,00'), 'Test 21500,00 give 21500 with french language');
|
$this->assertEquals(21500, price2num('21500,00'), 'Test 21500,00 give 21500 with french language');
|
||||||
/*
|
|
||||||
$this->assertEquals(21500, price2num('21.500,00'), 'Test 21.500,00 give 21500 with french language');
|
$this->assertEquals(21500, price2num('21.500,00'), 'Test 21.500,00 give 21500 with french language');
|
||||||
$this->assertEquals('1.023.210.00', price2num('1.023,210.00'), 'Test invalid 1.023,210.00 with french language');
|
$this->assertEquals('1.023.210.00', price2num('1.023,210.00'), 'Test invalid 1.023,210.00 with french language');
|
||||||
$this->assertEquals('1.023.210.00', price2num('1,023.210,00'), 'Test invalid 1,023.210,00 with french language');
|
$this->assertEquals('1.023.210.00', price2num('1,023.210,00'), 'Test invalid 1,023.210,00 with french language');
|
||||||
*/
|
|
||||||
|
|
||||||
$langs = $oldlangs;
|
$langs = $oldlangs;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user