spree_promo 0.60.6 → 0.70.RC1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +26 -0
- data/app/assets/javascripts/admin/promotions.js +131 -0
- data/app/assets/javascripts/admin/spree_promo.js +3 -0
- data/app/assets/javascripts/store/spree_promo.js +2 -0
- data/{public → app/assets}/stylesheets/admin/promotions.css +19 -29
- data/app/assets/stylesheets/admin/spree_promo.css +5 -0
- data/app/assets/stylesheets/store/spree_promo.css +4 -0
- data/app/controllers/admin/promotion_actions_controller.rb +28 -0
- data/app/controllers/admin/promotions_controller.rb +2 -2
- data/app/controllers/content_controller_decorator.rb +8 -0
- data/app/controllers/orders_controller_decorator.rb +14 -5
- data/app/models/adjustment_decorator.rb +3 -0
- data/app/models/order_decorator.rb +24 -0
- data/app/models/product_decorator.rb +10 -0
- data/app/models/product_group_decorator.rb +3 -0
- data/app/models/promotion.rb +61 -41
- data/app/models/promotion/actions/create_adjustment.rb +28 -0
- data/app/models/promotion/actions/create_line_items.rb +29 -0
- data/app/models/promotion/rules/first_order.rb +1 -1
- data/app/models/promotion/rules/item_total.rb +3 -2
- data/app/models/promotion/rules/landing_page.rb +17 -0
- data/app/models/promotion/rules/product.rb +1 -1
- data/app/models/promotion/rules/user.rb +1 -1
- data/app/models/promotion/rules/user_logged_in.rb +13 -0
- data/app/models/promotion_action.rb +19 -0
- data/app/models/promotion_action_line_item.rb +6 -0
- data/app/models/promotion_rule.rb +3 -16
- data/app/overrides/promo_admin_tabs.rb +5 -0
- data/app/overrides/promo_coupon_code_field.rb +5 -0
- data/app/overrides/promo_product_properties.rb +5 -0
- data/app/views/admin/promotion_actions/create.js.erb +7 -0
- data/app/views/admin/promotion_actions/destroy.js.erb +2 -0
- data/app/views/admin/promotions/_actions.html.erb +32 -0
- data/app/views/admin/promotions/_form.html.erb +17 -17
- data/app/views/admin/promotions/_promotion_action.html.erb +12 -0
- data/app/views/admin/promotions/_promotion_rule.html.erb +2 -2
- data/app/views/admin/promotions/_rules.html.erb +32 -0
- data/app/views/admin/promotions/actions/_create_adjustment.html.erb +19 -0
- data/app/views/admin/promotions/actions/_create_line_items.html.erb +52 -0
- data/app/views/admin/promotions/actions/_give_store_credit.html.erb +6 -0
- data/app/views/admin/promotions/edit.html.erb +4 -38
- data/app/views/admin/promotions/index.html.erb +5 -7
- data/app/views/admin/promotions/rules/_landing_page.html.erb +8 -0
- data/app/views/admin/promotions/rules/_user_logged_in.html.erb +3 -0
- data/app/views/checkout/_coupon_code_field.html.erb +1 -1
- data/config/locales/en.yml +27 -5
- data/config/routes.rb +1 -0
- data/db/migrate/20101026184833_migrate_adjustments.rb +9 -0
- data/db/migrate/20110331094351_promotion_changes_to_subclass_of_activator.rb +22 -0
- data/db/migrate/20110404123358_create_promotion_actions.rb +13 -0
- data/db/migrate/20110601202923_create_promotion_action_line_items.rb +12 -0
- data/lib/spree_promo.rb +24 -100
- metadata +77 -26
- data/app/models/promotion_credit.rb +0 -9
- data/db/migrate/20100419194457_fix_existing_coupon_credits.rb +0 -9
- data/lib/spree_promo_hooks.rb +0 -11
- data/lib/tasks/install.rake +0 -26
- data/public/javascripts/admin/promotions.js +0 -26
data/LICENSE
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
Copyright (c) 2007-2010, Rails Dog LLC and other contributors
|
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.
|
@@ -0,0 +1,131 @@
|
|
1
|
+
|
2
|
+
var initProductRuleSourceField = function(){
|
3
|
+
|
4
|
+
$products_source_field = jQuery('.products_rule_products_source_field input');
|
5
|
+
$products_source_field.click(function() {
|
6
|
+
$rule_container = jQuery(this).parents('.promotion-rule');
|
7
|
+
if(this.checked){
|
8
|
+
if(this.value == 'manual'){
|
9
|
+
$rule_container.find('.products_rule_products').show();
|
10
|
+
$rule_container.find('.products_rule_product_group').hide();
|
11
|
+
} else {
|
12
|
+
$rule_container.find('.products_rule_products').hide();
|
13
|
+
$rule_container.find('.products_rule_product_group').show();
|
14
|
+
}
|
15
|
+
}
|
16
|
+
});
|
17
|
+
$products_source_field.each(function() {
|
18
|
+
$(this).triggerHandler('click');
|
19
|
+
});
|
20
|
+
|
21
|
+
};
|
22
|
+
|
23
|
+
var initProductActions = function(){
|
24
|
+
|
25
|
+
$('.calculator-fields').each(function(){
|
26
|
+
var $fields_container = $(this);
|
27
|
+
var $type_select = $fields_container.find('.type-select');
|
28
|
+
var $settings = $fields_container.find('.settings');
|
29
|
+
var $warning = $fields_container.find('.warning');
|
30
|
+
var originalType = $type_select.val();
|
31
|
+
|
32
|
+
$warning.hide();
|
33
|
+
$type_select.change(function(){
|
34
|
+
if( $(this).val() == originalType ){
|
35
|
+
$warning.hide();
|
36
|
+
$settings.show();
|
37
|
+
$settings.find('input').removeAttr('disabled');
|
38
|
+
} else {
|
39
|
+
$warning.show();
|
40
|
+
$settings.hide();
|
41
|
+
$settings.find('input').attr('disabled', 'disabled');
|
42
|
+
}
|
43
|
+
});
|
44
|
+
});
|
45
|
+
|
46
|
+
|
47
|
+
//
|
48
|
+
// CreateLineItems Promotion Action
|
49
|
+
//
|
50
|
+
( function(){
|
51
|
+
// Autocomplete product and populate variant select
|
52
|
+
if($('.promotion_action.create_line_items ').is('*')){
|
53
|
+
$(".promotion_action.create_line_items input[name='add_product_name']").autocomplete("/admin/products.json?authenticity_token=" + $('meta[name=csrf-token]').attr("content"), {
|
54
|
+
parse: prep_autocomplete_data,
|
55
|
+
formatItem: function(item) {
|
56
|
+
return format_autocomplete(item);
|
57
|
+
}
|
58
|
+
}).result(function(event, data, formatted) {
|
59
|
+
if(data){
|
60
|
+
// $('#add_product_id').val(data.product.id);
|
61
|
+
var url = "/admin/products/" + data.product.permalink + "/variants.json?authenticity_token=" + $('meta[name=csrf-token]').attr("content");
|
62
|
+
var $variant_select = $("select[name='add_line_item_variant_id']");
|
63
|
+
$variant_select.html('');
|
64
|
+
$.getJSON(url, {}, function(variants_data){
|
65
|
+
$.each(variants_data, function(){
|
66
|
+
$variant_select.append($("<option />").val(this.id).text(this.label));
|
67
|
+
});
|
68
|
+
});
|
69
|
+
}
|
70
|
+
}
|
71
|
+
);
|
72
|
+
}
|
73
|
+
|
74
|
+
var hideOrShowItemTables = function(){
|
75
|
+
$('.promotion_action table').each(function(){
|
76
|
+
if($(this).find('td').length == 0){
|
77
|
+
$(this).hide();
|
78
|
+
} else {
|
79
|
+
$(this).show();
|
80
|
+
}
|
81
|
+
});
|
82
|
+
};
|
83
|
+
hideOrShowItemTables();
|
84
|
+
|
85
|
+
// Remove line item
|
86
|
+
var setupRemoveLineItems = function(){
|
87
|
+
$(".promotion_action.create_line_items table img").unbind('click').click(function(){
|
88
|
+
var $container = $(this).parents('.promotion_action');
|
89
|
+
var $hiddenField = $container.find("input[type='hidden']");
|
90
|
+
var $row = $(this).parents('tr');
|
91
|
+
var index = $row.parents('table').find('tr').index($row.get(0));
|
92
|
+
// Remove variant_id quantity pair from the string
|
93
|
+
var items = _($hiddenField.val().split(',')).compact();
|
94
|
+
items.splice(index - 1, 1);
|
95
|
+
$hiddenField.val(items.join(','));
|
96
|
+
$(this).parents('tr').remove();
|
97
|
+
hideOrShowItemTables();
|
98
|
+
});
|
99
|
+
};
|
100
|
+
setupRemoveLineItems();
|
101
|
+
// Add line item to list
|
102
|
+
$(".promotion_action.create_line_items button.add").click(function(){
|
103
|
+
var $container = $(this).parents('.promotion_action');
|
104
|
+
var product_name = $container.find("input[name='add_product_name']").val();
|
105
|
+
var variant_id = $container.find("select[name='add_line_item_variant_id']").val();
|
106
|
+
var variant_name = $container.find("select[name='add_line_item_variant_id'] option:selected").text();
|
107
|
+
var quantity = $container.find("input[name='add_quantity']").val();
|
108
|
+
if(variant_id){
|
109
|
+
// Add to the table
|
110
|
+
var newRow = "<tr><td>" + product_name + "</td><td>" + variant_name + "</td><td>" + quantity + "</td><td><img src='/admin/images/icons/cross.png' /></td></tr>";
|
111
|
+
$container.find('table').append(newRow);
|
112
|
+
// Add to serialized string in hidden text field
|
113
|
+
var $hiddenField = $container.find("input[type='hidden']");
|
114
|
+
$hiddenField.val($hiddenField.val() + "," + variant_id + "x" + quantity);
|
115
|
+
setupRemoveLineItems();
|
116
|
+
hideOrShowItemTables();
|
117
|
+
}
|
118
|
+
return false;
|
119
|
+
});
|
120
|
+
|
121
|
+
} )();
|
122
|
+
|
123
|
+
}
|
124
|
+
|
125
|
+
$(document).ready(function() {
|
126
|
+
initProductRuleSourceField();
|
127
|
+
initProductActions();
|
128
|
+
});
|
129
|
+
|
130
|
+
|
131
|
+
|
@@ -1,4 +1,4 @@
|
|
1
|
-
.
|
1
|
+
.promotion_rule, .promotion_action {
|
2
2
|
-moz-border-radius: 5px;
|
3
3
|
-webkit-border-radius: 5px;
|
4
4
|
padding: 8px 40px 8px 10px;
|
@@ -7,48 +7,38 @@
|
|
7
7
|
margin-bottom: 4px;
|
8
8
|
position: relative;
|
9
9
|
}
|
10
|
-
.
|
10
|
+
.promotion_rule .delete, .promotion_action .delete {
|
11
11
|
display: block;
|
12
12
|
position: absolute;
|
13
13
|
right: 10px;
|
14
14
|
top: 10px;
|
15
15
|
}
|
16
|
-
|
17
|
-
|
18
16
|
.products_rule_products_source_field label {
|
19
17
|
display: inline;
|
20
18
|
}
|
21
|
-
|
22
|
-
|
23
|
-
|
24
19
|
.edit_promotion #general_fields, .edit_promotion #expiry_fields {
|
25
|
-
|
26
|
-
|
20
|
+
float: left;
|
21
|
+
width: 46%;
|
27
22
|
}
|
28
|
-
|
29
23
|
.edit_promotion #general_fields {
|
30
|
-
|
24
|
+
margin-right: 10px;
|
31
25
|
}
|
32
|
-
|
33
|
-
.edit_promotion #expiry_fields {
|
34
|
-
|
35
|
-
}
|
36
|
-
|
26
|
+
.edit_promotion #expiry_fields {}
|
37
27
|
.edit_promotion #starts_at_field, .edit_promotion #expires_at_field {
|
38
|
-
|
39
|
-
|
40
|
-
|
28
|
+
width: 200px;
|
29
|
+
float: left;
|
30
|
+
clear: none;
|
41
31
|
}
|
42
32
|
.edit_promotion #starts_at_field label, .edit_promotion #expires_at_field label{
|
43
|
-
|
44
|
-
}
|
45
|
-
#rule_fields {
|
46
|
-
position: relative;
|
33
|
+
display: block;
|
47
34
|
}
|
48
|
-
#
|
49
|
-
|
50
|
-
text-align:right;
|
51
|
-
width: 300px;
|
52
|
-
right: 30px;
|
53
|
-
bottom: 20px;
|
35
|
+
#rule_fields, #action_fields {
|
36
|
+
position: relative;
|
54
37
|
}
|
38
|
+
#new_product_rule_form, #new_promotion_action_form {
|
39
|
+
position: absolute;
|
40
|
+
text-align:right;
|
41
|
+
width: 300px;
|
42
|
+
right: 30px;
|
43
|
+
bottom: 20px;
|
44
|
+
}
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class Admin::PromotionActionsController < Admin::BaseController
|
2
|
+
def create
|
3
|
+
@calculators = Promotion::Actions::CreateAdjustment.calculators
|
4
|
+
@promotion = Promotion.find(params[:promotion_id])
|
5
|
+
@promotion_action = params[:promotion_action][:type].constantize.new(params[:promotion_action])
|
6
|
+
@promotion_action.promotion = @promotion
|
7
|
+
if @promotion_action.save
|
8
|
+
flash[:notice] = I18n.t(:successfully_created, :resource => I18n.t(:promotion_action))
|
9
|
+
end
|
10
|
+
respond_to do |format|
|
11
|
+
format.html { redirect_to edit_admin_promotion_path(@promotion)}
|
12
|
+
format.js { render :layout => false }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def destroy
|
17
|
+
@promotion = Promotion.find(params[:promotion_id])
|
18
|
+
@promotion_action = @promotion.promotion_actions.find(params[:id])
|
19
|
+
if @promotion_action.destroy
|
20
|
+
flash[:notice] = I18n.t(:successfully_removed, :resource => I18n.t(:promotion_action))
|
21
|
+
end
|
22
|
+
respond_to do |format|
|
23
|
+
format.html { redirect_to edit_admin_promotion_path(@promotion)}
|
24
|
+
format.js { render :layout => false }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
@@ -10,12 +10,12 @@ class Admin::PromotionsController < Admin::ResourceController
|
|
10
10
|
end
|
11
11
|
@promotion
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
def location_after_save
|
15
15
|
edit_admin_promotion_url(@promotion)
|
16
16
|
end
|
17
17
|
|
18
18
|
def load_data
|
19
|
-
@calculators = Promotion.calculators
|
19
|
+
@calculators = Promotion::Actions::CreateAdjustment.calculators
|
20
20
|
end
|
21
21
|
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# Keep a record ot all static page paths visited for promotions that require them
|
2
|
+
ContentController.class_eval do
|
3
|
+
after_filter :store_visited_path
|
4
|
+
def store_visited_path
|
5
|
+
session[:visited_paths] ||= []
|
6
|
+
session[:visited_paths] = (session[:visited_paths] + [params[:path]]).compact.uniq
|
7
|
+
end
|
8
|
+
end
|
@@ -1,10 +1,19 @@
|
|
1
1
|
OrdersController.class_eval do
|
2
|
-
after_filter :clear_promotions
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
def update
|
4
|
+
@order = current_order
|
5
|
+
if @order.update_attributes(params[:order])
|
6
|
+
|
7
|
+
if @order.coupon_code.present?
|
8
|
+
fire_event('spree.checkout.coupon_code_added', :coupon_code => @order.coupon_code)
|
9
|
+
end
|
10
|
+
|
11
|
+
@order.line_items = @order.line_items.select {|li| li.quantity > 0 }
|
12
|
+
fire_event('spree.order.contents_changed')
|
13
|
+
respond_with(@order) { |format| format.html { redirect_to cart_path } }
|
14
|
+
else
|
15
|
+
respond_with(@order)
|
16
|
+
end
|
7
17
|
end
|
8
18
|
|
9
19
|
end
|
10
|
-
|
@@ -0,0 +1,24 @@
|
|
1
|
+
Order.class_eval do
|
2
|
+
|
3
|
+
attr_accessible :coupon_code
|
4
|
+
attr_accessor :coupon_code
|
5
|
+
|
6
|
+
def promotion_credit_exists?(promotion)
|
7
|
+
!! adjustments.promotion.reload.detect { |credit| credit.originator.promotion.id == promotion.id }
|
8
|
+
end
|
9
|
+
|
10
|
+
def products
|
11
|
+
line_items.map {|li| li.variant.product}
|
12
|
+
end
|
13
|
+
|
14
|
+
unless self.instance_methods.include?("update_adjustments_with_promotion_limiting")
|
15
|
+
def update_adjustments_with_promotion_limiting
|
16
|
+
update_adjustments_without_promotion_limiting
|
17
|
+
return if adjustments.promotion.eligible.none?
|
18
|
+
most_valuable_adjustment = adjustments.promotion.eligible.max{|a,b| a.amount.abs <=> b.amount.abs}
|
19
|
+
( adjustments.promotion.eligible - [most_valuable_adjustment] ).each{|adjustment| adjustment.update_attribute_without_callbacks(:eligible, false)}
|
20
|
+
end
|
21
|
+
alias_method_chain :update_adjustments, :promotion_limiting
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
Product.class_eval do
|
2
|
+
has_and_belongs_to_many :promotion_rules
|
3
|
+
|
4
|
+
def possible_promotions
|
5
|
+
rules_with_matching_product_groups = product_groups.map(&:promotion_rules).flatten
|
6
|
+
all_rules = promotion_rules + rules_with_matching_product_groups
|
7
|
+
promotion_ids = all_rules.map(&:activator_id).uniq
|
8
|
+
Promotion.advertised.where(:id => promotion_ids)
|
9
|
+
end
|
10
|
+
end
|
data/app/models/promotion.rb
CHANGED
@@ -1,13 +1,37 @@
|
|
1
|
-
class Promotion <
|
2
|
-
has_many :promotion_credits, :as => :source
|
3
|
-
calculated_adjustments
|
4
|
-
alias credits promotion_credits
|
1
|
+
class Promotion < Activator
|
5
2
|
|
6
|
-
|
7
|
-
|
3
|
+
|
4
|
+
MATCH_POLICIES = %w(all any)
|
5
|
+
|
6
|
+
preference :usage_limit, :integer
|
7
|
+
preference :match_policy, :string, :default => MATCH_POLICIES.first
|
8
|
+
preference :code, :string
|
9
|
+
preference :advertise, :boolean, :default => false
|
10
|
+
|
11
|
+
[:usage_limit, :match_policy, :code, :advertise].each do |field|
|
12
|
+
alias_method field, "preferred_#{field}"
|
13
|
+
alias_method "#{field}=", "preferred_#{field}="
|
14
|
+
end
|
15
|
+
|
16
|
+
has_many :promotion_rules, :foreign_key => 'activator_id', :autosave => true, :dependent => :destroy
|
8
17
|
alias_method :rules, :promotion_rules
|
18
|
+
accepts_nested_attributes_for :promotion_rules
|
19
|
+
|
20
|
+
has_many :promotion_actions, :foreign_key => 'activator_id', :autosave => true, :dependent => :destroy
|
21
|
+
alias_method :actions, :promotion_actions
|
22
|
+
accepts_nested_attributes_for :promotion_actions
|
23
|
+
|
24
|
+
# TODO: This shouldn't be necessary with :autosave option but nested attribute updating of actions is broken without it
|
25
|
+
after_save :save_rules_and_actions
|
26
|
+
def save_rules_and_actions
|
27
|
+
(rules + actions).each &:save
|
28
|
+
end
|
29
|
+
|
9
30
|
|
10
31
|
validates :name, :presence => true
|
32
|
+
validates :preferred_code, :presence => true, :if => lambda{|r| r.event_name == 'spree.ceckout.coupon_code_added' }
|
33
|
+
|
34
|
+
scope :advertised, includes(:stored_preferences).where(:preferences => {:name => 'advertise', :value => '1'})
|
11
35
|
|
12
36
|
# TODO: Remove that after fix for https://rails.lighthouseapp.com/projects/8994/tickets/4329-has_many-through-association-does-not-link-models-on-association-save
|
13
37
|
# is provided
|
@@ -17,52 +41,32 @@ class Promotion < ActiveRecord::Base
|
|
17
41
|
end
|
18
42
|
end
|
19
43
|
|
20
|
-
MATCH_POLICIES = %w(all any)
|
21
|
-
|
22
|
-
scope :automatic, where("code IS NULL OR code = ''")
|
23
|
-
scope :manual, where("code IS NOT NULL AND code <> ''")
|
24
|
-
|
25
|
-
def eligible?(order)
|
26
|
-
!expired? && rules_are_eligible?(order)
|
27
|
-
end
|
28
44
|
|
29
|
-
def
|
30
|
-
|
31
|
-
|
32
|
-
|
45
|
+
def activate(payload)
|
46
|
+
if eligible?(payload[:order], payload)
|
47
|
+
actions.each do |action|
|
48
|
+
action.perform(payload)
|
49
|
+
end
|
50
|
+
end
|
33
51
|
end
|
34
52
|
|
35
|
-
def
|
36
|
-
|
53
|
+
def eligible?(order, options = {})
|
54
|
+
!expired? && rules_are_eligible?(order, options) && coupon_is_eligible?(order, options[:coupon_code])
|
37
55
|
end
|
38
56
|
|
39
|
-
def rules_are_eligible?(order)
|
57
|
+
def rules_are_eligible?(order, options = {})
|
40
58
|
return true if rules.none?
|
41
59
|
if match_policy == 'all'
|
42
|
-
rules.all?{|r| r.eligible?(order)}
|
60
|
+
rules.all?{|r| r.eligible?(order, options)}
|
43
61
|
else
|
44
|
-
rules.any?{|r| r.eligible?(order)}
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
def create_discount(order)
|
49
|
-
return if order.promotion_credit_exists?(self)
|
50
|
-
if eligible?(order) and amount = compute(order)
|
51
|
-
order.promotion_credits.reload.clear unless combine? and order.promotion_credits.all? { |credit| credit.source.combine? }
|
52
|
-
order.update!
|
53
|
-
PromotionCredit.create!({
|
54
|
-
:label => "#{I18n.t(:coupon)} (#{code})",
|
55
|
-
:source => self,
|
56
|
-
:amount => -amount.abs,
|
57
|
-
:order => order
|
58
|
-
})
|
62
|
+
rules.any?{|r| r.eligible?(order, options)}
|
59
63
|
end
|
60
64
|
end
|
61
65
|
|
62
|
-
def
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
+
def coupon_is_eligible?(order, code = nil)
|
67
|
+
return true if order && order.promotion_credit_exists?(self)
|
68
|
+
return true if event_name != 'spree.checkout.coupon_code_added'
|
69
|
+
code.to_s.strip.downcase == preferred_code.to_s.strip.downcase
|
66
70
|
end
|
67
71
|
|
68
72
|
# Products assigned to all product rules
|
@@ -70,4 +74,20 @@ class Promotion < ActiveRecord::Base
|
|
70
74
|
@products ||= rules.of_type("Promotion::Rules::Product").map(&:products).flatten.uniq
|
71
75
|
end
|
72
76
|
|
77
|
+
def expired?
|
78
|
+
super || usage_limit_exceeded?
|
79
|
+
end
|
80
|
+
|
81
|
+
def usage_limit_exceeded?
|
82
|
+
preferred_usage_limit.present? && preferred_usage_limit > 0 && credits_count >= preferred_usage_limit
|
83
|
+
end
|
84
|
+
|
85
|
+
def credits
|
86
|
+
Adjustment.where(:originator_id => actions.map(&:id))
|
87
|
+
end
|
88
|
+
|
89
|
+
def credits_count
|
90
|
+
credits.count
|
91
|
+
end
|
92
|
+
|
73
93
|
end
|