solidus_core 3.1.5 → 3.2.0.alpha
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/helpers/spree/products_helper.rb +1 -1
- data/app/models/concerns/spree/active_storage_adapter/attachment.rb +26 -11
- data/app/models/concerns/spree/active_storage_adapter.rb +1 -1
- data/app/models/concerns/spree/user_address_book.rb +11 -1
- data/app/models/concerns/spree/user_methods.rb +20 -1
- data/app/models/spree/adjustment.rb +1 -0
- data/app/models/spree/carton.rb +1 -1
- data/app/models/spree/log_entry.rb +74 -1
- data/app/models/spree/option_value.rb +9 -0
- data/app/models/spree/order.rb +68 -29
- data/app/models/spree/order_contents.rb +2 -1
- data/app/models/spree/order_inventory.rb +1 -1
- data/app/models/spree/order_merger.rb +2 -2
- data/app/models/spree/order_shipping.rb +6 -9
- data/app/models/spree/order_taxation.rb +6 -4
- data/app/models/spree/order_updater.rb +4 -3
- data/app/models/spree/payment_method.rb +11 -0
- data/app/models/spree/product/scopes.rb +21 -3
- data/app/models/spree/product.rb +2 -1
- data/app/models/spree/promotion/actions/create_adjustment.rb +4 -0
- data/app/models/spree/promotion/actions/create_item_adjustments.rb +5 -6
- data/app/models/spree/promotion/rules/product.rb +20 -8
- data/app/models/spree/promotion/rules/store.rb +4 -0
- data/app/models/spree/promotion/rules/taxon.rb +4 -0
- data/app/models/spree/promotion/rules/user.rb +4 -0
- data/app/models/spree/promotion.rb +34 -23
- data/app/models/spree/promotion_action.rb +4 -0
- data/app/models/spree/promotion_code.rb +10 -6
- data/app/models/spree/promotion_handler/cart.rb +26 -6
- data/app/models/spree/promotion_rule.rb +5 -0
- data/app/models/spree/refund.rb +8 -0
- data/app/models/spree/reimbursement.rb +2 -2
- data/app/models/spree/return_item.rb +1 -2
- data/app/models/spree/stock/allocator/on_hand_first.rb +2 -2
- data/app/models/spree/stock/quantifier.rb +12 -8
- data/app/models/spree/stock/simple_coordinator.rb +2 -1
- data/app/models/spree/store_credit.rb +8 -0
- data/app/models/spree/tax/item_tax.rb +3 -2
- data/app/models/spree/tax/order_tax.rb +3 -1
- data/app/models/spree/tax/tax_location.rb +4 -7
- data/app/models/spree/tax_rate.rb +2 -0
- data/app/models/spree/variant.rb +1 -1
- data/app/subscribers/spree/mailer_subscriber.rb +4 -0
- data/app/subscribers/spree/order_mailer_subscriber.rb +35 -0
- data/config/locales/en.yml +9 -2
- data/db/migrate/20201127212108_add_type_before_removal_to_spree_payment_methods.rb +7 -0
- data/db/migrate/20220317165036_set_promotions_with_any_policy_to_all_if_possible.rb +20 -0
- data/lib/generators/solidus/install/install_generator/bundler_context.rb +97 -0
- data/lib/generators/solidus/install/install_generator/install_frontend.rb +50 -0
- data/lib/generators/solidus/install/install_generator/support_solidus_frontend_extraction.rb +48 -0
- data/lib/generators/solidus/install/install_generator.rb +58 -50
- data/lib/generators/solidus/install/templates/config/initializers/spree.rb.tt +6 -16
- data/lib/generators/solidus/install/templates/vendor/assets/javascripts/spree/backend/all.js +2 -2
- data/lib/spree/app_configuration.rb +43 -0
- data/lib/spree/bus.rb +20 -0
- data/lib/spree/core/controller_helpers/auth.rb +9 -1
- data/lib/spree/core/controller_helpers/current_host.rb +1 -3
- data/lib/spree/core/controller_helpers/order.rb +10 -10
- data/lib/spree/core/controller_helpers/search.rb +1 -1
- data/lib/spree/core/engine.rb +39 -8
- data/lib/spree/core/state_machines/order.rb +1 -1
- data/lib/spree/core/stock_configuration.rb +18 -0
- data/lib/spree/core/validators/email.rb +3 -1
- data/lib/spree/core/version.rb +1 -1
- data/lib/spree/core.rb +20 -0
- data/lib/spree/event/subscriber_registry.rb +4 -6
- data/lib/spree/event.rb +1 -1
- data/lib/spree/migrations.rb +1 -1
- data/lib/spree/permission_sets/default_customer.rb +8 -1
- data/lib/spree/permitted_attributes.rb +4 -4
- data/lib/spree/preferences/configuration.rb +34 -12
- data/lib/spree/preferences/preferable_class_methods.rb +1 -1
- data/lib/spree/preferences/preference_differentiator.rb +2 -1
- data/lib/spree/preferences/static_model_preferences.rb +0 -2
- data/lib/spree/rails_compatibility.rb +99 -0
- data/lib/spree/testing_support/bus_helpers.rb +101 -0
- data/lib/spree/testing_support/common_rake.rb +47 -19
- data/lib/spree/testing_support/dummy_app/assets/javascripts/spree/backend/all.js +1 -1
- data/lib/spree/testing_support/dummy_app/assets/javascripts/spree/frontend/all.js +1 -1
- data/lib/spree/testing_support/dummy_app.rb +6 -1
- data/lib/spree/testing_support/factories/address_factory.rb +7 -2
- data/lib/spree/testing_support/factories/inventory_unit_factory.rb +1 -1
- data/lib/spree/testing_support/factories/order_factory.rb +8 -4
- data/lib/spree/testing_support/factories/product_factory.rb +4 -1
- data/lib/spree/testing_support/factories/store_credit_factory.rb +4 -4
- data/lib/spree/testing_support/factories/user_factory.rb +6 -0
- data/lib/spree/testing_support/factory_bot.rb +1 -1
- data/lib/spree/testing_support/order_walkthrough.rb +5 -4
- data/lib/spree/testing_support/silence_deprecations.rb +9 -0
- data/lib/tasks/payment_method.rake +29 -0
- data/lib/tasks/solidus/delete_prices_with_nil_amount.rake +2 -2
- data/lib/tasks/solidus/split_promotions_with_any_match_policy.rake +33 -0
- data/solidus_core.gemspec +7 -2
- metadata +88 -23
- data/lib/generators/solidus/install/templates/vendor/assets/javascripts/spree/frontend/all.js +0 -10
- data/lib/generators/solidus/install/templates/vendor/assets/stylesheets/spree/frontend/all.css +0 -9
@@ -15,6 +15,10 @@ module Spree
|
|
15
15
|
before_destroy :remove_adjustments_from_incomplete_orders
|
16
16
|
before_discard :remove_adjustments_from_incomplete_orders
|
17
17
|
|
18
|
+
def preload_relations
|
19
|
+
[:calculator]
|
20
|
+
end
|
21
|
+
|
18
22
|
# Creates the adjustment related to a promotion for the order passed
|
19
23
|
# through options hash
|
20
24
|
#
|
@@ -15,6 +15,10 @@ module Spree
|
|
15
15
|
before_destroy :remove_adjustments_from_incomplete_orders
|
16
16
|
before_discard :remove_adjustments_from_incomplete_orders
|
17
17
|
|
18
|
+
def preload_relations
|
19
|
+
[:calculator]
|
20
|
+
end
|
21
|
+
|
18
22
|
def perform(payload = {})
|
19
23
|
order = payload[:order]
|
20
24
|
promotion = payload[:promotion]
|
@@ -83,13 +87,8 @@ module Spree
|
|
83
87
|
end
|
84
88
|
|
85
89
|
def line_items_to_adjust(promotion, order)
|
86
|
-
excluded_ids = adjustments.
|
87
|
-
where(adjustable_id: order.line_items.pluck(:id), adjustable_type: 'Spree::LineItem').
|
88
|
-
pluck(:adjustable_id).
|
89
|
-
to_set
|
90
|
-
|
91
90
|
order.line_items.select do |line_item|
|
92
|
-
|
91
|
+
line_item.adjustments.none? { |adjustment| adjustment.source == self } &&
|
93
92
|
promotion.line_item_actionable?(order, line_item)
|
94
93
|
end
|
95
94
|
end
|
@@ -12,6 +12,10 @@ module Spree
|
|
12
12
|
class_name: 'Spree::ProductPromotionRule'
|
13
13
|
has_many :products, class_name: 'Spree::Product', through: :product_promotion_rules
|
14
14
|
|
15
|
+
def preload_relations
|
16
|
+
[:products]
|
17
|
+
end
|
18
|
+
|
15
19
|
MATCH_POLICIES = %w(any all none)
|
16
20
|
|
17
21
|
validates_inclusion_of :preferred_match_policy, in: MATCH_POLICIES
|
@@ -31,17 +35,19 @@ module Spree
|
|
31
35
|
return true if eligible_products.empty?
|
32
36
|
|
33
37
|
case preferred_match_policy
|
34
|
-
when
|
35
|
-
unless eligible_products.all? { |product| order.
|
38
|
+
when "all"
|
39
|
+
unless eligible_products.all? { |product| order_products(order).include?(product) }
|
36
40
|
eligibility_errors.add(:base, eligibility_error_message(:missing_product), error_code: :missing_product)
|
37
41
|
end
|
38
|
-
when
|
39
|
-
unless order.
|
40
|
-
eligibility_errors.add(:base, eligibility_error_message(:no_applicable_products),
|
42
|
+
when "any"
|
43
|
+
unless order_products(order).any? { |product| eligible_products.include?(product) }
|
44
|
+
eligibility_errors.add(:base, eligibility_error_message(:no_applicable_products),
|
45
|
+
error_code: :no_applicable_products)
|
41
46
|
end
|
42
|
-
when
|
43
|
-
unless order.
|
44
|
-
eligibility_errors.add(:base, eligibility_error_message(:has_excluded_product),
|
47
|
+
when "none"
|
48
|
+
unless order_products(order).none? { |product| eligible_products.include?(product) }
|
49
|
+
eligibility_errors.add(:base, eligibility_error_message(:has_excluded_product),
|
50
|
+
error_code: :has_excluded_product)
|
45
51
|
end
|
46
52
|
else
|
47
53
|
raise "unexpected match policy: #{preferred_match_policy.inspect}"
|
@@ -68,6 +74,12 @@ module Spree
|
|
68
74
|
def product_ids_string=(product_ids)
|
69
75
|
self.product_ids = product_ids.to_s.split(',').map(&:strip)
|
70
76
|
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def order_products(order)
|
81
|
+
order.line_items.map(&:variant).map(&:product)
|
82
|
+
end
|
71
83
|
end
|
72
84
|
end
|
73
85
|
end
|
@@ -8,6 +8,10 @@ module Spree
|
|
8
8
|
dependent: :destroy
|
9
9
|
has_many :taxons, through: :promotion_rule_taxons, class_name: 'Spree::Taxon'
|
10
10
|
|
11
|
+
def preload_relations
|
12
|
+
[:taxons]
|
13
|
+
end
|
14
|
+
|
11
15
|
MATCH_POLICIES = %w(any all none)
|
12
16
|
|
13
17
|
validates_inclusion_of :preferred_match_policy, in: MATCH_POLICIES
|
@@ -3,6 +3,7 @@
|
|
3
3
|
module Spree
|
4
4
|
class Promotion < Spree::Base
|
5
5
|
MATCH_POLICIES = %w(all any)
|
6
|
+
|
6
7
|
UNACTIVATABLE_ORDER_STATES = ["complete", "awaiting_return", "returned"]
|
7
8
|
|
8
9
|
attr_reader :eligibility_errors
|
@@ -67,6 +68,19 @@ module Spree
|
|
67
68
|
).first
|
68
69
|
end
|
69
70
|
|
71
|
+
# All orders that have been discounted using this promotion
|
72
|
+
def discounted_orders
|
73
|
+
Spree::Order.
|
74
|
+
joins(:all_adjustments).
|
75
|
+
where(
|
76
|
+
spree_adjustments: {
|
77
|
+
source_type: "Spree::PromotionAction",
|
78
|
+
source_id: actions.map(&:id),
|
79
|
+
eligible: true
|
80
|
+
}
|
81
|
+
).distinct
|
82
|
+
end
|
83
|
+
|
70
84
|
def as_json(options = {})
|
71
85
|
options[:except] ||= :code
|
72
86
|
super
|
@@ -151,7 +165,7 @@ module Spree
|
|
151
165
|
return [] if rules.none?
|
152
166
|
|
153
167
|
eligible = lambda { |rule| rule.eligible?(promotable, options) }
|
154
|
-
specific_rules = rules.
|
168
|
+
specific_rules = rules.select { |rule| rule.applicable?(promotable) }
|
155
169
|
return [] if specific_rules.none?
|
156
170
|
|
157
171
|
if match_all?
|
@@ -163,6 +177,12 @@ module Spree
|
|
163
177
|
end
|
164
178
|
specific_rules
|
165
179
|
else
|
180
|
+
Spree::Deprecation.warn(
|
181
|
+
<<~WARN
|
182
|
+
Your promotion "#{name}" with ID #{id} has a match_policy of 'any'.
|
183
|
+
This is deprecated, please split the promotion into separate promotions for each rule.
|
184
|
+
WARN
|
185
|
+
)
|
166
186
|
unless specific_rules.any?(&eligible)
|
167
187
|
@eligibility_errors = specific_rules.map(&:eligibility_errors).detect(&:present?)
|
168
188
|
return nil
|
@@ -190,11 +210,11 @@ module Spree
|
|
190
210
|
# @param excluded_orders [Array<Spree::Order>] Orders to exclude from usage count
|
191
211
|
# @return [Integer] usage count
|
192
212
|
def usage_count(excluded_orders: [])
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
where(
|
197
|
-
count
|
213
|
+
discounted_orders.
|
214
|
+
complete.
|
215
|
+
where.not(id: [excluded_orders.map(&:id)]).
|
216
|
+
where.not(spree_orders: { state: :canceled }).
|
217
|
+
count
|
198
218
|
end
|
199
219
|
|
200
220
|
def line_item_actionable?(order, line_item, promotion_code: nil)
|
@@ -215,21 +235,12 @@ module Spree
|
|
215
235
|
end
|
216
236
|
|
217
237
|
def used_by?(user, excluded_orders = [])
|
218
|
-
|
219
|
-
|
220
|
-
:
|
221
|
-
:
|
222
|
-
|
223
|
-
|
224
|
-
spree_adjustments: {
|
225
|
-
source_type: "Spree::PromotionAction",
|
226
|
-
source_id: actions.map(&:id),
|
227
|
-
eligible: true
|
228
|
-
}
|
229
|
-
).where.not(
|
230
|
-
id: excluded_orders.map(&:id)
|
231
|
-
).any?
|
232
|
-
end
|
238
|
+
discounted_orders.
|
239
|
+
complete.
|
240
|
+
where.not(id: excluded_orders.map(&:id)).
|
241
|
+
where(user: user).
|
242
|
+
where.not(spree_orders: { state: :canceled }).
|
243
|
+
exists?
|
233
244
|
end
|
234
245
|
|
235
246
|
# Removes a promotion and any adjustments or other side effects from an
|
@@ -251,9 +262,9 @@ module Spree
|
|
251
262
|
def blacklisted?(promotable)
|
252
263
|
case promotable
|
253
264
|
when Spree::LineItem
|
254
|
-
!promotable.product.promotionable?
|
265
|
+
!promotable.variant.product.promotionable?
|
255
266
|
when Spree::Order
|
256
|
-
promotable.line_items.
|
267
|
+
promotable.line_items.any? { |line_item| !line_item.variant.product.promotionable? }
|
257
268
|
end
|
258
269
|
end
|
259
270
|
|
@@ -16,6 +16,10 @@ module Spree
|
|
16
16
|
scope :of_type, ->(type) { where(type: Array.wrap(type).map(&:to_s)) }
|
17
17
|
scope :shipping, -> { of_type(Spree::Config.environment.promotions.shipping_actions.to_a) }
|
18
18
|
|
19
|
+
def preload_relations
|
20
|
+
[]
|
21
|
+
end
|
22
|
+
|
19
23
|
# Updates the state of the order or performs some other action depending on
|
20
24
|
# the subclass options will contain the payload from the event that
|
21
25
|
# activated the promotion. This will include the key :user which allows
|
@@ -5,12 +5,12 @@ class Spree::PromotionCode < Spree::Base
|
|
5
5
|
belongs_to :promotion_code_batch, class_name: "Spree::PromotionCodeBatch", optional: true
|
6
6
|
has_many :adjustments
|
7
7
|
|
8
|
+
before_validation :normalize_code
|
9
|
+
|
8
10
|
validates :value, presence: true, uniqueness: { allow_blank: true, case_sensitive: true }
|
9
11
|
validates :promotion, presence: true
|
10
12
|
validate :promotion_not_apply_automatically, on: :create
|
11
13
|
|
12
|
-
before_save :normalize_code
|
13
|
-
|
14
14
|
self.whitelisted_ransackable_attributes = ['value']
|
15
15
|
|
16
16
|
# Whether the promotion code has exceeded its usage restrictions
|
@@ -28,10 +28,14 @@ class Spree::PromotionCode < Spree::Base
|
|
28
28
|
# @param excluded_orders [Array<Spree::Order>] Orders to exclude from usage count
|
29
29
|
# @return [Integer] usage count
|
30
30
|
def usage_count(excluded_orders: [])
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
31
|
+
promotion.
|
32
|
+
discounted_orders.
|
33
|
+
complete.
|
34
|
+
where.not(spree_orders: { state: :canceled }).
|
35
|
+
joins(:order_promotions).
|
36
|
+
where(spree_orders_promotions: { promotion_code_id: self.id }).
|
37
|
+
where.not(id: excluded_orders.map(&:id)).
|
38
|
+
count
|
35
39
|
end
|
36
40
|
|
37
41
|
def usage_limit
|
@@ -33,23 +33,43 @@ module Spree
|
|
33
33
|
private
|
34
34
|
|
35
35
|
def promotions
|
36
|
-
connected_order_promotions | sale_promotions
|
36
|
+
promos = connected_order_promotions | sale_promotions
|
37
|
+
promos.flat_map(&:promotion_actions).group_by(&:preload_relations).each do |preload_relations, actions|
|
38
|
+
preload(records: actions, associations: preload_relations)
|
39
|
+
end
|
40
|
+
promos.flat_map(&:promotion_rules).group_by(&:preload_relations).each do |preload_relations, rules|
|
41
|
+
preload(records: rules, associations: preload_relations)
|
42
|
+
end
|
43
|
+
promos
|
44
|
+
end
|
45
|
+
|
46
|
+
def preload(records:, associations:)
|
47
|
+
if Rails::VERSION::MAJOR >= 7
|
48
|
+
ActiveRecord::Associations::Preloader.new(records: records, associations: associations).call
|
49
|
+
else
|
50
|
+
ActiveRecord::Associations::Preloader.new.preload(records, associations)
|
51
|
+
end
|
37
52
|
end
|
38
53
|
|
39
54
|
def connected_order_promotions
|
40
|
-
|
41
|
-
joins(:order_promotions).
|
42
|
-
where(spree_orders_promotions: { order_id: order.id }).readonly(false).to_a
|
55
|
+
order.promotions.active.includes(promotion_includes)
|
43
56
|
end
|
44
57
|
|
45
58
|
def sale_promotions
|
46
|
-
Spree::Promotion.where(apply_automatically: true).active.includes(
|
59
|
+
Spree::Promotion.where(apply_automatically: true).active.includes(promotion_includes)
|
47
60
|
end
|
48
61
|
|
49
62
|
def promotion_code(promotion)
|
50
|
-
order_promotion =
|
63
|
+
order_promotion = order.order_promotions.detect { |op| op.promotion_id == promotion.id }
|
51
64
|
order_promotion.present? ? order_promotion.promotion_code : nil
|
52
65
|
end
|
66
|
+
|
67
|
+
def promotion_includes
|
68
|
+
[
|
69
|
+
:promotion_rules,
|
70
|
+
:promotion_actions,
|
71
|
+
]
|
72
|
+
end
|
53
73
|
end
|
54
74
|
end
|
55
75
|
end
|
@@ -14,9 +14,14 @@ module Spree
|
|
14
14
|
validates :promotion, presence: true
|
15
15
|
validate :unique_per_promotion, on: :create
|
16
16
|
|
17
|
+
def preload_relations
|
18
|
+
[]
|
19
|
+
end
|
20
|
+
|
17
21
|
def self.for(promotable)
|
18
22
|
all.select { |rule| rule.applicable?(promotable) }
|
19
23
|
end
|
24
|
+
deprecate :for, "Please select promotion rules by their applicable status on the promotable instead."
|
20
25
|
|
21
26
|
def applicable?(_promotable)
|
22
27
|
raise NotImplementedError, "applicable? should be implemented in a sub-class of Spree::PromotionRule"
|
data/app/models/spree/refund.rb
CHANGED
@@ -31,6 +31,14 @@ module Spree
|
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
+
# Sets this price's amount to a new value, parsing it if the new value is
|
35
|
+
# a string.
|
36
|
+
#
|
37
|
+
# @param price [String, #to_d] a new amount
|
38
|
+
def amount=(price)
|
39
|
+
self[:amount] = Spree::LocalizedNumber.parse(price)
|
40
|
+
end
|
41
|
+
|
34
42
|
def description
|
35
43
|
payment.payment_method.name
|
36
44
|
end
|
@@ -92,10 +92,10 @@ module Spree
|
|
92
92
|
|
93
93
|
if unpaid_amount_within_tolerance?
|
94
94
|
reimbursed!
|
95
|
-
Spree::
|
95
|
+
Spree::Bus.publish :reimbursement_reimbursed, reimbursement: self
|
96
96
|
else
|
97
97
|
errored!
|
98
|
-
Spree::
|
98
|
+
Spree::Bus.publish :reimbursement_errored, reimbursement: self
|
99
99
|
end
|
100
100
|
|
101
101
|
if errored?
|
@@ -30,7 +30,7 @@ module Spree
|
|
30
30
|
self.refund_amount_calculator = Calculator::Returns::DefaultRefundAmount
|
31
31
|
|
32
32
|
belongs_to :return_authorization, inverse_of: :return_items, optional: true
|
33
|
-
belongs_to :inventory_unit, inverse_of: :return_items
|
33
|
+
belongs_to :inventory_unit, inverse_of: :return_items
|
34
34
|
belongs_to :exchange_variant, class_name: 'Spree::Variant', optional: true
|
35
35
|
belongs_to :exchange_inventory_unit, class_name: 'Spree::InventoryUnit', inverse_of: :original_return_item, optional: true
|
36
36
|
belongs_to :customer_return, inverse_of: :return_items, optional: true
|
@@ -42,7 +42,6 @@ module Spree
|
|
42
42
|
validate :eligible_exchange_variant
|
43
43
|
validate :belongs_to_same_customer_order
|
44
44
|
validate :validate_acceptance_status_for_reimbursement
|
45
|
-
validates :inventory_unit, presence: true
|
46
45
|
validate :validate_no_other_completed_return_items
|
47
46
|
|
48
47
|
after_create :cancel_others, unless: :cancelled?
|
@@ -7,11 +7,11 @@ module Spree
|
|
7
7
|
def allocate_inventory(desired)
|
8
8
|
# Allocate any available on hand inventory
|
9
9
|
on_hand = allocate_on_hand(desired)
|
10
|
-
desired -= on_hand.values.
|
10
|
+
desired -= on_hand.values.reduce(&:+) if on_hand.present?
|
11
11
|
|
12
12
|
# Allocate remaining desired inventory from backorders
|
13
13
|
backordered = allocate_backordered(desired)
|
14
|
-
desired -= backordered.values.
|
14
|
+
desired -= backordered.values.reduce(&:+) if backordered.present?
|
15
15
|
|
16
16
|
# If all works at this point desired must be empty
|
17
17
|
[on_hand, backordered, desired]
|
@@ -6,14 +6,18 @@ module Spree
|
|
6
6
|
attr_reader :stock_items
|
7
7
|
|
8
8
|
# @param [Variant] variant The variant to check inventory for.
|
9
|
-
# @param [StockLocation, Integer]
|
10
|
-
|
9
|
+
# @param [StockLocation, Integer] stock_location_or_id
|
10
|
+
# The stock_location or stock location ID to check inventory in.
|
11
|
+
# If unspecified it will check inventory in all available StockLocations
|
12
|
+
def initialize(variant, stock_location_or_id = nil)
|
11
13
|
@variant = variant
|
12
|
-
@stock_items =
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
@stock_items = variant.stock_items.select do |stock_item|
|
15
|
+
if stock_location_or_id
|
16
|
+
stock_item.stock_location == stock_location_or_id ||
|
17
|
+
stock_item.stock_location_id == stock_location_or_id
|
18
|
+
else
|
19
|
+
stock_item.stock_location.active?
|
20
|
+
end
|
17
21
|
end
|
18
22
|
end
|
19
23
|
|
@@ -23,7 +27,7 @@ module Spree
|
|
23
27
|
# inventory is not tracked on the variant.
|
24
28
|
def total_on_hand
|
25
29
|
if @variant.should_track_inventory?
|
26
|
-
stock_items.sum(
|
30
|
+
stock_items.sum(&:count_on_hand)
|
27
31
|
else
|
28
32
|
Float::INFINITY
|
29
33
|
end
|
@@ -24,7 +24,8 @@ module Spree
|
|
24
24
|
|
25
25
|
def initialize(order, inventory_units = nil)
|
26
26
|
@order = order
|
27
|
-
@inventory_units =
|
27
|
+
@inventory_units =
|
28
|
+
inventory_units || Spree::Config.stock.inventory_unit_builder_class.new(order).units
|
28
29
|
@splitters = Spree::Config.environment.stock_splitters
|
29
30
|
|
30
31
|
filtered_stock_locations = Spree::Config.stock.location_filter_class.new(Spree::StockLocation.all, @order).filter
|
@@ -40,6 +40,14 @@ class Spree::StoreCredit < Spree::PaymentSource
|
|
40
40
|
extend Spree::DisplayMoney
|
41
41
|
money_methods :amount, :amount_used, :amount_authorized
|
42
42
|
|
43
|
+
# Sets this store credit's amount to a new value,
|
44
|
+
# parsing it as a localized number if the new value is a string.
|
45
|
+
#
|
46
|
+
# @param number [String, #to_d] a new amount
|
47
|
+
def amount=(number)
|
48
|
+
self[:amount] = Spree::LocalizedNumber.parse(number)
|
49
|
+
end
|
50
|
+
|
43
51
|
def amount_remaining
|
44
52
|
return 0.0.to_d if invalidated?
|
45
53
|
amount - amount_used - amount_authorized
|
@@ -5,9 +5,10 @@ module Spree
|
|
5
5
|
# Simple object used to hold tax data for an item.
|
6
6
|
#
|
7
7
|
# This generic object will hold the amount of tax that should be applied to
|
8
|
-
# an item. (Either a {Spree::LineItem} or a {Spree::Shipment}.)
|
8
|
+
# an item. (Either a {Spree::Order}, a {Spree::LineItem} or a {Spree::Shipment}.)
|
9
9
|
#
|
10
|
-
# @attr_reader [Integer] item_id the {Spree::LineItem} or {Spree::Shipment} ID
|
10
|
+
# @attr_reader [Integer] item_id the {Spree::LineItem} or {Spree::Shipment} ID.
|
11
|
+
# Or blank if an order-level tax.
|
11
12
|
# @attr_reader [String] label information about the taxes
|
12
13
|
# @attr_reader [Spree::TaxRate] tax_rate will be used as the source for tax
|
13
14
|
# adjustments
|
@@ -8,13 +8,15 @@ module Spree
|
|
8
8
|
# adjustments on an order.
|
9
9
|
#
|
10
10
|
# @attr_reader [Integer] order_id the {Spree::Order} these taxes apply to
|
11
|
+
# @attr_reader [Array<Spree::Tax::ItemTax>] order_taxes an array of tax
|
12
|
+
# data for the order
|
11
13
|
# @attr_reader [Array<Spree::Tax::ItemTax>] line_item_taxes an array of
|
12
14
|
# tax data for order's line items
|
13
15
|
# @attr_reader [Array<Spree::Tax::ItemTax>] shipment_taxes an array of
|
14
16
|
# tax data for the order's shipments
|
15
17
|
class OrderTax
|
16
18
|
include ActiveModel::Model
|
17
|
-
attr_accessor :order_id, :line_item_taxes, :shipment_taxes
|
19
|
+
attr_accessor :order_id, :order_taxes, :line_item_taxes, :shipment_taxes
|
18
20
|
end
|
19
21
|
end
|
20
22
|
end
|
@@ -8,7 +8,7 @@ module Spree
|
|
8
8
|
# @attr_reader [Integer] country_id the ID of a Spree::Country object
|
9
9
|
# @attr_reader [Integer] state_id the ID of a Spree::State object
|
10
10
|
class TaxLocation
|
11
|
-
attr_reader :
|
11
|
+
attr_reader :country, :state
|
12
12
|
|
13
13
|
# Create a new TaxLocation object
|
14
14
|
#
|
@@ -19,18 +19,15 @@ module Spree
|
|
19
19
|
#
|
20
20
|
# @return [Spree::Tax::TaxLocation] a Spree::Tax::TaxLocation object
|
21
21
|
def initialize(country: nil, state: nil)
|
22
|
-
@
|
23
|
-
@state_id = state && state.id
|
22
|
+
@country, @state = country, state
|
24
23
|
end
|
24
|
+
delegate :id, to: :state, prefix: true, allow_nil: true
|
25
|
+
delegate :id, to: :country, prefix: true, allow_nil: true
|
25
26
|
|
26
27
|
def ==(other)
|
27
28
|
state_id == other.state_id && country_id == other.country_id
|
28
29
|
end
|
29
30
|
|
30
|
-
def country
|
31
|
-
Spree::Country.find_by(id: country_id)
|
32
|
-
end
|
33
|
-
|
34
31
|
def empty?
|
35
32
|
country_id.nil? && state_id.nil?
|
36
33
|
end
|
@@ -27,6 +27,8 @@ module Spree
|
|
27
27
|
|
28
28
|
validates :amount, presence: true, numericality: true
|
29
29
|
|
30
|
+
self.whitelisted_ransackable_associations = %w[tax_categories zone]
|
31
|
+
|
30
32
|
# Finds all tax rates whose zones match a given address
|
31
33
|
scope :for_address, ->(address) { joins(:zone).merge(Spree::Zone.for_address(address)) }
|
32
34
|
scope :for_country,
|
data/app/models/spree/variant.rb
CHANGED
@@ -66,7 +66,7 @@ module Spree
|
|
66
66
|
|
67
67
|
validates :cost_price, numericality: { greater_than_or_equal_to: 0, allow_nil: true }
|
68
68
|
validates :price, numericality: { greater_than_or_equal_to: 0, allow_nil: true }
|
69
|
-
validates_uniqueness_of :sku, allow_blank: true, case_sensitive: true, if: :enforce_unique_sku?
|
69
|
+
validates_uniqueness_of :sku, allow_blank: true, case_sensitive: true, conditions: -> { where(deleted_at: nil) }, if: :enforce_unique_sku?
|
70
70
|
|
71
71
|
after_create :create_stock_items
|
72
72
|
after_create :set_position
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Spree
|
4
|
+
# Mailing after events on a {Spree::Order}
|
5
|
+
class OrderMailerSubscriber
|
6
|
+
include Omnes::Subscriber
|
7
|
+
|
8
|
+
handle :order_finalized,
|
9
|
+
with: :send_confirmation_email,
|
10
|
+
id: :spree_order_mailer_send_confirmation_email
|
11
|
+
|
12
|
+
handle :reimbursement_reimbursed,
|
13
|
+
with: :send_reimbursement_email,
|
14
|
+
id: :spree_order_mailer_send_reimbursement_email
|
15
|
+
|
16
|
+
# Sends confirmation email to the user
|
17
|
+
#
|
18
|
+
# @param event [Omnes::UnstructuredEvent]
|
19
|
+
def send_confirmation_email(event)
|
20
|
+
order = event[:order]
|
21
|
+
unless order.confirmation_delivered?
|
22
|
+
Spree::Config.order_mailer_class.confirm_email(order).deliver_later
|
23
|
+
order.update_column(:confirmation_delivered, true)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Sends reimbursement email to the user
|
28
|
+
#
|
29
|
+
# @param event [Omnes::UnstructuredEvent]
|
30
|
+
def send_reimbursement_email(event)
|
31
|
+
reimbursement = event[:reimbursement]
|
32
|
+
Spree::Config.reimbursement_mailer_class.reimbursement_email(reimbursement.id).deliver_later
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/config/locales/en.yml
CHANGED
@@ -365,7 +365,7 @@ en:
|
|
365
365
|
amount_authorized: Amount Authorized
|
366
366
|
amount_credited: Amount Credited
|
367
367
|
amount_used: Amount Used
|
368
|
-
category_id:
|
368
|
+
category_id: Category
|
369
369
|
created_at: Issued On
|
370
370
|
created_by_id: Created By
|
371
371
|
invalidated_at: Invalidated
|
@@ -866,6 +866,10 @@ en:
|
|
866
866
|
new_price: New Price
|
867
867
|
new:
|
868
868
|
new_price: New Price
|
869
|
+
master_variant_table:
|
870
|
+
master_variant: Master Variant Prices
|
871
|
+
table:
|
872
|
+
variant_pricing: Variant Prices
|
869
873
|
promotions:
|
870
874
|
actions:
|
871
875
|
calculator_label: Calculated by
|
@@ -998,12 +1002,16 @@ en:
|
|
998
1002
|
edit_variant: Edit Variant
|
999
1003
|
form:
|
1000
1004
|
dimensions: Dimensions
|
1005
|
+
options: Options
|
1001
1006
|
pricing: Pricing
|
1002
1007
|
pricing_hint: These values are populated from the product details page and
|
1003
1008
|
can be overridden below
|
1009
|
+
properties: Properties
|
1004
1010
|
use_product_tax_category: Use Product Tax Category
|
1005
1011
|
new:
|
1006
1012
|
new_variant: New Variant
|
1013
|
+
table:
|
1014
|
+
no_variants_found: No variants found for '%{term}'
|
1007
1015
|
table_filter:
|
1008
1016
|
show_deleted: Show Deleted Variants
|
1009
1017
|
administration: Administration
|
@@ -2248,7 +2256,6 @@ en:
|
|
2248
2256
|
value: Value
|
2249
2257
|
variant: Variant
|
2250
2258
|
variant_placeholder: Choose a Variant
|
2251
|
-
variant_pricing: Variant Pricing
|
2252
2259
|
variant_properties: Variant Properties
|
2253
2260
|
variant_search: Variant Search
|
2254
2261
|
variant_search_placeholder: SKU or Option Value
|