spree_promo 1.2.5 → 1.3.0.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/app/assets/javascripts/admin/promotions.js +10 -1
- data/app/assets/stylesheets/admin/spree_promo.css +0 -1
- data/app/controllers/spree/admin/promotion_actions_controller.rb +2 -2
- data/app/controllers/spree/admin/promotion_rules_controller.rb +2 -2
- data/app/controllers/spree/checkout_controller_decorator.rb +5 -16
- data/app/controllers/spree/orders_controller_decorator.rb +2 -18
- data/app/controllers/spree/store_controller_decorator.rb +55 -0
- data/app/models/spree/order_decorator.rb +5 -14
- data/app/models/spree/order_updater_decorator.rb +14 -0
- data/app/models/spree/promotion/actions/create_adjustment.rb +12 -22
- data/app/models/spree/promotion/actions/create_line_items.rb +2 -18
- data/app/models/spree/promotion/rules/first_order.rb +2 -19
- data/app/models/spree/promotion/rules/user_logged_in.rb +8 -1
- data/app/models/spree/promotion.rb +6 -6
- data/app/models/spree/promotion_action_line_item.rb +2 -0
- data/app/overrides/promo_admin_tabs.rb +1 -1
- data/app/views/spree/admin/promotion_actions/create.js.erb +6 -3
- data/app/views/spree/admin/promotion_actions/destroy.js.erb +1 -2
- data/app/views/spree/admin/promotion_rules/create.js.erb +6 -0
- data/app/views/spree/admin/promotion_rules/destroy.js.erb +1 -1
- data/app/views/spree/admin/promotions/_actions.html.erb +21 -15
- data/app/views/spree/admin/promotions/_form.html.erb +55 -51
- data/app/views/spree/admin/promotions/_promotion_action.html.erb +5 -5
- data/app/views/spree/admin/promotions/_promotion_rule.html.erb +5 -4
- data/app/views/spree/admin/promotions/_rules.html.erb +39 -26
- data/app/views/spree/admin/promotions/actions/_create_adjustment.html.erb +8 -9
- data/app/views/spree/admin/promotions/actions/_create_line_items.html.erb +19 -40
- data/app/views/spree/admin/promotions/edit.html.erb +22 -6
- data/app/views/spree/admin/promotions/index.html.erb +49 -35
- data/app/views/spree/admin/promotions/new.html.erb +13 -3
- data/app/views/spree/admin/promotions/rules/_item_total.html.erb +6 -7
- data/app/views/spree/admin/promotions/rules/_landing_page.html.erb +5 -8
- data/app/views/spree/admin/promotions/rules/_product.html.erb +7 -8
- data/app/views/spree/admin/promotions/rules/_user_logged_in.html.erb +0 -3
- data/config/locales/en.yml +8 -2
- data/config/routes.rb +1 -1
- data/db/migrate/20120831092359_spree_promo_one_two.rb +45 -0
- data/lib/spree/promo/engine.rb +5 -6
- metadata +11 -26
- data/app/assets/stylesheets/admin/promotions.css +0 -44
- data/db/migrate/20100419190933_rename_coupons_to_promotions.rb +0 -10
- data/db/migrate/20100426100726_create_promotion_rules.rb +0 -24
- data/db/migrate/20100501084722_match_policy_for_promotions.rb +0 -5
- data/db/migrate/20100501095202_create_promotion_rules_users.rb +0 -14
- data/db/migrate/20100502163939_name_for_promotions.rb +0 -5
- data/db/migrate/20100923095305_update_calculable_type_for_promotions.rb +0 -9
- data/db/migrate/20101026184833_migrate_adjustments.rb +0 -9
- data/db/migrate/20110331094351_promotion_changes_to_subclass_of_activator.rb +0 -22
- data/db/migrate/20110404123358_create_promotion_actions.rb +0 -9
- data/db/migrate/20110601202923_create_promotion_action_line_items.rb +0 -10
- data/db/migrate/20120119024707_namespace_promo_tables.rb +0 -85
- data/db/migrate/20120119024708_create_spree_pending_promotions.rb +0 -11
- data/db/migrate/20120119024721_content_visited_event.rb +0 -29
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
var initProductActions = function(){
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
// Add classes on promotion items for design
|
|
4
|
+
$('a.delete').live('mouseover mouseout', function(event) {
|
|
5
|
+
if (event.type == 'mouseover') {
|
|
6
|
+
$(this).parent().addClass('action-remove');
|
|
7
|
+
} else {
|
|
8
|
+
$(this).parent().removeClass('action-remove');
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
$(".variant_autocomplete").variantAutocomplete();
|
|
4
13
|
|
|
5
14
|
$('.calculator-fields').each(function(){
|
|
6
15
|
var $fields_container = $(this);
|
|
@@ -5,7 +5,7 @@ class Spree::Admin::PromotionActionsController < Spree::Admin::BaseController
|
|
|
5
5
|
@promotion_action = params[:action_type].constantize.new(params[:promotion_action])
|
|
6
6
|
@promotion_action.promotion = @promotion
|
|
7
7
|
if @promotion_action.save
|
|
8
|
-
flash
|
|
8
|
+
flash[:success] = I18n.t(:successfully_created, :resource => I18n.t(:promotion_action))
|
|
9
9
|
end
|
|
10
10
|
respond_to do |format|
|
|
11
11
|
format.html { redirect_to spree.edit_admin_promotion_path(@promotion)}
|
|
@@ -17,7 +17,7 @@ class Spree::Admin::PromotionActionsController < Spree::Admin::BaseController
|
|
|
17
17
|
@promotion = Spree::Promotion.find(params[:promotion_id])
|
|
18
18
|
@promotion_action = @promotion.promotion_actions.find(params[:id])
|
|
19
19
|
if @promotion_action.destroy
|
|
20
|
-
flash
|
|
20
|
+
flash[:success] = I18n.t(:successfully_removed, :resource => I18n.t(:promotion_action))
|
|
21
21
|
end
|
|
22
22
|
respond_to do |format|
|
|
23
23
|
format.html { redirect_to spree.edit_admin_promotion_path(@promotion)}
|
|
@@ -10,7 +10,7 @@ class Spree::Admin::PromotionRulesController < Spree::Admin::BaseController
|
|
|
10
10
|
@promotion_rule = promotion_rule_type.constantize.new(params[:promotion_rule])
|
|
11
11
|
@promotion_rule.promotion = @promotion
|
|
12
12
|
if @promotion_rule.save
|
|
13
|
-
flash
|
|
13
|
+
flash[:success] = I18n.t(:successfully_created, :resource => I18n.t(:promotion_rule))
|
|
14
14
|
end
|
|
15
15
|
respond_to do |format|
|
|
16
16
|
format.html { redirect_to spree.edit_admin_promotion_path(@promotion)}
|
|
@@ -22,7 +22,7 @@ class Spree::Admin::PromotionRulesController < Spree::Admin::BaseController
|
|
|
22
22
|
@promotion = Spree::Promotion.find(params[:promotion_id])
|
|
23
23
|
@promotion_rule = @promotion.promotion_rules.find(params[:id])
|
|
24
24
|
if @promotion_rule.destroy
|
|
25
|
-
flash
|
|
25
|
+
flash[:success] = I18n.t(:successfully_removed, :resource => I18n.t(:promotion_rule))
|
|
26
26
|
end
|
|
27
27
|
respond_to do |format|
|
|
28
28
|
format.html { redirect_to spree.edit_admin_promotion_path(@promotion)}
|
|
@@ -5,36 +5,25 @@ Spree::CheckoutController.class_eval do
|
|
|
5
5
|
if @order.update_attributes(object_params)
|
|
6
6
|
|
|
7
7
|
fire_event('spree.checkout.update')
|
|
8
|
-
|
|
9
|
-
if @order.coupon_code.present?
|
|
10
|
-
event_name = "spree.checkout.coupon_code_added"
|
|
11
|
-
if promo = Spree::Promotion.with_coupon_code(@order.coupon_code).where(:event_name => event_name).first
|
|
12
|
-
fire_event(event_name, :coupon_code => @order.coupon_code)
|
|
13
|
-
# If it doesn't exist, raise an error!
|
|
14
|
-
# Giving them another chance to enter a valid coupon code
|
|
15
|
-
else
|
|
16
|
-
flash[:error] = t(:promotion_not_found)
|
|
17
|
-
render :edit and return
|
|
18
|
-
end
|
|
19
|
-
end
|
|
8
|
+
render :edit and return unless apply_coupon_code
|
|
20
9
|
|
|
21
10
|
if @order.next
|
|
22
11
|
state_callback(:after)
|
|
23
12
|
else
|
|
24
13
|
flash[:error] = t(:payment_processing_failed)
|
|
25
|
-
|
|
14
|
+
redirect_to checkout_state_path(@order.state)
|
|
26
15
|
return
|
|
27
16
|
end
|
|
28
17
|
|
|
29
18
|
if @order.state == 'complete' || @order.completed?
|
|
30
19
|
flash.notice = t(:order_processed_successfully)
|
|
31
20
|
flash[:commerce_tracking] = 'nothing special'
|
|
32
|
-
|
|
21
|
+
redirect_to completion_route
|
|
33
22
|
else
|
|
34
|
-
|
|
23
|
+
redirect_to checkout_state_path(@order.state)
|
|
35
24
|
end
|
|
36
25
|
else
|
|
37
|
-
|
|
26
|
+
render :edit
|
|
38
27
|
end
|
|
39
28
|
end
|
|
40
29
|
|
|
@@ -3,14 +3,8 @@ Spree::OrdersController.class_eval do
|
|
|
3
3
|
def update
|
|
4
4
|
@order = current_order
|
|
5
5
|
if @order.update_attributes(params[:order])
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
flash[:notice] = t(:coupon_code_applied)
|
|
9
|
-
else
|
|
10
|
-
flash[:error] = t(:promotion_not_found)
|
|
11
|
-
render :edit and return
|
|
12
|
-
end
|
|
13
|
-
end
|
|
6
|
+
render :edit and return unless apply_coupon_code
|
|
7
|
+
|
|
14
8
|
@order.line_items = @order.line_items.select {|li| li.quantity > 0 }
|
|
15
9
|
fire_event('spree.order.contents_changed')
|
|
16
10
|
respond_with(@order) do |format|
|
|
@@ -27,14 +21,4 @@ Spree::OrdersController.class_eval do
|
|
|
27
21
|
end
|
|
28
22
|
end
|
|
29
23
|
|
|
30
|
-
def apply_coupon_code
|
|
31
|
-
return if @order.coupon_code.blank?
|
|
32
|
-
if promo = Spree::Promotion.with_coupon_code(@order.coupon_code).first
|
|
33
|
-
if promo.order_activatable?(@order)
|
|
34
|
-
fire_event('spree.checkout.coupon_code_added', :coupon_code => @order.coupon_code)
|
|
35
|
-
true
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
|
|
40
24
|
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
Spree::StoreController.class_eval do
|
|
2
|
+
|
|
3
|
+
protected
|
|
4
|
+
def apply_coupon_code
|
|
5
|
+
if @order.coupon_code.present?
|
|
6
|
+
# check if coupon code is already applied
|
|
7
|
+
if @order.adjustments.promotion.eligible.detect { |p| p.originator.promotion.code == @order.coupon_code }.present?
|
|
8
|
+
flash[:notice] = t(:coupon_code_already_applied)
|
|
9
|
+
true
|
|
10
|
+
else
|
|
11
|
+
event_name = "spree.checkout.coupon_code_added"
|
|
12
|
+
|
|
13
|
+
# TODO should restrict to payload's event name?
|
|
14
|
+
promotion = Spree::Promotion.find_by_code(@order.coupon_code)
|
|
15
|
+
|
|
16
|
+
if promotion.present?
|
|
17
|
+
if promotion.expired?
|
|
18
|
+
flash[:error] = t(:coupon_code_expired)
|
|
19
|
+
return false
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
if promotion.usage_limit_exceeded?
|
|
23
|
+
flash[:error] = t(:coupon_code_max_usage)
|
|
24
|
+
return false
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
previous_promo = @order.adjustments.promotion.eligible.first
|
|
28
|
+
fire_event(event_name, :coupon_code => @order.coupon_code)
|
|
29
|
+
promo = @order.adjustments.promotion.detect { |p| p.originator.promotion.code == @order.coupon_code }
|
|
30
|
+
|
|
31
|
+
if promo.present? and promo.eligible
|
|
32
|
+
flash[:success] = t(:coupon_code_applied)
|
|
33
|
+
true
|
|
34
|
+
elsif previous_promo.present? and promo.present?
|
|
35
|
+
flash[:error] = t(:coupon_code_better_exists)
|
|
36
|
+
false
|
|
37
|
+
elsif promo.present?
|
|
38
|
+
flash[:error] = t(:coupon_code_not_eligible)
|
|
39
|
+
false
|
|
40
|
+
else
|
|
41
|
+
# if the promotion was created after the order
|
|
42
|
+
flash[:error] = t(:coupon_code_not_found)
|
|
43
|
+
false
|
|
44
|
+
end
|
|
45
|
+
else
|
|
46
|
+
flash[:error] = t(:coupon_code_not_found)
|
|
47
|
+
false
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
else
|
|
51
|
+
true
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
end
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
Spree::Order.class_eval do
|
|
2
2
|
attr_accessible :coupon_code
|
|
3
|
-
|
|
3
|
+
attr_reader :coupon_code
|
|
4
|
+
|
|
5
|
+
def coupon_code=(code)
|
|
6
|
+
@coupon_code = code.strip.downcase rescue nil
|
|
7
|
+
end
|
|
4
8
|
|
|
5
9
|
# Tells us if there if the specified promotion is already associated with the order
|
|
6
10
|
# regardless of whether or not its currently eligible. Useful because generally
|
|
@@ -9,19 +13,6 @@ Spree::Order.class_eval do
|
|
|
9
13
|
!! adjustments.promotion.reload.detect { |credit| credit.originator.promotion.id == promotion.id }
|
|
10
14
|
end
|
|
11
15
|
|
|
12
|
-
unless self.method_defined?('update_adjustments_with_promotion_limiting')
|
|
13
|
-
def update_adjustments_with_promotion_limiting
|
|
14
|
-
update_adjustments_without_promotion_limiting
|
|
15
|
-
return if adjustments.promotion.eligible.none?
|
|
16
|
-
most_valuable_adjustment = adjustments.promotion.eligible.max{|a,b| a.amount.abs <=> b.amount.abs}
|
|
17
|
-
current_adjustments = (adjustments.promotion.eligible - [most_valuable_adjustment])
|
|
18
|
-
current_adjustments.each do |adjustment|
|
|
19
|
-
adjustment.update_attribute_without_callbacks(:eligible, false)
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
alias_method_chain :update_adjustments, :promotion_limiting
|
|
23
|
-
end
|
|
24
|
-
|
|
25
16
|
def promo_total
|
|
26
17
|
adjustments.eligible.promotion.map(&:amount).sum
|
|
27
18
|
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
Spree::OrderUpdater.class_eval do
|
|
2
|
+
unless self.method_defined?('update_adjustments_with_promotion_limiting')
|
|
3
|
+
def update_adjustments_with_promotion_limiting
|
|
4
|
+
update_adjustments_without_promotion_limiting
|
|
5
|
+
return if adjustments.promotion.eligible.none?
|
|
6
|
+
most_valuable_adjustment = adjustments.promotion.eligible.max{|a,b| a.amount.abs <=> b.amount.abs}
|
|
7
|
+
current_adjustments = (adjustments.promotion.eligible - [most_valuable_adjustment])
|
|
8
|
+
current_adjustments.each do |adjustment|
|
|
9
|
+
adjustment.update_attribute_without_callbacks(:eligible, false)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
alias_method_chain :update_adjustments, :promotion_limiting
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -1,36 +1,27 @@
|
|
|
1
1
|
module Spree
|
|
2
2
|
class Promotion
|
|
3
3
|
module Actions
|
|
4
|
-
# Responsible for the creation and management of an adjustment since an
|
|
5
|
-
# an adjustment uses its originator to also update its eligiblity and amount
|
|
6
4
|
class CreateAdjustment < PromotionAction
|
|
7
5
|
calculated_adjustments
|
|
8
6
|
|
|
9
|
-
has_many :adjustments, :as => :originator, :dependent => :destroy
|
|
10
|
-
|
|
11
7
|
delegate :eligible?, :to => :promotion
|
|
12
8
|
|
|
13
9
|
before_validation :ensure_action_has_calculator
|
|
14
10
|
|
|
15
|
-
# Creates the adjustment related to a promotion for the order passed
|
|
16
|
-
# through options hash
|
|
17
11
|
def perform(options = {})
|
|
18
|
-
order = options[:order]
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
12
|
+
return unless order = options[:order]
|
|
13
|
+
# Nothing to do if the promotion is already associated with the order
|
|
14
|
+
return if order.promotion_credit_exists?(promotion)
|
|
15
|
+
|
|
16
|
+
order.adjustments.promotion.reload.clear
|
|
17
|
+
order.update!
|
|
18
|
+
create_adjustment("#{I18n.t(:promotion)} (#{promotion.name})", order, order)
|
|
19
|
+
order.update!
|
|
22
20
|
end
|
|
23
21
|
|
|
24
|
-
#
|
|
25
|
-
# adjustments are added all the time. They will get their
|
|
26
|
-
# set to false if the amount is 0
|
|
27
|
-
#
|
|
28
|
-
# Currently an adjustment is created even when its promotion is not eligible.
|
|
29
|
-
# This helps to figure out later which adjustments should be eligible
|
|
30
|
-
# as the order is being updated
|
|
31
|
-
#
|
|
32
|
-
# BTW The order is updated (through order#update) every time an adjustment
|
|
33
|
-
# is saved
|
|
22
|
+
# override of CalculatedAdjustments#create_adjustment so promotional
|
|
23
|
+
# adjustments are added all the time. They will get their eligability
|
|
24
|
+
# set to false if the amount is 0
|
|
34
25
|
def create_adjustment(label, target, calculable, mandatory=false)
|
|
35
26
|
amount = compute_amount(calculable)
|
|
36
27
|
params = { :amount => amount,
|
|
@@ -41,8 +32,7 @@ module Spree
|
|
|
41
32
|
target.adjustments.create(params, :without_protection => true)
|
|
42
33
|
end
|
|
43
34
|
|
|
44
|
-
# Ensure a negative amount which does not exceed the sum of the order's
|
|
45
|
-
# item_total and ship_total
|
|
35
|
+
# Ensure a negative amount which does not exceed the sum of the order's item_total and ship_total
|
|
46
36
|
def compute_amount(calculable)
|
|
47
37
|
[(calculable.item_total + calculable.ship_total), super.to_f.abs].min * -1
|
|
48
38
|
end
|
|
@@ -3,14 +3,12 @@ module Spree
|
|
|
3
3
|
module Actions
|
|
4
4
|
class CreateLineItems < PromotionAction
|
|
5
5
|
has_many :promotion_action_line_items, :foreign_key => :promotion_action_id
|
|
6
|
+
accepts_nested_attributes_for :promotion_action_line_items
|
|
7
|
+
attr_accessible :promotion_action_line_items_attributes
|
|
6
8
|
|
|
7
|
-
delegate :eligible?, :to => :promotion
|
|
8
|
-
|
|
9
|
-
attr_accessor :line_items_string
|
|
10
9
|
|
|
11
10
|
def perform(options = {})
|
|
12
11
|
return unless order = options[:order]
|
|
13
|
-
return unless eligible?(order)
|
|
14
12
|
promotion_action_line_items.each do |item|
|
|
15
13
|
current_quantity = order.quantity_of(item.variant)
|
|
16
14
|
if current_quantity < item.quantity
|
|
@@ -19,20 +17,6 @@ module Spree
|
|
|
19
17
|
end
|
|
20
18
|
end
|
|
21
19
|
end
|
|
22
|
-
|
|
23
|
-
def line_items_string
|
|
24
|
-
promotion_action_line_items.map { |li| "#{li.variant_id}x#{li.quantity}" }.join(',')
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def line_items_string=(value)
|
|
28
|
-
promotion_action_line_items.destroy_all
|
|
29
|
-
value.to_s.split(',').each do |str|
|
|
30
|
-
variant_id, quantity = str.split('x')
|
|
31
|
-
if variant_id && quantity && variant = Variant.find_by_id(variant_id)
|
|
32
|
-
promotion_action_line_items.create({:variant => variant, :quantity => quantity.to_i}, :without_protection => true)
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
20
|
end
|
|
37
21
|
end
|
|
38
22
|
end
|
|
@@ -2,27 +2,10 @@ module Spree
|
|
|
2
2
|
class Promotion
|
|
3
3
|
module Rules
|
|
4
4
|
class FirstOrder < PromotionRule
|
|
5
|
-
attr_reader :user, :email
|
|
6
|
-
|
|
7
5
|
def eligible?(order, options = {})
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
if user || email
|
|
12
|
-
completed_orders.blank? || (completed_orders.first == order)
|
|
13
|
-
else
|
|
14
|
-
false
|
|
15
|
-
end
|
|
6
|
+
user = order.try(:user) || options[:user]
|
|
7
|
+
!!(user && user.orders.complete.count == 0)
|
|
16
8
|
end
|
|
17
|
-
|
|
18
|
-
private
|
|
19
|
-
def completed_orders
|
|
20
|
-
user ? user.orders.complete : orders_by_email
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def orders_by_email
|
|
24
|
-
Spree::Order.where(:email => email).complete
|
|
25
|
-
end
|
|
26
9
|
end
|
|
27
10
|
end
|
|
28
11
|
end
|
|
@@ -4,7 +4,14 @@ module Spree
|
|
|
4
4
|
class UserLoggedIn < PromotionRule
|
|
5
5
|
|
|
6
6
|
def eligible?(order, options = {})
|
|
7
|
-
|
|
7
|
+
# this is tricky. We couldn't use any of the devise methods since we aren't in the controller.
|
|
8
|
+
# we need to rely on the controller already having done this for us.
|
|
9
|
+
|
|
10
|
+
# The thinking is that the controller should have some sense of what state
|
|
11
|
+
# we should be in before firing events,
|
|
12
|
+
# so the controller will have to set this field.
|
|
13
|
+
|
|
14
|
+
return options && options[:user_signed_in]
|
|
8
15
|
end
|
|
9
16
|
|
|
10
17
|
end
|
|
@@ -36,16 +36,12 @@ module Spree
|
|
|
36
36
|
where(:advertise => true)
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
-
def self.with_coupon_code(coupon_code)
|
|
40
|
-
search(:code_cont => coupon_code).result
|
|
41
|
-
end
|
|
42
|
-
|
|
43
39
|
def activate(payload)
|
|
44
40
|
return unless order_activatable? payload[:order]
|
|
45
41
|
|
|
46
42
|
if code.present?
|
|
47
|
-
event_code = payload[:coupon_code]
|
|
48
|
-
return unless event_code == self.code
|
|
43
|
+
event_code = payload[:coupon_code]
|
|
44
|
+
return unless event_code == self.code
|
|
49
45
|
end
|
|
50
46
|
|
|
51
47
|
if path.present?
|
|
@@ -101,5 +97,9 @@ module Spree
|
|
|
101
97
|
credits.count
|
|
102
98
|
end
|
|
103
99
|
|
|
100
|
+
def code=(coupon_code)
|
|
101
|
+
write_attribute(:code, (coupon_code.downcase.strip rescue nil))
|
|
102
|
+
end
|
|
103
|
+
|
|
104
104
|
end
|
|
105
105
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Deface::Override.new(:virtual_path => "spree/layouts/admin",
|
|
2
2
|
:name => "promo_admin_tabs",
|
|
3
3
|
:insert_bottom => "[data-hook='admin_tabs'], #admin_tabs[data-hook]",
|
|
4
|
-
:text => "<%= tab(:promotions, :url => spree.admin_promotions_path) %>",
|
|
4
|
+
:text => "<%= tab(:promotions, :url => spree.admin_promotions_path, :icon => 'icon-bullhorn') %>",
|
|
5
5
|
:disabled => false,
|
|
6
6
|
:original => '3e847740dc3e7f924aba1ccb4cb00c7b841649e3')
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
$('#actions').append('<%= escape_javascript( render(:partial => 'spree/admin/promotions/promotion_action', :object => @promotion_action) ) %>');
|
|
2
|
-
|
|
2
|
+
$('#actions .no-objects-found').hide();
|
|
3
3
|
initProductActions();
|
|
4
4
|
|
|
5
|
+
<% unless Rails.env.test? %>
|
|
6
|
+
$('.type-select.select2').select2();
|
|
7
|
+
<% end %>
|
|
8
|
+
|
|
5
9
|
$('#<%= dom_id @promotion_action %>').hide();
|
|
6
10
|
$('#<%= dom_id @promotion_action %>').fadeIn();
|
|
7
11
|
$('#<%= dom_id @promotion_action %> .tokeninput.products').productPicker();
|
|
8
|
-
$('.product_autocomplete').product_autocomplete();
|
|
9
|
-
|
|
12
|
+
$('.product_autocomplete').product_autocomplete();
|
|
@@ -1,2 +1 @@
|
|
|
1
|
-
$('#<%= dom_id @promotion_action %>').fadeOut().remove();
|
|
2
|
-
|
|
1
|
+
$('#<%= dom_id @promotion_action %>').fadeOut().remove();
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
$('#rules').append('<%= escape_javascript( render(:partial => 'spree/admin/promotions/promotion_rule', :object => @promotion_rule) ) %>');
|
|
2
|
+
$('#rules .no-objects-found').hide();
|
|
3
|
+
|
|
2
4
|
$('#<%= dom_id @promotion_rule %>').hide();
|
|
3
5
|
$('#<%= dom_id @promotion_rule %>').fadeIn();
|
|
4
6
|
$('#<%= dom_id @promotion_rule %> .tokeninput.products').productPicker();
|
|
5
7
|
$('#<%= dom_id @promotion_rule %> .tokeninput.users').userPicker();
|
|
6
8
|
|
|
7
9
|
$('#promotion_rule_type').html('<%= escape_javascript options_for_promotion_rule_types(@promotion) %>');
|
|
10
|
+
|
|
11
|
+
<% unless Rails.env.test? %>
|
|
12
|
+
$('.select_item_total.select2, .select_product.select2').select2();
|
|
13
|
+
<% end %>
|
|
@@ -1,26 +1,32 @@
|
|
|
1
|
-
<fieldset id="action_fields">
|
|
2
|
-
|
|
1
|
+
<fieldset id="action_fields" class="eight columns omega no-border-top">
|
|
2
|
+
|
|
3
|
+
<%= form_tag spree.admin_promotion_promotion_actions_path(@promotion), :remote => true, :id => 'new_promotion_action_form' do %>
|
|
4
|
+
<% options = options_for_select( Rails.application.config.spree.promotions.actions.map(&:name).map {|name| [ t("promotion_action_types.#{name.demodulize.underscore}.name"), name] } ) %>
|
|
5
|
+
<fieldset>
|
|
6
|
+
<legend align="center"><%= t(:promotion_actions) %></legend>
|
|
7
|
+
<div class="field">
|
|
8
|
+
<%= label_tag :action_type, t(:add_action_of_type)%>
|
|
9
|
+
<%= select_tag 'action_type', options, :class => 'select2 fullwidth' %>
|
|
10
|
+
</div>
|
|
11
|
+
<div class="filter-actions actions">
|
|
12
|
+
<%= button t(:add), 'icon-plus', :class => 'fullwidth' %>
|
|
13
|
+
</div>
|
|
14
|
+
</fieldset>
|
|
15
|
+
<% end %>
|
|
3
16
|
|
|
4
17
|
<%= form_for @promotion, :url => spree.admin_promotion_path(@promotion), :method => :put do |f| %>
|
|
5
18
|
<div id="actions" class="filter_list">
|
|
6
19
|
<% if @promotion.actions.any? %>
|
|
7
20
|
<%= render :partial => 'promotion_action', :collection => @promotion.actions %>
|
|
8
21
|
<% else %>
|
|
9
|
-
|
|
22
|
+
<div class="no-objects-found">
|
|
23
|
+
<%= t(:no_actions_added) %>
|
|
24
|
+
</div>
|
|
10
25
|
<% end %>
|
|
11
26
|
</div>
|
|
12
|
-
<
|
|
13
|
-
<%= button t(:update) %>
|
|
14
|
-
</
|
|
15
|
-
<% end %>
|
|
16
|
-
|
|
17
|
-
<%= form_tag spree.admin_promotion_promotion_actions_path(@promotion), :remote => true, :id => 'new_promotion_action_form' do %>
|
|
18
|
-
<% options = options_for_select( Rails.application.config.spree.promotions.actions.map(&:name).map {|name| [ t("promotion_action_types.#{name.demodulize.underscore}.name"), name] } ) %>
|
|
19
|
-
<p>
|
|
20
|
-
<%= label_tag :action_type, t(:add_action_of_type) %>
|
|
21
|
-
<%= select_tag 'action_type', options %>
|
|
22
|
-
<%= submit_tag t(:add) %>
|
|
23
|
-
</p>
|
|
27
|
+
<div class="filter-actions actions promotion-update">
|
|
28
|
+
<%= button t(:update), 'icon-refresh' %>
|
|
29
|
+
</div>
|
|
24
30
|
<% end %>
|
|
25
31
|
|
|
26
32
|
</fieldset>
|
|
@@ -1,52 +1,56 @@
|
|
|
1
1
|
<%= render :partial => 'spree/shared/error_messages', :locals => { :target => @promotion } %>
|
|
2
|
-
<
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
<%= f.
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
2
|
+
<div class="row">
|
|
3
|
+
<div id="general_fields" class="alpha thirteen columns">
|
|
4
|
+
<div class="alpha four columns">
|
|
5
|
+
<%= f.field_container :name do %>
|
|
6
|
+
<%= f.label :name %>
|
|
7
|
+
<%= f.text_field :name, :class => 'fullwidth' %>
|
|
8
|
+
<% end %>
|
|
9
|
+
|
|
10
|
+
<%= f.field_container :event_name do %>
|
|
11
|
+
<%= f.label :event_name %>
|
|
12
|
+
<%= f.select :event_name, Spree::Activator.event_names.map{|name| [t("events.#{name}"), name] }, {}, {:class => 'select2 fullwidth'} %>
|
|
13
|
+
<% end %>
|
|
14
|
+
|
|
15
|
+
<%= f.field_container :code do %>
|
|
16
|
+
<%= f.label :code %>
|
|
17
|
+
<%= f.text_field :code, :class => 'fullwidth' %>
|
|
18
|
+
<% end %>
|
|
19
|
+
|
|
20
|
+
<%= f.field_container :path do %>
|
|
21
|
+
<%= f.label :path %>
|
|
22
|
+
<%= f.text_field :path, :class => 'fullwidth' %>
|
|
23
|
+
<% end %>
|
|
24
|
+
|
|
25
|
+
<%= f.field_container :advertise do %>
|
|
26
|
+
<%= f.check_box :advertise %>
|
|
27
|
+
<%= f.label :advertise %>
|
|
28
|
+
<% end %>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<div class="omega nine columns">
|
|
32
|
+
<%= f.field_container :description do %>
|
|
33
|
+
<%= f.label :description %><br />
|
|
34
|
+
<%= f.text_area :description, :rows => 7, :class => 'fullwidth' %>
|
|
35
|
+
<% end %>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<div id="expiry_fields" class="three columns omega">
|
|
40
|
+
<%= f.field_container :usage_limit do %>
|
|
41
|
+
<%= f.label :usage_limit %><br />
|
|
42
|
+
<%= f.number_field :usage_limit, :min => 0, :class => 'fullwidth' %><br>
|
|
43
|
+
<%= t(:current_promotion_usage, :count => @promotion.credits_count) %>
|
|
44
|
+
<% end %>
|
|
45
|
+
|
|
46
|
+
<div id="starts_at_field" class="field">
|
|
47
|
+
<%= f.label :starts_at %>
|
|
48
|
+
<%= f.text_field :starts_at, :class => 'datepicker datepicker-from fullwidth' %>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<div id="expires_at_field" class="field">
|
|
52
|
+
<%= f.label :expires_at %>
|
|
53
|
+
<%= f.text_field :expires_at, :class => 'datepicker datepicker-top fullwidth' %>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
<div class="promotion_action <%= promotion_action.type.to_s.demodulize.underscore %>" id="<%= dom_id promotion_action %>">
|
|
2
|
-
<span class="delete">
|
|
3
|
-
<%= link_to_with_icon 'cross', '', spree.admin_promotion_promotion_action_path(@promotion, promotion_action), :remote => true, :method => :delete %>
|
|
4
|
-
</span>
|
|
1
|
+
<div class="promotion_action promotion-block <%= promotion_action.type.to_s.demodulize.underscore %>" id="<%= dom_id promotion_action %>">
|
|
5
2
|
<% type_name = promotion_action.class.name.demodulize.underscore %>
|
|
3
|
+
<h6 class="promotion-title"><%= t("promotion_action_types.#{type_name}.description") %></h6>
|
|
4
|
+
<%= link_to_with_icon 'icon-trash', '', spree.admin_promotion_promotion_action_path(@promotion, promotion_action), :remote => true, :method => :delete, :class => 'delete' %>
|
|
5
|
+
|
|
6
6
|
<% param_prefix = "promotion[promotion_actions_attributes][#{promotion_action.id}]" %>
|
|
7
7
|
<%= hidden_field_tag "#{param_prefix}[id]", promotion_action.id %>
|
|
8
|
-
|
|
8
|
+
|
|
9
9
|
<%= render :partial => "spree/admin/promotions/actions/#{type_name}",
|
|
10
10
|
:locals => { :promotion_action => promotion_action, :param_prefix => param_prefix } %>
|
|
11
11
|
</div>
|