spree_core 6.0.0.beta1 → 6.0.0.beta3
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/jobs/spree/payments/handle_webhook_job.rb +1 -0
- data/app/jobs/spree/sample_data/load_job.rb +18 -0
- data/app/jobs/spree/seller_transfers/execute_pending_job.rb +1 -0
- data/app/mailers/spree/admin_user_mailer.rb +2 -2
- data/app/models/concerns/spree/acted_by.rb +210 -0
- data/app/models/concerns/spree/actor.rb +51 -0
- data/app/models/concerns/spree/admin_user_methods.rb +48 -17
- data/app/models/concerns/spree/customer_methods.rb +17 -1
- data/app/models/concerns/spree/has_number.rb +3 -2
- data/app/models/concerns/spree/prefixed_id.rb +4 -1
- data/app/models/concerns/spree/purchase/payment_processing.rb +1 -1
- data/app/models/concerns/spree/purchase/totals.rb +3 -1
- data/app/models/concerns/spree/ransackable_attributes.rb +38 -6
- data/app/models/concerns/spree/translatable_resource.rb +6 -7
- data/app/models/concerns/spree/webhook_payload_redaction.rb +132 -21
- data/app/models/spree/adjusters/promotion.rb +1 -1
- data/app/models/spree/api_key.rb +5 -3
- data/app/models/spree/authentication/lockout.rb +34 -0
- data/app/models/spree/authentication/strategies/email_password_strategy.rb +4 -12
- data/app/models/spree/authentication/strategies/oidc_strategy.rb +1 -1
- data/app/models/spree/catalog.rb +50 -13
- data/app/models/spree/claim.rb +3 -1
- data/app/models/spree/commission_line.rb +4 -2
- data/app/models/spree/commission_rule.rb +1 -1
- data/app/models/spree/coupon_code.rb +14 -2
- data/app/models/spree/current.rb +56 -13
- data/app/models/spree/exchange.rb +3 -1
- data/app/models/spree/export.rb +8 -5
- data/app/models/spree/exports/customers.rb +2 -2
- data/app/models/spree/fulfillment_group.rb +127 -0
- data/app/models/spree/gift_card.rb +9 -0
- data/app/models/spree/invitation.rb +18 -5
- data/app/models/spree/line_item.rb +1 -1
- data/app/models/spree/number_generators/sequential.rb +2 -2
- data/app/models/spree/option_value.rb +7 -0
- data/app/models/spree/order.rb +89 -18
- data/app/models/spree/order_group.rb +78 -6
- data/app/models/spree/payment_source.rb +1 -1
- data/app/models/spree/payment_split.rb +24 -0
- data/app/models/spree/price.rb +74 -0
- data/app/models/spree/pricing_provider/internal.rb +37 -15
- data/app/models/spree/product.rb +4 -0
- data/app/models/spree/product_option_type.rb +11 -0
- data/app/models/spree/promotion/actions/create_line_items.rb +92 -30
- data/app/models/spree/promotion/rules/category.rb +14 -9
- data/app/models/spree/promotion/rules/product.rb +17 -1
- data/app/models/spree/promotion.rb +1 -1
- data/app/models/spree/promotion_action.rb +12 -1
- data/app/models/spree/promotion_handler/coupon.rb +50 -5
- data/app/models/spree/promotion_rule.rb +14 -0
- data/app/models/spree/refund.rb +2 -1
- data/app/models/spree/return.rb +18 -7
- data/app/models/spree/return_line_item.rb +19 -0
- data/app/models/spree/search_provider/database.rb +10 -2
- data/app/models/spree/seller_payout.rb +3 -1
- data/app/models/spree/seller_transfer.rb +3 -1
- data/app/models/spree/stock_receipt.rb +7 -2
- data/app/models/spree/store.rb +4 -0
- data/app/models/spree/store_credit.rb +4 -0
- data/app/models/spree/translations/batch.rb +4 -2
- data/app/models/spree/user_identity.rb +10 -6
- data/app/models/spree/variant.rb +8 -3
- data/app/models/spree/webhook_delivery.rb +28 -0
- data/app/models/spree/webhook_endpoint.rb +14 -0
- data/app/presenters/spree/csv/order_line_item_presenter.rb +2 -2
- data/app/services/spree/carts/split_by_seller.rb +22 -12
- data/app/services/spree/companies/add_member.rb +8 -2
- data/app/services/spree/imports/row_processors/price_list_price.rb +6 -0
- data/app/services/spree/imports/row_processors/product_translation.rb +1 -1
- data/app/services/spree/imports/row_processors/product_variant.rb +17 -11
- data/app/services/spree/order_groups/allocate_payments.rb +3 -0
- data/app/services/spree/orders/approve.rb +3 -2
- data/app/services/spree/orders/build_fulfillments.rb +6 -0
- data/app/services/spree/orders/update_statuses.rb +8 -2
- data/app/services/spree/prices/bulk_upsert.rb +186 -47
- data/app/services/spree/sample_data/import_builder.rb +1 -1
- data/app/services/spree/sample_data/loader.rb +54 -40
- data/app/services/spree/seeds/all.rb +1 -26
- data/app/services/spree/seeds/allowed_origins.rb +5 -4
- data/app/services/spree/seeds/api_keys.rb +10 -9
- data/app/services/spree/seeds/channels.rb +9 -8
- data/app/services/spree/seeds/commission_rates.rb +21 -20
- data/app/services/spree/seeds/customer_groups.rb +5 -4
- data/app/services/spree/seeds/digital_delivery.rb +7 -8
- data/app/services/spree/seeds/payment_methods.rb +12 -11
- data/app/services/spree/seeds/product_types.rb +7 -8
- data/app/services/spree/seeds/returns_environment.rb +12 -11
- data/app/services/spree/seeds/roles.rb +8 -7
- data/app/services/spree/seeds/saved_reports.rb +19 -20
- data/app/services/spree/seeds/seller_requirements.rb +5 -4
- data/app/services/spree/seeds/store_resources.rb +41 -0
- data/app/services/spree/seeds/store_scoped.rb +15 -0
- data/app/services/spree/seeds/tax_categories.rb +10 -9
- data/app/services/spree/stock_locations/stock_items/create.rb +1 -1
- data/app/services/spree/stock_locations/stock_levels/create.rb +19 -3
- data/app/subscribers/spree/order_status_subscriber.rb +15 -7
- data/app/workflows/spree/carts/add_item.rb +5 -7
- data/app/workflows/spree/carts/complete.rb +20 -64
- data/app/workflows/spree/carts/recalculate_totals.rb +3 -1
- data/app/workflows/spree/claims/create.rb +31 -1
- data/app/workflows/spree/claims/resolve.rb +7 -11
- data/app/workflows/spree/exchanges/create.rb +11 -0
- data/app/workflows/spree/exchanges/fulfill.rb +21 -13
- data/app/workflows/spree/orders/cancel.rb +3 -2
- data/app/workflows/spree/orders/complete.rb +99 -2
- data/app/workflows/spree/price_lists/update.rb +17 -0
- data/app/workflows/spree/refunds/create.rb +1 -1
- data/app/workflows/spree/refunds/order_payments.rb +55 -1
- data/app/workflows/spree/returns/approve.rb +1 -1
- data/app/workflows/spree/returns/create.rb +11 -17
- data/app/workflows/spree/returns/refund.rb +16 -25
- data/app/workflows/spree/returns/returnable_quantity.rb +53 -0
- data/app/workflows/spree/sellers/invite.rb +1 -1
- data/config/locales/en.yml +8 -119
- data/db/migrate/20260916000001_add_email_delivery_flags_to_spree_order_groups.rb +14 -0
- data/db/migrate/20260916000001_order_operation_actors_polymorphic.rb +30 -0
- data/db/migrate/20260922000001_add_refunded_order_to_spree_store_credits.rb +5 -0
- data/db/migrate/20260923000001_change_spree_user_identities_tokens_to_text.rb +10 -0
- data/db/sample_data/channels.rb +1 -1
- data/db/sample_data/collections.rb +1 -1
- data/db/sample_data/custom_field_definitions.rb +1 -1
- data/db/sample_data/digital_products.rb +1 -1
- data/db/sample_data/fulfillment.rb +1 -1
- data/db/sample_data/options.rb +4 -2
- data/db/sample_data/orders.rb +9 -10
- data/db/sample_data/payment_methods.rb +18 -18
- data/db/sample_data/product_type_categories.rb +1 -1
- data/db/sample_data/product_types.rb +2 -2
- data/db/sample_data/promotions.rb +2 -4
- data/db/sample_data/wholesale.rb +1 -1
- data/lib/generators/spree/api_resource/api_resource_generator.rb +121 -6
- data/lib/generators/spree/api_resource/templates/admin_controller.rb.tt +8 -2
- data/lib/generators/spree/api_resource/templates/admin_controller_spec.rb.tt +3 -0
- data/lib/generators/spree/api_resource/templates/permissions.en.yml.tt +6 -0
- data/lib/generators/spree/api_resource/templates/store_controller.rb.tt +2 -2
- data/lib/generators/spree/api_resource/templates/store_controller_spec.rb.tt +4 -1
- data/lib/generators/spree/dummy/templates/rails/test.rb +7 -0
- data/lib/generators/spree/model/model_generator.rb +40 -1
- data/lib/generators/spree/model/templates/create_table_migration.rb.tt +9 -0
- data/lib/generators/spree/model/templates/model.rb.tt +10 -4
- data/lib/generators/spree/subscriber/subscriber_generator.rb +3 -3
- data/lib/spree/core/configuration.rb +25 -25
- data/lib/spree/core/engine.rb +35 -8
- data/lib/spree/core/preferences/runtime_configuration.rb +76 -2
- data/lib/spree/core/version.rb +1 -1
- data/lib/spree/core.rb +27 -0
- data/lib/spree/iso_data.rb +23 -4
- data/lib/spree/store_scope_guard.rb +16 -1
- data/lib/spree/testing_support/factories/invitation_factory.rb +1 -0
- data/lib/spree/testing_support/factories/order_group_factory.rb +49 -0
- data/lib/spree/testing_support/factories/payment_factory.rb +7 -2
- data/lib/spree/testing_support/factories/user_factory.rb +0 -1
- data/lib/spree/upgrades/5_6_to_6_0/manifest.yml +34 -0
- data/lib/tasks/backfill_actor_types.rake +35 -0
- data/lib/tasks/sample_data.rake +8 -2
- data/lib/tasks/store_settings.rake +1 -0
- data/lib/tasks/upgrade.rake +6 -21
- metadata +23 -9
- /data/{app/models → lib}/spree/checkout/default_requirements.rb +0 -0
- /data/{app/models → lib}/spree/checkout/registry.rb +0 -0
- /data/{app/models → lib}/spree/checkout/requirement.rb +0 -0
- /data/{app/models → lib}/spree/checkout/requirements.rb +0 -0
- /data/{app/models → lib}/spree/checkout/step.rb +0 -0
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
module Spree
|
|
2
2
|
module Preferences
|
|
3
|
+
# Raised when an environment variable backing a preference holds a value
|
|
4
|
+
# that does not parse as the declared type.
|
|
5
|
+
class InvalidEnvironmentValue < StandardError
|
|
6
|
+
def initialize(variable, value, type, expected = nil)
|
|
7
|
+
article = type == :integer ? 'an' : 'a'
|
|
8
|
+
expected ||= "#{article} #{type}"
|
|
9
|
+
super("#{variable}=#{value.inspect} is not valid for #{article} #{type} setting. Expected #{expected}.")
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
3
13
|
class RuntimeConfiguration
|
|
14
|
+
# Deliberately not ActiveModel::Type::Boolean: it casts "no", "Off" and
|
|
15
|
+
# every unrecognised string — a typo included — to true.
|
|
16
|
+
BOOLEAN_TRUE = %w[true 1 yes on].freeze
|
|
17
|
+
BOOLEAN_FALSE = %w[false 0 no off].freeze
|
|
18
|
+
|
|
4
19
|
def initialize
|
|
5
20
|
load_defaults
|
|
6
21
|
end
|
|
@@ -71,10 +86,12 @@ module Spree
|
|
|
71
86
|
# set in an initializer or at runtime always wins over the
|
|
72
87
|
# environment; the env var only fills an unset preference.
|
|
73
88
|
#
|
|
89
|
+
# @param type [Symbol] declared type, used to coerce the env var
|
|
74
90
|
# @param env [String, nil] environment variable backing this setting
|
|
75
|
-
def preference(name,
|
|
91
|
+
def preference(name, type, default: nil, deprecated: false, env: nil)
|
|
76
92
|
defaults[name] = default
|
|
77
93
|
deprecations[name] = deprecated
|
|
94
|
+
types[name] = type
|
|
78
95
|
env_vars[name] = env if env
|
|
79
96
|
|
|
80
97
|
attr_writer name
|
|
@@ -88,7 +105,12 @@ module Spree
|
|
|
88
105
|
value = instance_variable_get(ivar)
|
|
89
106
|
return value unless value.nil?
|
|
90
107
|
|
|
91
|
-
ENV[env]
|
|
108
|
+
raw = ENV[env]
|
|
109
|
+
return default if raw.nil? || raw.empty?
|
|
110
|
+
|
|
111
|
+
# Memoized into the ivar so coercion runs once per process, not
|
|
112
|
+
# once per read — these sit on the request path.
|
|
113
|
+
instance_variable_set(ivar, self.class.coerce_env(env, raw, type))
|
|
92
114
|
end
|
|
93
115
|
else
|
|
94
116
|
attr_reader name
|
|
@@ -103,10 +125,62 @@ module Spree
|
|
|
103
125
|
@deprecations ||= {}
|
|
104
126
|
end
|
|
105
127
|
|
|
128
|
+
# @return [Hash{Symbol => Symbol}] declared type per preference
|
|
129
|
+
def types
|
|
130
|
+
@types ||= {}
|
|
131
|
+
end
|
|
132
|
+
|
|
106
133
|
# @return [Hash{Symbol => String}] preferences backed by an env var
|
|
107
134
|
def env_vars
|
|
108
135
|
@env_vars ||= {}
|
|
109
136
|
end
|
|
137
|
+
|
|
138
|
+
# Reads every env-backed preference so a malformed value fails at boot
|
|
139
|
+
# rather than on the first request that happens to read it. Reading
|
|
140
|
+
# also memoizes the coerced value, so this doubles as a warm-up.
|
|
141
|
+
#
|
|
142
|
+
# @param instance [RuntimeConfiguration] configuration to validate
|
|
143
|
+
# @raise [Spree::Preferences::InvalidEnvironmentValue]
|
|
144
|
+
# @return [void]
|
|
145
|
+
def validate_env!(instance)
|
|
146
|
+
env_vars.each_key { |name| instance.public_send(name) }
|
|
147
|
+
nil
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Env vars arrive as strings, so an uncoerced `SPREE_X=false` would be
|
|
151
|
+
# the truthy String "false". Coercion is strict: a value that does not
|
|
152
|
+
# parse raises rather than falling back to the default, since a typo'd
|
|
153
|
+
# setting silently reverting is worse than a failed boot.
|
|
154
|
+
#
|
|
155
|
+
# @param variable [String] name of the environment variable
|
|
156
|
+
# @param value [String] raw value read from the environment
|
|
157
|
+
# @param type [Symbol] declared preference type
|
|
158
|
+
# @return [Object] the coerced value
|
|
159
|
+
def coerce_env(variable, value, type)
|
|
160
|
+
case type
|
|
161
|
+
when :boolean then coerce_env_boolean(variable, value)
|
|
162
|
+
when :integer then coerce_env_number(variable, value, type) { Integer(value, 10) }
|
|
163
|
+
when :decimal then coerce_env_number(variable, value, type) { BigDecimal(value) }
|
|
164
|
+
when :array then value.split(',').map(&:strip).reject(&:empty?)
|
|
165
|
+
else value
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
private
|
|
170
|
+
|
|
171
|
+
def coerce_env_boolean(variable, value)
|
|
172
|
+
normalized = value.strip.downcase
|
|
173
|
+
return true if BOOLEAN_TRUE.include?(normalized)
|
|
174
|
+
return false if BOOLEAN_FALSE.include?(normalized)
|
|
175
|
+
|
|
176
|
+
raise InvalidEnvironmentValue.new(variable, value, :boolean, "#{BOOLEAN_TRUE.join(', ')}, #{BOOLEAN_FALSE.join(', ')}")
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def coerce_env_number(variable, value, type)
|
|
180
|
+
yield
|
|
181
|
+
rescue ArgumentError, TypeError
|
|
182
|
+
raise InvalidEnvironmentValue.new(variable, value, type)
|
|
183
|
+
end
|
|
110
184
|
end
|
|
111
185
|
end
|
|
112
186
|
end
|
data/lib/spree/core/version.rb
CHANGED
data/lib/spree/core.rb
CHANGED
|
@@ -625,6 +625,25 @@ module Spree
|
|
|
625
625
|
Rails.application.config.spree.taggable_types = value
|
|
626
626
|
end
|
|
627
627
|
|
|
628
|
+
# Class-name strings for the models that may be recorded as having performed
|
|
629
|
+
# an action — what an `acted_by` association's `*_type` column is allowed to
|
|
630
|
+
# hold. The admin user class and `Spree::ApiKey` ship registered; an
|
|
631
|
+
# extension adds its own App or bot class in an initializer:
|
|
632
|
+
#
|
|
633
|
+
# Spree.actor_classes << 'MyApp::App'
|
|
634
|
+
#
|
|
635
|
+
# A class listed here includes {Spree::Actor}, so a timeline can name it.
|
|
636
|
+
# See docs/plans/6.0-action-actors.md.
|
|
637
|
+
#
|
|
638
|
+
# @return [Array<String>]
|
|
639
|
+
def self.actor_classes
|
|
640
|
+
Rails.application.config.spree.actor_classes
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
def self.actor_classes=(value)
|
|
644
|
+
Rails.application.config.spree.actor_classes = value
|
|
645
|
+
end
|
|
646
|
+
|
|
628
647
|
|
|
629
648
|
# Registry of the Getting Started onboarding tasks shown on the admin
|
|
630
649
|
# dashboard. See {Spree::SetupTasks} for the extension API.
|
|
@@ -903,6 +922,14 @@ require 'spree/reporting'
|
|
|
903
922
|
require 'spree/events'
|
|
904
923
|
require 'spree/store_scope_guard'
|
|
905
924
|
|
|
925
|
+
# Not autoloaded from app/: the registry keeps registered steps in
|
|
926
|
+
# class-level state, which a reload would discard.
|
|
927
|
+
require 'spree/checkout/step'
|
|
928
|
+
require 'spree/checkout/requirement'
|
|
929
|
+
require 'spree/checkout/registry'
|
|
930
|
+
require 'spree/checkout/default_requirements'
|
|
931
|
+
require 'spree/checkout/requirements'
|
|
932
|
+
|
|
906
933
|
require 'spree/core/controller_helpers/store'
|
|
907
934
|
|
|
908
935
|
require 'spree/core/preferences/store'
|
data/lib/spree/iso_data.rb
CHANGED
|
@@ -53,6 +53,13 @@ module Spree
|
|
|
53
53
|
}
|
|
54
54
|
}.freeze
|
|
55
55
|
|
|
56
|
+
# Some gem subdivision names are a placeholder the gem falls back to when it
|
|
57
|
+
# has no proper name for the entry: the value it would have translated,
|
|
58
|
+
# tagged with the locale it belongs to — Pakistan's "Sind (en)",
|
|
59
|
+
# "Baluchistan (en)". The clean name is the locale's own translation, so a
|
|
60
|
+
# name matching this is replaced rather than shown or matched against.
|
|
61
|
+
LOCALE_TAGGED_NAME = /\s\([a-z]{2}(?:-[a-z]{2})?\)\z/i
|
|
62
|
+
|
|
56
63
|
# Retired ISO codes mapped to their current equivalent. Reading an address
|
|
57
64
|
# written under the old code still finds the right subdivision; writing
|
|
58
65
|
# always stores the successor.
|
|
@@ -172,16 +179,28 @@ module Spree
|
|
|
172
179
|
# English deliberately reads the canonical +name+ rather than the +en+
|
|
173
180
|
# translation: the two disagree in places (the gem translates DC as
|
|
174
181
|
# "Washington DC", where the canonical name is "District of Columbia"),
|
|
175
|
-
# and the canonical spelling is the one Spree has always stored.
|
|
182
|
+
# and the canonical spelling is the one Spree has always stored. The
|
|
183
|
+
# exception is a name the gem left as a locale-tagged placeholder
|
|
184
|
+
# ("Sind (en)"): that is not a spelling to preserve, so the clean English
|
|
185
|
+
# translation wins.
|
|
176
186
|
#
|
|
177
187
|
# Note the translation keys are symbols here, unlike +Country#translation+,
|
|
178
188
|
# which takes a string.
|
|
179
189
|
def subdivision_display_name(subdivision)
|
|
180
190
|
locale = I18n.locale.to_s.downcase
|
|
181
|
-
return subdivision.name if locale.start_with?('en')
|
|
182
|
-
|
|
183
191
|
translations = subdivision.translations || {}
|
|
184
|
-
translations[
|
|
192
|
+
english = translations[:en].presence
|
|
193
|
+
|
|
194
|
+
if locale.start_with?('en')
|
|
195
|
+
name = subdivision.name
|
|
196
|
+
return name.match?(LOCALE_TAGGED_NAME) ? (english || name) : name
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
translated = translations[locale.to_sym].presence
|
|
200
|
+
return translated if translated
|
|
201
|
+
|
|
202
|
+
name = subdivision.name
|
|
203
|
+
name.match?(LOCALE_TAGGED_NAME) ? (english || name) : name
|
|
185
204
|
end
|
|
186
205
|
end
|
|
187
206
|
end
|
|
@@ -72,6 +72,19 @@ module Spree
|
|
|
72
72
|
RequestStore.store[ACTIVE_KEY] = previous
|
|
73
73
|
end
|
|
74
74
|
|
|
75
|
+
# Opens the window for whatever declared a store context — a job, a
|
|
76
|
+
# webhook, a console script — and leaves it open until Spree::Current
|
|
77
|
+
# resets, which Rails does per request and per job. Called from
|
|
78
|
+
# `Spree::Current#store=`; work that never declares a store stays
|
|
79
|
+
# unguarded, since reading the default store through the fallback is
|
|
80
|
+
# not a scoping claim.
|
|
81
|
+
def arm!
|
|
82
|
+
return unless enabled?
|
|
83
|
+
|
|
84
|
+
install!
|
|
85
|
+
Spree::Current.store_scope_guard_armed = true
|
|
86
|
+
end
|
|
87
|
+
|
|
75
88
|
# Suppresses the guard for a deliberately store-less lookup.
|
|
76
89
|
def skip
|
|
77
90
|
previous = RequestStore.store[SKIP_KEY]
|
|
@@ -82,7 +95,9 @@ module Spree
|
|
|
82
95
|
end
|
|
83
96
|
|
|
84
97
|
def active?
|
|
85
|
-
|
|
98
|
+
return false if RequestStore.store[SKIP_KEY]
|
|
99
|
+
|
|
100
|
+
RequestStore.store[ACTIVE_KEY] || Spree::Current.store_scope_guard_armed.present?
|
|
86
101
|
end
|
|
87
102
|
|
|
88
103
|
# Forgets memoized environment/schema state (specs, migrations).
|
|
@@ -5,6 +5,55 @@ FactoryBot.define do
|
|
|
5
5
|
currency { 'USD' }
|
|
6
6
|
email { customer&.email }
|
|
7
7
|
|
|
8
|
+
# A split checkout as it actually leaves completion: each child holding a
|
|
9
|
+
# seller's item and the parcel that ships it. `:with_orders` builds bare
|
|
10
|
+
# orders, which is enough for roll-up arithmetic and not enough for
|
|
11
|
+
# anything that renders the purchase.
|
|
12
|
+
#
|
|
13
|
+
# One stock location per seller by default, so each parcel is quoted on its
|
|
14
|
+
# own delivery method and the group ships in as many parcels as it has
|
|
15
|
+
# children. Pass `shared_stock_location: true` for the marketplace-fulfilled
|
|
16
|
+
# shape instead: every child ships out of one warehouse on one method, which
|
|
17
|
+
# is what a divided parcel looks like after the split clones it.
|
|
18
|
+
trait :with_parcels do
|
|
19
|
+
transient do
|
|
20
|
+
sellers_count { 2 }
|
|
21
|
+
shared_stock_location { false }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
after(:create) do |group, evaluator|
|
|
25
|
+
shared_location = create(:stock_location, name: "Shared #{group.number}") if evaluator.shared_stock_location
|
|
26
|
+
shared_method = create(:delivery_method, name: "Shared #{group.number}") if evaluator.shared_stock_location
|
|
27
|
+
|
|
28
|
+
evaluator.sellers_count.times do |index|
|
|
29
|
+
seller = create(:seller, :approved, store: group.store)
|
|
30
|
+
order = create(
|
|
31
|
+
:order_with_line_items,
|
|
32
|
+
store: group.store,
|
|
33
|
+
order_group: group,
|
|
34
|
+
seller: seller,
|
|
35
|
+
number: "#{group.number}-#{index + 1}",
|
|
36
|
+
customer: group.customer,
|
|
37
|
+
currency: group.currency,
|
|
38
|
+
line_items_count: 1
|
|
39
|
+
)
|
|
40
|
+
order.line_items.each { |line_item| line_item.update_columns(seller_id: seller.id) }
|
|
41
|
+
|
|
42
|
+
# The order factory already built the parcel; this decides where it
|
|
43
|
+
# ships from and by what, which is what the grouping reads.
|
|
44
|
+
fulfillment = order.fulfillments.first
|
|
45
|
+
fulfillment.update_columns(
|
|
46
|
+
stock_location_id: (shared_location || create(:stock_location, name: "#{seller.name} warehouse")).id
|
|
47
|
+
)
|
|
48
|
+
fulfillment.delivery_rates.destroy_all
|
|
49
|
+
fulfillment.add_delivery_method(shared_method || create(:delivery_method, name: "#{seller.name} delivery"), true)
|
|
50
|
+
order.recalculate_totals!
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
group.orders.reload
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
8
57
|
trait :with_orders do
|
|
9
58
|
transient do
|
|
10
59
|
sellers_count { 2 }
|
|
@@ -29,7 +29,12 @@ FactoryBot.define do
|
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
factory :store_credit_payment, class: Spree::Payment, parent: :payment do
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
# An order group owns payments like a cart or an order does, and a split
|
|
33
|
+
# checkout paid with store credit is exactly where that matters.
|
|
34
|
+
payment_method { create(:store_credit_payment_method, store: (order || cart || order_group).store) }
|
|
35
|
+
source do
|
|
36
|
+
owner = order || cart || order_group
|
|
37
|
+
create(:store_credit, store: owner.store, customer: owner.customer)
|
|
38
|
+
end
|
|
34
39
|
end
|
|
35
40
|
end
|
|
@@ -438,6 +438,22 @@ steps:
|
|
|
438
438
|
once several stores hold tax configuration. The NOT NULL constraints land
|
|
439
439
|
in 6.1. Idempotent.
|
|
440
440
|
|
|
441
|
+
- id: backfill_order_coupon_codes
|
|
442
|
+
name: "Backfill coupon codes on placed orders"
|
|
443
|
+
task: "spree:backfill_order_coupon_codes"
|
|
444
|
+
notes: |
|
|
445
|
+
spree_orders gains a coupon_code column in Spree 6, matching carts.
|
|
446
|
+
Historical placed orders applied coupons only through the promotion
|
|
447
|
+
join tables, so this fills the column from the order's coupon-code
|
|
448
|
+
record, or failing that from the code of an applied single-code
|
|
449
|
+
promotion, so admin filtering and the serializer answer consistently
|
|
450
|
+
for old orders. Reads promotion records only — no dependency on the
|
|
451
|
+
adjustments conversion — and touches completed orders only, so it is
|
|
452
|
+
independent of the cart conversion below. Tune the load size with
|
|
453
|
+
BATCH_SIZE (default 500).
|
|
454
|
+
|
|
455
|
+
Idempotent — only orders whose coupon_code is still blank are scanned.
|
|
456
|
+
|
|
441
457
|
- id: backfill_order_markets
|
|
442
458
|
name: "Backfill order markets"
|
|
443
459
|
task: "spree:backfill_order_markets"
|
|
@@ -664,6 +680,24 @@ steps:
|
|
|
664
680
|
stop the run. Purge the soft-deleted rows later with
|
|
665
681
|
spree:upgrade:purge_migrated_external_receives.
|
|
666
682
|
|
|
683
|
+
- id: backfill_actor_types
|
|
684
|
+
name: "Name the kind of actor on past orders, returns, refunds and receipts"
|
|
685
|
+
task: "spree:upgrade:backfill_actor_types"
|
|
686
|
+
notes: |
|
|
687
|
+
Orders, refunds, returns, exchanges, claims and stock receipts remember
|
|
688
|
+
who cancelled, approved, created or received them. In 6.0 that can be a
|
|
689
|
+
member of staff or an API key, so each actor id column gained a type
|
|
690
|
+
column beside it. Rows written before the upgrade carry an id and no
|
|
691
|
+
type — they could only ever have pointed at an admin user — so this
|
|
692
|
+
fills in the admin user class. Until it runs those rows still read as
|
|
693
|
+
the staff member they always were, with a deprecation warning.
|
|
694
|
+
|
|
695
|
+
Run AFTER migrate_returns and migrate_external_receives_to_purchase_orders
|
|
696
|
+
so any actor rows those steps write are covered too. Walks
|
|
697
|
+
every model that declares acted_by, so extensions are included.
|
|
698
|
+
|
|
699
|
+
Idempotent — a row that already carries a type is skipped.
|
|
700
|
+
|
|
667
701
|
- id: recount_stock_levels
|
|
668
702
|
name: "Fill in the reserved and incoming counters on every stock level"
|
|
669
703
|
task: "spree:stock:recount_levels"
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
namespace :spree do
|
|
2
|
+
namespace :upgrade do
|
|
3
|
+
desc <<~DESC
|
|
4
|
+
Names the admin user class on every "who did this" row written before
|
|
5
|
+
those associations became polymorphic (Spree 6.0). Rows with an actor id
|
|
6
|
+
and no actor type could only ever have pointed at an admin user, so the
|
|
7
|
+
type is filled in and the transitional reader stops being needed.
|
|
8
|
+
|
|
9
|
+
Runs over whatever each model declares with `acted_by`, so the 6.1
|
|
10
|
+
conversion of the remaining associations reuses this task unchanged.
|
|
11
|
+
Idempotent: a row that already carries a type is skipped.
|
|
12
|
+
DESC
|
|
13
|
+
task backfill_actor_types: :environment do
|
|
14
|
+
actor_type = Spree.admin_user_class.to_s
|
|
15
|
+
total = 0
|
|
16
|
+
|
|
17
|
+
Spree::ActedBy.models.each do |model|
|
|
18
|
+
model.acted_by_associations.each do |name|
|
|
19
|
+
count = model.
|
|
20
|
+
where.not(:"#{name}_id" => nil).
|
|
21
|
+
where(:"#{name}_type" => nil).
|
|
22
|
+
in_batches.
|
|
23
|
+
sum { |batch| batch.update_all(:"#{name}_type" => actor_type) }
|
|
24
|
+
|
|
25
|
+
next if count.zero?
|
|
26
|
+
|
|
27
|
+
total += count
|
|
28
|
+
puts " #{model.table_name}.#{name}: #{count} row(s) set to #{actor_type}."
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
puts total.zero? ? ' Nothing to backfill.' : " Backfilled #{total} actor reference(s)."
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
data/lib/tasks/sample_data.rake
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
namespace :spree do
|
|
2
|
-
desc 'Loads sample data (products, customers, orders, configuration)'
|
|
2
|
+
desc 'Loads sample data (products, customers, orders, configuration). Set STORE_ID (prefixed or numeric) or STORE_CODE to target a store other than the default one'
|
|
3
3
|
task load_sample_data: :environment do
|
|
4
|
-
|
|
4
|
+
store = if ENV['STORE_ID'].present?
|
|
5
|
+
Spree::Store.find_by_param!(ENV['STORE_ID'])
|
|
6
|
+
elsif ENV['STORE_CODE'].present?
|
|
7
|
+
Spree::Store.find_by!(code: ENV['STORE_CODE'])
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
Spree::SampleData::Loader.call(store: store)
|
|
5
11
|
end
|
|
6
12
|
end
|
|
7
13
|
|
|
@@ -6,6 +6,7 @@ namespace :spree do
|
|
|
6
6
|
# intent and is never copied.
|
|
7
7
|
MOVED_SETTINGS = {
|
|
8
8
|
address_requires_phone: { default: false },
|
|
9
|
+
always_include_confirm_step: { default: false },
|
|
9
10
|
company: { default: false, store_preference: :company_field_enabled },
|
|
10
11
|
default_stock_reservation_ttl_minutes: { default: 10, store_preference: :stock_reservation_ttl_minutes },
|
|
11
12
|
disable_sku_validation: { default: false },
|
data/lib/tasks/upgrade.rake
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
#
|
|
14
14
|
# Usage:
|
|
15
15
|
# bundle exec rake spree:upgrade # walk all eligible manifests
|
|
16
|
-
# bundle exec rake spree:upgrade DRY_RUN=1 # print
|
|
16
|
+
# bundle exec rake spree:upgrade DRY_RUN=1 # print the steps a run would execute, run nothing
|
|
17
17
|
# bundle exec rake spree:upgrade STEP=channels # run one step by id (any manifest)
|
|
18
18
|
# bundle exec rake spree:upgrade TO=5.4 # cap the walk at this version
|
|
19
19
|
#
|
|
@@ -72,10 +72,9 @@ module Spree
|
|
|
72
72
|
# rendering, invocation) stay separable and the plan can be inspected
|
|
73
73
|
# without execution.
|
|
74
74
|
class Runner
|
|
75
|
-
attr_reader :target_version, :step_id, :dry_run
|
|
75
|
+
attr_reader :target_version, :step_id, :dry_run
|
|
76
76
|
|
|
77
77
|
def initialize(target_version: nil, step_id: nil, dry_run: false)
|
|
78
|
-
@target_explicit = !target_version.nil?
|
|
79
78
|
@target_version = target_version || Spree::Upgrade.installed_minor_version
|
|
80
79
|
@step_id = step_id
|
|
81
80
|
@dry_run = dry_run
|
|
@@ -98,36 +97,22 @@ module Spree
|
|
|
98
97
|
|
|
99
98
|
private
|
|
100
99
|
|
|
101
|
-
#
|
|
102
|
-
#
|
|
103
|
-
# manifests whose `to` is ≤ target.
|
|
100
|
+
# Plan mode selects exactly what a real run executes, so a dry run after
|
|
101
|
+
# bumping the gem lists every backfill the upgrade is about to perform.
|
|
104
102
|
def eligible_manifests
|
|
105
103
|
Spree::Upgrade.available_manifests
|
|
106
|
-
.select { |manifest|
|
|
104
|
+
.select { |manifest| Spree::Upgrade.compare(manifest[:to], target_version) <= 0 }
|
|
107
105
|
.map { |entry| load_manifest_yaml(entry) }
|
|
108
106
|
end
|
|
109
107
|
|
|
110
|
-
def manifest_eligible?(manifest)
|
|
111
|
-
if dry_run && !target_explicit
|
|
112
|
-
Spree::Upgrade.compare(manifest[:from], Spree::Upgrade.installed_minor_version) >= 0
|
|
113
|
-
else
|
|
114
|
-
Spree::Upgrade.compare(manifest[:to], target_version) <= 0
|
|
115
|
-
end
|
|
116
|
-
end
|
|
117
|
-
|
|
118
108
|
def load_manifest_yaml(entry)
|
|
119
109
|
YAML.safe_load_file(File.join(entry[:dir], 'manifest.yml'))
|
|
120
110
|
end
|
|
121
111
|
|
|
122
112
|
def run_full_walk(manifests)
|
|
123
113
|
total_steps = manifests.sum { |m| m['steps'].size }
|
|
124
|
-
reported_target = if dry_run && !target_explicit
|
|
125
|
-
manifests.map { |m| m['to'] }.compact.max_by { |v| Spree::Upgrade.version_parts(v) } || target_version
|
|
126
|
-
else
|
|
127
|
-
target_version
|
|
128
|
-
end
|
|
129
114
|
puts
|
|
130
|
-
puts " Walking #{manifests.size} manifest(s), #{total_steps} step(s) total. Target: Spree #{
|
|
115
|
+
puts " Walking #{manifests.size} manifest(s), #{total_steps} step(s) total. Target: Spree #{target_version}."
|
|
131
116
|
|
|
132
117
|
manifests.each do |manifest|
|
|
133
118
|
print_manifest_header(manifest)
|
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: 6.0.0.
|
|
4
|
+
version: 6.0.0.beta3
|
|
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-09-
|
|
13
|
+
date: 2026-09-24 00:00:00.000000000 Z
|
|
14
14
|
dependencies:
|
|
15
15
|
- !ruby/object:Gem::Dependency
|
|
16
16
|
name: i18n-tasks
|
|
@@ -670,6 +670,7 @@ files:
|
|
|
670
670
|
- app/jobs/spree/products/refresh_metrics_job.rb
|
|
671
671
|
- app/jobs/spree/products/touch_categories_job.rb
|
|
672
672
|
- app/jobs/spree/products/touch_taxons_job.rb
|
|
673
|
+
- app/jobs/spree/sample_data/load_job.rb
|
|
673
674
|
- app/jobs/spree/search_provider/index_job.rb
|
|
674
675
|
- app/jobs/spree/search_provider/remove_job.rb
|
|
675
676
|
- app/jobs/spree/seller_payouts/sweep_due_job.rb
|
|
@@ -693,6 +694,8 @@ files:
|
|
|
693
694
|
- app/mailers/spree/webhook_mailer.rb
|
|
694
695
|
- app/models/acts_as_taggable_on/tag_decorator.rb
|
|
695
696
|
- app/models/concerns/spree/account_lockout.rb
|
|
697
|
+
- app/models/concerns/spree/acted_by.rb
|
|
698
|
+
- app/models/concerns/spree/actor.rb
|
|
696
699
|
- app/models/concerns/spree/admin_user_methods.rb
|
|
697
700
|
- app/models/concerns/spree/calculated_adjustments.rb
|
|
698
701
|
- app/models/concerns/spree/calculator/currency_amounts.rb
|
|
@@ -789,6 +792,7 @@ files:
|
|
|
789
792
|
- app/models/spree/allowed_origin.rb
|
|
790
793
|
- app/models/spree/api_key.rb
|
|
791
794
|
- app/models/spree/asset.rb
|
|
795
|
+
- app/models/spree/authentication/lockout.rb
|
|
792
796
|
- app/models/spree/authentication/strategies/base_strategy.rb
|
|
793
797
|
- app/models/spree/authentication/strategies/email_password_strategy.rb
|
|
794
798
|
- app/models/spree/authentication/strategies/oidc_strategy.rb
|
|
@@ -824,11 +828,6 @@ files:
|
|
|
824
828
|
- app/models/spree/channel.rb
|
|
825
829
|
- app/models/spree/channel/gating.rb
|
|
826
830
|
- app/models/spree/channel_stock_location.rb
|
|
827
|
-
- app/models/spree/checkout/default_requirements.rb
|
|
828
|
-
- app/models/spree/checkout/registry.rb
|
|
829
|
-
- app/models/spree/checkout/requirement.rb
|
|
830
|
-
- app/models/spree/checkout/requirements.rb
|
|
831
|
-
- app/models/spree/checkout/step.rb
|
|
832
831
|
- app/models/spree/claim.rb
|
|
833
832
|
- app/models/spree/claim_line_item.rb
|
|
834
833
|
- app/models/spree/claim_reason.rb
|
|
@@ -927,6 +926,7 @@ files:
|
|
|
927
926
|
- app/models/spree/fulfillment.rb
|
|
928
927
|
- app/models/spree/fulfillment/custom_events.rb
|
|
929
928
|
- app/models/spree/fulfillment_changer.rb
|
|
929
|
+
- app/models/spree/fulfillment_group.rb
|
|
930
930
|
- app/models/spree/fulfillment_item.rb
|
|
931
931
|
- app/models/spree/fulfillment_provider/base.rb
|
|
932
932
|
- app/models/spree/fulfillment_provider/digital.rb
|
|
@@ -1316,6 +1316,8 @@ files:
|
|
|
1316
1316
|
- app/services/spree/seeds/roles.rb
|
|
1317
1317
|
- app/services/spree/seeds/saved_reports.rb
|
|
1318
1318
|
- app/services/spree/seeds/seller_requirements.rb
|
|
1319
|
+
- app/services/spree/seeds/store_resources.rb
|
|
1320
|
+
- app/services/spree/seeds/store_scoped.rb
|
|
1319
1321
|
- app/services/spree/seeds/stores.rb
|
|
1320
1322
|
- app/services/spree/seeds/tax_categories.rb
|
|
1321
1323
|
- app/services/spree/sellers/requirements.rb
|
|
@@ -1463,6 +1465,7 @@ files:
|
|
|
1463
1465
|
- app/workflows/spree/returns/purchase_label.rb
|
|
1464
1466
|
- app/workflows/spree/returns/receive.rb
|
|
1465
1467
|
- app/workflows/spree/returns/refund.rb
|
|
1468
|
+
- app/workflows/spree/returns/returnable_quantity.rb
|
|
1466
1469
|
- app/workflows/spree/seller_payouts/complete.rb
|
|
1467
1470
|
- app/workflows/spree/seller_payouts/sweep.rb
|
|
1468
1471
|
- app/workflows/spree/seller_requirement_submissions/accept.rb
|
|
@@ -1787,6 +1790,10 @@ files:
|
|
|
1787
1790
|
- db/migrate/20260912120000_index_reporting_lifetime_and_promotion_columns.rb
|
|
1788
1791
|
- db/migrate/20260914000001_add_settlement_to_spree_seller_transfers.rb
|
|
1789
1792
|
- db/migrate/20260914230000_add_store_to_spree_option_types.rb
|
|
1793
|
+
- db/migrate/20260916000001_add_email_delivery_flags_to_spree_order_groups.rb
|
|
1794
|
+
- db/migrate/20260916000001_order_operation_actors_polymorphic.rb
|
|
1795
|
+
- db/migrate/20260922000001_add_refunded_order_to_spree_store_credits.rb
|
|
1796
|
+
- db/migrate/20260923000001_change_spree_user_identities_tokens_to_text.rb
|
|
1790
1797
|
- db/sample_data/channels.rb
|
|
1791
1798
|
- db/sample_data/collections.rb
|
|
1792
1799
|
- db/sample_data/custom_field_definitions.rb
|
|
@@ -1811,6 +1818,7 @@ files:
|
|
|
1811
1818
|
- lib/generators/spree/api_resource/templates/admin_controller_spec.rb.tt
|
|
1812
1819
|
- lib/generators/spree/api_resource/templates/admin_serializer.rb.tt
|
|
1813
1820
|
- lib/generators/spree/api_resource/templates/factory.rb.tt
|
|
1821
|
+
- lib/generators/spree/api_resource/templates/permissions.en.yml.tt
|
|
1814
1822
|
- lib/generators/spree/api_resource/templates/store_aliased_serializer.rb.tt
|
|
1815
1823
|
- lib/generators/spree/api_resource/templates/store_controller.rb.tt
|
|
1816
1824
|
- lib/generators/spree/api_resource/templates/store_controller_spec.rb.tt
|
|
@@ -1840,6 +1848,11 @@ files:
|
|
|
1840
1848
|
- lib/mobility/plugins/store_based_fallbacks.rb
|
|
1841
1849
|
- lib/spree/analytics.rb
|
|
1842
1850
|
- lib/spree/category_permalink_deduplicator.rb
|
|
1851
|
+
- lib/spree/checkout/default_requirements.rb
|
|
1852
|
+
- lib/spree/checkout/registry.rb
|
|
1853
|
+
- lib/spree/checkout/requirement.rb
|
|
1854
|
+
- lib/spree/checkout/requirements.rb
|
|
1855
|
+
- lib/spree/checkout/step.rb
|
|
1843
1856
|
- lib/spree/core.rb
|
|
1844
1857
|
- lib/spree/core/configuration.rb
|
|
1845
1858
|
- lib/spree/core/controller_helpers/store.rb
|
|
@@ -2052,6 +2065,7 @@ files:
|
|
|
2052
2065
|
- lib/spree/validators.rb
|
|
2053
2066
|
- lib/spree/workflow.rb
|
|
2054
2067
|
- lib/spree_core.rb
|
|
2068
|
+
- lib/tasks/backfill_actor_types.rake
|
|
2055
2069
|
- lib/tasks/backfill_custom_field_definition_stores.rake
|
|
2056
2070
|
- lib/tasks/backfill_import_export_tenancy.rake
|
|
2057
2071
|
- lib/tasks/backfill_library_media_placements.rake
|
|
@@ -2122,9 +2136,9 @@ licenses:
|
|
|
2122
2136
|
- BSD-3-Clause
|
|
2123
2137
|
metadata:
|
|
2124
2138
|
bug_tracker_uri: https://github.com/spree/spree/issues
|
|
2125
|
-
changelog_uri: https://github.com/spree/spree/releases/tag/v6.0.0.
|
|
2139
|
+
changelog_uri: https://github.com/spree/spree/releases/tag/v6.0.0.beta3
|
|
2126
2140
|
documentation_uri: https://docs.spreecommerce.org/
|
|
2127
|
-
source_code_uri: https://github.com/spree/spree/tree/v6.0.0.
|
|
2141
|
+
source_code_uri: https://github.com/spree/spree/tree/v6.0.0.beta3
|
|
2128
2142
|
post_install_message:
|
|
2129
2143
|
rdoc_options: []
|
|
2130
2144
|
require_paths:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|