From 20acd28194159cb0de9fdd4167259ab5aa4c0ac3 Mon Sep 17 00:00:00 2001 From: Benjamin Morel Date: Thu, 9 Jan 2020 11:07:21 +0100 Subject: [PATCH] Add CurrencyConverter::convertToRational() --- src/CurrencyConverter.php | 22 +++++++++++++++++- tests/CurrencyConverterTest.php | 41 +++++++++++++++++++++++++++++++-- 2 files changed, 60 insertions(+), 3 deletions(-) diff --git a/src/CurrencyConverter.php b/src/CurrencyConverter.php index aa4adaa..73ef6f4 100644 --- a/src/CurrencyConverter.php +++ b/src/CurrencyConverter.php @@ -13,6 +13,10 @@ /** * Converts monies into different currencies, using an exchange rate provider. + * + * @todo Now that this class provides methods to convert to both Money and RationalMoney, it makes little sense to + * provide the context in the constructor, as this only applies to convert() and not convertToRational(). + * This should probably be a parameter to convert(). */ final class CurrencyConverter { @@ -33,6 +37,7 @@ final class CurrencyConverter /** * @param ExchangeRateProvider $exchangeRateProvider The exchange rate provider. * @param Context|null $context A context to create the monies in, or null to use the default. + * The context only applies to convert(), not convertToRational(). */ public function __construct(ExchangeRateProvider $exchangeRateProvider, Context $context = null) { @@ -57,6 +62,21 @@ public function __construct(ExchangeRateProvider $exchangeRateProvider, Context * @throws RoundingNecessaryException If rounding is necessary and RoundingMode::UNNECESSARY is used. */ public function convert(MoneyContainer $moneyContainer, $currency, int $roundingMode = RoundingMode::UNNECESSARY) : Money + { + return $this->convertToRational($moneyContainer, $currency)->to($this->context, $roundingMode); + } + + /** + * Converts the given money to the given currency, and returns the result as a RationalMoney with no rounding. + * + * @param MoneyContainer $moneyContainer The Money, RationalMoney or MoneyBag to convert. + * @param Currency|string|int $currency The Currency instance, ISO currency code or ISO numeric currency code. + * + * @return RationalMoney + * + * @throws CurrencyConversionException If the exchange rate is not available. + */ + public function convertToRational(MoneyContainer $moneyContainer, $currency) : RationalMoney { if (! $currency instanceof Currency) { $currency = Currency::of($currency); @@ -75,6 +95,6 @@ public function convert(MoneyContainer $moneyContainer, $currency, int $rounding $total = $total->plus($amount); } - return Money::create($total, $currency, $this->context, $roundingMode); + return new RationalMoney($total, $currency); } } diff --git a/tests/CurrencyConverterTest.php b/tests/CurrencyConverterTest.php index 7274681..56fe49a 100644 --- a/tests/CurrencyConverterTest.php +++ b/tests/CurrencyConverterTest.php @@ -86,7 +86,7 @@ public function providerConvertMoney() * @param string $currency The target currency code. * @param Context $context The target context. * @param int $roundingMode The rounding mode to use. - * @param string $total The expected total + * @param string $total The expected total. */ public function testConvertMoneyBag(array $monies, $currency, Context $context, $roundingMode, $total) { @@ -108,7 +108,7 @@ public function testConvertMoneyBag(array $monies, $currency, Context $context, /** * @return array */ - public function providerConvertMoneyBag() + public function providerConvertMoneyBag() : array { return [ [[['354.40005', 'EUR'], ['3.1234', 'JPY']], 'USD', new DefaultContext(), RoundingMode::DOWN, 'USD 437.56'], @@ -119,6 +119,43 @@ public function providerConvertMoneyBag() ]; } + /** + * @dataProvider providerConvertMoneyBagToRational + * + * @param array $monies The mixed monies to add. + * @param string $currency The target currency code. + * @param string $expectedTotal The expected total. + */ + public function testConvertMoneyBagToRational(array $monies, string $currency, string $expectedTotal) : void + { + $exchangeRateProvider = new ConfigurableProvider(); + $exchangeRateProvider->setExchangeRate('EUR', 'USD', '1.123456789'); + $exchangeRateProvider->setExchangeRate('JPY', 'USD', '0.0098765432123456789'); + + $moneyBag = new MoneyBag(); + + foreach ($monies as $money) { + $money = Money::of($money[0], $money[1], new AutoContext()); + $moneyBag->add($money); + } + + $currencyConverter = new CurrencyConverter($exchangeRateProvider); + $actualTotal = $currencyConverter->convertToRational($moneyBag, $currency)->simplified(); + + $this->assertRationalMoneyEquals($expectedTotal, $actualTotal); + } + + /** + * @return array + */ + public function providerConvertMoneyBagToRational() : array + { + return [ + [[['354.40005', 'EUR'], ['3.1234', 'JPY']], 'USD', 'USD 19909199529475444524673813/50000000000000000000000'], + [[['1234.56', 'EUR'], ['31562', 'JPY']], 'USD', 'USD 8493491351479471587209/5000000000000000000'] + ]; + } + /** * @dataProvider providerConvertRationalMoney *