Skip to content

Commit

Permalink
Merge pull request #11 from npbreland/money-input
Browse files Browse the repository at this point in the history
Fix to MoneyInput
  • Loading branch information
pelmered authored Feb 18, 2024
2 parents db5f702 + b6bc755 commit 4f20e72
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 38 deletions.
59 changes: 45 additions & 14 deletions src/Forms/Components/MoneyInput.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,50 +15,81 @@ protected function setUp(): void
{
parent::setUp();

$this->inputMode('decimal');
$this->step(0.01);
$this->minValue = 0;

$this->formatStateUsing(function (MoneyInput $component, $state): ?string {

$currency = $component->getCurrency()->getCode();
$state = MoneyFormatter::parseDecimal($state, $currency, $component->getLocale());

$this->prepare($component);

$currency = $component->getCurrency();
$locale = $component->getLocale();

if (is_null($state)) {
return '';
}
if(!is_numeric($state)) {
return $state;
}

return MoneyFormatter::decimalToMoneyString($state / 100, $component->getLocale());
return MoneyFormatter::formatAsDecimal($state, $currency, $locale);
});

$this->dehydrateStateUsing(function (MoneyInput $component, $state): string {

$this->prepare($component);

$currency = $component->getCurrency()->getCode();
$state = MoneyFormatter::parseDecimal($state, $currency, $component->getLocale());

$this->prepare($component);

return $state;
});
}

protected function prepare(MoneyInput $component): void
{
$formattingRules = MoneyFormatter::getFormattingRules($component->getLocale());

$this->prefix($formattingRules->currencySymbol);

if (config('filament-money-field.use_input_mask')) {
$this->mask(RawJs::make('$money($input, \'' . $formattingRules->decimalSeparator . '\', \'' . $formattingRules->groupingSeparator . '\', ' . $formattingRules->fractionDigits . ')'));
}
}

public function minValue(mixed $min): static
{
$this->rule(static function (MoneyInput $component, mixed $state) use ($min) {
return function (string $attribute, mixed $value, \Closure $fail) use ($component, $state, $min) {

$value = MoneyFormatter::parseDecimal(
$state,
$component->getCurrency()->getCode(),
$component->getLocale()
);

if ($value < $min) {
$fail('The :attribute must be greater than or equal to ' . $min . '.');
}
};
});

return $this;
}

public function maxValue(mixed $max): static
{
$this->rule(static function (MoneyInput $component, mixed $state) use ($max) {
return function (string $attribute, mixed $value, \Closure $fail) use ($component, $state, $max) {

$value = MoneyFormatter::parseDecimal(
$state,
$component->getCurrency()->getCode(),
$component->getLocale()
);

if ($value > $max) {
$fail('The :attribute must be less than or equal to ' . $max . '.');
}
};
});

$this->stripCharacters($formattingRules->groupingSeparator);
// OR
$this->stripCharacters([',', '.', ' ',]);
return $this;
}
}
13 changes: 13 additions & 0 deletions src/MoneyFormatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,19 @@ public static function format($value, $currency, $locale, $monetarySeparator = n
return $moneyFormatter->format($money);
}

public static function formatAsDecimal($value, $currency, $locale): string
{
if (is_null($value) || $value === '') {
return '';
}

$numberFormatter = self::getNumberFormatter($locale, \NumberFormatter::DECIMAL);
$moneyFormatter = new IntlMoneyFormatter($numberFormatter, new ISOCurrencies());

$money = new Money($value, $currency);
return $moneyFormatter->format($money); // outputs 1.000,00
}

public static function parseDecimal($moneyString, $currency, $locale): string
{
if (is_null($moneyString) || $moneyString === '') {
Expand Down
106 changes: 82 additions & 24 deletions tests/MoneyFormatterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
namespace Pelmered\FilamentMoneyField\Tests;
use Money\Currency;
use Pelmered\FilamentMoneyField\MoneyFormatter;
use PHPUnit\Framework;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\DataProvider;

#[CoversClass(MoneyFormatter::class)]
final class MoneyFormatterTest extends TestCase
{
public static function provideMoneyDataSEK(): array
Expand Down Expand Up @@ -33,6 +35,32 @@ public static function provideMoneyDataSEK(): array
];
}

public static function provideDecimalMoneyDataSEK(): array
{
return [
'thousands' => [
1000000,
'10 000,00',
],
'decimals' => [
10045,
'100,45',
],
'millions' => [
123456789,
'1 234 567,89',
],
'empty_string' => [
'',
'',
],
'null' => [
null,
'',
],
];
}

public static function provideMoneyDataUSD(): array
{
return [
Expand All @@ -59,6 +87,32 @@ public static function provideMoneyDataUSD(): array
];
}

public static function provideDecimalMoneyDataUSD(): array
{
return [
'thousands' => [
1000000,
'10,000.00',
],
'decimals' => [
10045,
'100.45',
],
'millions' => [
123456789,
'1,234,567.89',
],
'empty_string' => [
'',
'',
],
'null' => [
null,
'',
],
];
}

public static function provideDecimalDataSEK(): array
{
return [
Expand Down Expand Up @@ -110,13 +164,17 @@ public static function provideDecimalDataUSD(): array
],
];
}

#[DataProvider('provideMoneyDataUSD')]
public function testMoneyFormatterUSD(mixed $input, string $expectedOutput)
{
self::assertSame(
static::replaceNonBreakingSpaces($expectedOutput),
MoneyFormatter::format($input, new Currency('USD'), 'en_US')
);
}


/**
* @covers MoneyFormatter::format
* @dataProvider provideMoneyDataSEK
*/
#[Framework\CoversClass(MoneyFormatter::class)]
#[DataProvider('provideMoneyDataSEK')]
public function testMoneyFormatterSEK(mixed $input, string $expectedOutput)
{
self::assertSame(
Expand All @@ -125,24 +183,28 @@ public function testMoneyFormatterSEK(mixed $input, string $expectedOutput)
);
}

/**
* @covers MoneyFormatter::format
* @dataProvider provideMoneyDataUSD
*/
#[Framework\CoversClass(MoneyFormatter::class)]
public function testMoneyFormatterUSD(mixed $input, string $expectedOutput)
#[DataProvider('provideDecimalMoneyDataUSD')]
//#[CoversClass(MoneyFormatter::class)]
public function testMoneyDecimalFormatterUSD(mixed $input, string $expectedOutput)
{
self::assertSame(
static::replaceNonBreakingSpaces($expectedOutput),
MoneyFormatter::format($input, new Currency('USD'), 'en_US')
MoneyFormatter::formatAsDecimal($input, new Currency('USD'), 'en_US')
);
}

/**
* @covers MoneyFormatter::parseDecimal
* @dataProvider provideDecimalDataSEK
*/
#[Framework\CoversClass(MoneyFormatter::class)]
#[DataProvider('provideDecimalMoneyDataSEK')]
//#[CoversClass(MoneyFormatter::class)]
public function testMoneyDecimalFormatterSEK(mixed $input, string $expectedOutput)
{
self::assertSame(
static::replaceNonBreakingSpaces($expectedOutput),
MoneyFormatter::formatAsDecimal($input, new Currency('SEK'), 'sv_SE')
);
}

#[DataProvider('provideDecimalDataSEK')]
//#[CoversClass(MoneyFormatter::class)]
public function testMoneyParserDecimalSEK(mixed $input, string $expectedOutput)
{
self::assertSame(
Expand All @@ -151,11 +213,7 @@ public function testMoneyParserDecimalSEK(mixed $input, string $expectedOutput)
);
}

/**
* @covers MoneyFormatter::parseDecimal
* @dataProvider provideDecimalDataUSD
*/
#[Framework\CoversClass(MoneyFormatter::class)]
#[DataProvider('provideDecimalDataUSD')]
public function testMoneyParserDecimalUSD(mixed $input, string $expectedOutput)
{
self::assertSame(
Expand Down

0 comments on commit 4f20e72

Please sign in to comment.