spree_core 5.0.4 → 5.1.0.beta2
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/assets/images/logo.png +0 -0
- data/app/finders/spree/countries/find.rb +0 -8
- data/app/finders/spree/products/find.rb +28 -1
- data/app/finders/spree/taxons/find.rb +1 -1
- data/app/helpers/spree/base_helper.rb +4 -16
- data/app/helpers/spree/integrations_helper.rb +15 -0
- data/app/helpers/spree/mail_helper.rb +4 -3
- data/app/helpers/spree/products_helper.rb +1 -4
- data/app/javascript/spree/core/controllers/disable_submit_button_controller.js +19 -0
- data/app/mailers/spree/invitation_mailer.rb +24 -0
- data/app/models/concerns/spree/integrations_concern.rb +11 -0
- data/app/models/concerns/spree/product_scopes.rb +6 -6
- data/app/models/concerns/spree/translatable_resource.rb +4 -0
- data/app/models/concerns/spree/translatable_resource_scopes.rb +17 -3
- data/app/models/concerns/spree/unique_name.rb +2 -0
- data/app/models/concerns/spree/user_management.rb +33 -0
- data/app/models/concerns/spree/user_methods.rb +19 -0
- data/app/models/concerns/spree/user_roles.rb +43 -17
- data/app/models/spree/ability.rb +8 -6
- data/app/models/spree/asset.rb +1 -6
- data/app/models/spree/base.rb +1 -0
- data/app/models/spree/base_analytics_event_handler.rb +7 -2
- data/app/models/spree/classification.rb +13 -0
- data/app/models/spree/custom_domain.rb +2 -1
- data/app/models/spree/export.rb +1 -1
- data/app/models/spree/integration.rb +63 -0
- data/app/models/spree/invitation.rb +153 -0
- data/app/models/spree/invitations/store.rb +6 -0
- data/app/models/spree/order.rb +16 -1
- data/app/models/spree/order_merger.rb +7 -5
- data/app/models/spree/product_property.rb +1 -14
- data/app/models/spree/property.rb +3 -1
- data/app/models/spree/reports/sales_total.rb +5 -1
- data/app/models/spree/role.rb +16 -0
- data/app/models/spree/role_user.rb +32 -1
- data/app/models/spree/shipment_handler.rb +1 -0
- data/app/models/spree/shipping_method.rb +1 -1
- data/app/models/spree/store.rb +9 -4
- data/app/models/spree/store_credit_category.rb +4 -0
- data/app/models/spree/taxon.rb +4 -3
- data/app/services/spree/country_to_timezone.rb +273 -0
- data/app/services/spree/seeds/admin_user.rb +4 -2
- data/app/services/spree/seeds/all.rb +3 -1
- data/app/services/spree/seeds/digital_delivery.rb +20 -0
- data/app/services/spree/seeds/returns_environment.rb +27 -0
- data/app/services/spree/seeds/tax_categories.rb +12 -0
- data/app/services/spree/stores/settings_defaults_by_country.rb +38 -0
- data/app/services/spree/tags/bulk_add.rb +13 -7
- data/app/views/spree/invitation_mailer/invitation_accepted.html.erb +12 -0
- data/app/views/spree/invitation_mailer/invitation_email.html.erb +21 -0
- data/config/locales/en.yml +43 -9
- data/db/migrate/20250407085228_create_spree_integrations.rb +12 -0
- data/db/migrate/20250410061306_create_spree_invitations.rb +20 -0
- data/db/migrate/20250418174652_add_resource_to_spree_role_users.rb +8 -0
- data/db/migrate/20250508060800_add_selected_locale_to_spree_admin_users.rb +8 -0
- data/db/migrate/20250509143831_add_session_id_to_spree_assets.rb +5 -0
- data/lib/generators/spree/authentication/devise/devise_generator.rb +5 -2
- data/lib/generators/spree/authentication/devise/templates/authentication_helpers.rb.tt +3 -3
- data/lib/generators/spree/dummy/dummy_generator.rb +1 -1
- data/lib/generators/spree/dummy/templates/app/assets/config/manifest.js +0 -0
- data/lib/generators/spree/dummy/templates/rails/application.rb +1 -2
- data/lib/generators/spree/install/install_generator.rb +5 -0
- data/lib/spree/core/controller_helpers/auth.rb +15 -14
- data/lib/spree/core/controller_helpers/common.rb +0 -71
- data/lib/spree/core/controller_helpers/currency.rb +11 -0
- data/lib/spree/core/controller_helpers/strong_parameters.rb +3 -2
- data/lib/spree/core/engine.rb +14 -3
- data/lib/spree/core/preferences/preferable.rb +10 -0
- data/lib/spree/core/preferences/preferable_class_methods.rb +52 -0
- data/lib/spree/core/version.rb +1 -1
- data/lib/spree/core.rb +1 -0
- data/lib/spree/permitted_attributes.rb +121 -13
- data/lib/spree/testing_support/controller_requests.rb +0 -92
- data/lib/spree/testing_support/factories/integration_factory.rb +7 -0
- data/lib/spree/testing_support/factories/invitation_factory.rb +6 -0
- data/lib/spree/testing_support/factories/page_block_factory.rb +1 -0
- data/lib/spree/testing_support/factories/promotion_action_factory.rb +4 -0
- data/lib/spree/testing_support/factories/user_factory.rb +14 -1
- data/lib/spree/translation_migrations.rb +27 -15
- data/lib/tasks/core.rake +8 -0
- metadata +43 -7
- data/app/validators/db_maximum_length_validator.rb +0 -16
- data/lib/generators/spree/dummy/templates/rails/script/rails +0 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f8150046e1776256aacec11512b848d9cbd5074d17b7aacafd7b2736cfd934c3
|
4
|
+
data.tar.gz: 8a04db5d73b2abf21a587ea66313d4fb7ff7120ed46ef9d22f38ddf1b3c5f295
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8024d8aa8b617d7be12713acf1787a7c4569dc708d74cbf34850ec6bc8fcea5630c15fdfe6efa4796c82825fc9724d637b219da887f888f909dd29d22582178f
|
7
|
+
data.tar.gz: 60db3009f9a9a30bfca2b8c6895b7777a35eaac8819ca06a112e2315e234137ee87e5fea3796335e53e8a8e5c562f22183d7d8c6c3c3b1f8e9466d76ba3bbde5
|
Binary file
|
@@ -7,14 +7,6 @@ module Spree
|
|
7
7
|
@shippable = String(params[:filter][:shippable]) unless params[:filter].nil?
|
8
8
|
end
|
9
9
|
|
10
|
-
def call
|
11
|
-
Spree::Deprecation.warn(<<-DEPRECATION, caller)
|
12
|
-
Spree::Countries::Find.new.call is deprecated and will be removed in Spree 5.0.
|
13
|
-
Please use Spree::Countries::Find.new.execute instead
|
14
|
-
DEPRECATION
|
15
|
-
execute
|
16
|
-
end
|
17
|
-
|
18
10
|
def execute
|
19
11
|
countries = by_shippability(scope)
|
20
12
|
|
@@ -11,6 +11,7 @@ module Spree
|
|
11
11
|
@currency = params.dig(:filter, :currency) || params[:currency] || Spree::Store.default.default_currency
|
12
12
|
@taxons = taxon_ids(params.dig(:filter, :taxons))
|
13
13
|
@concat_taxons = taxon_ids(params.dig(:filter, :concat_taxons))
|
14
|
+
@taxonomies = params.dig(:filter, :taxonomy_ids).to_h
|
14
15
|
@name = params.dig(:filter, :name)
|
15
16
|
@slug = params.dig(:filter, :slug)
|
16
17
|
@options = params.dig(:filter, :options).try(:to_unsafe_hash)
|
@@ -52,6 +53,7 @@ module Spree
|
|
52
53
|
products = show_only_backorderable(products)
|
53
54
|
products = show_only_purchasable(products)
|
54
55
|
products = show_only_out_of_stock(products)
|
56
|
+
products = by_taxonomies(products)
|
55
57
|
products = ordered(products)
|
56
58
|
products = by_vendor_ids(products)
|
57
59
|
|
@@ -62,7 +64,7 @@ module Spree
|
|
62
64
|
|
63
65
|
attr_reader :ids, :skus, :price, :currency, :taxons, :concat_taxons, :name, :options, :option_value_ids, :scope,
|
64
66
|
:sort_by, :deleted, :discontinued, :properties, :store, :in_stock, :backorderable, :purchasable, :tags,
|
65
|
-
:query, :vendor_ids, :out_of_stock, :slug
|
67
|
+
:query, :vendor_ids, :out_of_stock, :slug, :taxonomies
|
66
68
|
|
67
69
|
def query?
|
68
70
|
query.present?
|
@@ -169,6 +171,21 @@ module Spree
|
|
169
171
|
products.where(id: product_ids)
|
170
172
|
end
|
171
173
|
|
174
|
+
def by_taxonomies(products)
|
175
|
+
return products if taxonomies.none?
|
176
|
+
|
177
|
+
taxon_groups = taxonomies.values.map { |taxonomy| taxon_ids(taxonomy[:taxon_ids].join(',')) }.compact_blank
|
178
|
+
|
179
|
+
return products if taxon_groups.empty?
|
180
|
+
|
181
|
+
taxonomies_products = products.joins(:classifications).where(Classification.table_name => { taxon_id: taxon_groups.flatten.uniq })
|
182
|
+
|
183
|
+
# No need to filter if there is only one taxonomy
|
184
|
+
return taxonomies_products if taxonomies.size == 1
|
185
|
+
|
186
|
+
products.where(id: products_matching_all_taxonomies_ids(taxonomies_products.ids, taxon_groups))
|
187
|
+
end
|
188
|
+
|
172
189
|
def by_name(products)
|
173
190
|
return products unless name?
|
174
191
|
|
@@ -336,6 +353,16 @@ module Spree
|
|
336
353
|
def order_by_best_selling(scope)
|
337
354
|
scope.by_best_selling(:desc)
|
338
355
|
end
|
356
|
+
|
357
|
+
def products_matching_all_taxonomies_ids(products_ids, taxon_groups)
|
358
|
+
classifications = Spree::Classification.grouped_taxon_ids_for_products(products_ids, taxon_groups.flatten)
|
359
|
+
classifications_hash = classifications.to_h.transform_values { |taxon_ids| taxon_ids.split(',') }
|
360
|
+
|
361
|
+
# Find products ids that match all taxonomies to tighten filter results
|
362
|
+
classifications_hash.filter_map do |product_id, product_taxon_ids|
|
363
|
+
product_id if taxon_groups.all? { |group| group.intersect?(product_taxon_ids) }
|
364
|
+
end
|
365
|
+
end
|
339
366
|
end
|
340
367
|
end
|
341
368
|
end
|
@@ -68,7 +68,7 @@ module Spree
|
|
68
68
|
if Spree.use_translations?
|
69
69
|
taxons.joins(:parent).
|
70
70
|
join_translation_table(Taxon, 'parents_spree_taxons').
|
71
|
-
where(
|
71
|
+
where(Taxon.translation_table_alias => { permalink: parent_permalink })
|
72
72
|
else
|
73
73
|
taxons.joins(:parent).where(parent: { permalink: parent_permalink })
|
74
74
|
end
|
@@ -136,10 +136,7 @@ module Spree
|
|
136
136
|
def pretty_time(time)
|
137
137
|
return '' if time.blank?
|
138
138
|
|
139
|
-
Spree::Deprecation.warn(
|
140
|
-
`BaseHelper#pretty_time` is deprecated and will be removed in Spree 6.0.
|
141
|
-
Please add `local_time` gem to your Gemfile and use `local_time(time)` instead
|
142
|
-
DEPRECATION
|
139
|
+
Spree::Deprecation.warn('BaseHelper#pretty_time is deprecated and will be removed in Spree 6.0. Please add `local_time` gem to your Gemfile and use `local_time(time)` instead')
|
143
140
|
|
144
141
|
[I18n.l(time.to_date, format: :long), time.strftime('%l:%M %p %Z')].join(' ')
|
145
142
|
end
|
@@ -147,10 +144,7 @@ module Spree
|
|
147
144
|
def pretty_date(date)
|
148
145
|
return '' if date.blank?
|
149
146
|
|
150
|
-
Spree::Deprecation.warn(
|
151
|
-
`BaseHelper#pretty_date` is deprecated and will be removed in Spree 6.0.
|
152
|
-
Please add `local_time` gem to your Gemfile and use `local_date(date)` instead
|
153
|
-
DEPRECATION
|
147
|
+
Spree::Deprecation.warn('BaseHelper#pretty_date is deprecated and will be removed in Spree 6.0. Please add `local_time` gem to your Gemfile and use `local_date(date)` instead')
|
154
148
|
|
155
149
|
[I18n.l(date.to_date, format: :long)].join(' ')
|
156
150
|
end
|
@@ -219,19 +213,13 @@ module Spree
|
|
219
213
|
# we should always try to render image of the default variant
|
220
214
|
# same as it's done on PDP
|
221
215
|
def default_image_for_product(product)
|
222
|
-
Spree::Deprecation.warn(
|
223
|
-
`BaseHelper#default_image_for_product` is deprecated and will be removed in Spree 6.0.
|
224
|
-
Please use `product.default_image` instead
|
225
|
-
DEPRECATION
|
216
|
+
Spree::Deprecation.warn('BaseHelper#default_image_for_product is deprecated and will be removed in Spree 6.0. Please use product.default_image instead')
|
226
217
|
|
227
218
|
product.default_image
|
228
219
|
end
|
229
220
|
|
230
221
|
def default_image_for_product_or_variant(product_or_variant)
|
231
|
-
Spree::Deprecation.warn(
|
232
|
-
`BaseHelper#default_image_for_product_or_variant` is deprecated and will be removed in Spree 6.0.
|
233
|
-
Please use `product_or_variant.default_image` instead
|
234
|
-
DEPRECATION
|
222
|
+
Spree::Deprecation.warn('BaseHelper#default_image_for_product_or_variant is deprecated and will be removed in Spree 6.0. Please use product_or_variant.default_image instead')
|
235
223
|
|
236
224
|
product_or_variant.default_image
|
237
225
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Spree
|
2
|
+
module IntegrationsHelper
|
3
|
+
def store_integrations
|
4
|
+
@store_integrations ||= current_store.integrations.active.to_a
|
5
|
+
end
|
6
|
+
|
7
|
+
def store_integration(name)
|
8
|
+
store_integrations.find { |integration| integration.type.to_s.demodulize.underscore == name }
|
9
|
+
end
|
10
|
+
|
11
|
+
def grouped_available_store_integrations
|
12
|
+
Rails.application.config.spree.integrations.group_by(&:integration_group).sort_by { |group, _| group }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -1,10 +1,11 @@
|
|
1
1
|
module Spree
|
2
2
|
module MailHelper
|
3
|
-
include BaseHelper
|
3
|
+
include Spree::BaseHelper
|
4
|
+
include Spree::ImagesHelper
|
4
5
|
|
5
6
|
def variant_image_url(variant)
|
6
|
-
image =
|
7
|
-
image ?
|
7
|
+
image = variant.default_image
|
8
|
+
image.present? && image.attached? ? spree_image_url(image, width: 100, height: 100) : image_url('noimage/small.png')
|
8
9
|
end
|
9
10
|
|
10
11
|
def name_for(order)
|
@@ -121,10 +121,7 @@ module Spree
|
|
121
121
|
end
|
122
122
|
|
123
123
|
def related_products
|
124
|
-
Spree::Deprecation.warn(
|
125
|
-
ProductsHelper#related_products is deprecated and will be removed in Spree 5.0.
|
126
|
-
Please use ProductsHelper#relations from now on.
|
127
|
-
DEPRECATION
|
124
|
+
Spree::Deprecation.warn('ProductsHelper#related_products is deprecated and will be removed in Spree 6.0. Please use ProductsHelper#relations from now on.')
|
128
125
|
|
129
126
|
return [] unless @product.respond_to?(:has_related_products?)
|
130
127
|
|
@@ -0,0 +1,19 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static targets = ["button"]
|
5
|
+
|
6
|
+
connect() {
|
7
|
+
this.element.addEventListener('submit', this.toggleDisabledButton)
|
8
|
+
}
|
9
|
+
|
10
|
+
disconnect() {
|
11
|
+
this.element.removeEventListener('submit', this.toggleDisabledButton)
|
12
|
+
}
|
13
|
+
|
14
|
+
toggleDisabledButton = () => {
|
15
|
+
if (this.hasButtonTarget) {
|
16
|
+
this.buttonTarget.disabled = !this.buttonTarget.disabled
|
17
|
+
}
|
18
|
+
}
|
19
|
+
}
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Spree
|
2
|
+
class InvitationMailer < BaseMailer
|
3
|
+
# invitation email, sending email to the invited to let them know they have been invited to join a store/account/vendor
|
4
|
+
def invitation_email(invitation)
|
5
|
+
@invitation = invitation
|
6
|
+
mail(to: invitation.email,
|
7
|
+
from: from_address,
|
8
|
+
reply_to: reply_to_address,
|
9
|
+
subject: Spree.t('invitation_mailer.invitation_email.subject',
|
10
|
+
resource_name: invitation.resource&.name))
|
11
|
+
end
|
12
|
+
|
13
|
+
# sending email to the inviter to let them know the invitee has accepted the invitation
|
14
|
+
def invitation_accepted(invitation)
|
15
|
+
@invitation = invitation
|
16
|
+
mail(to: invitation.inviter.email,
|
17
|
+
from: from_address,
|
18
|
+
reply_to: reply_to_address,
|
19
|
+
subject: Spree.t('invitation_mailer.invitation_accepted.subject',
|
20
|
+
invitee_name: invitation.invitee&.name,
|
21
|
+
resource_name: invitation.resource&.name))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Spree
|
2
|
+
module IntegrationsConcern
|
3
|
+
def store_integrations
|
4
|
+
@store_integrations ||= Spree::Store.current.integrations.active.to_a
|
5
|
+
end
|
6
|
+
|
7
|
+
def store_integration(name)
|
8
|
+
store_integrations.find { |integration| integration.type.to_s.demodulize.underscore == name }
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -49,11 +49,11 @@ module Spree
|
|
49
49
|
add_simple_scopes simple_scopes
|
50
50
|
|
51
51
|
add_search_scope :ascend_by_master_price do
|
52
|
-
order(
|
52
|
+
order(price_table_name => { amount: :asc })
|
53
53
|
end
|
54
54
|
|
55
55
|
add_search_scope :descend_by_master_price do
|
56
|
-
order(
|
56
|
+
order(price_table_name => { amount: :desc })
|
57
57
|
end
|
58
58
|
|
59
59
|
add_search_scope :price_between do |low, high|
|
@@ -61,11 +61,11 @@ module Spree
|
|
61
61
|
end
|
62
62
|
|
63
63
|
add_search_scope :master_price_lte do |price|
|
64
|
-
where(
|
64
|
+
where(Price.table_name => { amount: ..price })
|
65
65
|
end
|
66
66
|
|
67
67
|
add_search_scope :master_price_gte do |price|
|
68
|
-
where(
|
68
|
+
where(Price.table_name => { amount: price.. })
|
69
69
|
end
|
70
70
|
|
71
71
|
add_search_scope :in_stock do
|
@@ -137,11 +137,11 @@ module Spree
|
|
137
137
|
joins(:properties).
|
138
138
|
join_translation_table(Property).
|
139
139
|
join_translation_table(ProductProperty).
|
140
|
-
where(
|
140
|
+
where(ProductProperty.translation_table_alias => { value: value }).
|
141
141
|
where(property_conditions(property))
|
142
142
|
else
|
143
143
|
joins(:properties).
|
144
|
-
where(
|
144
|
+
where(ProductProperty.table_name => { value: value }).
|
145
145
|
where(property_conditions(property))
|
146
146
|
end
|
147
147
|
end
|
@@ -15,9 +15,23 @@ module Spree
|
|
15
15
|
end
|
16
16
|
translatable_class_foreign_key = "#{translatable_class.table_name.singularize}_id"
|
17
17
|
|
18
|
-
joins(
|
19
|
-
|
20
|
-
|
18
|
+
joins(
|
19
|
+
Arel::Nodes::OuterJoin.new(
|
20
|
+
Arel::Table.new(translatable_class::Translation.table_name).alias(translatable_class.translation_table_alias),
|
21
|
+
Arel::Nodes::On.new(
|
22
|
+
Arel::Nodes::And.new([
|
23
|
+
Arel::Nodes::Equality.new(
|
24
|
+
Arel::Table.new(translatable_class.translation_table_alias)[translatable_class_foreign_key],
|
25
|
+
Arel::Table.new(join_on_table_name)[:id]
|
26
|
+
),
|
27
|
+
Arel::Nodes::Equality.new(
|
28
|
+
Arel::Table.new(translatable_class.translation_table_alias)[:locale],
|
29
|
+
Arel::Nodes::Quoted.new(Mobility.locale.to_s)
|
30
|
+
)
|
31
|
+
])
|
32
|
+
)
|
33
|
+
)
|
34
|
+
)
|
21
35
|
end
|
22
36
|
end
|
23
37
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Spree
|
2
|
+
module UserManagement
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
has_many :role_users, class_name: 'Spree::RoleUser', as: :resource, dependent: :destroy
|
7
|
+
has_many :users, through: :role_users, source: :user, source_type: Spree.admin_user_class.to_s
|
8
|
+
has_many :invitations, class_name: 'Spree::Invitation', as: :resource, dependent: :destroy
|
9
|
+
end
|
10
|
+
|
11
|
+
# Adds a user to the resource with the default user role
|
12
|
+
# If no role is provided, the default user role will be used
|
13
|
+
# If a role is provided, it will be used instead of the default user role
|
14
|
+
# @param user [Spree.admin_user_class] The user to add to the resource
|
15
|
+
# @param role [Spree::Role] The role to add the user to
|
16
|
+
def add_user(user, role = nil)
|
17
|
+
role = role || default_user_role
|
18
|
+
role_users.find_or_create_by!(user: user, role: role)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Revokes a user's access to the resource
|
22
|
+
# @param user [Spree.admin_user_class] The user to remove from the resource
|
23
|
+
# @return [void]
|
24
|
+
def remove_user(user)
|
25
|
+
role_users.where(user: user).destroy_all
|
26
|
+
end
|
27
|
+
|
28
|
+
# this can be overridden in the base model to use a different user role, eg. 'vendor'
|
29
|
+
def default_user_role
|
30
|
+
Spree::Role.default_admin_role
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -84,6 +84,11 @@ module Spree
|
|
84
84
|
end
|
85
85
|
end
|
86
86
|
|
87
|
+
# Returns the last incomplete spree order for the current store
|
88
|
+
# @param [Spree::Store] store
|
89
|
+
# @param [Hash] options
|
90
|
+
# @option options [Array<Symbol>] :includes
|
91
|
+
# @return [Spree::Order]
|
87
92
|
def last_incomplete_spree_order(store, options = {})
|
88
93
|
orders.where(store: store).incomplete.not_canceled.
|
89
94
|
includes(options[:includes]).
|
@@ -91,12 +96,19 @@ module Spree
|
|
91
96
|
first
|
92
97
|
end
|
93
98
|
|
99
|
+
# Returns the total available store credit for the current store per currency
|
100
|
+
# @param [Spree::Store] store
|
101
|
+
# @param [String] currency
|
102
|
+
# @return [Float]
|
94
103
|
def total_available_store_credit(currency = nil, store = nil)
|
95
104
|
store ||= Store.default
|
96
105
|
currency ||= store.default_currency
|
97
106
|
store_credits.for_store(store).where(currency: currency).reload.to_a.sum(&:amount_remaining)
|
98
107
|
end
|
99
108
|
|
109
|
+
# Returns the available store credits for the current store per currency
|
110
|
+
# @param [Spree::Store] store
|
111
|
+
# @return [Array<Spree::Money>]
|
100
112
|
def available_store_credits(store)
|
101
113
|
store ||= Store.default
|
102
114
|
|
@@ -105,12 +117,18 @@ module Spree
|
|
105
117
|
end
|
106
118
|
end
|
107
119
|
|
120
|
+
# Returns the default wishlist for the current store
|
121
|
+
# if no default wishlist exists, it creates one
|
122
|
+
# @param [Spree::Store] current_store
|
123
|
+
# @return [Spree::Wishlist]
|
108
124
|
def default_wishlist_for_store(current_store)
|
109
125
|
wishlists.find_by(is_default: true, store_id: current_store.id) || ActiveRecord::Base.connected_to(role: :writing) do
|
110
126
|
wishlists.create!(store: current_store, is_default: true, name: Spree.t(:default_wishlist_name))
|
111
127
|
end
|
112
128
|
end
|
113
129
|
|
130
|
+
# Returns true if the user can be deleted
|
131
|
+
# @return [Boolean]
|
114
132
|
def can_be_deleted?
|
115
133
|
orders.complete.none?
|
116
134
|
end
|
@@ -140,6 +158,7 @@ module Spree
|
|
140
158
|
use_billing.in?([true, 'true', '1'])
|
141
159
|
end
|
142
160
|
|
161
|
+
# Scrambles the email and names of the user
|
143
162
|
def scramble_email_and_names
|
144
163
|
self.email = "#{SecureRandom.uuid}@example.net"
|
145
164
|
self.first_name = 'Deleted'
|
@@ -5,38 +5,64 @@ module Spree
|
|
5
5
|
included do
|
6
6
|
has_many :role_users, class_name: 'Spree::RoleUser', foreign_key: :user_id, as: :user, dependent: :destroy
|
7
7
|
has_many :spree_roles, through: :role_users, class_name: 'Spree::Role', source: :role
|
8
|
+
has_many :stores, through: :role_users, source: :resource, source_type: 'Spree::Store'
|
9
|
+
has_many :invitations, class_name: 'Spree::Invitation', foreign_key: :invitee_id, dependent: :destroy
|
8
10
|
|
9
|
-
scope :spree_admin, -> { joins(:spree_roles).where(Spree::Role.table_name => { name:
|
11
|
+
scope :spree_admin, -> { joins(:spree_roles).where(Spree::Role.table_name => { name: Spree::Role::ADMIN_ROLE }) }
|
10
12
|
|
11
|
-
#
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
def
|
17
|
-
|
13
|
+
# Adds a role to a resource
|
14
|
+
#
|
15
|
+
# @param role_name [String] The name of the role to add, eg. 'admin'
|
16
|
+
# @param resource [Spree::Base] The resource to add the role to
|
17
|
+
# @return [Spree::RoleUser] The role user created
|
18
|
+
def add_role(role_name, resource = nil)
|
19
|
+
resource ||= Spree::Store.current
|
20
|
+
role = Spree::Role.find_by(name: role_name)
|
21
|
+
return if role.nil?
|
18
22
|
|
19
|
-
|
23
|
+
role_users.find_or_create_by!(role: role, resource: resource)
|
20
24
|
end
|
21
25
|
|
22
|
-
|
23
|
-
|
26
|
+
# Removes a role from a resource
|
27
|
+
#
|
28
|
+
# @param role_name [String] The name of the role to remove, eg. 'admin'
|
29
|
+
# @param resource [Spree::Base] The resource to remove the role from
|
30
|
+
def remove_role(role_name, resource = nil)
|
31
|
+
resource ||= Spree::Store.current
|
32
|
+
role = Spree::Role.find_by(name: role_name)
|
33
|
+
return if role.nil?
|
24
34
|
|
25
|
-
|
35
|
+
role_users.where(role: role, resource: resource).destroy_all
|
26
36
|
end
|
27
37
|
|
28
|
-
|
29
|
-
|
38
|
+
# has_spree_role? simply needs to return true or false whether a user has a role or not.
|
39
|
+
#
|
40
|
+
# @param role_name [String] The name of the role to check for
|
41
|
+
# @param resource [Spree::Base] The resource to get roles for
|
42
|
+
# @return [Boolean] Whether the user has the role for the resource
|
43
|
+
def has_spree_role?(role_name, resource = nil)
|
44
|
+
resource ||= Spree::Store.current
|
30
45
|
|
31
|
-
|
46
|
+
role_users.where(resource: resource).joins(:role).where(Spree::Role.table_name => { name: role_name }).exists?
|
32
47
|
end
|
33
48
|
|
34
49
|
def self.spree_admin_created?
|
35
50
|
spree_admin.exists?
|
36
51
|
end
|
37
52
|
|
38
|
-
|
39
|
-
|
53
|
+
# Returns true if the user has the admin role for a given resource
|
54
|
+
#
|
55
|
+
# @param resource [Spree::Base] The resource to check the admin role for
|
56
|
+
# @return [Boolean] Whether the user has the admin role for the resource
|
57
|
+
def spree_admin?(resource = nil)
|
58
|
+
resource ||= Spree::Store.current
|
59
|
+
has_spree_role?(Spree::Role::ADMIN_ROLE, resource)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Returns the user who invited the current user
|
63
|
+
# @return [Spree.admin_user_class]
|
64
|
+
def invited_by
|
65
|
+
invitations.first&.inviter
|
40
66
|
end
|
41
67
|
end
|
42
68
|
end
|
data/app/models/spree/ability.rb
CHANGED
@@ -23,15 +23,16 @@ module Spree
|
|
23
23
|
abilities.delete(ability)
|
24
24
|
end
|
25
25
|
|
26
|
-
def initialize(user)
|
26
|
+
def initialize(user, options = {})
|
27
27
|
alias_cancan_delete_action
|
28
28
|
|
29
29
|
user ||= Spree.user_class.new
|
30
|
+
store ||= options[:store] || Spree::Current.store
|
30
31
|
|
31
|
-
if user.persisted? && user.try(:spree_admin
|
32
|
-
apply_admin_permissions(user)
|
32
|
+
if user.persisted? && user.is_a?(Spree.admin_user_class) && user.try(:spree_admin?, store)
|
33
|
+
apply_admin_permissions(user, options)
|
33
34
|
else
|
34
|
-
apply_user_permissions(user)
|
35
|
+
apply_user_permissions(user, options)
|
35
36
|
end
|
36
37
|
|
37
38
|
# Include any abilities registered by extensions, etc.
|
@@ -56,7 +57,7 @@ module Spree
|
|
56
57
|
alias_action :create, :update, :destroy, to: :modify
|
57
58
|
end
|
58
59
|
|
59
|
-
def apply_admin_permissions(
|
60
|
+
def apply_admin_permissions(_user, _options)
|
60
61
|
can :manage, :all
|
61
62
|
cannot :cancel, Spree::Order
|
62
63
|
can :cancel, Spree::Order, &:allow_cancel?
|
@@ -64,7 +65,7 @@ module Spree
|
|
64
65
|
cannot [:edit, :update], Spree::ReimbursementType, mutable: false
|
65
66
|
end
|
66
67
|
|
67
|
-
def apply_user_permissions(user)
|
68
|
+
def apply_user_permissions(user, _options)
|
68
69
|
can :read, ::Spree::Country
|
69
70
|
can :read, ::Spree::OptionType
|
70
71
|
can :read, ::Spree::OptionValue
|
@@ -95,6 +96,7 @@ module Spree
|
|
95
96
|
can [:create, :update, :destroy], ::Spree::WishedItem do |wished_item|
|
96
97
|
wished_item.wishlist.user == user
|
97
98
|
end
|
99
|
+
can :accept, Spree::Invitation, invitee_id: [user.id, nil], invitee_type: user.class.name, status: 'pending'
|
98
100
|
end
|
99
101
|
|
100
102
|
def protect_admin_role
|
data/app/models/spree/asset.rb
CHANGED
@@ -17,12 +17,7 @@ module Spree
|
|
17
17
|
|
18
18
|
store_accessor :private_metadata, :session_uploaded_assets_uuid
|
19
19
|
scope :with_session_uploaded_assets_uuid, lambda { |uuid|
|
20
|
-
|
21
|
-
when 'PostgreSQL'
|
22
|
-
where("#{table_name}.private_metadata @> ?", { session_uploaded_assets_uuid: uuid }.to_json)
|
23
|
-
when 'Mysql2', 'SQLite'
|
24
|
-
where("JSON_EXTRACT(private_metadata, '$.session_uploaded_assets_uuid') = '#{uuid}'")
|
25
|
-
end
|
20
|
+
where(session_id: uuid)
|
26
21
|
}
|
27
22
|
|
28
23
|
def product
|
data/app/models/spree/base.rb
CHANGED
@@ -2,6 +2,7 @@ class Spree::Base < ApplicationRecord
|
|
2
2
|
include Spree::Preferences::Preferable
|
3
3
|
include Spree::RansackableAttributes
|
4
4
|
include Spree::TranslatableResourceScopes
|
5
|
+
include Spree::IntegrationsConcern
|
5
6
|
|
6
7
|
after_initialize do
|
7
8
|
if has_attribute?(:preferences) && !preferences.nil?
|
@@ -5,13 +5,16 @@ module Spree
|
|
5
5
|
# @param opts [Hash] The options
|
6
6
|
# @option opts [User] :user
|
7
7
|
# @option opts [String] :session_id
|
8
|
+
# @option opts [Spree::Store] :store
|
8
9
|
def initialize(opts = {})
|
9
10
|
@user = opts[:user]
|
10
11
|
@session = opts[:session]
|
11
12
|
@request = opts[:request]
|
13
|
+
@store = opts[:store]
|
14
|
+
@visitor_id = opts[:visitor_id]
|
12
15
|
end
|
13
16
|
|
14
|
-
attr_reader :user, :session, :request
|
17
|
+
attr_reader :user, :session, :request, :store, :visitor_id
|
15
18
|
|
16
19
|
# Returns the client
|
17
20
|
# @return [Object] The client object
|
@@ -42,7 +45,9 @@ module Spree
|
|
42
45
|
def identity_hash
|
43
46
|
{
|
44
47
|
user_id: user&.id,
|
45
|
-
|
48
|
+
# session.id is a custom class (not a string), which has overridden the `to_json` method, we have to convert it to a string first so it does not send garbage to the analytics service
|
49
|
+
session_id: session&.id&.to_s,
|
50
|
+
visitor_id: visitor_id
|
46
51
|
}
|
47
52
|
end
|
48
53
|
end
|
@@ -23,5 +23,18 @@ module Spree
|
|
23
23
|
group("#{Spree::Classification.table_name}.id").
|
24
24
|
reorder(completed_orders_count: order_direction, completed_orders_total: order_direction)
|
25
25
|
}
|
26
|
+
|
27
|
+
scope :grouped_taxon_ids_for_products, lambda { |product_ids, taxon_groups|
|
28
|
+
where(product_id: product_ids, taxon_id: taxon_groups).
|
29
|
+
group(:product_id).
|
30
|
+
then do |query|
|
31
|
+
case ActiveRecord::Base.connection.adapter_name
|
32
|
+
when 'PostgreSQL'
|
33
|
+
query.pluck(:product_id, Arel.sql("STRING_AGG(taxon_id::text, ',')"))
|
34
|
+
when 'Mysql2', 'SQLite'
|
35
|
+
query.pluck(:product_id, Arel.sql('GROUP_CONCAT(taxon_id)'))
|
36
|
+
end
|
37
|
+
end
|
38
|
+
}
|
26
39
|
end
|
27
40
|
end
|
@@ -14,7 +14,7 @@ module Spree
|
|
14
14
|
# Validations
|
15
15
|
#
|
16
16
|
validates :url, presence: true, uniqueness: true, format: {
|
17
|
-
with: %r{[a-
|
17
|
+
with: %r{\A(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\z}i
|
18
18
|
}, length: { in: 1..63 }
|
19
19
|
validate :url_is_valid
|
20
20
|
|
@@ -26,6 +26,7 @@ module Spree
|
|
26
26
|
after_validation :ensure_default, on: :create
|
27
27
|
|
28
28
|
def url_is_valid
|
29
|
+
return if url.blank?
|
29
30
|
parts = url.split('.')
|
30
31
|
|
31
32
|
errors.add(:url, 'use domain or subdomain') if (parts[0] != 'www' && parts.size > 3) || (parts[0] == 'www' && parts.size > 4) || parts.size < 2
|