Skip to content

\Magento\Directory\Model\PriceCurrency::format() fails without conversion rate #6965

Closed
@Vinai

Description

@Vinai

The issue is about the implementation of the method \Magento\Framework\Pricing\PriceCurrencyInterface::format().

Even though no conversion takes place during the formatting of the given amount, the method only works if a conversion rate from the base currency to the specified currency is configured.

There is no technical need for this limitation.

Preconditions

A currency code without a configured conversion rate to the base currency.

Steps to reproduce

Call the method \Magento\Directory\Model\PriceCurrency::format() with a currency code without a configured conversion rate to the base currency.

Expected result

The specified amount should be formatted in the specified currency.

Actual result

The specified amount is surprisingly rendered in the base currency.

Further information

The method format() looks like this:

public function format(
    $amount,
    $includeContainer = true,
    $precision = self::DEFAULT_PRECISION,
    $scope = null,
    $currency = null
) {
    return $this->getCurrency($scope, $currency)
        ->formatPrecision($amount, $precision, [], $includeContainer);
}

This problem occurs in the getCurrency() method where the constraint that the currency needs a configured conversion rate is applies:

public function getCurrency($scope = null, $currency = null)
{
    if ($currency instanceof Currency) {
        $currentCurrency = $currency;
    } elseif (is_string($currency)) {
        $currency = $this->currencyFactory->create()
            ->load($currency);
        $baseCurrency = $this->getStore($scope)
            ->getBaseCurrency();
        $currentCurrency = $baseCurrency->getRate($currency) ? $currency : $baseCurrency;
    } else {
        $currentCurrency = $this->getStore($scope)
            ->getCurrentCurrency();
    }

    return $currentCurrency;
}

With a currency code the middle conditional branch is executed:

        $currency = $this->currencyFactory->create()
            ->load($currency);
        $baseCurrency = $this->getStore($scope)
            ->getBaseCurrency();
        $currentCurrency = $baseCurrency->getRate($currency) ? $currency : $baseCurrency;

This rule makes sense in some contexts, but not in the context of formatting a currency value.

Proposed solution:

Extract the loading of the given currency into a separate method and call that from format() instead of the current getCurrency() method. This way there is no behavioral backward compatibility break for methods calling getCurrency() elsewhere.

Metadata

Metadata

Assignees

Labels

Component: TaxFixed in 2.2.xThe issue has been fixed in 2.2 release lineIssue: Clear DescriptionGate 2 Passed. Manual verification of the issue description passedIssue: ConfirmedGate 3 Passed. Manual verification of the issue completed. Issue is confirmedIssue: Format is validGate 1 Passed. Automatic verification of issue format passedIssue: Ready for WorkGate 4. Acknowledged. Issue is added to backlog and ready for developmentReproduced on 2.1.xThe issue has been reproduced on latest 2.1 releaseReproduced on 2.2.xThe issue has been reproduced on latest 2.2 releaseReproduced on 2.3.xThe issue has been reproduced on latest 2.3 releasebug report

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions