spree_core 5.0.3 → 5.1.0.beta

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 (85) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/images/logo.png +0 -0
  3. data/app/finders/spree/products/find.rb +28 -1
  4. data/app/finders/spree/taxons/find.rb +1 -1
  5. data/app/helpers/spree/integrations_helper.rb +15 -0
  6. data/app/javascript/spree/core/controllers/disable_submit_button_controller.js +19 -0
  7. data/app/mailers/spree/invitation_mailer.rb +24 -0
  8. data/app/models/concerns/spree/integrations_concern.rb +11 -0
  9. data/app/models/concerns/spree/product_scopes.rb +6 -6
  10. data/app/models/concerns/spree/translatable_resource.rb +4 -0
  11. data/app/models/concerns/spree/translatable_resource_scopes.rb +17 -3
  12. data/app/models/concerns/spree/unique_name.rb +2 -0
  13. data/app/models/concerns/spree/user_management.rb +33 -0
  14. data/app/models/concerns/spree/user_methods.rb +19 -0
  15. data/app/models/concerns/spree/user_roles.rb +43 -17
  16. data/app/models/spree/ability.rb +8 -6
  17. data/app/models/spree/asset.rb +1 -6
  18. data/app/models/spree/base.rb +1 -0
  19. data/app/models/spree/base_analytics_event_handler.rb +7 -2
  20. data/app/models/spree/classification.rb +13 -0
  21. data/app/models/spree/custom_domain.rb +2 -1
  22. data/app/models/spree/export.rb +1 -1
  23. data/app/models/spree/integration.rb +63 -0
  24. data/app/models/spree/invitation.rb +153 -0
  25. data/app/models/spree/invitations/store.rb +6 -0
  26. data/app/models/spree/order.rb +17 -1
  27. data/app/models/spree/order_merger.rb +7 -5
  28. data/app/models/spree/page_blocks/products/buy_buttons.rb +8 -0
  29. data/app/models/spree/page_blocks/products/quantity_selector.rb +4 -0
  30. data/app/models/spree/page_blocks/products/variant_picker.rb +4 -0
  31. data/app/models/spree/page_sections/featured_product.rb +4 -0
  32. data/app/models/spree/page_sections/image_banner.rb +12 -0
  33. data/app/models/spree/page_sections/image_with_text.rb +12 -0
  34. data/app/models/spree/page_sections/newsletter.rb +1 -1
  35. data/app/models/spree/page_sections/rich_text.rb +11 -0
  36. data/app/models/spree/page_sections/video.rb +8 -0
  37. data/app/models/spree/product.rb +10 -2
  38. data/app/models/spree/product_property.rb +1 -1
  39. data/app/models/spree/property.rb +3 -1
  40. data/app/models/spree/reports/sales_total.rb +5 -1
  41. data/app/models/spree/role.rb +16 -0
  42. data/app/models/spree/role_user.rb +32 -1
  43. data/app/models/spree/shipment.rb +1 -1
  44. data/app/models/spree/shipment_handler.rb +1 -0
  45. data/app/models/spree/shipping_method.rb +1 -1
  46. data/app/models/spree/store.rb +11 -4
  47. data/app/models/spree/store_credit_category.rb +4 -0
  48. data/app/models/spree/taxon.rb +4 -3
  49. data/app/models/spree/variant.rb +9 -1
  50. data/app/services/spree/country_to_timezone.rb +273 -0
  51. data/app/services/spree/seeds/admin_user.rb +4 -2
  52. data/app/services/spree/seeds/all.rb +3 -1
  53. data/app/services/spree/seeds/digital_delivery.rb +20 -0
  54. data/app/services/spree/seeds/returns_environment.rb +27 -0
  55. data/app/services/spree/seeds/tax_categories.rb +12 -0
  56. data/app/services/spree/stores/settings_defaults_by_country.rb +38 -0
  57. data/app/services/spree/tags/bulk_add.rb +13 -7
  58. data/app/views/spree/invitation_mailer/invitation_accepted.html.erb +12 -0
  59. data/app/views/spree/invitation_mailer/invitation_email.html.erb +21 -0
  60. data/config/locales/en.yml +48 -9
  61. data/db/migrate/20250407085228_create_spree_integrations.rb +12 -0
  62. data/db/migrate/20250410061306_create_spree_invitations.rb +20 -0
  63. data/db/migrate/20250418174652_add_resource_to_spree_role_users.rb +8 -0
  64. data/db/migrate/20250508060800_add_selected_locale_to_spree_admin_users.rb +8 -0
  65. data/db/migrate/20250509143831_add_session_id_to_spree_assets.rb +5 -0
  66. data/lib/generators/spree/authentication/devise/devise_generator.rb +5 -2
  67. data/lib/generators/spree/authentication/devise/templates/authentication_helpers.rb.tt +3 -3
  68. data/lib/generators/spree/install/install_generator.rb +5 -0
  69. data/lib/spree/core/controller_helpers/auth.rb +16 -14
  70. data/lib/spree/core/controller_helpers/currency.rb +11 -0
  71. data/lib/spree/core/controller_helpers/order.rb +2 -1
  72. data/lib/spree/core/controller_helpers/strong_parameters.rb +3 -2
  73. data/lib/spree/core/engine.rb +13 -2
  74. data/lib/spree/core/version.rb +1 -1
  75. data/lib/spree/core.rb +1 -0
  76. data/lib/spree/permitted_attributes.rb +118 -13
  77. data/lib/spree/testing_support/capybara_config.rb +1 -1
  78. data/lib/spree/testing_support/factories/integration_factory.rb +7 -0
  79. data/lib/spree/testing_support/factories/invitation_factory.rb +6 -0
  80. data/lib/spree/testing_support/factories/promotion_action_factory.rb +4 -0
  81. data/lib/spree/testing_support/factories/stock_item_factory.rb +5 -1
  82. data/lib/spree/testing_support/factories/user_factory.rb +14 -1
  83. data/lib/spree/translation_migrations.rb +27 -15
  84. data/lib/tasks/core.rake +8 -0
  85. metadata +41 -4
@@ -0,0 +1,153 @@
1
+ module Spree
2
+ class Invitation < Spree.base_class
3
+ has_secure_token
4
+ acts_as_paranoid
5
+
6
+ #
7
+ # Virtual Attributes
8
+ #
9
+ attribute :skip_email, :boolean, default: false
10
+
11
+ #
12
+ # Associations
13
+ #
14
+ belongs_to :resource, polymorphic: true # eg. Store, Vendor, Account
15
+ belongs_to :inviter, polymorphic: true # User or AdminUser
16
+ belongs_to :invitee, polymorphic: true, optional: true # User or AdminUser
17
+ belongs_to :role, class_name: 'Spree::Role'
18
+ has_one :role_user, dependent: :destroy, class_name: 'Spree::RoleUser'
19
+
20
+ #
21
+ # Validations
22
+ #
23
+ validates :email, email: true, presence: true
24
+ validates :token, presence: true, uniqueness: true
25
+ validates :inviter, :resource, :role, presence: true
26
+ validate :invitee_is_not_inviter, on: :create
27
+ validate :invitee_already_exists, on: :create
28
+
29
+ #
30
+ # Scopes
31
+ #
32
+ scope :pending, -> { where(status: 'pending') }
33
+ scope :accepted, -> { where(status: 'accepted') }
34
+ scope :not_expired, -> { where('expires_at > ?', Time.current) }
35
+
36
+ #
37
+ # State Machine
38
+ #
39
+ state_machine initial: :pending, attribute: :status do
40
+ state :accepted do
41
+ validate :accept_invitation_within_time_limit
42
+ validates :invitee, presence: true
43
+ end
44
+
45
+ event :accept do
46
+ transition pending: :accepted
47
+ end
48
+ after_transition to: :accepted, do: :after_accept
49
+ end
50
+
51
+ #
52
+ # Callbacks
53
+ #
54
+ after_initialize :set_defaults, if: :new_record?
55
+ before_validation :set_invitee_from_email, on: :create
56
+ after_create :send_invitation_email, unless: :skip_email
57
+
58
+ # returns the store for the invitation
59
+ # if the resource is a store, return the resource
60
+ # if the resource responds to store, return the store
61
+ # otherwise, return the current store
62
+ # @return [Spree::Store]
63
+ def store
64
+ if resource.is_a?(Spree::Store)
65
+ resource
66
+ elsif resource.respond_to?(:store)
67
+ resource.store
68
+ else
69
+ Spree::Store.current
70
+ end
71
+ end
72
+
73
+ # returns true if the invitation has expired
74
+ # @return [Boolean]
75
+ def expired?
76
+ expires_at < Time.current
77
+ end
78
+
79
+ # Resends the invitation email if the invitation is pending and not expired
80
+ def resend!
81
+ return if expired? || deleted? || accepted?
82
+
83
+ send_invitation_email
84
+ end
85
+
86
+ private
87
+
88
+ # this method can be extended by developers now
89
+ def after_accept
90
+ create_role_user
91
+ set_accepted_at
92
+ send_acceptance_notification
93
+ end
94
+
95
+ def send_invitation_email
96
+ Spree::InvitationMailer.invitation_email(self).deliver_later
97
+ end
98
+
99
+ def send_acceptance_notification
100
+ Spree::InvitationMailer.invitation_accepted(self).deliver_later
101
+ end
102
+
103
+ def set_defaults
104
+ self.expires_at ||= 2.weeks.from_now
105
+ self.resource ||= Spree::Store.current
106
+ self.role ||= Spree::Role.default_admin_role
107
+ end
108
+
109
+ def invitee_is_not_inviter
110
+ if invitee == inviter
111
+ errors.add(:invitee, 'cannot be the same as the inviter')
112
+ end
113
+ end
114
+
115
+ def invitee_already_exists
116
+ return if resource.blank?
117
+
118
+ exists = if invitee.present?
119
+ resource.users.include?(invitee)
120
+ else
121
+ resource.users.exists?(email: email)
122
+ end
123
+
124
+ if exists
125
+ errors.add(:email, 'already exists')
126
+ end
127
+ end
128
+
129
+ def set_accepted_at
130
+ update!(accepted_at: Time.current)
131
+ end
132
+
133
+ def create_role_user
134
+ return if invitee.blank?
135
+
136
+ role_user = resource.add_user(invitee, role)
137
+ self.role_user = role_user
138
+ save!
139
+ end
140
+
141
+ def set_invitee_from_email
142
+ return if invitee.present?
143
+
144
+ self.invitee = Spree.admin_user_class.find_by(email: email)
145
+ end
146
+
147
+ def accept_invitation_within_time_limit
148
+ if Time.current > expires_at
149
+ errors.add(:base, 'Invitation expired')
150
+ end
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,6 @@
1
+ module Spree
2
+ module Invitations
3
+ class Store < Base
4
+ end
5
+ end
6
+ end
@@ -91,6 +91,8 @@ module Spree
91
91
  acts_as_taggable_on :tags
92
92
  acts_as_taggable_tenant :store_id
93
93
 
94
+ ASSOCIATED_USER_ATTRIBUTES = [:user_id, :email, :created_by_id, :bill_address_id, :ship_address_id]
95
+
94
96
  belongs_to :user, class_name: "::#{Spree.user_class}", optional: true, autosave: true
95
97
  belongs_to :created_by, class_name: "::#{Spree.admin_user_class}", optional: true
96
98
  belongs_to :approver, class_name: "::#{Spree.admin_user_class}", optional: true
@@ -247,6 +249,13 @@ module Spree
247
249
  pre_tax_item_amount + shipments.sum(:pre_tax_amount)
248
250
  end
249
251
 
252
+ # Returns the subtotal used for analytics integrations
253
+ # It's a sum of the item total and the promo total
254
+ # @return [Float]
255
+ def analytics_subtotal
256
+ (item_total + line_items.sum(:promo_total)).to_f
257
+ end
258
+
250
259
  def shipping_discount
251
260
  shipment_adjustments.non_tax.eligible.sum(:amount) * - 1
252
261
  end
@@ -355,7 +364,7 @@ module Spree
355
364
  self.bill_address ||= user.bill_address
356
365
  self.ship_address ||= user.ship_address
357
366
 
358
- changes = slice(:user_id, :email, :created_by_id, :bill_address_id, :ship_address_id)
367
+ changes = slice(*ASSOCIATED_USER_ATTRIBUTES)
359
368
 
360
369
  # immediately persist the changes we just made, but don't use save
361
370
  # since we might have an invalid address associated
@@ -364,6 +373,12 @@ module Spree
364
373
  end
365
374
  end
366
375
 
376
+ def disassociate_user!
377
+ nullified_attributes = ASSOCIATED_USER_ATTRIBUTES.index_with(nil)
378
+
379
+ update!(nullified_attributes)
380
+ end
381
+
367
382
  def quantity_of(variant, options = {})
368
383
  line_item = find_line_item_by_variant(variant, options)
369
384
  line_item ? line_item.quantity : 0
@@ -443,6 +458,7 @@ module Spree
443
458
  # @return [Array<Spree::Variant>] the backordered variants for the order
444
459
  def backordered_variants
445
460
  variants.
461
+ where(track_inventory: true).
446
462
  joins(:stock_items, :product).
447
463
  where(Spree::StockItem.table_name => { count_on_hand: ..0, backorderable: true })
448
464
  end
@@ -7,7 +7,7 @@ module Spree
7
7
  @order = order
8
8
  end
9
9
 
10
- def merge!(other_order, user = nil)
10
+ def merge!(other_order, user = nil, discard_merged: true)
11
11
  other_order.line_items.each do |other_order_line_item|
12
12
  next unless other_order_line_item.currency == order.currency
13
13
 
@@ -16,12 +16,14 @@ module Spree
16
16
  end
17
17
 
18
18
  set_user(user)
19
- clear_addresses(other_order)
19
+ clear_addresses(other_order) if discard_merged
20
20
  persist_merge
21
21
 
22
- # So that the destroy doesn't take out line items which may have been re-assigned
23
- other_order.line_items.reload
24
- other_order.destroy
22
+ if discard_merged
23
+ # So that the destroy doesn't take out line items which may have been re-assigned
24
+ other_order.line_items.reload
25
+ other_order.destroy
26
+ end
25
27
  end
26
28
 
27
29
  # Compare the line item of the other order with mine.
@@ -6,6 +6,14 @@ module Spree
6
6
  BOTTOM_PADDING_DEFAULT = 20
7
7
 
8
8
  preference :text_color, :string
9
+
10
+ def icon_name
11
+ 'shopping-cart-plus'
12
+ end
13
+
14
+ def display_name
15
+ Spree.t('page_blocks.products.buy_buttons.display_name')
16
+ end
9
17
  end
10
18
  end
11
19
  end
@@ -6,6 +6,10 @@ module Spree
6
6
  BOTTOM_PADDING_DEFAULT = 20
7
7
 
8
8
  preference :text_color, :string
9
+
10
+ def icon_name
11
+ 'selector'
12
+ end
9
13
  end
10
14
  end
11
15
  end
@@ -3,6 +3,10 @@ module Spree
3
3
  module Products
4
4
  class VariantPicker < Spree::PageBlock
5
5
  preference :text_color, :string
6
+
7
+ def icon_name
8
+ 'select'
9
+ end
6
10
  end
7
11
  end
8
12
  end
@@ -41,6 +41,10 @@ module Spree
41
41
  def blocks_available?
42
42
  true
43
43
  end
44
+
45
+ def can_sort_blocks?
46
+ true
47
+ end
44
48
  end
45
49
  end
46
50
  end
@@ -26,10 +26,22 @@ module Spree
26
26
  ]
27
27
  end
28
28
 
29
+ def available_blocks_to_add
30
+ [
31
+ Spree::PageBlocks::Buttons,
32
+ Spree::PageBlocks::Heading,
33
+ Spree::PageBlocks::Text
34
+ ]
35
+ end
36
+
29
37
  def blocks_available?
30
38
  true
31
39
  end
32
40
 
41
+ def can_sort_blocks?
42
+ true
43
+ end
44
+
33
45
  def icon_name
34
46
  'slideshow'
35
47
  end
@@ -27,6 +27,14 @@ module Spree
27
27
  ]
28
28
  end
29
29
 
30
+ def available_blocks_to_add
31
+ [
32
+ Spree::PageBlocks::Buttons,
33
+ Spree::PageBlocks::Heading,
34
+ Spree::PageBlocks::Text
35
+ ]
36
+ end
37
+
30
38
  def default_links
31
39
  @default_links.presence || [
32
40
  Spree::PageLink.new(
@@ -40,6 +48,10 @@ module Spree
40
48
  true
41
49
  end
42
50
 
51
+ def can_sort_blocks?
52
+ true
53
+ end
54
+
43
55
  def icon_name
44
56
  'photo'
45
57
  end
@@ -32,7 +32,7 @@ module Spree
32
32
  end
33
33
 
34
34
  def available_blocks_to_add
35
- [Spree::PageBlocks::Image]
35
+ [Spree::PageBlocks::Image, Spree::PageBlocks::Heading, Spree::PageBlocks::Text, Spree::PageBlocks::NewsletterForm]
36
36
  end
37
37
 
38
38
  def can_sort_blocks?
@@ -8,10 +8,21 @@ module Spree
8
8
  ]
9
9
  end
10
10
 
11
+ def available_blocks_to_add
12
+ [
13
+ Spree::PageBlocks::Heading,
14
+ Spree::PageBlocks::Text
15
+ ]
16
+ end
17
+
11
18
  def blocks_available?
12
19
  true
13
20
  end
14
21
 
22
+ def can_sort_blocks?
23
+ true
24
+ end
25
+
15
26
  def icon_name
16
27
  'text-caption'
17
28
  end
@@ -44,6 +44,10 @@ module Spree
44
44
  ]
45
45
  end
46
46
 
47
+ def available_blocks_to_add
48
+ [Spree::PageBlocks::Heading]
49
+ end
50
+
47
51
  def icon_name
48
52
  'movie'
49
53
  end
@@ -52,6 +56,10 @@ module Spree
52
56
  true
53
57
  end
54
58
 
59
+ def can_sort_blocks?
60
+ true
61
+ end
62
+
55
63
  def video_embed
56
64
  @video_embed ||= ::ActionText::VideoEmbed.find_by(id: preferred_youtube_video_embed_id) if preferred_youtube_video_embed_id.present?
57
65
  end
@@ -595,7 +595,11 @@ module Spree
595
595
  join_translation_table(Taxonomy).
596
596
  find_by(Taxonomy.translation_table_alias => { name: Spree.t(:taxonomy_brands_name) })
597
597
  else
598
- taxons.joins(:taxonomy).find_by(Taxonomy.table_name => { name: Spree.t(:taxonomy_brands_name) })
598
+ if taxons.loaded?
599
+ taxons.find { |taxon| taxon.taxonomy.name == Spree.t(:taxonomy_brands_name) }
600
+ else
601
+ taxons.joins(:taxonomy).find_by(Taxonomy.table_name => { name: Spree.t(:taxonomy_brands_name) })
602
+ end
599
603
  end
600
604
  end
601
605
 
@@ -606,7 +610,11 @@ module Spree
606
610
  order(depth: :desc).
607
611
  find_by(Taxonomy.translation_table_alias => { name: Spree.t(:taxonomy_categories_name) })
608
612
  else
609
- taxons.joins(:taxonomy).order(depth: :desc).find_by(Taxonomy.table_name => { name: Spree.t(:taxonomy_categories_name) })
613
+ if taxons.loaded?
614
+ taxons.find { |taxon| taxon.taxonomy.name == Spree.t(:taxonomy_categories_name) }
615
+ else
616
+ taxons.joins(:taxonomy).order(depth: :desc).find_by(Taxonomy.table_name => { name: Spree.t(:taxonomy_categories_name) })
617
+ end
610
618
  end
611
619
  end
612
620
 
@@ -33,7 +33,7 @@ module Spree
33
33
  scope :filterable, -> { joins(:property).where(Property.table_name => { filterable: true }) }
34
34
  scope :for_products, ->(products) { joins(:product).merge(products) }
35
35
  scope :sort_by_property_position, -> {
36
- joins(:property).order("spree_properties.position ASC")
36
+ unscope(:order).joins(:property).order("spree_properties.position ASC")
37
37
  }
38
38
 
39
39
  self.whitelisted_ransackable_attributes = ['value', 'filter_param']
@@ -39,8 +39,10 @@ module Spree
39
39
  KIND_OPTIONS = { short_text: 0, long_text: 1, number: 2, rich_text: 3 }.freeze
40
40
  enum :kind, KIND_OPTIONS
41
41
 
42
+ DEPENDENCY_UPDATE_FIELDS = [:presentation, :name, :kind, :filterable, :display_on, :position].freeze
43
+
42
44
  after_touch :touch_all_products
43
- after_update :touch_all_products, if: -> { saved_changes.key?(:presentation) }
45
+ after_update :touch_all_products, if: -> { DEPENDENCY_UPDATE_FIELDS.any? { |field| saved_changes.key?(field) } }
44
46
  after_save :ensure_product_properties_have_filter_params
45
47
 
46
48
  self.whitelisted_ransackable_attributes = ['presentation', 'filterable']
@@ -2,12 +2,16 @@ module Spree
2
2
  module Reports
3
3
  class SalesTotal < Spree::Report
4
4
  def line_items_scope
5
- store.line_items.where(
5
+ scope = store.line_items.where(
6
6
  order: Spree::Order.complete.where(
7
7
  currency: currency,
8
8
  completed_at: (date_from.to_time.beginning_of_day)..(date_to.to_time.end_of_day)
9
9
  )
10
10
  ).includes(:order, variant: :product)
11
+
12
+ scope = scope.where(vendor_id: vendor.id) if defined?(vendor) && vendor.present?
13
+
14
+ scope
11
15
  end
12
16
  end
13
17
  end
@@ -4,8 +4,24 @@ module Spree
4
4
 
5
5
  ADMIN_ROLE = 'admin'
6
6
 
7
+ #
8
+ # Associations
9
+ #
7
10
  has_many :role_users, class_name: 'Spree::RoleUser', dependent: :destroy
8
11
  has_many :users, through: :role_users, source: :user, source_type: Spree.user_class.to_s
9
12
  has_many :admin_users, through: :role_users, source: :user, source_type: Spree.admin_user_class.to_s
13
+ has_many :invitations, class_name: 'Spree::Invitation', dependent: :destroy
14
+
15
+ #
16
+ # Scopes
17
+ #
18
+ scope :admin, -> { where(name: ADMIN_ROLE) }
19
+
20
+ #
21
+ # Class Methods
22
+ #
23
+ def self.default_admin_role
24
+ find_or_create_by(name: ADMIN_ROLE)
25
+ end
10
26
  end
11
27
  end
@@ -1,6 +1,37 @@
1
1
  module Spree
2
2
  class RoleUser < Spree.base_class
3
- belongs_to :role, class_name: 'Spree::Role'
3
+ #
4
+ # Associations
5
+ #
6
+ belongs_to :role, class_name: 'Spree::Role', foreign_key: :role_id
4
7
  belongs_to :user, polymorphic: true
8
+ belongs_to :resource, polymorphic: true
9
+ belongs_to :invitation, class_name: 'Spree::Invitation', optional: true
10
+
11
+ #
12
+ # Validations
13
+ #
14
+ validates :role, presence: true
15
+ validates :user, presence: true
16
+ validates :resource, presence: true
17
+ validates :role_id, uniqueness: { scope: [:user_id, :resource_id, :user_type, :resource_type] }
18
+
19
+ #
20
+ # Delegations
21
+ #
22
+ delegate :name, to: :user
23
+
24
+ #
25
+ # Callbacks
26
+ #
27
+ before_validation :set_default_resource
28
+
29
+ private
30
+
31
+ # Set the default resource to the default store if the resource is not set
32
+ # this will allow a graceful migration from the old roles system to the new one
33
+ def set_default_resource
34
+ self.resource ||= Spree::Store.current
35
+ end
5
36
  end
6
37
  end
@@ -430,7 +430,7 @@ module Spree
430
430
  end
431
431
 
432
432
  def manifest_unstock(item)
433
- stock_location.unstock item.variant, item.quantity, self
433
+ stock_location.unstock(item.variant, item.quantity, self) if item.variant.track_inventory?
434
434
  end
435
435
 
436
436
  def recalculate_adjustments
@@ -1,6 +1,7 @@
1
1
  module Spree
2
2
  class ShipmentHandler
3
3
  include Spree::Shipment::Emails
4
+ include Spree::IntegrationsConcern
4
5
 
5
6
  class << self
6
7
  def factory(shipment)
@@ -50,7 +50,7 @@ module Spree
50
50
  def include?(address)
51
51
  return false unless address
52
52
 
53
- zones.includes(:zone_members).any? do |zone|
53
+ zones.includes(zone_members: :zoneable).any? do |zone|
54
54
  zone.include?(address)
55
55
  end
56
56
  end
@@ -13,6 +13,7 @@ module Spree
13
13
  include Spree::Stores::Socials
14
14
  include Spree::Webhooks::HasWebhooks if defined?(Spree::Webhooks::HasWebhooks)
15
15
  include Spree::Security::Stores if defined?(Spree::Security::Stores)
16
+ include Spree::UserManagement
16
17
 
17
18
  #
18
19
  # Magic methods
@@ -100,8 +101,10 @@ module Spree
100
101
  has_many :custom_domains, class_name: 'Spree::CustomDomain', dependent: :destroy
101
102
  has_one :default_custom_domain, -> { where(default: true) }, class_name: 'Spree::CustomDomain'
102
103
 
103
- has_many :posts
104
- has_many :post_categories
104
+ has_many :posts, class_name: 'Spree::Post'
105
+ has_many :post_categories, class_name: 'Spree::PostCategory'
106
+
107
+ has_many :integrations, class_name: 'Spree::Integration'
105
108
 
106
109
  #
107
110
  # Page Builder associations
@@ -338,9 +341,11 @@ module Spree
338
341
  end
339
342
  end
340
343
 
344
+ # Returns the default stock location for the store or creates a new one if it doesn't exist
345
+ # @return [Spree::StockLocation]
341
346
  def default_stock_location
342
347
  @default_stock_location ||= begin
343
- stock_location_scope = Spree::StockLocation.order_default
348
+ stock_location_scope = Spree::StockLocation.where(default: true)
344
349
  stock_location_scope.first || ActiveRecord::Base.connected_to(role: :writing) do
345
350
  stock_location_scope.create(default: true, name: Spree.t(:default_stock_location_name), country: default_country)
346
351
  end
@@ -348,7 +353,9 @@ module Spree
348
353
  end
349
354
 
350
355
  def admin_users
351
- @admin_users ||= Spree.admin_user_class.joins(:spree_roles).where(spree_roles: { name: :admin })
356
+ Spree::Deprecation.warn('Store#admin_users is deprecated and will be removed in Spree 6.0. Please use Store#users instead.')
357
+
358
+ users
352
359
  end
353
360
 
354
361
  def favicon
@@ -28,6 +28,10 @@ module Spree
28
28
  end
29
29
  end
30
30
 
31
+ def can_be_deleted?
32
+ !store_credit_category_used?
33
+ end
34
+
31
35
  class << self
32
36
  def default_reimbursement_category(_options = {})
33
37
  Spree::StoreCreditCategory.first
@@ -1,4 +1,3 @@
1
- # TODO: let friendly id take care of sanitizing the url
2
1
  require 'stringex'
3
2
 
4
3
  module Spree
@@ -96,9 +95,11 @@ module Spree
96
95
  if Spree.use_translations?
97
96
  joins(:taxonomy).
98
97
  join_translation_table(Taxonomy).
99
- where(["LOWER(#{Taxonomy.translation_table_alias}.name) = ?", taxonomy_name.downcase.strip])
98
+ where(
99
+ Taxonomy.arel_table_alias[:name].lower.matches(taxonomy_name.downcase.strip)
100
+ )
100
101
  else
101
- joins(:taxonomy).where(["LOWER(#{Spree::Taxonomy.table_name}.name) = ?", taxonomy_name.downcase.strip])
102
+ joins(:taxonomy).where(Spree::Taxonomy.arel_table[:name].lower.matches(taxonomy_name.downcase.strip))
102
103
  end
103
104
  }
104
105
 
@@ -73,6 +73,8 @@ module Spree
73
73
  after_create :create_stock_items
74
74
  after_create :set_master_out_of_stock, unless: :is_master?
75
75
  after_commit :clear_line_items_cache, on: :update
76
+
77
+ after_create :create_default_stock_item, unless: :track_inventory?
76
78
  after_update_commit :handle_track_inventory_change
77
79
 
78
80
  after_commit :remove_prices_from_master_variant, on: [:create, :update], unless: :is_master?
@@ -553,11 +555,17 @@ module Spree
553
555
  line_items.update_all(updated_at: Time.current)
554
556
  end
555
557
 
558
+ def create_default_stock_item
559
+ return if stock_items.any?
560
+
561
+ Spree::Store.current.default_stock_location.set_up_stock_item(self)
562
+ end
563
+
556
564
  def handle_track_inventory_change
557
565
  return unless track_inventory_previously_changed?
558
566
  return if track_inventory
559
567
 
560
- stock_items.update_all(backorderable: true, count_on_hand: 0, updated_at: Time.current)
568
+ stock_items.update_all(count_on_hand: 0, updated_at: Time.current)
561
569
  end
562
570
 
563
571
  def remove_prices_from_master_variant