silvershop-core
silvershop-core copied to clipboard
Multi-Currency support
Multi-Currency
Shop currently operates with just one currency. Having a good support for multi-currency was requested multiple times already. Since support for multi-currency touches on many core elements of the shop, it's best to integrate this functionality directly into core.
The goal of this issue is to find a good architecture to implement multi-currency into shop.
Basic architecture
With only a few currencies, a shop owner might prefer to be able to set prices per currency, to have better control over the prices being displayed in shop. With many currencies, you'd most likely want your prices to adjust based on conversion-rates instead.
The basic idea is to always have the prices in the "master" currency and use helper-classes to convert to the other supported currencies. This will also make it easier to transition from one model (separate prices) to the other (conversion-rates).
Price conversions should be performed by specialised/reusable classes, that can be individually tested and also used by module-developers. It should be simple to swap conversion classes with another implementation (eg. via config).
Calculated prices (eg. the Order-Total, Tax etc.) should always be stored in the order-currency and be recalculated if the currency changes.
General considerations
- Keep as much of the current API/Datamodel intact as possible
- The currency needs to be stored with every Order (if a conversion-rate was applied, this might have to be stored with Order as well)
- There might be a need to convert from a currency value back to the master-currency (reports come to mind)
- All instances of floating-point math should be eliminated and replaced with bc-math
Separately defined prices per product
The Product shouldn't get any additional field (easier upgrade-path).
Every additional price/currency will be added as a has_many relation to product (Product has_many Prices). Each price will be added as Money field (eg. Amount and Currency)
Product->getPrice should return the price in the current currency, as Money DBField.
Implications/Risks
Potentially affects a lot of other areas/modules of shop as well. Basically everything that operates with fixed prices, such as shipping, discounts etc.
UX
In the CMS: If product-prices should be defined per currency, an input-field for every currency is needed. Ideally, this would be some sort of a combined field that also highlights if a currency value is missing.
Questions
- What's a good UI to enter prices in different currencies
- @wilr What about the discounts module? I guess this would have a severe impact on that one?
Currency conversion
Convert prices to other currencies via conversion-rates. The conversion-rates could be hard-coded in config files, or looked up via an existing API (with caching to always have rates available, even when the API is down).
To support different APIs, there should be some sort of a "service" class, that can be easily swapped via config.
Possible APIs
Implications/Risks
Should be quite straightforward. Orders would ideally store the conversion-rate used for calculation, so that calculations back to the "master" currency become possible.
UX
In the frontend: Maybe additional formatting/rounding rules could be defined per currency, so that converted prices look nicer (conversion must then happen on the individual units, not the total).
When conversion-rates are being pulled from an API, it's important that these stay the same for the duration of an ordering-process. So that conversion-rates aren't updated during checkout. The worst case scenario would be an update to the currency-rate, when the order gets finalized. That's another reason to store the conversion-rate with each Order.
Roadmap & Implementation
Since this will most likely break existing APIs, this feature has to go into a new major release. We're going to have to create a new major release for SS 4 compatibility anyway. Question: Should this feature be available for SS3 or go straight into the SS4 release? Vote on this comment with
- :tada: for SS4
- ❤️ for SS3.
Roadmap: I'm looking for sponsors or contributors for this one. Any takers?
We can share an extension module that auto-creates DB fields by yml specified currencies. It calculates totals and displays prices in domain specified currency
@a2nt That would be great. Is it already open-source? It seems like you've implemented the use-case of separately defined prices per currency? How did you solve the problems such as Shipping-Costs or Discounts?
+1 for DataObject based price objects. A UI for this might be using inline editing in a GridField.
@bummzack need to separate it from the other parts of our project, document it and test it separately and we will share it with open source community next week.
Shipping-Costs and Discounts comes from extended functionality of these shop objects.
As you can see currency maybe set to a specific domain name or few currencies with currency selector maybe done:
ProductMultiCurrency:
default_currency: 'USD'
domains:
localdev.dev:
currencies:
- USD
- EUR
localdev-se.dev:
currencies:
- SEK

@a2nt Oh wow, your code looks pretty much identical to a module we started a few days ago https://github.com/kreationsbyran/silvershop-multicurrency (the yml setup)
@sanderha sure we work on it together)
What happened to the multi currency update?