spree_core 5.4.0.beta2 → 5.4.0.beta4

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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/app/finders/spree/products/find.rb +1 -14
  3. data/app/jobs/spree/exports/generate_job.rb +1 -1
  4. data/app/jobs/spree/reports/generate_job.rb +1 -1
  5. data/app/models/concerns/spree/product_scopes.rb +32 -4
  6. data/app/models/spree/address.rb +0 -14
  7. data/app/models/spree/api_key.rb +8 -5
  8. data/app/models/spree/country.rb +2 -23
  9. data/app/models/spree/coupon_code.rb +6 -1
  10. data/app/models/spree/current.rb +3 -2
  11. data/app/models/spree/exports/coupon_codes.rb +18 -0
  12. data/app/models/spree/market.rb +9 -2
  13. data/app/models/spree/market_country.rb +17 -0
  14. data/app/models/spree/newsletter_subscriber.rb +0 -3
  15. data/app/models/spree/order.rb +2 -5
  16. data/app/models/spree/product.rb +3 -13
  17. data/app/models/spree/reimbursement.rb +0 -2
  18. data/app/models/spree/shipment.rb +0 -1
  19. data/app/models/spree/shipment_handler.rb +0 -2
  20. data/app/models/spree/taxon.rb +10 -10
  21. data/app/models/spree/variant.rb +1 -1
  22. data/app/models/spree/zone.rb +21 -9
  23. data/app/presenters/spree/csv/coupon_code_presenter.rb +31 -0
  24. data/app/services/spree/newsletter/subscribe.rb +2 -2
  25. data/app/subscribers/spree/invitation_email_subscriber.rb +1 -1
  26. data/config/locales/en.yml +1 -0
  27. data/lib/spree/core/engine.rb +2 -1
  28. data/lib/spree/core/version.rb +1 -1
  29. data/lib/spree/core.rb +0 -1
  30. data/lib/spree_core.rb +0 -1
  31. data/lib/tasks/cli.rake +50 -0
  32. metadata +7 -29
  33. data/app/models/concerns/spree/filter_param.rb +0 -21
  34. data/app/models/spree/newsletter_subscriber/emails.rb +0 -12
  35. data/app/models/spree/order/emails.rb +0 -24
  36. data/app/models/spree/reimbursement/emails.rb +0 -11
  37. data/app/models/spree/shipment/emails.rb +0 -12
  38. data/lib/generators/spree/cursor_rules/cursor_rules_generator.rb +0 -19
  39. data/lib/generators/spree/cursor_rules/templates/spree_rules.mdc +0 -385
  40. data/lib/normalize_string.rb +0 -18
  41. data/lib/spree/core/importer/order.rb +0 -244
  42. data/lib/spree/core/importer/product.rb +0 -67
  43. data/lib/spree/core/importer.rb +0 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: af6f55a25483bfb02dab0b15f67356a2732cbeb190074b9ddb75a1692594fb7a
4
- data.tar.gz: 8f0b8f77983432ef1e1baa52def6ed2674f9e0794bc632dd91757551c4ee71c4
3
+ metadata.gz: 29b95e3e3222214cdeef8094a1a9553636c875d88857244241431196d06399d2
4
+ data.tar.gz: 93c9f4d387fce7ba5e2e9f4b6454f9c46f1b8a15cfe180a16739c17bf76a5125
5
5
  SHA512:
6
- metadata.gz: 46c6fc812dc90ba1ae3061b2e4066df5e9dd8ad530fde2db85095a7b5cb444fec2d51f4736a528c61ee4423e64d986478fdcf5478c40888e99818b849e444d60
7
- data.tar.gz: aa2913c4e9882dd0e77d03ddcfae4f8277e4657b807080ce2d3549fd63f5c6da18448e2574b7742ad82b227a299dccfce3717a7df504be4442e3c7f0bf21629e
6
+ metadata.gz: 5c8a23c3edd35dacb0493c5435e28d114fc2611b4b25a0d85b4b5e4579f38402fb61a3a65a9f3b361fae8f67d5f58ac73984b6ab2cd4bcbb0326813589a18330
7
+ data.tar.gz: 1fabd1160061e412e2e9a4a1e92083686938a5313c3b29193e410bc07d6118d60af4bd7c2951794273c20f8b053f1dfc5acba8dfe748a722f5895aacc17d33d9
@@ -22,14 +22,8 @@ module Spree
22
22
  @in_stock = params.dig(:filter, :in_stock)
23
23
  @backorderable = params.dig(:filter, :backorderable)
24
24
  @purchasable = params.dig(:filter, :purchasable)
25
- @out_of_stock = params.dig(:filter, :out_of_stock).to_b
26
25
  @tags = params.dig(:filter, :tags).to_s.split(',').compact_blank
27
26
  @vendor_ids = params.dig(:filter, :vendor_ids)&.split(',')&.compact_blank || []
28
-
29
- if @purchasable.present? && @out_of_stock.present?
30
- @purchasable = false
31
- @out_of_stock = false
32
- end
33
27
  end
34
28
 
35
29
  def execute
@@ -50,7 +44,6 @@ module Spree
50
44
  products = show_only_stock(products)
51
45
  products = show_only_backorderable(products)
52
46
  products = show_only_purchasable(products)
53
- products = show_only_out_of_stock(products)
54
47
  products = by_taxonomies(products)
55
48
  products = ordered(products)
56
49
  products = by_vendor_ids(products)
@@ -62,7 +55,7 @@ module Spree
62
55
 
63
56
  attr_reader :ids, :skus, :price, :currency, :taxons, :concat_taxons, :name, :options, :option_value_ids, :scope,
64
57
  :sort_by, :deleted, :discontinued, :store, :in_stock, :backorderable, :purchasable, :tags,
65
- :query, :vendor_ids, :out_of_stock, :slug, :taxonomies
58
+ :query, :vendor_ids, :slug, :taxonomies
66
59
 
67
60
  def query?
68
61
  query.present?
@@ -294,12 +287,6 @@ module Spree
294
287
  products.in_stock_or_backorderable
295
288
  end
296
289
 
297
- def show_only_out_of_stock(products)
298
- return products unless out_of_stock.present?
299
-
300
- products.out_of_stock
301
- end
302
-
303
290
  def map_prices(prices)
304
291
  prices.map do |price|
305
292
  price == 'Infinity' ? BigDecimal::INFINITY : price.to_f
@@ -4,7 +4,7 @@ module Spree
4
4
  queue_as Spree.queues.exports
5
5
 
6
6
  def perform(export_id)
7
- export = Spree::Export.find(export_id)
7
+ export = Spree::Export.find_by_prefix_id!(export_id)
8
8
  export.generate
9
9
  end
10
10
  end
@@ -4,7 +4,7 @@ module Spree
4
4
  queue_as Spree.queues.reports
5
5
 
6
6
  def perform(report_id)
7
- report = Spree::Report.find(report_id)
7
+ report = Spree::Report.find_by_prefix_id!(report_id)
8
8
  report.generate
9
9
  end
10
10
  end
@@ -81,9 +81,34 @@ module Spree
81
81
  where(Price.table_name => { amount: price.. })
82
82
  end
83
83
 
84
- add_search_scope :in_stock do
85
- joins(:variants_including_master).merge(Spree::Variant.in_stock)
84
+ # Can't use add_search_scope for this as it needs a default argument
85
+ # Ransack calls with '1' to activate, '0' or nil to skip
86
+ # In Ruby code: in_stock(true) for in-stock, in_stock(false) for out-of-stock
87
+ def self.in_stock(in_stock = true)
88
+ if in_stock == '0' || !in_stock
89
+ all
90
+ else
91
+ joins(:variants_including_master).merge(Spree::Variant.in_stock_or_backorderable)
92
+ end
93
+ end
94
+
95
+ add_search_scope :price_lte do |price|
96
+ where(Price.table_name => { amount: ..price })
97
+ end
98
+
99
+ add_search_scope :price_gte do |price|
100
+ where(Price.table_name => { amount: price.. })
101
+ end
102
+ search_scopes << :in_stock
103
+
104
+ def self.out_of_stock(out_of_stock = true)
105
+ if out_of_stock == '0' || !out_of_stock
106
+ all
107
+ else
108
+ where.not(id: joins(:variants_including_master).merge(Spree::Variant.in_stock_or_backorderable))
109
+ end
86
110
  end
111
+ search_scopes << :out_of_stock
87
112
 
88
113
  add_search_scope :backorderable do
89
114
  joins(:variants_including_master).merge(Spree::Variant.backorderable)
@@ -243,7 +268,7 @@ module Spree
243
268
 
244
269
  def self.not_discontinued(only_not_discontinued = true)
245
270
  if only_not_discontinued != '0' && only_not_discontinued
246
- where(discontinue_on: [nil, Time.current..])
271
+ where(discontinue_on: [nil, Time.current.beginning_of_minute..])
247
272
  else
248
273
  all
249
274
  end
@@ -261,7 +286,10 @@ module Spree
261
286
  # Can't use add_search_scope for this as it needs a default argument
262
287
  def self.available(available_on = nil, currency = nil)
263
288
  scope = not_discontinued.where(status: 'active')
264
- scope = scope.where("#{Product.quoted_table_name}.available_on <= ?", available_on) if available_on
289
+ if available_on
290
+ available_on = available_on.beginning_of_minute if available_on.respond_to?(:beginning_of_minute)
291
+ scope = scope.where("#{Product.quoted_table_name}.available_on <= ?", available_on)
292
+ end
265
293
 
266
294
  unless Spree::Config.show_products_without_price
267
295
  currency ||= Spree::Store.default.default_currency
@@ -27,8 +27,6 @@ module Spree
27
27
  # those attributes depending of the logic of their applications
28
28
  ADDRESS_FIELDS = %w(firstname lastname company address1 address2 city state zipcode country phone)
29
29
  EXCLUDED_KEYS_FOR_COMPARISON = %w(id updated_at created_at deleted_at label user_id public_metadata private_metadata)
30
- FIELDS_TO_NORMALIZE = %w(firstname lastname phone alternative_phone company address1 address2 city zipcode)
31
-
32
30
  if defined?(Spree::Security::Addresses)
33
31
  include Spree::Security::Addresses
34
32
  end
@@ -53,7 +51,6 @@ module Spree
53
51
  before_validation :normalize_country
54
52
  before_validation :normalize_state
55
53
  before_validation :clear_invalid_state_entities, if: -> { country.present? }, on: :update
56
- before_validation :remove_emoji_and_normalize
57
54
 
58
55
  after_create :set_user_attributes, if: -> { user.present? }
59
56
 
@@ -294,17 +291,6 @@ module Spree
294
291
  end
295
292
  end
296
293
 
297
- def remove_emoji_and_normalize
298
- attributes_to_normalize = attributes.slice(*FIELDS_TO_NORMALIZE)
299
- normalized_attributes = attributes_to_normalize.compact_blank.deep_transform_values do |value|
300
- NormalizeString.remove_emoji_and_normalize(value.to_s).strip
301
- end
302
-
303
- normalized_attributes.transform_keys! { |key| key.gsub('original_', '') } if defined?(Spree::Security::Addresses)
304
-
305
- assign_attributes(normalized_attributes)
306
- end
307
-
308
294
  def set_user_attributes
309
295
  if user.name.blank?
310
296
  user.first_name = firstname
@@ -6,11 +6,14 @@ module Spree
6
6
  PREFIXES = { 'publishable' => 'pk_', 'secret' => 'sk_' }.freeze
7
7
  TOKEN_LENGTH = 24
8
8
 
9
- # @!attribute [r] plaintext_token
10
- # The raw token value, only available in memory immediately after creation
11
- # of a secret key. Not persisted to the database.
12
- # @return [String, nil]
13
- attr_reader :plaintext_token
9
+ # Returns the raw token value. For publishable keys this is the persisted
10
+ # +token+ column. For secret keys it is only available in memory immediately
11
+ # after creation (not persisted).
12
+ #
13
+ # @return [String, nil]
14
+ def plaintext_token
15
+ publishable? ? token : @plaintext_token
16
+ end
14
17
 
15
18
  belongs_to :store, class_name: 'Spree::Store'
16
19
  belongs_to :created_by, polymorphic: true, optional: true
@@ -38,29 +38,8 @@ module Spree
38
38
  iso.upcase.chars.map { |c| (c.ord + 127397).chr(Encoding::UTF_8) }.join
39
39
  end
40
40
 
41
- # Returns the currency for this country from its market in the current store.
42
- # Looks up which market contains this country and returns that market's currency.
43
- #
44
- # @return [String, nil] currency code (e.g., 'USD', 'EUR') or nil if no market found
45
- def market_currency
46
- current_market&.currency
47
- end
48
-
49
- # Returns the default locale for this country from its market in the current store.
50
- # Looks up which market contains this country and returns that market's default locale.
51
- #
52
- # @return [String, nil] locale code (e.g., 'en', 'de') or nil if no market found
53
- def market_locale
54
- current_market&.default_locale
55
- end
56
-
57
- # Returns the supported locales for this country from its market in the current store.
58
- #
59
- # @return [Array<String>] locale codes (e.g., ['en', 'fr']) or empty array if no market found
60
- def market_supported_locales
61
- current_market&.supported_locales_list || []
62
- end
63
-
41
+ # Lookups Market for Country for the current Store
42
+ # @return [Spree::Market, nil]
64
43
  def current_market
65
44
  @current_market ||= Spree::Current.store&.market_for_country(self)
66
45
  end
@@ -19,7 +19,8 @@ module Spree
19
19
  validates :code, presence: true, uniqueness: { scope: spree_base_uniqueness_scope, conditions: -> { where(deleted_at: nil) } }
20
20
  validates :state, :promotion, presence: true
21
21
 
22
- self.whitelisted_ransackable_attributes = %w[state code]
22
+ self.whitelisted_ransackable_attributes = %w[state code promotion_id]
23
+ self.whitelisted_ransackable_associations = %w[promotion]
23
24
 
24
25
  def self.used?(code)
25
26
  used_with_code(code).any?
@@ -36,5 +37,9 @@ module Spree
36
37
  def display_code
37
38
  code.upcase
38
39
  end
40
+
41
+ def to_csv(_store = nil)
42
+ Spree::CSV::CouponCodePresenter.new(self).call
43
+ end
39
44
  end
40
45
  end
@@ -34,10 +34,11 @@ module Spree
34
34
  super || market&.default_locale || store&.default_locale
35
35
  end
36
36
 
37
- # Returns the current tax zone, falling back to the default tax zone.
37
+ # Returns the current tax zone.
38
+ # Fallback: market's tax zone (from default country) -> global default tax zone.
38
39
  # @return [Spree::Zone, nil]
39
40
  def zone
40
- super || default_tax_zone
41
+ super || market&.tax_zone || default_tax_zone
41
42
  end
42
43
 
43
44
  # Returns the default tax zone (memoized per request).
@@ -0,0 +1,18 @@
1
+ module Spree
2
+ module Exports
3
+ class CouponCodes < Spree::Export
4
+ def csv_headers
5
+ Spree::CSV::CouponCodePresenter::HEADERS
6
+ end
7
+
8
+ def scope_includes
9
+ [:promotion, :order]
10
+ end
11
+
12
+ def scope
13
+ model_class.joins(promotion: :stores).where(spree_stores: { id: store.id })
14
+ .accessible_by(current_ability)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -44,8 +44,7 @@ module Spree
44
44
  joins(:market_countries)
45
45
  .where(store_id: store.id)
46
46
  .where(spree_market_countries: { country_id: country.id })
47
- .order(:position)
48
- .first
47
+ .take
49
48
  end
50
49
 
51
50
  # Returns the default market for a store, or falls back to the first by position
@@ -65,6 +64,14 @@ module Spree
65
64
  countries.order(:name).first
66
65
  end
67
66
 
67
+ # Returns the tax zone matching this market's default country.
68
+ # Used by Spree::Current to determine the browsing tax zone before a customer enters an address.
69
+ #
70
+ # @return [Spree::Zone, nil]
71
+ def tax_zone
72
+ @tax_zone ||= Spree::Zone.match(default_country)
73
+ end
74
+
68
75
  # Returns supported locales as an array, always including default_locale
69
76
  #
70
77
  # @return [Array<String>]
@@ -8,6 +8,7 @@ module Spree
8
8
  validates :market, :country, presence: true
9
9
  validates :country_id, uniqueness: { scope: :market_id }
10
10
  validate :country_covered_by_shipping_zone
11
+ validate :country_unique_per_store
11
12
 
12
13
  private
13
14
 
@@ -21,5 +22,21 @@ module Spree
21
22
  errors.add(:country, :not_in_shipping_zone)
22
23
  end
23
24
  end
25
+
26
+ def country_unique_per_store
27
+ return if market.blank? || country.blank?
28
+
29
+ store = market.store
30
+ return if store.blank?
31
+
32
+ existing = self.class.joins(:market)
33
+ .where(country_id: country_id)
34
+ .where(spree_markets: { store_id: store.id, deleted_at: nil })
35
+ .where.not(id: id)
36
+
37
+ if existing.exists?
38
+ errors.add(:country, :already_in_market)
39
+ end
40
+ end
24
41
  end
25
42
  end
@@ -1,10 +1,7 @@
1
- require_dependency 'spree/newsletter_subscriber/emails'
2
-
3
1
  module Spree
4
2
  class NewsletterSubscriber < Spree.base_class
5
3
  has_prefix_id :sub
6
4
 
7
- include Spree::NewsletterSubscriber::Emails
8
5
  include Spree::Metafields
9
6
 
10
7
  publishes_lifecycle_events
@@ -3,7 +3,6 @@ require_dependency 'spree/order/currency_updater'
3
3
  require_dependency 'spree/order/digital'
4
4
  require_dependency 'spree/order/payments'
5
5
  require_dependency 'spree/order/store_credit'
6
- require_dependency 'spree/order/emails'
7
6
  require_dependency 'spree/order/gift_card'
8
7
 
9
8
  module Spree
@@ -22,7 +21,6 @@ module Spree
22
21
  include Spree::Order::Payments
23
22
  include Spree::Order::StoreCredit
24
23
  include Spree::Order::AddressBook
25
- include Spree::Order::Emails
26
24
  include Spree::Order::Webhooks
27
25
  include Spree::Core::NumberGenerator.new(prefix: 'R')
28
26
  include Spree::Order::GiftCard
@@ -311,9 +309,9 @@ module Spree
311
309
  # @return [Boolean]
312
310
  def order_refunded?
313
311
  return false if item_count.zero?
312
+ return false if refunds_total.zero?
314
313
 
315
- (payment_state.in?(%w[void failed]) && refunds_total.positive?) ||
316
- refunds_total == total_minus_store_credits - additional_tax_total.abs
314
+ payment_state.in?(%w[void failed]) || refunds_total == total_minus_store_credits - additional_tax_total.abs
317
315
  end
318
316
 
319
317
  def refunds_total
@@ -965,7 +963,6 @@ module Spree
965
963
  payments.store_credits.pending.each(&:void!)
966
964
  end
967
965
 
968
- send_cancel_email
969
966
  update_with_updater!
970
967
  send_order_canceled_webhook
971
968
  publish_order_canceled_event
@@ -197,18 +197,6 @@ module Spree
197
197
  scope :by_source, ->(source) { send(source) }
198
198
  scope :paused, -> { where(status: 'paused') }
199
199
  scope :published, -> { where(status: 'active') }
200
- scope :in_stock_items, -> { joins(:variants).merge(Spree::Variant.in_stock_or_backorderable) }
201
- scope :out_of_stock_items, lambda {
202
- joins(variants_including_master: :stock_items).
203
- where(spree_variants: { track_inventory: true }).
204
- where.not(id: Spree::Variant.where(track_inventory: false).pluck(:product_id).uniq).
205
- where(spree_stock_items: { backorderable: false }).
206
- group(:id).
207
- having("SUM(#{Spree::StockItem.table_name}.count_on_hand) <= 0")
208
- }
209
- scope :out_of_stock, lambda {
210
- joins(:stock_items).where("#{Spree::Variant.table_name}.track_inventory = ? OR #{Spree::StockItem.table_name}.count_on_hand <= ?", false, 0)
211
- }
212
200
 
213
201
  attr_accessor :option_values_hash
214
202
 
@@ -232,7 +220,9 @@ module Spree
232
220
  self.whitelisted_ransackable_associations = %w[taxons stores variants_including_master master variants tags labels
233
221
  shipping_category classifications option_types]
234
222
  self.whitelisted_ransackable_scopes = %w[not_discontinued search_by_name in_taxon price_between
235
- multi_search in_stock_items out_of_stock_items with_option_value_ids
223
+ price_lte price_gte
224
+ multi_search in_stock out_of_stock with_option_value_ids
225
+
236
226
  ascend_by_price descend_by_price]
237
227
 
238
228
  [
@@ -4,7 +4,6 @@ module Spree
4
4
 
5
5
  include Spree::Core::NumberGenerator.new(prefix: 'RI', length: 9)
6
6
  include Spree::NumberIdentifier
7
- include Spree::Reimbursement::Emails
8
7
 
9
8
  class IncompleteReimbursementError < StandardError; end
10
9
 
@@ -116,7 +115,6 @@ module Spree
116
115
  if unpaid_amount_within_tolerance?
117
116
  reimbursed!
118
117
  reimbursement_success_hooks.each { |h| h.call self }
119
- send_reimbursement_email
120
118
  else
121
119
  errored!
122
120
  reimbursement_failure_hooks.each { |h| h.call self }
@@ -14,7 +14,6 @@ module Spree
14
14
  if defined?(Spree::VendorConcern)
15
15
  include Spree::VendorConcern
16
16
  end
17
- include Spree::Shipment::Emails
18
17
  include Spree::Shipment::Webhooks
19
18
  include Spree::Shipment::CustomEvents
20
19
 
@@ -1,6 +1,5 @@
1
1
  module Spree
2
2
  class ShipmentHandler
3
- include Spree::Shipment::Emails
4
3
  include Spree::IntegrationsConcern
5
4
 
6
5
  class << self
@@ -25,7 +24,6 @@ module Spree
25
24
  @shipment.process_order_payments if Spree::Config[:auto_capture_on_dispatch]
26
25
  @shipment.touch :shipped_at
27
26
  update_order_shipment_state
28
- send_shipped_email
29
27
  end
30
28
 
31
29
  protected
@@ -5,16 +5,16 @@ module Spree
5
5
  has_prefix_id :txn # Spree-specific: taxon
6
6
 
7
7
  RULES_MATCH_POLICIES = %w[all any].freeze
8
- SORT_ORDERS = %w[
9
- manual
10
- best-selling
11
- name-a-z
12
- name-z-a
13
- price-high-to-low
14
- price-low-to-high
15
- newest-first
16
- oldest-first
17
- ]
8
+ SORT_ORDERS = [
9
+ 'manual',
10
+ 'best_selling',
11
+ 'price asc',
12
+ 'price desc',
13
+ 'available_on desc',
14
+ 'available_on asc',
15
+ 'name asc',
16
+ 'name desc'
17
+ ].freeze
18
18
 
19
19
  include Spree::TranslatableResource
20
20
  include Spree::TranslatableResourceSlug
@@ -103,7 +103,7 @@ module Spree
103
103
  scope :not_discontinued, lambda {
104
104
  where(
105
105
  arel_table[:discontinue_on].eq(nil).or(
106
- arel_table[:discontinue_on].gteq(Time.current)
106
+ arel_table[:discontinue_on].gteq(Time.current.beginning_of_minute)
107
107
  )
108
108
  )
109
109
  }
@@ -52,15 +52,27 @@ module Spree
52
52
 
53
53
  # Returns the matching zone with the highest priority zone type (State, Country, Zone.)
54
54
  # Returns nil in the case of no matches.
55
- def self.match(address)
56
- return unless address &&
57
- matches = includes(:zone_members).
58
- order('spree_zones.zone_members_count', 'spree_zones.created_at').
59
- where("(spree_zone_members.zoneable_type = 'Spree::Country' AND " \
60
- 'spree_zone_members.zoneable_id = ?) OR ' \
61
- "(spree_zone_members.zoneable_type = 'Spree::State' AND " \
62
- 'spree_zone_members.zoneable_id = ?)', address.country_id, address.state_id).
63
- references(:zones)
55
+ # Accepts either an address (with country_id/state_id) or a Spree::Country directly.
56
+ def self.match(address_or_country)
57
+ return unless address_or_country
58
+
59
+ if address_or_country.is_a?(Spree::Country)
60
+ country_id = address_or_country.id
61
+ state_id = nil
62
+ else
63
+ country_id = address_or_country.country_id
64
+ state_id = address_or_country.state_id
65
+ end
66
+
67
+ matches = includes(:zone_members).
68
+ order('spree_zones.zone_members_count', 'spree_zones.created_at').
69
+ where("(spree_zone_members.zoneable_type = 'Spree::Country' AND " \
70
+ 'spree_zone_members.zoneable_id = ?) OR ' \
71
+ "(spree_zone_members.zoneable_type = 'Spree::State' AND " \
72
+ 'spree_zone_members.zoneable_id = ?)', country_id, state_id).
73
+ references(:zones)
74
+
75
+ return if matches.empty?
64
76
 
65
77
  %w[state country].each do |zone_kind|
66
78
  if match = matches.detect { |zone| zone_kind == zone.kind }
@@ -0,0 +1,31 @@
1
+ module Spree
2
+ module CSV
3
+ class CouponCodePresenter
4
+ HEADERS = [
5
+ 'Code',
6
+ 'State',
7
+ 'Promotion Name',
8
+ 'Order Number',
9
+ 'Created At',
10
+ 'Updated At'
11
+ ].freeze
12
+
13
+ def initialize(coupon_code)
14
+ @coupon_code = coupon_code
15
+ end
16
+
17
+ attr_accessor :coupon_code
18
+
19
+ def call
20
+ [
21
+ coupon_code.display_code,
22
+ coupon_code.state,
23
+ coupon_code.promotion&.name,
24
+ coupon_code.order&.number,
25
+ coupon_code.created_at&.strftime('%Y-%m-%d %H:%M:%S'),
26
+ coupon_code.updated_at&.strftime('%Y-%m-%d %H:%M:%S')
27
+ ]
28
+ end
29
+ end
30
+ end
31
+ end
@@ -19,8 +19,8 @@ module Spree
19
19
  end
20
20
  end
21
21
 
22
- # deliver confirmation email after the transaction is completed
23
- subscriber.deliver_newsletter_email_verification unless subscriber.verified?
22
+ # publish event to trigger email delivery via subscriber
23
+ subscriber.publish_event('newsletter_subscriber.subscribed') unless subscriber.verified?
24
24
  subscriber
25
25
  end
26
26
 
@@ -34,7 +34,7 @@ module Spree
34
34
 
35
35
  def find_invitation(event)
36
36
  invitation_id = event.payload['id']
37
- Spree::Invitation.find_by(id: invitation_id)
37
+ Spree::Invitation.find_by_prefix_id(invitation_id)
38
38
  end
39
39
  end
40
40
  end
@@ -292,6 +292,7 @@ en:
292
292
  spree/market_country:
293
293
  attributes:
294
294
  country:
295
+ already_in_market: is already assigned to another market in this store
295
296
  not_in_shipping_zone: is not covered by any shipping zone. Please set up a shipping zone first.
296
297
  spree/payment:
297
298
  attributes:
@@ -180,7 +180,8 @@ module Spree
180
180
  Spree::Exports::Orders,
181
181
  Spree::Exports::Customers,
182
182
  Spree::Exports::GiftCards,
183
- Spree::Exports::NewsletterSubscribers
183
+ Spree::Exports::NewsletterSubscribers,
184
+ Spree::Exports::CouponCodes
184
185
  ]
185
186
 
186
187
  Rails.application.config.spree.import_types = [
@@ -1,5 +1,5 @@
1
1
  module Spree
2
- VERSION = '5.4.0.beta2'.freeze
2
+ VERSION = '5.4.0.beta4'.freeze
3
3
 
4
4
  def self.version
5
5
  VERSION
data/lib/spree/core.rb CHANGED
@@ -427,7 +427,6 @@ require 'spree/events'
427
427
  require 'spree/webhooks'
428
428
 
429
429
  require 'spree/core/partials'
430
- require 'spree/core/importer'
431
430
  require 'spree/core/controller_helpers/auth'
432
431
  require 'spree/core/controller_helpers/common'
433
432
  require 'spree/core/controller_helpers/order'
data/lib/spree_core.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  require 'friendly_id/paranoia'
2
2
  require 'friendly_id/history_decorator'
3
3
  require 'mobility/plugins/store_based_fallbacks'
4
- require 'normalize_string'
5
4
 
6
5
  require 'spree/core'