spree_promo 0.60.6 → 0.70.RC1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|