Solidus is known for its flexibility, but some stores need special rules to chose what price to use. Hence, it might depend on roles, quantity, location, etc. We'll guide you on how Solidus works internally to find the right price and how can be modified at the configuration level. No more monkey-patching!
Solidus pricing: internals
Solidus is very flexible when it comes to deciding what price to use for a certain product/variant. It has a super simple mechanism to delegate everything to two classes:
Spree::Variant::PricingOptions
Spree::Variant::PricingSelector
And both are controlled by the following setting:
Spree::Config.variant_price_selector_class
Spree::Variant::PricingSelector
has hardcoded what pricing option it needs.
The first thing is to have a look at the Spree::Core::ControllerHelpers::Pricing module, that's where the helper to find the right options is located. If you find the code, it looks like this:
def current_pricing_options
Spree::Config.pricing_options_class.from_context(self)
end
That current_pricing_options is used everywhere to find the price, and even for building cache keys, etc. For example:
# display price helper
def display_price(product_or_variant)
product_or_variant.price_for(current_pricing_options).to_html
end
# from the views
- cache [I18n.locale, current_pricing_options, @product] do
= display_price(@product)
What can we do with this flexibility?
Endless possible combinations, some examples would be:
- Pricing levels
- Pricing per roles
- Wholesale prices
- Employee discounts
- Sale prices
- Prices by season
- Etc
Pricing levels
Let's say you want to have different pricing levels depending on how often your clients buy, or special arrangements from your sales department. Then, all you need to do is the following:
- Add a new Pricing Level column to the Users and Prices tables
- Modify in the backend the User and Price forms to include a new column
- Create a new Pricing Options module to include Pricing Level and its defaults
- Create a new Pricing Selector module to find prices based on the new Pricing Level
The code might look like this:
module Store
module Variant
class PricingOptions < ::Spree::Variant::PricingOptions
def self.default_price_attributes
{
currency: Spree::Config.currency,
country_iso: Spree::Config.admin_vat_country_iso,
pricing_Level: 1
}
end
def self.from_context(context)
new(
currency: context.current_store.try!(:default_currency).presence || Spree::Config[:currency],
country_iso: context.current_store.try!(:cart_tax_country_iso).presence
pricing_level: context.spree_current_user.try(:pricing_Level).presence || 1
)
end
end
end
end
module Store
module Variant
class PriceSelector < ::Spree::Variant::PriceSelector
def self.pricing_options_class
Store::Variant::PricingOptions
end
def price_for(price_options)
variant.currently_valid_prices.detect do |price|
( price.country_iso == price_options.desired_attributes[:country_iso] || price.country_iso.nil? ) &&
price.currency == price_options.desired_attributes[:currency] &&
price.pricing_level == price_options.desired_attributes[:pricing_level]
end.try!(:money)
end
end
end
# Specify what pricing selector to use in spree initializer:
Spree.config do |config|
config.variant_price_selector_class = 'Store::Variant::PriceSelector'
end
And that's it! Solidus takes care of the rest.
As you can see, it is very easy to customize how to select what price to use in Solidus. If you still need some help customizing your Solidus, you can contact us and we will be more than happy to help!
Thanks for reading!