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
|
@@ -17,6 +17,7 @@ module Spree
|
|
|
17
17
|
# - Factory (managed — overwrite on re-run)
|
|
18
18
|
# - Controller specs (managed — overwrite on re-run)
|
|
19
19
|
# - Routes (idempotent inject between sentinels)
|
|
20
|
+
# - Permission scope + label (idempotent inject; locale owned-once)
|
|
20
21
|
#
|
|
21
22
|
# Owned-once contract: if the model file already exists, the generator
|
|
22
23
|
# leaves it (and the migration) alone — domain code is yours after
|
|
@@ -59,6 +60,11 @@ module Spree
|
|
|
59
60
|
default: false,
|
|
60
61
|
desc: "Don't inject routes into spree/api/config/routes.rb"
|
|
61
62
|
|
|
63
|
+
class_option :permission_group,
|
|
64
|
+
type: :string,
|
|
65
|
+
default: 'catalog',
|
|
66
|
+
desc: 'Permission picker group for the read_/write_ keys (orders, catalog, customers, settings, …)'
|
|
67
|
+
|
|
62
68
|
class_option :skip_specs,
|
|
63
69
|
type: :boolean,
|
|
64
70
|
default: false,
|
|
@@ -76,7 +82,7 @@ module Spree
|
|
|
76
82
|
# the second run with "Spree::Brand is already used in your application".
|
|
77
83
|
class_option :skip_collision_check, type: :boolean, default: true, desc: false
|
|
78
84
|
|
|
79
|
-
desc '
|
|
85
|
+
desc 'Creates a new Spree API resource'
|
|
80
86
|
|
|
81
87
|
# --- Owned-once gating ---
|
|
82
88
|
#
|
|
@@ -171,8 +177,10 @@ module Spree
|
|
|
171
177
|
|
|
172
178
|
routes_file = api_routes_path
|
|
173
179
|
|
|
180
|
+
# An installed gem is read-only, which is the normal case: a host app
|
|
181
|
+
# declares its own routes instead, through the engine's route hook.
|
|
174
182
|
unless File.exist?(routes_file) && File.writable?(routes_file)
|
|
175
|
-
|
|
183
|
+
draw_app_routes
|
|
176
184
|
return
|
|
177
185
|
end
|
|
178
186
|
|
|
@@ -180,11 +188,96 @@ module Spree
|
|
|
180
188
|
inject_route_for(:admin, admin_route_line) if options[:admin]
|
|
181
189
|
end
|
|
182
190
|
|
|
191
|
+
# The Admin controller declares `scoped_resource :<plural>`; without a
|
|
192
|
+
# matching catalog scope its keys cannot be granted to a staff role or
|
|
193
|
+
# minted on a secret API key, so only full-access principals get in.
|
|
194
|
+
def register_permission_scope
|
|
195
|
+
return unless options[:admin]
|
|
196
|
+
|
|
197
|
+
if File.exist?(File.join(destination_root, INITIALIZER_PATH))
|
|
198
|
+
if File.read(File.join(destination_root, INITIALIZER_PATH)).include?("register_scope(:#{plural_name},")
|
|
199
|
+
say_status :identical, "#{INITIALIZER_PATH} (#{plural_name} permission scope)", :blue
|
|
200
|
+
else
|
|
201
|
+
append_to_file INITIALIZER_PATH, permission_scope_block
|
|
202
|
+
end
|
|
203
|
+
else
|
|
204
|
+
create_file INITIALIZER_PATH, permission_scope_block.lstrip
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def create_permission_locale
|
|
209
|
+
return unless options[:admin]
|
|
210
|
+
|
|
211
|
+
path = "config/locales/spree_#{plural_name}.en.yml"
|
|
212
|
+
if File.exist?(File.join(destination_root, path))
|
|
213
|
+
say_status :skip, "#{path} (owned-once; already exists)", :yellow
|
|
214
|
+
return
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
template 'permissions.en.yml.tt', path
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
no_tasks do
|
|
221
|
+
# A host app declares its API routes in config/routes.rb, inside the
|
|
222
|
+
# engine's route hook that spree-starter ships ready to fill in. The
|
|
223
|
+
# block must be registered before the engine draws its routes, which is
|
|
224
|
+
# why it lives in routes.rb rather than an initializer.
|
|
225
|
+
def draw_app_routes
|
|
226
|
+
path = 'config/routes.rb'
|
|
227
|
+
full = File.join(destination_root, path)
|
|
228
|
+
|
|
229
|
+
unless File.exist?(full)
|
|
230
|
+
say_status :skip, "#{path} not found — add the routes by hand", :yellow
|
|
231
|
+
return
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
unless File.read(full).include?('Spree::Core::Engine.add_routes')
|
|
235
|
+
say_status :skip, "#{path} has no `Spree::Core::Engine.add_routes` block — add the routes by hand", :yellow
|
|
236
|
+
return
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
append_app_route(path, :store, store_route_line.strip) if options[:store]
|
|
240
|
+
append_app_route(path, :admin, admin_route_line.strip) if options[:admin]
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# Inserts one `resources` line into the named namespace. Idempotent: a
|
|
244
|
+
# resource already declared there is left alone, so re-runs are safe.
|
|
245
|
+
def append_app_route(path, namespace, route_line)
|
|
246
|
+
full = File.join(destination_root, path)
|
|
247
|
+
content = File.read(full)
|
|
248
|
+
|
|
249
|
+
# Confined to the engine's route hook: an application may well have its
|
|
250
|
+
# own `namespace :admin` earlier in the file, and a route inserted there
|
|
251
|
+
# would never be drawn on the engine.
|
|
252
|
+
hook = content[/^(\s*)Spree::Core::Engine\.add_routes do\n.*?^\1end\n/m]
|
|
253
|
+
unless hook
|
|
254
|
+
say_status :skip, "#{path} (no `Spree::Core::Engine.add_routes` block — add `#{route_line}` by hand)", :yellow
|
|
255
|
+
return
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
match = hook.match(/^(\s*)namespace :#{namespace} do\n/)
|
|
259
|
+
unless match
|
|
260
|
+
say_status :skip, "#{path} (no :#{namespace} namespace — add `#{route_line}` by hand)", :yellow
|
|
261
|
+
return
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
block = hook[match.end(0)..].to_s[/\A.*?^#{match[1]}end$/m].to_s
|
|
265
|
+
if block.include?(route_line)
|
|
266
|
+
say_status :identical, "#{path} (#{namespace}: #{route_line})", :blue
|
|
267
|
+
return
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
updated_hook = hook.sub(match[0], "#{match[0]}#{match[1]} #{route_line}\n")
|
|
271
|
+
File.write(full, content.sub(hook, updated_hook))
|
|
272
|
+
say_status :route, "#{path} (#{namespace}: #{route_line})", :green
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
|
|
183
276
|
def print_summary
|
|
184
277
|
say ''
|
|
185
278
|
say "✓ Generated Spree::#{bare_class_name} API resource", :green
|
|
186
279
|
say ''
|
|
187
|
-
say " Prefixed ID: #{id_prefix}_xxxxxxxxxx
|
|
280
|
+
say " Prefixed ID: #{id_prefix}_xxxxxxxxxx"
|
|
188
281
|
if store_external_name != bare_class_name
|
|
189
282
|
say " Store API: /api/v3/store/#{store_external_plural} (aliased from #{bare_class_name})"
|
|
190
283
|
elsif options[:store]
|
|
@@ -195,10 +288,14 @@ module Spree
|
|
|
195
288
|
say ' Next steps:', :yellow
|
|
196
289
|
say ' 1. Review the generated model — add validations, scopes, callbacks'
|
|
197
290
|
say ' 2. Apply the migration: pnpm exec spree migrate'
|
|
198
|
-
|
|
199
|
-
|
|
291
|
+
if options[:admin]
|
|
292
|
+
say " 3. Grant read_#{plural_name} / write_#{plural_name} to staff roles or secret API keys"
|
|
293
|
+
else
|
|
294
|
+
say ' 3. Add an Admin API resource later to manage it from the dashboard'
|
|
295
|
+
end
|
|
296
|
+
say " 4. Add `has_many :#{plural_name}` to Spree::Store" if store_scoped?
|
|
200
297
|
if options[:store] || options[:admin]
|
|
201
|
-
say
|
|
298
|
+
say " #{store_scoped? ? 5 : 4}. Run the specs: pnpm exec spree rspec spec/controllers/spree/api/v3/"
|
|
202
299
|
end
|
|
203
300
|
say ''
|
|
204
301
|
end
|
|
@@ -237,6 +334,13 @@ module Spree
|
|
|
237
334
|
file_name.pluralize
|
|
238
335
|
end
|
|
239
336
|
|
|
337
|
+
def permission_group
|
|
338
|
+
group = options[:permission_group].to_s
|
|
339
|
+
raise Thor::Error, "--permission-group must be a lowercase identifier (got #{group.inspect})" unless group.match?(/\A[a-z][a-z0-9_]*\z/)
|
|
340
|
+
|
|
341
|
+
group
|
|
342
|
+
end
|
|
343
|
+
|
|
240
344
|
# Attributes the controller permits on write. By default, every column
|
|
241
345
|
# except references (FKs come through nested), attachments, rich text.
|
|
242
346
|
def permitted_attribute_names
|
|
@@ -248,6 +352,17 @@ module Spree
|
|
|
248
352
|
|
|
249
353
|
private
|
|
250
354
|
|
|
355
|
+
INITIALIZER_PATH = 'config/initializers/spree.rb'.freeze
|
|
356
|
+
|
|
357
|
+
def permission_scope_block
|
|
358
|
+
<<~RUBY
|
|
359
|
+
|
|
360
|
+
# Permissions for the #{bare_class_name} API — grants read_#{plural_name} / write_#{plural_name}
|
|
361
|
+
# to staff roles and secret API keys. Labels: config/locales/spree_#{plural_name}.en.yml
|
|
362
|
+
Spree.permissions.register_scope(:#{plural_name}, group: :#{permission_group}, resources: -> { [Spree::#{bare_class_name}] })
|
|
363
|
+
RUBY
|
|
364
|
+
end
|
|
365
|
+
|
|
251
366
|
# Where the model file lands. We use parent's class_path + file_name so
|
|
252
367
|
# this stays in sync with whatever Spree::ModelGenerator decides.
|
|
253
368
|
def model_file_destination
|
|
@@ -3,6 +3,12 @@ module Spree
|
|
|
3
3
|
module V3
|
|
4
4
|
module Admin
|
|
5
5
|
class <%= plural_name.camelize %>Controller < ResourceController
|
|
6
|
+
# The permission that authorizes this endpoint — `read_<%= plural_name %>` and
|
|
7
|
+
# `write_<%= plural_name %>`, registered in config/initializers/spree.rb so
|
|
8
|
+
# they can be granted to staff roles and secret API keys. Point it at an
|
|
9
|
+
# existing resource name instead if this endpoint belongs under one.
|
|
10
|
+
scoped_resource :<%= plural_name %>
|
|
11
|
+
|
|
6
12
|
protected
|
|
7
13
|
|
|
8
14
|
def model_class
|
|
@@ -13,8 +19,8 @@ module Spree
|
|
|
13
19
|
Spree::Api::V3::Admin::<%= bare_class_name %>Serializer
|
|
14
20
|
end
|
|
15
21
|
|
|
16
|
-
def
|
|
17
|
-
|
|
22
|
+
def resource_permitted_attributes
|
|
23
|
+
[<%= permitted_attribute_names.map { |a| ":#{a}" }.join(', ') %>]
|
|
18
24
|
end
|
|
19
25
|
end
|
|
20
26
|
end
|
|
@@ -2,6 +2,9 @@ require 'spec_helper'
|
|
|
2
2
|
|
|
3
3
|
RSpec.describe Spree::Api::V3::Admin::<%= plural_name.camelize %>Controller, type: :controller do
|
|
4
4
|
render_views
|
|
5
|
+
# The controller is mounted by the engine, so its paths resolve
|
|
6
|
+
# through the engine's router rather than the application's.
|
|
7
|
+
routes { Spree::Core::Engine.routes }
|
|
5
8
|
|
|
6
9
|
include_context 'API v3 Admin authenticated'
|
|
7
10
|
|
|
@@ -20,8 +20,8 @@ module Spree
|
|
|
20
20
|
<% end -%>
|
|
21
21
|
<% if writable? -%>
|
|
22
22
|
|
|
23
|
-
def
|
|
24
|
-
|
|
23
|
+
def resource_permitted_attributes
|
|
24
|
+
[<%= permitted_attribute_names.map { |a| ":#{a}" }.join(', ') %>]
|
|
25
25
|
end
|
|
26
26
|
<% end -%>
|
|
27
27
|
end
|
|
@@ -2,8 +2,11 @@ require 'spec_helper'
|
|
|
2
2
|
|
|
3
3
|
RSpec.describe Spree::Api::V3::Store::<%= plural_name.camelize %>Controller, type: :controller do
|
|
4
4
|
render_views
|
|
5
|
+
# The controller is mounted by the engine, so its paths resolve
|
|
6
|
+
# through the engine's router rather than the application's.
|
|
7
|
+
routes { Spree::Core::Engine.routes }
|
|
5
8
|
|
|
6
|
-
include_context 'API v3 Store'
|
|
9
|
+
include_context 'API v3 Store guest'
|
|
7
10
|
|
|
8
11
|
let!(:<%= singular_name %>) { create(:<%= singular_name %>) }
|
|
9
12
|
|
|
@@ -43,5 +43,12 @@ Dummy::Application.configure do
|
|
|
43
43
|
|
|
44
44
|
config.cache_store = :null_store
|
|
45
45
|
|
|
46
|
+
# Spree encrypts secrets at rest (webhook signing keys, OAuth tokens) only when
|
|
47
|
+
# Active Record encryption is configured, so the suite configures it like a
|
|
48
|
+
# production app would.
|
|
49
|
+
config.active_record.encryption.primary_key = 'spree-test-primary-key'
|
|
50
|
+
config.active_record.encryption.deterministic_key = 'spree-test-deterministic-key'
|
|
51
|
+
config.active_record.encryption.key_derivation_salt = 'spree-test-key-derivation-salt'
|
|
52
|
+
|
|
46
53
|
routes.default_url_options = { host: 'localhost', port: 3000 }
|
|
47
54
|
end
|
|
@@ -25,7 +25,24 @@ module Spree
|
|
|
25
25
|
default: false,
|
|
26
26
|
desc: 'Include Spree::HasCustomFields and Spree::Metadata concerns'
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
# On by default so every new resource is observable from the moment it
|
|
29
|
+
# exists — subscribers and webhooks both hang off these events, and a
|
|
30
|
+
# model that publishes nothing is invisible to integrations.
|
|
31
|
+
class_option :lifecycle_events,
|
|
32
|
+
type: :boolean,
|
|
33
|
+
default: true,
|
|
34
|
+
desc: 'Publish created/updated/deleted events'
|
|
35
|
+
|
|
36
|
+
# Store scoping is the default because commerce records belong to a store:
|
|
37
|
+
# catalog, configuration and orders are all per-store. Opt out with
|
|
38
|
+
# --no-store-scoped only for genuinely global reference data (countries,
|
|
39
|
+
# states, roles), which is rare enough to be the explicit case.
|
|
40
|
+
class_option :store_scoped,
|
|
41
|
+
type: :boolean,
|
|
42
|
+
default: true,
|
|
43
|
+
desc: 'Scope the model to a store'
|
|
44
|
+
|
|
45
|
+
desc 'Creates a new Spree model'
|
|
29
46
|
|
|
30
47
|
def create_module_file
|
|
31
48
|
return
|
|
@@ -44,6 +61,28 @@ module Spree
|
|
|
44
61
|
options[:custom_fields]
|
|
45
62
|
end
|
|
46
63
|
|
|
64
|
+
def lifecycle_events?
|
|
65
|
+
options[:lifecycle_events]
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Whether the record belongs to a store, however that reference got
|
|
69
|
+
# there: the concern below, or a `store:references` the caller passed.
|
|
70
|
+
# Uniqueness and indexes key off this, so a value stays unique within a
|
|
71
|
+
# store rather than across the whole installation.
|
|
72
|
+
def store_scoped?
|
|
73
|
+
options[:store_scoped] || declares_store_reference?
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# The concern is what adds `belongs_to :store`, so a model that already
|
|
77
|
+
# declares one must not include it as well.
|
|
78
|
+
def includes_store_concern?
|
|
79
|
+
options[:store_scoped] && !declares_store_reference?
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def declares_store_reference?
|
|
83
|
+
attributes.any? { |attribute| attribute.name == 'store' }
|
|
84
|
+
end
|
|
85
|
+
|
|
47
86
|
def class_path
|
|
48
87
|
['spree']
|
|
49
88
|
end
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
|
2
2
|
def change
|
|
3
3
|
create_table :<%= table_name %><%= primary_key_type %> do |t|
|
|
4
|
+
<% if includes_store_concern? -%>
|
|
5
|
+
t.references :store, null: false, index: true, foreign_key: false
|
|
6
|
+
|
|
7
|
+
<% end -%>
|
|
4
8
|
<% attributes.reject(&:reference?).reject(&:virtual?).each do |attribute| -%>
|
|
5
9
|
<% if attribute.password_digest? -%>
|
|
6
10
|
t.string :password_digest, null: false
|
|
@@ -27,9 +31,14 @@ class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Mi
|
|
|
27
31
|
name: 'index_<%= table_name %>_on_<%= attribute.name %>'
|
|
28
32
|
<% end -%>
|
|
29
33
|
<% attributes.select(&:has_uniq_index?).reject(&:token?).each do |attribute| -%>
|
|
34
|
+
<% if store_scoped? -%>
|
|
35
|
+
add_index :<%= table_name %>, [:store_id, :<%= attribute.name %>], unique: true,
|
|
36
|
+
name: 'index_<%= table_name %>_on_store_id_and_<%= attribute.name %>'
|
|
37
|
+
<% else -%>
|
|
30
38
|
add_index :<%= table_name %>, :<%= attribute.name %>, unique: true,
|
|
31
39
|
name: 'index_<%= table_name %>_on_<%= attribute.name %>'
|
|
32
40
|
<% end -%>
|
|
41
|
+
<% end -%>
|
|
33
42
|
<% attributes.select { |a| a.has_index? && !a.has_uniq_index? && !a.reference? && !a.token? }.each do |attribute| -%>
|
|
34
43
|
add_index :<%= table_name %>, :<%= attribute.name %>
|
|
35
44
|
<% end -%>
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
module Spree
|
|
2
2
|
class <%= class_name.demodulize %> < <%= parent_class_name %>
|
|
3
|
+
<% if includes_store_concern? -%>
|
|
4
|
+
include Spree::SingleStoreResource
|
|
5
|
+
|
|
6
|
+
<% end -%>
|
|
3
7
|
<% if custom_fields? -%>
|
|
4
8
|
include Spree::HasCustomFields
|
|
5
9
|
include Spree::Metadata
|
|
@@ -10,7 +14,9 @@ module Spree
|
|
|
10
14
|
|
|
11
15
|
<% end -%>
|
|
12
16
|
has_prefix_id :<%= id_prefix %>
|
|
13
|
-
|
|
17
|
+
<% if lifecycle_events? -%>
|
|
18
|
+
publishes_lifecycle_events
|
|
19
|
+
<% end -%>
|
|
14
20
|
<% attributes.select(&:reference?).each do |attribute| -%>
|
|
15
21
|
<% if attribute.polymorphic? -%>
|
|
16
22
|
belongs_to :<%= attribute.name %>, polymorphic: true
|
|
@@ -38,10 +44,10 @@ module Spree
|
|
|
38
44
|
has_secure_password
|
|
39
45
|
<% end -%>
|
|
40
46
|
<% required_attrs = attributes.reject { |a| a.reference? || a.type == :boolean || a.token? || a.attachment? || a.attachments? || a.rich_text? || a.password_digest? } -%>
|
|
41
|
-
<% if required_attrs.any?
|
|
42
|
-
<% required_attrs.each do |attr| -%>
|
|
43
|
-
validates :<%= attr.name %>, presence: true<% if attr.has_uniq_index? %>, uniqueness: { scope: spree_base_uniqueness_scope }<% end %>
|
|
47
|
+
<% if required_attrs.any? -%>
|
|
44
48
|
|
|
49
|
+
<% required_attrs.each do |attr| -%>
|
|
50
|
+
validates :<%= attr.name %>, presence: true<% if attr.has_uniq_index? %>, uniqueness: { scope: [<%= ':store_id, ' if store_scoped? %>*spree_base_uniqueness_scope] }<% end %>
|
|
45
51
|
<% end -%>
|
|
46
52
|
<% end -%>
|
|
47
53
|
<% ransackable = attributes.reject { |a| a.reference? || a.attachment? || a.attachments? || a.rich_text? || a.token? || a.password_digest? }.map(&:name) -%>
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
module Spree
|
|
4
4
|
# spree:subscriber — scaffold an event subscriber and register it.
|
|
5
5
|
#
|
|
6
|
-
# bin/rails g spree:subscriber OmsOrderSync order.
|
|
6
|
+
# bin/rails g spree:subscriber OmsOrderSync order.placed order.canceled
|
|
7
7
|
# bin/rails g spree:subscriber MyApp::BrandSync brand.created brand.updated
|
|
8
|
-
# bin/rails g spree:subscriber CriticalSync order.
|
|
8
|
+
# bin/rails g spree:subscriber CriticalSync order.placed --sync
|
|
9
9
|
#
|
|
10
10
|
# Subscribers are not auto-discovered — they must be appended to the
|
|
11
11
|
# Spree.subscribers array. Forgetting that step produces a silent no-op,
|
|
@@ -64,7 +64,7 @@ module Spree
|
|
|
64
64
|
def warn_about_missing_events
|
|
65
65
|
return if events.any?
|
|
66
66
|
|
|
67
|
-
say_status :note, "no events given — edit `subscribes_to` in the generated subscriber (e.g. 'order.
|
|
67
|
+
say_status :note, "no events given — edit `subscribes_to` in the generated subscriber (e.g. 'order.placed')", :yellow
|
|
68
68
|
end
|
|
69
69
|
|
|
70
70
|
private
|
|
@@ -40,23 +40,23 @@ module Spree
|
|
|
40
40
|
# case seller invitations fall back to the dashboard origin.
|
|
41
41
|
preference :seller_panel_url, :string, default: nil, env: 'SPREE_SELLER_PANEL_URL'
|
|
42
42
|
preference :allow_checkout_on_gateway_error, :boolean, default: false, deprecated: 'Nothing reads this in Spree 6 — completion checks whether payments cover the total, so a failed gateway call never completes an order'
|
|
43
|
-
preference :allow_empty_price_amount, :boolean, default: false
|
|
43
|
+
preference :allow_empty_price_amount, :boolean, default: false, env: 'SPREE_ALLOW_EMPTY_PRICE_AMOUNT'
|
|
44
44
|
preference :alternative_shipping_phone, :boolean, default: false, deprecated: 'Nothing reads this in Spree 6'
|
|
45
|
-
preference :always_include_confirm_step, :boolean, default: false
|
|
46
|
-
preference :always_use_translations, :boolean, default: false
|
|
45
|
+
preference :always_include_confirm_step, :boolean, default: false, deprecated: 'Use the always_include_confirm_step preference in the Spree::Store model'
|
|
46
|
+
preference :always_use_translations, :boolean, default: false, env: 'SPREE_ALWAYS_USE_TRANSLATIONS'
|
|
47
47
|
preference :auto_capture, :boolean, default: true, deprecated: 'Set it on the store instead' # automatically capture the credit card (as opposed to just authorize and capture later)
|
|
48
48
|
preference :auto_capture_on_dispatch, :boolean, default: false, deprecated: 'Set it on the store instead' # Captures payment on dispatch rather than at checkout.
|
|
49
49
|
preference :company, :boolean, default: false, deprecated: 'Use the company_field_enabled preference in the Spree::Store model' # Request company field for billing and shipping addr
|
|
50
|
-
preference :credit_to_new_allocation, :boolean, default: false
|
|
51
|
-
preference :disable_migration_check, :boolean, default: false # when turned on disables the startup warning about missing engine migrations
|
|
50
|
+
preference :credit_to_new_allocation, :boolean, default: false, env: 'SPREE_CREDIT_TO_NEW_ALLOCATION'
|
|
51
|
+
preference :disable_migration_check, :boolean, default: false, env: 'SPREE_DISABLE_MIGRATION_CHECK' # when turned on disables the startup warning about missing engine migrations
|
|
52
52
|
preference :disable_sku_validation, :boolean, default: false, deprecated: 'Set it on the store instead' # when turned on disables the built-in SKU uniqueness validation
|
|
53
53
|
preference :disable_store_presence_validation, :boolean, default: false, deprecated: true # when turned off disables Store presence validation for Products and Payment Methods
|
|
54
|
-
preference :events_log_enabled, :boolean, default: true # Log all Spree events to Rails logger
|
|
54
|
+
preference :events_log_enabled, :boolean, default: true, env: 'SPREE_EVENTS_LOG_ENABLED' # Log all Spree events to Rails logger
|
|
55
55
|
preference :expedited_exchanges, :boolean, default: false, deprecated: 'Exchanges are their own record in Spree 6 — see Spree::Exchange and the Exchanges::Fulfill workflow'
|
|
56
56
|
preference :expedited_exchanges_days_window, :integer, default: 14, deprecated: 'Exchanges are their own record in Spree 6 — see Spree::Exchange and the Exchanges::Fulfill workflow'
|
|
57
|
-
preference :geocode_addresses, :boolean, default: true
|
|
58
|
-
preference :images_save_from_url_job_attempts, :integer, default: 5
|
|
59
|
-
preference :max_image_download_size, :integer, default: 20_971_520 # 20 MB in bytes
|
|
57
|
+
preference :geocode_addresses, :boolean, default: true, env: 'SPREE_GEOCODE_ADDRESSES'
|
|
58
|
+
preference :images_save_from_url_job_attempts, :integer, default: 5, env: 'SPREE_IMAGES_SAVE_FROM_URL_JOB_ATTEMPTS'
|
|
59
|
+
preference :max_image_download_size, :integer, default: 20_971_520, env: 'SPREE_MAX_IMAGE_DOWNLOAD_SIZE' # 20 MB in bytes
|
|
60
60
|
|
|
61
61
|
# Preprocessed product image variant sizes at 2x retina resolution.
|
|
62
62
|
# These variants are generated on upload to reduce runtime processing.
|
|
@@ -113,12 +113,12 @@ module Spree
|
|
|
113
113
|
end
|
|
114
114
|
|
|
115
115
|
# Maximum size of an uploaded product video, in bytes.
|
|
116
|
-
preference :max_video_upload_size, :integer, default: 524_288_000 # 500 MB
|
|
116
|
+
preference :max_video_upload_size, :integer, default: 524_288_000, env: 'SPREE_MAX_VIDEO_UPLOAD_SIZE' # 500 MB
|
|
117
117
|
|
|
118
118
|
# Maximum size of a document a seller uploads for an onboarding
|
|
119
119
|
# requirement, in bytes. Small on purpose: these are certificates and
|
|
120
120
|
# registrations, and an unbounded upload is a way to fill a disk.
|
|
121
|
-
preference :max_seller_document_upload_size, :integer, default: 20_971_520 # 20 MB
|
|
121
|
+
preference :max_seller_document_upload_size, :integer, default: 20_971_520, env: 'SPREE_MAX_SELLER_DOCUMENT_UPLOAD_SIZE' # 20 MB
|
|
122
122
|
preference :non_expiring_credit_types, :array, default: [], deprecated: 'Nothing reads this in Spree 6 — store credits no longer carry a category, and expiry lives on Spree::GiftCard'
|
|
123
123
|
preference :products_per_page, :integer, default: 12, deprecated: 'Nothing reads this in Spree 6 — pass per_page to the API instead'
|
|
124
124
|
preference :restock_inventory, :boolean, default: true, deprecated: 'Restocking is decided per line item by Spree::ReturnLineItem#resellable'
|
|
@@ -132,9 +132,9 @@ module Spree
|
|
|
132
132
|
# into failures (what this repo's API test suite runs); 'off' disables.
|
|
133
133
|
preference :store_scope_guard, :string, default: 'log', env: 'SPREE_STORE_SCOPE_GUARD'
|
|
134
134
|
# Tiered cart-expiry reaper (docs/plans/6.0-cart-order-split.md Decision 5)
|
|
135
|
-
preference :guest_cart_expiry_days, :integer, default: 30
|
|
136
|
-
preference :customer_cart_expiry_days, :integer, default: 90
|
|
137
|
-
preference :empty_cart_expiry_hours, :integer, default: 48
|
|
135
|
+
preference :guest_cart_expiry_days, :integer, default: 30, env: 'SPREE_GUEST_CART_EXPIRY_DAYS'
|
|
136
|
+
preference :customer_cart_expiry_days, :integer, default: 90, env: 'SPREE_CUSTOMER_CART_EXPIRY_DAYS'
|
|
137
|
+
preference :empty_cart_expiry_hours, :integer, default: 48, env: 'SPREE_EMPTY_CART_EXPIRY_HOURS'
|
|
138
138
|
preference :show_variant_full_price, :boolean, default: false, deprecated: 'Nothing reads this in Spree 6 — a storefront decides how it renders prices'
|
|
139
139
|
preference :show_products_without_price, :boolean, default: false, deprecated: 'Set it on the store instead'
|
|
140
140
|
preference :tax_using_ship_address, :boolean, default: true, deprecated: 'Set it on the store instead'
|
|
@@ -149,33 +149,33 @@ module Spree
|
|
|
149
149
|
preference :storefront_pages_path, :string, default: 'pages', deprecated: 'Nothing reads this in Spree 6 — storefront routes are owned by the storefront'
|
|
150
150
|
|
|
151
151
|
# coupon codes
|
|
152
|
-
preference :coupon_codes_web_limit, :integer, default: 500 # number of coupon codes to be generated in the web process, more than this will be generated in a background job
|
|
153
|
-
preference :coupon_codes_total_limit, :integer, default: 5000 # the maximum number of coupon codes to be generated
|
|
152
|
+
preference :coupon_codes_web_limit, :integer, default: 500, env: 'SPREE_COUPON_CODES_WEB_LIMIT' # number of coupon codes to be generated in the web process, more than this will be generated in a background job
|
|
153
|
+
preference :coupon_codes_total_limit, :integer, default: 5000, env: 'SPREE_COUPON_CODES_TOTAL_LIMIT' # the maximum number of coupon codes to be generated
|
|
154
154
|
|
|
155
155
|
# password reset
|
|
156
|
-
preference :admin_password_reset_expires_in, :integer, default: 15 # admin password reset token expiration time in minutes
|
|
157
|
-
preference :customer_password_reset_expires_in, :integer, default: 15 # password reset token expiration time in minutes
|
|
156
|
+
preference :admin_password_reset_expires_in, :integer, default: 15, env: 'SPREE_ADMIN_PASSWORD_RESET_EXPIRES_IN' # admin password reset token expiration time in minutes
|
|
157
|
+
preference :customer_password_reset_expires_in, :integer, default: 15, env: 'SPREE_CUSTOMER_PASSWORD_RESET_EXPIRES_IN' # password reset token expiration time in minutes
|
|
158
158
|
|
|
159
159
|
# account lockout
|
|
160
|
-
preference :max_failed_login_attempts, :integer, default: 5 # failed login attempts before an account is locked
|
|
161
|
-
preference :lockout_duration, :integer, default: 1800 # lockout duration in seconds (30 minutes)
|
|
160
|
+
preference :max_failed_login_attempts, :integer, default: 5, env: 'SPREE_MAX_FAILED_LOGIN_ATTEMPTS' # failed login attempts before an account is locked
|
|
161
|
+
preference :lockout_duration, :integer, default: 1800, env: 'SPREE_LOCKOUT_DURATION' # lockout duration in seconds (30 minutes)
|
|
162
162
|
|
|
163
163
|
# password policy
|
|
164
164
|
# NIST 800-63B recommends a length floor with no composition rules (no forced
|
|
165
165
|
# symbols/digits, which push users toward predictable substitutions).
|
|
166
|
-
preference :minimum_password_length, :integer, default: 8
|
|
166
|
+
preference :minimum_password_length, :integer, default: 8, env: 'SPREE_MINIMUM_PASSWORD_LENGTH'
|
|
167
167
|
# bcrypt silently truncates past 72 bytes — without a cap a long passphrase and
|
|
168
168
|
# its 72-byte prefix are the same password. A correctness guard, not policy.
|
|
169
|
-
preference :maximum_password_length, :integer, default: ActiveModel::SecurePassword::MAX_PASSWORD_LENGTH_ALLOWED
|
|
169
|
+
preference :maximum_password_length, :integer, default: ActiveModel::SecurePassword::MAX_PASSWORD_LENGTH_ALLOWED, env: 'SPREE_MAXIMUM_PASSWORD_LENGTH'
|
|
170
170
|
# To replace the policy itself, assign Spree.password_validator — a class,
|
|
171
171
|
# not a preference.
|
|
172
172
|
|
|
173
173
|
# gift cards
|
|
174
|
-
preference :gift_card_batch_web_limit, :integer, default: 500 # number of gift card codes to be generated in the web process, more than this will be generated in a background job
|
|
175
|
-
preference :gift_card_batch_limit, :integer, default: 50_000
|
|
174
|
+
preference :gift_card_batch_web_limit, :integer, default: 500, env: 'SPREE_GIFT_CARD_BATCH_WEB_LIMIT' # number of gift card codes to be generated in the web process, more than this will be generated in a background job
|
|
175
|
+
preference :gift_card_batch_limit, :integer, default: 50_000, env: 'SPREE_GIFT_CARD_BATCH_LIMIT'
|
|
176
176
|
|
|
177
177
|
# imports
|
|
178
|
-
preference :large_import_threshold, :integer, default: 500 # imports with more rows than this skip per-row UI broadcasts and use bulk processing
|
|
178
|
+
preference :large_import_threshold, :integer, default: 500, env: 'SPREE_LARGE_IMPORT_THRESHOLD' # imports with more rows than this skip per-row UI broadcasts and use bulk processing
|
|
179
179
|
|
|
180
180
|
end
|
|
181
181
|
end
|
data/lib/spree/core/engine.rb
CHANGED
|
@@ -45,6 +45,7 @@ module Spree
|
|
|
45
45
|
:reports,
|
|
46
46
|
:translatable_resources,
|
|
47
47
|
:taggable_types,
|
|
48
|
+
:actor_classes,
|
|
48
49
|
:custom_fields,
|
|
49
50
|
:reporting,
|
|
50
51
|
:analytics_events,
|
|
@@ -88,6 +89,12 @@ module Spree
|
|
|
88
89
|
Spree::Deprecation = ActiveSupport::Deprecation.new('6.0', 'Spree')
|
|
89
90
|
end
|
|
90
91
|
|
|
92
|
+
# Runs after initializers so an explicitly assigned preference — which
|
|
93
|
+
# wins over the environment — is never rejected for a stale env var.
|
|
94
|
+
config.after_initialize do
|
|
95
|
+
Spree::Core::Configuration.validate_env!(Spree::Config)
|
|
96
|
+
end
|
|
97
|
+
|
|
91
98
|
# I18n's config lives in fiber/thread-local storage that survives across
|
|
92
99
|
# requests on reused server threads, so a request that never assigns its
|
|
93
100
|
# own locale would render in whatever locale the previous request on the
|
|
@@ -105,6 +112,13 @@ module Spree
|
|
|
105
112
|
app.config.spree.subscribers = []
|
|
106
113
|
end
|
|
107
114
|
|
|
115
|
+
# Seeded before application initializers so an extension registering an
|
|
116
|
+
# actor class has something to append to. The defaults are unioned in
|
|
117
|
+
# after initialization, where Spree.admin_user_class is finally known.
|
|
118
|
+
initializer 'spree.register.actor_classes', before: :load_config_initializers do |app|
|
|
119
|
+
app.config.spree.actor_classes = []
|
|
120
|
+
end
|
|
121
|
+
|
|
108
122
|
initializer 'spree.register.calculators', before: :after_initialize do |app|
|
|
109
123
|
end
|
|
110
124
|
|
|
@@ -121,6 +135,14 @@ module Spree
|
|
|
121
135
|
app.config.spree.line_item_comparison_hooks = Set.new
|
|
122
136
|
end
|
|
123
137
|
|
|
138
|
+
# The one eligibility rule core ships: a per-market return window,
|
|
139
|
+
# bypassed by staff. Registered before application initializers so a
|
|
140
|
+
# store can unregister it in its own initializer.
|
|
141
|
+
initializer 'spree.returns.register_eligibility_validator', before: :load_config_initializers do
|
|
142
|
+
Spree.hooks.register('returns.create.validate', 'Spree::Returns::EligibilityValidator')
|
|
143
|
+
Spree.hooks.register('exchanges.create.validate', 'Spree::Returns::EligibilityValidator')
|
|
144
|
+
end
|
|
145
|
+
|
|
124
146
|
initializer 'spree.register.payment_methods', after: 'acts_as_list.insert_into_active_record' do |app|
|
|
125
147
|
end
|
|
126
148
|
|
|
@@ -525,6 +547,19 @@ module Spree
|
|
|
525
547
|
Spree.customer_class.to_s
|
|
526
548
|
]
|
|
527
549
|
|
|
550
|
+
# Models that may be recorded as having performed an action — the
|
|
551
|
+
# vocabulary an `acted_by` association's `*_type` column is validated
|
|
552
|
+
# against. Extend in an app initializer to register an App or bot
|
|
553
|
+
# class, which must include Spree::Actor:
|
|
554
|
+
# Rails.application.config.spree.actor_classes << 'MyApp::App'.
|
|
555
|
+
#
|
|
556
|
+
# Unioned rather than assigned, so what an initializer registered
|
|
557
|
+
# above survives.
|
|
558
|
+
Rails.application.config.spree.actor_classes |= [
|
|
559
|
+
Spree.admin_user_class.to_s,
|
|
560
|
+
'Spree::ApiKey'
|
|
561
|
+
]
|
|
562
|
+
|
|
528
563
|
Rails.application.config.spree.custom_fields.types = [
|
|
529
564
|
Spree::CustomFields::ShortText,
|
|
530
565
|
Spree::CustomFields::LongText,
|
|
@@ -664,14 +699,6 @@ module Spree
|
|
|
664
699
|
end
|
|
665
700
|
end
|
|
666
701
|
|
|
667
|
-
# The one eligibility rule core ships: a per-market return window,
|
|
668
|
-
# bypassed by staff. Registered after application initializers so a
|
|
669
|
-
# store can unregister it in its own initializer.
|
|
670
|
-
initializer 'spree.returns.register_eligibility_validator', after: :load_config_initializers do
|
|
671
|
-
Spree.hooks.register('returns.create.validate', 'Spree::Returns::EligibilityValidator')
|
|
672
|
-
Spree.hooks.register('exchanges.create.validate', 'Spree::Returns::EligibilityValidator')
|
|
673
|
-
end
|
|
674
|
-
|
|
675
702
|
# A hook registered against a key no workflow declares would never fire
|
|
676
703
|
# and never say so. Checking after eager load turns that typo into a
|
|
677
704
|
# boot failure. Skipped when eager loading is off (development,
|