diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php index 97083e95f96..13a13dcb046 100644 --- a/htdocs/core/lib/functions.lib.php +++ b/htdocs/core/lib/functions.lib.php @@ -403,38 +403,38 @@ function isModEnabled($module) */ function getWarningDelay($module, $parmlevel1, $parmlevel2 = '') { - global $conf; + global $conf; - // For compatibility with bad naming on module - $moduletomoduletouse = array( - 'invoice' => 'facture', - ); - $moduleParmsMapping = array( - 'product' => 'produit', - ); + // For compatibility with bad naming on module + $moduletomoduletouse = array( + 'invoice' => 'facture', + ); + $moduleParmsMapping = array( + 'product' => 'produit', + ); - if (!empty($moduletomoduletouse[$module])) { - $module = $moduletomoduletouse[$module]; - } + if (!empty($moduletomoduletouse[$module])) { + $module = $moduletomoduletouse[$module]; + } - $warningDelayPath = $parmlevel1; - if (!empty($moduleParmsMapping[$warningDelayPath])) { - $warningDelayPath = $moduleParmsMapping[$warningDelayPath]; - } + $warningDelayPath = $parmlevel1; + if (!empty($moduleParmsMapping[$warningDelayPath])) { + $warningDelayPath = $moduleParmsMapping[$warningDelayPath]; + } - if ($parmlevel2) { - if (!empty($conf->$module->$warningDelayPath->warning_delay)) { - if (!empty($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; + if ($parmlevel2) { + if (!empty($conf->$module->$warningDelayPath->warning_delay)) { + if (!empty($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; + } + } - 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 > 0) ? ']' : ''); - } elseif (!empty($_SERVER['HTTP_CLIENT_IP']) ) { + } elseif (!empty($_SERVER['HTTP_CLIENT_IP'])) { $tmpips = explode(',', $_SERVER['HTTP_CLIENT_IP']); $data['ip'] = ''; $foundremoteip = 0; @@ -7258,9 +7258,30 @@ function price2num($amount, $rounding = '', $option = 0) } //print "QQ".$amount."
\n"; - // Now make replace (the main goal of function) + // Now make replaceents (the main goal of function) + 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 `/^(?[^,]*,|[^.]*\.)(?[^.,]*)$/u` can be broken down as follows: + // - `(?[^,]*,|[^.]*\.)` 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. + // - `(?[^.,]*)`: The sequence after the character accepted as the decimal point, not including it. + if (preg_match('/^(?[^,]*,|[^.]*\.)(?[^.,]*)$/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 @@ -12122,16 +12143,23 @@ function dolExplodeKeepIfQuotes($input) * @return string */ static function ($a, $b, $c) { - if ($a !== '') return $a; - if ($b !== '') return $b; - if ($c !== '') return $c; + if ($a !== '') { + return $a; + } + if ($b !== '') { + return $b; + } + if ($c !== '') { + return $c; + } return ''; }, $matches[1], $matches[2], $matches[3] ); - return array_values(array_filter($result, + return array_values(array_filter( + $result, /** * Filter out empty strings from the result array. * @@ -12140,7 +12168,8 @@ function dolExplodeKeepIfQuotes($input) */ static function ($val) { return $val !== ''; - })); + } + )); } diff --git a/test/phpunit/FunctionsLibTest.php b/test/phpunit/FunctionsLibTest.php index 88bbbccd788..a80f7f66e10 100644 --- a/test/phpunit/FunctionsLibTest.php +++ b/test/phpunit/FunctionsLibTest.php @@ -2,6 +2,7 @@ /* Copyright (C) 2010-2014 Laurent Destailleur * Copyright (C) 2015 Juanjo Menent * Copyright (C) 2023 Alexandre Janniaux + * Copyright (C) 2025 MDW * * 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 @@ -482,10 +483,10 @@ class FunctionsLibTest extends CommonClassTest /** - * testGetBrowserInfo - * - * @return void - */ + * testGetBrowserInfo + * + * @return void + */ public function testGetBrowserInfo() { // MSIE 5.0 @@ -1618,6 +1619,9 @@ class FunctionsLibTest extends CommonClassTest $this->assertEquals('12.4', price2num('12.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=, $newlangs2 = new Translate('', $conf); $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(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('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;