spree_core 5.6.0.rc2 → 5.6.0.rc3
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/rich_text_helper.rb +37 -0
- data/app/jobs/spree/imports/process_group_job.rb +46 -0
- data/app/models/concerns/spree/publishable.rb +29 -1
- data/app/models/spree/address.rb +7 -0
- data/app/models/spree/calculator/default_tax.rb +31 -2
- data/app/models/spree/line_item.rb +23 -0
- data/app/models/spree/order.rb +10 -0
- data/app/models/spree/shipment.rb +9 -0
- data/app/models/spree/tax_rate.rb +7 -0
- data/app/services/spree/addresses/update.rb +32 -25
- data/lib/generators/spree/dummy/templates/rails/database.yml +1 -0
- data/lib/spree/core/version.rb +1 -1
- data/lib/spree/events.rb +31 -7
- data/lib/spree/testing_support/lifecycle_events.rb +12 -0
- metadata +5 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 45a9c3a76133036f89b3eabe73c9e53cd57dfd6417ee32211f88335424b9da94
|
|
4
|
+
data.tar.gz: f409d6453ba2a5a6fa9f66c3b26ffa8686c05723bcae99110cb7eede6d3fedb1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 596464be183b137236eb76e984a2b8d84091ea95e22a553d7ffb3ab0a5bc3055d380d1283946517ad20e3a8bbd3617d9a246ebbe2157923943e64b054a014aaa
|
|
7
|
+
data.tar.gz: 58e3bb7aed929b0d17ac96510dee26475cdcfc645b49e491fa38625c958d5228ef81e5f9d703488dbe576167e2ef8c2078d993cce6a37feaad714f62c0b6b8c6
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
# Converts stored rich text HTML (Tiptap output persisted in plain text
|
|
3
|
+
# columns, e.g. +Spree::Product#description+) into a faithful plain-text
|
|
4
|
+
# rendering for API +description+/+body+ fields.
|
|
5
|
+
#
|
|
6
|
+
# Tiptap serializes blocks with no whitespace between them
|
|
7
|
+
# (+<p>a</p><p>b</p>+), so a naive tag strip glues adjacent blocks into a
|
|
8
|
+
# single run ("ab") and loses every paragraph and line break. This maps
|
|
9
|
+
# block boundaries and +<br>+ to newlines before stripping tags, keeping the
|
|
10
|
+
# text readable for storefronts, feeds, search indexing, and meta tags.
|
|
11
|
+
#
|
|
12
|
+
# Newlines already present in the markup (pretty-printed source from the
|
|
13
|
+
# legacy admin's TinyMCE code view, imports, seeds) render as a single space
|
|
14
|
+
# in a browser and are treated the same way here — only +<br>+ and block
|
|
15
|
+
# boundaries produce line breaks.
|
|
16
|
+
module RichTextHelper
|
|
17
|
+
# Closing block tags whose boundaries become line breaks.
|
|
18
|
+
BLOCK_BOUNDARY = %r{</(?:p|div|li|h[1-6]|blockquote|tr|ul|ol|table|section|article|header|footer|pre)>}i
|
|
19
|
+
|
|
20
|
+
# Hard line breaks.
|
|
21
|
+
LINE_BREAK = %r{<br\s*/?>}i
|
|
22
|
+
|
|
23
|
+
# @param html [String, nil] rich text HTML
|
|
24
|
+
# @return [String] plain text with paragraph and line breaks preserved
|
|
25
|
+
def self.to_plain_text(html)
|
|
26
|
+
return '' if html.blank?
|
|
27
|
+
|
|
28
|
+
with_breaks = html.gsub(/\s+/, ' ').gsub(LINE_BREAK, "\n").gsub(BLOCK_BOUNDARY, "\\0\n")
|
|
29
|
+
|
|
30
|
+
Nokogiri::HTML.fragment(with_breaks).text
|
|
31
|
+
.gsub(/[^\S\n]+/, ' ') # collapse runs of non-newline whitespace
|
|
32
|
+
.gsub(/ *\n */, "\n") # trim spaces hugging newlines
|
|
33
|
+
.gsub(/\n{3,}/, "\n\n") # cap consecutive blank lines
|
|
34
|
+
.strip
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
module Spree
|
|
2
2
|
module Imports
|
|
3
3
|
class ProcessGroupJob < Spree::Imports::BaseJob
|
|
4
|
+
# A large import fans out into many group jobs; without a cap they
|
|
5
|
+
# occupy every worker thread as one frees up, starving checkout-critical
|
|
6
|
+
# queues that share the pool. Cap concurrent groups per import at 75%
|
|
7
|
+
# of JOB_THREADS (at least 1), or SPREE_IMPORT_JOB_CONCURRENCY when set
|
|
8
|
+
# (0 disables the cap) — excess jobs wait as blocked executions, not in
|
|
9
|
+
# worker threads. Both values are read by the process enqueueing the
|
|
10
|
+
# import, so on a split web/worker deployment set the explicit override
|
|
11
|
+
# on the web service. The semaphore duration must outlive the slowest
|
|
12
|
+
# group; expiry just lifts the cap, it doesn't lose jobs. Solid
|
|
13
|
+
# Queue-specific (no-op on other adapters).
|
|
14
|
+
import_concurrency = ENV['SPREE_IMPORT_JOB_CONCURRENCY'].presence&.to_i ||
|
|
15
|
+
[Integer(ENV.fetch('JOB_THREADS', 3)) * 3 / 4, 1].max
|
|
16
|
+
if import_concurrency.positive? && respond_to?(:limits_concurrency)
|
|
17
|
+
limits_concurrency to: import_concurrency, key: ->(import_id, _row_ids) { import_id }, duration: 30.minutes
|
|
18
|
+
end
|
|
19
|
+
|
|
4
20
|
# Rows are loaded in slices so a large group never holds every ImportRow
|
|
5
21
|
# (and its raw CSV data) in memory for the whole job.
|
|
6
22
|
ROWS_BATCH_SIZE = 100
|
|
@@ -12,6 +28,9 @@ module Spree
|
|
|
12
28
|
mappings = import.mappings.mapped.to_a
|
|
13
29
|
schema_fields = import.schema_fields
|
|
14
30
|
large = import.large_import?
|
|
31
|
+
grouped = import.group_column.present? && mappings.any? { |m| m.schema_field == import.group_column }
|
|
32
|
+
started_at = Time.current
|
|
33
|
+
processed_rows = []
|
|
15
34
|
|
|
16
35
|
row_ids.each_slice(ROWS_BATCH_SIZE) do |ids|
|
|
17
36
|
# Skip rows already completed on a prior attempt so retries don't double-process them.
|
|
@@ -25,18 +44,45 @@ module Spree
|
|
|
25
44
|
Spree::Events.disable do
|
|
26
45
|
rows.each { |row| row.bulk_process!(mappings: mappings, schema_fields: schema_fields) }
|
|
27
46
|
end
|
|
47
|
+
elsif grouped
|
|
48
|
+
# A group is one product plus its variants: per-record lifecycle
|
|
49
|
+
# events (variant.created, price.created, product.updated per
|
|
50
|
+
# touch) are noise to subscribers — one product event is published
|
|
51
|
+
# for the whole group below. import_row.* events still flow.
|
|
52
|
+
Spree::Events.disable_lifecycle do
|
|
53
|
+
rows.each { |row| row.process!(mappings: mappings, schema_fields: schema_fields) }
|
|
54
|
+
end
|
|
28
55
|
else
|
|
29
56
|
rows.each do |row|
|
|
30
57
|
row.process!(mappings: mappings, schema_fields: schema_fields)
|
|
31
58
|
end
|
|
32
59
|
end
|
|
60
|
+
processed_rows.concat(rows)
|
|
33
61
|
end
|
|
34
62
|
|
|
63
|
+
publish_group_events(processed_rows, started_at) if grouped
|
|
35
64
|
check_import_completion(import, large)
|
|
36
65
|
end
|
|
37
66
|
|
|
38
67
|
private
|
|
39
68
|
|
|
69
|
+
# One event per product touched by this group: `product.created` when the
|
|
70
|
+
# product came into existence during this run, `product.updated` otherwise
|
|
71
|
+
# (including retries of a group whose first attempt created it).
|
|
72
|
+
def publish_group_events(rows, started_at)
|
|
73
|
+
products = rows.select { |row| row.status == 'completed' }.filter_map do |row|
|
|
74
|
+
item = row.item
|
|
75
|
+
next item if item.is_a?(Spree::Product)
|
|
76
|
+
|
|
77
|
+
item.product if item.respond_to?(:product)
|
|
78
|
+
end.uniq
|
|
79
|
+
|
|
80
|
+
products.each do |product|
|
|
81
|
+
event = product.created_at >= started_at ? 'product.created' : 'product.updated'
|
|
82
|
+
product.publish_event(event)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
40
86
|
# Completion is row-state-derived so retry-induced over-increments of the counter
|
|
41
87
|
# stay harmless. The counter pre-check just shortcuts the row scan for workers
|
|
42
88
|
# that obviously can't be the last group to finish. `in_flight` excludes orphaned
|
|
@@ -39,6 +39,18 @@ module Spree
|
|
|
39
39
|
included do
|
|
40
40
|
class_attribute :publish_events, default: true
|
|
41
41
|
class_attribute :lifecycle_events_enabled, default: false
|
|
42
|
+
|
|
43
|
+
after_update :mark_real_update_for_events
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# A bare `touch` (variant → product, price → variant, asset → variants)
|
|
47
|
+
# writes no attribute changes — the resulting update event would carry
|
|
48
|
+
# nothing subscribers haven't already been told. Track it here so the
|
|
49
|
+
# commit callback can tell a touch-only commit from a real update;
|
|
50
|
+
# `saved_changes` can't (a touch replays the previous save's changeset).
|
|
51
|
+
def touch(*names, time: nil)
|
|
52
|
+
@_spree_touched_for_events = true
|
|
53
|
+
super
|
|
42
54
|
end
|
|
43
55
|
|
|
44
56
|
class_methods do
|
|
@@ -228,7 +240,21 @@ module Spree
|
|
|
228
240
|
end
|
|
229
241
|
|
|
230
242
|
def should_publish_events?
|
|
231
|
-
self.class.publish_events && Spree::Events.
|
|
243
|
+
self.class.publish_events && Spree::Events.lifecycle_enabled?
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# True when this commit was caused only by `touch` — no real save ran
|
|
247
|
+
# (`after_update` fires for saves, never for touches). Flags reset every
|
|
248
|
+
# commit so a later genuine update publishes normally.
|
|
249
|
+
def touch_only_update?
|
|
250
|
+
touched = @_spree_touched_for_events
|
|
251
|
+
updated = @_spree_updated_for_events
|
|
252
|
+
@_spree_touched_for_events = @_spree_updated_for_events = nil
|
|
253
|
+
touched && !updated
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def mark_real_update_for_events
|
|
257
|
+
@_spree_updated_for_events = true
|
|
232
258
|
end
|
|
233
259
|
|
|
234
260
|
def publish_create_event
|
|
@@ -236,6 +262,8 @@ module Spree
|
|
|
236
262
|
end
|
|
237
263
|
|
|
238
264
|
def publish_update_event
|
|
265
|
+
return if touch_only_update?
|
|
266
|
+
|
|
239
267
|
publish_event("#{event_prefix}.updated")
|
|
240
268
|
end
|
|
241
269
|
|
data/app/models/spree/address.rb
CHANGED
|
@@ -233,6 +233,7 @@ module Spree
|
|
|
233
233
|
assign_new_default_address_to_user
|
|
234
234
|
|
|
235
235
|
if can_be_deleted?
|
|
236
|
+
unassign_from_incomplete_orders
|
|
236
237
|
super
|
|
237
238
|
else
|
|
238
239
|
update_column :deleted_at, Time.current
|
|
@@ -364,5 +365,11 @@ module Spree
|
|
|
364
365
|
def assign_new_default_address_to_user_scope
|
|
365
366
|
user.addresses.not_quick_checkout.reorder(created_at: :desc)
|
|
366
367
|
end
|
|
368
|
+
|
|
369
|
+
def unassign_from_incomplete_orders
|
|
370
|
+
orders = Spree::Order.incomplete.where(user_id: user_id)
|
|
371
|
+
orders.where(ship_address_id: id).update_all(ship_address_id: nil, state: 'address', updated_at: Time.current)
|
|
372
|
+
orders.where(bill_address_id: id).update_all(bill_address_id: nil, state: 'address', updated_at: Time.current)
|
|
373
|
+
end
|
|
367
374
|
end
|
|
368
375
|
end
|
|
@@ -24,11 +24,21 @@ module Spree
|
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
# When it comes to computing shipments or line items: same same.
|
|
27
|
+
#
|
|
28
|
+
# Both tax modes are computed from the item's live taxable basis, which
|
|
29
|
+
# follows every discount: item-level promotions and the item's share of
|
|
30
|
+
# whole-order promotions (see LineItem#taxable_basis). Included
|
|
31
|
+
# (VAT-style) tax intentionally does not read the stored pre_tax_amount
|
|
32
|
+
# column — that column is only refreshed by Spree::TaxRate.adjust on
|
|
33
|
+
# checkout transitions, so it goes stale the moment a promotion is
|
|
34
|
+
# applied or removed.
|
|
27
35
|
def compute_shipment_or_line_item(item)
|
|
36
|
+
basis = item.taxable_basis
|
|
37
|
+
|
|
28
38
|
if rate.included_in_price
|
|
29
|
-
deduced_total_by_rate(item
|
|
39
|
+
deduced_total_by_rate(net_basis(item, basis), rate)
|
|
30
40
|
else
|
|
31
|
-
round_to_two_places(
|
|
41
|
+
round_to_two_places(basis * rate.amount)
|
|
32
42
|
end
|
|
33
43
|
end
|
|
34
44
|
|
|
@@ -54,5 +64,24 @@ module Spree
|
|
|
54
64
|
def deduced_total_by_rate(pre_tax_amount, rate)
|
|
55
65
|
round_to_two_places(pre_tax_amount * rate.amount)
|
|
56
66
|
end
|
|
67
|
+
|
|
68
|
+
# Backs all included-in-price rates out of the gross basis, mirroring
|
|
69
|
+
# Spree::TaxRate.store_pre_tax_amount (several rates can share a tax
|
|
70
|
+
# category, e.g. MOSS VAT). Falls back to this rate's amount when no
|
|
71
|
+
# rate matches the item, e.g. in bare setups without matching zones.
|
|
72
|
+
def net_basis(item, basis)
|
|
73
|
+
included = included_rates_amount(item)
|
|
74
|
+
included = rate.amount if included.zero?
|
|
75
|
+
|
|
76
|
+
basis / (1 + included)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Memoized per tax category: tax rate and zone rows are configuration,
|
|
80
|
+
# invariant within a recalculation pass.
|
|
81
|
+
def included_rates_amount(item)
|
|
82
|
+
@included_rates_amount ||= {}
|
|
83
|
+
@included_rates_amount[item.tax_category_id] ||=
|
|
84
|
+
Spree::TaxRate.included_tax_amount_for(tax_zone: rate.zone, tax_category: item.tax_category)
|
|
85
|
+
end
|
|
57
86
|
end
|
|
58
87
|
end
|
|
@@ -146,6 +146,29 @@ module Spree
|
|
|
146
146
|
alias discounted_money display_discounted_amount
|
|
147
147
|
alias discounted_amount taxable_amount
|
|
148
148
|
|
|
149
|
+
# Returns the amount this line item is taxed on: the discounted amount
|
|
150
|
+
# reduced further by the line item's proportional share of any
|
|
151
|
+
# whole-order promotions, which are adjustments on the order itself and
|
|
152
|
+
# therefore not part of +taxable_adjustment_total+. Never negative.
|
|
153
|
+
#
|
|
154
|
+
# @return [BigDecimal]
|
|
155
|
+
def taxable_basis
|
|
156
|
+
basis = taxable_amount
|
|
157
|
+
return basis if basis <= 0
|
|
158
|
+
|
|
159
|
+
order_discount = order.order_level_promo_total
|
|
160
|
+
return basis if order_discount.zero?
|
|
161
|
+
|
|
162
|
+
# summed in SQL so the allocation uses the persisted adjustment totals
|
|
163
|
+
# of all line items, not a possibly stale cached association
|
|
164
|
+
items_total = order.line_items.reorder(nil).pick(
|
|
165
|
+
Arel.sql('COALESCE(SUM(price * quantity + taxable_adjustment_total), 0)')
|
|
166
|
+
)
|
|
167
|
+
return basis if items_total <= 0
|
|
168
|
+
|
|
169
|
+
[basis + (order_discount * basis / items_total), BigDecimal(0)].max
|
|
170
|
+
end
|
|
171
|
+
|
|
149
172
|
# Returns the final amount of the line item
|
|
150
173
|
#
|
|
151
174
|
# @return [BigDecimal]
|
data/app/models/spree/order.rb
CHANGED
|
@@ -360,6 +360,16 @@ module Spree
|
|
|
360
360
|
line_items.sum(:pre_tax_amount)
|
|
361
361
|
end
|
|
362
362
|
|
|
363
|
+
# Sum of the eligible promotion adjustments applied to the order itself
|
|
364
|
+
# (whole-order discounts created by Promotion::Actions::CreateAdjustment),
|
|
365
|
+
# as opposed to promotions applied to individual line items or shipments.
|
|
366
|
+
# Zero or negative.
|
|
367
|
+
#
|
|
368
|
+
# @return [BigDecimal]
|
|
369
|
+
def order_level_promo_total
|
|
370
|
+
adjustments.promotion.eligible.sum(:amount)
|
|
371
|
+
end
|
|
372
|
+
|
|
363
373
|
# Sum of all line item and shipment pre-tax
|
|
364
374
|
def pre_tax_total
|
|
365
375
|
pre_tax_item_amount + shipments.sum(:pre_tax_amount)
|
|
@@ -207,6 +207,15 @@ module Spree
|
|
|
207
207
|
end
|
|
208
208
|
alias discounted_amount discounted_cost
|
|
209
209
|
|
|
210
|
+
# Returns the amount this shipment is taxed on: its discounted cost,
|
|
211
|
+
# never negative (stacked shipping promotions can push discounted_cost
|
|
212
|
+
# below zero). Whole-order promotions are not allocated to shipments.
|
|
213
|
+
#
|
|
214
|
+
# @return [BigDecimal]
|
|
215
|
+
def taxable_basis
|
|
216
|
+
[discounted_cost, BigDecimal(0)].max
|
|
217
|
+
end
|
|
218
|
+
|
|
210
219
|
def final_price
|
|
211
220
|
cost + adjustment_total
|
|
212
221
|
end
|
|
@@ -54,6 +54,13 @@ module Spree
|
|
|
54
54
|
# Pre-tax amounts must be stored so that we can calculate
|
|
55
55
|
# correct rate amounts in the future. For example:
|
|
56
56
|
# https://github.com/spree/spree/issues/4318#issuecomment-34723428
|
|
57
|
+
#
|
|
58
|
+
# NOTE: this deliberately excludes the item's share of whole-order
|
|
59
|
+
# promotions and is therefore NOT the basis tax is computed on (that is
|
|
60
|
+
# LineItem#taxable_basis / Shipment#taxable_basis). Refund and reporting
|
|
61
|
+
# code (e.g. Calculator::Returns::DefaultRefundAmount) weighs order-level
|
|
62
|
+
# adjustments separately on top of this column, so allocating them here
|
|
63
|
+
# would double-count discounts.
|
|
57
64
|
def self.store_pre_tax_amount(item, rates)
|
|
58
65
|
pre_tax_amount = case item.class.to_s
|
|
59
66
|
when 'Spree::LineItem' then item.discounted_amount
|
|
@@ -7,7 +7,16 @@ module Spree
|
|
|
7
7
|
attr_accessor :country
|
|
8
8
|
|
|
9
9
|
def call(address:, address_params:, **opts)
|
|
10
|
-
|
|
10
|
+
ApplicationRecord.transaction do
|
|
11
|
+
perform(address: address, address_params: address_params, **opts)
|
|
12
|
+
end
|
|
13
|
+
rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotDestroyed => e
|
|
14
|
+
failure(e.record)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def perform(address:, address_params:, **opts)
|
|
11
20
|
default_billing = address_params.key?(:is_default_billing) ? address_params.delete(:is_default_billing) : opts.fetch(:default_billing, false)
|
|
12
21
|
default_shipping = address_params.key?(:is_default_shipping) ? address_params.delete(:is_default_shipping) : opts.fetch(:default_shipping, false)
|
|
13
22
|
address_changes_except = opts.fetch(:address_changes_except, [])
|
|
@@ -35,24 +44,23 @@ module Spree
|
|
|
35
44
|
return success(address) unless address_changed
|
|
36
45
|
|
|
37
46
|
if address.editable?
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
order.update(state: 'address') if order.present?
|
|
49
|
-
|
|
50
|
-
success(address)
|
|
51
|
-
else
|
|
52
|
-
failure(address)
|
|
47
|
+
address.update!(address_params)
|
|
48
|
+
|
|
49
|
+
if address.user.present?
|
|
50
|
+
assign_to_user_as_default(
|
|
51
|
+
user: address.user,
|
|
52
|
+
address_id: address.id,
|
|
53
|
+
default_billing: default_billing,
|
|
54
|
+
default_shipping: default_shipping
|
|
55
|
+
)
|
|
53
56
|
end
|
|
57
|
+
|
|
58
|
+
reassign_incomplete_orders(address.id, address)
|
|
59
|
+
|
|
60
|
+
success(address)
|
|
54
61
|
elsif new_address(address_params).valid?
|
|
55
|
-
address.
|
|
62
|
+
old_address_id = address.id
|
|
63
|
+
address.destroy!
|
|
56
64
|
|
|
57
65
|
if new_address.user.present?
|
|
58
66
|
default_billing = address.user_default_billing? || default_billing
|
|
@@ -66,12 +74,7 @@ module Spree
|
|
|
66
74
|
)
|
|
67
75
|
end
|
|
68
76
|
|
|
69
|
-
|
|
70
|
-
order.ship_address = new_address if order.ship_address_id == address.id
|
|
71
|
-
order.bill_address = new_address if order.bill_address_id == address.id
|
|
72
|
-
order.state = 'address'
|
|
73
|
-
order.save
|
|
74
|
-
end
|
|
77
|
+
reassign_incomplete_orders(old_address_id, new_address)
|
|
75
78
|
|
|
76
79
|
success(new_address)
|
|
77
80
|
else
|
|
@@ -79,8 +82,6 @@ module Spree
|
|
|
79
82
|
end
|
|
80
83
|
end
|
|
81
84
|
|
|
82
|
-
private
|
|
83
|
-
|
|
84
85
|
def prepare_address_params!(address, address_params)
|
|
85
86
|
address_params[:user_id] = address&.user_id
|
|
86
87
|
address_params[:country_id] ||= address.country_id
|
|
@@ -88,6 +89,12 @@ module Spree
|
|
|
88
89
|
address_params.transform_values!(&:presence)
|
|
89
90
|
end
|
|
90
91
|
|
|
92
|
+
def reassign_incomplete_orders(old_address_id, new_address)
|
|
93
|
+
orders = Spree::Order.incomplete.where(user_id: new_address.user_id)
|
|
94
|
+
orders.where(ship_address_id: old_address_id).update_all(ship_address_id: new_address.id, state: 'address', updated_at: Time.current)
|
|
95
|
+
orders.where(bill_address_id: old_address_id).update_all(bill_address_id: new_address.id, state: 'address', updated_at: Time.current)
|
|
96
|
+
end
|
|
97
|
+
|
|
91
98
|
def defaults_changed?(address, default_billing, default_shipping)
|
|
92
99
|
user = address.user
|
|
93
100
|
|
data/lib/spree/core/version.rb
CHANGED
data/lib/spree/events.rb
CHANGED
|
@@ -187,7 +187,7 @@ module Spree
|
|
|
187
187
|
#
|
|
188
188
|
# @return [Boolean]
|
|
189
189
|
def enabled?
|
|
190
|
-
!@globally_disabled && !
|
|
190
|
+
!@globally_disabled && !RequestStore.store[:spree_events_disabled]
|
|
191
191
|
end
|
|
192
192
|
|
|
193
193
|
# Temporarily disable events within a block
|
|
@@ -202,11 +202,35 @@ module Spree
|
|
|
202
202
|
# end
|
|
203
203
|
#
|
|
204
204
|
def disable
|
|
205
|
-
previous =
|
|
206
|
-
|
|
205
|
+
previous = RequestStore.store[:spree_events_disabled]
|
|
206
|
+
RequestStore.store[:spree_events_disabled] = true
|
|
207
207
|
yield
|
|
208
208
|
ensure
|
|
209
|
-
|
|
209
|
+
RequestStore.store[:spree_events_disabled] = previous
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# Check if automatic lifecycle events (*.created/updated/deleted) are enabled
|
|
213
|
+
#
|
|
214
|
+
# @return [Boolean]
|
|
215
|
+
def lifecycle_enabled?
|
|
216
|
+
enabled? && !RequestStore.store[:spree_lifecycle_events_disabled]
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
# Temporarily disable automatic lifecycle events within a block
|
|
220
|
+
#
|
|
221
|
+
# Unlike {disable}, explicitly published events (`publish_event`) still
|
|
222
|
+
# flow — only the per-record *.created/updated/deleted callbacks are
|
|
223
|
+
# suppressed. Used by bulk operations that emit their own coarser events
|
|
224
|
+
# afterwards (e.g. one product event per imported group).
|
|
225
|
+
#
|
|
226
|
+
# @yield Block during which lifecycle events are disabled
|
|
227
|
+
# @return [Object] Return value of the block
|
|
228
|
+
def disable_lifecycle
|
|
229
|
+
previous = RequestStore.store[:spree_lifecycle_events_disabled]
|
|
230
|
+
RequestStore.store[:spree_lifecycle_events_disabled] = true
|
|
231
|
+
yield
|
|
232
|
+
ensure
|
|
233
|
+
RequestStore.store[:spree_lifecycle_events_disabled] = previous
|
|
210
234
|
end
|
|
211
235
|
|
|
212
236
|
# Globally disable events
|
|
@@ -233,13 +257,13 @@ module Spree
|
|
|
233
257
|
#
|
|
234
258
|
def enable
|
|
235
259
|
previous_global = @globally_disabled
|
|
236
|
-
previous_thread =
|
|
260
|
+
previous_thread = RequestStore.store[:spree_events_disabled]
|
|
237
261
|
@globally_disabled = false
|
|
238
|
-
|
|
262
|
+
RequestStore.store[:spree_events_disabled] = false
|
|
239
263
|
yield
|
|
240
264
|
ensure
|
|
241
265
|
@globally_disabled = previous_global
|
|
242
|
-
|
|
266
|
+
RequestStore.store[:spree_events_disabled] = previous_thread
|
|
243
267
|
end
|
|
244
268
|
|
|
245
269
|
# Globally enable events
|
|
@@ -21,6 +21,18 @@ shared_examples_for 'lifecycle events' do |factory: nil, event_prefix: nil|
|
|
|
21
21
|
expect(record).to receive(:publish_event).with("#{lifecycle_event_prefix}.updated")
|
|
22
22
|
allow(record).to receive(:publish_event).with(anything)
|
|
23
23
|
|
|
24
|
+
# update_attribute, not update!: Spree::Shipment#update! shadows the
|
|
25
|
+
# Active Record method with the legacy state-recalculation API.
|
|
26
|
+
Timecop.travel(1.minute.from_now) do
|
|
27
|
+
record.update_attribute(:updated_at, Time.current)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'does not publish updated event on a bare touch' do
|
|
32
|
+
record = create(lifecycle_factory)
|
|
33
|
+
expect(record).not_to receive(:publish_event).with("#{lifecycle_event_prefix}.updated")
|
|
34
|
+
allow(record).to receive(:publish_event).with(anything)
|
|
35
|
+
|
|
24
36
|
record.touch
|
|
25
37
|
end
|
|
26
38
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: spree_core
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 5.6.0.
|
|
4
|
+
version: 5.6.0.rc3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sean Schofield
|
|
@@ -10,7 +10,7 @@ authors:
|
|
|
10
10
|
autorequire:
|
|
11
11
|
bindir: bin
|
|
12
12
|
cert_chain: []
|
|
13
|
-
date: 2026-07-
|
|
13
|
+
date: 2026-07-19 00:00:00.000000000 Z
|
|
14
14
|
dependencies:
|
|
15
15
|
- !ruby/object:Gem::Dependency
|
|
16
16
|
name: i18n-tasks
|
|
@@ -849,6 +849,7 @@ files:
|
|
|
849
849
|
- app/helpers/spree/locale_helper.rb
|
|
850
850
|
- app/helpers/spree/localized_names_helper.rb
|
|
851
851
|
- app/helpers/spree/products_helper.rb
|
|
852
|
+
- app/helpers/spree/rich_text_helper.rb
|
|
852
853
|
- app/helpers/spree/shipment_helper.rb
|
|
853
854
|
- app/javascript/spree/core/controllers/address_autocomplete_controller.js
|
|
854
855
|
- app/javascript/spree/core/controllers/address_form_controller.js
|
|
@@ -1860,9 +1861,9 @@ licenses:
|
|
|
1860
1861
|
- BSD-3-Clause
|
|
1861
1862
|
metadata:
|
|
1862
1863
|
bug_tracker_uri: https://github.com/spree/spree/issues
|
|
1863
|
-
changelog_uri: https://github.com/spree/spree/releases/tag/v5.6.0.
|
|
1864
|
+
changelog_uri: https://github.com/spree/spree/releases/tag/v5.6.0.rc3
|
|
1864
1865
|
documentation_uri: https://docs.spreecommerce.org/
|
|
1865
|
-
source_code_uri: https://github.com/spree/spree/tree/v5.6.0.
|
|
1866
|
+
source_code_uri: https://github.com/spree/spree/tree/v5.6.0.rc3
|
|
1866
1867
|
post_install_message:
|
|
1867
1868
|
rdoc_options: []
|
|
1868
1869
|
require_paths:
|