spree_core 2.1.12 → 2.2.0
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.
- checksums.yaml +4 -4
- data/app/helpers/spree/base_helper.rb +4 -7
- data/app/helpers/spree/products_helper.rb +11 -9
- data/app/helpers/spree/store_helper.rb +5 -0
- data/app/models/spree/ability.rb +4 -0
- data/app/models/spree/address.rb +6 -6
- data/app/models/spree/adjustment.rb +40 -61
- data/app/models/spree/app_configuration.rb +1 -14
- data/app/models/spree/calculator.rb +12 -4
- data/app/models/spree/calculator/default_tax.rb +42 -38
- data/app/models/spree/calculator/flat_percent_item_total.rb +2 -4
- data/app/models/spree/calculator/free_shipping.rb +5 -2
- data/app/models/spree/calculator/percent_on_line_item.rb +15 -0
- data/app/models/spree/calculator/percent_per_item.rb +3 -0
- data/app/models/spree/classification.rb +3 -2
- data/app/models/spree/credit_card.rb +7 -25
- data/app/models/spree/gateway/bogus.rb +5 -5
- data/app/models/spree/gateway/bogus_simple.rb +0 -8
- data/app/models/spree/image.rb +0 -9
- data/app/models/spree/inventory_unit.rb +10 -4
- data/app/models/spree/item_adjustments.rb +65 -0
- data/app/models/spree/legacy_user.rb +1 -0
- data/app/models/spree/line_item.rb +33 -13
- data/app/models/spree/option_type.rb +2 -2
- data/app/models/spree/option_value.rb +1 -1
- data/app/models/spree/order.rb +109 -89
- data/app/models/spree/order/checkout.rb +48 -0
- data/app/models/spree/order_contents.rb +72 -37
- data/app/models/spree/order_inventory.rb +65 -68
- data/app/models/spree/order_populator.rb +3 -17
- data/app/models/spree/order_updater.rb +63 -44
- data/app/models/spree/payment.rb +20 -5
- data/app/models/spree/payment/processing.rb +19 -25
- data/app/models/spree/payment_capture_event.rb +9 -0
- data/app/models/spree/payment_method/check.rb +0 -2
- data/app/models/spree/price.rb +1 -1
- data/app/models/spree/product.rb +14 -16
- data/app/models/spree/product/scopes.rb +4 -6
- data/app/models/spree/product_option_type.rb +2 -2
- data/app/models/spree/product_property.rb +2 -2
- data/app/models/spree/promotion.rb +71 -50
- data/app/models/spree/promotion/actions/create_adjustment.rb +31 -32
- data/app/models/spree/promotion/actions/create_item_adjustments.rb +83 -0
- data/app/models/spree/promotion/actions/free_shipping.rb +36 -0
- data/app/models/spree/promotion/rules/first_order.rb +4 -0
- data/app/models/spree/promotion/rules/item_total.rb +5 -1
- data/app/models/spree/promotion/rules/product.rb +4 -0
- data/app/models/spree/promotion/rules/user.rb +5 -6
- data/app/models/spree/promotion/rules/user_logged_in.rb +4 -0
- data/app/models/spree/promotion_action.rb +1 -5
- data/app/models/spree/promotion_handler/cart.rb +38 -0
- data/app/models/spree/promotion_handler/coupon.rb +76 -0
- data/app/models/spree/promotion_handler/free_shipping.rb +31 -0
- data/app/models/spree/promotion_handler/page.rb +24 -0
- data/app/models/spree/promotion_rule.rb +15 -7
- data/app/models/spree/property.rb +1 -1
- data/app/models/spree/return_authorization.rb +7 -1
- data/app/models/spree/shipment.rb +113 -49
- data/app/models/spree/shipping_calculator.rb +4 -5
- data/app/models/spree/shipping_category.rb +2 -2
- data/app/models/spree/shipping_method.rb +12 -6
- data/app/models/spree/shipping_rate.rb +27 -7
- data/app/models/spree/stock/availability_validator.rb +1 -1
- data/app/models/spree/stock/estimator.rb +13 -1
- data/app/models/spree/stock/package.rb +11 -7
- data/app/models/spree/stock/packer.rb +3 -3
- data/app/models/spree/stock/quantifier.rb +9 -1
- data/app/models/spree/stock_item.rb +11 -6
- data/app/models/spree/stock_movement.rb +1 -2
- data/app/models/spree/tax_category.rb +6 -1
- data/app/models/spree/tax_rate.rb +57 -49
- data/app/models/spree/taxon.rb +10 -5
- data/app/models/spree/taxonomy.rb +5 -2
- data/app/models/spree/variant.rb +33 -16
- data/app/models/spree/zone.rb +24 -24
- data/app/views/spree/shared/_routes.html.erb +3 -0
- data/config/locales/en.yml +42 -26
- data/db/migrate/20130213191427_create_default_stock.rb +3 -3
- data/db/migrate/20130413230529_add_name_to_spree_credit_cards.rb +5 -0
- data/db/migrate/20130414000512_update_name_fields_on_spree_credit_cards.rb +13 -0
- data/db/migrate/20130417120035_update_adjustment_states.rb +2 -2
- data/db/migrate/20130417123427_add_shipping_rates_to_shipments.rb +1 -1
- data/db/migrate/20130509115210_add_number_to_stock_transfer.rb +1 -1
- data/db/migrate/20130611054351_rename_shipping_methods_zones_to_spree_shipping_methods_zones.rb +0 -5
- data/db/migrate/20130802022321_migrate_tax_categories_to_line_items.rb +7 -5
- data/db/migrate/20130807024301_upgrade_adjustments.rb +39 -0
- data/db/migrate/20130807024302_rename_adjustment_fields.rb +17 -0
- data/db/migrate/20130813004002_add_shipment_total_to_spree_orders.rb +5 -0
- data/db/migrate/20130813232134_rename_activators_to_promotions.rb +5 -0
- data/db/migrate/20130815000406_add_adjustment_total_to_line_items.rb +5 -0
- data/db/migrate/20130815024413_add_adjustment_total_to_shipments.rb +5 -0
- data/db/migrate/20130828234942_add_tax_total_to_line_items_shipments_and_orders.rb +8 -0
- data/db/migrate/20130830001159_migrate_old_shipping_calculators.rb +1 -1
- data/db/migrate/20130903183026_add_code_to_spree_promotion_rules.rb +5 -0
- data/db/migrate/20130917024658_remove_promotions_event_name_field.rb +5 -0
- data/db/migrate/20130924040529_add_promo_total_to_line_items_and_shipments_and_orders.rb +7 -0
- data/db/migrate/20131001013410_remove_unused_credit_card_fields.rb +7 -3
- data/db/migrate/20131107132123_add_tax_category_to_variants.rb +6 -0
- data/db/migrate/20131118043959_add_included_to_adjustments.rb +5 -0
- data/db/migrate/20131118050234_rename_tax_total_fields.rb +11 -0
- data/db/migrate/20131118183431_add_line_item_id_to_spree_inventory_units.rb +21 -0
- data/db/migrate/20131127001002_add_position_to_classifications.rb +5 -0
- data/db/migrate/20131211112807_create_spree_orders_promotions.rb +8 -0
- data/db/migrate/20131218054603_add_item_count_to_spree_orders.rb +5 -0
- data/db/migrate/20140106224208_rename_permalink_to_slug_for_products.rb +5 -0
- data/db/migrate/20140124023232_rename_activator_id_in_rules_and_actions_to_promotion_id.rb +6 -0
- data/db/migrate/20140203161722_add_approver_id_and_approved_at_to_orders.rb +6 -0
- data/db/migrate/20140204115338_add_confirmation_delivered_to_spree_orders.rb +5 -0
- data/db/migrate/20140205120320_create_spree_payment_capture_events.rb +12 -0
- data/db/migrate/20140205144710_add_uncaptured_amount_to_payments.rb +5 -0
- data/db/migrate/20140207085910_add_tax_category_id_to_shipping_methods.rb +5 -0
- data/db/migrate/20140207093021_add_tax_rate_id_to_shipping_rates.rb +5 -0
- data/db/migrate/20140211040159_add_pre_tax_amount_to_line_items_and_shipments.rb +6 -0
- data/db/migrate/20140213184916_add_more_indexes.rb +13 -0
- data/db/migrate/20140219060952_add_considered_risky_to_orders.rb +5 -0
- data/lib/generators/spree/dummy/dummy_generator.rb +1 -6
- data/lib/generators/spree/install/install_generator.rb +6 -6
- data/lib/generators/spree/install/templates/{app/assets/javascripts/admin → vendor/assets/javascripts/spree/backend}/all.js +3 -3
- data/lib/generators/spree/install/templates/{app/assets/javascripts/store → vendor/assets/javascripts/spree/frontend}/all.js +3 -3
- data/lib/generators/spree/install/templates/{app/assets/stylesheets/store → vendor/assets/stylesheets/spree/backend}/all.css +3 -3
- data/lib/generators/spree/install/templates/{app/assets/stylesheets/admin → vendor/assets/stylesheets/spree/frontend}/all.css +3 -3
- data/lib/spree/core.rb +21 -8
- data/lib/spree/core/calculated_adjustments.rb +0 -40
- data/lib/spree/core/controller_helpers.rb +5 -0
- data/lib/spree/core/controller_helpers/auth.rb +2 -2
- data/lib/spree/core/controller_helpers/common.rb +0 -5
- data/lib/spree/core/controller_helpers/order.rb +8 -9
- data/lib/spree/core/engine.rb +10 -17
- data/lib/spree/core/permalinks.rb +1 -1
- data/lib/spree/core/product_duplicator.rb +3 -8
- data/lib/spree/core/user_address.rb +1 -1
- data/lib/spree/core/validators/email.rb +23 -1
- data/lib/spree/core/version.rb +1 -1
- data/lib/spree/money.rb +1 -1
- data/lib/spree/permitted_attributes.rb +2 -2
- data/lib/spree/testing_support/caching.rb +47 -0
- data/lib/spree/testing_support/factories/adjustment_factory.rb +11 -2
- data/lib/spree/testing_support/factories/credit_card_factory.rb +2 -1
- data/lib/spree/testing_support/factories/order_factory.rb +10 -5
- data/lib/spree/testing_support/factories/payment_factory.rb +2 -2
- data/lib/spree/testing_support/factories/payment_method_factory.rb +3 -3
- data/lib/spree/testing_support/factories/promotion_factory.rb +16 -1
- data/lib/spree/testing_support/factories/shipment_factory.rb +8 -4
- data/lib/spree/testing_support/factories/shipping_method_factory.rb +1 -3
- data/lib/spree/testing_support/factories/stock_factory.rb +1 -1
- data/lib/spree/testing_support/factories/tax_rate_factory.rb +2 -2
- data/lib/spree/testing_support/order_walkthrough.rb +1 -1
- data/lib/tasks/core.rake +2 -2
- data/vendor/assets/fonts/FontAwesome.otf +0 -0
- data/vendor/assets/fonts/fontawesome-webfont.eot +0 -0
- data/vendor/assets/fonts/fontawesome-webfont.svg +399 -0
- data/vendor/assets/fonts/fontawesome-webfont.ttf +0 -0
- data/vendor/assets/fonts/fontawesome-webfont.woff +0 -0
- data/vendor/assets/stylesheets/font-awesome.scss +1475 -0
- metadata +73 -44
- data/app/assets/javascripts/admin/handlebar_extensions.js +0 -9
- data/app/helpers/spree/admin/adjustments_helper.rb +0 -26
- data/app/helpers/spree/admin/images_helper.rb +0 -18
- data/app/helpers/spree/promotion_rules_helper.rb +0 -13
- data/app/models/spree/activator.rb +0 -29
- data/app/models/spree/calculator/per_item.rb +0 -41
- data/app/models/spree/stock/remaining_packer.rb +0 -22
- data/app/views/spree/payments/_payment.html.erb +0 -18
- data/db/migrate/20131118041203_add_tax_total_to_spree_orders.rb +0 -5
- data/db/migrate/20131118043021_add_order_id_to_spree_adjustments.rb +0 -6
- data/db/migrate/20131118074808_add_included_to_spree_adjustments.rb +0 -5
- data/db/migrate/20140415041315_add_user_id_created_by_id_index_to_order.rb +0 -5
- data/lib/spree/core/gateway_error.rb +0 -5
- data/lib/spree/core/preference_rescue.rb +0 -25
- data/lib/spree/core/s3_support.rb +0 -25
- data/lib/spree/promo/coupon_applicator.rb +0 -71
- data/lib/spree/testing_support/factories/activator_factory.rb +0 -8
@@ -1,12 +1,10 @@
|
|
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
|
include Spree::Core::CalculatedAdjustments
|
8
6
|
|
9
|
-
has_many :adjustments, as: :
|
7
|
+
has_many :adjustments, as: :source
|
10
8
|
|
11
9
|
delegate :eligible?, to: :promotion
|
12
10
|
|
@@ -15,32 +13,23 @@ module Spree
|
|
15
13
|
|
16
14
|
# Creates the adjustment related to a promotion for the order passed
|
17
15
|
# through options hash
|
16
|
+
#
|
17
|
+
# Returns `true` if an adjustment is applied to an order,
|
18
|
+
# `false` if the promotion has already been applied.
|
18
19
|
def perform(options = {})
|
19
20
|
order = options[:order]
|
20
|
-
return if
|
21
|
-
|
22
|
-
self.create_adjustment("#{Spree.t(:promotion)} (#{promotion.name})", order, order)
|
23
|
-
end
|
21
|
+
return if promotion_credit_exists?(order)
|
24
22
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
#
|
29
|
-
# Currently an adjustment is created even when its promotion is not eligible.
|
30
|
-
# This helps to figure out later which adjustments should be eligible
|
31
|
-
# as the order is being updated
|
32
|
-
#
|
33
|
-
# BTW The order is updated (through order#update) every time an adjustment
|
34
|
-
# is saved
|
35
|
-
def create_adjustment(label, target, calculable, mandatory=false)
|
36
|
-
amount = compute_amount(calculable)
|
37
|
-
target.adjustments.create(
|
23
|
+
amount = compute_amount(order)
|
24
|
+
return if amount == 0
|
25
|
+
Spree::Adjustment.create!(
|
38
26
|
amount: amount,
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
27
|
+
order: order,
|
28
|
+
adjustable: order,
|
29
|
+
source: self,
|
30
|
+
label: "#{Spree.t(:promotion)} (#{promotion.name})"
|
43
31
|
)
|
32
|
+
true
|
44
33
|
end
|
45
34
|
|
46
35
|
# Ensure a negative amount which does not exceed the sum of the order's
|
@@ -51,20 +40,30 @@ module Spree
|
|
51
40
|
end
|
52
41
|
|
53
42
|
private
|
43
|
+
# Tells us if there if the specified promotion is already associated with the line item
|
44
|
+
# regardless of whether or not its currently eligible. Useful because generally
|
45
|
+
# you would only want a promotion action to apply to order no more than once.
|
46
|
+
#
|
47
|
+
# Receives an adjustment +source+ (here a PromotionAction object) and tells
|
48
|
+
# if the order has adjustments from that already
|
49
|
+
def promotion_credit_exists?(adjustable)
|
50
|
+
self.adjustments.where(:adjustable_id => adjustable.id).exists?
|
51
|
+
end
|
52
|
+
|
54
53
|
def ensure_action_has_calculator
|
55
54
|
return if self.calculator
|
56
55
|
self.calculator = Calculator::FlatPercentItemTotal.new
|
57
56
|
end
|
58
57
|
|
59
58
|
def deals_with_adjustments
|
60
|
-
self.adjustments.
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
59
|
+
adjustment_scope = self.adjustments.joins("LEFT OUTER JOIN spree_orders ON spree_orders.id = spree_adjustments.adjustable_id")
|
60
|
+
# For incomplete orders, remove the adjustment completely.
|
61
|
+
adjustment_scope.where("spree_orders.completed_at IS NULL").readonly(false).destroy_all
|
62
|
+
|
63
|
+
# For complete orders, the source will be invalid.
|
64
|
+
# Therefore we nullify the source_id, leaving the adjustment in place.
|
65
|
+
# This would mean that the order's total is not altered at all.
|
66
|
+
adjustment_scope.where("spree_orders.completed_at IS NOT NULL").update_all("source_id = NULL")
|
68
67
|
end
|
69
68
|
end
|
70
69
|
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Spree
|
2
|
+
class Promotion
|
3
|
+
module Actions
|
4
|
+
class CreateItemAdjustments < PromotionAction
|
5
|
+
include Spree::Core::CalculatedAdjustments
|
6
|
+
|
7
|
+
has_many :adjustments, as: :source
|
8
|
+
|
9
|
+
delegate :eligible?, to: :promotion
|
10
|
+
|
11
|
+
before_validation :ensure_action_has_calculator
|
12
|
+
before_destroy :deals_with_adjustments
|
13
|
+
|
14
|
+
def perform(payload = {})
|
15
|
+
order = payload[:order]
|
16
|
+
# Find only the line items which have not already been adjusted by this promotion
|
17
|
+
# HACK: Need to use [0] because `pluck` may return an empty array, which AR helpfully
|
18
|
+
# coverts to meaning NOT IN (NULL) and the DB isn't happy about that.
|
19
|
+
already_adjusted_line_items = [0] + self.adjustments.pluck(:adjustable_id)
|
20
|
+
result = false
|
21
|
+
order.line_items.where("id NOT IN (?)", already_adjusted_line_items).find_each do |line_item|
|
22
|
+
current_result = self.create_adjustment(line_item, order)
|
23
|
+
result ||= current_result
|
24
|
+
end
|
25
|
+
return result
|
26
|
+
end
|
27
|
+
|
28
|
+
def create_adjustment(adjustable, order)
|
29
|
+
amount = self.compute_amount(adjustable)
|
30
|
+
return if amount == 0
|
31
|
+
self.adjustments.create!(
|
32
|
+
amount: amount,
|
33
|
+
adjustable: adjustable,
|
34
|
+
order: order,
|
35
|
+
label: "#{Spree.t(:promotion)} (#{promotion.name})",
|
36
|
+
)
|
37
|
+
true
|
38
|
+
end
|
39
|
+
|
40
|
+
# Ensure a negative amount which does not exceed the sum of the order's
|
41
|
+
# item_total and ship_total
|
42
|
+
def compute_amount(adjustable)
|
43
|
+
amount = self.calculator.compute(adjustable).to_f.abs
|
44
|
+
[adjustable.total, amount].min * -1
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
# Tells us if there if the specified promotion is already associated with the line item
|
49
|
+
# regardless of whether or not its currently eligible. Useful because generally
|
50
|
+
# you would only want a promotion action to apply to line item no more than once.
|
51
|
+
#
|
52
|
+
# Receives an adjustment +source+ (here a PromotionAction object) and tells
|
53
|
+
# if the order has adjustments from that already
|
54
|
+
def promotion_credit_exists?(adjustable)
|
55
|
+
self.adjustments.where(:adjustable_id => adjustable.id).exists?
|
56
|
+
end
|
57
|
+
|
58
|
+
def ensure_action_has_calculator
|
59
|
+
return if self.calculator
|
60
|
+
self.calculator = Calculator::PercentOnLineItem.new
|
61
|
+
end
|
62
|
+
|
63
|
+
def deals_with_adjustments
|
64
|
+
adjustment_scope = Adjustment.includes(:order).references(:spree_orders)
|
65
|
+
# For incomplete orders, remove the adjustment completely.
|
66
|
+
adjustment_scope.where("spree_orders.completed_at IS NULL").each do |adjustment|
|
67
|
+
adjustment.destroy
|
68
|
+
end
|
69
|
+
|
70
|
+
# For complete orders, the source will be invalid.
|
71
|
+
# Therefore we nullify the source_id, leaving the adjustment in place.
|
72
|
+
# This would mean that the order's total is not altered at all.
|
73
|
+
adjustment_scope.where("spree_orders.completed_at IS NOT NULL").each do |adjustment|
|
74
|
+
adjustment.update_columns(
|
75
|
+
source_id: nil,
|
76
|
+
updated_at: Time.now,
|
77
|
+
)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Spree
|
2
|
+
class Promotion
|
3
|
+
module Actions
|
4
|
+
class FreeShipping < Spree::PromotionAction
|
5
|
+
def perform(payload={})
|
6
|
+
order = payload[:order]
|
7
|
+
|
8
|
+
label = "#{Spree.t(:promotion)} (#{promotion.name})"
|
9
|
+
results = order.shipments.map do |shipment|
|
10
|
+
return false if promotion_credit_exists?(shipment)
|
11
|
+
shipment.adjustments.create!(
|
12
|
+
order: shipment.order,
|
13
|
+
amount: compute_amount(shipment),
|
14
|
+
source: self,
|
15
|
+
label: label,
|
16
|
+
)
|
17
|
+
true
|
18
|
+
end
|
19
|
+
# Did we actually end up applying any adjustments?
|
20
|
+
# If so, then this action should be classed as 'successful'
|
21
|
+
results.any? { |r| r == true }
|
22
|
+
end
|
23
|
+
|
24
|
+
def compute_amount(shipment)
|
25
|
+
shipment.cost * -1
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def promotion_credit_exists?(shipment)
|
31
|
+
shipment.adjustments.where(:source_id => self.id).exists?
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -9,8 +9,12 @@ module Spree
|
|
9
9
|
|
10
10
|
OPERATORS = ['gt', 'gte']
|
11
11
|
|
12
|
+
def applicable?(promotable)
|
13
|
+
promotable.is_a?(Spree::Order)
|
14
|
+
end
|
15
|
+
|
12
16
|
def eligible?(order, options = {})
|
13
|
-
item_total = order.
|
17
|
+
item_total = order.item_total
|
14
18
|
item_total.send(preferred_operator == 'gte' ? :>= : :>, BigDecimal.new(preferred_amount.to_s))
|
15
19
|
end
|
16
20
|
end
|
@@ -2,12 +2,11 @@ module Spree
|
|
2
2
|
class Promotion
|
3
3
|
module Rules
|
4
4
|
class User < PromotionRule
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
has_and_belongs_to_many :users, join_table: 'spree_promotion_rules_users', foreign_key: 'promotion_rule_id', :class_name => Spree.user_class
|
5
|
+
belongs_to :user, class_name: Spree.user_class
|
6
|
+
has_and_belongs_to_many :users, class_name: Spree.user_class, join_table: 'spree_promotion_rules_users', foreign_key: 'promotion_rule_id'
|
7
|
+
|
8
|
+
def applicable?(promotable)
|
9
|
+
promotable.is_a?(Spree::Order)
|
11
10
|
end
|
12
11
|
|
13
12
|
def eligible?(order, options = {})
|
@@ -2,7 +2,7 @@
|
|
2
2
|
# PromotionActions perform the necessary tasks when a promotion is activated by an event and determined to be eligible.
|
3
3
|
module Spree
|
4
4
|
class PromotionAction < ActiveRecord::Base
|
5
|
-
belongs_to :promotion,
|
5
|
+
belongs_to :promotion, class_name: 'Spree::Promotion'
|
6
6
|
|
7
7
|
scope :of_type, ->(t) { where(type: t) }
|
8
8
|
|
@@ -13,9 +13,5 @@ module Spree
|
|
13
13
|
def perform(options = {})
|
14
14
|
raise 'perform should be implemented in a sub-class of PromotionAction'
|
15
15
|
end
|
16
|
-
|
17
|
-
def credit_exists_on_order?(order)
|
18
|
-
!!order.promotion_credit_exists?(self)
|
19
|
-
end
|
20
16
|
end
|
21
17
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Spree
|
2
|
+
module PromotionHandler
|
3
|
+
# Decides which promotion should be activated given the current order context
|
4
|
+
#
|
5
|
+
# By activated it doesn't necessarily mean that the order will have a
|
6
|
+
# discount for every activated promotion. It means that the discount will be
|
7
|
+
# created and might eventually become eligible. The intention here is to
|
8
|
+
# reduce overhead. e.g. a promotion that requires item A to be eligible
|
9
|
+
# shouldn't be eligible unless item A is added to the order.
|
10
|
+
#
|
11
|
+
# It can be used as a wrapper for custom handlers as well. Different
|
12
|
+
# applications might have completely different requirements to make
|
13
|
+
# the promotions system accurate and performant. Here they can plug custom
|
14
|
+
# handler to activate promos as they wish once an item is added to cart
|
15
|
+
class Cart
|
16
|
+
attr_reader :line_item, :order
|
17
|
+
attr_accessor :error, :success
|
18
|
+
|
19
|
+
def initialize(order, line_item=nil)
|
20
|
+
@order, @line_item = order, line_item
|
21
|
+
end
|
22
|
+
|
23
|
+
def activate
|
24
|
+
promotions.each do |promotion|
|
25
|
+
if (line_item && promotion.eligible?(line_item)) || promotion.eligible?(order)
|
26
|
+
promotion.activate(line_item: line_item, order: order)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def promotions
|
34
|
+
Promotion.active.includes(:promotion_rules).where(:code => nil, :path => nil)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Spree
|
2
|
+
module PromotionHandler
|
3
|
+
class Coupon
|
4
|
+
attr_reader :order
|
5
|
+
attr_accessor :error, :success
|
6
|
+
|
7
|
+
def initialize(order)
|
8
|
+
@order = order
|
9
|
+
end
|
10
|
+
|
11
|
+
def apply
|
12
|
+
if order.coupon_code.present?
|
13
|
+
if promotion.present? && promotion.actions.exists?
|
14
|
+
handle_present_promotion(promotion)
|
15
|
+
else
|
16
|
+
if Promotion.with_coupon_code(order.coupon_code).try(:expired?)
|
17
|
+
self.error = Spree.t(:coupon_code_expired)
|
18
|
+
else
|
19
|
+
self.error = Spree.t(:coupon_code_not_found)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
def promotion
|
28
|
+
@promotion ||= Promotion.active.includes(:promotion_rules, :promotion_actions).with_coupon_code(order.coupon_code)
|
29
|
+
end
|
30
|
+
|
31
|
+
def successful?
|
32
|
+
success.present? && error.blank?
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def handle_present_promotion(promotion)
|
38
|
+
return promotion_usage_limit_exceeded if promotion.usage_limit_exceeded?(order)
|
39
|
+
return ineligible_for_this_order unless promotion.eligible?(order)
|
40
|
+
|
41
|
+
# If any of the actions for the promotion return `true`,
|
42
|
+
# then result here will also be `true`.
|
43
|
+
result = promotion.activate(:order => order)
|
44
|
+
if result
|
45
|
+
determine_promotion_application_result(result)
|
46
|
+
else
|
47
|
+
self.error = Spree.t(:coupon_code_already_applied)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def promotion_usage_limit_exceeded
|
52
|
+
self.error = Spree.t(:coupon_code_max_usage)
|
53
|
+
end
|
54
|
+
|
55
|
+
def ineligible_for_this_order
|
56
|
+
self.error = Spree.t(:coupon_code_not_eligible)
|
57
|
+
end
|
58
|
+
|
59
|
+
def determine_promotion_application_result(result)
|
60
|
+
detector = lambda { |p| p.source.promotion.code.downcase == order.coupon_code.downcase }
|
61
|
+
discount = order.line_item_adjustments.promotion.detect(&detector)
|
62
|
+
discount ||= order.shipment_adjustments.promotion.detect(&detector)
|
63
|
+
discount ||= order.adjustments.promotion.detect(&detector)
|
64
|
+
|
65
|
+
if result and discount.eligible
|
66
|
+
order.update_totals
|
67
|
+
order.persist_totals
|
68
|
+
self.success = Spree.t(:coupon_code_applied)
|
69
|
+
else
|
70
|
+
# if the promotion was created after the order
|
71
|
+
self.error = Spree.t(:coupon_code_not_found)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Spree
|
2
|
+
module PromotionHandler
|
3
|
+
# Used for activating promotions with shipping rules
|
4
|
+
class FreeShipping
|
5
|
+
attr_reader :order
|
6
|
+
attr_accessor :error, :success
|
7
|
+
|
8
|
+
def initialize(order)
|
9
|
+
@order = order
|
10
|
+
end
|
11
|
+
|
12
|
+
def activate
|
13
|
+
promotions.each do |promotion|
|
14
|
+
if promotion.eligible?(order)
|
15
|
+
promotion.activate(order: order)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def promotions
|
23
|
+
Spree::Promotion.active.where({
|
24
|
+
:id => Spree::Promotion::Actions::FreeShipping.pluck(:promotion_id),
|
25
|
+
:code => nil,
|
26
|
+
:path => nil
|
27
|
+
})
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|