solidus_core 3.1.7 → 3.2.0.alpha
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/products_helper.rb +1 -1
- data/app/models/concerns/spree/active_storage_adapter/attachment.rb +23 -10
- 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/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_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 +1 -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 +8 -4
- data/app/models/spree/promotion_handler/cart.rb +26 -6
- data/app/models/spree/promotion_rule.rb +5 -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/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 +56 -49
- 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 +27 -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 +33 -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 -2
- 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/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 +6 -2
- metadata +68 -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
|
@@ -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
|
|
@@ -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"
|
|
@@ -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
|
|
@@ -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
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class SetPromotionsWithAnyPolicyToAllIfPossible < ActiveRecord::Migration[5.2]
|
|
4
|
+
def up
|
|
5
|
+
Spree::Promotion.where(match_policy: :any).includes(:promotion_rules).all.each do |promotion|
|
|
6
|
+
if promotion.promotion_rules.length <= 1
|
|
7
|
+
promotion.update!(match_policy: :all)
|
|
8
|
+
else
|
|
9
|
+
raise StandardError, <<~MSG
|
|
10
|
+
You have promotions with a match policy of any and more than one rule. Please
|
|
11
|
+
run `bundle exec rake solidus:split_promotions_with_any_match_policy`.
|
|
12
|
+
MSG
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def down
|
|
18
|
+
# No-Op
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Solidus
|
|
4
|
+
class InstallGenerator < Rails::Generators::Base
|
|
5
|
+
# Bundler context during the install process.
|
|
6
|
+
#
|
|
7
|
+
# This class gives access to information about the bundler context in which
|
|
8
|
+
# the install generator is run. I.e., which solidus components are present
|
|
9
|
+
# in the user's Gemfile. It also allows modifying the Gemfile to add or
|
|
10
|
+
# remove gems.
|
|
11
|
+
#
|
|
12
|
+
# @api private
|
|
13
|
+
class BundlerContext
|
|
14
|
+
# Write and remove into and from a Gemfile
|
|
15
|
+
#
|
|
16
|
+
# This custom injector fixes support for path, git and custom sources,
|
|
17
|
+
# which is missing in bundler's upstream injector for a dependency fetched
|
|
18
|
+
# with `Bundled.locked_gems.dependencies`.
|
|
19
|
+
#
|
|
20
|
+
# @api private
|
|
21
|
+
class InjectorWithPathSupport < Bundler::Injector
|
|
22
|
+
private def build_gem_lines(conservative_versioning)
|
|
23
|
+
@deps.map do |d|
|
|
24
|
+
name = d.name.dump
|
|
25
|
+
is_local = d.source.instance_of?(Bundler::Source::Path)
|
|
26
|
+
is_git = d.source.instance_of?(Bundler::Source::Git)
|
|
27
|
+
|
|
28
|
+
requirement = if is_local
|
|
29
|
+
", path: \"#{d.source.path}\""
|
|
30
|
+
elsif is_git
|
|
31
|
+
", git: \"#{d.git}\"".yield_self { |g| d.ref ? g + ", ref: \"#{d.ref}\"" : g }
|
|
32
|
+
elsif conservative_versioning
|
|
33
|
+
", \"#{conservative_version(@definition.specs[d.name][0])}\""
|
|
34
|
+
else
|
|
35
|
+
", #{d.requirement.as_list.map(&:dump).join(", ")}"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
source = ", :source => \"#{d.source.remotes.join(",")}\"" unless is_local || is_git || d.source.nil?
|
|
39
|
+
|
|
40
|
+
%(gem #{name}#{requirement}#{source})
|
|
41
|
+
end.join("\n")
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
attr_reader :dependencies, :injector
|
|
46
|
+
|
|
47
|
+
def self.bundle_cleanly(&block)
|
|
48
|
+
Bundler.respond_to?(:with_unbundled_env) ? Bundler.with_unbundled_env(&block) : Bundler.with_clean_env(&block)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def initialize
|
|
52
|
+
@dependencies = Bundler.locked_gems.dependencies
|
|
53
|
+
@injector = InjectorWithPathSupport
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def solidus_in_gemfile?
|
|
57
|
+
!solidus_dependency.nil?
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def component_in_gemfile?(name)
|
|
61
|
+
!@dependencies["solidus_#{name}"].nil?
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def break_down_components(components)
|
|
65
|
+
raise <<~MSG unless solidus_in_gemfile?
|
|
66
|
+
solidus meta gem needs to be present in the Gemfile to build the component dependency
|
|
67
|
+
MSG
|
|
68
|
+
|
|
69
|
+
@injector.inject(
|
|
70
|
+
components.map { |component| dependency_for_component(component) }
|
|
71
|
+
)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def remove(*args, **kwargs, &block)
|
|
75
|
+
@injector.remove(*args, **kwargs, &block)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
private
|
|
79
|
+
|
|
80
|
+
def dependency_for_component(component)
|
|
81
|
+
Bundler::Dependency.new(
|
|
82
|
+
"solidus_#{component}",
|
|
83
|
+
solidus_dependency.requirement,
|
|
84
|
+
{
|
|
85
|
+
"source" => solidus_dependency.source,
|
|
86
|
+
"git" => solidus_dependency.source.try(:uri),
|
|
87
|
+
"ref" => solidus_dependency.source.try(:ref)
|
|
88
|
+
}
|
|
89
|
+
)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def solidus_dependency
|
|
93
|
+
@dependencies['solidus']
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Solidus
|
|
4
|
+
class InstallGenerator < Rails::Generators::Base
|
|
5
|
+
class InstallFrontend
|
|
6
|
+
attr_reader :bundler_context,
|
|
7
|
+
:generator_context
|
|
8
|
+
|
|
9
|
+
def initialize(bundler_context:, generator_context:)
|
|
10
|
+
@bundler_context = bundler_context
|
|
11
|
+
@generator_context = generator_context
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def call(frontend, installer_adds_auth:)
|
|
15
|
+
case frontend
|
|
16
|
+
when 'solidus_frontend'
|
|
17
|
+
install_solidus_frontend
|
|
18
|
+
when 'solidus_starter_frontend'
|
|
19
|
+
install_solidus_starter_frontend(installer_adds_auth)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def install_solidus_frontend
|
|
26
|
+
unless @bundler_context.component_in_gemfile?(:frontend)
|
|
27
|
+
BundlerContext.bundle_cleanly do
|
|
28
|
+
`bundle add solidus_frontend`
|
|
29
|
+
`bundle install`
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
@generator_context.generate('solidus_frontend:install')
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def install_solidus_starter_frontend(installer_adds_auth)
|
|
37
|
+
@bundler_context.remove(['solidus_frontend']) if @bundler_context.component_in_gemfile?(:frontend)
|
|
38
|
+
|
|
39
|
+
# TODO: Move installation of solidus_auth_devise to the
|
|
40
|
+
# solidus_starter_frontend template
|
|
41
|
+
BundlerContext.bundle_cleanly { `bundle add solidus_auth_devise` } unless auth_present?(installer_adds_auth)
|
|
42
|
+
`LOCATION="https://raw.githubusercontent.com/solidusio/solidus_starter_frontend/main/template.rb" bin/rails app:template`
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def auth_present?(installer_adds_auth)
|
|
46
|
+
installer_adds_auth || @bundler_context.component_in_gemfile?(:auth_devise)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|