silvershop / silvershop-core

SilverShop is an e-commerce shopping cart module for the SilverStripe CMS
http://silvershop.github.io
BSD 2-Clause "Simplified" License
113 stars 119 forks source link

Multi-Currency support #621

Open bummzack opened 6 years ago

bummzack commented 6 years ago

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

bummzack commented 6 years ago

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

bummzack commented 6 years ago

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.

bummzack commented 6 years ago

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

Roadmap: I'm looking for sponsors or contributors for this one. Any takers?

a2nt commented 6 years ago

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

bummzack commented 6 years ago

@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?

sanderha commented 6 years ago

+1 for DataObject based price objects. A UI for this might be using inline editing in a GridField.

a2nt commented 6 years ago

@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

screen shot 2017-10-25 at 4 22 38 pm

sanderha commented 6 years ago

@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)

a2nt commented 6 years ago

@sanderha sure we work on it together)

sfimedia commented 5 months ago

What happened to the multi currency update?