Spree Sales

Code Climate

Build Status

Add sales prices to products and variants


Add spree_sales to your Gemfile:

gem 'spree_sales', github: 'ronzalo/spree_sales', branch: 'master'

Bundle your dependencies and run the installation generator:

bundle exec rails g spree_sales:install


At the moment there is only a Ruby interface because I haven't had time to make an admin interface yet. I hope to be able
to get to that soon.

Complete admin interface is available!

Simple example assuming you have a product in your database with the price of $20 and you want to put it on sale
immediately for $10:

product = Spree::Product.first # or variant

puts product.price.to_f              # => 20.0
puts product.on_sale?                # => false

product.put_on_sale 10

puts product.price.to_f              # => 10.0
puts product.original_price.to_f     # => 20.0
puts product.on_sale?                # => true

This extension gives you all of the below methods on both your Products and Variants. If accessed on the Product when reading values,
it will return values from your Master variant. If accessed on the Product when writing values, it will by default update
all variants including the master variants. If you change the all_variants parameter to false, it will only then write to
the master variant and leave the other variants untouched.

price Returns the sale price if currently on sale, the original price if not

sale_price Returns the sale price if currently on sale, nil if not

original_price Always returns the original price

on_sale? Return a boolean indication if it is currently on sale (enabled is set to true and we are currently within the active date range)

put_on_sale(value[, ...]) Put this item on sale (see below sections for options and more information)

create_sale Alias of put_on_sale

active_sale Returns the currently active sale (Spree::SalePrice object) that price and sale_price will use. If there is more than one potentially active sale, the one with the latest created_at timestamp is used. See the section on "Multiple active sales" for the reasoning behind that.

current_sale Alias of active_sale

next_active_sale Currently returns the latest created Spree::SalePrice object (active or not.) The name is kind of misleading so it should probably be changed. We may also want to make this only return the latest created inactive sale, since that's kind of the original intention of it to be used inside of enable_sale and start_sale. Needs more thought.

next_current_sale Alias of next_active_sale

enable_sale(all_variants = true) Enable the sale returned by next_active_sale by setting that Spree::SalePrice object's enabled flag to true. Does not change the start and end dates so it does not necessary mean that the sale will then become active. Therefore, you can enable a sale in this manner and still have it not take effect on the site. (Use start_sale for that) Note: The all_variants flag is only available on Spree::Product (not on Spree::Variant)

disable_sale(all_variants = true) Disable the sale returned by active_sale by setting that Spree::SalePrice object's enabled flag to false. This always makes the sale inactive, regardless of the date range. Note: The all_variants flag is only available on Spree::Product (not on Spree::Variant)

start_sale(end_time = nil, all_variants = true) Start the sale returned by next_active_sale (and make it active) by setting that Spree::SalePrice object's enabled flag to true and ensuring that the current time is in between the start and end dates. Note: The all_variants flag is only available on Spree::Product (not on Spree::Variant)

stop_sale(all_variants = true) Stop the sale returned by active_sale by setting that Spree::SalePrice object's enabled flag to false and the end date to the current time. Note: The all_variants flag is only available on Spree::Product (not on Spree::Variant)

Since you have these methods available to both your products and variants, it is possible to put the product and all
variants on sale or just particular variants. See the explanation of put_on_sale below for more information.

Options for put_on_sale (create_sale)

put_on_sale(value, calculator_type = "Spree::Calculator::DollarAmountSalePriceCalculator", all_variants = true, start_at = Time.now, end_at = nil, enabled = true)

value (float)

This is either the sale price that you want to sell the product for (if using the default DollarAmountSalePriceCalculator)
or the float representation of the percentage off of the original price (between 0 and 1)

calculator_type (string) - Default: "Spree::Calculator::DollarAmountSalePriceCalculator"

Specify which calculator to use for determining the sale price. The default calculator will take the value as is and use it
as the sale price. You can also pass in another calculator value to determine the sale price differently, such as the
provided "Spree::Calculator::PercentOffSalePriceCalculator", which will take a given percentage off of the original

all_variants (boolean) - Default: true

Only for Spree::Product. By default it set all of variants (including the master variant) for the product on sale. If you change this value to false
it will only put the master variant on sale. Only change this if you know the implications.

start_at (DateTime or nil) - Default: Time.now

Specify the date and time that the sale takes effect. By default it uses the current time. It can also be nil but it's not
recommended because for future reporting reasons you will probably want to know exactly when the sale started.

end_at (DateTime or nil) - Default: nil

Specify the end date of the sale or nil to keep the sale running indefinitely. For future reporting reasons it's recommended
to set this at the time you decide to deactivate the sale rather than just setting enabled to false.

enabled (boolean) - Default: true

Disable this sale temporarily by setting this to false (overrides the start_at and end_at range). It's not recommended to
use this to stop the sale when you decide to end it because it could impact future reporting needs. It's mainly intended
to keep the sale disabled while you are still working on it and it isn't quite ready, or if you need to disable temporarily
for some reason in the middle of a sale.

Multiple active sales

Technically you can have more than one active sale at a time. However, because Spree is going to use product.price or
variant.price throughout (with no additional parameters or means to identify a particular sale), we have to consistently
work with a single sale price so that the customer is always charged the same price as they see on the site. What we do then
is always take the last created sale price. We do this so that if you want to temporarily make a new sale to override a
currently running one, you just add a new active sale. Then when that new sale ends, the old sale will be in effect again
(provided it's still active, of course.) So you can add more than one active sale but only one will actually be used at
a given time.


Be sure to bundle your dependencies and then create a dummy test app for the specs to run against.

bundle exec rake test_app
bundle exec rspec spec

When testing your applications integration with this extension you may use it's factories.
Simply add this require statement to your spec_helper:

require 'spree_sales/factories'


Update docs, add more tests.

Forked from @jonathandean https://github.com/jonathandean/spree-sale-pricing
Improved by all contributors.

Copyright (c) 2014 Gonzalo Moreno https://github.com/acidlabs/, released under the New BSD License


No comments yet

Please login to add comment.