spree_core 5.2.0 → 5.2.2

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.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/app/finders/spree/line_items/find_by_variant.rb +1 -1
  3. data/app/helpers/spree/images_helper.rb +34 -18
  4. data/app/helpers/spree/mail_helper.rb +1 -1
  5. data/app/jobs/spree/imports/create_rows_job.rb +1 -1
  6. data/app/jobs/spree/variants/remove_line_item_job.rb +1 -1
  7. data/app/models/spree/ability.rb +75 -10
  8. data/app/models/spree/asset.rb +26 -2
  9. data/app/models/spree/export.rb +1 -1
  10. data/app/models/spree/image/configuration/active_storage.rb +6 -15
  11. data/app/models/spree/image.rb +2 -1
  12. data/app/models/spree/import.rb +1 -1
  13. data/app/models/spree/order/gift_card.rb +3 -3
  14. data/app/models/spree/order/store_credit.rb +2 -2
  15. data/app/models/spree/order.rb +4 -4
  16. data/app/models/spree/order_contents.rb +9 -9
  17. data/app/models/spree/order_merger.rb +3 -3
  18. data/app/models/spree/page_sections/featured_taxon.rb +1 -2
  19. data/app/models/spree/permission_sets/base.rb +81 -0
  20. data/app/models/spree/permission_sets/configuration_management.rb +47 -0
  21. data/app/models/spree/permission_sets/dashboard_display.rb +17 -0
  22. data/app/models/spree/permission_sets/default_customer.rb +66 -0
  23. data/app/models/spree/permission_sets/order_display.rb +27 -0
  24. data/app/models/spree/permission_sets/order_management.rb +33 -0
  25. data/app/models/spree/permission_sets/product_display.rb +27 -0
  26. data/app/models/spree/permission_sets/product_management.rb +27 -0
  27. data/app/models/spree/permission_sets/promotion_management.rb +22 -0
  28. data/app/models/spree/permission_sets/role_management.rb +21 -0
  29. data/app/models/spree/permission_sets/stock_display.rb +19 -0
  30. data/app/models/spree/permission_sets/stock_management.rb +19 -0
  31. data/app/models/spree/permission_sets/super_user.rb +28 -0
  32. data/app/models/spree/permission_sets/user_display.rb +19 -0
  33. data/app/models/spree/permission_sets/user_management.rb +20 -0
  34. data/app/models/spree/promotion/actions/create_line_items.rb +6 -6
  35. data/app/models/spree/promotion_handler/coupon.rb +1 -1
  36. data/app/models/spree/shipping_method.rb +1 -1
  37. data/app/models/spree/store.rb +1 -1
  38. data/app/models/spree/themes/default.rb +7 -4
  39. data/app/presenters/spree/csv/product_variant_presenter.rb +1 -4
  40. data/app/services/spree/cart/add_item.rb +2 -2
  41. data/app/services/spree/cart/remove_item.rb +4 -4
  42. data/app/services/spree/cart/remove_line_item.rb +3 -3
  43. data/app/services/spree/cart/remove_out_of_stock_items.rb +1 -1
  44. data/app/services/spree/cart/set_quantity.rb +1 -1
  45. data/app/services/spree/checkout/advance.rb +1 -1
  46. data/app/services/spree/checkout/complete.rb +1 -1
  47. data/app/services/spree/data_feeds/google/rss.rb +4 -4
  48. data/app/services/spree/line_items/helper.rb +1 -1
  49. data/app/services/spree/shipments/helper.rb +2 -2
  50. data/config/locales/en.yml +1 -0
  51. data/lib/friendly_id/history_decorator.rb +26 -0
  52. data/lib/generators/spree/install/templates/config/initializers/spree.rb +25 -0
  53. data/lib/spree/core/configuration.rb +30 -0
  54. data/lib/spree/core/controller_helpers/auth.rb +1 -1
  55. data/lib/spree/core/controller_helpers/store.rb +1 -1
  56. data/lib/spree/core/dependencies.rb +2 -2
  57. data/lib/spree/core/dependencies_helper.rb +100 -5
  58. data/lib/spree/core/permission_configuration.rb +102 -0
  59. data/lib/spree/core/search/base.rb +1 -1
  60. data/lib/spree/core/version.rb +1 -1
  61. data/lib/spree/core.rb +51 -0
  62. data/lib/spree/testing_support/authorization_helpers.rb +2 -2
  63. data/lib/spree_core.rb +1 -0
  64. data/lib/tasks/dependencies.rake +76 -0
  65. metadata +25 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ae177c6a311a310cbcb6d6faf5b879afc52177f9f6bf4c7063edb5ece85fc755
4
- data.tar.gz: 54794ac79f785c9965c7a1f69fe0c88470fdf751cb789cab9db047b6d9ddbaa4
3
+ metadata.gz: 40e658898150457b9405d6d68e58d78c1b31b23518e2ffdb6829d3bfb0d68a90
4
+ data.tar.gz: e36b7e5d2e39426e2ea9cb4078625f72c36ed58e626959b277174f7f033bdd1f
5
5
  SHA512:
6
- metadata.gz: a0f404ec813d9fc4eaabcab6cac7a461aae2671d43a55c5b59b3e53c10fbb442acf6fa01171690999f576f93e90ab77a26f767881f77dbe6f14dd73ec5619689
7
- data.tar.gz: d4bacd845e8c842ab4ee8e394feb1006a4289c85ca7569e017eee6053fe29492dfc30466e413793b1af07b00a2f2575487d8746cf9a9ecdf959292b4cc0e6763
6
+ metadata.gz: f01a4d3e5245986e4a851dc51fb18103405cf2fab2669a43d504c922a5e7284739a23f6cd17da01276a6ab9a71b4c9589667a4bd7e3373d2c83a9508ea9b87e4
7
+ data.tar.gz: 1be2f451d4c370d7c72eee740dfce1536308d61ad7f16c114a7db2eb2462401150ca874b56cda455645abccdc65e9bc97e5810ef82dac16a263ed64ea584c7f7
@@ -5,7 +5,7 @@ module Spree
5
5
  line_item = order.line_items.loaded? ? order.line_items.detect { |li| li.variant_id == variant.id } : order.line_items.find_by(variant_id: variant.id)
6
6
 
7
7
  if line_item
8
- Spree::Dependencies.cart_compare_line_items_service.constantize.call(order: order, line_item: line_item, options: options).value
8
+ Spree.cart_compare_line_items_service.call(order: order, line_item: line_item, options: options).value
9
9
  end
10
10
 
11
11
  line_item
@@ -7,10 +7,17 @@ module Spree
7
7
  # @param options [Hash] options for the image tag
8
8
  # @option options [Integer] :width the width of the image
9
9
  # @option options [Integer] :height the height of the image
10
+ # @option options [Symbol] :variant use a preprocessed named variant (e.g., :mini, :small, :medium, :large, :xlarge)
10
11
  def spree_image_tag(image, options = {})
12
+ url_options = if options[:variant].present?
13
+ { variant: options[:variant] }
14
+ else
15
+ { width: options[:width], height: options[:height], format: options[:format] }
16
+ end
17
+
11
18
  image_tag(
12
- spree_image_url(image, { width: options[:width], height: options[:height], format: options[:format] }),
13
- options
19
+ spree_image_url(image, url_options),
20
+ options.except(:variant, :format)
14
21
  )
15
22
  end
16
23
 
@@ -19,9 +26,17 @@ module Spree
19
26
  return unless image.variable?
20
27
  return if image.respond_to?(:attached?) && !image.attached?
21
28
  url_helpers = respond_to?(:main_app) ? main_app : Rails.application.routes.url_helpers
29
+
30
+ # Use preprocessed named variant if specified (e.g., :mini, :small, :medium, :large, :xlarge)
31
+ if options[:variant].present?
32
+ return url_helpers.cdn_image_url(image.variant(options[:variant]))
33
+ end
34
+
35
+ # Dynamic variant generation for width/height options
22
36
  width = options[:width]
23
37
  height = options[:height]
24
38
 
39
+ # Double dimensions for retina displays
25
40
  width *= 2 if width.present?
26
41
  height *= 2 if height.present?
27
42
 
@@ -74,27 +89,28 @@ module Spree
74
89
  # @param options [Hash] options for the image variant
75
90
  # @option options [Integer] :width the width of the image
76
91
  # @option options [Integer] :height the height of the image
92
+ #
93
+ # @note The key order matters for variation digest matching with preprocessed variants.
94
+ # Active Storage reorders keys alphabetically, so use: format, resize_to_fill/limit, saver
95
+ # @note Use string values (not symbols) for format because variation keys are JSON-encoded
96
+ # in URLs and JSON converts symbols to strings, causing digest mismatches.
77
97
  def spree_image_variant_options(options = {})
78
- {
79
- saver: options[:format] == :png ? png_variant_options : webp_variant_options,
80
- format: options[:format] || :webp
81
- }.merge(options.except(:format))
98
+ format_opt = options[:format]&.to_s
99
+ saver_options = format_opt == "png" ? png_saver_options : Spree::Asset::WEBP_SAVER_OPTIONS
100
+ format = format_opt || "webp"
101
+
102
+ # Build hash in alphabetical order to match Active Storage's key ordering
103
+ result = {}
104
+ result[:format] = format
105
+ result[:resize_to_fill] = options[:resize_to_fill] if options[:resize_to_fill]
106
+ result[:resize_to_limit] = options[:resize_to_limit] if options[:resize_to_limit]
107
+ result[:saver] = saver_options
108
+ result
82
109
  end
83
110
 
84
111
  private
85
112
 
86
- def webp_variant_options
87
- {
88
- strip: true,
89
- quality: 75,
90
- lossless: false,
91
- alpha_q: 85,
92
- reduction_effort: 6,
93
- smart_subsample: true
94
- }
95
- end
96
-
97
- def png_variant_options
113
+ def png_saver_options
98
114
  {
99
115
  strip: true,
100
116
  compression_level: 8,
@@ -5,7 +5,7 @@ module Spree
5
5
 
6
6
  def variant_image_url(variant)
7
7
  image = variant.default_image
8
- image.present? && image.attached? ? spree_image_url(image, width: 100, height: 100, format: :png) : image_url('noimage/small.png')
8
+ image.present? && image.attached? ? spree_image_url(image, variant: :mini) : image_url('noimage/small.png')
9
9
  end
10
10
 
11
11
  def name_for(order)
@@ -31,7 +31,7 @@ module Spree
31
31
 
32
32
  upsert_options = {}
33
33
  if %w[PostgreSQL SQLite].include?(ActiveRecord::Base.connection.adapter_name)
34
- upsert_options[:unique_by] = %i[import_id row_number]
34
+ upsert_options[:unique_by] = :index_spree_import_rows_on_import_id_and_row_number
35
35
  end
36
36
  # NOTE: Ensure a DB-level unique index on [:import_id, :row_number] for all adapters.
37
37
 
@@ -4,7 +4,7 @@ module Spree
4
4
  queue_as Spree.queues.variants
5
5
 
6
6
  def perform(line_item:)
7
- Spree::Dependencies.cart_remove_line_item_service.constantize.call(order: line_item.order, line_item: line_item)
7
+ Spree.cart_remove_line_item_service.call(order: line_item.order, line_item: line_item)
8
8
  end
9
9
  end
10
10
  end
@@ -1,6 +1,15 @@
1
1
  # Implementation class for Cancan gem. Instead of overriding this class, consider adding new permissions
2
2
  # using the special +register_ability+ method which allows extensions to add their own abilities.
3
3
  #
4
+ # The preferred way to add permissions is now through permission sets. See Spree::PermissionSets::Base
5
+ # for more details on creating custom permission sets.
6
+ #
7
+ # @example Configuring role permissions
8
+ # Spree.permissions.assign(:customer_service, [
9
+ # Spree::PermissionSets::OrderDisplay,
10
+ # Spree::PermissionSets::UserManagement
11
+ # ])
12
+ #
4
13
  # See https://github.com/CanCanCommunity/cancancan for more details.
5
14
  require 'cancan'
6
15
 
@@ -11,6 +20,12 @@ module Spree
11
20
  class_attribute :abilities
12
21
  self.abilities = Set.new
13
22
 
23
+ # @return [Object] the current user
24
+ attr_reader :user
25
+
26
+ # @return [Spree::Store, nil] the current store
27
+ attr_reader :store
28
+
14
29
  # Allows us to go beyond the standard cancan initialize method which makes it difficult for engines to
15
30
  # modify the default +Ability+ of an application. The +ability+ argument must be a class that includes
16
31
  # the +CanCan::Ability+ module. The registered ability should behave properly as a stand-alone class
@@ -26,22 +41,16 @@ module Spree
26
41
  def initialize(user, options = {})
27
42
  alias_cancan_delete_action
28
43
 
29
- user ||= Spree.user_class.new
30
- store ||= options[:store] || Spree::Current.store
44
+ @user = user || Spree.user_class.new
45
+ @store = options[:store] || Spree::Current.store
31
46
 
32
- if user.persisted? && user.is_a?(Spree.admin_user_class) && user.try(:spree_admin?, store)
33
- apply_admin_permissions(user, options)
34
- else
35
- apply_user_permissions(user, options)
36
- end
47
+ apply_permissions_from_sets
37
48
 
38
49
  # Include any abilities registered by extensions, etc.
39
50
  # this is legacy behaviour and should be removed in Spree 5.0
40
51
  Ability.abilities.merge(abilities_to_register).each do |clazz|
41
- merge clazz.new(user)
52
+ merge clazz.new(@user)
42
53
  end
43
-
44
- protect_admin_role
45
54
  end
46
55
 
47
56
  protected
@@ -57,6 +66,62 @@ module Spree
57
66
  alias_action :create, :update, :destroy, to: :modify
58
67
  end
59
68
 
69
+ # Applies permissions based on the user's roles and the configured permission sets.
70
+ def apply_permissions_from_sets
71
+ role_names = determine_role_names
72
+ permission_sets = Spree.permissions.permission_sets_for_roles(role_names)
73
+
74
+ # If no permission sets are configured for the user's roles, use legacy behavior
75
+ if permission_sets.empty?
76
+ apply_legacy_permissions
77
+ else
78
+ activate_permission_sets(permission_sets)
79
+ end
80
+ end
81
+
82
+ # Determines the role names for the current user.
83
+ #
84
+ # @return [Array<Symbol>] the role names
85
+ def determine_role_names
86
+ return [:default] unless @user.persisted?
87
+
88
+ # First, try to get roles from the spree_roles association
89
+ if @user.respond_to?(:spree_roles)
90
+ role_names = @user.spree_roles.pluck(:name).map(&:to_sym)
91
+ return role_names if role_names.any?
92
+ end
93
+
94
+ # Fall back to checking spree_admin? for backward compatibility
95
+ # This supports cases where roles are mocked or admin status is determined differently
96
+ if @user.try(:spree_admin?, @store)
97
+ [:admin]
98
+ else
99
+ [:default]
100
+ end
101
+ end
102
+
103
+ # Activates the given permission sets.
104
+ #
105
+ # @param permission_sets [Array<Class>] the permission set classes to activate
106
+ def activate_permission_sets(permission_sets)
107
+ permission_sets.each do |permission_set_class|
108
+ permission_set = permission_set_class.new(self)
109
+ permission_set.activate!
110
+ end
111
+ end
112
+
113
+ # Legacy permission application for backward compatibility.
114
+ # This is used when no permission sets are configured for the user's roles.
115
+ def apply_legacy_permissions
116
+ if @user.persisted? && @user.is_a?(Spree.admin_user_class) && @user.try(:spree_admin?, @store)
117
+ apply_admin_permissions(@user, { store: @store })
118
+ else
119
+ apply_user_permissions(@user, { store: @store })
120
+ end
121
+
122
+ protect_admin_role
123
+ end
124
+
60
125
  def apply_admin_permissions(_user, _options)
61
126
  can :manage, :all
62
127
  cannot :cancel, Spree::Order
@@ -12,9 +12,33 @@ module Spree
12
12
  belongs_to :viewable, polymorphic: true, touch: true
13
13
  acts_as_list scope: [:viewable_id, :viewable_type]
14
14
 
15
- delegate :key, :attached?, :variant, :variable?, :blob, :filename, to: :attachment
15
+ delegate :key, :attached?, :variant, :variable?, :blob, :filename, :variation, to: :attachment
16
16
 
17
- has_one_attached :attachment, service: Spree.public_storage_service_name
17
+ WEBP_SAVER_OPTIONS = {
18
+ strip: true,
19
+ quality: 75,
20
+ lossless: false,
21
+ alpha_q: 85,
22
+ reduction_effort: 6,
23
+ smart_subsample: true
24
+ }.freeze
25
+
26
+ has_one_attached :attachment, service: Spree.public_storage_service_name do |attachable|
27
+ # Note: Key order matters for variation digest matching.
28
+ # Active Storage reorders keys alphabetically when calling variant(:name),
29
+ # so we must define them in alphabetical order: format, resize_to_fill, saver
30
+ #
31
+ # IMPORTANT: Use string values (not symbols) for format because the variation key
32
+ # is JSON-encoded in URLs. JSON converts symbols to strings, so "webp" != :webp
33
+ # after round-tripping, which causes digest mismatches.
34
+ Spree::Config.product_image_variant_sizes.each do |name, (width, height)|
35
+ attachable.variant name,
36
+ format: "webp",
37
+ resize_to_fill: [width, height],
38
+ saver: WEBP_SAVER_OPTIONS,
39
+ preprocessed: true
40
+ end
41
+ end
18
42
 
19
43
  default_scope { includes(attachment_attachment: :blob) }
20
44
 
@@ -157,7 +157,7 @@ module Spree
157
157
  end
158
158
 
159
159
  def current_ability
160
- @current_ability ||= Spree::Dependencies.ability_class.constantize.new(user, { store: store })
160
+ @current_ability ||= Spree.ability_class.new(user, { store: store })
161
161
  end
162
162
 
163
163
  # eg. Spree::Exports::Products => products-store-my-store-code-20241030133348.csv
@@ -7,25 +7,16 @@ module Spree
7
7
  included do
8
8
  validates :attachment, attached: true, content_type: Rails.application.config.active_storage.web_image_content_types
9
9
 
10
+ # Returns image styles derived from Spree::Config.product_image_variant_sizes
11
+ # Format: { variant_name: 'WxH>' } for API compatibility
10
12
  def self.styles
11
- @styles ||= {
12
- mini: '48x48>',
13
- small: '100x100>',
14
- product: '240x240>',
15
- pdp_thumbnail: '160x200>',
16
- plp_and_carousel: '448x600>',
17
- plp_and_carousel_xs: '254x340>',
18
- plp_and_carousel_sm: '350x468>',
19
- plp_and_carousel_md: '222x297>',
20
- plp_and_carousel_lg: '278x371>',
21
- large: '600x600>',
22
- plp: '278x371>',
23
- zoomed: '650x870>'
24
- }
13
+ @styles ||= Spree::Config.product_image_variant_sizes.transform_values do |dimensions|
14
+ "#{dimensions[0]}x#{dimensions[1]}>"
15
+ end
25
16
  end
26
17
 
27
18
  def default_style
28
- :product
19
+ :small
29
20
  end
30
21
  end
31
22
  end
@@ -51,7 +51,8 @@ module Spree
51
51
  end
52
52
 
53
53
  def plp_url
54
- generate_url(size: self.class.styles[:plp_and_carousel])
54
+ Spree::Deprecation.warn "Image#plp_url is deprecated. Use variant(:large) instead."
55
+ generate_url(size: self.class.styles[:large])
55
56
  end
56
57
 
57
58
  private
@@ -217,7 +217,7 @@ module Spree
217
217
  # Returns the current ability for the import
218
218
  # @return [Spree::Ability]
219
219
  def current_ability
220
- @current_ability ||= Spree::Dependencies.ability_class.constantize.new(user, { store: store })
220
+ @current_ability ||= Spree.ability_class.new(user, { store: store })
221
221
  end
222
222
 
223
223
  class << self
@@ -25,13 +25,13 @@ module Spree
25
25
  # @param gift_card [Spree::GiftCard] the gift card to apply
26
26
  # @return [Spree::Order] the order with the gift card applied
27
27
  def apply_gift_card(gift_card)
28
- Spree::Dependencies.gift_card_apply_service.constantize.call(gift_card: gift_card, order: self)
28
+ Spree.gift_card_apply_service.call(gift_card: gift_card, order: self)
29
29
  end
30
30
 
31
31
  # Removes a gift card from the order
32
32
  # @return [Spree::Order] the order with the gift card removed
33
33
  def remove_gift_card
34
- Spree::Dependencies.gift_card_remove_service.constantize.call(order: self)
34
+ Spree.gift_card_remove_service.call(order: self)
35
35
  end
36
36
 
37
37
  def recalculate_gift_card
@@ -44,7 +44,7 @@ module Spree
44
44
  def redeem_gift_card
45
45
  return unless gift_card.present?
46
46
 
47
- Spree::Dependencies.gift_card_redeem_service.constantize.call(gift_card: gift_card)
47
+ Spree.gift_card_redeem_service.call(gift_card: gift_card)
48
48
  end
49
49
  end
50
50
  end
@@ -2,11 +2,11 @@ module Spree
2
2
  class Order < Spree.base_class
3
3
  module StoreCredit
4
4
  def add_store_credit_payments(amount = nil)
5
- Spree::Dependencies.checkout_add_store_credit_service.constantize.call(order: self, amount: amount)
5
+ Spree.checkout_add_store_credit_service.call(order: self, amount: amount)
6
6
  end
7
7
 
8
8
  def remove_store_credit_payments
9
- Spree::Dependencies.checkout_remove_store_credit_service.constantize.call(order: self)
9
+ Spree.checkout_remove_store_credit_service.call(order: self)
10
10
  end
11
11
 
12
12
  def covered_by_store_credit?
@@ -349,7 +349,7 @@ module Spree
349
349
  end
350
350
 
351
351
  def updater
352
- @updater ||= Spree::Dependencies.order_updater.constantize.new(self)
352
+ @updater ||= Spree.order_updater.new(self)
353
353
  end
354
354
 
355
355
  def update_with_updater!
@@ -407,7 +407,7 @@ module Spree
407
407
  def find_line_item_by_variant(variant, options = {})
408
408
  line_items.detect do |line_item|
409
409
  line_item.variant_id == variant.id &&
410
- Spree::Dependencies.cart_compare_line_items_service.constantize.new.call(order: self, line_item: line_item, options: options).value
410
+ Spree.cart_compare_line_items_service.new.call(order: self, line_item: line_item, options: options).value
411
411
  end
412
412
  end
413
413
 
@@ -578,7 +578,7 @@ module Spree
578
578
  def empty!
579
579
  raise Spree.t(:cannot_empty_completed_order) if completed?
580
580
 
581
- result = Spree::Dependencies.cart_empty_service.constantize.call(order: self)
581
+ result = Spree.cart_empty_service.call(order: self)
582
582
  result.value
583
583
  end
584
584
 
@@ -957,7 +957,7 @@ module Spree
957
957
  if gift_card.present?
958
958
  recalculate_gift_card
959
959
  elsif using_store_credit?
960
- Spree::Dependencies.checkout_add_store_credit_service.constantize.call(order: self)
960
+ Spree.checkout_add_store_credit_service.call(order: self)
961
961
  end
962
962
  end
963
963
  end
@@ -7,17 +7,17 @@ module Spree
7
7
  end
8
8
 
9
9
  def add(variant, quantity = 1, options = {})
10
- Spree::Dependencies.cart_add_item_service.constantize.call(order: order,
11
- variant: variant,
12
- quantity: quantity,
13
- options: options).value
10
+ Spree.cart_add_item_service.call(order: order,
11
+ variant: variant,
12
+ quantity: quantity,
13
+ options: options).value
14
14
  end
15
15
 
16
16
  def remove(variant, quantity = 1, options = {})
17
- Spree::Dependencies.cart_remove_item_service.constantize.call(order: order,
18
- variant: variant,
19
- quantity: quantity,
20
- options: options).value
17
+ Spree.cart_remove_item_service.call(order: order,
18
+ variant: variant,
19
+ quantity: quantity,
20
+ options: options).value
21
21
  end
22
22
 
23
23
  def remove_line_item(line_item, options = {})
@@ -25,7 +25,7 @@ module Spree
25
25
  end
26
26
 
27
27
  def update_cart(params)
28
- Spree::Dependencies.cart_update_service.constantize.call(order: order, params: params).value
28
+ Spree.cart_update_service.call(order: order, params: params).value
29
29
  end
30
30
  end
31
31
  end
@@ -35,9 +35,9 @@ module Spree
35
35
  def find_matching_line_item(other_order_line_item)
36
36
  order.line_items.detect do |my_li|
37
37
  my_li.variant == other_order_line_item.variant &&
38
- Spree::Dependencies.cart_compare_line_items_service.constantize.new.call(order: order,
39
- line_item: my_li,
40
- options: other_order_line_item.serializable_hash).value
38
+ Spree.cart_compare_line_items_service.new.call(order: order,
39
+ line_item: my_li,
40
+ options: other_order_line_item.serializable_hash).value
41
41
  end
42
42
  end
43
43
 
@@ -61,8 +61,7 @@ module Spree
61
61
  sort_by: 'default'
62
62
  }
63
63
 
64
- products_finder = Spree::Dependencies.products_finder.constantize
65
- products_finder.new(scope: store.products, params: finder_params).execute.limit(preferred_max_products_to_show)
64
+ Spree.products_finder.new(scope: store.products, params: finder_params).execute.limit(preferred_max_products_to_show)
66
65
  end
67
66
  end
68
67
 
@@ -0,0 +1,81 @@
1
+ # Base class for all permission sets.
2
+ #
3
+ # Permission sets are reusable groups of permissions that can be assigned to roles.
4
+ # They provide a clean abstraction over CanCanCan abilities, making it easier to
5
+ # manage permissions in a modular way.
6
+ #
7
+ # @example Creating a custom permission set
8
+ # class Spree::PermissionSets::InventoryManagement < Spree::PermissionSets::Base
9
+ # def activate!
10
+ # can :manage, Spree::StockItem
11
+ # can :manage, Spree::StockLocation
12
+ # can :manage, Spree::StockMovement
13
+ # end
14
+ # end
15
+ #
16
+ # @example Assigning the permission set to a role
17
+ # Spree.permissions.assign(:warehouse_manager, Spree::PermissionSets::InventoryManagement)
18
+ #
19
+ module Spree
20
+ module PermissionSets
21
+ class Base
22
+ # @return [CanCan::Ability] the ability instance to add permissions to
23
+ attr_reader :ability
24
+
25
+ # @param ability [CanCan::Ability] the ability instance to add permissions to
26
+ def initialize(ability)
27
+ @ability = ability
28
+ end
29
+
30
+ # Activates this permission set by adding its permissions to the ability.
31
+ # Override this method in subclasses to define the permissions.
32
+ #
33
+ # @abstract
34
+ # @return [void]
35
+ def activate!
36
+ raise NotImplementedError, "#{self.class} must implement #activate!"
37
+ end
38
+
39
+ protected
40
+
41
+ # Delegates the `can` method to the ability instance.
42
+ #
43
+ # @param args [Array] arguments to pass to CanCan::Ability#can
44
+ # @param block [Proc] optional block for conditional permissions
45
+ def can(*args, &block)
46
+ ability.can(*args, &block)
47
+ end
48
+
49
+ # Delegates the `cannot` method to the ability instance.
50
+ #
51
+ # @param args [Array] arguments to pass to CanCan::Ability#cannot
52
+ # @param block [Proc] optional block for conditional permissions
53
+ def cannot(*args, &block)
54
+ ability.cannot(*args, &block)
55
+ end
56
+
57
+ # Delegates the `can?` method to the ability instance.
58
+ #
59
+ # @param args [Array] arguments to pass to CanCan::Ability#can?
60
+ # @return [Boolean]
61
+ def can?(*args)
62
+ ability.can?(*args)
63
+ end
64
+
65
+ # Returns the user from the ability instance.
66
+ # This method assumes the ability has a user accessor.
67
+ #
68
+ # @return [Object] the current user
69
+ def user
70
+ ability.user
71
+ end
72
+
73
+ # Returns the store from the ability instance options.
74
+ #
75
+ # @return [Spree::Store, nil] the current store
76
+ def store
77
+ ability.store
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,47 @@
1
+ # Permission set for managing store configuration and settings.
2
+ #
3
+ # This permission set provides access to manage store settings,
4
+ # payment methods, shipping methods, and other configuration.
5
+ #
6
+ # @example
7
+ # Spree.permissions.assign(:store_admin, Spree::PermissionSets::ConfigurationManagement)
8
+ #
9
+ module Spree
10
+ module PermissionSets
11
+ class ConfigurationManagement < Base
12
+ def activate!
13
+ # Store settings
14
+ can :manage, Spree::Store
15
+
16
+ # Payment configuration
17
+ can :manage, Spree::PaymentMethod
18
+ can :manage, Spree::Gateway
19
+
20
+ # Shipping configuration
21
+ can :manage, Spree::ShippingMethod
22
+ can :manage, Spree::ShippingCategory
23
+ can :manage, Spree::Zone
24
+ can :manage, Spree::ZoneMember
25
+
26
+ # Tax configuration
27
+ can :manage, Spree::TaxCategory
28
+ can :manage, Spree::TaxRate
29
+
30
+ # General configuration
31
+ can :manage, Spree::RefundReason
32
+ can :manage, Spree::ReimbursementType
33
+ can :manage, Spree::ReturnReason
34
+
35
+ # Restrictions on immutable types
36
+ cannot [:edit, :update], Spree::RefundReason, mutable: false
37
+ cannot [:edit, :update], Spree::ReimbursementType, mutable: false
38
+
39
+ # Metafield configuration
40
+ can :manage, Spree::MetafieldDefinition
41
+
42
+ # Policies
43
+ can :manage, Spree::Policy
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,17 @@
1
+ # Permission set for viewing the admin dashboard.
2
+ #
3
+ # This permission set provides access to view the admin dashboard
4
+ # and basic admin navigation.
5
+ #
6
+ # @example
7
+ # Spree.permissions.assign(:viewer, Spree::PermissionSets::DashboardDisplay)
8
+ #
9
+ module Spree
10
+ module PermissionSets
11
+ class DashboardDisplay < Base
12
+ def activate!
13
+ can [:admin, :index, :show], :dashboard
14
+ end
15
+ end
16
+ end
17
+ end