diff --git a/classes/Components/I18n/Bootstrap.php b/classes/Components/I18n/Bootstrap.php index e0b5fd08..2a6b6c3d 100644 --- a/classes/Components/I18n/Bootstrap.php +++ b/classes/Components/I18n/Bootstrap.php @@ -1,5 +1,5 @@ * SPDX-License-Identifier: AGPL-3.0-only */ @@ -16,7 +16,7 @@ use Xentral\Core\DependencyInjection\ServiceContainer; /** * Factory for localization object. * - * @see Localization + * @see Localization * @author Roland Rusch, easy-smart solution GmbH */ final class Bootstrap @@ -28,6 +28,7 @@ final class Bootstrap { return [ 'Localization' => 'onInitLocalization', + 'FormatterService' => 'onInitFormatterService', ]; } @@ -61,7 +62,7 @@ final class Bootstrap $subject = strtolower($lang); foreach ((new Iso639()) as $key => $val) { if (array_filter($val, function ($str) use ($subject) { - return $str && ((strtolower($str) == $subject) || (self::replaceUmlauts(strtolower($str)) == $subject)); + return $str && ((strtolower($str) == $subject) || (strtolower(self::replaceUmlauts($str)) == $subject)); })) { return $val; } @@ -83,7 +84,7 @@ final class Bootstrap $subject = strtolower($region); foreach ((new Iso3166()) as $key => $val) { if (array_filter($val, function ($str) use ($subject) { - return $str && ((strtolower($str) == $subject) || (self::replaceUmlauts(strtolower($str)) == $subject)); + return $str && ((strtolower($str) == $subject) || (strtolower(self::replaceUmlauts($str)) == $subject)); })) { return $val; } @@ -111,16 +112,16 @@ final class Bootstrap /** @var Database $db */ $db = $container->get('Database'); - $config=[]; - $firmaLang=null; - $firmaRegion=null; + $config = []; + $firmaLang = null; + $firmaRegion = null; // Get language from system settings and normalize to 3-letter-code and 2-letter-code - if ($firmaLang = self::findLanguage($app->erp->Firmendaten('preferredLanguage'))) { + if ($firmaLang = self::findLanguage(strval($app->erp->Firmendaten('preferredLanguage')))) { $config[Localization::LANGUAGE_DEFAULT] = $firmaLang[Iso639\Key::ALPHA_3]; } // Get region from system settings and normalize to 2-letter-code - if ($firmaLang && ($firmaRegion = self::findRegion($app->erp->Firmendaten('land')))) { + if ($firmaLang && ($firmaRegion = self::findRegion(strval($app->erp->Firmendaten('land'))))) { $config[Localization::LOCALE_DEFAULT] = "{$firmaLang[Iso639\Key::ALPHA_2]}_{$firmaRegion[Iso3166\Key::ALPHA_2]}"; } @@ -135,12 +136,12 @@ final class Bootstrap ); // Get language from user account and normalize to 3-letter-code and 2-letter-code - if ($userLang = self::findLanguage($user->GetSprache())) { + if ($userLang = self::findLanguage(strval($user->GetSprache()))) { $usersettings['language'] = $userLang[Iso639\Key::ALPHA_3]; } // Get region from user account and normalize to 2-letter-code - if ($userLang && ($userRegion = self::findRegion($userAddress['land']))) { + if ($userLang && ($userRegion = self::findRegion(strval($userAddress['land'])))) { $usersettings['locale'] = "{$userLang[Iso639\Key::ALPHA_2]}_{$userRegion[Iso3166\Key::ALPHA_2]}"; } } @@ -148,4 +149,14 @@ final class Bootstrap // Create Localization object return new Localization($request, $session, $usersettings, $config); } + + + + public static function onInitFormatterService(ServiceContainer $container): FormatterService + { + $localization = $container->get('Localization'); + $locale = $localization->getLocale(); + return new FormatterService($locale); + } + } diff --git a/classes/Components/I18n/Exception/LocaleNotSetException.php b/classes/Components/I18n/Exception/LocaleNotSetException.php new file mode 100644 index 00000000..e313d039 --- /dev/null +++ b/classes/Components/I18n/Exception/LocaleNotSetException.php @@ -0,0 +1,14 @@ + + * SPDX-License-Identifier: AGPL-3.0-only + */ + +declare(strict_types=1); + +namespace Xentral\Components\I18n\Exception; + +class LocaleNotSetException extends \RuntimeException +{ + +} \ No newline at end of file diff --git a/classes/Components/I18n/Formatter/AbstractFormatter.php b/classes/Components/I18n/Formatter/AbstractFormatter.php new file mode 100644 index 00000000..a2955f5f --- /dev/null +++ b/classes/Components/I18n/Formatter/AbstractFormatter.php @@ -0,0 +1,168 @@ + + * SPDX-License-Identifier: AGPL-3.0-only + */ + +declare(strict_types=1); + +namespace Xentral\Components\I18n\Formatter; + +use Vtiful\Kernel\Format; +use Xentral\Components\I18n\Formatter\Exception\TypeErrorException; + +/** + * AbstractFormatter. + * + * @author Roland Rusch, easy-smart solution GmbH + */ +abstract class AbstractFormatter implements FormatterInterface +{ + private string $locale; + private FormatterMode $strictness; + private mixed $parsedValue; + + + + public function __construct(string $locale, FormatterMode $strictness = FormatterMode::MODE_STRICT) + { + $this->locale = $locale; + $this->strictness = $strictness; + $this->init(); + } + + + + /** + * Initialize the formatter. Overload this instead of the constructor. + * + * @return void + */ + protected function init(): void + { + } + + + + /** + * Set the native PHP value in the formatter. + * The value must ALWAYS be of the requested type or an Exception is thrown. + * + * @param mixed $input + * + * @return self + */ + protected function setParsedValue($value): self + { + $this->parsedValue = $value; + return $this; + } + + + + /** + * Set the native PHP value in the formatter. + * The value must ALWAYS be of the requested type (or NULL or '' depending on the strictness). + * + * @param mixed $input + * + * @return self + */ + public function setPhpVal(mixed $input): self + { + if ($this->isStrictValidPhpVal($input) + || $this->isEmptyValidPhpValue($input) + || $this->isNullValidPhpValue($input)) { + $this->parsedValue = $input; + return $this; + } else { + throw new TypeErrorException( + "Value " . var_export($input, true) . " is not a valid type for " . get_class( + $this + ) . " with strictness {$this->getStrictness()->name}" + ); + } + } + + + + /** + * Get the native PHP value from the formatter. + * The value must ALWAYS be of the requested type or an Exception is thrown. + * + * @return mixed + */ + public function getPhpVal(): mixed + { + return $this->parsedValue; + } + + + + /** + * Return the locale used for output formatting. + * + * @return string + */ + protected function getLocale(): string + { + return $this->locale; + } + + + + /** + * Return the current strictness mode. + * + * @return FormatterMode + * @see FormatterMode + * + */ + public function getStrictness(): FormatterMode + { + return $this->strictness; + } + + + + /** + * Check if $input conforms to the MODE_NULL strictness AND strictness is set. + * + * @param mixed $input + * + * @return bool + */ + protected function isNullValidPhpValue(mixed $input): bool + { + return ($this->getStrictness() == FormatterMode::MODE_NULL) && ($input === null); + } + + + + /** + * Check if $input conforms to the MODE_EMPTY strictness AND strictness is set. + * + * @param mixed $input + * + * @return bool + */ + protected function isEmptyValidPhpValue(mixed $input): bool + { + return ($this->getStrictness() == FormatterMode::MODE_EMPTY) && is_string($input) && (trim($input) === ''); + } + + + + /** + * Clean up input string. + * + * @param string $string + * + * @return string + */ + protected function sanitizeInputString(string $string): string + { + return trim($string); + } + +} \ No newline at end of file diff --git a/classes/Components/I18n/Formatter/CurrencyFormatter.php b/classes/Components/I18n/Formatter/CurrencyFormatter.php new file mode 100644 index 00000000..82c3a042 --- /dev/null +++ b/classes/Components/I18n/Formatter/CurrencyFormatter.php @@ -0,0 +1,163 @@ + + * SPDX-License-Identifier: AGPL-3.0-only + */ + +declare(strict_types=1); + +namespace Xentral\Components\I18n\Formatter; + +use Xentral\Components\I18n\Bootstrap; +use Xentral\Components\I18n\Formatter\FloatFormatter; + +class CurrencyFormatter extends FloatFormatter +{ + private string $ccy; + private bool $showCcy = true; + + + + protected function init(): void + { + $this->parseType = \NumberFormatter::TYPE_CURRENCY; + $this->formatterStyle = \NumberFormatter::CURRENCY; + parent::init(); + + $parsedLocale = \Locale::parseLocale($this->getLocale()); + $this->setCcy( + Bootstrap::findRegion($parsedLocale['region'])[\Xentral\Components\I18n\Iso3166\Key::CURRENCY_CODE] ?? '' + ); + + // Set text representation for currency, not the symbol + $this->getNumberFormatter()->setSymbol(\NumberFormatter::CURRENCY_SYMBOL, $this->getCcy()); + } + + + + /** + * Return the currency. + * + * @return string + */ + public function getCcy(): string + { + return $this->ccy; + } + + + + /** + * Set the currency. Currency is also automatically set by locale, if class is + * instantiated. + * + * @param string $ccy + * + * @return $this + */ + public function setCcy(string $ccy): self + { + $this->ccy = $ccy; + return $this; + } + + + + /** + * Don't show currency in formatted output and don't expect currency in string to parse. + * + * @return $this + */ + public function hideCurrency(): self + { + $this->showCcy = false; + $this->parseType = \NumberFormatter::TYPE_DOUBLE; + return $this; + } + + + + /** + * Show currency in formatted output and expect it in string to parse. + * + * @return $this + */ + public function showCurrency(): self + { + $this->showCcy = true; + $this->parseType = \NumberFormatter::TYPE_CURRENCY; + return $this; + } + + + + protected function sanitizeInputString(string $string): string + { + return $this->showCcy + ? trim($string) + : parent::sanitizeInputString($string); + } + + + + /** + * Return a string that can be used in an SQL query to format the value for presentation to a User. + * Should return the same string as if it was formatted by FormatterInterface::formatForUser(), but directly from + * the database. + * This function does not need a native PHP value, but a table column is needed. + * + * @TODO This function is not complete. It does not add a currency symbol. + * + * @param string $col + * + * @return string + * @deprecated + * + */ + public function formatForUserWithSqlStatement(string $col): string + { + return parent::formatForUserWithSqlStatement($col); + } + + + + protected function parse(string $input, ?\NumberFormatter $numberFormatter = null): false|float|int + { + if (!$this->showCcy) { + // If currency is not shown, create a new \NumberFormatter with style DECIMAL + $numberFormatter = new \NumberFormatter( + $this->getNumberFormatter()->getLocale(), + \NumberFormatter::DECIMAL, + $this->getNumberFormatter()->getPattern() + ); + return parent::parse($input, $numberFormatter); + } + + if ($numberFormatter === null) { + $numberFormatter = $this->getNumberFormatter(); + } + return $numberFormatter->parseCurrency($input, $this->ccy); + } + + + + protected function format(float|int $phpVal, ?\NumberFormatter $numberFormatter = null): false|string + { + if (!$this->showCcy) { + // If currency is not shown, create a new \NumberFormatter with style DECIMAL + $numberFormatter = new \NumberFormatter( + $this->getNumberFormatter()->getLocale(), + \NumberFormatter::DECIMAL, + $this->getNumberFormatter()->getPattern() + ); + return parent::format($phpVal, $numberFormatter); + } + + if ($numberFormatter === null) { + $numberFormatter = $this->getNumberFormatter(); + } + return $numberFormatter->formatCurrency($phpVal, $this->ccy); + } + + +} \ No newline at end of file diff --git a/classes/Components/I18n/Formatter/Exception/TypeErrorException.php b/classes/Components/I18n/Formatter/Exception/TypeErrorException.php new file mode 100644 index 00000000..bf398911 --- /dev/null +++ b/classes/Components/I18n/Formatter/Exception/TypeErrorException.php @@ -0,0 +1,14 @@ + + * SPDX-License-Identifier: AGPL-3.0-only + */ + +declare(strict_types=1); + +namespace Xentral\Components\I18n\Formatter\Exception; + +class TypeErrorException extends \RuntimeException +{ + +} \ No newline at end of file diff --git a/classes/Components/I18n/Formatter/FloatFormatter.php b/classes/Components/I18n/Formatter/FloatFormatter.php new file mode 100644 index 00000000..dbcea5cd --- /dev/null +++ b/classes/Components/I18n/Formatter/FloatFormatter.php @@ -0,0 +1,327 @@ + + * SPDX-License-Identifier: AGPL-3.0-only + */ + +declare(strict_types=1); + +namespace Xentral\Components\I18n\Formatter; + +use Xentral\Components\I18n\Formatter\Exception\TypeErrorException; + +/** + * Parse and format float numbers. This class is also used as base for IntegerFormatter and + * CurrencyFormatter. + * + * @author Roland Rusch, easy-smart solution GmbH + */ +class FloatFormatter extends AbstractFormatter implements FormatterInterface +{ + private \NumberFormatter $numberFormatter; + protected int $parseType = \NumberFormatter::TYPE_DOUBLE; + protected int $formatterStyle = \NumberFormatter::DECIMAL; + + + + /** + * Initialize PHP \NumberFormatter. + * + * @return void + */ + protected function init(): void + { + $this->setMinDigits(0); + $this->setMaxDigits(100); + } + + + + public function isStrictValidPhpVal($input): bool + { + return is_numeric($input); + } + + + + /** + * Parse string from user input and store as float in object. + * If parsing fails, an Exception is thrown. + * + * @param string $input + * + * @return self + */ + public function parseUserInput(string $input): self + { + // Sanitize string + $input = $this->sanitizeInputString($input); + + if ($input === '') { + // Check if user has entered an empty value and we are in strictness MODE_NULL + if ($this->getStrictness() == FormatterMode::MODE_NULL) { + $this->setParsedValue(null); + return $this; + } + + // Check if user has entered an empty string and we are in strictness MODE_EMPTY + if ($this->getStrictness() == FormatterMode::MODE_EMPTY) { + $this->setParsedValue(''); + return $this; + } + + // User has entered an empty string, but this is not allowed in strictness MODE_STRICT + throw new TypeErrorException( + "Value " . var_export($input, true) . " is not a valid type for " . get_class( + $this + ) . " with strictness {$this->getStrictness()->name}" + ); + } + + + // From here on, $input must contain a parseable input + + if (($output = $this->parse($input)) === false) { + throw new \RuntimeException("{$this->getNumberFormatter()->getErrorMessage()}. \$input={$input}"); + } + + $this->setParsedValue($output); + return $this; + } + + + + /** + * Return a string representing the PHP value as a formatted value. + * Throws an Exception if no value was set before or the value is of the wrong type. + * + * @return string + */ + public function formatForUser(): string + { + return ($this->isNullValidPhpValue($this->getPhpVal()) || $this->isEmptyValidPhpValue($this->getPhpVal())) + ? '' + : $this->format($this->getPhpVal()); + } + + + + /** + * Return a string that can be used in an SQL query to format the value for presentation to a User. + * Should return the same string as if it was formatted by FormatterInterface::formatForUser(), but directly from + * the database. + * This function does not need a native PHP value, but a table column is needed. + * + * @param string $col + * + * @return string + */ + public function formatForUserWithSqlStatement(string $col): string + { + $min_decimals = $this->getNumberFormatter()->getAttribute(\NumberFormatter::MIN_FRACTION_DIGITS); + $max_decimals = $this->getNumberFormatter()->getAttribute(\NumberFormatter::MAX_FRACTION_DIGITS); + + $sql = "FORMAT({$col},LEAST('{$max_decimals}',GREATEST('{$min_decimals}',LENGTH(TRIM(TRAILING '0' FROM SUBSTRING_INDEX(CAST({$col} AS CHAR),'.',-1))))),'{$this->getLocale()}')"; + + if (!$this->getNumberFormatter()->getAttribute(\NumberFormatter::GROUPING_USED)) { + $sql = "REPLACE({$sql}, '{$this->getNumberFormatter()->getSymbol(\NumberFormatter::GROUPING_SEPARATOR_SYMBOL)}', '')"; + } + return $sql; + } + + + + /** + * Set the minimum displayed fraction digits for formatted output. + * + * @param int $digits + * + * @return $this + */ + public function setMinDigits(int $digits): self + { + $this->getNumberFormatter()->setAttribute(\NumberFormatter::MIN_FRACTION_DIGITS, $digits); + return $this; + } + + + + /** + * Set the maximum displayed fraction digits for formatted output. + * + * @param int $digits + * + * @return $this + */ + public function setMaxDigits(int $digits): self + { + $this->getNumberFormatter()->setAttribute(\NumberFormatter::MAX_FRACTION_DIGITS, $digits); + return $this; + } + + + + /** + * Set the minimum displayed integer digits for formatted output. + * + * @param int $digits + * + * @return $this + */ + public function setMinIntDigits(int $digits): self + { + $this->getNumberFormatter()->setAttribute(\NumberFormatter::MIN_INTEGER_DIGITS, $digits); + return $this; + } + + + + /** + * Set the maximum displayed integer digits for formatted output. + * + * @param int $digits + * + * @return $this + */ + public function setMaxIntDigits(int $digits): self + { + $this->getNumberFormatter()->setAttribute(\NumberFormatter::MAX_INTEGER_DIGITS, $digits); + return $this; + } + + + + /** + * Set the minimum displayed significant digits for formatted output. + * + * @param int $digits + * + * @return $this + */ + public function setMinSignificantDigits(int $digits): self + { + $this->getNumberFormatter()->setAttribute(\NumberFormatter::MIN_SIGNIFICANT_DIGITS, $digits); + return $this; + } + + + + /** + * Set the maximum displayed significant digits for formatted output. + * + * @param int $digits + * + * @return $this + */ + public function setMaxSignificantDigits(int $digits): self + { + $this->getNumberFormatter()->setAttribute(\NumberFormatter::MAX_SIGNIFICANT_DIGITS, $digits); + return $this; + } + + + + /** + * Return the locale defined decimal symbol. + * + * @return string + */ + public function getDecimalSymbol(): string + { + return $this->getNumberFormatter()->getSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL); + } + + + + /** + * Overwrite the locale defined decimal symbol. + * + * @param string $symbol + * + * @return $this + */ + public function setDecimalSymbol(string $symbol): self + { + $this->getNumberFormatter()->setSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL, $symbol); + return $this; + } + + + + /** + * Disable grouping (thousands). + * + * @return $this + */ + public function hideGrouping(): self + { + $this->getNumberFormatter()->setAttribute(\NumberFormatter::GROUPING_USED, 0); + return $this; + } + + + + /** + * Return a \NumberFormatter object from cache. If the object does not exist, it is created first. + * + * @return \NumberFormatter + */ + protected function getNumberFormatter(): \NumberFormatter + { + if (!isset($this->numberFormatter)) { + $this->numberFormatter = new \NumberFormatter($this->getLocale(), $this->formatterStyle); + $this->numberFormatter->setAttribute(\NumberFormatter::LENIENT_PARSE, 1); + } + return $this->numberFormatter; + } + + + + protected function sanitizeInputString(string $string): string + { + return trim(strtolower(stripslashes(strval($string))), "abcdefghijklmnopqrstuvwxyz \t\n\r\0\x0B"); + } + + + + /** + * Internal parse function. Calls \NumberFormatter::parse(). + * + * @param string $input + * @param \NumberFormatter|null $numberFormatter + * + * @return false|float|int + */ + protected function parse(string $input, \NumberFormatter|null $numberFormatter = null): false|float|int + { + if ($numberFormatter === null) { + $numberFormatter = $this->getNumberFormatter(); + } + if (($output = $numberFormatter->parse($input, $this->parseType)) === false) { + // could not parse number + // as a last resort, try to parse the number with locale de_DE + // This is necessary, because of many str_replace, where dot is replaced with comma + $numFmt = new \NumberFormatter('de_DE', \NumberFormatter::DECIMAL); + $output = $numFmt->parse($input, $this->parseType); + } + return $output; + } + + + + /** + * Internal format function. Calls \NumberFormatter::format(). + * + * @param float|int $phpVal + * @param \NumberFormatter|null $numberFormatter + * + * @return false|string + */ + protected function format(float|int $phpVal, \NumberFormatter|null $numberFormatter = null): false|string + { + if ($numberFormatter === null) { + $numberFormatter = $this->getNumberFormatter(); + } + return $numberFormatter->format($phpVal); + } +} \ No newline at end of file diff --git a/classes/Components/I18n/Formatter/FormatterInterface.php b/classes/Components/I18n/Formatter/FormatterInterface.php new file mode 100644 index 00000000..036dd408 --- /dev/null +++ b/classes/Components/I18n/Formatter/FormatterInterface.php @@ -0,0 +1,84 @@ + + * SPDX-License-Identifier: AGPL-3.0-only + */ + +declare(strict_types=1); + +namespace Xentral\Components\I18n\Formatter; + +/** + * FormatterInterface defines the mandatory interface each formatter has to provide. + * + * @author Roland Rusch, easy-smart solution GmbH + */ +interface FormatterInterface +{ + /** + * Parse string from user input and store as desired type in object. + * If parsing fails, an Exception is thrown. + * + * @param string $input + * + * @return self + */ + public function parseUserInput(string $input): self; + + + + /** + * Set the native PHP value in the formatter. + * The value must ALWAYS be of the requested type or an Exception is thrown. + * + * @param mixed $input + * + * @return self + */ + public function setPhpVal(mixed $input): self; + + + + /** + * Get the native PHP value from the formatter. + * The value must ALWAYS be of the requested type or an Exception is thrown. + * + * @return mixed + */ + public function getPhpVal(): mixed; + + + + /** + * Return a string representing the PHP value as a formatted value. + * Throws an Exception if no value was set before or the value is of the wrong type. + * + * @return string + */ + public function formatForUser(): string; + + + + /** + * Return a string that can be used in an SQL query to format the value for presentation to a User. + * Should return the same string as if it was formatted by FormatterInterface::formatForUser(), but directly from + * the database. + * This function does not need a native PHP value, but a table column is needed. + * + * @param string $col + * + * @return string + */ + public function formatForUserWithSqlStatement(string $col): string; + + + + /** + * Test $input for the correct (strict) type the class supports. Return false otherwise. + * + * @param mixed $input + * + * @return bool + */ + public function isStrictValidPhpVal($input): bool; +} \ No newline at end of file diff --git a/classes/Components/I18n/Formatter/FormatterMode.php b/classes/Components/I18n/Formatter/FormatterMode.php new file mode 100644 index 00000000..bb28b329 --- /dev/null +++ b/classes/Components/I18n/Formatter/FormatterMode.php @@ -0,0 +1,31 @@ + + * SPDX-License-Identifier: AGPL-3.0-only + */ + +declare(strict_types=1); + +namespace Xentral\Components\I18n\Formatter; + +enum FormatterMode +{ + + /** + * Only values representing the class type are allowed for input and output. This means, you can only store a + * float (or it's localized string representation) to a FloatFormatter). + */ + case MODE_STRICT; + + /** + * Allow NULL as valid value in addition to the formatter type. If the user enters an empty string, this results + * in a PHP value of NULL and vice versa. + */ + case MODE_NULL; + + /** + * Allow an empty string in addition to the formatter type. If the user entern an empty string, this results in + * an emtpy string as PHP value and vice versa. + */ + case MODE_EMPTY; +} diff --git a/classes/Components/I18n/Formatter/IntegerFormatter.php b/classes/Components/I18n/Formatter/IntegerFormatter.php new file mode 100644 index 00000000..efafbd4f --- /dev/null +++ b/classes/Components/I18n/Formatter/IntegerFormatter.php @@ -0,0 +1,82 @@ + + * SPDX-License-Identifier: AGPL-3.0-only + */ + +declare(strict_types=1); + +namespace Xentral\Components\I18n\Formatter; + +use Xentral\Components\I18n\Formatter\FloatFormatter; + +class IntegerFormatter extends FloatFormatter +{ + + + protected function init(): void + { + $this->parseType = \NumberFormatter::TYPE_INT64; + parent::init(); + parent::setMaxDigits(0); + } + + + + /** + * For IntegerFormatter, strip decimals from $input if a number is given. + * + * @param mixed $input + * + * @return AbstractFormatter + */ + public function setPhpVal(mixed $input): AbstractFormatter + { + return parent::setPhpVal(is_numeric($input) ? intval($input) : $input); + } + + + + /** + * Check if $input is an integer. + * + * @param $input + * + * @return bool + */ + public function isStrictValidPhpVal($input): bool + { + return is_integer($input); + } + + + + /** + * Don't allow setting of min digits. + * + * @param int $digits + * + * @return \Xentral\Components\I18n\Formatter\FloatFormatter + */ + public function setMinDigits(int $digits): \Xentral\Components\I18n\Formatter\FloatFormatter + { + return $this; + } + + + + /** + * Don't allow setting of max digits. + * + * @param int $digits + * + * @return \Xentral\Components\I18n\Formatter\FloatFormatter + */ + + public function setMaxDigits(int $digits): \Xentral\Components\I18n\Formatter\FloatFormatter + { + return $this; + } + + +} \ No newline at end of file diff --git a/classes/Components/I18n/FormatterService.php b/classes/Components/I18n/FormatterService.php new file mode 100644 index 00000000..b927002d --- /dev/null +++ b/classes/Components/I18n/FormatterService.php @@ -0,0 +1,336 @@ + + * SPDX-License-Identifier: AGPL-3.0-only + */ + +declare(strict_types=1); + +namespace Xentral\Components\I18n; + +use Xentral\Components\I18n\Formatter\CurrencyFormatter; +use Xentral\Components\I18n\Formatter\FloatFormatter; +use Xentral\Components\I18n\Formatter\FormatterInterface; +use Xentral\Components\I18n\Formatter\FormatterMode; +use Xentral\Components\I18n\Formatter\IntegerFormatter; + +/** + * This service creates formatters for localized input and output. + * + * @author Roland Rusch, easy-smart solution GmbH + */ +class FormatterService +{ + private string $locale; + + + + /** + * Construct a FormatterService object. + * + * @param string $locale + */ + public function __construct(string $locale) + { + $this->locale = $locale; + } + + + + /** + * Return the currently set locale. + * + * @return string + */ + public function getLocale(): string + { + return $this->locale; + } + + + + /** + * Factory for FormatterInterface objects. There will be a FormatterInterface object for every data type + * necessary. + * + * @param string $type + * @param FormatterMode $strictness + * + * @return FormatterInterface + */ + public function factory(string $type, FormatterMode $strictness = FormatterMode::MODE_STRICT): FormatterInterface + { + return new $type($this->locale, $strictness); + } + + + + /** + * Shortcut function for creating a FloatFormatter and parsing a user input. + * + * @param string $input + * @param FormatterMode $strictness + * + * @return FloatFormatter + */ + public function floatFromUserInput( + string $input, + FormatterMode $strictness = FormatterMode::MODE_NULL + ): FloatFormatter { + $formatter = new FloatFormatter($this->locale, $strictness); + $formatter->parseUserInput($input); + return $formatter; + } + + + + /** + * Shortcut function for creating a FloatFormatter and setting a PHP value. + * + * @param string|float|null $input + * @param FormatterMode $strictness + * + * @return FloatFormatter + */ + public function floatFromPhpVal( + string|null|float $input, + FormatterMode $strictness = FormatterMode::MODE_NULL + ): FloatFormatter { + $formatter = $this->factory(FloatFormatter::class, $strictness); + $formatter->setPhpVal($input); + return $formatter; + } + + + + /** + * Replace callback function for \FormActionHandler. + * Parses and Formats an integer and allows an empty string. + * + * @param int $toDatabase + * @param mixed $value + * @param $fromForm + * + * @return mixed|string + * @see \FormActionHandler + */ + public function replaceIntegerOrEmpty(int $toDatabase, mixed $value, $fromForm) + { + $formatter = new IntegerFormatter($this->locale, FormatterMode::MODE_EMPTY); + + + if (!is_numeric($value) || $fromForm) { + $formatter->parseUserInput(strval($value)); + } else { + $formatter->setPhpVal($value); + } + + + if ($toDatabase) { + return $formatter->getPhpVal(); + } else { + return $formatter->formatForUser(); + } + } + + + + /** + * Replace callback function for \FormActionHandler. + * Parses and Formats a float and allows an empty string. + * Output shows decimals, if present. + * + * @param int $toDatabase + * @param mixed $value + * @param $fromForm + * + * @return mixed|string + * @see \FormActionHandler + */ + public function replaceDecimalOrEmpty(int $toDatabase, mixed $value, $fromForm) + { + $formatter = new FloatFormatter($this->locale, FormatterMode::MODE_EMPTY); + + + if (!is_numeric($value) || $fromForm) { + $formatter->parseUserInput(strval($value)); + } else { + $formatter->setPhpVal(floatval($value)); + } + + + if ($toDatabase) { + return $formatter->getPhpVal(); + } else { + return $formatter->formatForUser(); + } + } + + + + /** + * Replace callback function for \FormActionHandler. + * Parses and Formats a float and allows an empty string. + * Output always shows at least 7 decimal. + * + * @param int $toDatabase + * @param mixed $value + * @param $fromForm + * + * @return mixed|string + * @see \FormActionHandler + */ + public function replaceDecimalGeoOrEmpty(int $toDatabase, mixed $value, $fromForm) + { + $formatter = new FloatFormatter($this->locale, FormatterMode::MODE_EMPTY); + $formatter->setMinDigits(7); + + + if (!is_numeric($value) || $fromForm) { + $formatter->parseUserInput(strval($value)); + } else { + $formatter->setPhpVal(floatval($value)); + } + + + if ($toDatabase) { + return $formatter->getPhpVal(); + } else { + return $formatter->formatForUser(); + } + } + + + + /** + * Replace callback function for \FormActionHandler. + * Parses and Formats a float and allows an empty string. + * Output always shows at least 1 decimal. + * + * @param int $toDatabase + * @param mixed $value + * @param $fromForm + * + * @return mixed|string + * @see \FormActionHandler + */ + public function replaceDecimalPercentOrEmpty(int $toDatabase, mixed $value, $fromForm) + { + $formatter = new FloatFormatter($this->locale, FormatterMode::MODE_EMPTY); + $formatter->setMinDigits(1); + + + if (!is_numeric($value) || $fromForm) { + $formatter->parseUserInput(strval($value)); + } else { + $formatter->setPhpVal(floatval($value)); + } + + + if ($toDatabase) { + return $formatter->getPhpVal(); + } else { + return $formatter->formatForUser(); + } + } + + + + /** + * Replace callback function for \FormActionHandler. + * Parses and Formats a float and allows an empty string. + * + * @param int $toDatabase + * @param mixed $value + * @param $fromForm + * + * @return mixed|string + * @see \FormActionHandler + */ + public function replaceCurrencyOrEmpty(int $toDatabase, mixed $value, $fromForm) + { + $formatter = (new FloatFormatter($this->locale, FormatterMode::MODE_EMPTY)); + $formatter->setMinDigits(2); + + + if (!is_numeric($value) || $fromForm) { + $formatter->parseUserInput(strval($value)); + } else { + $formatter->setPhpVal(floatval($value)); + } + + + if ($toDatabase) { + return $formatter->getPhpVal(); + } else { + return $formatter->formatForUser(); + } + } + + + + /** + * Format a quantity value for output. + * + * @param mixed $menge + * + * @return string + */ + public function formatMenge(mixed $menge): string + { + $formatter = new FloatFormatter($this->locale, FormatterMode::MODE_EMPTY); + $formatter->setPhpVal(floatval($menge)); + return $formatter->formatForUser(); + } + + + + /** + * Parse a quantity from a form and parse for database input. + * + * @param mixed $string + * + * @return string|float|null + */ + public function parseMenge(mixed $string): string|null|float + { + $formatter = new FloatFormatter($this->locale, FormatterMode::MODE_EMPTY); + $formatter->parseUserInput(strval($string)); + return $formatter->getPhpVal(); + } + + + + /** + * Format a price value for output. + * + * @param mixed $menge + * + * @return string + */ + public function formatPreis(mixed $menge): string + { + $formatter = new FloatFormatter($this->locale, FormatterMode::MODE_EMPTY); + $formatter->setMinDigits(2); + $formatter->setPhpVal(floatval($menge)); + return $formatter->formatForUser(); + } + + + + /** + * Parse a price from a form and parse for database input. + * + * @param mixed $string + * + * @return string|float|null + */ + public function parsePreis(mixed $string): string|null|float + { + $formatter = new FloatFormatter($this->locale, FormatterMode::MODE_EMPTY); + $formatter->setMinDigits(2); + $formatter->parseUserInput(strval($string)); + return $formatter->getPhpVal(); + } + +} \ No newline at end of file diff --git a/classes/Components/I18n/Localization.php b/classes/Components/I18n/Localization.php index a29fb7f3..940e34c8 100644 --- a/classes/Components/I18n/Localization.php +++ b/classes/Components/I18n/Localization.php @@ -12,6 +12,7 @@ use Locale; use Xentral\Components\Http\Request; use Xentral\Components\Http\Session\Session; use Xentral\Components\I18n\Exception\LanguageNotInitializedException; +use Xentral\Components\I18n\Exception\LocaleNotSetException; use Xentral\Components\I18n\Exception\UnsupportedLocaleStringException; /** @@ -31,7 +32,7 @@ final class Localization implements LocalizationInterface private array $language = []; - private array $locale = []; + private string|null $locale; @@ -41,6 +42,11 @@ final class Localization implements LocalizationInterface $this->session = $session; $this->usersettings = $usersettings; $this->config = $config; + + // Set config if no default is given + $this->config[Localization::LOCALE_DEFAULT]=$this->config[Localization::LOCALE_DEFAULT] ?? 'de_DE'; + $this->config[Localization::LANGUAGE_DEFAULT]=$this->config[Localization::LANGUAGE_DEFAULT] ?? 'deu'; + $this->process(); } @@ -49,39 +55,48 @@ final class Localization implements LocalizationInterface public function process() { // Hardcoded defaults if config is not available - $localeDefault = $this->config[Localization::LOCALE_DEFAULT] ?? 'de_DE'; +// $localeDefault = $this->config[Localization::LOCALE_DEFAULT] ?? 'de_DE'; $localeAttrName = $this->config[Localization::LOCALE_ATTRIBUTE_NAME] ?? 'locale'; $langDefault = $this->config[Localization::LANGUAGE_DEFAULT] ?? 'deu'; $langAttrName = $this->config[Localization::LANGUAGE_ATTRIBUTE_NAME] ?? 'language'; $segmentName = 'i18n'; + $this->setLocale((string)$this->config[Localization::LOCALE_DEFAULT]); + // Get the locale from the session, if available - if ($this->session && ($locale = $this->session->getValue($segmentName, $localeAttrName))) { + if ($this->session && ($sessionLocale = $this->session->getValue($segmentName, $localeAttrName))) { + $this->setLocale((string)$sessionLocale, $this->getLocale()); } else { // Get locale from request, fallback to the user's browser preference if ($this->request) { - $locale = $this->request->attributes->get( - $localeAttrName, - Locale::acceptFromHttp( - $this->request->getHeader('Accept-Language', $localeDefault) - ) ?? $localeDefault - ); + try { + $this->setLocale((string)$this->request->attributes->get($localeAttrName, '')); + } catch (UnsupportedLocaleStringException $e) { + $this->setLocale( + (string) + Locale::acceptFromHttp( + $this->request->getHeader('Accept-Language', '') + ) + , + $this->getLocale() + ); + } } else { - $locale = Locale::acceptFromHttp($_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? $localeDefault); + $this->setLocale((string)Locale::acceptFromHttp($_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? ''), $this->getLocale()); } } // Get locale from user // This overrides all previous attempts to find a locale if (array_key_exists('locale', $this->usersettings)) { - $locale = $this->usersettings['locale']; + $this->setLocale((string)$this->usersettings['locale'], $this->getLocale()); } // Get locale from query string // This overrides all previous attempts to find a locale if ($this->request) { - $locale = $this->request->getParam($localeAttrName, $locale ?? $localeDefault); + $this->setLocale((string)$this->request->getParam($localeAttrName), $this->getLocale()); } else { - $locale = $_GET[$localeAttrName] ?? $locale ?? $localeDefault; + $this->setLocale((string)$_GET[$localeAttrName], $this->getLocale()); } @@ -90,9 +105,9 @@ final class Localization implements LocalizationInterface } else { // Get language from request, fallback to the current locale if ($this->request) { - $language = $this->request->attributes->get($langAttrName, Locale::getPrimaryLanguage($locale)); + $language = $this->request->attributes->get($langAttrName, Locale::getPrimaryLanguage($this->getLocale())); } else { - $language = Locale::getPrimaryLanguage($locale); + $language = Locale::getPrimaryLanguage($this->getLocale()); } } // Get language from user @@ -114,22 +129,21 @@ final class Localization implements LocalizationInterface // Store the locale and language to the LocalizationInterface $this->setLanguage($language); - $this->setLocale($locale); // Store the locale and language to the session if ($this->session) { - $this->session->setValue($segmentName, $localeAttrName, $locale); - $this->session->setValue($segmentName, $langAttrName, $language); + $this->session->setValue($segmentName, $localeAttrName, $this->getLocale()); + $this->session->setValue($segmentName, $langAttrName, $this->getLanguage()); } // Store the locale and language as a request attribute if ($this->request) { - $this->request->attributes->set($localeAttrName, $locale); - $this->request->attributes->set($langAttrName, $language); + $this->request->attributes->set($localeAttrName, $this->getLocale()); + $this->request->attributes->set($langAttrName, $this->getLanguage()); } // Set the default locale - Locale::setDefault($locale); + Locale::setDefault($this->getLocale()); // error_log(self::class . ": {$locale}"); } @@ -179,19 +193,42 @@ final class Localization implements LocalizationInterface /** * Set the locale. * - * @param string $locale + * @param string $locale + * @param string|null $fallbackLocale */ - public function setLocale(string $locale) + public function setLocale(string $locale, string|null $fallbackLocale = null): void { - $parsedLocale = Locale::parseLocale($locale); - $locale = Locale::composeLocale([ - 'language' => $parsedLocale['language'], - 'region' => $parsedLocale['region'], - ]); + if(!empty($locale)) { + // Check if locale is already set + try { + if ($locale == $this->getLocale()) { + return; + } + } catch (LocaleNotSetException $e) { + }; + + // Parse and re-compose locale to make sure, it is sane + $parsedLocale = Locale::parseLocale($locale); + $composedLocale = Locale::composeLocale([ + 'language' => $parsedLocale['language'], + 'region' => $parsedLocale['region'], + ]); + } else { + $composedLocale=null; + } - if(!$locale) throw new UnsupportedLocaleStringException("The given locale string '{$locale}' is not supported"); + // If sanity check fails, set fallbackLocale if present + // throw exception otherwise + if (!$composedLocale) { + if ($fallbackLocale === null) { + throw new UnsupportedLocaleStringException("The given locale string '{$locale}' is not supported"); + } else { + $this->setLocale($fallbackLocale); + return; + } + } - $this->locale[Iso3166\Key::DEFAULT] = $locale; + $this->locale = $composedLocale; } @@ -199,13 +236,14 @@ final class Localization implements LocalizationInterface /** * Return the locale string as defined by $key. * - * @param string|null $key A constant from Iso3166\Key - * * @return string */ - public function getLocale(string $key = null): string + public function getLocale(): string { - return $this->locale[Iso3166\Key::DEFAULT]; + if (!isset($this->locale)) { + throw new LocaleNotSetException(); + } + return $this->locale; } @@ -222,7 +260,7 @@ final class Localization implements LocalizationInterface $localization = clone $this; // Find language from address array or keep current language - if (!$lang = Bootstrap::findLanguage($adresse['sprache'])) { + if (!$lang = Bootstrap::findLanguage($adresse['sprache'] ?? '')) { $lang = Bootstrap::findLanguage($this->getLanguage()); } if ($lang) { @@ -230,7 +268,7 @@ final class Localization implements LocalizationInterface } // Find region from address or keep current region - if (!$region = Bootstrap::findRegion($adresse['land'])) { + if (!$region = Bootstrap::findRegion($adresse['land'] ?? '')) { $parsedLocale = Locale::parseLocale($this->getLocale()); $region = Bootstrap::findRegion($parsedLocale['region']); } diff --git a/classes/Components/I18n/LocalizationInterface.php b/classes/Components/I18n/LocalizationInterface.php index 9bb39aaf..24390222 100644 --- a/classes/Components/I18n/LocalizationInterface.php +++ b/classes/Components/I18n/LocalizationInterface.php @@ -45,18 +45,17 @@ interface LocalizationInterface /** * Set the locale. * - * @param string $locale + * @param string $locale + * @param string|null $fallbackLocale */ - public function setLocale(string $locale); + public function setLocale(string $locale, string|null $fallbackLocale=null); /** * Return the locale string as defined by $key. * - * @param string|null $key A constant from Iso3166\Key - * * @return string */ - public function getLocale(string $key = null): string; + public function getLocale(): string; } \ No newline at end of file diff --git a/classes/Components/I18n/Test/CurrencyFormatterTest.php b/classes/Components/I18n/Test/CurrencyFormatterTest.php new file mode 100644 index 00000000..94c420c8 --- /dev/null +++ b/classes/Components/I18n/Test/CurrencyFormatterTest.php @@ -0,0 +1,155 @@ + + * SPDX-License-Identifier: AGPL-3.0-only + */ + +declare(strict_types=1); + +namespace Xentral\Components\I18n\Test; + +require(__DIR__ . '/../../../bootstrap.php'); + +use Xentral\Components\I18n\Formatter\CurrencyFormatter; +use PHPUnit\Framework\TestCase; +use Xentral\Components\I18n\Formatter\FormatterMode; + +class CurrencyFormatterTest extends TestCase +{ + public function testStrictModeWithValue() + { + $floatFormatter = new CurrencyFormatter('de_DE', FormatterMode::MODE_STRICT); + $output = $floatFormatter->setPhpVal(floatval(1234.56))->formatForUser(); + var_dump($output); + $this->assertIsString($output); + $this->assertEquals('1.234,56 EUR', $output); + + $floatFormatter = new CurrencyFormatter('de_DE', FormatterMode::MODE_STRICT); + $output = $floatFormatter->setPhpVal(intval(1234))->formatForUser(); + var_dump($output); + $this->assertIsString($output); + $this->assertEquals('1.234 EUR', $output); + + $floatFormatter = new CurrencyFormatter('de_CH', FormatterMode::MODE_STRICT); + $output = $floatFormatter->setPhpVal(floatval(1234.56))->formatForUser(); + var_dump($output); + $this->assertIsString($output); + $this->assertEquals('CHF 1’234.56', $output); + } + + + + public function testStrictModeWithValueWithoutCCY() + { + $floatFormatter = new CurrencyFormatter('de_CH', FormatterMode::MODE_STRICT); + $floatFormatter->hideCurrency(); + $output = $floatFormatter->setPhpVal(floatval(1234.56))->formatForUser(); + var_dump($output); + $this->assertIsString($output); + $this->assertEquals('1’234.56', $output); + } + + + + public function testStrictModeWithValueInput() + { + $floatFormatter = new CurrencyFormatter('de_DE', FormatterMode::MODE_STRICT); + $output = $floatFormatter->parseUserInput('1234,56 EUR')->getPhpVal(); + var_dump($output); + $this->assertIsFloat($output); + $this->assertEquals(1234.56, $output); + + $output = $floatFormatter->parseUserInput('EUR 1234')->getPhpVal(); + var_dump($output); + $this->assertIsFloat($output); + $this->assertEquals(1234, $output); + } + + + + public function testStrictModeWithValueInputWithoutCCY() + { + $floatFormatter = (new CurrencyFormatter('de_DE', FormatterMode::MODE_STRICT))->hideCurrency(); + $output = $floatFormatter->parseUserInput('1234,56')->getPhpVal(); + var_dump($output); + $this->assertIsFloat($output); + $this->assertEquals(1234.56, $output); + + $floatFormatter = (new CurrencyFormatter('de_DE', FormatterMode::MODE_STRICT))->hideCurrency(); + $output = $floatFormatter->parseUserInput('1234')->getPhpVal(); + var_dump($output); + $this->assertIsFloat($output); + $this->assertEquals(1234, $output); + } + + + + public function testCanParseOwnOutput() + { + $value = 1234567.89; + $locale = 'de_DE'; + $floatFormatter1 = new CurrencyFormatter($locale); + $output = $floatFormatter1->setPhpVal($value)->formatForUser(); + var_dump($output); + $floatFormatter2 = new CurrencyFormatter($locale); + $input = $floatFormatter2->parseUserInput($output)->getPhpVal(); + var_dump($input); + $this->assertEquals($value, $input); + + $locale = 'de_CH'; + $floatFormatter1 = new CurrencyFormatter($locale); + $output = $floatFormatter1->setPhpVal($value)->formatForUser(); + var_dump($output); + $floatFormatter2 = new CurrencyFormatter($locale); + $input = $floatFormatter2->parseUserInput($output)->getPhpVal(); + var_dump($input); + $this->assertEquals($value, $input); + + $locale = 'de_AT'; + $floatFormatter1 = new CurrencyFormatter($locale); + $output = $floatFormatter1->setPhpVal($value)->formatForUser(); + var_dump($output); + $floatFormatter2 = new CurrencyFormatter($locale); + $input = $floatFormatter2->parseUserInput($output)->getPhpVal(); + var_dump($input); + $this->assertEquals($value, $input); + } + + + + public function testCanParseOwnOutputWithoutCCY() + { + $value = 1234567.89; + $locale = 'de_DE'; + $floatFormatter1 = new CurrencyFormatter($locale); + $floatFormatter1->hideCurrency(); + $output = $floatFormatter1->setPhpVal($value)->formatForUser(); + var_dump($output); + $floatFormatter2 = new CurrencyFormatter($locale); + $floatFormatter2->hideCurrency(); + $input = $floatFormatter2->parseUserInput($output)->getPhpVal(); + var_dump($input); + $this->assertEquals($value, $input); + + $locale = 'de_CH'; + $floatFormatter1 = (new CurrencyFormatter($locale))->hideCurrency(); + $output = $floatFormatter1->setPhpVal($value)->formatForUser(); + var_dump($output); + $floatFormatter2 = new CurrencyFormatter($locale); + $floatFormatter2->hideCurrency(); + $input = $floatFormatter2->parseUserInput($output)->getPhpVal(); + var_dump($input); + $this->assertEquals($value, $input); + + $locale = 'de_AT'; + $floatFormatter1 = (new CurrencyFormatter($locale))->hideCurrency(); + $output = $floatFormatter1->setPhpVal($value)->formatForUser(); + $floatFormatter1->hideCurrency(); + var_dump($output); + $floatFormatter2 = (new CurrencyFormatter($locale))->hideCurrency(); + $input = $floatFormatter2->parseUserInput($output)->getPhpVal(); + var_dump($input); + $this->assertEquals($value, $input); + } + +} diff --git a/classes/Components/I18n/Test/FloatFormatterTest.php b/classes/Components/I18n/Test/FloatFormatterTest.php new file mode 100644 index 00000000..ce2fc93d --- /dev/null +++ b/classes/Components/I18n/Test/FloatFormatterTest.php @@ -0,0 +1,307 @@ + + * SPDX-License-Identifier: AGPL-3.0-only + */ + +declare(strict_types=1); + +namespace Xentral\Components\I18n\Test; + +require(__DIR__ . '/../../../bootstrap.php'); + +use Xentral\Components\I18n\Formatter\Exception\TypeErrorException; +use Xentral\Components\I18n\Formatter\FloatFormatter; +use PHPUnit\Framework\TestCase; +use Xentral\Components\I18n\Formatter\FormatterMode; + +class FloatFormatterTest extends TestCase +{ + public function testStrictModeWithValue() + { + $floatFormatter = new FloatFormatter('de_DE', FormatterMode::MODE_STRICT); + $output = $floatFormatter->setPhpVal(floatval(1234.56))->formatForUser(); + var_dump($output); + $this->assertIsString($output); + $this->assertEquals('1.234,56', $output); + + $output = $floatFormatter->setPhpVal(intval(1234))->formatForUser(); + var_dump($output); + $this->assertIsString($output); + $this->assertEquals('1.234', $output); + } + + + + public function testStrictModeWithValueInput() + { + $floatFormatter = new FloatFormatter('de_DE', FormatterMode::MODE_STRICT); + $output = $floatFormatter->parseUserInput('1234,56')->getPhpVal(); + var_dump($output); + $this->assertIsFloat($output); + $this->assertEquals(1234.56, $output); + + $output = $floatFormatter->parseUserInput('1234')->getPhpVal(); + var_dump($output); + $this->assertIsFloat($output); + $this->assertEquals(1234, $output); + } + + + + public function testStrictModeWithValueInputNoDecimals() + { + $floatFormatter = new FloatFormatter('de_DE', FormatterMode::MODE_STRICT); + $output = $floatFormatter->parseUserInput('1234')->getPhpVal(); + var_dump($output); + $this->assertIsFloat($output); + } + + + + public function testStrictModeWithNull() + { + $floatFormatter = new FloatFormatter('de_DE', FormatterMode::MODE_STRICT); + $this->expectException(TypeErrorException::class); + $output = $floatFormatter->setPhpVal(null)->formatForUser(); + } + + + + public function testStrictModeWithNullInput() + { + $floatFormatter = new FloatFormatter('de_DE', FormatterMode::MODE_STRICT); + $this->expectException(TypeErrorException::class); + $output = $floatFormatter->parseUserInput('')->getPhpVal(); + } + + + + public function testStrictModeWithEmpty() + { + $floatFormatter = new FloatFormatter('de_DE', FormatterMode::MODE_STRICT); + $this->expectException(TypeErrorException::class); + $output = $floatFormatter->setPhpVal('')->formatForUser(); + } + + + + public function testStrictModeWithEmptyInput() + { + $floatFormatter = new FloatFormatter('de_DE', FormatterMode::MODE_STRICT); + $this->expectException(TypeErrorException::class); + $output = $floatFormatter->parseUserInput('')->getPhpVal(); + } + + + + public function testNullModeWithValue() + { + $floatFormatter = new FloatFormatter('de_DE', FormatterMode::MODE_NULL); + $output = $floatFormatter->setPhpVal(floatval(1234.56))->formatForUser(); + var_dump($output); + $this->assertIsString($output); + $this->assertEquals('1.234,56', $output); + + $output = $floatFormatter->setPhpVal(intval(1234))->formatForUser(); + var_dump($output); + $this->assertIsString($output); + $this->assertEquals('1.234', $output); + } + + + + public function testNullModeWithValueInput() + { + $floatFormatter = new FloatFormatter('de_DE', FormatterMode::MODE_NULL); + $output = $floatFormatter->parseUserInput('1234,56')->getPhpVal(); + var_dump($output); + $this->assertIsFloat($output); + $this->assertEquals(1234.56, $output); + + $output = $floatFormatter->parseUserInput('1234')->getPhpVal(); + var_dump($output); + $this->assertIsFloat($output); + $this->assertEquals(1234, $output); + } + + + + public function testNullModeWithNull() + { + $floatFormatter = new FloatFormatter('de_DE', FormatterMode::MODE_NULL); + $output = $floatFormatter->setPhpVal(null)->formatForUser(); + var_dump($output); + $this->assertIsString($output); + $this->assertEquals('', $output); + } + + + + public function testNullModeWithNullInput() + { + $floatFormatter = new FloatFormatter('de_DE', FormatterMode::MODE_NULL); + $output = $floatFormatter->parseUserInput('')->getPhpVal(); + var_dump($output); + $this->assertNull($output); + } + + + + public function testNullModeWithEmpty() + { + $floatFormatter = new FloatFormatter('de_DE', FormatterMode::MODE_NULL); + $this->expectException(TypeErrorException::class); + $output = $floatFormatter->setPhpVal('')->formatForUser(); + } + + + + public function testNullModeWithEmptyInput() + { + $floatFormatter = new FloatFormatter('de_DE', FormatterMode::MODE_NULL); + $output = $floatFormatter->parseUserInput('')->getPhpVal(); + var_dump($output); + $this->assertNull($output); + } + + + + public function testEmptyModeWithValue() + { + $floatFormatter = new FloatFormatter('de_DE', FormatterMode::MODE_EMPTY); + $output = $floatFormatter->setPhpVal(1234.56)->formatForUser(); + var_dump($output); + $this->assertIsString($output); + $this->assertEquals('1.234,56', $output); + + $output = $floatFormatter->setPhpVal(1234)->formatForUser(); + var_dump($output); + $this->assertIsString($output); + $this->assertEquals('1.234', $output); + } + + + + public function testEmptyModeWithValueInput() + { + $floatFormatter = new FloatFormatter('de_DE', FormatterMode::MODE_EMPTY); + $output = $floatFormatter->parseUserInput('1234,56')->getPhpVal(); + var_dump($output); + $this->assertIsFloat($output); + $this->assertEquals(1234.56, $output); + + $output = $floatFormatter->parseUserInput('1234')->getPhpVal(); + var_dump($output); + $this->assertIsFloat($output); + $this->assertEquals(1234, $output); + } + + + + public function testEmptyModeWithNull() + { + $floatFormatter = new FloatFormatter('de_DE', FormatterMode::MODE_EMPTY); + $this->expectException(TypeErrorException::class); + $output = $floatFormatter->setPhpVal(null)->formatForUser(); + } + + + + public function testEmptyModeWithNullInput() + { + $floatFormatter = new FloatFormatter('de_DE', FormatterMode::MODE_EMPTY); + $output = $floatFormatter->parseUserInput('')->getPhpVal(); + var_dump($output); + $this->assertIsString($output); + $this->assertEquals('', $output); + } + + + + public function testEmptyModeWithEmpty() + { + $floatFormatter = new FloatFormatter('de_DE', FormatterMode::MODE_EMPTY); + $output = $floatFormatter->setPhpVal('')->formatForUser(); + var_dump($output); + $this->assertIsString($output); + $this->assertEquals('', $output); + } + + + + public function testEmptyModeWithEmptyInput() + { + $floatFormatter = new FloatFormatter('de_DE', FormatterMode::MODE_EMPTY); + $output = $floatFormatter->parseUserInput('')->getPhpVal(); + var_dump($output); + $this->assertIsString($output); + $this->assertEquals('', $output); + } + + + + public function testCanParseOwnOutput() + { + $value = 1234567.89; + $locale = 'de_DE'; + $floatFormatter1 = new FloatFormatter($locale); + $output = $floatFormatter1->setPhpVal($value)->formatForUser(); + var_dump($output); + $floatFormatter2 = new FloatFormatter($locale); + $input = $floatFormatter2->parseUserInput($output)->getPhpVal(); + var_dump($input); + $this->assertEquals($value, $input); + + $locale = 'de_CH'; + $floatFormatter1 = new FloatFormatter($locale); + $output = $floatFormatter1->setPhpVal($value)->formatForUser(); + var_dump($output); + $floatFormatter2 = new FloatFormatter($locale); + $input = $floatFormatter2->parseUserInput($output)->getPhpVal(); + var_dump($input); + $this->assertEquals($value, $input); + + $locale = 'de_AT'; + $floatFormatter1 = new FloatFormatter($locale); + $output = $floatFormatter1->setPhpVal($value)->formatForUser(); + var_dump($output); + $floatFormatter2 = new FloatFormatter($locale); + $input = $floatFormatter2->parseUserInput($output)->getPhpVal(); + var_dump($input); + $this->assertEquals($value, $input); + } + + + + public function testCanSetDigits() + { + $value = 1234567.8901234; + $locale = 'de_DE'; + $floatFormatter1 = new FloatFormatter($locale); + $output = $floatFormatter1->setPhpVal($value)->setMaxDigits(4)->formatForUser(); + var_dump($output); + $this->assertEquals('1.234.567,8901', $output); + + $floatFormatter1 = new FloatFormatter($locale); + $output = $floatFormatter1->setPhpVal($value)->setMaxDigits(3)->formatForUser(); + var_dump($output); + $this->assertEquals('1.234.567,89', $output); + + $floatFormatter1 = new FloatFormatter($locale); + $output = $floatFormatter1->setPhpVal($value)->setMaxDigits(3)->setMinDigits(3)->formatForUser(); + var_dump($output); + $this->assertEquals('1.234.567,890', $output); + + $floatFormatter1 = new FloatFormatter($locale); + $output = $floatFormatter1->setPhpVal($value)->setMinDigits(10)->formatForUser(); + var_dump($output); + $this->assertEquals('1.234.567,8901234000', $output); + + $floatFormatter1 = new FloatFormatter($locale); + $output = $floatFormatter1->setPhpVal($value)->setMaxDigits(0)->formatForUser(); + var_dump($output); + $this->assertEquals('1.234.568', $output); + } + +} diff --git a/classes/Components/I18n/Test/FormatterServiceTest.php b/classes/Components/I18n/Test/FormatterServiceTest.php new file mode 100644 index 00000000..51ec905d --- /dev/null +++ b/classes/Components/I18n/Test/FormatterServiceTest.php @@ -0,0 +1,144 @@ + + * SPDX-License-Identifier: AGPL-3.0-only + */ + +declare(strict_types=1); + +namespace Xentral\Components\I18n\Test; + +require(__DIR__ . '/../../../bootstrap.php'); + + +use Xentral\Components\I18n\Formatter\FloatFormatter; +use Xentral\Components\I18n\FormatterService; +use PHPUnit\Framework\TestCase; + +class FormatterServiceTest extends TestCase +{ + public function testCanCreateFormatterService() + { + $formatterService = new FormatterService('de_DE'); + $this->assertInstanceOf(FormatterService::class, $formatterService); + } + + + + public function testFloatFromUserInput() + { + $formatterService = new FormatterService('de_DE'); + /** @var FloatFormatter $floatFormatter */ + $floatFormatter = $formatterService->floatFromUserInput("1.234,56"); + $phpVal = $floatFormatter->getPhpVal(); + $this->assertIsFloat($phpVal); + $this->assertEquals(1234.56, $phpVal); + + $formatterService = new FormatterService('de_DE'); + /** @var FloatFormatter $floatFormatter */ + $floatFormatter = $formatterService->floatFromUserInput("1234,56"); + $phpVal = $floatFormatter->getPhpVal(); + $this->assertIsFloat($phpVal); + $this->assertEquals(1234.56, $phpVal); + + $formatterService = new FormatterService('de_DE'); + /** @var FloatFormatter $floatFormatter */ + $floatFormatter = $formatterService->floatFromUserInput("1.234.567,89"); + $phpVal = $floatFormatter->getPhpVal(); + $this->assertIsFloat($phpVal); + $this->assertEquals(1234567.89, $phpVal); + + $formatterService = new FormatterService('de_DE'); + /** @var FloatFormatter $floatFormatter */ + $floatFormatter = $formatterService->floatFromUserInput("1234567,89"); + $phpVal = $floatFormatter->getPhpVal(); + $this->assertIsFloat($phpVal); + $this->assertEquals(1234567.89, $phpVal); + + + $formatterService = new FormatterService('de_CH'); + /** @var FloatFormatter $floatFormatter */ + $floatFormatter = $formatterService->floatFromUserInput("1'234.56"); + $phpVal = $floatFormatter->getPhpVal(); + $this->assertIsFloat($phpVal); + $this->assertEquals(1234.56, $phpVal); + + $formatterService = new FormatterService('de_CH'); + /** @var FloatFormatter $floatFormatter */ + $floatFormatter = $formatterService->floatFromUserInput("1234.56"); + $phpVal = $floatFormatter->getPhpVal(); + $this->assertIsFloat($phpVal); + $this->assertEquals(1234.56, $phpVal); + + $formatterService = new FormatterService('de_CH'); + /** @var FloatFormatter $floatFormatter */ + $floatFormatter = $formatterService->floatFromUserInput("1'234'567.89"); + $phpVal = $floatFormatter->getPhpVal(); + $this->assertIsFloat($phpVal); + $this->assertEquals(1234567.89, $phpVal); + + $formatterService = new FormatterService('de_CH'); + /** @var FloatFormatter $floatFormatter */ + $floatFormatter = $formatterService->floatFromUserInput("1234567.89"); + $phpVal = $floatFormatter->getPhpVal(); + $this->assertIsFloat($phpVal); + $this->assertEquals(1234567.89, $phpVal); + + + $formatterService = new FormatterService('de_AT'); + /** @var FloatFormatter $floatFormatter */ + $floatFormatter = $formatterService->floatFromUserInput("1 234,56"); + $phpVal = $floatFormatter->getPhpVal(); + $this->assertIsFloat($phpVal); + $this->assertEquals(1234.56, $phpVal); + + $formatterService = new FormatterService('de_AT'); + /** @var FloatFormatter $floatFormatter */ + $floatFormatter = $formatterService->floatFromUserInput("1234,56"); + $phpVal = $floatFormatter->getPhpVal(); + $this->assertIsFloat($phpVal); + $this->assertEquals(1234.56, $phpVal); + + $formatterService = new FormatterService('de_AT'); + /** @var FloatFormatter $floatFormatter */ + $floatFormatter = $formatterService->floatFromUserInput("1 234 567,89"); + $phpVal = $floatFormatter->getPhpVal(); + $this->assertIsFloat($phpVal); + $this->assertEquals(1234567.89, $phpVal); + + $formatterService = new FormatterService('de_AT'); + /** @var FloatFormatter $floatFormatter */ + $floatFormatter = $formatterService->floatFromUserInput("1234567,89"); + $phpVal = $floatFormatter->getPhpVal(); + $this->assertIsFloat($phpVal); + $this->assertEquals(1234567.89, $phpVal); + } + + + + public function testFloatFromPhpVal() + { + $formatterService = new FormatterService('de_DE'); + /** @var FloatFormatter $floatFormatter */ + $floatFormatter = $formatterService->floatFromPhpVal(1234567.89); + $formattedString = $floatFormatter->formatForUser(); + $this->assertIsString($formattedString); + $this->assertEquals("1.234.567,89", $formattedString); + + + $formatterService = new FormatterService('de_CH'); + /** @var FloatFormatter $floatFormatter */ + $floatFormatter = $formatterService->floatFromPhpVal(1234567.89); + $formattedString = $floatFormatter->formatForUser(); + $this->assertIsString($formattedString); + $this->assertEquals("1’234’567.89", $formattedString); + + + $formatterService = new FormatterService('de_AT'); + /** @var FloatFormatter $floatFormatter */ + $floatFormatter = $formatterService->floatFromPhpVal(1234567.89); + $formattedString = $floatFormatter->formatForUser(); + $this->assertIsString($formattedString); + $this->assertEquals("1 234 567,89", $formattedString); + } +} diff --git a/classes/Components/I18n/Test/IntegerFormatterTest.php b/classes/Components/I18n/Test/IntegerFormatterTest.php new file mode 100644 index 00000000..16c27885 --- /dev/null +++ b/classes/Components/I18n/Test/IntegerFormatterTest.php @@ -0,0 +1,247 @@ + + * SPDX-License-Identifier: AGPL-3.0-only + */ + +declare(strict_types=1); + +namespace Xentral\Components\I18n\Test; + +require(__DIR__ . '/../../../bootstrap.php'); + +use Xentral\Components\I18n\Formatter\Exception\TypeErrorException; +use Xentral\Components\I18n\Formatter\FormatterMode; +use Xentral\Components\I18n\Formatter\IntegerFormatter; +use PHPUnit\Framework\TestCase; + +class IntegerFormatterTest extends TestCase +{ + public function testStrictModeWithValue() + { + $floatFormatter = new IntegerFormatter('de_DE', FormatterMode::MODE_STRICT); + $output = $floatFormatter->setPhpVal(floatval(1234.56))->formatForUser(); + var_dump($output); + $this->assertIsString($output); + $this->assertEquals('1.234', $output); + + $output = $floatFormatter->setPhpVal(intval(1234))->formatForUser(); + var_dump($output); + $this->assertIsString($output); + $this->assertEquals('1.234', $output); + + } + + public function testStrictModeWithValueInput() + { + $floatFormatter = new IntegerFormatter('de_DE', FormatterMode::MODE_STRICT); + $output = $floatFormatter->parseUserInput('1234,56')->getPhpVal(); + var_dump($output); + $this->assertIsInt($output); + $this->assertEquals(1234, $output); + + $output = $floatFormatter->parseUserInput('1234')->getPhpVal(); + var_dump($output); + $this->assertIsInt($output); + $this->assertEquals(1234, $output); + + } + + public function testStrictModeWithNull() + { + $floatFormatter = new IntegerFormatter('de_DE', FormatterMode::MODE_STRICT); + $this->expectException(TypeErrorException::class); + $output = $floatFormatter->setPhpVal(null)->formatForUser(); + } + public function testStrictModeWithNullInput() + { + $floatFormatter = new IntegerFormatter('de_DE', FormatterMode::MODE_STRICT); + $this->expectException(TypeErrorException::class); + $output = $floatFormatter->parseUserInput('')->getPhpVal(); + } + public function testStrictModeWithEmpty() + { + $floatFormatter = new IntegerFormatter('de_DE', FormatterMode::MODE_STRICT); + $this->expectException(TypeErrorException::class); + $output = $floatFormatter->setPhpVal('')->formatForUser(); + } + public function testStrictModeWithEmptyInput() + { + $floatFormatter = new IntegerFormatter('de_DE', FormatterMode::MODE_STRICT); + $this->expectException(TypeErrorException::class); + $output = $floatFormatter->parseUserInput('')->getPhpVal(); + } + public function testNullModeWithValue() + { + $floatFormatter = new IntegerFormatter('de_DE', FormatterMode::MODE_NULL); + $output = $floatFormatter->setPhpVal(floatval(1234.56))->formatForUser(); + var_dump($output); + $this->assertIsString($output); + $this->assertEquals('1.234', $output); + + $output = $floatFormatter->setPhpVal(intval(1234))->formatForUser(); + var_dump($output); + $this->assertIsString($output); + $this->assertEquals('1.234', $output); + } + + public function testNullModeWithValueInput() + { + $floatFormatter = new IntegerFormatter('de_DE', FormatterMode::MODE_NULL); + $output = $floatFormatter->parseUserInput('1234,56')->getPhpVal(); + var_dump($output); + $this->assertIsInt($output); + $this->assertEquals(1234, $output); + + $output = $floatFormatter->parseUserInput('1234')->getPhpVal(); + var_dump($output); + $this->assertIsInt($output); + $this->assertEquals(1234, $output); + } + + public function testNullModeWithNull() + { + $floatFormatter = new IntegerFormatter('de_DE', FormatterMode::MODE_NULL); + $output = $floatFormatter->setPhpVal(null)->formatForUser(); + var_dump($output); + $this->assertIsString($output); + $this->assertEquals('', $output); + } + public function testNullModeWithNullInput() + { + $floatFormatter = new IntegerFormatter('de_DE', FormatterMode::MODE_NULL); + $output = $floatFormatter->parseUserInput('')->getPhpVal(); + var_dump($output); + $this->assertNull($output); + } + public function testNullModeWithEmpty() + { + $floatFormatter = new IntegerFormatter('de_DE', FormatterMode::MODE_NULL); + $this->expectException(TypeErrorException::class); + $output = $floatFormatter->setPhpVal('')->formatForUser(); + } + public function testNullModeWithEmptyInput() + { + $floatFormatter = new IntegerFormatter('de_DE', FormatterMode::MODE_NULL); + $output = $floatFormatter->parseUserInput('')->getPhpVal(); + var_dump($output); + $this->assertNull($output); + } + public function testEmptyModeWithValue() + { + $floatFormatter = new IntegerFormatter('de_DE', FormatterMode::MODE_EMPTY); + $output = $floatFormatter->setPhpVal(1234.56)->formatForUser(); + var_dump($output); + $this->assertIsString($output); + $this->assertEquals('1.234', $output); + + $output = $floatFormatter->setPhpVal(1234)->formatForUser(); + var_dump($output); + $this->assertIsString($output); + $this->assertEquals('1.234', $output); + } + public function testEmptyModeWithValueInput() + { + $floatFormatter = new IntegerFormatter('de_DE', FormatterMode::MODE_EMPTY); + $output = $floatFormatter->parseUserInput('1234,56')->getPhpVal(); + var_dump($output); + $this->assertIsInt($output); + $this->assertEquals(1234, $output); + + $output = $floatFormatter->parseUserInput('1234')->getPhpVal(); + var_dump($output); + $this->assertIsInt($output); + $this->assertEquals(1234, $output); + } + public function testEmptyModeWithNull() + { + $floatFormatter = new IntegerFormatter('de_DE', FormatterMode::MODE_EMPTY); + $this->expectException(TypeErrorException::class); + $output = $floatFormatter->setPhpVal(null)->formatForUser(); + } + public function testEmptyModeWithNullInput() + { + $floatFormatter = new IntegerFormatter('de_DE', FormatterMode::MODE_EMPTY); + $output = $floatFormatter->parseUserInput('')->getPhpVal(); + var_dump($output); + $this->assertIsString($output); + $this->assertEquals('', $output); + } + public function testEmptyModeWithEmpty() + { + $floatFormatter = new IntegerFormatter('de_DE', FormatterMode::MODE_EMPTY); + $output = $floatFormatter->setPhpVal('')->formatForUser(); + var_dump($output); + $this->assertIsString($output); + $this->assertEquals('', $output); + } + public function testEmptyModeWithEmptyInput() + { + $floatFormatter = new IntegerFormatter('de_DE', FormatterMode::MODE_EMPTY); + $output = $floatFormatter->parseUserInput('')->getPhpVal(); + var_dump($output); + $this->assertIsString($output); + $this->assertEquals('', $output); + } + public function testCanParseOwnOutput() + { + $value = 1234567.89; + $locale = 'de_DE'; + $floatFormatter1 = new IntegerFormatter($locale); + $output = $floatFormatter1->setPhpVal($value)->formatForUser(); + var_dump($output); + $floatFormatter2 = new IntegerFormatter($locale); + $input = $floatFormatter2->parseUserInput($output)->getPhpVal(); + var_dump($input); + $this->assertEquals(intval($value), $input); + + $locale = 'de_CH'; + $floatFormatter1 = new IntegerFormatter($locale); + $output = $floatFormatter1->setPhpVal($value)->formatForUser(); + var_dump($output); + $floatFormatter2 = new IntegerFormatter($locale); + $input = $floatFormatter2->parseUserInput($output)->getPhpVal(); + var_dump($input); + $this->assertEquals(intval($value), $input); + + $locale = 'de_AT'; + $floatFormatter1 = new IntegerFormatter($locale); + $output = $floatFormatter1->setPhpVal($value)->formatForUser(); + var_dump($output); + $floatFormatter2 = new IntegerFormatter($locale); + $input = $floatFormatter2->parseUserInput($output)->getPhpVal(); + var_dump($input); + $this->assertEquals(intval($value), $input); + } + + public function testCanSetDigits() + { + $value = 1234567.8901234; + $locale = 'de_DE'; + $floatFormatter1 = new IntegerFormatter($locale); + $output = $floatFormatter1->setPhpVal($value)->setMinIntDigits(10)->setMaxDigits(4)->formatForUser(); + var_dump($output); + $this->assertEquals('0.001.234.567', $output); + + $floatFormatter1 = new IntegerFormatter($locale); + $output = $floatFormatter1->setPhpVal($value)->setMaxIntDigits(4)->formatForUser(); + var_dump($output); + $this->assertEquals('4.567', $output); + + $floatFormatter1 = new IntegerFormatter($locale); + $output = $floatFormatter1->setPhpVal($value)->setMaxDigits(3)->setMinDigits(3)->formatForUser(); + var_dump($output); + $this->assertEquals('1.234.567', $output); + + $floatFormatter1 = new IntegerFormatter($locale); + $output = $floatFormatter1->setPhpVal($value)->setMaxSignificantDigits(3)->formatForUser(); + var_dump($output); + $this->assertEquals('1.230.000', $output); + + $floatFormatter1 = new IntegerFormatter($locale); + $output = $floatFormatter1->setPhpVal($value)->setMinSignificantDigits(3)->formatForUser(); + var_dump($output); + $this->assertEquals('1.234.567', $output); + } + +} diff --git a/classes/Modules/FiskalyApi/Service/FiskalyTransferService.php b/classes/Modules/FiskalyApi/Service/FiskalyTransferService.php index 46d73541..df25c880 100644 --- a/classes/Modules/FiskalyApi/Service/FiskalyTransferService.php +++ b/classes/Modules/FiskalyApi/Service/FiskalyTransferService.php @@ -75,7 +75,7 @@ class FiskalyTransferService * @throws Exception * @return Transaction * - * @depracated + * @deprecated */ public function createTransactionFromSingleJournal( int $cashierId, @@ -270,7 +270,7 @@ class FiskalyTransferService * @throws Exception * @return Transaction * - * @depracated + * @deprecated */ public function transferPosSession($posSession): Transaction { diff --git a/phpwf/plugins/class.user.php b/phpwf/plugins/class.user.php index 29a4954e..ef19fc4b 100644 --- a/phpwf/plugins/class.user.php +++ b/phpwf/plugins/class.user.php @@ -474,9 +474,12 @@ class User return $this->cache[$cacheKey]['name']; } - + + + /** * @return array + * @deprecated Eine Liste der verfügbaren Sprachen sollte IMHO zentral im Übersetzer generiert werden. */ public function GetSprachen() { @@ -505,15 +508,20 @@ class User return $defaultLanguages; } - + + + /** - * @return string + * Die vom User gewählte Sprache zurückgeben. + * Null, falls die Sprache nicht erkannt wird. + * + * @return string|null */ - public function GetSprache() + public function GetSprache(): string|null { - $sprachen = $this->GetSprachen(); - - return reset($sprachen); + return \Xentral\Components\I18n\Bootstrap::findLanguage( + strval($this->GetField('sprachebevorzugen')) + )[Xentral\Components\I18n\Iso639\Key::DEFAULT]; } /** diff --git a/phpwf/plugins/class.yui.php b/phpwf/plugins/class.yui.php index c15023bf..de6edb5a 100644 --- a/phpwf/plugins/class.yui.php +++ b/phpwf/plugins/class.yui.php @@ -449,12 +449,6 @@ class YUI { break; case 2: // Betrag $value = $this->app->erp->FromFormatZahlToDB($value); - if(strpos($value,',')) - { - $value = str_replace(",", ".",str_replace('.','', $value)); - }else{ - $value = str_replace(",", ".", $value); - } $this->app->DB->Update("UPDATE $table SET betrag='$value' WHERE id='$id' LIMIT 1"); $result = $this->app->DB->Select("SELECT ".$this->FormatPreis('betrag')." FROM $table WHERE id='$id' LIMIT 1"); break; @@ -476,19 +470,12 @@ class YUI { case 2: // Menge $value = $this->app->erp->FromFormatZahlToDB($value); - $value = str_replace(",", ".", $value); $this->app->DB->Update("UPDATE $table SET menge='$value' WHERE id='$id' LIMIT 1"); $result = $this->app->DB->Select("SELECT ".$this->app->erp->FormatMenge("menge")." FROM $table WHERE id='$id' LIMIT 1"); break; case 3: // Betrag $value = $this->app->erp->FromFormatZahlToDB($value); - if(strpos($value,',')) - { - $value = str_replace(",", ".",str_replace('.','', $value)); - }else{ - $value = str_replace(",", ".", $value); - } $this->app->DB->Update("UPDATE $table SET betrag='$value' WHERE id='$id' LIMIT 1"); $result = $this->app->DB->Select("SELECT ".$this->FormatPreis('betrag')." FROM $table WHERE id='$id' LIMIT 1"); break; @@ -519,12 +506,6 @@ class YUI { break; case 4: // preis $value = $this->app->erp->FromFormatZahlToDB($value); - if(strpos($value,',')) - { - $value = str_replace(",", ".",str_replace('.','', $value)); - }else{ - $value = str_replace(",", ".", $value); - } $this->app->DB->Update("UPDATE $table SET preis='$value' WHERE id='$id' LIMIT 1"); $result = $this->app->DB->Select("SELECT ".$this->FormatPreis('preis')." FROM $table WHERE id='$id' LIMIT 1"); break; @@ -544,7 +525,6 @@ class YUI { break; case 4: // Menge $value = $this->app->erp->FromFormatZahlToDB($value); - $value = str_replace(',', '.', $value); if($value < 0 ) { $value=1; } @@ -650,12 +630,6 @@ class YUI { break; case 5: //preis $value = $this->app->erp->FromFormatZahlToDB($value); - if(strpos($value,',')) - { - $value = str_replace(",", ".",str_replace('.','', $value)); - }else{ - $value = str_replace(",", ".", $value); - } $join = ""; $preiscell = 'b.preis'; if($module == 'auftrag' || $module == 'rechnung' || $module == 'gutschrift' || $module == 'angebot' || $module == 'proformarechnung') @@ -718,12 +692,6 @@ class YUI { if($module == 'auftrag' || $module == 'rechnung' || $module == 'angebot' || $module == 'gutschrift' || $module == 'proformarechnung') { $value = $this->app->erp->FromFormatZahlToDB($value); - if(strpos($value,',')) - { - $value = str_replace(",", ".",str_replace('.','', $value)); - }else{ - $value = str_replace(",", ".", $value); - } if($value == '')$value = '0'; $this->app->DB->Update("UPDATE $table SET rabatt='$value',keinrabatterlaubt=1 WHERE id='$id' LIMIT 1"); $result = $this->app->DB->Select("SELECT ".$this->FormatPreis('rabatt')." FROM $table WHERE id='$id' LIMIT 1"); @@ -741,13 +709,6 @@ class YUI { if($module == 'auftrag' || $module == 'rechnung' || $module == 'angebot' || $module == 'gutschrift' ) { $value = $this->app->erp->FromFormatZahlToDB($value); - if(strpos($value,',')) - { - $value = str_replace(",", ".",str_replace('.','', $value)); - }else{ - $value = str_replace(",", ".", $value); - } - if($value == '')$value = '0'; $this->app->DB->Update("UPDATE $table SET einkaufspreis='$value' WHERE id='$id' LIMIT 1"); $result = $this->app->DB->Select("SELECT ".$this->FormatPreis('einkaufspreis')." FROM $table WHERE id='$id' LIMIT 1"); } @@ -1552,30 +1513,22 @@ class YUI { $module, $id, implode(', ', array_unique($positionsIds)) ) ); - + + /** @var \Xentral\Components\I18n\FormatterService $fs */ + $fs=$this->app->Container->get('FormatterService'); + if(!empty($positions)) { foreach($positions as $position) { $positionId = $position['id']; if(isset($idToPrice[$positionId]) && $position['preis'] != $idToPrice[$positionId] ) { - $price = rtrim(number_format($position['preis'], 8, ',', '.'), '0'); - $priceSplit = explode(',', $price); - if(strlen($priceSplit[(empty($priceSplit)?0:count($priceSplit))-1]) < 2) { - $price .= str_repeat('0',2-strlen($priceSplit[(empty($priceSplit)?0:count($priceSplit))-1])); - } - $ret[] = ['elid' => $arr[$positionId]['price_id'], 'value' => $price]; + $ret[] = ['elid' => $arr[$positionId]['price_id'], 'value' => $fs->formatPreis($position['preis'])]; } if(isset($idToQuantity[$positionId]) && $position['menge'] != $idToQuantity[$positionId] ) { - $quantity = rtrim(number_format($position['menge'], 8, ',', ''), '0'); - $quantitySplit = explode(',', $quantity); - if(isset($quantitySplit[1]) && $quantitySplit[1] === '') { - $quantity = $quantitySplit[0]; - } - - $ret[] = ['elid'=> $arr[$positionId]['quantity_id'], 'value' => $quantity]; + $ret[] = ['elid'=> $arr[$positionId]['quantity_id'], 'value' => $fs->formatMenge($position['menge'])]; } } } @@ -1781,9 +1734,7 @@ class YUI { $preis = $this->app->Secure->GetPOST("preis"); $preis = $this->app->erp->FromFormatZahlToDB($preis); $menge = $this->app->Secure->GetPOST("menge"); - //$menge = str_replace(',', '.', $menge); $menge= $this->app->erp->FromFormatZahlToDB($menge); - if($menge < 0) $menge = 1; $ort = $this->app->Secure->GetPOST("ort"); @@ -1791,8 +1742,7 @@ class YUI { $lieferdatum = $this->app->String->Convert($lieferdatum, "%1.%2.%3", "%3-%2-%1"); $datum = $this->app->Secure->GetPOST("datum"); $datum = $this->app->String->Convert($datum, "%1.%2.%3", "%3-%2-%1"); - $rabatt = $this->app->Secure->GetPOST("rabatt"); - $rabatt = str_replace(',', '.', $rabatt); + $rabatt = $this->app->erp->FromFormatZahlToDB($this->app->Secure->GetPOST("rabatt")); if($rabatt > 0 || $rabatt=="0") $keinrabatterlaubt=1; else $keinrabatterlaubt=0; if ($lieferdatum == "") $lieferdatum = "00.00.0000"; @@ -1970,8 +1920,7 @@ class YUI { $artikel = $this->app->Secure->GetPOST("artikel"); $stueckliste = $this->app->Secure->GetPOST("stueckliste"); $beschreibung = $this->app->Secure->GetPOST("beschreibung"); - $betrag = $this->app->Secure->GetPOST("betrag"); - $betrag = str_replace(',', '.', $betrag); + $betrag = $this->app->erp->FromFormatZahlToDB($this->app->Secure->GetPOST("betrag")); $kalkulationart = $this->app->Secure->GetPOST("kalkulationart"); //$projekt = $this->app->DB->Select("SELECT projekt FROM kalkulation WHERE mitarbeiternummer='$adresse' LIMIT 1"); @@ -2004,8 +1953,7 @@ class YUI { if ($module == "reisekosten") { $bezeichnung = $this->app->Secure->GetPOST("bezeichnung"); - $betrag = $this->app->Secure->GetPOST("betrag"); - $betrag = str_replace(',', '.', $betrag); + $betrag = $this->app->erp->FromFormatZahlToDB($this->app->Secure->GetPOST("betrag")); $reisekostenart = $this->app->Secure->GetPOST("reisekostenart"); $abrechnen = $this->app->Secure->GetPOST("abrechnen"); $keineust = $this->app->Secure->GetPOST("keineust"); @@ -2023,8 +1971,7 @@ class YUI { } else if ($module == "inventur" && $artikel_id > 0) { $bezeichnung = $this->app->Secure->GetPOST("artikel"); - $preis = $this->app->Secure->GetPOST("preis"); - $preis = str_replace(',', '.', $preis); + $preis = $this->app->erp->FromFormatZahlToDB($this->app->Secure->GetPOST("preis")); $nummer = $this->app->Secure->GetPOST("nummer"); /*adresse = $this->app->Secure->GetPOST("adresse"); @@ -2046,8 +1993,7 @@ class YUI { /* $bezeichnung = $this->app->Secure->GetPOST("artikel"); - $preis = $this->app->Secure->GetPOST("preis"); - $preis = str_replace(',','.',$preis); + $preis = $this->app->erp->FromFormatZahlToDB($this->app->Secure->GetPOST("preis")); $nummer = $this->app->Secure->GetPOST("nummer"); */ diff --git a/www/lib/class.erpapi.php b/www/lib/class.erpapi.php index e45f32a5..91a1d09d 100644 --- a/www/lib/class.erpapi.php +++ b/www/lib/class.erpapi.php @@ -1445,37 +1445,57 @@ public function NavigationHooks(&$menu) { return 'concat('.$spalte.",' ".$this->GetGewichtbezeichnung()."')"; } - - // @refactor DbHelper Komponente + + + + /** + * Erstelle die lokalisierten Formatierungsanweisungen für das SQL-Query. + * + * @deprecated Es wäre besser, die Formatierung in PHP zu machen + */ function FormatPreis($spalte, $stellen = null, $punkt = false) { - if(is_null($stellen))return "if(trim(round( $spalte *100))+0 <> trim($spalte*100)+0, format($spalte, length( trim($spalte)+0)-length(round($spalte))-1 ".($punkt?"":" ,'de_DE'")."),format($spalte,2".($punkt?"":" ,'de_DE'")."))"; - return "format($spalte,$stellen".($punkt?"":" ,'de_DE'").")"; - } + if (is_null($stellen)) { + return "if(trim(round( $spalte *100))+0 <> trim($spalte*100)+0, format($spalte, length( trim($spalte)+0)-length(round($spalte))-1 " . ($punkt ? "" : " ,'de_DE'") . "),format($spalte,2" . ($punkt ? "" : " ,'de_DE'") . "))"; + } + return "format($spalte,$stellen" . ($punkt ? "" : " ,'de_DE'") . ")"; + // Wenn die Zahlen umformatiert werden, funktioniert in den Tabellen die in Javascript implementierte Summierung nicht mehr! + /** @var \Xentral\Components\I18n\FormatterService $fs */ + $fs = $this->app->Container->get('FormatterService'); + $currencyFormatter = new \Xentral\Components\I18n\Formatter\CurrencyFormatter( + $punkt ? 'en_US' : $fs->getLocale(), + \Xentral\Components\I18n\Formatter\FormatterMode::MODE_NULL + ); + if ($punkt) { + $currencyFormatter->hideGrouping(); + } + $currencyFormatter->hideCurrency(); + if ($stellen !== null) { + $currencyFormatter->setMinDigits($stellen); + } + return $currencyFormatter->formatForUserWithSqlStatement($spalte); +// if(is_null($stellen))return "if(trim(round( $spalte *100))+0 <> trim($spalte*100)+0, format($spalte, length( trim($spalte)+0)-length(round($spalte))-1 ".($punkt?"":" ,'de_DE'")."),format($spalte,2".($punkt?"":" ,'de_DE'")."))"; +// return "format($spalte,$stellen".($punkt?"":" ,'de_DE'").")"; + } + + + /** - * @deprecated + * Wandelt eine Formatierte Zahl in das Format für die Datenbank um. * * @param $value * * @return mixed + * @deprecated + * */ function FromFormatZahlToDB($value) { - $poskomma = strrpos($value, ','); - $pospunkt = strrpos($value, '.'); - if($poskomma === false) { - return $value; - } - if($pospunkt === false) { - return str_replace(',','.', $value); - } - - if($poskomma > $pospunkt) { - return str_replace(['.', ','], ['', '.'], $value); - } - - return str_replace(',','', $value); + /** @var \Xentral\Components\I18n\FormatterService $fs */ + $fs = $this->app->Container->get('FormatterService'); + $number = $fs->floatFromUserInput(strval($value)); + return $number->getPhpVal(); } // @refactor Formater Komponente @@ -1508,13 +1528,27 @@ public function NavigationHooks(&$menu) } return $prefix.'.'.'belegnr'; } - - // @refactor DbHelper Komponente + + + + /** + * Erstelle die lokalisierten Formatierungsanweisungen für das SQL-Query. + * Wird and folgenden Stellen benutzt: + * -\Artikel::TableSearch() + * + * @param $spalte + * @param $decimals + * + * @return mixed + * @deprecated Es wäre besser, die Formatierung in PHP zu machen + * + */ function FormatMenge($spalte, $decimals = 0) { - return ('FORMAT('.$spalte.','.$decimals.',\'de_DE\')'); - -// return "replace(trim($spalte)+0,'.',',')"; + /** @var \Xentral\Components\I18n\FormatterService $fn */ + $fs = $this->app->Container->get('FormatterService'); + $number = $fs->factory(\Xentral\Components\I18n\Formatter\FloatFormatter::class); + return $number->setMinDigits($decimals)->formatForUserWithSqlStatement($spalte); } function FormatUCfirst($spalte) @@ -12769,12 +12803,21 @@ function SendPaypalFromAuftrag($auftrag, $test = false) return $obj->LieferscheinCheck($lieferschein); } } - - - // @refactor Formater Komponente + + + + /** + * Formatiert den $betrag als Währungs-Ausgabe-String. + * + * @param $betrag + * + * @return string + */ public function EUR($betrag) { - return number_format($betrag,2,',','.'); + /** @var \Xentral\Components\I18n\FormatterService $fs */ + $fs = $this->app->Container->get('FormatterService'); + return $fs->formatPreis($betrag); } // @refactor Adresse Modul @@ -12795,36 +12838,30 @@ function SendPaypalFromAuftrag($auftrag, $test = false) return $kreditlimit >= ($rechnungen+$auftraege); } - - // @refactor FormHelper Komponente - public function ReplaceBetrag($db,$value,$fromform = null) + + + + /** + * Formatiert bzw. parst einen Geldbetrag für die Ausgabe im Formular oder die Speicherung in der DB. + * + * @param $db Richtung (true=Speichern in DB, false=Ausgabe im Formular) + * @param $value + * @param $fromform + * + * @return float|mixed|string + */ + public function ReplaceBetrag($db, $value, $fromform = null) { + /** @var \Xentral\Components\I18n\FormatterService $fs */ + $fs = $this->app->Container->get('FormatterService'); + // wenn ziel datenbank - if($db) - { - // wenn . und , vorhanden dann entferne punkt - $pos_punkt = strrpos($value, '.'); - $pos_komma = strrpos($value, ','); - if(($pos_punkt !== false) && ($pos_komma !== false)){ - if($pos_punkt < $pos_komma){ - $value = str_replace('.', '', $value); - }else{ - $value = str_replace(',', '', $value); - } - } - return str_replace(',','.',$value); + if ($db) { + return $fs->parsePreis($value); } + // wenn ziel formular - - if ($value != "") { - //return $abkuerzung; - - if($value == round((float) $value, 2)) { - return number_format((float)$value,2,',',''); - } - } - - return rtrim(str_replace('.',',',$value),'0'); + return $fs->formatPreis($value); } // @refactor FormHelper Komponente @@ -13052,83 +13089,63 @@ function SendPaypalFromAuftrag($auftrag, $test = false) } return $value; } - - // @refactor FormHelper Komponente - function ReplaceMenge($db,$value,$fromform) + + + + /** + * Formatiert bzw. parst eine Menge für die Ausgabe im Formular oder die Speicherung in der DB. + * + * @param $db Richtung (true=Speichern in DB, false=Ausgabe im Formular) + * @param $value + * @param $fromform + * + * @return float|mixed|string + */ + function ReplaceMenge($db, $value, $fromform) { - $tcheck = str_replace(',','.',$value); - if($tcheck < 0) { - return 1; - } - - - $dbformat = 0; - if(strpos($value,'.') > 0) { - $dbformat = 1; - } - + /** @var \Xentral\Components\I18n\FormatterService $fs */ + $fs = $this->app->Container->get('FormatterService'); + // wenn ziel datenbank - if($db) - { - if($dbformat) { - return $value; - } - - if($value!=''){ - return str_replace(',', '.', $value); - } - - return ''; + if ($db) { + return $fs->parseMenge($value); } + // wenn ziel formular - if(strpos($value,'.') !== false) - { - $value = rtrim(rtrim($value,'0'),'.'); - if($value[0] === '.') { - $value = '0'.$value; - } - } - if($dbformat) { - if($value!='') { - return str_replace('.',',',$value); - } - return ''; - } - - return $value; + return $fs->formatMenge($value); } - - // @refactor FormHelper Komponente - function ReplaceDecimal($db,$value,$fromform) + + + + /** + * Formatiert bzw. parst eine Zahl für die Ausgabe im Formular oder die Speicherung in der DB. + * + * @param $db Richtung (true=Speichern in DB, false=Ausgabe im Formular) + * @param $value + * @param $fromform + * + * @return array|mixed|string|string[] + */ + function ReplaceDecimal($db, $value, $fromform) { - //value muss hier vom format ueberprueft werden - $dbformat = 0; - if(strpos($value,'.') > 0) { - $dbformat = 1; - } - + /** @var \Xentral\Components\I18n\FormatterService $fs */ + $fs = $this->app->Container->get('FormatterService'); + // wenn ziel datenbank - if($db) - { - if($dbformat) { - return $value; - } - - if($value!=''){ - return str_replace(',', '.', $value); - } - return ''; + if ($db) { + $floatFormatter = $fs->floatFromUserInput(strval($value)); + return $floatFormatter->getPhpVal(); } + // wenn ziel formular - - if($dbformat) { - if($value!='') { - return $value; - } - return ''; + if (is_numeric($value)) { + $floatFormatter = $fs->floatFromPhpVal(floatval($value)); + return $floatFormatter->formatForUser(); } - - return $value; + + // Soweit sollte es gar nicht kommen + // TODO: soll hier eine Exception geworfen werden? + return null; } @@ -24944,7 +24961,7 @@ function ChargenMHDAuslagern($artikel, $menge, $lagerplatztyp, $lpid,$typ,$wert, } $ansprechpartner = str_replace('<','<',$ansprechpartner); $ansprechpartner = str_replace('>','>',$ansprechpartner); - list($name, $email) = explode('<', trim($ansprechpartner,'>')); + [$name, $email] = explode('<', trim($ansprechpartner,'>')); $betreff = str_replace('\"','"',$betreff); $betreff = str_replace("\'","'",$betreff); diff --git a/www/pages/artikel.php b/www/pages/artikel.php index 680d1175..6e130efc 100644 --- a/www/pages/artikel.php +++ b/www/pages/artikel.php @@ -22,6 +22,7 @@ include '_gen/artikel.php'; class Artikel extends GenArtikel { /** @var Application $app */ var $app; + protected \Xentral\Components\I18n\FormatterService $formatterService; const MODULE_NAME = 'Article'; public function TableSearch($app, $name, $erlaubtevars) @@ -938,10 +939,10 @@ class Artikel extends GenArtikel { IF(s.art='it','
- Informationsteil/Dienstleistung',''),IF(s.art='bt','
- Beistellung',''), COALESCE((SELECT GROUP_CONCAT('
- ', art.nummer, ' ', art.name_de, ' (', alt.reason, ')', '' SEPARATOR '') FROM parts_list_alternative AS alt INNER JOIN artikel AS art ON art.id = alt.alternative_article_id WHERE alt.parts_list_id = s.id), '')) as artikel, CONCAT('',a.nummer,'') as nummer, s.referenz, - trim(s.menge)+0 as menge, a.einheit, - ".$this->app->erp->FormatMenge('ifnull(lag.menge,0)').' as lager, + {$this->app->erp->FormatMenge('s.menge')} as menge, a.einheit, + {$this->app->erp->FormatMenge('ifnull(lag.menge,0)')} as lager, CASE WHEN (SELECT SUM(lr.menge) FROM lager_reserviert lr WHERE lr.artikel=a.id) > 0 - THEN (SELECT '.$this->app->erp->FormatMenge('SUM(lr.menge)')." FROM lager_reserviert lr WHERE lr.artikel=a.id) + THEN (SELECT {$this->app->erp->FormatMenge('SUM(lr.menge)')} FROM lager_reserviert lr WHERE lr.artikel=a.id) ELSE 0 END as reserviert, s.id as menu @@ -952,7 +953,7 @@ class Artikel extends GenArtikel { INNER JOIN ( SELECT artikel FROM stueckliste - WHERE stuecklistevonartikel='$id' GROUP BY artikel + WHERE stuecklistevonartikel='{$id}' GROUP BY artikel ) AS s2 ON lpi.artikel = s2.artikel INNER JOIN lager_platz AS lp ON lpi.lager_platz = lp.id AND ifnull(lp.sperrlager,0) = 0 GROUP BY lpi.artikel) AS lag ON a.id = lag.artikel @@ -1871,6 +1872,9 @@ class Artikel extends GenArtikel { public function __construct($app, $intern = false) { //parent::GenArtikel($app); $this->app=$app; + + $this->formatterService = $this->app->Container->get('FormatterService'); + if($intern){ return; } @@ -6003,7 +6007,8 @@ class Artikel extends GenArtikel { $name = 'node'.$k; $$name = new stdClass(); $$name->id = $v['id']; - $$name->label = ' '.$v['menge'].' x '.$v['nummer'].' '.(strlen($v['name_de']) < 30?$v['name_de']:(mb_substr($v['name_de'],0,27).'...')).''; + $$name->label = " {$this->formatterService->formatMenge($v['menge'])} x " + . ''.$v['nummer'].' '.(strlen($v['name_de']) < 30?$v['name_de']:(mb_substr($v['name_de'],0,27).'...')).''; $$name->checkbox = false; $$name->inode = false; $$name->radio = false; @@ -6035,7 +6040,8 @@ class Artikel extends GenArtikel { $name = 'node'.$k; $$name = new stdClass(); $$name->id = $v['id']; - $$name->label = ' '.$v['menge'].' x '.$v['nummer'].' '.(strlen($v['name_de']) < 30?$v['name_de']:(mb_substr($v['name_de'],0,27).'...')).''; + $$name->label = " {$this->formatterService->formatMenge($v['menge'])} x " + . ''.$v['nummer'].' '.(strlen($v['name_de']) < 30?$v['name_de']:(mb_substr($v['name_de'],0,27).'...')).''; $$name->checkbox = false; $$name->inode = false; $$name->radio = false; @@ -6321,7 +6327,7 @@ class Artikel extends GenArtikel { $id = (int)$this->app->Secure->GetPOST('id'); - $data = $this->app->DB->SelectRow("SELECT s.id, s.artikel, trim(s.menge)+0 as menge, s.art, s.referenz, s.layer, s.place, s.wert, s.bauform, s.zachse, s.xpos, s.ypos FROM stueckliste s WHERE s.id = '$id' LIMIT 1"); + $data = $this->app->DB->SelectRow("SELECT s.id, s.artikel, s.menge, s.art, s.referenz, s.layer, s.place, s.wert, s.bauform, s.zachse, s.xpos, s.ypos FROM stueckliste s WHERE s.id = '{$id}' LIMIT 1"); if($data){ if($data['artikel'] == 0){ @@ -6349,7 +6355,8 @@ class Artikel extends GenArtikel { } } }*/ - + + $data['menge'] = $this->formatterService->formatMenge($data['menge']); }else{ $data['id'] = 0; @@ -6378,7 +6385,7 @@ class Artikel extends GenArtikel { $id = (int)$this->app->Secure->GetPOST('eid'); $startikelid = (int)$this->app->Secure->GetPOST('estartikelid'); $artikel = trim($this->app->Secure->GetPOST('eartikel')); - $menge = str_replace(',','.',trim($this->app->Secure->GetPOST('emenge'))); + $menge = $this->formatterService->parseMenge($this->app->Secure->GetPOST('emenge')); $art = trim($this->app->Secure->GetPOST('eart')); //$alternative = trim($this->app->Secure->GetPOST('ealternative')); $referenz = trim($this->app->Secure->GetPOST('ereferenz')); diff --git a/www/pages/firmendaten.php b/www/pages/firmendaten.php index 6d9acea9..86f316e5 100644 --- a/www/pages/firmendaten.php +++ b/www/pages/firmendaten.php @@ -13,6 +13,9 @@ */ ?> sprachebevorzugen speichern - * @param $language + * Sprache in firmendaten_werte speichern, wenn sie geändert wurde. + * Falls der Wert in der Tabelle noch nicht existiert, wird er angelegt. + * + * @param string $newLanguage Sprache (wird in ISO 639 Alpha-3 Code umgewandelt) */ - private function savePreferredLanguage($language){ - -/* THIS NEEDS TO BE REIMPLEMENTED - $lang = $this->app->erp->Firmendaten('preferredLanguage'); - if($lang != $language){ - $this->app->erp->FirmendatenSet('preferredLanguage',$language); - $this->app->DB->Update("UPDATE `user` SET sprachebevorzugen = '".$language."'"); + private function savePreferredLanguage(string $newLanguage): void + { + $newLanguage = \Xentral\Components\I18n\Bootstrap::findLanguage( + $newLanguage + )[Xentral\Components\I18n\Iso639\Key::DEFAULT] ?? $this->getPreferredLanguage(); + + if ($newLanguage != ($oldLanguage = $this->app->erp->Firmendaten('preferredLanguage'))) { + if ($oldLanguage) { + $this->app->erp->FirmendatenSet('preferredLanguage', $newLanguage); + } else { + // Workaround, weil \erpAPI::AddNeuenFirmendatenWert() nicht funktioniert + /** @var Database $db */ + $db = $this->app->Container->get('Database'); + $insert= $db->insert()->into('firmendaten_werte')->cols( + [ + 'name' => 'preferredLanguage', + 'typ' => 'varchar', + 'typ1' => '64', + 'typ2' => '', + 'wert' => $newLanguage, + 'default_value' => $this->getPreferredLanguage(), + 'default_null' => 0, + 'darf_null' => 0, + ] + ); + $db->perform($insert->getStatement(), $insert->getBindValues()); + /* + $this->app->erp->AddNeuenFirmendatenWert( + 'preferredLanguage', + 'varchar', + '64', + '', + $newLanguage, + $this->getPreferredLanguage(), + 0, + 0 + ); + */ + } } -*/ } - + + + /** - * Bevorzugte Sprache aus firmendaten_werte - * @return string Sprache + * Bevorzugte Sprache aus firmendaten_werte zurückgeben. + * Wenn die Sprache nicht gesetzt ist, die Default-Sprache zurückgeben. + * + * @return string Sprache als ISO 639 Alpha-3 Code */ - private function getPreferredLanguage(){ - - $lang = $this->app->erp->Firmendaten('preferredLanguage'); - if(!empty($lang)) { - return $lang; - } - -// $this->app->erp->AddNeuenFirmendatenWert('preferredLanguage','varchar','64','','deutsch','deutsch',0,0); - return 'deutsch'; + private function getPreferredLanguage(): string + { + /** @var \Xentral\Components\I18n\Localization $localization */ + $localization = $this->app->Container->get('Localization'); + return \Xentral\Components\I18n\Bootstrap::findLanguage( + $this->app->erp->Firmendaten('preferredLanguage') ?? $localization->getLanguage() + )[Xentral\Components\I18n\Iso639\Key::DEFAULT]; } - + + + /** - * Liefert einen String aus HTML-Optionen zurück - * @param string $select Wert aus der Datenbank + * Liefert einen String aus HTML-Optionen zurück. + * + * @param string $select Gewählter Wert + * * @return string */ - private function languageSelectOptions($select='deutsch'){ - + private function languageSelectOptions(string $select) + { + $select=\Xentral\Components\I18n\Bootstrap::findLanguage($select)[Xentral\Components\I18n\Iso639\Key::DEFAULT]; $out = ''; - $sprachen = $this->getLanguages(); - - foreach($sprachen as $sprache) { - $selected = (($select==$sprache) ? 'selected' : ''); - $out .= ""; + foreach ($this->getLanguages() as $sprache) { + if($language=\Xentral\Components\I18n\Bootstrap::findLanguage($sprache)) { + $selected = (($select == $language[Xentral\Components\I18n\Iso639\Key::DEFAULT]) ? ' selected="selected"' : ''); + $out .= ""; + } } return $out; } - + + + /** - * Liefert einen Array aus Strings zurück. Immer mindestens 'deutsch' enthalten + * Liefert einen Array aus Strings zurück. Immer mindestens 'deutsch' enthalten. + * * @return array + * @todo Sollte eventuell zusammengelegt oder in den Übersetzer verschoben werden. + * @see \Firmendaten::getLanguages() + * @deprecated */ private function getLanguages(){ diff --git a/www/pages/welcome.php b/www/pages/welcome.php index 9c1d91ed..5d8dbd2a 100644 --- a/www/pages/welcome.php +++ b/www/pages/welcome.php @@ -2819,7 +2819,19 @@ $this->app->Tpl->Add('TODOFORUSER',"".$tmp[$i]['aufgabe']. header('Location: '.$_SERVER['HTTP_REFERER']); exit; } - + + + + /** + * Diese Funktion holt die Sprache von den Firmendaten, falls keine Sprache in $fromPost übergeben wurde. + * Sie wurde nur verwendet, um eine Vorgabe im sprachebevorzugen-SELECT beim Benutzer zu erzeugen. + * Das wird jetzt anders gelöst. + * + * @param $fromPost + * + * @return array|mixed|string|null + * @deprecated Nicht mehr benötigt + */ private function getCurrentDefaultLanguage($fromPost){ if(empty($fromPost)){ @@ -2831,29 +2843,43 @@ $this->app->Tpl->Add('TODOFORUSER',"".$tmp[$i]['aufgabe']. } return $fromPost; } - + + + /** - * Liefert einen String aus HTML-Optionen zurück - * @param string $fromPost + * Liefert einen String aus HTML-Select-Optionen zurück für die Sprachauswahl beim Benutzer. + * + * @param string|null $userPreferredLanguage Gewählte Sprache von den Usereinstellungen + * * @return string */ - private function languageSelectOptions($fromPost=''){ - - $select = $this->getCurrentDefaultLanguage($fromPost); - - $out = ""; - $sprachen = $this->getLanguages(); - - foreach($sprachen as $sprache) { - $selected = (($select==$sprache) ? 'selected' : ''); - $out .= ""; + private function languageSelectOptions(string|null $userPreferredLanguage='') { + $select=\Xentral\Components\I18n\Bootstrap::findLanguage(strval($userPreferredLanguage))[Xentral\Components\I18n\Iso639\Key::DEFAULT]; + if(empty($select)) { + /** @var \Xentral\Components\I18n\Localization $localization */ + $localization=$this->app->Container->get('Localization'); + $select=$localization->getLanguage(); + } + $out = ''; + foreach ($this->getLanguages() as $sprache) { + if($language=\Xentral\Components\I18n\Bootstrap::findLanguage($sprache)) { + $selected = (($select == $language[Xentral\Components\I18n\Iso639\Key::DEFAULT]) ? ' selected="selected"' : ''); + $out .= ""; + } } return $out; + } - + + + /** - * Liefert einen Array aus Strings zurück. Immer mindestens 'deutsch' enthalten + * Liefert einen Array aus Strings zurück. Immer mindestens 'deutsch' enthalten. + * * @return array + * @todo Sollte eventuell zusammengelegt oder in den Übersetzer verschoben werden. + * @see \Firmendaten::getLanguages() + * @deprecated */ private function getLanguages(){ diff --git a/www/widgets/_gen/widget.gen.adresse.php b/www/widgets/_gen/widget.gen.adresse.php index d1b78512..a4b97bd1 100644 --- a/www/widgets/_gen/widget.gen.adresse.php +++ b/www/widgets/_gen/widget.gen.adresse.php @@ -20,10 +20,12 @@ class WidgetGenadresse private $app; //application object public $form; //store form object protected $parsetarget; //target for content - + protected \Xentral\Components\I18n\FormatterService $formatterService; + public function __construct($app,$parsetarget) { $this->app = $app; + $this->formatterService = $this->app->Container->get('FormatterService'); $this->parsetarget = $parsetarget; $this->Form(); } diff --git a/www/widgets/widget.adresse.php b/www/widgets/widget.adresse.php index 32e9018b..783e7eed 100644 --- a/www/widgets/widget.adresse.php +++ b/www/widgets/widget.adresse.php @@ -28,17 +28,21 @@ class WidgetAdresse extends WidgetGenAdresse $this->form->ReplaceFunction("mandatsreferenzdatum",$this,"ReplaceDatum"); $this->form->ReplaceFunction("liefersperredatum",$this,"ReplaceDatum"); - $this->form->ReplaceFunction("arbeitszeitprowoche",$this,"ReplaceBetrag"); - $this->form->ReplaceFunction("zahlungszielskonto",$this,"ReplaceBetrag"); - $this->form->ReplaceFunction("zahlungszielskontolieferant",$this,"ReplaceBetrag"); - $this->form->ReplaceFunction("provision",$this,"ReplaceBetrag"); - $this->form->ReplaceFunction("portofreiab",$this,"ReplaceBetrag"); - $this->form->ReplaceFunction("portofreiablieferant",$this,"ReplaceBetrag"); - $this->form->ReplaceFunction("kreditlimit",$this,"ReplaceBetrag"); - $this->form->ReplaceFunction("kreditlimiteinmalig",$this,"ReplaceBetrag"); + $this->form->ReplaceFunction("arbeitszeitprowoche", $this->formatterService, "replaceDecimalOrEmpty"); + $this->form->ReplaceFunction("zahlungszieltage", $this->formatterService, "replaceIntegerOrEmpty"); + $this->form->ReplaceFunction("zahlungszieltagelieferant", $this->formatterService, "replaceIntegerOrEmpty"); + $this->form->ReplaceFunction("zahlungszieltageskonto", $this->formatterService, "replaceDecimalOrEmpty"); + $this->form->ReplaceFunction("zahlungszieltageskontolieferant", $this->formatterService, "replaceDecimalOrEmpty"); + $this->form->ReplaceFunction("zahlungszielskonto", $this->formatterService, "replaceDecimalPercentOrEmpty"); + $this->form->ReplaceFunction("zahlungszielskontolieferant", $this->formatterService, "replaceDecimalPercentOrEmpty"); + $this->form->ReplaceFunction("provision", $this->formatterService, "replaceDecimalPercentOrEmpty"); + $this->form->ReplaceFunction("portofreiab", $this->formatterService, "replaceCurrencyOrEmpty"); + $this->form->ReplaceFunction("portofreiablieferant", $this->formatterService, "replaceCurrencyOrEmpty"); + $this->form->ReplaceFunction("kreditlimit", $this->formatterService, "replaceCurrencyOrEmpty"); + $this->form->ReplaceFunction("kreditlimiteinmalig", $this->formatterService, "replaceCurrencyOrEmpty"); - $this->form->ReplaceFunction("lat",$this,"ReplaceBetrag"); - $this->form->ReplaceFunction("lng",$this,"ReplaceBetrag"); + $this->form->ReplaceFunction("lat", $this->formatterService, "replaceDecimalGeoOrEmpty"); + $this->form->ReplaceFunction("lng", $this->formatterService, "replaceDecimalGeoOrEmpty"); $this->form->ReplaceFunction("name",$this,"ReplaceTrim"); $this->app->YUI->CkEditor("sonstiges","internal");