solidus_sale_pricing 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.rspec +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +25 -0
- data/LICENSE +26 -0
- data/README.md +173 -0
- data/Rakefile +21 -0
- data/app/assets/javascripts/spree/backend/solidus_sale_pricing.js +2 -0
- data/app/assets/javascripts/spree/frontend/solidus_sale_pricing.js +2 -0
- data/app/assets/stylesheets/spree/backend/solidus_sale_pricing.css +13 -0
- data/app/assets/stylesheets/spree/frontend/solidus_sale_pricing.css +4 -0
- data/app/controllers/spree/admin/product_controller_decorator.rb +10 -0
- data/app/controllers/spree/admin/sale_prices_controller.rb +73 -0
- data/app/helpers/spree/base_helper_decorator.rb +21 -0
- data/app/models/spree/calculator/dollar_amount_sale_price_calculator.rb +12 -0
- data/app/models/spree/calculator/percent_off_sale_price_calculator.rb +12 -0
- data/app/models/spree/price_decorator.rb +93 -0
- data/app/models/spree/product_decorator.rb +49 -0
- data/app/models/spree/sale_price.rb +55 -0
- data/app/models/spree/variant_decorator.rb +65 -0
- data/app/overrides/add_sale_price_to_product_view.rb +12 -0
- data/app/overrides/add_sale_product_admin_tabs.rb +6 -0
- data/app/views/spree/admin/products/_sale_products.html.erb +3 -0
- data/app/views/spree/admin/sale_prices/_form.html.erb +31 -0
- data/app/views/spree/admin/sale_prices/edit.html.erb +17 -0
- data/app/views/spree/admin/sale_prices/index.html.erb +54 -0
- data/app/views/spree/admin/sale_prices/new.html.erb +15 -0
- data/bin/rails +7 -0
- data/config/locales/en.yml +34 -0
- data/config/routes.rb +10 -0
- data/db/migrate/20160622203615_add_spree_create_sale_prices_table.rb +19 -0
- data/lib/generators/solidus_sale_pricing/install/install_generator.rb +31 -0
- data/lib/solidus_sale_pricing.rb +8 -0
- data/lib/solidus_sale_pricing/engine.rb +22 -0
- data/lib/solidus_sale_pricing/factories.rb +6 -0
- data/lib/solidus_sale_pricing/version.rb +18 -0
- data/solidus_sale_pricing.gemspec +38 -0
- data/spec/controllers/sale_prices_controller_spec.rb +56 -0
- data/spec/factories/sale_price.rb +14 -0
- data/spec/models/sale_price_spec.rb +5 -0
- data/spec/spec_helper.rb +95 -0
- metadata +262 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 43f2552daf03ea9d96228ae50b12181d542a6ddc
|
4
|
+
data.tar.gz: d0507eccc04923af5cb7cc8c7b775d3feb8706ec
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a011d0c4fb2e6d7294b15f475eb8e9a4760e55773c4e797bc75ee18d89b49e95b898e29094eafb77eb3160b55fc8ef472aea7ea38f1fc95766f2fa88f73f7cdc
|
7
|
+
data.tar.gz: a5a39ca9f034a2be81c354df766adf22ce0b66925d8f5fc99325a39536e3ab2485de83de53b958af80bbe64b557c9b7562fc64f366a1dbccd1c204c8eee054de
|
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.3.0
|
data/Gemfile
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
branch = ENV.fetch('SOLIDUS_BRANCH', 'master')
|
4
|
+
gem 'solidus', github: 'solidusio/solidus', branch: branch
|
5
|
+
gem 'solidus_auth_devise'
|
6
|
+
|
7
|
+
if branch == 'master' || branch >= 'v2.0'
|
8
|
+
gem 'rails-controller-testing', group: :test
|
9
|
+
else
|
10
|
+
gem 'rails_test_params_backport', group: :test
|
11
|
+
gem 'rails', '~> 4.2.7'
|
12
|
+
end
|
13
|
+
|
14
|
+
gem 'pg'
|
15
|
+
gem 'mysql2'
|
16
|
+
|
17
|
+
group :development, :test do
|
18
|
+
gem 'pry-rails'
|
19
|
+
gem 'pry-byebug'
|
20
|
+
gem 'vcr'
|
21
|
+
gem 'webmock'
|
22
|
+
gem 'timecop'
|
23
|
+
end
|
24
|
+
|
25
|
+
gemspec
|
data/LICENSE
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
Copyright (c) 2016 [name of plugin creator]
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without modification,
|
5
|
+
are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
* Redistributions of source code must retain the above copyright notice,
|
8
|
+
this list of conditions and the following disclaimer.
|
9
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
10
|
+
this list of conditions and the following disclaimer in the documentation
|
11
|
+
and/or other materials provided with the distribution.
|
12
|
+
* Neither the name Spree nor the names of its contributors may be used to
|
13
|
+
endorse or promote products derived from this software without specific
|
14
|
+
prior written permission.
|
15
|
+
|
16
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
17
|
+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
18
|
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
19
|
+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
20
|
+
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
21
|
+
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
22
|
+
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
23
|
+
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
24
|
+
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
25
|
+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
26
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,173 @@
|
|
1
|
+
Solidus Sale Pricing
|
2
|
+
=======================
|
3
|
+
|
4
|
+
Based on https://github.com/jonathandean/spree-sale-pricing.
|
5
|
+
|
6
|
+
New changes
|
7
|
+
===========
|
8
|
+
|
9
|
+
- Added backend interface
|
10
|
+
- Added missing methods
|
11
|
+
|
12
|
+
Solidus Sale Pricing
|
13
|
+
==================
|
14
|
+
|
15
|
+
A Solidus extension (Rails Engine) that lets you set sale prices on products, either by a fixed sale price or a
|
16
|
+
percentage off of the original price. Sale prices have a start date, end date and enabled flag to allow you to schedule
|
17
|
+
sales, have a historical record of sale prices and put sales on hold.
|
18
|
+
|
19
|
+
Installing
|
20
|
+
----------
|
21
|
+
|
22
|
+
In your `Gemfile` add the following for the latest released version:
|
23
|
+
```ruby
|
24
|
+
gem 'solidus_sale_pricing'
|
25
|
+
```
|
26
|
+
|
27
|
+
_OR_ to work from master:
|
28
|
+
```ruby
|
29
|
+
gem 'solidus_sale_pricing', :git => 'git://github.com/jtapia/solidus_sale_pricing.git'
|
30
|
+
```
|
31
|
+
|
32
|
+
Install the Gem:
|
33
|
+
```sh
|
34
|
+
bundle install
|
35
|
+
```
|
36
|
+
|
37
|
+
Copy the migrations in your app:
|
38
|
+
```sh
|
39
|
+
bundle exec rake railties:install:migrations
|
40
|
+
```
|
41
|
+
|
42
|
+
Run database migrations in your app:
|
43
|
+
```sh
|
44
|
+
bundle exec rake db:migrate
|
45
|
+
```
|
46
|
+
|
47
|
+
Usage
|
48
|
+
-----
|
49
|
+
|
50
|
+
Simple example assuming you have a product in your database with the price of $20 and you want to put it on sale
|
51
|
+
immediately for $10:
|
52
|
+
```ruby
|
53
|
+
product = Spree::Product.first
|
54
|
+
|
55
|
+
puts product.price.to_f # => 20.0
|
56
|
+
puts product.on_sale? # => false
|
57
|
+
|
58
|
+
product.put_on_sale 10
|
59
|
+
|
60
|
+
puts product.price.to_f # => 10.0
|
61
|
+
puts product.original_price.to_f # => 20.0
|
62
|
+
puts product.on_sale? # => true
|
63
|
+
```
|
64
|
+
|
65
|
+
By default it uses the supplied Spree::Calculator::DollarAmountSalePriceCalculator which essentially just returns the
|
66
|
+
value you give it as the sale price.
|
67
|
+
|
68
|
+
You can also give a certain percentage off by specifying that you want to use Spree::Calculator::PercentOffSalePriceCalculator.
|
69
|
+
Note that the percentage is given as a float between 0 and 1, not the integer amount from 0 to 100.
|
70
|
+
```ruby
|
71
|
+
product.put_on_sale 0.2, "Spree::Calculator::PercentOffSalePriceCalculator"
|
72
|
+
puts product.price.to_f # => 16.0
|
73
|
+
```
|
74
|
+
|
75
|
+
This extension gives you all of the below methods on both your Products and Variants. If accessed on the Product when reading values,
|
76
|
+
it will return values from your Master variant. If accessed on the Product when writing values, it will by default update
|
77
|
+
all variants including the master variants. If you change the all_variants parameter to false, it will only then write to
|
78
|
+
the master variant and leave the other variants untouched.
|
79
|
+
|
80
|
+
**price** Returns the sale price if currently on sale, the original price if not
|
81
|
+
|
82
|
+
**sale_price** Returns the sale price if currently on sale, nil if not
|
83
|
+
|
84
|
+
**original_price** Always returns the original price
|
85
|
+
|
86
|
+
**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)
|
87
|
+
|
88
|
+
**put\_on\_sale(value[, ...])** Put this item on sale (see below sections for options and more information)
|
89
|
+
|
90
|
+
**create_sale** Alias of ```put_on_sale```
|
91
|
+
|
92
|
+
**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.
|
93
|
+
|
94
|
+
**current_sale** Alias of ```active_sale```
|
95
|
+
|
96
|
+
**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.
|
97
|
+
|
98
|
+
**next_current_sale** Alias of ```next_active_sale```
|
99
|
+
|
100
|
+
**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)
|
101
|
+
|
102
|
+
**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)
|
103
|
+
|
104
|
+
**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)
|
105
|
+
|
106
|
+
**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)
|
107
|
+
|
108
|
+
Since you have these methods available to both your products and variants, it is possible to put the product and all
|
109
|
+
variants on sale or just particular variants. See the explanation of put\_on\_sale below for more information.
|
110
|
+
|
111
|
+
|
112
|
+
Options for put\_on\_sale (create_sale)
|
113
|
+
---------------------------------------
|
114
|
+
```ruby
|
115
|
+
put_on_sale(value, calculator_type = "Spree::Calculator::DollarAmountSalePriceCalculator", all_variants = true, start_at = Time.now, end_at = nil, enabled = true)
|
116
|
+
```
|
117
|
+
**value** (_float_)
|
118
|
+
|
119
|
+
This is either the sale price that you want to sell the product for (if using the default DollarAmountSalePriceCalculator)
|
120
|
+
or the float representation of the percentage off of the original price (between 0 and 1)
|
121
|
+
|
122
|
+
**calculator_type** (_string_) - Default: **"Spree::Calculator::DollarAmountSalePriceCalculator"**
|
123
|
+
|
124
|
+
Specify which calculator to use for determining the sale price. The default calculator will take the value as is and use it
|
125
|
+
as the sale price. You can also pass in another calculator value to determine the sale price differently, such as the
|
126
|
+
provided "Spree::Calculator::PercentOffSalePriceCalculator", which will take a given percentage off of the original
|
127
|
+
price.
|
128
|
+
|
129
|
+
**all_variants** (_boolean_) - Default: **true**
|
130
|
+
|
131
|
+
_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
|
132
|
+
it will only put the master variant on sale. Only change this if you know the implications.
|
133
|
+
|
134
|
+
**start_at** (_DateTime or nil_) - Default: **Time.now**
|
135
|
+
|
136
|
+
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
|
137
|
+
recommended because for future reporting reasons you will probably want to know exactly when the sale started.
|
138
|
+
|
139
|
+
**end_at** (_DateTime or nil_) - Default: **nil**
|
140
|
+
|
141
|
+
Specify the end date of the sale or nil to keep the sale running indefinitely. For future reporting reasons it's recommended
|
142
|
+
to set this at the time you decide to deactivate the sale rather than just setting enabled to false.
|
143
|
+
|
144
|
+
**enabled** (_boolean_) - Default: **true**
|
145
|
+
|
146
|
+
Disable this sale temporarily by setting this to false (overrides the start_at and end_at range). It's not recommended to
|
147
|
+
use this to stop the sale when you decide to end it because it could impact future reporting needs. It's mainly intended
|
148
|
+
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
|
149
|
+
for some reason in the middle of a sale.
|
150
|
+
|
151
|
+
Multiple active sales
|
152
|
+
---------------------
|
153
|
+
|
154
|
+
Technically you can have more than one active sale at a time. However, because
|
155
|
+
Solidus is going to use product.price or
|
156
|
+
variant.price throughout (with no additional parameters or means to identify a particular sale), we have to consistently
|
157
|
+
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
|
158
|
+
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
|
159
|
+
currently running one, you just add a new active sale. Then when that new sale ends, the old sale will be in effect again
|
160
|
+
(provided it's still active, of course.) So you can add more than one active sale but only one will actually be used at
|
161
|
+
a given time.
|
162
|
+
|
163
|
+
Testing
|
164
|
+
-------
|
165
|
+
|
166
|
+
Tests are in progress, so there aren't any yet. I know, TDD, blah blah blah.
|
167
|
+
|
168
|
+
Be sure to bundle your dependencies and then create a dummy test app for the specs to run against.
|
169
|
+
```sh
|
170
|
+
$ bundle
|
171
|
+
$ bundle exec rake test app
|
172
|
+
$ bundle exec rspec spec
|
173
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
require 'spree/testing_support/extension_rake'
|
6
|
+
|
7
|
+
RSpec::Core::RakeTask.new
|
8
|
+
|
9
|
+
task :default do
|
10
|
+
if Dir["spec/dummy"].empty?
|
11
|
+
Rake::Task[:test_app].invoke
|
12
|
+
Dir.chdir("../../")
|
13
|
+
end
|
14
|
+
Rake::Task[:spec].invoke
|
15
|
+
end
|
16
|
+
|
17
|
+
desc 'Generates a dummy app for testing'
|
18
|
+
task :test_app do
|
19
|
+
ENV['LIB_NAME'] = 'solidus_sale_pricing'
|
20
|
+
Rake::Task['extension:test_app'].invoke
|
21
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
/*
|
2
|
+
Placeholder manifest file.
|
3
|
+
the installer will append this file to the app vendored assets here: 'vendor/assets/stylesheets/spree/backend/all.css'
|
4
|
+
*/
|
5
|
+
|
6
|
+
table th .actions [class*='fa-'].stop:hover, td .actions [class*='fa-'].stop:hover {
|
7
|
+
background-color: #C60F13;
|
8
|
+
color: #FFFFFF;
|
9
|
+
}
|
10
|
+
|
11
|
+
table th .actions [class*='fa-'].stop:active, td .actions [class*='fa-'].stop:active {
|
12
|
+
color: #C60F13;
|
13
|
+
}
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Spree
|
2
|
+
module Admin
|
3
|
+
class SalePricesController < ResourceController
|
4
|
+
before_action :load_data
|
5
|
+
before_action :load_sale_price, only: [:update, :destroy]
|
6
|
+
|
7
|
+
def index
|
8
|
+
@sale_prices = @product.sale_prices
|
9
|
+
end
|
10
|
+
|
11
|
+
def create
|
12
|
+
begin
|
13
|
+
@product.create_sale(sale_price_params)
|
14
|
+
flash[:success] = Spree.t(:sale_price_successfully_created)
|
15
|
+
redirect_to admin_product_sale_prices_path(@product)
|
16
|
+
rescue => e
|
17
|
+
flash[:error] = Spree.t(:error_on_create)
|
18
|
+
render :new
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def update
|
23
|
+
if @sale_price.update(sale_price_params)
|
24
|
+
flash.now[:success] = Spree.t(:sale_price_successfully_updated)
|
25
|
+
else
|
26
|
+
flash.now[:error] = Spree.t(:error_on_update)
|
27
|
+
end
|
28
|
+
|
29
|
+
render :edit
|
30
|
+
end
|
31
|
+
|
32
|
+
def stop
|
33
|
+
if @product.stop_sale
|
34
|
+
flash[:success] = Spree.t(:sale_price_stopped)
|
35
|
+
|
36
|
+
respond_with(@product) do |format|
|
37
|
+
format.html { redirect_to admin_product_sale_prices_path(@product) }
|
38
|
+
format.js { redirect_to admin_product_sale_prices_path(@product) }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def enable
|
44
|
+
if @product.enable_sale
|
45
|
+
flash[:success] = Spree.t(:sale_price_enabled)
|
46
|
+
|
47
|
+
respond_with(@product) do |format|
|
48
|
+
format.html { redirect_to admin_product_sale_prices_path(@product) }
|
49
|
+
format.js { redirect_to admin_product_sale_prices_path(@product) }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def sale_price_params
|
57
|
+
params.require(:sale_price).permit(permitted_sale_price_attributes)
|
58
|
+
end
|
59
|
+
|
60
|
+
def permitted_sale_price_attributes
|
61
|
+
[ :value, :start_at, :end_at, :caclulator_type ]
|
62
|
+
end
|
63
|
+
|
64
|
+
def load_data
|
65
|
+
@product ||= Spree::Product.friendly.find(params[:product_id])
|
66
|
+
end
|
67
|
+
|
68
|
+
def load_sale_price
|
69
|
+
@sale_price ||= Spree::SalePrice.find(params[:id])
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
Spree::BaseHelper.class_eval do
|
2
|
+
def display_original_price(product_or_variant)
|
3
|
+
product_or_variant.original_price_in(current_currency).display_price.to_html
|
4
|
+
end
|
5
|
+
|
6
|
+
def display_discount_percent(product_or_variant, append_text = 'Off')
|
7
|
+
discount = product_or_variant.discount_percent_in current_currency
|
8
|
+
|
9
|
+
# number_to_percentage(discount, precision: 0).to_html
|
10
|
+
|
11
|
+
if discount > 0
|
12
|
+
return "#{number_to_percentage(discount, precision: 0).to_html} #{append_text}"
|
13
|
+
else
|
14
|
+
return ''
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def format_date date
|
19
|
+
date.strftime('%Y-%m-%dT%H:%M:%S') if date
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Spree
|
2
|
+
class Calculator::DollarAmountSalePriceCalculator < Spree::Calculator
|
3
|
+
# TODO validate that the sale price is less than the original price
|
4
|
+
def self.description
|
5
|
+
'Calculates the sale price for a Variant by returning the provided fixed sale price'
|
6
|
+
end
|
7
|
+
|
8
|
+
def compute(sale_price)
|
9
|
+
sale_price.value
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Spree
|
2
|
+
class Calculator::PercentOffSalePriceCalculator < Spree::Calculator
|
3
|
+
# TODO validate that the sale price is between 0 and 1
|
4
|
+
def self.description
|
5
|
+
'Calculates the sale price for a Variant by taking off a percentage of the original price'
|
6
|
+
end
|
7
|
+
|
8
|
+
def compute(sale_price)
|
9
|
+
(1.0 - sale_price.value.to_f) * sale_price.variant.original_price.to_f
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
Spree::Price.class_eval do
|
2
|
+
has_many :sale_prices
|
3
|
+
|
4
|
+
# TODO also accept a class reference for calculator type instead of only a string
|
5
|
+
def put_on_sale(attrs={})
|
6
|
+
new_sale(attrs).save!
|
7
|
+
end
|
8
|
+
alias :create_sale :put_on_sale
|
9
|
+
|
10
|
+
def new_sale(attrs={})
|
11
|
+
sale_price = sale_prices.new({
|
12
|
+
value: attrs[:value],
|
13
|
+
start_at: attrs[:start_at] || Time.now,
|
14
|
+
end_at: attrs[:end_at],
|
15
|
+
enabled: attrs[:enabled] || true
|
16
|
+
})
|
17
|
+
sale_price.calculator_type = attrs[:calculator_type] || 'Spree::Calculator::DollarAmountSalePriceCalculator'
|
18
|
+
sale_price
|
19
|
+
end
|
20
|
+
|
21
|
+
def active_sale
|
22
|
+
on_sale? ? first_sale(sale_prices.active) : nil
|
23
|
+
end
|
24
|
+
alias :current_sale :active_sale
|
25
|
+
|
26
|
+
def next_active_sale
|
27
|
+
sale_prices.present? ? first_sale(sale_prices) : nil
|
28
|
+
end
|
29
|
+
alias :next_current_sale :next_active_sale
|
30
|
+
|
31
|
+
def sale_price
|
32
|
+
on_sale? ? active_sale.price : nil
|
33
|
+
end
|
34
|
+
|
35
|
+
def sale_price=(value)
|
36
|
+
on_sale? ? active_sale.update_attribute(:value, value) : put_on_sale(value)
|
37
|
+
end
|
38
|
+
|
39
|
+
def discount_percent
|
40
|
+
on_sale? ? (1 - (sale_price / original_price)) * 100 : 0.0
|
41
|
+
end
|
42
|
+
|
43
|
+
def on_sale?
|
44
|
+
sale_prices.active.present? && first_sale(sale_prices.active).value != original_price
|
45
|
+
end
|
46
|
+
|
47
|
+
def original_price
|
48
|
+
self[:amount]
|
49
|
+
end
|
50
|
+
|
51
|
+
def original_price=(value)
|
52
|
+
self.price = value
|
53
|
+
end
|
54
|
+
|
55
|
+
def price
|
56
|
+
on_sale? ? sale_price : original_price
|
57
|
+
end
|
58
|
+
|
59
|
+
def amount
|
60
|
+
price
|
61
|
+
end
|
62
|
+
|
63
|
+
def enable_sale
|
64
|
+
return nil unless next_active_sale.present?
|
65
|
+
next_active_sale.enable
|
66
|
+
end
|
67
|
+
|
68
|
+
def disable_sale
|
69
|
+
return nil unless active_sale.present?
|
70
|
+
active_sale.disable
|
71
|
+
end
|
72
|
+
|
73
|
+
def start_sale(end_time = nil)
|
74
|
+
return nil unless next_active_sale.present?
|
75
|
+
next_active_sale.start(end_time)
|
76
|
+
end
|
77
|
+
|
78
|
+
def stop_sale
|
79
|
+
return nil unless active_sale.present?
|
80
|
+
active_sale.stop
|
81
|
+
end
|
82
|
+
|
83
|
+
def update_sale(attrs)
|
84
|
+
return nil unless active_sale.present?
|
85
|
+
active_sale.update(attrs)
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def first_sale(scope)
|
91
|
+
scope.order("created_at DESC").first
|
92
|
+
end
|
93
|
+
end
|