solidus_core 2.1.1 → 2.2.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of solidus_core might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Rakefile +0 -1
- data/app/assets/config/solidus_core_manifest.js +1 -0
- data/app/assets/javascripts/spree.js.erb +72 -0
- data/app/helpers/spree/store_helper.rb +5 -0
- data/app/jobs/spree/promotion_code_batch_job.rb +24 -0
- data/app/mailers/spree/promotion_code_batch_mailer.rb +13 -0
- data/app/models/concerns/spree/calculated_adjustments.rb +1 -1
- data/app/models/concerns/spree/ordered_property_value_list.rb +2 -2
- data/app/models/concerns/spree/user_address_book.rb +4 -4
- data/app/models/concerns/spree/user_methods.rb +7 -0
- data/app/models/concerns/spree/user_payment_source.rb +12 -5
- data/app/models/spree/address.rb +14 -3
- data/app/models/spree/adjustment.rb +13 -1
- data/app/models/spree/app_configuration.rb +0 -19
- data/app/models/spree/base.rb +2 -0
- data/app/models/spree/credit_card.rb +34 -43
- data/app/models/spree/gateway/bogus.rb +1 -1
- data/app/models/spree/gateway.rb +6 -4
- data/app/models/spree/inventory_unit.rb +3 -2
- data/app/models/spree/order/checkout.rb +187 -273
- data/app/models/spree/order.rb +137 -71
- data/app/models/spree/order_contents.rb +1 -1
- data/app/models/spree/order_inventory.rb +11 -11
- data/app/models/spree/order_promotion.rb +2 -0
- data/app/models/spree/order_update_attributes.rb +1 -8
- data/app/models/spree/order_updater.rb +67 -63
- data/app/models/spree/payment.rb +0 -1
- data/app/models/spree/payment_create.rb +27 -7
- data/app/models/spree/payment_method/store_credit.rb +3 -3
- data/app/models/spree/payment_method.rb +4 -1
- data/app/models/spree/payment_source.rb +45 -0
- data/app/models/spree/product/scopes.rb +24 -24
- data/app/models/spree/product.rb +4 -4
- data/app/models/spree/promotion.rb +2 -0
- data/app/models/spree/promotion_code/batch_builder.rb +63 -0
- data/app/models/spree/promotion_code.rb +1 -0
- data/app/models/spree/promotion_code_batch.rb +25 -0
- data/app/models/spree/promotion_handler/cart.rb +2 -2
- data/app/models/spree/promotion_handler/coupon.rb +1 -2
- data/app/models/spree/promotion_handler/free_shipping.rb +32 -21
- data/app/models/spree/promotion_handler/page.rb +1 -1
- data/app/models/spree/reimbursement.rb +1 -1
- data/app/models/spree/return_authorization.rb +0 -28
- data/app/models/spree/return_item.rb +1 -1
- data/app/models/spree/shipment.rb +4 -4
- data/app/models/spree/shipping_method.rb +2 -2
- data/app/models/spree/shipping_rate.rb +1 -1
- data/app/models/spree/stock/availability_validator.rb +16 -17
- data/app/models/spree/stock/coordinator.rb +3 -3
- data/app/models/spree/stock/package.rb +1 -1
- data/app/models/spree/stock/quantifier.rb +5 -4
- data/app/models/spree/stock_location.rb +2 -2
- data/app/models/spree/store.rb +2 -2
- data/app/models/spree/store_credit.rb +1 -1
- data/app/models/spree/tax/tax_helpers.rb +3 -3
- data/app/models/spree/tax_rate.rb +7 -1
- data/app/models/spree/taxonomy.rb +1 -1
- data/app/models/spree/variant/scopes.rb +5 -5
- data/app/models/spree/variant/vat_price_generator.rb +8 -5
- data/app/models/spree/variant.rb +1 -0
- data/app/models/spree/wallet/add_payment_sources_to_wallet.rb +19 -10
- data/app/models/spree/wallet/default_payment_builder.rb +6 -6
- data/app/models/spree/wallet.rb +71 -0
- data/app/models/spree/wallet_payment_source.rb +17 -0
- data/app/models/spree/zone.rb +1 -1
- data/app/views/spree/carton_mailer/shipped_email.text.erb +1 -1
- data/app/views/spree/promotion_code_batch_mailer/promotion_code_batch_errored.text.erb +2 -0
- data/app/views/spree/promotion_code_batch_mailer/promotion_code_batch_finished.text.erb +2 -0
- data/app/views/spree/reimbursement_mailer/reimbursement_email.html.erb +0 -7
- data/app/views/spree/reimbursement_mailer/reimbursement_email.text.erb +0 -5
- data/app/views/spree/shared/_error_messages.html.erb +1 -1
- data/app/views/spree/shipment_mailer/shipped_email.html.erb +1 -1
- data/config/initializers/assets.rb +1 -1
- data/config/initializers/friendly_id.rb +1 -1
- data/config/locales/en.yml +50 -12
- data/db/default/spree/store_credit.rb +2 -1
- data/db/migrate/20130826062534_add_depth_to_spree_taxons.rb +4 -6
- data/db/migrate/20160420044191_create_spree_wallet_payment_sources.rb +23 -0
- data/db/migrate/20160420181916_migrate_credit_cards_to_wallet_payment_sources.rb +26 -0
- data/db/migrate/20161017102621_create_spree_promotion_code_batch.rb +36 -0
- data/db/migrate/20161129035810_add_index_to_spree_payments_number.rb +5 -0
- data/db/migrate/20170223235001_remove_spree_store_credits_column.rb +5 -0
- data/lib/generators/spree/dummy/templates/rails/application.rb +1 -1
- data/lib/generators/spree/dummy/templates/rails/test.rb +1 -1
- data/lib/generators/spree/install/install_generator.rb +6 -5
- data/lib/spree/core/controller_helpers/payment_parameters.rb +54 -0
- data/lib/spree/core/engine.rb +6 -9
- data/lib/spree/core/version.rb +1 -1
- data/lib/spree/core.rb +0 -1
- data/lib/spree/money.rb +18 -0
- data/lib/spree/permission_sets/default_customer.rb +1 -1
- data/lib/spree/permitted_attributes.rb +1 -1
- data/lib/spree/testing_support/authorization_helpers.rb +1 -0
- data/lib/spree/testing_support/capybara_ext.rb +13 -0
- data/lib/spree/testing_support/factories/order_factory.rb +5 -1
- data/lib/spree/testing_support/factories/payment_factory.rb +1 -1
- data/lib/spree/testing_support/factories/shipment_factory.rb +0 -1
- data/solidus_core.gemspec +3 -3
- data/spec/jobs/promotion_code_batch_job_spec.rb +65 -0
- data/spec/lib/calculated_adjustments_spec.rb +105 -1
- data/spec/lib/spree/core/testing_support/factories/order_factory_spec.rb +4 -1
- data/spec/lib/spree/core/testing_support/factories/payment_factory_spec.rb +8 -0
- data/spec/lib/spree/money_spec.rb +32 -0
- data/spec/lib/spree/permission_sets/default_customer_spec.rb +20 -0
- data/spec/mailers/promotion_code_batch_mailer_spec.rb +45 -0
- data/spec/models/spree/credit_card_spec.rb +86 -86
- data/spec/models/spree/gateway_spec.rb +3 -1
- data/spec/models/spree/inventory_unit_spec.rb +12 -4
- data/spec/models/spree/order/checkout_spec.rb +11 -32
- data/spec/models/spree/order/tax_spec.rb +2 -2
- data/spec/models/spree/order_contents_spec.rb +24 -1
- data/spec/models/spree/order_inventory_spec.rb +130 -83
- data/spec/models/spree/order_spec.rb +15 -117
- data/spec/models/spree/order_update_attributes_spec.rb +1 -44
- data/spec/models/spree/order_updater_spec.rb +10 -13
- data/spec/models/spree/payment_create_spec.rb +5 -1
- data/spec/models/spree/payment_method_spec.rb +16 -0
- data/spec/models/spree/payment_spec.rb +14 -8
- data/spec/models/spree/promotion_code/batch_builder_spec.rb +61 -0
- data/spec/models/spree/promotion_code_batch_spec.rb +58 -0
- data/spec/models/spree/promotion_code_spec.rb +4 -0
- data/spec/models/spree/promotion_spec.rb +3 -6
- data/spec/models/spree/return_authorization_spec.rb +0 -59
- data/spec/models/spree/shipment_spec.rb +4 -4
- data/spec/models/spree/stock/availability_validator_spec.rb +64 -9
- data/spec/models/spree/tax/item_adjuster_spec.rb +1 -2
- data/spec/models/spree/unit_cancel_spec.rb +0 -85
- data/spec/models/spree/user_spec.rb +3 -1
- data/spec/models/spree/variant/vat_price_generator_spec.rb +8 -2
- data/spec/models/spree/variant_spec.rb +16 -4
- data/spec/models/spree/wallet_payment_source_spec.rb +46 -0
- data/spec/models/spree/wallet_spec.rb +128 -0
- data/spec/support/concerns/payment_source.rb +64 -0
- metadata +51 -25
- data/app/assets/javascripts/spree.js.coffee.erb +0 -64
- data/app/models/spree/promotion_builder.rb +0 -55
- data/app/models/spree/promotion_code/code_builder.rb +0 -62
- data/config/initializers/premailer_assets.rb +0 -1
- data/lib/spree/core/unreturned_item_charger.rb +0 -106
- data/lib/tasks/exchanges.rake +0 -47
- data/spec/lib/spree/core/unreturned_item_charger_spec.rb +0 -126
- data/spec/lib/tasks/exchanges_spec.rb +0 -220
- data/spec/models/spree/promotion_builder_spec.rb +0 -120
- data/spec/models/spree/promotion_code/code_builder_spec.rb +0 -77
@@ -5,7 +5,8 @@ module Spree
|
|
5
5
|
# @param attributes [Hash,ActionController::Parameters] attributes which are assigned to the new payment
|
6
6
|
# * :payment_method_id Id of payment method used for this payment
|
7
7
|
# * :source_attributes Attributes used to build the source of this payment. Usually a {CreditCard}
|
8
|
-
# * :existing_card_id (Integer) The id of an existing {CreditCard} object to use
|
8
|
+
# * :existing_card_id (Integer) Deprecated: The id of an existing {CreditCard} object to use
|
9
|
+
# * :wallet_payment_source_id (Integer): The id of a {WalletPaymentSource} to use
|
9
10
|
# @param request_env [Hash] rack env of user creating the payment
|
10
11
|
# @param payment [Payment] Internal use only. Instead of making a new payment, change the attributes for an existing one.
|
11
12
|
def initialize(order, attributes, payment: nil, request_env: {})
|
@@ -27,7 +28,13 @@ module Spree
|
|
27
28
|
@payment.attributes = @attributes
|
28
29
|
|
29
30
|
if source_attributes[:existing_card_id].present?
|
31
|
+
Spree::Deprecation.warn(
|
32
|
+
"Passing existing_card_id to PaymentCreate is deprecated. Use wallet_payment_source_id instead.",
|
33
|
+
caller,
|
34
|
+
)
|
30
35
|
build_existing_card
|
36
|
+
elsif source_attributes[:wallet_payment_source_id].present?
|
37
|
+
build_from_wallet_payment_source
|
31
38
|
else
|
32
39
|
build_source
|
33
40
|
end
|
@@ -44,27 +51,40 @@ module Spree
|
|
44
51
|
if source_attributes.present? && payment_method.try(:payment_source_class)
|
45
52
|
payment.source = payment_method.payment_source_class.new(source_attributes)
|
46
53
|
payment.source.payment_method_id = payment_method.id
|
47
|
-
payment.source.
|
54
|
+
if order && payment.source.respond_to?(:user=)
|
55
|
+
payment.source.user = order.user
|
56
|
+
end
|
48
57
|
end
|
49
58
|
end
|
50
59
|
|
60
|
+
def build_from_wallet_payment_source
|
61
|
+
wallet_payment_source_id = source_attributes.fetch(:wallet_payment_source_id)
|
62
|
+
raise(ActiveRecord::RecordNotFound) if order.user.nil?
|
63
|
+
wallet_payment_source = order.user.wallet.find(wallet_payment_source_id)
|
64
|
+
raise(ActiveRecord::RecordNotFound) if wallet_payment_source.nil?
|
65
|
+
build_from_payment_source(wallet_payment_source.payment_source)
|
66
|
+
end
|
67
|
+
|
51
68
|
def build_existing_card
|
52
69
|
credit_card = available_cards.find(source_attributes[:existing_card_id])
|
70
|
+
build_from_payment_source(credit_card)
|
71
|
+
end
|
53
72
|
|
73
|
+
def build_from_payment_source(payment_source)
|
54
74
|
# FIXME: does this work?
|
55
75
|
if source_attributes[:verification_value]
|
56
|
-
|
76
|
+
payment_source.verification_value = source_attributes[:verification_value]
|
57
77
|
end
|
58
78
|
|
59
|
-
payment.source =
|
60
|
-
payment.payment_method_id =
|
79
|
+
payment.source = payment_source
|
80
|
+
payment.payment_method_id = payment_source.payment_method_id
|
61
81
|
end
|
62
82
|
|
63
83
|
def available_cards
|
64
84
|
if user_id = order.user_id
|
65
|
-
CreditCard.where(user_id: user_id)
|
85
|
+
Spree::CreditCard.where(user_id: user_id)
|
66
86
|
else
|
67
|
-
CreditCard.none
|
87
|
+
Spree::CreditCard.none
|
68
88
|
end
|
69
89
|
end
|
70
90
|
end
|
@@ -109,7 +109,7 @@ module Spree
|
|
109
109
|
|
110
110
|
def handle_action(action, action_name, auth_code)
|
111
111
|
# Find first event with provided auth_code
|
112
|
-
store_credit = StoreCreditEvent.find_by_authorization_code(auth_code).try(:store_credit)
|
112
|
+
store_credit = Spree::StoreCreditEvent.find_by_authorization_code(auth_code).try(:store_credit)
|
113
113
|
|
114
114
|
if store_credit.nil?
|
115
115
|
ActiveMerchant::Billing::Response.new(false, Spree.t('store_credit.unable_to_find_for_action', auth_code: auth_code, action: action_name), {}, {})
|
@@ -119,8 +119,8 @@ module Spree
|
|
119
119
|
end
|
120
120
|
|
121
121
|
def auth_or_capture_event(auth_code)
|
122
|
-
capture_event = StoreCreditEvent.find_by(authorization_code: auth_code, action: Spree::StoreCredit::CAPTURE_ACTION)
|
123
|
-
auth_event = StoreCreditEvent.find_by(authorization_code: auth_code, action: Spree::StoreCredit::AUTHORIZE_ACTION)
|
122
|
+
capture_event = Spree::StoreCreditEvent.find_by(authorization_code: auth_code, action: Spree::StoreCredit::CAPTURE_ACTION)
|
123
|
+
auth_event = Spree::StoreCreditEvent.find_by(authorization_code: auth_code, action: Spree::StoreCredit::AUTHORIZE_ACTION)
|
124
124
|
capture_event || auth_event
|
125
125
|
end
|
126
126
|
end
|
@@ -17,7 +17,10 @@ module Spree
|
|
17
17
|
scope :active, -> { where(active: true) }
|
18
18
|
scope :available_to_users, -> { where(available_to_users: true) }
|
19
19
|
scope :available_to_admin, -> { where(available_to_admin: true) }
|
20
|
-
scope :available_to_store, ->
|
20
|
+
scope :available_to_store, ->(store) do
|
21
|
+
raise ArgumentError, "You must provide a store" if store.nil?
|
22
|
+
store.payment_methods.empty? ? all : where(id: store.payment_method_ids)
|
23
|
+
end
|
21
24
|
|
22
25
|
include Spree::Preferences::StaticallyConfigurable
|
23
26
|
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Spree
|
2
|
+
class PaymentSource < Spree::Base
|
3
|
+
self.abstract_class = true
|
4
|
+
|
5
|
+
belongs_to :payment_method
|
6
|
+
|
7
|
+
has_many :payments, as: :source
|
8
|
+
has_many :wallet_payment_sources, class_name: 'Spree::WalletPaymentSource', as: :payment_source, inverse_of: :payment_source
|
9
|
+
|
10
|
+
attr_accessor :imported
|
11
|
+
|
12
|
+
# @return [Array<String>] the actions available on this payment source
|
13
|
+
def actions
|
14
|
+
%w(capture void credit)
|
15
|
+
end
|
16
|
+
|
17
|
+
# @param payment [Spree::Payment] the payment we want to know if can be captured
|
18
|
+
# @return [Boolean] true when the payment is in the pending or checkout states
|
19
|
+
def can_capture?(payment)
|
20
|
+
payment.pending? || payment.checkout?
|
21
|
+
end
|
22
|
+
|
23
|
+
# @param payment [Spree::Payment] the payment we want to know if can be voided
|
24
|
+
# @return [Boolean] true when the payment is not failed or voided
|
25
|
+
def can_void?(payment)
|
26
|
+
!payment.failed? && !payment.void?
|
27
|
+
end
|
28
|
+
|
29
|
+
# Indicates whether its possible to credit the payment. Note that most
|
30
|
+
# gateways require that the payment be settled first which generally
|
31
|
+
# happens within 12-24 hours of the transaction.
|
32
|
+
#
|
33
|
+
# @param payment [Spree::Payment] the payment we want to know if can be credited
|
34
|
+
# @return [Boolean] true when the payment is completed and can be credited
|
35
|
+
def can_credit?(payment)
|
36
|
+
payment.completed? && payment.credit_allowed > 0
|
37
|
+
end
|
38
|
+
|
39
|
+
# Indicates whether this payment source can be used more than once. E.g. a
|
40
|
+
# credit card with a 'payment profile'.
|
41
|
+
def reusable?
|
42
|
+
false
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -46,12 +46,12 @@ module Spree
|
|
46
46
|
# This scope selects products in taxon AND all its descendants
|
47
47
|
# If you need products only within one taxon use
|
48
48
|
#
|
49
|
-
# Spree::Product.joins(:taxons).where(Taxon.table_name => { :
|
49
|
+
# Spree::Product.joins(:taxons).where(Taxon.table_name => { id: taxon.id })
|
50
50
|
#
|
51
51
|
# If you're using count on the result of this scope, you must use the
|
52
52
|
# `:distinct` option as well:
|
53
53
|
#
|
54
|
-
# Spree::Product.in_taxon(taxon).count(:
|
54
|
+
# Spree::Product.in_taxon(taxon).count(distinct: true)
|
55
55
|
#
|
56
56
|
# This is so that the count query is distinct'd:
|
57
57
|
#
|
@@ -84,12 +84,12 @@ module Spree
|
|
84
84
|
# note that it can test for properties with NULL values, but not for absent values
|
85
85
|
add_search_scope :with_property_value do |property, value|
|
86
86
|
joins(:properties)
|
87
|
-
.where("#{ProductProperty.table_name}.value = ?", value)
|
87
|
+
.where("#{Spree::ProductProperty.table_name}.value = ?", value)
|
88
88
|
.where(property_conditions(property))
|
89
89
|
end
|
90
90
|
|
91
91
|
add_search_scope :with_option do |option|
|
92
|
-
option_types = OptionType.table_name
|
92
|
+
option_types = Spree::OptionType.table_name
|
93
93
|
conditions = case option
|
94
94
|
when String then { "#{option_types}.name" => option }
|
95
95
|
when OptionType then { "#{option_types}.id" => option.id }
|
@@ -100,10 +100,10 @@ module Spree
|
|
100
100
|
end
|
101
101
|
|
102
102
|
add_search_scope :with_option_value do |option, value|
|
103
|
-
option_values = OptionValue.table_name
|
103
|
+
option_values = Spree::OptionValue.table_name
|
104
104
|
option_type_id = case option
|
105
|
-
when String then OptionType.find_by(name: option) || option.to_i
|
106
|
-
when OptionType then option.id
|
105
|
+
when String then Spree::OptionType.find_by(name: option) || option.to_i
|
106
|
+
when Spree::OptionType then option.id
|
107
107
|
else option.to_i
|
108
108
|
end
|
109
109
|
|
@@ -117,7 +117,7 @@ module Spree
|
|
117
117
|
add_search_scope :with do |value|
|
118
118
|
includes(variants_including_master: :option_values).
|
119
119
|
includes(:product_properties).
|
120
|
-
where("#{OptionValue.table_name}.name = ? OR #{ProductProperty.table_name}.value = ?", value, value)
|
120
|
+
where("#{Spree::OptionValue.table_name}.name = ? OR #{Spree::ProductProperty.table_name}.value = ?", value, value)
|
121
121
|
end
|
122
122
|
|
123
123
|
# Finds all products that have a name containing the given words.
|
@@ -147,38 +147,38 @@ module Spree
|
|
147
147
|
# there is alternative faster and more elegant solution, it has small drawback though,
|
148
148
|
# it doesn stack with other scopes :/
|
149
149
|
#
|
150
|
-
# :
|
151
|
-
# :
|
150
|
+
# joins: "LEFT OUTER JOIN (SELECT line_items.variant_id as vid, COUNT(*) as cnt FROM line_items GROUP BY line_items.variant_id) AS popularity_count ON variants.id = vid",
|
151
|
+
# order: 'COALESCE(cnt, 0) DESC'
|
152
152
|
add_search_scope :descend_by_popularity do
|
153
153
|
joins(:master).
|
154
154
|
order(%{
|
155
155
|
COALESCE((
|
156
156
|
SELECT
|
157
|
-
COUNT(#{LineItem.quoted_table_name}.id)
|
157
|
+
COUNT(#{Spree::LineItem.quoted_table_name}.id)
|
158
158
|
FROM
|
159
|
-
#{LineItem.quoted_table_name}
|
159
|
+
#{Spree::LineItem.quoted_table_name}
|
160
160
|
JOIN
|
161
|
-
#{Variant.quoted_table_name} AS popular_variants
|
161
|
+
#{Spree::Variant.quoted_table_name} AS popular_variants
|
162
162
|
ON
|
163
|
-
popular_variants.id = #{LineItem.quoted_table_name}.variant_id
|
163
|
+
popular_variants.id = #{Spree::LineItem.quoted_table_name}.variant_id
|
164
164
|
WHERE
|
165
|
-
popular_variants.product_id = #{Product.quoted_table_name}.id
|
165
|
+
popular_variants.product_id = #{Spree::Product.quoted_table_name}.id
|
166
166
|
), 0) DESC
|
167
167
|
})
|
168
168
|
end
|
169
169
|
|
170
170
|
add_search_scope :not_deleted do
|
171
|
-
where("#{Product.quoted_table_name}.deleted_at IS NULL or #{Product.quoted_table_name}.deleted_at >= ?", Time.current)
|
171
|
+
where("#{Spree::Product.quoted_table_name}.deleted_at IS NULL or #{Spree::Product.quoted_table_name}.deleted_at >= ?", Time.current)
|
172
172
|
end
|
173
173
|
|
174
174
|
# Can't use add_search_scope for this as it needs a default argument
|
175
175
|
def self.available(available_on = nil)
|
176
|
-
joins(master: :prices).where("#{Product.quoted_table_name}.available_on <= ?", available_on || Time.current)
|
176
|
+
joins(master: :prices).where("#{Spree::Product.quoted_table_name}.available_on <= ?", available_on || Time.current)
|
177
177
|
end
|
178
178
|
search_scopes << :available
|
179
179
|
|
180
180
|
add_search_scope :taxons_name_eq do |name|
|
181
|
-
group("spree_products.id").joins(:taxons).where(Taxon.arel_table[:name].eq(name))
|
181
|
+
group("spree_products.id").joins(:taxons).where(Spree::Taxon.arel_table[:name].eq(name))
|
182
182
|
end
|
183
183
|
|
184
184
|
def self.distinct_by_product_ids(sort_order = nil)
|
@@ -210,13 +210,13 @@ module Spree
|
|
210
210
|
private
|
211
211
|
|
212
212
|
def price_table_name
|
213
|
-
Price.quoted_table_name
|
213
|
+
Spree::Price.quoted_table_name
|
214
214
|
end
|
215
215
|
|
216
216
|
# specifically avoid having an order for taxon search (conflicts with main order)
|
217
217
|
def prepare_taxon_conditions(taxons)
|
218
218
|
ids = taxons.map { |taxon| taxon.self_and_descendants.pluck(:id) }.flatten.uniq
|
219
|
-
joins(:taxons).where("#{Taxon.table_name}.id" => ids)
|
219
|
+
joins(:taxons).where("#{Spree::Taxon.table_name}.id" => ids)
|
220
220
|
end
|
221
221
|
|
222
222
|
# Produce an array of keywords for use in scopes.
|
@@ -228,14 +228,14 @@ module Spree
|
|
228
228
|
end
|
229
229
|
|
230
230
|
def get_taxons(*ids_or_records_or_names)
|
231
|
-
taxons = Taxon.table_name
|
231
|
+
taxons = Spree::Taxon.table_name
|
232
232
|
ids_or_records_or_names.flatten.map { |t|
|
233
233
|
case t
|
234
|
-
when Integer then Taxon.find_by(id: t)
|
234
|
+
when Integer then Spree::Taxon.find_by(id: t)
|
235
235
|
when ActiveRecord::Base then t
|
236
236
|
when String
|
237
|
-
Taxon.find_by(name: t) ||
|
238
|
-
Taxon.where("#{taxons}.permalink LIKE ? OR #{taxons}.permalink = ?", "%/#{t}/", "#{t}/").first
|
237
|
+
Spree::Taxon.find_by(name: t) ||
|
238
|
+
Spree::Taxon.where("#{taxons}.permalink LIKE ? OR #{taxons}.permalink = ?", "%/#{t}/", "#{t}/").first
|
239
239
|
end
|
240
240
|
}.compact.flatten.uniq
|
241
241
|
end
|
data/app/models/spree/product.rb
CHANGED
@@ -91,7 +91,7 @@ module Spree
|
|
91
91
|
validates :name, presence: true
|
92
92
|
validates :price, presence: true, if: proc { Spree::Config[:require_master_price] }
|
93
93
|
validates :shipping_category_id, presence: true
|
94
|
-
validates :slug,
|
94
|
+
validates :slug, presence: true, uniqueness: { allow_blank: true }
|
95
95
|
|
96
96
|
attr_accessor :option_values_hash
|
97
97
|
|
@@ -110,7 +110,7 @@ module Spree
|
|
110
110
|
|
111
111
|
# @return [Spree::TaxCategory] tax category for this product, or the default tax category
|
112
112
|
def tax_category
|
113
|
-
super || TaxCategory.find_by(is_default: true)
|
113
|
+
super || Spree::TaxCategory.find_by(is_default: true)
|
114
114
|
end
|
115
115
|
|
116
116
|
# Ensures option_types and product_option_types exist for keys in
|
@@ -232,8 +232,8 @@ module Spree
|
|
232
232
|
def set_property(property_name, property_value)
|
233
233
|
ActiveRecord::Base.transaction do
|
234
234
|
# Works around spree_i18n https://github.com/spree/spree/issues/301
|
235
|
-
property = Property.create_with(presentation: property_name).find_or_create_by(name: property_name)
|
236
|
-
product_property = ProductProperty.where(product: self, property: property).first_or_initialize
|
235
|
+
property = Spree::Property.create_with(presentation: property_name).find_or_create_by(name: property_name)
|
236
|
+
product_property = Spree::ProductProperty.where(product: self, property: property).first_or_initialize
|
237
237
|
product_property.value = property_value
|
238
238
|
product_property.save!
|
239
239
|
end
|
@@ -19,6 +19,8 @@ module Spree
|
|
19
19
|
has_many :codes, class_name: "Spree::PromotionCode", inverse_of: :promotion, dependent: :destroy
|
20
20
|
alias_method :promotion_codes, :codes
|
21
21
|
|
22
|
+
has_many :promotion_code_batches, class_name: "Spree::PromotionCodeBatch", dependent: :destroy
|
23
|
+
|
22
24
|
accepts_nested_attributes_for :promotion_actions, :promotion_rules
|
23
25
|
|
24
26
|
validates_associated :rules
|
@@ -0,0 +1,63 @@
|
|
1
|
+
class ::Spree::PromotionCode::BatchBuilder
|
2
|
+
attr_reader :promotion_code_batch
|
3
|
+
delegate :promotion, :number_of_codes, :base_code, to: :promotion_code_batch
|
4
|
+
|
5
|
+
class_attribute :random_code_length, :batch_size, :sample_characters
|
6
|
+
self.random_code_length = 6
|
7
|
+
self.batch_size = 1_000
|
8
|
+
self.sample_characters = ('a'..'z').to_a + (2..9).to_a.map(&:to_s)
|
9
|
+
|
10
|
+
def initialize(promotion_code_batch)
|
11
|
+
@promotion_code_batch = promotion_code_batch
|
12
|
+
end
|
13
|
+
|
14
|
+
def build_promotion_codes
|
15
|
+
generate_random_codes
|
16
|
+
promotion_code_batch.update!(state: "completed")
|
17
|
+
rescue => e
|
18
|
+
promotion_code_batch.update!(
|
19
|
+
error: e.inspect,
|
20
|
+
state: "failed"
|
21
|
+
)
|
22
|
+
raise e
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def generate_random_codes
|
28
|
+
total_batches = (number_of_codes.to_f / self.class.batch_size).ceil
|
29
|
+
|
30
|
+
total_batches.times do |i|
|
31
|
+
codes_for_current_batch = Set.new
|
32
|
+
codes_to_generate = [self.class.batch_size, number_of_codes - i * batch_size].min
|
33
|
+
|
34
|
+
while codes_for_current_batch.size < codes_to_generate
|
35
|
+
new_codes = Array.new(codes_to_generate) { generate_random_code }.to_set
|
36
|
+
codes_for_current_batch += get_unique_codes(new_codes)
|
37
|
+
end
|
38
|
+
|
39
|
+
codes_for_current_batch.map do |value|
|
40
|
+
promotion.codes.build(value: value, promotion_code_batch: promotion_code_batch)
|
41
|
+
end
|
42
|
+
|
43
|
+
promotion.save!
|
44
|
+
|
45
|
+
# We have to reload the promotion because otherwise all promotion codes
|
46
|
+
# we are creating will remain in memory. Doing a reload will remove all
|
47
|
+
# codes from memory.
|
48
|
+
promotion.reload
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def generate_random_code
|
53
|
+
suffix = Array.new(self.class.random_code_length) do
|
54
|
+
sample_characters.sample
|
55
|
+
end.join
|
56
|
+
|
57
|
+
"#{base_code}_#{suffix}"
|
58
|
+
end
|
59
|
+
|
60
|
+
def get_unique_codes(code_set)
|
61
|
+
code_set - Spree::PromotionCode.where(value: code_set.to_a).pluck(:value)
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Spree
|
2
|
+
class PromotionCodeBatch < ActiveRecord::Base
|
3
|
+
class CantProcessStartedBatch < StandardError
|
4
|
+
end
|
5
|
+
|
6
|
+
belongs_to :promotion, class_name: "Spree::Promotion"
|
7
|
+
has_many :promotion_codes, class_name: "Spree::PromotionCode", dependent: :destroy
|
8
|
+
|
9
|
+
validates :number_of_codes, numericality: { greater_than: 0 }
|
10
|
+
validates_presence_of :base_code, :number_of_codes
|
11
|
+
|
12
|
+
def finished?
|
13
|
+
state == "completed"
|
14
|
+
end
|
15
|
+
|
16
|
+
def process
|
17
|
+
if state == "pending"
|
18
|
+
update!(state: "processing")
|
19
|
+
PromotionCodeBatchJob.perform_later(self)
|
20
|
+
else
|
21
|
+
raise CantProcessStartedBatch.new("Batch #{id} already started")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -35,13 +35,13 @@ module Spree
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def connected_order_promotions
|
38
|
-
Promotion.active.includes(:promotion_rules).
|
38
|
+
Spree::Promotion.active.includes(:promotion_rules).
|
39
39
|
joins(:order_promotions).
|
40
40
|
where(spree_orders_promotions: { order_id: order.id }).readonly(false).to_a
|
41
41
|
end
|
42
42
|
|
43
43
|
def sale_promotions
|
44
|
-
Promotion.where(apply_automatically: true).active.includes(:promotion_rules)
|
44
|
+
Spree::Promotion.where(apply_automatically: true).active.includes(:promotion_rules)
|
45
45
|
end
|
46
46
|
|
47
47
|
def promotion_code(promotion)
|
@@ -95,8 +95,7 @@ module Spree
|
|
95
95
|
discount ||= order.adjustments.promotion.detect(&detector)
|
96
96
|
|
97
97
|
if discount && discount.eligible
|
98
|
-
order.
|
99
|
-
order.persist_totals
|
98
|
+
order.update!
|
100
99
|
set_success_code :coupon_code_applied
|
101
100
|
elsif order.promotions.with_coupon_code(order.coupon_code)
|
102
101
|
# if the promotion exists on an order, but wasn't found above,
|
@@ -10,38 +10,49 @@ module Spree
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def activate
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
13
|
+
connected_promotions.each do |order_promotion|
|
14
|
+
order_promotion.promotion.activate(
|
15
|
+
order: order,
|
16
|
+
promotion_code: order_promotion.promotion_code,
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
not_connected_automatic_promotions.each do |promotion|
|
21
|
+
promotion.activate(order: order)
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
25
|
private
|
26
26
|
|
27
|
-
def
|
28
|
-
|
27
|
+
def not_connected_automatic_promotions
|
28
|
+
automatic_promotions - connected_promotions.map(&:promotion)
|
29
|
+
end
|
30
|
+
|
31
|
+
def automatic_promotions
|
32
|
+
@automatic_promotions ||= active_free_shipping_promotions.
|
33
|
+
where(apply_automatically: true).
|
34
|
+
to_a.
|
35
|
+
uniq
|
36
|
+
end
|
37
|
+
|
38
|
+
def connected_promotions
|
39
|
+
@connected_promotions ||= order.order_promotions.
|
40
|
+
joins(:promotion).
|
41
|
+
includes(:promotion).
|
42
|
+
merge(active_free_shipping_promotions).
|
43
|
+
to_a.
|
44
|
+
uniq
|
45
|
+
end
|
46
|
+
|
47
|
+
def active_free_shipping_promotions
|
48
|
+
Spree::Promotion.all.
|
29
49
|
active.
|
30
50
|
joins(:promotion_actions).
|
31
51
|
merge(
|
32
52
|
Spree::PromotionAction.of_type(
|
33
53
|
Spree::Promotion::Actions::FreeShipping
|
34
54
|
)
|
35
|
-
)
|
36
|
-
distinct
|
37
|
-
end
|
38
|
-
|
39
|
-
def existing_order_promotion(promotion)
|
40
|
-
@lookup ||= order.order_promotions.map do |order_promotion|
|
41
|
-
[order_promotion.promotion_id, order_promotion]
|
42
|
-
end.to_h
|
43
|
-
|
44
|
-
@lookup[promotion.id]
|
55
|
+
)
|
45
56
|
end
|
46
57
|
end
|
47
58
|
end
|
@@ -38,7 +38,7 @@ module Spree
|
|
38
38
|
# Refund.total_amount_reimbursed_for(reimbursement)
|
39
39
|
# See the `reimbursement_generator` property regarding the generation of custom reimbursements.
|
40
40
|
class_attribute :reimbursement_models
|
41
|
-
self.reimbursement_models = [Refund, Reimbursement::Credit]
|
41
|
+
self.reimbursement_models = [Spree::Refund, Spree::Reimbursement::Credit]
|
42
42
|
|
43
43
|
# The reimbursement_performer property should be set to an object that responds to the following methods:
|
44
44
|
# - #perform
|
@@ -13,8 +13,6 @@ module Spree
|
|
13
13
|
|
14
14
|
before_create :generate_number
|
15
15
|
|
16
|
-
after_save :generate_expedited_exchange_reimbursements
|
17
|
-
|
18
16
|
accepts_nested_attributes_for :return_items, allow_destroy: true
|
19
17
|
|
20
18
|
validates :order, presence: true
|
@@ -22,11 +20,6 @@ module Spree
|
|
22
20
|
validate :must_have_shipped_units, on: :create
|
23
21
|
validate :no_previously_exchanged_inventory_units, on: :create
|
24
22
|
|
25
|
-
# These are called prior to generating expedited exchanges shipments.
|
26
|
-
# Should respond to a "call" method that takes the list of return items
|
27
|
-
class_attribute :pre_expedited_exchange_hooks
|
28
|
-
self.pre_expedited_exchange_hooks = []
|
29
|
-
|
30
23
|
state_machine initial: :authorized do
|
31
24
|
before_transition to: :canceled, do: :cancel_return_items
|
32
25
|
|
@@ -88,26 +81,5 @@ module Spree
|
|
88
81
|
def cancel_return_items
|
89
82
|
return_items.each { |item| item.cancel! if item.can_cancel? }
|
90
83
|
end
|
91
|
-
|
92
|
-
def generate_expedited_exchange_reimbursements
|
93
|
-
return unless Spree::Config[:expedited_exchanges]
|
94
|
-
|
95
|
-
items_to_exchange = return_items.select(&:exchange_required?)
|
96
|
-
items_to_exchange.each(&:attempt_accept)
|
97
|
-
items_to_exchange.select!(&:accepted?)
|
98
|
-
|
99
|
-
return if items_to_exchange.blank?
|
100
|
-
|
101
|
-
pre_expedited_exchange_hooks.each { |h| h.call items_to_exchange }
|
102
|
-
|
103
|
-
reimbursement = Reimbursement.new(return_items: items_to_exchange, order: order)
|
104
|
-
|
105
|
-
if reimbursement.save
|
106
|
-
reimbursement.perform!
|
107
|
-
else
|
108
|
-
errors.add(:base, reimbursement.errors.full_messages)
|
109
|
-
raise ActiveRecord::RecordInvalid.new(self)
|
110
|
-
end
|
111
|
-
end
|
112
84
|
end
|
113
85
|
end
|
@@ -124,7 +124,7 @@ module Spree
|
|
124
124
|
transition to: :manual_intervention_required, from: [:accepted, :pending, :manual_intervention_required]
|
125
125
|
end
|
126
126
|
|
127
|
-
after_transition any => any, :
|
127
|
+
after_transition any => any, do: :persist_acceptance_status_errors
|
128
128
|
end
|
129
129
|
|
130
130
|
# @param inventory_unit [Spree::InventoryUnit] the inventory for which we
|
@@ -72,7 +72,7 @@ module Spree
|
|
72
72
|
self.whitelisted_ransackable_associations = ['order']
|
73
73
|
self.whitelisted_ransackable_attributes = ['number']
|
74
74
|
|
75
|
-
delegate :tax_category, to: :selected_shipping_rate, allow_nil: true
|
75
|
+
delegate :tax_category, :tax_category_id, to: :selected_shipping_rate, allow_nil: true
|
76
76
|
|
77
77
|
def can_transition_from_pending_to_shipped?
|
78
78
|
!requires_shipment?
|
@@ -136,9 +136,9 @@ module Spree
|
|
136
136
|
def finalize!
|
137
137
|
transaction do
|
138
138
|
pending_units = inventory_units.select(&:pending?)
|
139
|
-
pending_manifest = ShippingManifest.new(inventory_units: pending_units)
|
139
|
+
pending_manifest = Spree::ShippingManifest.new(inventory_units: pending_units)
|
140
140
|
pending_manifest.items.each { |item| manifest_unstock(item) }
|
141
|
-
InventoryUnit.finalize_units!(pending_units)
|
141
|
+
Spree::InventoryUnit.finalize_units!(pending_units)
|
142
142
|
end
|
143
143
|
end
|
144
144
|
|
@@ -391,7 +391,7 @@ module Spree
|
|
391
391
|
end
|
392
392
|
|
393
393
|
def ensure_can_destroy
|
394
|
-
|
394
|
+
if shipped? || canceled?
|
395
395
|
errors.add(:state, :cannot_destroy, state: state)
|
396
396
|
throw :abort
|
397
397
|
end
|
@@ -34,7 +34,7 @@ module Spree
|
|
34
34
|
# Some extra care is needed with the having clause to ensure we are
|
35
35
|
# counting distinct records of the join table. Otherwise a join could
|
36
36
|
# cause this to return incorrect results.
|
37
|
-
join_table = ShippingMethodCategory.arel_table
|
37
|
+
join_table = Spree::ShippingMethodCategory.arel_table
|
38
38
|
having = join_table[:id].count(true).eq(shipping_category_ids.count)
|
39
39
|
joins(:shipping_method_categories).
|
40
40
|
where(spree_shipping_method_categories: { shipping_category_id: shipping_category_ids }).
|
@@ -46,7 +46,7 @@ module Spree
|
|
46
46
|
# @return [ActiveRecord::Relation] shipping methods which are available
|
47
47
|
# with the stock location or are marked available_to_all
|
48
48
|
def self.available_in_stock_location(stock_location)
|
49
|
-
smsl_table = ShippingMethodStockLocation.arel_table
|
49
|
+
smsl_table = Spree::ShippingMethodStockLocation.arel_table
|
50
50
|
|
51
51
|
# We are searching for either a matching entry in the stock location join
|
52
52
|
# table or available_to_all being true.
|
@@ -12,7 +12,7 @@ module Spree
|
|
12
12
|
dependent: :destroy
|
13
13
|
|
14
14
|
delegate :order, :currency, to: :shipment
|
15
|
-
delegate :name, :tax_category, to: :shipping_method
|
15
|
+
delegate :name, :tax_category, :tax_category_id, to: :shipping_method
|
16
16
|
delegate :code, to: :shipping_method, prefix: true
|
17
17
|
alias_attribute :amount, :cost
|
18
18
|
|