2019-08-28 04:36:53 +02:00
|
|
|
<?php
|
|
|
|
/**
|
2020-09-06 11:38:10 +02:00
|
|
|
* Invoice Ninja (https://invoiceninja.com).
|
2019-08-28 04:36:53 +02:00
|
|
|
*
|
|
|
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
|
|
|
*
|
2024-04-12 06:15:41 +02:00
|
|
|
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
2019-08-28 04:36:53 +02:00
|
|
|
*
|
2021-06-16 08:58:16 +02:00
|
|
|
* @license https://www.elastic.co/licensing/elastic-license
|
2019-08-28 04:36:53 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
namespace App\Utils;
|
|
|
|
|
2021-03-17 16:12:25 +01:00
|
|
|
use App\Models\Company;
|
2019-10-07 12:21:02 +02:00
|
|
|
use App\Models\Currency;
|
2019-08-28 04:36:53 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Class Number.
|
|
|
|
*/
|
|
|
|
class Number
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* @param float $value
|
|
|
|
* @param int $precision
|
|
|
|
*
|
|
|
|
* @return float
|
|
|
|
*/
|
2024-01-14 05:05:00 +01:00
|
|
|
public static function roundValue(float $value, int $precision = 2): float
|
2019-08-28 04:36:53 +02:00
|
|
|
{
|
|
|
|
return round($value, $precision, PHP_ROUND_HALF_UP);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-09-06 11:38:10 +02:00
|
|
|
* Formats a given value based on the clients currency.
|
2019-12-30 22:59:12 +01:00
|
|
|
*
|
2020-08-26 02:47:50 +02:00
|
|
|
* @param float $value The number to be formatted
|
2019-08-28 04:36:53 +02:00
|
|
|
* @param object $currency The client currency object
|
2019-12-30 22:59:12 +01:00
|
|
|
*
|
2020-08-26 02:47:50 +02:00
|
|
|
* @return string The formatted value
|
2019-08-28 04:36:53 +02:00
|
|
|
*/
|
2024-01-14 05:05:00 +01:00
|
|
|
public static function formatValue($value, $currency): string
|
2019-08-28 04:36:53 +02:00
|
|
|
{
|
|
|
|
$value = floatval($value);
|
|
|
|
|
|
|
|
$thousand = $currency->thousand_separator;
|
|
|
|
$decimal = $currency->decimal_separator;
|
|
|
|
$precision = $currency->precision;
|
|
|
|
|
|
|
|
return number_format($value, $precision, $decimal, $thousand);
|
|
|
|
}
|
|
|
|
|
2022-02-13 08:54:33 +01:00
|
|
|
/**
|
|
|
|
* Formats a given value based on the clients currency.
|
|
|
|
*
|
|
|
|
* @param float $value The number to be formatted
|
|
|
|
*
|
|
|
|
* @return string The formatted value
|
|
|
|
*/
|
2024-01-14 05:05:00 +01:00
|
|
|
public static function formatValueNoTrailingZeroes($value, $entity): string
|
2022-02-13 08:54:33 +01:00
|
|
|
{
|
|
|
|
$value = floatval($value);
|
|
|
|
|
2022-10-19 00:17:32 +02:00
|
|
|
$currency = $entity->currency();
|
|
|
|
|
2022-02-13 08:54:33 +01:00
|
|
|
$thousand = $currency->thousand_separator;
|
|
|
|
$decimal = $currency->decimal_separator;
|
2022-10-19 00:17:32 +02:00
|
|
|
// $precision = $currency->precision;
|
|
|
|
|
|
|
|
if ($entity instanceof Company) {
|
|
|
|
$country = $entity->country();
|
|
|
|
} else {
|
|
|
|
$country = $entity->country;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Country settings override client settings */
|
|
|
|
if (isset($country->thousand_separator) && strlen($country->thousand_separator) >= 1) {
|
|
|
|
$thousand = $country->thousand_separator;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isset($country->decimal_separator) && strlen($country->decimal_separator) >= 1) {
|
|
|
|
$decimal = $country->decimal_separator;
|
|
|
|
}
|
2022-02-13 08:54:33 +01:00
|
|
|
|
2022-02-26 04:04:05 +01:00
|
|
|
$precision = 10;
|
|
|
|
|
2022-06-21 11:57:17 +02:00
|
|
|
return rtrim(rtrim(number_format($value, $precision, $decimal, $thousand), '0'), $decimal);
|
2022-02-13 08:54:33 +01:00
|
|
|
}
|
|
|
|
|
2024-03-14 04:44:01 +01:00
|
|
|
public static function parseFloat($value)
|
|
|
|
{
|
|
|
|
|
2024-06-14 09:09:44 +02:00
|
|
|
if(!$value) {
|
2024-03-14 04:44:01 +01:00
|
|
|
return 0;
|
2024-06-14 09:09:44 +02:00
|
|
|
}
|
2024-03-14 04:44:01 +01:00
|
|
|
|
|
|
|
//remove everything except for numbers, decimals, commas and hyphens
|
|
|
|
$value = preg_replace('/[^0-9.,-]+/', '', $value);
|
|
|
|
|
|
|
|
$decimal = strpos($value, '.');
|
|
|
|
$comma = strpos($value, ',');
|
|
|
|
|
2024-06-14 09:09:44 +02:00
|
|
|
if($comma === false) { //no comma must be a decimal number already
|
2024-03-14 04:44:01 +01:00
|
|
|
return (float) $value;
|
2024-06-14 09:09:44 +02:00
|
|
|
}
|
2024-03-14 04:44:01 +01:00
|
|
|
|
2024-06-14 09:09:44 +02:00
|
|
|
if(!$decimal && substr($value, -3, 1) != ",") {
|
2024-04-14 23:57:24 +02:00
|
|
|
$value = $value.".00";
|
|
|
|
}
|
2024-06-14 09:09:44 +02:00
|
|
|
|
2024-04-14 23:57:24 +02:00
|
|
|
$decimal = strpos($value, '.');
|
|
|
|
|
2024-06-14 09:09:44 +02:00
|
|
|
if($decimal < $comma) { //decimal before a comma = euro
|
2024-03-14 04:44:01 +01:00
|
|
|
$value = str_replace(['.',','], ['','.'], $value);
|
|
|
|
return (float) $value;
|
|
|
|
}
|
|
|
|
|
|
|
|
//comma first = traditional thousand separator
|
|
|
|
$value = str_replace(',', '', $value);
|
|
|
|
|
|
|
|
return (float)$value;
|
|
|
|
|
|
|
|
|
|
|
|
}
|
2024-06-14 09:09:44 +02:00
|
|
|
|
2020-08-26 02:47:50 +02:00
|
|
|
/**
|
|
|
|
* Formats a given value based on the clients currency
|
2020-09-06 11:38:10 +02:00
|
|
|
* BACK to a float.
|
2020-08-26 02:47:50 +02:00
|
|
|
*
|
2020-10-28 11:10:49 +01:00
|
|
|
* @param string $value The formatted number to be converted back to float
|
2020-08-26 02:47:50 +02:00
|
|
|
* @return float The formatted value
|
|
|
|
*/
|
2024-03-14 04:44:01 +01:00
|
|
|
public static function parseFloatXX($value)
|
2020-08-26 02:47:50 +02:00
|
|
|
{
|
2024-03-14 04:44:01 +01:00
|
|
|
|
2024-06-14 09:09:44 +02:00
|
|
|
if(!$value) {
|
2024-03-11 09:37:53 +01:00
|
|
|
return 0;
|
2024-06-14 09:09:44 +02:00
|
|
|
}
|
2024-02-16 04:28:57 +01:00
|
|
|
|
2024-03-11 09:37:53 +01:00
|
|
|
$multiplier = false;
|
2020-08-26 02:47:50 +02:00
|
|
|
|
2024-06-14 09:09:44 +02:00
|
|
|
if(substr($value, 0, 1) == '-') {
|
2024-03-11 09:37:53 +01:00
|
|
|
$multiplier = -1;
|
2024-06-14 09:09:44 +02:00
|
|
|
}
|
2020-08-26 02:47:50 +02:00
|
|
|
|
2024-03-11 09:37:53 +01:00
|
|
|
$s = str_replace(',', '.', $value);
|
2024-02-20 03:29:30 +01:00
|
|
|
|
2024-03-11 09:37:53 +01:00
|
|
|
$s = preg_replace("/[^0-9\.]/", '', $s);
|
2024-02-20 03:29:30 +01:00
|
|
|
|
2024-03-11 09:37:53 +01:00
|
|
|
if ($s < 1) {
|
|
|
|
return (float) $s;
|
|
|
|
}
|
2021-09-24 12:55:21 +02:00
|
|
|
|
2024-03-11 09:37:53 +01:00
|
|
|
$s = str_replace('.', '', substr($s, 0, -3)).substr($s, -3);
|
2020-08-26 02:47:50 +02:00
|
|
|
|
2024-06-14 09:09:44 +02:00
|
|
|
if($multiplier) {
|
|
|
|
$s = floatval($s) * -1;
|
|
|
|
}
|
2024-02-16 04:28:57 +01:00
|
|
|
|
2024-03-11 09:37:53 +01:00
|
|
|
return (float) $s;
|
2020-08-26 02:47:50 +02:00
|
|
|
}
|
|
|
|
|
2024-06-14 09:09:44 +02:00
|
|
|
|
2024-02-29 02:31:59 +01:00
|
|
|
//next iteration of float parsing
|
2024-03-11 09:37:53 +01:00
|
|
|
public static function parseFloat2($value)
|
2024-02-29 02:31:59 +01:00
|
|
|
{
|
|
|
|
|
|
|
|
if(!$value) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//remove everything except for numbers, decimals, commas and hyphens
|
|
|
|
$value = preg_replace('/[^0-9.,-]+/', '', $value);
|
|
|
|
|
|
|
|
$decimal = strpos($value, '.');
|
|
|
|
$comma = strpos($value, ',');
|
|
|
|
|
|
|
|
//check the 3rd last character
|
|
|
|
if(!in_array(substr($value, -3, 1), [".", ","])) {
|
|
|
|
|
|
|
|
if($comma && (substr($value, -3, 1) != ".")) {
|
|
|
|
$value .= ".00";
|
|
|
|
} elseif($decimal && (substr($value, -3, 1) != ",")) {
|
|
|
|
$value .= ",00";
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
$decimal = strpos($value, '.');
|
|
|
|
$comma = strpos($value, ',');
|
|
|
|
|
|
|
|
if($comma === false) { //no comma must be a decimal number already
|
|
|
|
return (float) $value;
|
|
|
|
}
|
|
|
|
|
|
|
|
if($decimal < $comma) { //decimal before a comma = euro
|
|
|
|
$value = str_replace(['.',','], ['','.'], $value);
|
|
|
|
return (float) $value;
|
|
|
|
}
|
|
|
|
|
2024-03-11 03:35:16 +01:00
|
|
|
//comma first = traditional thousand separator
|
2024-02-29 02:31:59 +01:00
|
|
|
$value = str_replace(',', '', $value);
|
|
|
|
|
|
|
|
return (float)$value;
|
|
|
|
|
|
|
|
}
|
2024-06-14 09:09:44 +02:00
|
|
|
|
|
|
|
|
2020-12-20 10:02:10 +01:00
|
|
|
public static function parseStringFloat($value)
|
|
|
|
{
|
|
|
|
$value = preg_replace('/[^0-9-.]+/', '', $value);
|
|
|
|
|
|
|
|
// check for comma as decimal separator
|
|
|
|
if (preg_match('/,[\d]{1,2}$/', $value)) {
|
|
|
|
$value = str_replace(',', '.', $value);
|
|
|
|
}
|
|
|
|
|
|
|
|
$value = preg_replace('/[^0-9\.\-]/', '', $value);
|
|
|
|
|
|
|
|
return floatval($value);
|
|
|
|
}
|
2021-03-17 16:12:25 +01:00
|
|
|
|
2019-08-28 04:36:53 +02:00
|
|
|
/**
|
2020-09-06 11:38:10 +02:00
|
|
|
* Formats a given value based on the clients currency AND country.
|
2019-12-30 22:59:12 +01:00
|
|
|
*
|
2023-04-26 11:25:33 +02:00
|
|
|
* @param $value The number to be formatted
|
2021-03-17 16:12:25 +01:00
|
|
|
* @param $entity
|
2019-09-04 23:55:49 +02:00
|
|
|
* @return string The formatted value
|
2019-08-28 04:36:53 +02:00
|
|
|
*/
|
2024-01-14 05:05:00 +01:00
|
|
|
public static function formatMoney($value, $entity): string
|
2019-08-28 04:36:53 +02:00
|
|
|
{
|
2022-11-01 11:20:28 +01:00
|
|
|
$value = floatval($value);
|
|
|
|
|
2023-02-17 07:19:33 +01:00
|
|
|
$_value = $value;
|
|
|
|
|
2021-03-17 16:12:25 +01:00
|
|
|
$currency = $entity->currency();
|
2019-10-07 12:21:02 +02:00
|
|
|
|
2019-10-16 12:24:33 +02:00
|
|
|
$thousand = $currency->thousand_separator;
|
|
|
|
$decimal = $currency->decimal_separator;
|
|
|
|
$precision = $currency->precision;
|
|
|
|
$code = $currency->code;
|
2019-10-23 22:37:20 +02:00
|
|
|
$swapSymbol = $currency->swap_currency_symbol;
|
2019-08-28 04:36:53 +02:00
|
|
|
|
2021-03-17 16:12:25 +01:00
|
|
|
if ($entity instanceof Company) {
|
|
|
|
$country = $entity->country();
|
|
|
|
} else {
|
|
|
|
$country = $entity->country;
|
|
|
|
}
|
|
|
|
|
2019-12-30 22:59:12 +01:00
|
|
|
/* Country settings override client settings */
|
2021-03-17 16:12:25 +01:00
|
|
|
if (isset($country->thousand_separator) && strlen($country->thousand_separator) >= 1) {
|
|
|
|
$thousand = $country->thousand_separator;
|
2019-12-30 22:59:12 +01:00
|
|
|
}
|
2020-09-06 11:38:10 +02:00
|
|
|
|
2021-03-17 16:12:25 +01:00
|
|
|
if (isset($country->decimal_separator) && strlen($country->decimal_separator) >= 1) {
|
|
|
|
$decimal = $country->decimal_separator;
|
2019-12-30 22:59:12 +01:00
|
|
|
}
|
2019-10-24 11:23:37 +02:00
|
|
|
|
2024-07-11 07:53:18 +02:00
|
|
|
if (isset($country->swap_currency_symbol) && $country->swap_currency_symbol == 1) {
|
2021-03-17 16:12:25 +01:00
|
|
|
$swapSymbol = $country->swap_currency_symbol;
|
2019-12-30 22:59:12 +01:00
|
|
|
}
|
2019-10-24 06:46:24 +02:00
|
|
|
|
2019-08-28 04:36:53 +02:00
|
|
|
$value = number_format($value, $precision, $decimal, $thousand);
|
2019-10-16 12:24:33 +02:00
|
|
|
$symbol = $currency->symbol;
|
2020-09-06 11:38:10 +02:00
|
|
|
|
2021-03-17 16:12:25 +01:00
|
|
|
if ($entity->getSetting('show_currency_code') === true && $currency->code == 'CHF') {
|
2020-12-08 21:35:26 +01:00
|
|
|
return "{$code} {$value}";
|
2021-03-17 16:12:25 +01:00
|
|
|
} elseif ($entity->getSetting('show_currency_code') === true) {
|
2019-08-28 04:36:53 +02:00
|
|
|
return "{$value} {$code}";
|
|
|
|
} elseif ($swapSymbol) {
|
2020-09-06 11:38:10 +02:00
|
|
|
return "{$value} ".trim($symbol);
|
2021-03-17 16:12:25 +01:00
|
|
|
} elseif ($entity->getSetting('show_currency_code') === false) {
|
2023-02-17 22:36:51 +01:00
|
|
|
/* Ensures we place the negative symbol ahead of the currency symbol*/
|
|
|
|
if ($_value < 0) {
|
|
|
|
$value = substr($value, 1);
|
|
|
|
$symbol = "-{$symbol}";
|
|
|
|
}
|
2023-02-17 07:19:33 +01:00
|
|
|
|
2019-08-28 04:36:53 +02:00
|
|
|
return "{$symbol}{$value}";
|
|
|
|
} else {
|
2024-07-15 05:35:21 +02:00
|
|
|
return self::formatValue($value, $currency); //@phpstan-ignore-line
|
2019-08-28 04:36:53 +02:00
|
|
|
}
|
|
|
|
}
|
2022-01-08 10:16:21 +01:00
|
|
|
|
2022-06-21 11:57:17 +02:00
|
|
|
/**
|
2022-01-08 10:16:21 +01:00
|
|
|
* Formats a given value based on the clients currency AND country.
|
|
|
|
*
|
2023-06-17 07:14:51 +02:00
|
|
|
* @param float $value The number to be formatted
|
|
|
|
* @param mixed $entity
|
2022-01-08 10:16:21 +01:00
|
|
|
* @return string The formatted value
|
|
|
|
*/
|
2024-01-14 05:05:00 +01:00
|
|
|
public static function formatMoneyNoRounding($value, $entity): string
|
2022-01-08 10:16:21 +01:00
|
|
|
{
|
|
|
|
$currency = $entity->currency();
|
2024-01-14 05:05:00 +01:00
|
|
|
|
2023-03-01 06:32:47 +01:00
|
|
|
$_value = $value;
|
2022-01-08 10:16:21 +01:00
|
|
|
|
|
|
|
$thousand = $currency->thousand_separator;
|
|
|
|
$decimal = $currency->decimal_separator;
|
|
|
|
$precision = $currency->precision;
|
|
|
|
$code = $currency->code;
|
|
|
|
$swapSymbol = $currency->swap_currency_symbol;
|
|
|
|
|
|
|
|
if ($entity instanceof Company) {
|
|
|
|
$country = $entity->country();
|
|
|
|
} else {
|
|
|
|
$country = $entity->country;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Country settings override client settings */
|
|
|
|
if (isset($country->thousand_separator) && strlen($country->thousand_separator) >= 1) {
|
|
|
|
$thousand = $country->thousand_separator;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isset($country->decimal_separator) && strlen($country->decimal_separator) >= 1) {
|
|
|
|
$decimal = $country->decimal_separator;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isset($country->swap_currency_symbol) && strlen($country->swap_currency_symbol) >= 1) {
|
|
|
|
$swapSymbol = $country->swap_currency_symbol;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 08-01-2022 allow increased precision for unit price*/
|
2022-06-21 11:57:17 +02:00
|
|
|
$v = rtrim(sprintf('%f', $value), '0');
|
2023-03-06 01:03:19 +01:00
|
|
|
$parts = explode('.', $v);
|
2022-06-21 11:57:17 +02:00
|
|
|
|
2023-02-08 01:27:38 +01:00
|
|
|
/* 08-02-2023 special if block to render $0.5 to $0.50*/
|
|
|
|
if ($v < 1 && strlen($v) == 3) {
|
|
|
|
$precision = 2;
|
2023-02-16 02:36:09 +01:00
|
|
|
} elseif ($v < 1) {
|
2022-02-20 08:07:05 +01:00
|
|
|
$precision = strlen($v) - strrpos($v, '.') - 1;
|
2023-03-18 08:24:56 +01:00
|
|
|
}
|
2024-01-14 05:05:00 +01:00
|
|
|
|
2023-03-18 08:24:56 +01:00
|
|
|
if (is_array($parts) && $parts[0] != 0) {
|
2023-03-06 01:03:19 +01:00
|
|
|
$precision = 2;
|
2022-06-21 11:57:17 +02:00
|
|
|
}
|
2022-02-20 05:49:31 +01:00
|
|
|
|
2023-04-04 13:15:02 +02:00
|
|
|
//04-04-2023 if currency = JPY override precision to 0
|
|
|
|
if($currency->code == 'JPY') {
|
|
|
|
$precision = 0;
|
|
|
|
}
|
|
|
|
|
2024-07-15 05:35:21 +02:00
|
|
|
$value = number_format($v, $precision, $decimal, $thousand);//@phpstan-ignore-line
|
2022-01-08 10:16:21 +01:00
|
|
|
$symbol = $currency->symbol;
|
|
|
|
|
|
|
|
if ($entity->getSetting('show_currency_code') === true && $currency->code == 'CHF') {
|
|
|
|
return "{$code} {$value}";
|
|
|
|
} elseif ($entity->getSetting('show_currency_code') === true) {
|
|
|
|
return "{$value} {$code}";
|
|
|
|
} elseif ($swapSymbol) {
|
|
|
|
return "{$value} ".trim($symbol);
|
|
|
|
} elseif ($entity->getSetting('show_currency_code') === false) {
|
2023-03-01 06:32:47 +01:00
|
|
|
if ($_value < 0) {
|
|
|
|
$value = substr($value, 1);
|
|
|
|
$symbol = "-{$symbol}";
|
|
|
|
}
|
|
|
|
|
2022-01-08 10:16:21 +01:00
|
|
|
return "{$symbol}{$value}";
|
|
|
|
} else {
|
2024-07-15 05:35:21 +02:00
|
|
|
return self::formatValue($value, $currency); //@phpstan-ignore-line
|
2022-01-08 10:16:21 +01:00
|
|
|
}
|
|
|
|
}
|
2019-08-28 04:36:53 +02:00
|
|
|
}
|