spree_core 2.2.0 → 2.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE +26 -0
- data/app/helpers/spree/taxons_helper.rb +2 -2
- data/app/models/concerns/spree/user_api_authentication.rb +13 -0
- data/app/models/concerns/spree/user_reporting.rb +27 -0
- data/app/models/spree/adjustment.rb +3 -2
- data/app/models/spree/app_configuration.rb +1 -0
- data/app/models/spree/calculator/default_tax.rb +5 -1
- data/app/models/spree/calculator.rb +2 -2
- data/app/models/spree/credit_card.rb +11 -5
- data/app/models/spree/gateway.rb +25 -0
- data/app/models/spree/item_adjustments.rb +2 -0
- data/app/models/spree/legacy_user.rb +2 -3
- data/app/models/spree/line_item.rb +7 -0
- data/app/models/spree/order/checkout.rb +19 -7
- data/app/models/spree/order/currency_updater.rb +40 -0
- data/app/models/spree/order.rb +19 -3
- data/app/models/spree/order_updater.rb +1 -1
- data/app/models/spree/payment.rb +5 -2
- data/app/models/spree/payment_method.rb +1 -0
- data/app/models/spree/product/scopes.rb +13 -17
- data/app/models/spree/product.rb +10 -2
- data/app/models/spree/promotion/actions/free_shipping.rb +4 -2
- data/app/models/spree/promotion/rules/product.rb +0 -9
- data/app/models/spree/promotion.rb +1 -1
- data/app/models/spree/shipment.rb +19 -14
- data/app/models/spree/shipping_method.rb +2 -2
- data/app/models/spree/shipping_rate.rb +15 -9
- data/app/models/spree/stock/estimator.rb +4 -1
- data/app/models/spree/stock_location.rb +1 -0
- data/app/models/spree/tax_rate.rb +81 -7
- data/config/initializers/user_class_extensions.rb +3 -0
- data/config/locales/en.yml +26 -0
- data/db/default/spree/countries.rb +2 -1
- data/db/migrate/20130213191427_create_default_stock.rb +3 -3
- 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/20130802022321_migrate_tax_categories_to_line_items.rb +5 -7
- data/db/migrate/20140307235515_add_user_id_to_spree_credit_cards.rb +13 -0
- data/lib/generators/spree/dummy/templates/rails/database.yml +6 -6
- data/lib/spree/core/controller_helpers/auth.rb +10 -1
- data/lib/spree/core/controller_helpers/order.rb +1 -1
- data/lib/spree/core/controller_helpers/respond_with.rb +6 -1
- data/lib/spree/core/importer/order.rb +168 -0
- data/lib/spree/core/importer.rb +8 -0
- data/lib/spree/core/product_duplicator.rb +1 -1
- data/lib/spree/core/user_address.rb +2 -0
- data/lib/spree/core/user_payment_source.rb +20 -0
- data/lib/spree/core/version.rb +1 -1
- data/lib/spree/core.rb +4 -0
- data/lib/spree/migrations.rb +1 -1
- data/lib/spree/permitted_attributes.rb +1 -1
- data/lib/spree/testing_support/capybara_ext.rb +23 -1
- data/lib/spree/testing_support/factories/credit_card_factory.rb +1 -6
- data/lib/spree/testing_support/factories/order_factory.rb +8 -8
- data/lib/spree/testing_support/factories/shipping_method_factory.rb +3 -1
- data/lib/spree/testing_support/factories/user_factory.rb +5 -0
- metadata +11 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d6877ed94eb40aa392d3e75311ae1c46f07d16c4
|
4
|
+
data.tar.gz: 86f962251bf0d966fce2dc5562a4e537b05db77c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b9f99321e59660959d319148d1c7deed17c10caedb97e6d29d95dd15c195b2dac43bcfb35d5117c915bc456a1d8c0fb8f2d319971a7bd681f842f77a91ba25b6
|
7
|
+
data.tar.gz: 154ee0e0f7d1bd5fa2ce9bce96008951200122a3c687dfe88b6e75284987bef2c12d464c6bba83a2a97747ccd2e9dae7537b2fddc3a4fb9e9cc924c5c11c8491
|
data/LICENSE
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
Copyright (c) 2007-2014, Spree Commerce, Inc. 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.
|
@@ -4,12 +4,12 @@ module Spree
|
|
4
4
|
# that we can use configurations as well as make it easier for end users to override this determination. One idea is
|
5
5
|
# to show the most popular products for a particular taxon (that is an exercise left to the developer.)
|
6
6
|
def taxon_preview(taxon, max=4)
|
7
|
-
products = taxon.active_products.limit(max)
|
7
|
+
products = taxon.active_products.select("DISTINCT (spree_products.id), spree_products.*, spree_products_taxons.position").limit(max)
|
8
8
|
if (products.size < max)
|
9
9
|
products_arel = Spree::Product.arel_table
|
10
10
|
taxon.descendants.each do |taxon|
|
11
11
|
to_get = max - products.length
|
12
|
-
products += taxon.active_products.where(products_arel[:id].not_in(products.map(&:id))).limit(to_get)
|
12
|
+
products += taxon.active_products.select("DISTINCT (spree_products.id), spree_products.*, spree_products_taxons.position").where(products_arel[:id].not_in(products.map(&:id))).limit(to_get)
|
13
13
|
break if products.size >= max
|
14
14
|
end
|
15
15
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Spree
|
2
|
+
module UserReporting
|
3
|
+
def lifetime_value
|
4
|
+
orders.complete.pluck(:total).sum
|
5
|
+
end
|
6
|
+
|
7
|
+
def display_lifetime_value
|
8
|
+
Spree::Money.new(lifetime_value)
|
9
|
+
end
|
10
|
+
|
11
|
+
def order_count
|
12
|
+
BigDecimal(orders.complete.count)
|
13
|
+
end
|
14
|
+
|
15
|
+
def average_order_value
|
16
|
+
if order_count.to_i > 0
|
17
|
+
lifetime_value / order_count
|
18
|
+
else
|
19
|
+
BigDecimal("0.00")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def display_average_order_value
|
24
|
+
Spree::Money.new(average_order_value)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -34,14 +34,15 @@ module Spree
|
|
34
34
|
transition from: :open, to: :closed
|
35
35
|
end
|
36
36
|
|
37
|
-
event :
|
38
|
-
transition from: :
|
37
|
+
event :open do
|
38
|
+
transition from: :closed, to: :open
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
42
|
after_create :update_adjustable_adjustment_total
|
43
43
|
|
44
44
|
scope :open, -> { where(state: 'open') }
|
45
|
+
scope :closed, -> { where(state: 'closed') }
|
45
46
|
scope :tax, -> { where(source_type: 'Spree::TaxRate') }
|
46
47
|
scope :price, -> { where(adjustable_type: 'Spree::LineItem') }
|
47
48
|
scope :shipping, -> { where(adjustable_type: 'Spree::Shipment') }
|
@@ -58,6 +58,7 @@ module Spree
|
|
58
58
|
preference :orders_per_page, :integer, default: 15
|
59
59
|
preference :prices_inc_tax, :boolean, default: false
|
60
60
|
preference :products_per_page, :integer, default: 12
|
61
|
+
preference :promotions_per_page, :integer, default: 15
|
61
62
|
preference :redirect_https_to_http, :boolean, :default => false
|
62
63
|
preference :require_master_price, :boolean, default: true
|
63
64
|
preference :shipment_inc_vat, :boolean, default: false
|
@@ -38,7 +38,11 @@ module Spree
|
|
38
38
|
def compute_shipping_rate(shipping_rate)
|
39
39
|
if rate.included_in_price
|
40
40
|
pre_tax_amount = shipping_rate.cost / (1 + rate.amount)
|
41
|
-
|
41
|
+
if rate.zone == shipping_rate.shipment.order.tax_zone
|
42
|
+
deduced_total_by_rate(pre_tax_amount, rate)
|
43
|
+
else
|
44
|
+
deduced_total_by_rate(pre_tax_amount, rate) * - 1
|
45
|
+
end
|
42
46
|
else
|
43
47
|
with_tax_amount = shipping_rate.cost * rate.amount
|
44
48
|
round_to_two_places(with_tax_amount)
|
@@ -10,9 +10,9 @@ module Spree
|
|
10
10
|
computable_name = computable.class.name.demodulize.underscore
|
11
11
|
method = "compute_#{computable_name}".to_sym
|
12
12
|
calculator_class = self.class
|
13
|
-
|
13
|
+
if respond_to?(method)
|
14
14
|
self.send(method, computable)
|
15
|
-
|
15
|
+
else
|
16
16
|
raise NotImplementedError, "Please implement '#{method}(#{computable_name})' in your calculator: #{calculator_class.name}"
|
17
17
|
end
|
18
18
|
end
|
@@ -1,15 +1,17 @@
|
|
1
1
|
module Spree
|
2
2
|
class CreditCard < ActiveRecord::Base
|
3
|
+
belongs_to :payment_method
|
3
4
|
has_many :payments, as: :source
|
4
5
|
|
5
6
|
before_save :set_last_digits
|
6
7
|
|
7
|
-
attr_accessor :number, :verification_value
|
8
|
+
attr_accessor :number, :verification_value, :encrypted_data
|
9
|
+
|
10
|
+
validates :month, :year, numericality: { only_integer: true }, if: :require_card_numbers?, on: :create
|
11
|
+
validates :number, presence: true, if: :require_card_numbers?, on: :create
|
12
|
+
validates :name, presence: true, if: :require_card_numbers?, on: :create
|
13
|
+
validates :verification_value, presence: true, if: :require_card_numbers?, on: :create
|
8
14
|
|
9
|
-
validates :month, :year, numericality: { only_integer: true }, unless: :has_payment_profile?
|
10
|
-
validates :number, presence: true, unless: :has_payment_profile?, on: :create
|
11
|
-
validates :name, presence: true
|
12
|
-
validates :verification_value, presence: true, unless: :has_payment_profile?, on: :create
|
13
15
|
validate :expiry_not_in_the_past
|
14
16
|
|
15
17
|
scope :with_payment_profile, -> { where('gateway_customer_profile_id IS NOT NULL') }
|
@@ -118,5 +120,9 @@ module Spree
|
|
118
120
|
end
|
119
121
|
end
|
120
122
|
end
|
123
|
+
|
124
|
+
def require_card_numbers?
|
125
|
+
!self.encrypted_data.present? && !self.has_payment_profile?
|
126
|
+
end
|
121
127
|
end
|
122
128
|
end
|
data/app/models/spree/gateway.rb
CHANGED
@@ -50,5 +50,30 @@ module Spree
|
|
50
50
|
return false unless source.brand
|
51
51
|
provider_class.supports?(source.brand)
|
52
52
|
end
|
53
|
+
|
54
|
+
def disable_customer_profile(source)
|
55
|
+
if source.is_a? CreditCard
|
56
|
+
source.update_column :gateway_customer_profile_id, nil
|
57
|
+
else
|
58
|
+
raise 'You must implement disable_customer_profile method for this gateway.'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def sources_by_order(order)
|
63
|
+
source_ids = order.payments.where(source_type: payment_source_class.to_s, payment_method_id: self.id).pluck(:source_id).uniq
|
64
|
+
payment_source_class.where(id: source_ids).with_payment_profile
|
65
|
+
end
|
66
|
+
|
67
|
+
def sources_with_profile(order)
|
68
|
+
if order.completed?
|
69
|
+
sources_by_order order
|
70
|
+
else
|
71
|
+
if order.user_id
|
72
|
+
self.credit_cards.where(user_id: order.user_id).with_payment_profile
|
73
|
+
else
|
74
|
+
[]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
53
78
|
end
|
54
79
|
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
module Spree
|
3
3
|
class LegacyUser < ActiveRecord::Base
|
4
4
|
include Core::UserAddress
|
5
|
+
include Core::UserPaymentSource
|
5
6
|
|
6
7
|
self.table_name = 'spree_users'
|
7
8
|
|
@@ -9,8 +10,6 @@ module Spree
|
|
9
10
|
|
10
11
|
before_destroy :check_completed_orders
|
11
12
|
|
12
|
-
class DestroyWithOrdersError < StandardError; end
|
13
|
-
|
14
13
|
def has_spree_role?(role)
|
15
14
|
true
|
16
15
|
end
|
@@ -21,7 +20,7 @@ module Spree
|
|
21
20
|
private
|
22
21
|
|
23
22
|
def check_completed_orders
|
24
|
-
raise DestroyWithOrdersError if orders.complete.present?
|
23
|
+
raise Spree::Core::DestroyWithOrdersError if orders.complete.present?
|
25
24
|
end
|
26
25
|
end
|
27
26
|
end
|
@@ -22,6 +22,7 @@ module Spree
|
|
22
22
|
validates :price, numericality: true
|
23
23
|
validates_with Stock::AvailabilityValidator
|
24
24
|
|
25
|
+
validate :ensure_proper_currency
|
25
26
|
before_destroy :update_inventory
|
26
27
|
|
27
28
|
after_save :update_inventory
|
@@ -118,6 +119,12 @@ module Spree
|
|
118
119
|
def create_tax_charge
|
119
120
|
Spree::TaxRate.adjust(order, [self])
|
120
121
|
end
|
122
|
+
|
123
|
+
def ensure_proper_currency
|
124
|
+
unless currency == order.currency
|
125
|
+
errors.add(:currency, t(:must_match_order_currency))
|
126
|
+
end
|
127
|
+
end
|
121
128
|
end
|
122
129
|
end
|
123
130
|
|
@@ -81,17 +81,17 @@ module Spree
|
|
81
81
|
before_transition :from => :address, :do => :create_tax_charge!
|
82
82
|
end
|
83
83
|
|
84
|
+
if states[:payment]
|
85
|
+
before_transition :to => :payment, :do => :set_shipments_cost
|
86
|
+
before_transition :to => :payment, :do => :create_tax_charge!
|
87
|
+
end
|
88
|
+
|
84
89
|
if states[:delivery]
|
85
90
|
before_transition :to => :delivery, :do => :create_proposed_shipments
|
86
91
|
before_transition :to => :delivery, :do => :ensure_available_shipping_rates
|
87
92
|
before_transition :from => :delivery, :do => :apply_free_shipping_promotions
|
88
93
|
end
|
89
94
|
|
90
|
-
if states[:payment]
|
91
|
-
before_transition :to => :payment, :do => :set_shipments_cost
|
92
|
-
before_transition :to => :payment, :do => :create_tax_charge!
|
93
|
-
end
|
94
|
-
|
95
95
|
after_transition :to => :complete, :do => :finalize!
|
96
96
|
after_transition :to => :resumed, :do => :after_resume
|
97
97
|
after_transition :to => :canceled, :do => :after_cancel
|
@@ -211,6 +211,20 @@ module Spree
|
|
211
211
|
@updating_params = params
|
212
212
|
run_callbacks :updating_from_params do
|
213
213
|
attributes = @updating_params[:order] ? @updating_params[:order].permit(permitted_params) : {}
|
214
|
+
|
215
|
+
# Set existing card after setting permitted parameters because
|
216
|
+
# rails would slice parameters containg ruby objects, apparently
|
217
|
+
if @updating_params[:existing_card].present?
|
218
|
+
credit_card = CreditCard.find(@updating_params[:existing_card])
|
219
|
+
if credit_card.user_id != self.user_id || credit_card.user_id.blank?
|
220
|
+
raise Core::GatewayError.new Spree.t(:invalid_credit_card)
|
221
|
+
end
|
222
|
+
|
223
|
+
attributes[:payments_attributes].first[:source] = credit_card
|
224
|
+
attributes[:payments_attributes].first[:payment_method_id] = credit_card.payment_method_id
|
225
|
+
attributes[:payments_attributes].first.delete :source_attributes
|
226
|
+
end
|
227
|
+
|
214
228
|
success = self.update_attributes(attributes)
|
215
229
|
end
|
216
230
|
@updating_params = nil
|
@@ -222,7 +236,6 @@ module Spree
|
|
222
236
|
# attributes for a single payment and its source, discarding attributes
|
223
237
|
# for payment methods other than the one selected
|
224
238
|
def update_params_payment_source
|
225
|
-
# respond_to check is necessary due to issue described in #2910
|
226
239
|
if has_checkout_step?("payment") && self.payment?
|
227
240
|
if @updating_params[:payment_source].present?
|
228
241
|
source_params = @updating_params.delete(:payment_source)[@updating_params[:order][:payments_attributes].first[:payment_method_id].underscore]
|
@@ -237,7 +250,6 @@ module Spree
|
|
237
250
|
end
|
238
251
|
end
|
239
252
|
end
|
240
|
-
|
241
253
|
end
|
242
254
|
end
|
243
255
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Spree
|
2
|
+
class Order < ActiveRecord::Base
|
3
|
+
module CurrencyUpdater
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
|
8
|
+
def homogenize_line_item_currencies
|
9
|
+
update_line_item_currencies!
|
10
|
+
update!
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
# Updates prices of order's line items
|
16
|
+
def update_line_item_currencies!
|
17
|
+
line_items.where('currency != ?', currency).each do |line_item|
|
18
|
+
update_line_item_price!(line_item)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns the price object from given item
|
23
|
+
def price_from_line_item(line_item)
|
24
|
+
line_item.variant.prices.where(currency: currency).first
|
25
|
+
end
|
26
|
+
|
27
|
+
# Updates price from given line item
|
28
|
+
def update_line_item_price!(line_item)
|
29
|
+
price = price_from_line_item(line_item)
|
30
|
+
|
31
|
+
if price
|
32
|
+
line_item.update_attributes!(currency: price.currency, price: price.amount)
|
33
|
+
else
|
34
|
+
raise RuntimeError, "no #{currency} price found for #{line_item.product.name} (#{line_item.variant.sku})"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/app/models/spree/order.rb
CHANGED
@@ -4,13 +4,15 @@ require 'spree/order/checkout'
|
|
4
4
|
module Spree
|
5
5
|
class Order < ActiveRecord::Base
|
6
6
|
include Checkout
|
7
|
+
include CurrencyUpdater
|
7
8
|
|
8
9
|
checkout_flow do
|
9
10
|
go_to_state :address
|
10
11
|
go_to_state :delivery
|
11
|
-
go_to_state :payment, if: ->(order)
|
12
|
+
go_to_state :payment, if: ->(order) do
|
13
|
+
order.set_shipments_cost if order.shipments.any?
|
12
14
|
order.payment_required?
|
13
|
-
|
15
|
+
end
|
14
16
|
go_to_state :confirm, if: ->(order) { order.confirmation_required? }
|
15
17
|
go_to_state :complete
|
16
18
|
remove_transition from: :delivery, to: :confirm
|
@@ -68,6 +70,7 @@ module Spree
|
|
68
70
|
attr_accessor :use_billing
|
69
71
|
|
70
72
|
before_create :link_by_email
|
73
|
+
before_update :homogenize_line_item_currencies, if: :currency_changed?
|
71
74
|
|
72
75
|
validates :email, presence: true, if: :require_email
|
73
76
|
validates :email, email: true, if: :require_email, allow_blank: true
|
@@ -489,7 +492,7 @@ module Spree
|
|
489
492
|
# to delivery again so that proper updated shipments are created.
|
490
493
|
# e.g. customer goes back from payment step and changes order items
|
491
494
|
def ensure_updated_shipments
|
492
|
-
if shipments.any?
|
495
|
+
if shipments.any? && !self.completed?
|
493
496
|
self.shipments.destroy_all
|
494
497
|
self.update_column(:shipment_total, 0)
|
495
498
|
restart_checkout_flow
|
@@ -559,6 +562,19 @@ module Spree
|
|
559
562
|
update_column(:considered_risky, false)
|
560
563
|
end
|
561
564
|
|
565
|
+
# moved from api order_decorator. This is a better place for it.
|
566
|
+
def update_line_items(line_item_params)
|
567
|
+
return if line_item_params.blank?
|
568
|
+
line_item_params.each_value do |attributes|
|
569
|
+
if attributes[:id].present?
|
570
|
+
self.line_items.find(attributes[:id]).update_attributes!(attributes)
|
571
|
+
else
|
572
|
+
self.line_items.create!(attributes)
|
573
|
+
end
|
574
|
+
end
|
575
|
+
self.ensure_updated_shipments
|
576
|
+
end
|
577
|
+
|
562
578
|
private
|
563
579
|
|
564
580
|
def link_by_email
|
data/app/models/spree/payment.rb
CHANGED
@@ -104,7 +104,7 @@ module Spree
|
|
104
104
|
end
|
105
105
|
|
106
106
|
def credit_allowed
|
107
|
-
amount - offsets_total
|
107
|
+
amount - offsets_total.abs
|
108
108
|
end
|
109
109
|
|
110
110
|
def can_credit?
|
@@ -116,6 +116,8 @@ module Spree
|
|
116
116
|
return if source_attributes.nil?
|
117
117
|
if payment_method and payment_method.payment_source_class
|
118
118
|
self.source = payment_method.payment_source_class.new(source_attributes)
|
119
|
+
self.source.payment_method_id = payment_method.id
|
120
|
+
self.source.user_id = self.order.user_id if self.order
|
119
121
|
end
|
120
122
|
end
|
121
123
|
|
@@ -159,7 +161,8 @@ module Spree
|
|
159
161
|
end
|
160
162
|
|
161
163
|
def create_payment_profile
|
162
|
-
return unless source.
|
164
|
+
return unless source.respond_to?(:has_payment_profile?) && !source.has_payment_profile?
|
165
|
+
|
163
166
|
payment_method.create_profile(self)
|
164
167
|
rescue ActiveMerchant::ConnectionError => e
|
165
168
|
gateway_error e
|
@@ -27,6 +27,15 @@ module Spree
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
+
def self.property_conditions(property)
|
31
|
+
properties = Property.table_name
|
32
|
+
conditions = case property
|
33
|
+
when String then { "#{properties}.name" => property }
|
34
|
+
when Property then { "#{properties}.id" => property.id }
|
35
|
+
else { "#{properties}.id" => property.to_i }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
30
39
|
add_simple_scopes simple_scopes
|
31
40
|
|
32
41
|
add_search_scope :ascend_by_master_price do
|
@@ -83,28 +92,15 @@ module Spree
|
|
83
92
|
|
84
93
|
# a scope that finds all products having property specified by name, object or id
|
85
94
|
add_search_scope :with_property do |property|
|
86
|
-
properties
|
87
|
-
conditions = case property
|
88
|
-
when String then { "#{properties}.name" => property }
|
89
|
-
when Property then { "#{properties}.id" => property.id }
|
90
|
-
else { "#{properties}.id" => property.to_i }
|
91
|
-
end
|
92
|
-
|
93
|
-
joins(:properties).where(conditions)
|
95
|
+
joins(:properties).where(property_conditions(property))
|
94
96
|
end
|
95
97
|
|
96
98
|
# a simple test for product with a certain property-value pairing
|
97
99
|
# note that it can test for properties with NULL values, but not for absent values
|
98
100
|
add_search_scope :with_property_value do |property, value|
|
99
|
-
properties
|
100
|
-
|
101
|
-
|
102
|
-
when Property then ["#{properties}.id = ?", property.id]
|
103
|
-
else ["#{properties}.id = ?", property.to_i]
|
104
|
-
end
|
105
|
-
conditions = ["#{ProductProperty.table_name}.value = ? AND #{conditions[0]}", value, conditions[1]]
|
106
|
-
|
107
|
-
joins(:properties).where(conditions)
|
101
|
+
joins(:properties)
|
102
|
+
.where("#{ProductProperty.table_name}.value = ?", value)
|
103
|
+
.where(property_conditions(property))
|
108
104
|
end
|
109
105
|
|
110
106
|
add_search_scope :with_option do |option|
|
data/app/models/spree/product.rb
CHANGED
@@ -78,6 +78,8 @@ module Spree
|
|
78
78
|
validates :shipping_category_id, presence: true
|
79
79
|
validates :slug, length: { minimum: 3 }
|
80
80
|
|
81
|
+
before_validation :normalize_slug, on: :update
|
82
|
+
|
81
83
|
attr_accessor :option_values_hash
|
82
84
|
|
83
85
|
accepts_nested_attributes_for :product_properties, allow_destroy: true, reject_if: lambda { |pp| pp[:property_name].blank? }
|
@@ -132,8 +134,11 @@ module Spree
|
|
132
134
|
!!deleted_at
|
133
135
|
end
|
134
136
|
|
137
|
+
# determine if product is available.
|
138
|
+
# deleted products and products with nil or future available_on date
|
139
|
+
# are not available
|
135
140
|
def available?
|
136
|
-
!(available_on.nil? || available_on.future?)
|
141
|
+
!(available_on.nil? || available_on.future?) && !deleted?
|
137
142
|
end
|
138
143
|
|
139
144
|
# split variants list into hash which shows mapping of opt value onto matching variants
|
@@ -188,7 +193,7 @@ module Spree
|
|
188
193
|
end
|
189
194
|
|
190
195
|
def possible_promotions
|
191
|
-
promotion_ids = promotion_rules.map(&:
|
196
|
+
promotion_ids = promotion_rules.map(&:promotion_id).uniq
|
192
197
|
Spree::Promotion.advertised.where(id: promotion_ids).reject(&:expired?)
|
193
198
|
end
|
194
199
|
|
@@ -208,6 +213,9 @@ module Spree
|
|
208
213
|
end
|
209
214
|
|
210
215
|
private
|
216
|
+
def normalize_slug
|
217
|
+
self.slug = normalize_friendly_id(slug)
|
218
|
+
end
|
211
219
|
|
212
220
|
# Builds variants from a hash of option types & values
|
213
221
|
def build_variants_from_option_values_hash
|
@@ -4,8 +4,6 @@ module Spree
|
|
4
4
|
class FreeShipping < Spree::PromotionAction
|
5
5
|
def perform(payload={})
|
6
6
|
order = payload[:order]
|
7
|
-
|
8
|
-
label = "#{Spree.t(:promotion)} (#{promotion.name})"
|
9
7
|
results = order.shipments.map do |shipment|
|
10
8
|
return false if promotion_credit_exists?(shipment)
|
11
9
|
shipment.adjustments.create!(
|
@@ -21,6 +19,10 @@ module Spree
|
|
21
19
|
results.any? { |r| r == true }
|
22
20
|
end
|
23
21
|
|
22
|
+
def label
|
23
|
+
"#{Spree.t(:promotion)} (#{promotion.name})"
|
24
|
+
end
|
25
|
+
|
24
26
|
def compute_amount(shipment)
|
25
27
|
shipment.cost * -1
|
26
28
|
end
|
@@ -6,7 +6,6 @@ module Spree
|
|
6
6
|
module Rules
|
7
7
|
class Product < PromotionRule
|
8
8
|
has_and_belongs_to_many :products, class_name: '::Spree::Product', join_table: 'spree_products_promotion_rules', foreign_key: 'promotion_rule_id'
|
9
|
-
validate :only_one_promotion_per_product
|
10
9
|
|
11
10
|
MATCH_POLICIES = %w(any all)
|
12
11
|
preference :match_policy, :string, default: MATCH_POLICIES.first
|
@@ -36,14 +35,6 @@ module Spree
|
|
36
35
|
def product_ids_string=(s)
|
37
36
|
self.product_ids = s.to_s.split(',').map(&:strip)
|
38
37
|
end
|
39
|
-
|
40
|
-
private
|
41
|
-
|
42
|
-
def only_one_promotion_per_product
|
43
|
-
if Spree::Promotion::Rules::Product.all.map(&:products).flatten.uniq!
|
44
|
-
errors[:base] << "You can't create two promotions for the same product"
|
45
|
-
end
|
46
|
-
end
|
47
38
|
end
|
48
39
|
end
|
49
40
|
end
|