Skip to content

Commit

Permalink
Merge pull request #3189 from fdjohnston/currencies_stored_as_strings
Browse files Browse the repository at this point in the history
Handle Currencies Stored as Strings in Formulas
  • Loading branch information
MarkBaker authored Nov 25, 2022
2 parents 6a259d9 + 80e4d3a commit bdb34b9
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 0 deletions.
28 changes: 28 additions & 0 deletions src/PhpSpreadsheet/Calculation/Engine/FormattedNumber.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace PhpOffice\PhpSpreadsheet\Calculation\Engine;

use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;

class FormattedNumber
{
Expand All @@ -17,6 +18,7 @@ class FormattedNumber
[self::class, 'convertToNumberIfNumeric'],
[self::class, 'convertToNumberIfFraction'],
[self::class, 'convertToNumberIfPercent'],
[self::class, 'convertToNumberIfCurrency'],
];

/**
Expand Down Expand Up @@ -92,4 +94,30 @@ public static function convertToNumberIfPercent(string &$operand): bool

return false;
}

/**
* Identify whether a string contains a currency value, and if so,
* convert it to a numeric.
*
* @param string $operand string value to test
*/
public static function convertToNumberIfCurrency(string &$operand): bool
{
$quotedCurrencyCode = preg_quote(StringHelper::getCurrencyCode());

$value = preg_replace('/(\d),(\d)/u', '$1$2', $operand);
$regExp = '~^(?:(?: *(?<PrefixedSign>[-+])? *' . $quotedCurrencyCode . ' *(?<PrefixedSign2>[-+])? *(?<PrefixedValue>[0-9]+\.?[0-9*]*(?:E[-+]?[0-9]*)?) *)|(?: *(?<PostfixedSign>[-+])? *(?<PostfixedValue>[0-9]+\.?[0-9]*(?:E[-+]?[0-9]*)?) *' . $quotedCurrencyCode . ' *))$~ui';

$match = [];
if ($value !== null && preg_match($regExp, $value, $match, PREG_UNMATCHED_AS_NULL)) {
//Determine the sign
$sign = ($match['PrefixedSign'] ?? $match['PrefixedSign2'] ?? $match['PostfixedSign']) ?? '';
//Cast to a float
$operand = (float) ($sign . ($match['PostfixedValue'] ?? $match['PrefixedValue']));

return true;
}

return false;
}
}
15 changes: 15 additions & 0 deletions tests/PhpSpreadsheetTests/Calculation/CalculationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PHPUnit\Framework\TestCase;

Expand Down Expand Up @@ -216,6 +217,20 @@ public function testCellWithStringPercentage(): void
self::assertSame(2.0, $cell2->getCalculatedValue());
}

public function testCellWithStringCurrency(): void
{
$currencyCode = StringHelper::getCurrencyCode();

$spreadsheet = new Spreadsheet();
$workSheet = $spreadsheet->getActiveSheet();
$cell1 = $workSheet->getCell('A1');
$cell1->setValue($currencyCode . '2');
$cell2 = $workSheet->getCell('B1');
$cell2->setValue('=100*A1');

self::assertSame(200.0, $cell2->getCalculatedValue());
}

public function testBranchPruningFormulaParsingSimpleCase(): void
{
$calculation = Calculation::getInstance();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Engine;

use PhpOffice\PhpSpreadsheet\Calculation\Engine\FormattedNumber;
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
use PHPUnit\Framework\TestCase;

class FormattedNumberTest extends TestCase
Expand Down Expand Up @@ -183,4 +184,51 @@ public function providerPercentages(): array
'permutation_88' => [' - % 2.50e - 06 ', ' - % 2.50e - 06 '],
];
}

/**
* @dataProvider providerCurrencies
*/
public function testCurrencies(string $expected, string $value): void
{
$originalValue = $value;
$result = FormattedNumber::convertToNumberIfCurrency($value);
if ($result === false) {
self::assertSame($expected, $originalValue);
self::assertSame($expected, $value);
} else {
self::assertSame($expected, (string) $value);
self::assertNotEquals($value, $originalValue);
}
}

public function providerCurrencies(): array
{
$currencyCode = StringHelper::getCurrencyCode();

return [
'basic_prefix_currency' => ['2.75', "{$currencyCode}2.75"],
'basic_postfix_currency' => ['2.75', "2.75{$currencyCode}"],

'basic_prefix_currency_with_spaces' => ['2.75', "{$currencyCode} 2.75"],
'basic_postfix_currency_with_spaces' => ['2.75', "2.75 {$currencyCode}"],

'negative_basic_prefix_currency' => ['-2.75', "-{$currencyCode}2.75"],
'negative_basic_postfix_currency' => ['-2.75', "-2.75{$currencyCode}"],

'negative_basic_prefix_currency_with_spaces' => ['-2.75', "-{$currencyCode} 2.75"],
'negative_basic_postfix_currency_with_spaces' => ['-2.75', "-2.75 {$currencyCode}"],

'positive_signed_prefix_currency_with_spaces' => ['2.75', "+{$currencyCode} 2.75"],
'positive_signed_prefix_currency_with_spaces-2' => ['2.75', "{$currencyCode} +2.75"],
'positive_signed_postfix_currency_with_spaces' => ['2.75', "+2.75 {$currencyCode}"],

'basic_prefix_scientific_currency' => ['2000000', "{$currencyCode}2E6"],
'basic_postfix_scientific_currency' => ['2000000', "2E6{$currencyCode}"],

'basic_prefix_scientific_currency_with_spaces' => ['2000000', "{$currencyCode} 2E6"],
'basic_postfix_scientific_currency_with_spaces' => ['2000000', "2E6 {$currencyCode}"],

'high_value_currency_with_thousands_separator' => ['2750000', "+{$currencyCode} 2,750,000"],
];
}
}

0 comments on commit bdb34b9

Please sign in to comment.