money icon indicating copy to clipboard operation
money copied to clipboard

Change currency rate on the fly

Open dalholm opened this issue 2 years ago • 3 comments

Before I start something unnecessary, I thought I would check if there is a good way to update an exchange rate on the fly?

Use case You have an order with a specific exchange rate. A couple of days later, the course has changed and you want to present what they paid.

Note, In my system, all prices are saved in a base currency.

An idea would be something similar

currency()->updateRate(1.0);

dalholm avatar Dec 30 '21 11:12 dalholm

Of course there was a smarter solution for this! :)

$newRate = 4.0
$money = money(100)->convertTo(
  new \ArchTech\Money\Currency('EUR', 'Euro', $newRate, '', 'EUR')
);

// $money->value(); // should now be 400

dalholm avatar Dec 30 '21 12:12 dalholm

A couple of days later, the course has changed and you want to present what they paid.

In this case the order should store the exchange rate that was used at the time of purchase.

I see your point about changing the currency rate to format the value correctly. Hmm. Right now the package expects Money to be created with a currency code, and the actual currency is stored in the CurrencyManager.

Updating the rate for the entire currency would be wrong here, because you could have e.g. a navbar with the cart total, and you only want to change how some value is rendered on a specific part of the page (like a "show order" table).

The code you just posted seems to be a good solution. I checked the Currency class and it doesn't register the currency into CurrencyManager automatically, so it won't override the global EUR value.

But perhaps we could add something like:

money(100)->convertTo(EUR::class, rate: $newRate);

As in, the ability to specify overrides after the currency code. I'll re-open this issue to consider that in the future.

I'd also advise storing the value that the user actually paid, and converting that to the base currency if needed. There probably isn't a difference when you handle the math well, but it seems like a better practice to store what the user actually paid rather than having to compute it from some other value.

stancl avatar Dec 30 '21 12:12 stancl

The code you just posted seems to be a good solution. I checked the Currency class and it doesn't register the currency into CurrencyManager automatically, so it won't override the global EUR value.

But perhaps we could add something like:

money(100)->convertTo(EUR::class, rate: $newRate);

That solution would be really nice, no need for duplication then. :)

As in, the ability to specify overrides after the currency code. I'll re-open this issue to consider that in the future.

I'd also advise storing the value that the user actually paid, and converting that to the base currency if needed. There probably isn't a difference when you handle the math well, but it seems like a better practice to store what the user actually paid rather than having to compute it from some other value.

I save the current rate of what the customer paid. In my case i store everything in SEK with a currency rate. Its easier to maintain and report statistics on.

To make this possible i use cast for returning a DataType class with this money package and some custom functions. e.g:

// your model

protected $casts = ['price' => MoneyCast::class]; // Returns a DataType with logical stuff in it
// Usage
$item->price->value; // Original value, i do all the calculations on
$item->price->currencyValue; // Calculated currency value (used for payments etc)
$item->price->formatted; // Formatted in selected currency or related currency and rate
// and more

dalholm avatar Dec 30 '21 13:12 dalholm