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
data/app/models/spree/payment.rb
CHANGED
@@ -4,16 +4,19 @@ module Spree
|
|
4
4
|
|
5
5
|
IDENTIFIER_CHARS = (('A'..'Z').to_a + ('0'..'9').to_a - %w(0 1 I O)).freeze
|
6
6
|
|
7
|
-
belongs_to :order, class_name: 'Spree::Order', touch: true
|
7
|
+
belongs_to :order, class_name: 'Spree::Order', touch: true, inverse_of: :payments
|
8
8
|
belongs_to :source, polymorphic: true
|
9
9
|
belongs_to :payment_method, class_name: 'Spree::PaymentMethod'
|
10
10
|
|
11
11
|
has_many :offsets, -> { where("source_type = 'Spree::Payment' AND amount < 0 AND state = 'completed'") },
|
12
12
|
class_name: "Spree::Payment", foreign_key: :source_id
|
13
13
|
has_many :log_entries, as: :source
|
14
|
+
has_many :state_changes, as: :stateful
|
15
|
+
has_many :capture_events, :class_name => 'Spree::PaymentCaptureEvent'
|
14
16
|
|
15
17
|
before_validation :validate_source
|
16
18
|
before_create :set_unique_identifier
|
19
|
+
before_save :update_uncaptured_amount
|
17
20
|
|
18
21
|
after_save :create_payment_profile, if: :profiles_supported?
|
19
22
|
|
@@ -67,6 +70,14 @@ module Spree
|
|
67
70
|
event :invalidate do
|
68
71
|
transition from: [:checkout], to: :invalid
|
69
72
|
end
|
73
|
+
|
74
|
+
after_transition do |payment, transition|
|
75
|
+
payment.state_changes.create!(
|
76
|
+
previous_state: transition.from,
|
77
|
+
next_state: transition.to,
|
78
|
+
name: 'payment',
|
79
|
+
)
|
80
|
+
end
|
70
81
|
end
|
71
82
|
|
72
83
|
def currency
|
@@ -83,7 +94,7 @@ module Spree
|
|
83
94
|
case amount
|
84
95
|
when String
|
85
96
|
separator = I18n.t('number.currency.format.separator')
|
86
|
-
number = amount.delete("^0-9-#{separator}
|
97
|
+
number = amount.delete("^0-9-#{separator}").tr(separator, '.')
|
87
98
|
number.to_d if number.present?
|
88
99
|
end || amount
|
89
100
|
end
|
@@ -93,7 +104,7 @@ module Spree
|
|
93
104
|
end
|
94
105
|
|
95
106
|
def credit_allowed
|
96
|
-
amount - offsets_total
|
107
|
+
amount - offsets_total
|
97
108
|
end
|
98
109
|
|
99
110
|
def can_credit?
|
@@ -102,8 +113,8 @@ module Spree
|
|
102
113
|
|
103
114
|
# see https://github.com/spree/spree/issues/981
|
104
115
|
def build_source
|
105
|
-
return
|
106
|
-
if
|
116
|
+
return if source_attributes.nil?
|
117
|
+
if payment_method and payment_method.payment_source_class
|
107
118
|
self.source = payment_method.payment_source_class.new(source_attributes)
|
108
119
|
end
|
109
120
|
end
|
@@ -179,5 +190,9 @@ module Spree
|
|
179
190
|
def generate_identifier
|
180
191
|
Array.new(8){ IDENTIFIER_CHARS.sample }.join
|
181
192
|
end
|
193
|
+
|
194
|
+
def update_uncaptured_amount
|
195
|
+
self.uncaptured_amount = amount - capture_events.sum(:amount)
|
196
|
+
end
|
182
197
|
end
|
183
198
|
end
|
@@ -27,28 +27,31 @@ module Spree
|
|
27
27
|
gateway_action(source, :authorize, :pend)
|
28
28
|
end
|
29
29
|
|
30
|
+
# Captures the entire amount of a payment.
|
30
31
|
def purchase!
|
31
32
|
started_processing!
|
32
|
-
gateway_action(source, :purchase, :complete)
|
33
|
+
result = gateway_action(source, :purchase, :complete)
|
34
|
+
# This won't be called if gateway_action raises a GatewayError
|
35
|
+
capture_events.create!(amount: amount)
|
33
36
|
end
|
34
37
|
|
35
|
-
|
38
|
+
# Takes the amount in cents to capture.
|
39
|
+
# Can be used to capture partial amounts of a payment.
|
40
|
+
def capture!(amount=nil)
|
41
|
+
amount ||= money.money.cents
|
36
42
|
return true if completed?
|
37
43
|
started_processing!
|
38
44
|
protect_from_connection_error do
|
39
45
|
check_environment
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
gateway_options)
|
50
|
-
end
|
51
|
-
|
46
|
+
# Standard ActiveMerchant capture usage
|
47
|
+
response = payment_method.capture(
|
48
|
+
amount,
|
49
|
+
response_code,
|
50
|
+
gateway_options
|
51
|
+
)
|
52
|
+
|
53
|
+
money = ::Money.new(amount, Spree::Config[:currency])
|
54
|
+
capture_events.create!(amount: money.to_f)
|
52
55
|
handle_response(response, :complete, :failure)
|
53
56
|
end
|
54
57
|
end
|
@@ -108,14 +111,6 @@ module Spree
|
|
108
111
|
end
|
109
112
|
end
|
110
113
|
|
111
|
-
def cancel!
|
112
|
-
if payment_method.respond_to?(:cancel)
|
113
|
-
payment_method.cancel(response_code)
|
114
|
-
else
|
115
|
-
credit!
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
114
|
def partial_credit(amount)
|
120
115
|
return if amount > credit_allowed
|
121
116
|
started_processing!
|
@@ -123,7 +118,6 @@ module Spree
|
|
123
118
|
end
|
124
119
|
|
125
120
|
def gateway_options
|
126
|
-
order.reload
|
127
121
|
options = { :email => order.email,
|
128
122
|
:customer => order.email,
|
129
123
|
:customer_id => order.user_id,
|
@@ -135,9 +129,9 @@ module Spree
|
|
135
129
|
:order_id => gateway_order_id }
|
136
130
|
|
137
131
|
options.merge!({ :shipping => order.ship_total * 100,
|
138
|
-
:tax => order.
|
132
|
+
:tax => order.additional_tax_total * 100,
|
139
133
|
:subtotal => order.item_total * 100,
|
140
|
-
:discount => order.
|
134
|
+
:discount => order.promo_total * 100,
|
141
135
|
:currency => currency })
|
142
136
|
|
143
137
|
options.merge!({ :billing_address => order.bill_address.try(:active_merchant_hash),
|
data/app/models/spree/price.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Spree
|
2
2
|
class Price < ActiveRecord::Base
|
3
3
|
acts_as_paranoid
|
4
|
-
belongs_to :variant, class_name: 'Spree::Variant'
|
4
|
+
belongs_to :variant, class_name: 'Spree::Variant', inverse_of: :prices
|
5
5
|
|
6
6
|
validate :check_price
|
7
7
|
validates :amount, numericality: { greater_than_or_equal_to: 0 }, allow_nil: true
|
data/app/models/spree/product.rb
CHANGED
@@ -20,18 +20,21 @@
|
|
20
20
|
|
21
21
|
module Spree
|
22
22
|
class Product < ActiveRecord::Base
|
23
|
+
extend FriendlyId
|
24
|
+
friendly_id :name, use: :slugged
|
25
|
+
|
23
26
|
acts_as_paranoid
|
24
|
-
has_many :product_option_types, dependent: :destroy
|
27
|
+
has_many :product_option_types, dependent: :destroy, inverse_of: :product
|
25
28
|
has_many :option_types, through: :product_option_types
|
26
|
-
has_many :product_properties, dependent: :destroy
|
29
|
+
has_many :product_properties, dependent: :destroy, inverse_of: :product
|
27
30
|
has_many :properties, through: :product_properties
|
28
31
|
|
29
|
-
has_many :classifications, dependent: :delete_all
|
32
|
+
has_many :classifications, dependent: :delete_all, inverse_of: :product
|
30
33
|
has_many :taxons, through: :classifications
|
31
34
|
has_and_belongs_to_many :promotion_rules, join_table: :spree_products_promotion_rules
|
32
35
|
|
33
36
|
belongs_to :tax_category, class_name: 'Spree::TaxCategory'
|
34
|
-
belongs_to :shipping_category, class_name: 'Spree::ShippingCategory'
|
37
|
+
belongs_to :shipping_category, class_name: 'Spree::ShippingCategory', inverse_of: :products
|
35
38
|
|
36
39
|
has_one :master,
|
37
40
|
-> { where is_master: true },
|
@@ -62,6 +65,8 @@ module Spree
|
|
62
65
|
after_create :add_properties_and_option_types_from_prototype
|
63
66
|
after_create :build_variants_from_option_values_hash, if: :option_values_hash
|
64
67
|
after_save :save_master
|
68
|
+
after_save :touch
|
69
|
+
after_touch :touch_taxons
|
65
70
|
|
66
71
|
delegate :images, to: :master, prefix: true
|
67
72
|
alias_method :images, :master_images
|
@@ -69,24 +74,20 @@ module Spree
|
|
69
74
|
has_many :variant_images, -> { order(:position) }, source: :images, through: :variants_including_master
|
70
75
|
|
71
76
|
validates :name, presence: true
|
72
|
-
validates :permalink, presence: true
|
73
77
|
validates :price, presence: true, if: proc { Spree::Config[:require_master_price] }
|
74
78
|
validates :shipping_category_id, presence: true
|
79
|
+
validates :slug, length: { minimum: 3 }
|
75
80
|
|
76
81
|
attr_accessor :option_values_hash
|
77
82
|
|
78
83
|
accepts_nested_attributes_for :product_properties, allow_destroy: true, reject_if: lambda { |pp| pp[:property_name].blank? }
|
79
84
|
|
80
|
-
make_permalink order: :name
|
81
|
-
|
82
85
|
alias :options :product_option_types
|
83
86
|
|
84
87
|
after_initialize :ensure_master
|
85
88
|
|
86
|
-
before_destroy :punch_permalink
|
87
|
-
|
88
89
|
def to_param
|
89
|
-
|
90
|
+
slug
|
90
91
|
end
|
91
92
|
|
92
93
|
# the master variant is not a member of the variants array
|
@@ -131,11 +132,8 @@ module Spree
|
|
131
132
|
!!deleted_at
|
132
133
|
end
|
133
134
|
|
134
|
-
# determine if product is available.
|
135
|
-
# deleted products and products with nil or future available_on date
|
136
|
-
# are not available
|
137
135
|
def available?
|
138
|
-
!(available_on.nil? || available_on.future?)
|
136
|
+
!(available_on.nil? || available_on.future?)
|
139
137
|
end
|
140
138
|
|
141
139
|
# split variants list into hash which shows mapping of opt value onto matching variants
|
@@ -251,8 +249,8 @@ module Spree
|
|
251
249
|
self.master ||= Variant.new
|
252
250
|
end
|
253
251
|
|
254
|
-
def
|
255
|
-
|
252
|
+
def touch_taxons
|
253
|
+
self.taxons.each(&:touch)
|
256
254
|
end
|
257
255
|
end
|
258
256
|
end
|
@@ -67,11 +67,9 @@ module Spree
|
|
67
67
|
#
|
68
68
|
# SELECT COUNT(*) ...
|
69
69
|
add_search_scope :in_taxon do |taxon|
|
70
|
-
|
71
|
-
where(
|
72
|
-
|
73
|
-
where(Taxon.table_name => { :id => taxon.self_and_descendants.pluck(:id) })
|
74
|
-
)
|
70
|
+
includes(:classifications).
|
71
|
+
where("spree_products_taxons.taxon_id" => taxon.self_and_descendants.pluck(:id)).
|
72
|
+
order("spree_products_taxons.position ASC")
|
75
73
|
end
|
76
74
|
|
77
75
|
# This scope selects products in all taxons AND all its descendants
|
@@ -216,7 +214,7 @@ module Spree
|
|
216
214
|
distinct_fields = ["id", sort_column].compact.join(",")
|
217
215
|
select("DISTINCT ON(#{distinct_fields}) spree_products.*")
|
218
216
|
else
|
219
|
-
|
217
|
+
all
|
220
218
|
end
|
221
219
|
else
|
222
220
|
select("DISTINCT spree_products.*")
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module Spree
|
2
2
|
class ProductOptionType < ActiveRecord::Base
|
3
|
-
belongs_to :product, class_name: 'Spree::Product'
|
4
|
-
belongs_to :option_type, class_name: 'Spree::OptionType'
|
3
|
+
belongs_to :product, class_name: 'Spree::Product', inverse_of: :product_option_types
|
4
|
+
belongs_to :option_type, class_name: 'Spree::OptionType', inverse_of: :product_option_types
|
5
5
|
acts_as_list scope: :product
|
6
6
|
end
|
7
7
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module Spree
|
2
2
|
class ProductProperty < ActiveRecord::Base
|
3
|
-
belongs_to :product, touch: true, class_name: 'Spree::Product'
|
4
|
-
belongs_to :property, class_name: 'Spree::Property'
|
3
|
+
belongs_to :product, touch: true, class_name: 'Spree::Product', inverse_of: :product_properties
|
4
|
+
belongs_to :property, class_name: 'Spree::Property', inverse_of: :product_properties
|
5
5
|
|
6
6
|
validates :property, presence: true
|
7
7
|
validates :value, length: { maximum: 255 }
|
@@ -1,75 +1,94 @@
|
|
1
1
|
module Spree
|
2
|
-
class Promotion <
|
2
|
+
class Promotion < ActiveRecord::Base
|
3
3
|
MATCH_POLICIES = %w(all any)
|
4
4
|
UNACTIVATABLE_ORDER_STATES = ["complete", "awaiting_return", "returned"]
|
5
5
|
|
6
|
-
|
7
|
-
Activator.event_names << 'spree.content.visited'
|
8
|
-
|
9
|
-
has_many :promotion_rules, foreign_key: :activator_id, autosave: true, dependent: :destroy
|
6
|
+
has_many :promotion_rules, autosave: true, dependent: :destroy
|
10
7
|
alias_method :rules, :promotion_rules
|
11
8
|
|
12
|
-
has_many :promotion_actions,
|
9
|
+
has_many :promotion_actions, autosave: true, dependent: :destroy
|
13
10
|
alias_method :actions, :promotion_actions
|
14
11
|
|
12
|
+
has_and_belongs_to_many :orders, join_table: 'spree_orders_promotions'
|
13
|
+
|
15
14
|
accepts_nested_attributes_for :promotion_actions, :promotion_rules
|
16
15
|
|
17
16
|
validates_associated :rules
|
18
17
|
|
19
18
|
validates :name, presence: true
|
20
|
-
validates :
|
21
|
-
validates :path, presence: true, if: lambda{|r| r.event_name == 'spree.content.visited' }
|
19
|
+
validates :path, uniqueness: true, allow_blank: true
|
22
20
|
validates :usage_limit, numericality: { greater_than: 0, allow_nil: true }
|
21
|
+
validates :description, length: { maximum: 255 }
|
22
|
+
|
23
|
+
before_save :normalize_blank_values
|
23
24
|
|
24
25
|
def self.advertised
|
25
26
|
where(advertise: true)
|
26
27
|
end
|
27
28
|
|
28
|
-
def self.
|
29
|
-
where(
|
29
|
+
def self.with_coupon_code(coupon_code)
|
30
|
+
where("lower(code) = ?", coupon_code.strip.downcase).first
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.active
|
34
|
+
where('starts_at IS NULL OR starts_at < ?', Time.now).
|
35
|
+
where('expires_at IS NULL OR expires_at > ?', Time.now)
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.order_activatable?(order)
|
39
|
+
order && !UNACTIVATABLE_ORDER_STATES.include?(order.state)
|
40
|
+
end
|
41
|
+
|
42
|
+
def expired?
|
43
|
+
starts_at && Time.now < starts_at || expires_at && Time.now > expires_at
|
30
44
|
end
|
31
45
|
|
32
46
|
def activate(payload)
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
return unless path == payload[:path]
|
42
|
-
end
|
43
|
-
|
44
|
-
actions.each do |action|
|
45
|
-
action.perform(payload)
|
46
|
-
end
|
47
|
-
|
48
|
-
return true
|
47
|
+
order = payload[:order]
|
48
|
+
return unless self.class.order_activatable?(order)
|
49
|
+
|
50
|
+
# Track results from actions to see if any action has been taken.
|
51
|
+
# Actions should return nil/false if no action has been taken.
|
52
|
+
# If an action returns true, then an action has been taken.
|
53
|
+
results = actions.map do |action|
|
54
|
+
action.perform(payload)
|
49
55
|
end
|
50
|
-
|
56
|
+
# If an action has been taken, report back to whatever activated this promotion.
|
57
|
+
action_taken = results.include?(true)
|
58
|
+
|
59
|
+
if action_taken
|
60
|
+
# connect to the order
|
61
|
+
# create the join_table entry.
|
62
|
+
self.orders << order
|
63
|
+
self.save
|
64
|
+
end
|
65
|
+
|
66
|
+
return action_taken
|
51
67
|
end
|
52
68
|
|
53
69
|
# called anytime order.update! happens
|
54
|
-
def eligible?(
|
55
|
-
return false if expired? || usage_limit_exceeded?(
|
56
|
-
rules_are_eligible?(
|
70
|
+
def eligible?(promotable)
|
71
|
+
return false if expired? || usage_limit_exceeded?(promotable)
|
72
|
+
rules_are_eligible?(promotable, {})
|
57
73
|
end
|
58
74
|
|
59
|
-
def rules_are_eligible?(
|
75
|
+
def rules_are_eligible?(promotable, options = {})
|
76
|
+
# Promotions without rules are eligible by default.
|
60
77
|
return true if rules.none?
|
61
|
-
eligible = lambda { |r| r.eligible?(
|
78
|
+
eligible = lambda { |r| r.eligible?(promotable, options) }
|
79
|
+
specific_rules = rules.for(promotable)
|
80
|
+
return true if specific_rules.none?
|
62
81
|
if match_policy == 'all'
|
63
|
-
rules
|
82
|
+
# If there are rules for this promotion, but no rules for this
|
83
|
+
# particular promotable, then the promotion is ineligible by default.
|
84
|
+
specific_rules.any? && specific_rules.all?(&eligible)
|
64
85
|
else
|
65
|
-
rules.
|
86
|
+
# If there are no rules for this promotable, then this will return false.
|
87
|
+
# If there are rules for this promotable, but they are ineligible, this will return false.
|
88
|
+
specific_rules.any?(&eligible)
|
66
89
|
end
|
67
90
|
end
|
68
91
|
|
69
|
-
def order_activatable?(order)
|
70
|
-
order && !UNACTIVATABLE_ORDER_STATES.include?(order.state)
|
71
|
-
end
|
72
|
-
|
73
92
|
# Products assigned to all product rules
|
74
93
|
def products
|
75
94
|
@products ||= self.rules.to_a.inject([]) do |products, rule|
|
@@ -77,29 +96,31 @@ module Spree
|
|
77
96
|
end.flatten.uniq
|
78
97
|
end
|
79
98
|
|
80
|
-
def
|
81
|
-
|
99
|
+
def product_ids
|
100
|
+
products.map(&:id)
|
82
101
|
end
|
83
102
|
|
84
|
-
def
|
85
|
-
|
86
|
-
|
103
|
+
def usage_limit_exceeded?(promotable)
|
104
|
+
usage_limit.present? && usage_limit > 0 && adjusted_credits_count(promotable) >= usage_limit
|
105
|
+
end
|
106
|
+
|
107
|
+
def adjusted_credits_count(promotable)
|
108
|
+
credits_count - promotable.adjustments.promotion.where(:source_id => actions.pluck(:id)).count
|
87
109
|
end
|
88
110
|
|
89
111
|
def credits
|
90
|
-
Adjustment.eligible.promotion.where(
|
112
|
+
Adjustment.eligible.promotion.where(source_id: actions.map(&:id))
|
91
113
|
end
|
92
114
|
|
93
115
|
def credits_count
|
94
116
|
credits.count
|
95
117
|
end
|
96
118
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
actions.any? { |a| a.credit_exists_on_order?(order) }
|
119
|
+
private
|
120
|
+
def normalize_blank_values
|
121
|
+
[:code, :path].each do |column|
|
122
|
+
self[column] = nil if self[column].blank?
|
123
|
+
end
|
103
124
|
end
|
104
125
|
end
|
105
126
|
end
|