1
0
Fork 0
mirror of https://github.com/archtechx/money.git synced 2025-12-12 03:14:03 +00:00

feat: extract currency from the formatted string

This commit is contained in:
Gaurav 2022-03-10 11:39:53 +05:30
parent fa337d29b4
commit 93aeb2c300
4 changed files with 57 additions and 3 deletions

View file

@ -172,7 +172,7 @@ $money = Money::new(123456, CZK::class);
$money->rawFormatted(); // 1 235,56 Kč $money->rawFormatted(); // 1 235,56 Kč
``` ```
And converting the formatted value back to the Money instance is also possible: And converting the formatted value back to the Money instance is also possible. The package tries to extract the currency from the provided string.
```php ```php
$money = money(1000); $money = money(1000);
$formatted = $money->formatted(); // $10.00 $formatted = $money->formatted(); // $10.00
@ -180,7 +180,18 @@ $fromFormatted = Money::fromFormatted($formatted);
$fromFormatted->is($money); // true $fromFormatted->is($money); // true
``` ```
Note: `fromFormatted()` misses the cents if the [math decimals](#math-decimals) are greater than [display decimals](#display-decimals). Optional overrides for the [currency specification](#currency-logic) are accepted too.
```php
$money = money(1000);
$formatted = $money->formatted(); // $10.00
$fromFormatted = Money::fromFormatted($formatted, USD::class, ['decimalSeparator' => ',', 'prefix' => '$ ', 'suffix' => ' USD']);
$fromFormatted->is($money); // true
```
Notes:
1) If currency is not specified and none of the currencies match the prefix and suffix, an exception will be thrown.
2) If currency is not specified and multiple currencies use the same prefix and suffix, an exception will be thrown.
3) `fromFormatted()` misses the cents if the [math decimals](#math-decimals) are greater than [display decimals](#display-decimals).
### Rounding money ### Rounding money

View file

@ -174,6 +174,10 @@ final class Money implements JsonSerializable, Arrayable, Wireable
/** Create a Money instance from a formatted string. */ /** Create a Money instance from a formatted string. */
public static function fromFormatted(string $formatted, Currency|string $currency = null, mixed ...$overrides): self public static function fromFormatted(string $formatted, Currency|string $currency = null, mixed ...$overrides): self
{ {
$currency = isset($currency)
? currency($currency)
: PriceFormatter::extractCurrency($formatted);
$decimal = PriceFormatter::resolve($formatted, currency($currency), variadic_array($overrides)); $decimal = PriceFormatter::resolve($formatted, currency($currency), variadic_array($overrides));
return static::fromDecimal($decimal, currency($currency)); return static::fromDecimal($decimal, currency($currency));

View file

@ -23,7 +23,7 @@ class PriceFormatter
return $currency->prefix() . $decimal . $currency->suffix(); return $currency->prefix() . $decimal . $currency->suffix();
} }
/** Extract the decimal from the formatter string as per the currency's specifications. */ /** Extract the decimal from the formatted string as per the currency's specifications. */
public static function resolve(string $formatted, Currency $currency, array $overrides = []): float public static function resolve(string $formatted, Currency $currency, array $overrides = []): float
{ {
$currency = Currency::fromArray( $currency = Currency::fromArray(
@ -41,4 +41,25 @@ class PriceFormatter
return (float) str_replace($currency->decimalSeparator(), '.', $removeNonDigits); return (float) str_replace($currency->decimalSeparator(), '.', $removeNonDigits);
} }
/** Tries to extract the currency from the formatted string. */
public static function extractCurrency(string $formatted): Currency
{
$possibleCurrency = null;
foreach (currencies()->all() as $currency) {
if (
str_starts_with($formatted, $currency->prefix())
&& str_ends_with($formatted, $currency->suffix())
) {
if ($possibleCurrency) {
throw new \Exception('Multiple currencies are using the same prefix and suffix. Please specify the currency of the formatted string.');
}
$possibleCurrency = $currency;
}
}
return $possibleCurrency ?? throw new \Exception('None of the currencies are using the prefix and suffix that would match with the formatted string.');
}
} }

View file

@ -159,6 +159,24 @@ test('money can be created from a raw formatted string', function () {
expect($money->value())->toBe(123456); expect($money->value())->toBe(123456);
}); });
test('an exception is thrown if none of the currencies match the prefix and suffix', function () {
$money = money(1000);
$formatted = $money->formatted();
currencies()->remove(USD::class);
pest()->expectException(\Exception::class);
Money::fromFormatted($formatted);
});
test('an exception is thrown if multiple currencies are using the same prefix and suffix', function () {
currencies()->add(['code' => 'USD2', 'name' => 'USD2', 'prefix' => '$']);
$money = money(1000);
pest()->expectException(\Exception::class);
Money::fromFormatted($money->formatted());
});
test('converting money to a string returns the formatted string', function () { test('converting money to a string returns the formatted string', function () {
expect( expect(
(string) Money::fromDecimal(10.00, USD::class) (string) Money::fromDecimal(10.00, USD::class)