spree_core 6.0.0.beta2 → 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 +7 -2
- 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/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/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/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/claim.rb +1 -0
- 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 +15 -0
- data/app/models/spree/exchange.rb +1 -0
- 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/number_generators/sequential.rb +2 -2
- data/app/models/spree/option_value.rb +7 -0
- data/app/models/spree/order.rb +35 -12
- 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/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/return.rb +16 -6
- 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/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/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/complete.rb +99 -2
- data/app/workflows/spree/price_lists/update.rb +17 -0
- data/app/workflows/spree/refunds/order_payments.rb +55 -1
- data/app/workflows/spree/returns/create.rb +9 -15
- data/app/workflows/spree/returns/refund.rb +15 -24
- data/app/workflows/spree/returns/returnable_quantity.rb +53 -0
- data/app/workflows/spree/sellers/invite.rb +1 -1
- data/config/locales/en.yml +7 -119
- data/db/migrate/20260916000001_add_email_delivery_flags_to_spree_order_groups.rb +14 -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 +58 -1
- data/lib/generators/spree/api_resource/templates/admin_controller.rb.tt +6 -5
- 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/dummy/templates/rails/test.rb +7 -0
- data/lib/generators/spree/subscriber/subscriber_generator.rb +3 -3
- data/lib/spree/core/engine.rb +8 -8
- data/lib/spree/core/version.rb +1 -1
- 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/upgrades/5_6_to_6_0/manifest.yml +34 -0
- data/lib/tasks/sample_data.rake +8 -2
- data/lib/tasks/upgrade.rake +6 -21
- metadata +13 -4
|
@@ -4,65 +4,79 @@ module Spree
|
|
|
4
4
|
prepend Spree::ServiceModule::Base
|
|
5
5
|
include Spree::SampleData::Helper
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
# Loads the demo data into one store. The sample files and the imports
|
|
8
|
+
# they run read `Spree::Current.store`, so it is set for the whole run
|
|
9
|
+
# and restored afterwards.
|
|
10
|
+
#
|
|
11
|
+
# @param store [Spree::Store, nil] the store to load into; defaults to
|
|
12
|
+
# the default store, resolved after the seeds so a bare install works
|
|
13
|
+
# @return [Spree::ServiceModule::Result]
|
|
14
|
+
def call(store: nil)
|
|
8
15
|
Spree::Events.disable do
|
|
9
16
|
without_geocoding do
|
|
10
17
|
ActiveRecord::Base.no_touching do
|
|
11
18
|
puts 'Running seeds first...' unless seeds_loaded?
|
|
12
19
|
ensure_seeds_loaded
|
|
13
20
|
|
|
14
|
-
|
|
15
|
-
|
|
21
|
+
@store = store || Spree::Store.default
|
|
22
|
+
Spree::Current.set(store: @store) { load_sample_data }
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
16
27
|
|
|
17
|
-
|
|
18
|
-
load_ruby_file('channels')
|
|
28
|
+
private
|
|
19
29
|
|
|
20
|
-
|
|
21
|
-
load_ruby_file('custom_field_definitions')
|
|
30
|
+
attr_reader :store
|
|
22
31
|
|
|
23
|
-
|
|
24
|
-
|
|
32
|
+
def load_sample_data
|
|
33
|
+
puts 'Loading sample configuration data...'
|
|
34
|
+
load_configuration_data
|
|
25
35
|
|
|
26
|
-
|
|
27
|
-
|
|
36
|
+
puts 'Loading sample channels...'
|
|
37
|
+
load_ruby_file('channels')
|
|
28
38
|
|
|
29
|
-
|
|
30
|
-
|
|
39
|
+
puts 'Loading sample custom_field definitions...'
|
|
40
|
+
load_ruby_file('custom_field_definitions')
|
|
31
41
|
|
|
32
|
-
|
|
33
|
-
|
|
42
|
+
puts 'Loading sample options...'
|
|
43
|
+
load_ruby_file('options')
|
|
34
44
|
|
|
35
|
-
|
|
36
|
-
|
|
45
|
+
puts 'Loading sample product types...'
|
|
46
|
+
load_ruby_file('product_types')
|
|
37
47
|
|
|
38
|
-
|
|
39
|
-
|
|
48
|
+
puts 'Loading sample products...'
|
|
49
|
+
load_products
|
|
40
50
|
|
|
41
|
-
|
|
42
|
-
|
|
51
|
+
puts 'Attaching the sample digital product file...'
|
|
52
|
+
load_ruby_file('digital_products')
|
|
43
53
|
|
|
44
|
-
|
|
45
|
-
|
|
54
|
+
puts 'Publishing sample products on every channel...'
|
|
55
|
+
publish_sample_products(store)
|
|
46
56
|
|
|
47
|
-
|
|
48
|
-
|
|
57
|
+
puts 'Loading sample product translations...'
|
|
58
|
+
load_product_translations
|
|
49
59
|
|
|
50
|
-
|
|
51
|
-
|
|
60
|
+
puts 'Linking product types to categories...'
|
|
61
|
+
load_ruby_file('product_type_categories')
|
|
52
62
|
|
|
53
|
-
|
|
54
|
-
|
|
63
|
+
puts 'Loading sample collections...'
|
|
64
|
+
load_ruby_file('collections')
|
|
55
65
|
|
|
56
|
-
|
|
57
|
-
|
|
66
|
+
puts 'Loading sample customers...'
|
|
67
|
+
load_customers
|
|
58
68
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
end
|
|
69
|
+
puts 'Loading wholesale demo data...'
|
|
70
|
+
load_ruby_file('wholesale')
|
|
64
71
|
|
|
65
|
-
|
|
72
|
+
puts 'Loading sample orders...'
|
|
73
|
+
load_ruby_file('orders')
|
|
74
|
+
|
|
75
|
+
puts 'Loading sample posts...'
|
|
76
|
+
load_ruby_file('posts')
|
|
77
|
+
|
|
78
|
+
puts 'Sample data loaded successfully!'
|
|
79
|
+
end
|
|
66
80
|
|
|
67
81
|
def load_configuration_data
|
|
68
82
|
load_ruby_file('fulfillment')
|
|
@@ -72,19 +86,19 @@ module Spree
|
|
|
72
86
|
|
|
73
87
|
def load_products
|
|
74
88
|
csv_path = sample_data_path.join('products.csv')
|
|
75
|
-
Spree::SampleData::ImportRunner.call(csv_path: csv_path, import_class: Spree::Imports::Products, inline: true)
|
|
89
|
+
Spree::SampleData::ImportRunner.call(csv_path: csv_path, import_class: Spree::Imports::Products, store: store, inline: true)
|
|
76
90
|
end
|
|
77
91
|
|
|
78
92
|
def load_product_translations
|
|
79
93
|
csv_path = sample_data_path.join('product_translations.csv')
|
|
80
94
|
return unless csv_path.exist?
|
|
81
95
|
|
|
82
|
-
Spree::SampleData::ImportRunner.call(csv_path: csv_path, import_class: Spree::Imports::ProductTranslations, inline: true)
|
|
96
|
+
Spree::SampleData::ImportRunner.call(csv_path: csv_path, import_class: Spree::Imports::ProductTranslations, store: store, inline: true)
|
|
83
97
|
end
|
|
84
98
|
|
|
85
99
|
def load_customers
|
|
86
100
|
csv_path = sample_data_path.join('customers.csv')
|
|
87
|
-
Spree::SampleData::ImportRunner.call(csv_path: csv_path, import_class: Spree::Imports::Customers, inline: true)
|
|
101
|
+
Spree::SampleData::ImportRunner.call(csv_path: csv_path, import_class: Spree::Imports::Customers, store: store, inline: true)
|
|
88
102
|
end
|
|
89
103
|
end
|
|
90
104
|
end
|
|
@@ -10,39 +10,14 @@ module Spree
|
|
|
10
10
|
# countries gem, and zones are migration-only, so none of them
|
|
11
11
|
# are seeded.
|
|
12
12
|
|
|
13
|
-
# additional data
|
|
14
|
-
TaxCategories.call
|
|
15
|
-
|
|
16
|
-
# store & channels
|
|
17
13
|
Stores.call
|
|
18
|
-
|
|
19
|
-
# Roles are store-owned, so they can only be seeded once a store
|
|
20
|
-
# exists — every store gets its own immutable admin role.
|
|
21
|
-
Roles.call
|
|
22
|
-
# Store-scoped, so it can only run once a store exists.
|
|
23
|
-
DigitalDelivery.call
|
|
14
|
+
Spree::Store.find_each { |store| StoreResources.call(store: store) }
|
|
24
15
|
# The warehouse, delivery zones and pickup are not seeded: their
|
|
25
16
|
# shape depends on which country the shop sells from, and nobody
|
|
26
17
|
# has answered that yet. Spree::Stores::ProvisionDefaults builds
|
|
27
18
|
# them from the merchant's answer — at first-run setup, or here
|
|
28
19
|
# when ADMIN_EMAIL/ADMIN_PASSWORD name an install that skips it.
|
|
29
20
|
AdminUser.call
|
|
30
|
-
|
|
31
|
-
# add store resources
|
|
32
|
-
PaymentMethods.call
|
|
33
|
-
TaxCategories.call
|
|
34
|
-
ProductTypes.call
|
|
35
|
-
CustomerGroups.call
|
|
36
|
-
ReturnsEnvironment.call
|
|
37
|
-
# The marketplace's catch-all rate, at the bottom of the list so
|
|
38
|
-
# narrower rates added later resolve ahead of it.
|
|
39
|
-
CommissionRates.call
|
|
40
|
-
# What a new seller has to do before this marketplace will let
|
|
41
|
-
# them trade. Runs after stores for the same reason roles do.
|
|
42
|
-
SellerRequirements.call
|
|
43
|
-
ApiKeys.call
|
|
44
|
-
SavedReports.call
|
|
45
|
-
AllowedOrigins.call
|
|
46
21
|
end
|
|
47
22
|
end
|
|
48
23
|
end
|
|
@@ -2,11 +2,12 @@ module Spree
|
|
|
2
2
|
module Seeds
|
|
3
3
|
class AllowedOrigins
|
|
4
4
|
prepend Spree::ServiceModule::Base
|
|
5
|
+
include StoreScoped
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
def seed(store)
|
|
10
|
+
store.allowed_origins.find_or_create_by!(origin: 'http://localhost')
|
|
10
11
|
end
|
|
11
12
|
end
|
|
12
13
|
end
|
|
@@ -2,17 +2,18 @@ module Spree
|
|
|
2
2
|
module Seeds
|
|
3
3
|
class ApiKeys
|
|
4
4
|
prepend Spree::ServiceModule::Base
|
|
5
|
+
include StoreScoped
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
Spree::Store.find_each do |store|
|
|
8
|
-
unless store.api_keys.active.publishable.where(channel_id: nil).exists?
|
|
9
|
-
store.api_keys.create!(name: 'Default', key_type: 'publishable')
|
|
10
|
-
end
|
|
7
|
+
private
|
|
11
8
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
def seed(store)
|
|
10
|
+
unless store.api_keys.active.publishable.where(channel_id: nil).exists?
|
|
11
|
+
store.api_keys.create!(name: 'Default', key_type: 'publishable')
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
wholesale = store.channels.find_by(code: Channels::WHOLESALE_CODE)
|
|
15
|
+
if wholesale && !store.api_keys.active.publishable.where(channel: wholesale).exists?
|
|
16
|
+
store.api_keys.create!(name: 'Storefront (Wholesale)', key_type: 'publishable', channel: wholesale)
|
|
16
17
|
end
|
|
17
18
|
end
|
|
18
19
|
end
|
|
@@ -6,18 +6,19 @@ module Spree
|
|
|
6
6
|
# See docs/plans/5.6-store-channel-context-and-key-binding.md.
|
|
7
7
|
class Channels
|
|
8
8
|
prepend Spree::ServiceModule::Base
|
|
9
|
+
include StoreScoped
|
|
9
10
|
|
|
10
11
|
WHOLESALE_CODE = 'wholesale'.freeze
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
Spree::Store.find_each do |store|
|
|
14
|
-
store.ensure_default_channel
|
|
13
|
+
private
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
15
|
+
def seed(store)
|
|
16
|
+
store.ensure_default_channel
|
|
17
|
+
|
|
18
|
+
store.channels.find_or_create_by!(code: WHOLESALE_CODE) do |channel|
|
|
19
|
+
channel.name = 'Wholesale'
|
|
20
|
+
channel.preferred_storefront_access = 'login_required'
|
|
21
|
+
channel.preferred_guest_checkout = false
|
|
21
22
|
end
|
|
22
23
|
end
|
|
23
24
|
end
|
|
@@ -22,31 +22,32 @@ module Spree
|
|
|
22
22
|
# setting the value is the operator's first commission decision.
|
|
23
23
|
class CommissionRates
|
|
24
24
|
prepend Spree::ServiceModule::Base
|
|
25
|
+
include StoreScoped
|
|
25
26
|
|
|
26
27
|
DEFAULT_CODE = 'marketplace-default'.freeze
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
Spree::Store.find_each do |store|
|
|
30
|
-
# Live rows only, matching the partial unique index: an operator who
|
|
31
|
-
# deleted the seeded fallback gets it back on the next run rather
|
|
32
|
-
# than being left with no bottom-of-list catch-all forever.
|
|
33
|
-
next if store.commission_rates.exists?(code: DEFAULT_CODE)
|
|
29
|
+
private
|
|
34
30
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
enabled: false
|
|
41
|
-
)
|
|
31
|
+
def seed(store)
|
|
32
|
+
# Live rows only, matching the partial unique index: an operator who
|
|
33
|
+
# deleted the seeded fallback gets it back on the next run rather
|
|
34
|
+
# than being left with no bottom-of-list catch-all forever.
|
|
35
|
+
return if store.commission_rates.exists?(code: DEFAULT_CODE)
|
|
42
36
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
37
|
+
rate = store.commission_rates.create!(
|
|
38
|
+
name: Spree.t('seed.commission_rates.marketplace_default'),
|
|
39
|
+
code: DEFAULT_CODE,
|
|
40
|
+
kind: 'percentage',
|
|
41
|
+
value: 0,
|
|
42
|
+
enabled: false
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
# New rates are born at the top, which is right for one an operator
|
|
46
|
+
# adds and wrong for this one: it matches every sale, so anywhere but
|
|
47
|
+
# the bottom leaves the rates below it unreachable. A store seeded
|
|
48
|
+
# after it already has rates would otherwise have them all shadowed
|
|
49
|
+
# the moment this was enabled.
|
|
50
|
+
rate.move_to_bottom
|
|
50
51
|
end
|
|
51
52
|
end
|
|
52
53
|
end
|
|
@@ -5,13 +5,14 @@ module Spree
|
|
|
5
5
|
# wholesale price lists key off.
|
|
6
6
|
class CustomerGroups
|
|
7
7
|
prepend Spree::ServiceModule::Base
|
|
8
|
+
include StoreScoped
|
|
8
9
|
|
|
9
10
|
WHOLESALE_NAME = 'Wholesale'.freeze
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def seed(store)
|
|
15
|
+
store.customer_groups.find_or_create_by!(name: WHOLESALE_NAME)
|
|
15
16
|
end
|
|
16
17
|
end
|
|
17
18
|
end
|
|
@@ -2,16 +2,11 @@ module Spree
|
|
|
2
2
|
module Seeds
|
|
3
3
|
class DigitalDelivery
|
|
4
4
|
prepend Spree::ServiceModule::Base
|
|
5
|
-
|
|
6
|
-
def call
|
|
7
|
-
Spree::Store.all.find_each do |store|
|
|
8
|
-
create_for(store)
|
|
9
|
-
end
|
|
10
|
-
end
|
|
5
|
+
include StoreScoped
|
|
11
6
|
|
|
12
7
|
private
|
|
13
8
|
|
|
14
|
-
def
|
|
9
|
+
def seed(store)
|
|
15
10
|
# Digital goods are a distinct product set, so they get their own
|
|
16
11
|
# profile; assigning a product to it is what makes it digital.
|
|
17
12
|
profile = Spree::DeliveryProfiles::Digital.find_by(store: store) ||
|
|
@@ -22,7 +17,11 @@ module Spree
|
|
|
22
17
|
digital_delivery_method.delivery_profile = profile
|
|
23
18
|
digital_delivery_method.storefront_visible = true
|
|
24
19
|
digital_delivery_method.fulfillment_provider = 'Spree::FulfillmentProvider::Digital'
|
|
25
|
-
|
|
20
|
+
# Explicit, because the calculator's own default is the default
|
|
21
|
+
# store's currency rather than this store's.
|
|
22
|
+
digital_delivery_method.calculator ||= Spree::Calculator::Shipping::DigitalDelivery.create!(
|
|
23
|
+
preferred_currency: store.default_currency
|
|
24
|
+
)
|
|
26
25
|
digital_delivery_method.save!
|
|
27
26
|
end
|
|
28
27
|
end
|
|
@@ -2,19 +2,20 @@ module Spree
|
|
|
2
2
|
module Seeds
|
|
3
3
|
class PaymentMethods
|
|
4
4
|
prepend Spree::ServiceModule::Base
|
|
5
|
+
include StoreScoped
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
Spree::Store.all.find_each do |store|
|
|
8
|
-
payment_method = store.payment_methods.find_or_initialize_by(
|
|
9
|
-
type: 'Spree::PaymentMethod::StoreCredit'
|
|
10
|
-
)
|
|
11
|
-
next if payment_method.persisted?
|
|
7
|
+
private
|
|
12
8
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
9
|
+
def seed(store)
|
|
10
|
+
payment_method = store.payment_methods.find_or_initialize_by(
|
|
11
|
+
type: 'Spree::PaymentMethod::StoreCredit'
|
|
12
|
+
)
|
|
13
|
+
return if payment_method.persisted?
|
|
14
|
+
|
|
15
|
+
payment_method.name = Spree.t(:store_credit_name)
|
|
16
|
+
payment_method.description = Spree.t(:store_credit_name)
|
|
17
|
+
payment_method.active = true
|
|
18
|
+
payment_method.save!
|
|
18
19
|
end
|
|
19
20
|
end
|
|
20
21
|
end
|
|
@@ -2,18 +2,17 @@ module Spree
|
|
|
2
2
|
module Seeds
|
|
3
3
|
class ProductTypes
|
|
4
4
|
prepend Spree::ServiceModule::Base
|
|
5
|
+
include StoreScoped
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
def seed(store)
|
|
10
|
+
Spree::ProductType.where(store: store).find_or_create_by!(name: I18n.t('spree.seed.product_types.default'))
|
|
11
|
+
Spree::ProductType.where(store: store).find_or_create_by!(name: I18n.t('spree.seed.product_types.digital')) do |product_type|
|
|
12
|
+
product_type.delivery_profile = digital_profile_for(store)
|
|
12
13
|
end
|
|
13
14
|
end
|
|
14
15
|
|
|
15
|
-
private
|
|
16
|
-
|
|
17
16
|
def digital_profile_for(store)
|
|
18
17
|
Spree::DeliveryProfiles::Digital.find_by(store: store) ||
|
|
19
18
|
Spree::DeliveryProfiles::Digital.create!(store: store, name: I18n.t('spree.seed.delivery_profiles.digital'))
|
|
@@ -2,6 +2,7 @@ module Spree
|
|
|
2
2
|
module Seeds
|
|
3
3
|
class ReturnsEnvironment
|
|
4
4
|
prepend Spree::ServiceModule::Base
|
|
5
|
+
include StoreScoped
|
|
5
6
|
|
|
6
7
|
RETURN_REASONS = [
|
|
7
8
|
'Better price available',
|
|
@@ -37,18 +38,18 @@ module Spree
|
|
|
37
38
|
'Item not as described'
|
|
38
39
|
].freeze
|
|
39
40
|
|
|
40
|
-
|
|
41
|
-
# Every vocabulary is store-owned, so every store gets its own.
|
|
42
|
-
Spree::Store.all.find_each do |store|
|
|
43
|
-
# Through the model helper, not a literal — the workflows look this
|
|
44
|
-
# row up by the same constant, so a hard-coded string here would
|
|
45
|
-
# silently create a second, unfindable reason if the name ever moves.
|
|
46
|
-
Spree::RefundReason.return_processing_reason(store)
|
|
41
|
+
private
|
|
47
42
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
43
|
+
# Every vocabulary is store-owned, so every store gets its own.
|
|
44
|
+
def seed(store)
|
|
45
|
+
# Through the model helper, not a literal — the workflows look this
|
|
46
|
+
# row up by the same constant, so a hard-coded string here would
|
|
47
|
+
# silently create a second, unfindable reason if the name ever moves.
|
|
48
|
+
Spree::RefundReason.return_processing_reason(store)
|
|
49
|
+
|
|
50
|
+
RETURN_REASONS.each { |name| Spree::ReturnReason.where(store: store).find_or_create_by!(name: name) }
|
|
51
|
+
CLAIM_REASONS.each { |name| Spree::ClaimReason.where(store: store).find_or_create_by!(name: name) }
|
|
52
|
+
ORDER_CANCELLATION_REASONS.each { |name| Spree::OrderCancellationReason.where(store: store).find_or_create_by!(name: name) }
|
|
52
53
|
end
|
|
53
54
|
end
|
|
54
55
|
end
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
module Spree
|
|
2
2
|
module Seeds
|
|
3
|
+
# A role belongs to what it governs, so every store gets its own
|
|
4
|
+
# immutable admin.
|
|
3
5
|
class Roles
|
|
4
6
|
prepend Spree::ServiceModule::Base
|
|
7
|
+
include StoreScoped
|
|
5
8
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def
|
|
9
|
-
Spree::
|
|
10
|
-
|
|
11
|
-
admin.update_column(:mutable, false) if admin.read_attribute(:mutable)
|
|
12
|
-
end
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
def seed(store)
|
|
12
|
+
admin = Spree::Role.default_admin_role(store)
|
|
13
|
+
admin.update_column(:mutable, false) if admin.read_attribute(:mutable)
|
|
13
14
|
end
|
|
14
15
|
end
|
|
15
16
|
end
|
|
@@ -6,6 +6,7 @@ module Spree
|
|
|
6
6
|
# only never-created ones are added.
|
|
7
7
|
class SavedReports
|
|
8
8
|
prepend Spree::ServiceModule::Base
|
|
9
|
+
include StoreScoped
|
|
9
10
|
|
|
10
11
|
REPORTS = [
|
|
11
12
|
{ key: 'sales_over_time',
|
|
@@ -70,31 +71,29 @@ module Spree
|
|
|
70
71
|
'time_range' => { 'preset' => 'last_4_weeks' } } }
|
|
71
72
|
].freeze
|
|
72
73
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
74
|
+
private
|
|
75
|
+
|
|
76
|
+
def seed(store)
|
|
77
|
+
# Compared case-insensitively like the model's uniqueness rule, so a
|
|
78
|
+
# merchant's own "top products" never makes a re-seed raise.
|
|
79
|
+
existing = store.saved_reports.pluck(:name).map(&:downcase).to_set
|
|
78
80
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
81
|
+
REPORTS.each do |report|
|
|
82
|
+
# Every locale's name for this report, not just the current one:
|
|
83
|
+
# the name is translated, so a store seeded in one language and
|
|
84
|
+
# re-seeded in another would otherwise recognise none of its own
|
|
85
|
+
# built-ins and create a second full set.
|
|
86
|
+
next if known_names(report[:key]).intersect?(existing)
|
|
85
87
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
end
|
|
88
|
+
store.saved_reports.create!(
|
|
89
|
+
name: Spree.t("reporting.seeds.#{report[:key]}.name"),
|
|
90
|
+
description: Spree.t("reporting.seeds.#{report[:key]}.description"),
|
|
91
|
+
query: report[:query],
|
|
92
|
+
seeded: true
|
|
93
|
+
)
|
|
93
94
|
end
|
|
94
95
|
end
|
|
95
96
|
|
|
96
|
-
private
|
|
97
|
-
|
|
98
97
|
# The downcased name this report carries in every locale core ships.
|
|
99
98
|
def known_names(key)
|
|
100
99
|
@known_names ||= {}
|
|
@@ -8,11 +8,12 @@ module Spree
|
|
|
8
8
|
# a licence) is theirs to add, and there is no sensible default for it.
|
|
9
9
|
class SellerRequirements
|
|
10
10
|
prepend Spree::ServiceModule::Base
|
|
11
|
+
include StoreScoped
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def seed(store)
|
|
16
|
+
Spree::SellerRequirement.provision_defaults(store)
|
|
16
17
|
end
|
|
17
18
|
end
|
|
18
19
|
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Seeds
|
|
3
|
+
# Seeds what a single store needs to trade, without touching any other
|
|
4
|
+
# store.
|
|
5
|
+
#
|
|
6
|
+
# Country-shaped defaults (market, warehouse, delivery zones, pickup) are
|
|
7
|
+
# not part of it: the caller runs Spree::Stores::ProvisionDefaults
|
|
8
|
+
# afterwards with the merchant's answers.
|
|
9
|
+
class StoreResources
|
|
10
|
+
prepend Spree::ServiceModule::Base
|
|
11
|
+
|
|
12
|
+
# @param store [Spree::Store] the store to seed, already persisted
|
|
13
|
+
# @return [Spree::ServiceModule::Result]
|
|
14
|
+
def call(store:)
|
|
15
|
+
# A nil store would fall through to every seed's all-stores mode.
|
|
16
|
+
raise ArgumentError, 'store is required' if store.nil?
|
|
17
|
+
|
|
18
|
+
Spree::Events.disable do
|
|
19
|
+
ActiveRecord::Base.no_touching do
|
|
20
|
+
TaxCategories.call(store: store)
|
|
21
|
+
Channels.call(store: store)
|
|
22
|
+
Roles.call(store: store)
|
|
23
|
+
DigitalDelivery.call(store: store)
|
|
24
|
+
PaymentMethods.call(store: store)
|
|
25
|
+
ProductTypes.call(store: store)
|
|
26
|
+
CustomerGroups.call(store: store)
|
|
27
|
+
ReturnsEnvironment.call(store: store)
|
|
28
|
+
# The marketplace's catch-all rate, at the bottom of the list so
|
|
29
|
+
# narrower rates added later resolve ahead of it.
|
|
30
|
+
CommissionRates.call(store: store)
|
|
31
|
+
SellerRequirements.call(store: store)
|
|
32
|
+
# Binds to the wholesale channel that Channels creates above.
|
|
33
|
+
ApiKeys.call(store: store)
|
|
34
|
+
SavedReports.call(store: store)
|
|
35
|
+
AllowedOrigins.call(store: store)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Seeds
|
|
3
|
+
# Shared entry point for seeds whose rows belong to a store. Including
|
|
4
|
+
# classes implement a private `seed(store)` that seeds exactly one store
|
|
5
|
+
# and is safe to re-run.
|
|
6
|
+
module StoreScoped
|
|
7
|
+
# @param store [Spree::Store, nil] the store to seed; every store when nil
|
|
8
|
+
def call(store: nil)
|
|
9
|
+
return seed(store) if store
|
|
10
|
+
|
|
11
|
+
Spree::Store.find_each { |each_store| seed(each_store) }
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -2,17 +2,18 @@ module Spree
|
|
|
2
2
|
module Seeds
|
|
3
3
|
class TaxCategories
|
|
4
4
|
prepend Spree::ServiceModule::Base
|
|
5
|
+
include StoreScoped
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
Spree::Store.find_each do |store|
|
|
8
|
-
# The block form only runs on create, so a store that already had a
|
|
9
|
-
# "Default" category with the flag unset kept no default at all — and
|
|
10
|
-
# the default is what taxes an item carrying no category of its own.
|
|
11
|
-
default = store.tax_categories.find_or_create_by!(name: 'Default')
|
|
12
|
-
default.update!(is_default: true) unless default.is_default?
|
|
7
|
+
private
|
|
13
8
|
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
def seed(store)
|
|
10
|
+
# The block form only runs on create, so a store that already had a
|
|
11
|
+
# "Default" category with the flag unset kept no default at all — and
|
|
12
|
+
# the default is what taxes an item carrying no category of its own.
|
|
13
|
+
default = store.tax_categories.find_or_create_by!(name: 'Default')
|
|
14
|
+
default.update!(is_default: true) unless default.is_default?
|
|
15
|
+
|
|
16
|
+
store.tax_categories.find_or_create_by!(name: 'Non-taxable')
|
|
16
17
|
end
|
|
17
18
|
end
|
|
18
19
|
end
|