spree_core 5.1.0.beta4 → 5.1.0.rc1

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 (75) hide show
  1. checksums.yaml +4 -4
  2. data/app/helpers/spree/base_helper.rb +6 -1
  3. data/app/helpers/spree/images_helper.rb +19 -16
  4. data/app/helpers/spree/shipment_helper.rb +12 -0
  5. data/app/jobs/spree/gift_cards/bulk_generate_job.rb +13 -0
  6. data/app/models/concerns/spree/parameterizable_name.rb +11 -0
  7. data/app/models/concerns/spree/product_scopes.rb +1 -5
  8. data/app/models/concerns/spree/stores/socials.rb +6 -2
  9. data/app/models/concerns/spree/user_methods.rb +2 -1
  10. data/app/models/spree/ability.rb +2 -0
  11. data/app/models/spree/address.rb +11 -3
  12. data/app/models/spree/gift_card.rb +162 -0
  13. data/app/models/spree/gift_card_batch.rb +79 -0
  14. data/app/models/spree/inventory_unit.rb +11 -4
  15. data/app/models/spree/line_item.rb +13 -2
  16. data/app/models/spree/option_type.rb +5 -15
  17. data/app/models/spree/option_value.rb +18 -12
  18. data/app/models/spree/order/checkout.rb +2 -0
  19. data/app/models/spree/order/gift_card.rb +51 -0
  20. data/app/models/spree/order/store_credit.rb +20 -1
  21. data/app/models/spree/order.rb +51 -6
  22. data/app/models/spree/order_merger.rb +12 -0
  23. data/app/models/spree/page_sections/featured_posts.rb +4 -0
  24. data/app/models/spree/payment_method/store_credit.rb +1 -1
  25. data/app/models/spree/payment_method.rb +1 -0
  26. data/app/models/spree/post.rb +1 -0
  27. data/app/models/spree/price.rb +7 -1
  28. data/app/models/spree/product/slugs.rb +103 -0
  29. data/app/models/spree/product.rb +7 -81
  30. data/app/models/spree/product_property.rb +2 -2
  31. data/app/models/spree/promotion_handler/coupon.rb +39 -0
  32. data/app/models/spree/property.rb +2 -8
  33. data/app/models/spree/reimbursement_type/reimbursement_helpers.rb +1 -1
  34. data/app/models/spree/return_item.rb +4 -0
  35. data/app/models/spree/shipment.rb +19 -0
  36. data/app/models/spree/shipping_method.rb +8 -9
  37. data/app/models/spree/store.rb +2 -0
  38. data/app/models/spree/store_credit.rb +9 -6
  39. data/app/models/spree/store_credit_event.rb +8 -4
  40. data/app/models/spree/taxon.rb +8 -1
  41. data/app/models/spree/theme.rb +1 -1
  42. data/app/models/spree/variant.rb +1 -1
  43. data/app/presenters/spree/csv/product_variant_presenter.rb +14 -3
  44. data/app/services/spree/addresses/phone_validator.rb +20 -0
  45. data/app/services/spree/cart/destroy.rb +1 -1
  46. data/app/services/spree/cart/recalculate.rb +1 -0
  47. data/app/services/spree/gift_cards/apply.rb +66 -0
  48. data/app/services/spree/gift_cards/redeem.rb +17 -0
  49. data/app/services/spree/gift_cards/remove.rb +38 -0
  50. data/app/services/spree/products/prepare_nested_attributes.rb +2 -2
  51. data/app/views/spree/addresses/_form.html.erb +1 -1
  52. data/app/views/spree/shared/_payment.html.erb +4 -4
  53. data/config/locales/en.yml +44 -14
  54. data/config/routes.rb +1 -0
  55. data/db/migrate/20250506073057_create_spree_gift_cards_and_spree_gift_card_batches.rb +37 -0
  56. data/db/migrate/20250530101236_enable_pg_trgm_extension.rb +13 -0
  57. data/db/migrate/20250605131334_add_missing_fields_to_users.rb +25 -0
  58. data/lib/generators/spree/install/install_generator.rb +0 -8
  59. data/lib/spree/core/configuration.rb +4 -0
  60. data/lib/spree/core/controller_helpers/store.rb +13 -1
  61. data/lib/spree/core/dependencies.rb +5 -0
  62. data/lib/spree/core/engine.rb +7 -1
  63. data/lib/spree/core/version.rb +1 -1
  64. data/lib/spree/core.rb +2 -1
  65. data/lib/spree/permitted_attributes.rb +22 -9
  66. data/lib/spree/testing_support/authorization_helpers.rb +5 -9
  67. data/lib/spree/testing_support/common_rake.rb +7 -1
  68. data/lib/spree/testing_support/factories/gift_card_batch_factory.rb +5 -0
  69. data/lib/spree/testing_support/factories/gift_card_factory.rb +17 -0
  70. data/lib/spree/testing_support/factories/page_section_factory.rb +2 -1
  71. data/lib/spree/testing_support/factories/post_factory.rb +4 -0
  72. data/lib/spree/testing_support/factories/promotion_rule_factory.rb +4 -0
  73. data/lib/spree/testing_support/factories/user_factory.rb +2 -2
  74. metadata +45 -11
  75. data/config/initializers/state_machine.rb +0 -37
@@ -9,19 +9,13 @@ module Spree
9
9
  include Spree::Webhooks::HasWebhooks
10
10
  end
11
11
 
12
- if Spree.always_use_translations?
13
- TRANSLATABLE_FIELDS = %i[name presentation].freeze
14
- translates(*TRANSLATABLE_FIELDS)
15
- else
16
- TRANSLATABLE_FIELDS = %i[presentation].freeze
17
- translates(*TRANSLATABLE_FIELDS, column_fallback: true)
18
- end
12
+ TRANSLATABLE_FIELDS = %i[presentation].freeze
13
+ translates(*TRANSLATABLE_FIELDS, column_fallback: !Spree.always_use_translations?)
19
14
 
20
15
  self::Translation.class_eval do
21
16
  auto_strip_attributes :presentation
22
17
  end
23
18
 
24
- auto_strip_attributes :name, :presentation
25
19
  acts_as_list
26
20
 
27
21
  has_many :property_prototypes, class_name: 'Spree::PropertyPrototype'
@@ -49,7 +49,7 @@ module Spree
49
49
  user: reimbursement.order.user,
50
50
  amount: unpaid_amount,
51
51
  category: category,
52
- created_by: Spree::StoreCredit.default_created_by,
52
+ created_by: reimbursement.order.store.users.first,
53
53
  memo: "Refund for uncreditable payments on order #{reimbursement.order.number}",
54
54
  currency: reimbursement.order.currency,
55
55
  store: reimbursement.order.store
@@ -13,6 +13,10 @@ module Spree
13
13
  @_return_quantity = value.to_i
14
14
  end
15
15
 
16
+ def pre_tax_amount=(amount)
17
+ self[:pre_tax_amount] = Spree::LocalizedNumber.parse(amount)
18
+ end
19
+
16
20
  def return_quantity
17
21
  @_return_quantity.nil? ? inventory_unit.quantity : @_return_quantity
18
22
  end
@@ -221,10 +221,27 @@ module Spree
221
221
  inventory_units.where(line_item_id: line_item.id, variant_id: line_item.variant_id || variant.id)
222
222
  end
223
223
 
224
+ # Returns the cost of the shipment
225
+ #
226
+ # @return [BigDecimal]
224
227
  def item_cost
225
228
  manifest.map { |m| (m.line_item.price + (m.line_item.adjustment_total / m.line_item.quantity)) * m.quantity }.sum
226
229
  end
227
230
 
231
+ # Returns the weight of the shipment
232
+ #
233
+ # @return [BigDecimal]
234
+ def item_weight
235
+ manifest.map { |m| m.line_item.item_weight }.sum
236
+ end
237
+
238
+ # Returns the weight unit of the shipment
239
+ #
240
+ # @return [String]
241
+ def weight_unit
242
+ manifest.first.line_item.weight_unit
243
+ end
244
+
228
245
  def line_items
229
246
  inventory_units.includes(:line_item).map(&:line_item).uniq
230
247
  end
@@ -416,6 +433,8 @@ module Spree
416
433
  end
417
434
 
418
435
  def can_get_rates?
436
+ return true unless order.requires_ship_address?
437
+
419
438
  order.ship_address&.valid?
420
439
  end
421
440
 
@@ -39,22 +39,21 @@ module Spree
39
39
  joins(:calculator).where(spree_calculators: { type: Spree::Calculator::Shipping::DigitalDelivery.to_s })
40
40
  }
41
41
 
42
- if defined?(PgSearch)
43
- # full text search
44
- include PgSearch::Model
45
- pg_search_scope :search_by_name, against: %i[name]
46
- else
47
- scope :search_by_name, ->(query) { where('name LIKE ?', "%#{query}%") }
48
- end
42
+ scope :search_by_name, ->(query) { where(arel_table[:name].lower.matches("%#{query}%")) }
49
43
 
50
44
  def include?(address)
45
+ return true unless requires_zone_check?
51
46
  return false unless address
52
47
 
53
- zones.includes(zone_members: :zoneable).any? do |zone|
48
+ zones.includes(:zone_members).any? do |zone|
54
49
  zone.include?(address)
55
50
  end
56
51
  end
57
52
 
53
+ def requires_zone_check?
54
+ !calculator.is_a?(Spree::Calculator::Shipping::DigitalDelivery)
55
+ end
56
+
58
57
  def build_tracking_url(tracking)
59
58
  return if tracking.blank?
60
59
 
@@ -93,7 +92,7 @@ module Spree
93
92
  if estimated_transit_business_days_min == estimated_transit_business_days_max
94
93
  estimated_transit_business_days_min.to_s
95
94
  else
96
- "#{estimated_transit_business_days_min}-#{estimated_transit_business_days_max}"
95
+ [estimated_transit_business_days_min, estimated_transit_business_days_max].compact.join("-")
97
96
  end
98
97
  end
99
98
 
@@ -109,6 +109,8 @@ module Spree
109
109
 
110
110
  has_many :integrations, class_name: 'Spree::Integration'
111
111
 
112
+ has_many :gift_cards, class_name: 'Spree::GiftCard', dependent: :destroy
113
+
112
114
  #
113
115
  # Page Builder associations
114
116
  #
@@ -16,13 +16,12 @@ module Spree
16
16
  AUTHORIZE_ACTION = 'authorize'.freeze
17
17
  ALLOCATION_ACTION = 'allocation'.freeze
18
18
 
19
- DEFAULT_CREATED_BY_EMAIL = 'spree@example.com'.freeze
20
-
19
+ belongs_to :store, class_name: 'Spree::Store'
21
20
  belongs_to :user, class_name: "::#{Spree.user_class}", foreign_key: 'user_id'
22
21
  belongs_to :category, class_name: 'Spree::StoreCreditCategory', optional: true
23
22
  belongs_to :created_by, class_name: "::#{Spree.admin_user_class}", foreign_key: 'created_by_id', optional: true
24
23
  belongs_to :credit_type, class_name: 'Spree::StoreCreditType', foreign_key: 'type_id', optional: true
25
- belongs_to :store, class_name: 'Spree::Store'
24
+ belongs_to :originator, polymorphic: true, optional: true
26
25
 
27
26
  has_many :store_credit_events, class_name: 'Spree::StoreCreditEvent'
28
27
  has_many :payments, as: :source, class_name: 'Spree::Payment'
@@ -34,14 +33,16 @@ module Spree
34
33
  validate :amount_used_less_than_or_equal_to_amount
35
34
  validate :amount_authorized_less_than_or_equal_to_amount
36
35
 
37
- delegate :name, to: :category, prefix: true
38
- delegate :email, to: :created_by, prefix: true
36
+ delegate :name, to: :category, prefix: true, allow_nil: true
37
+ delegate :email, to: :created_by, prefix: true, allow_nil: true
39
38
 
40
39
  scope :order_by_priority, -> { includes(:credit_type).order('spree_store_credit_types.priority ASC') }
41
40
 
42
41
  scope :not_authorized, -> { where(amount_authorized: 0) }
43
42
  scope :not_used, -> { where("#{Spree::StoreCredit.table_name}.amount_used < #{Spree::StoreCredit.table_name}.amount") }
44
43
  scope :available, -> { not_authorized.not_used }
44
+ scope :with_gift_card, -> { where(originator_type: 'Spree::GiftCard') }
45
+ scope :without_gift_card, -> { where(originator_type: [nil, '']).or(where.not(originator_type: 'Spree::GiftCard')) }
45
46
 
46
47
  after_save :store_event
47
48
  before_destroy :validate_no_amount_used
@@ -184,7 +185,9 @@ module Spree
184
185
 
185
186
  class << self
186
187
  def default_created_by
187
- Spree.user_class.find_by(email: DEFAULT_CREATED_BY_EMAIL)
188
+ Spree::Deprecation.warn('StoreCredit#default_created_by is deprecated and will be removed in Spree 6.0. Please use store.users.first instead.')
189
+
190
+ Spree::Store.current.users.first
188
191
  end
189
192
  end
190
193
 
@@ -2,9 +2,15 @@ module Spree
2
2
  class StoreCreditEvent < Spree.base_class
3
3
  acts_as_paranoid
4
4
 
5
+ #
6
+ # Associations
5
7
  belongs_to :store_credit
6
8
  belongs_to :originator, polymorphic: true
9
+ has_one :payment, -> { where(source_type: Spree::StoreCredit.to_s) }, foreign_key: :response_code, primary_key: :authorization_code
10
+ has_one :order, through: :payment
7
11
 
12
+ #
13
+ # Scopes
8
14
  scope :exposed_events, -> { where.not(action: [Spree::StoreCredit::ELIGIBLE_ACTION, Spree::StoreCredit::AUTHORIZE_ACTION]) }
9
15
  scope :reverse_chronological, -> { order(created_at: :desc) }
10
16
 
@@ -21,15 +27,13 @@ module Spree
21
27
  Spree.t('store_credit.authorized')
22
28
  when Spree::StoreCredit::ALLOCATION_ACTION
23
29
  Spree.t('store_credit.allocated')
30
+ when Spree::StoreCredit::ELIGIBLE_ACTION
31
+ Spree.t('store_credit.eligible')
24
32
  when Spree::StoreCredit::VOID_ACTION, Spree::StoreCredit::CREDIT_ACTION
25
33
  Spree.t('store_credit.credit')
26
34
  end
27
35
  end
28
36
 
29
- def order
30
- store.payments.find_by(response_code: authorization_code).try(:order)
31
- end
32
-
33
37
  def allocation?
34
38
  action == Spree::StoreCredit::ALLOCATION_ACTION
35
39
  end
@@ -17,10 +17,13 @@ module Spree
17
17
  include Spree::TranslatableResource
18
18
  include Spree::TranslatableResourceSlug
19
19
  include Spree::Metadata
20
+ include Spree::MemoizedData
20
21
  if defined?(Spree::Webhooks::HasWebhooks)
21
22
  include Spree::Webhooks::HasWebhooks
22
23
  end
23
24
 
25
+ MEMOIZED_METHODS = %w[cached_self_and_descendants_ids].freeze
26
+
24
27
  #
25
28
  # Magic methods
26
29
  #
@@ -154,6 +157,10 @@ module Spree
154
157
  sort_order == 'manual'
155
158
  end
156
159
 
160
+ def page_builder_image
161
+ square_image.presence || image
162
+ end
163
+
157
164
  def active_products_with_descendants
158
165
  @active_products_with_descendants ||= store.products.
159
166
  joins(:classifications).
@@ -368,7 +375,7 @@ module Spree
368
375
  end
369
376
 
370
377
  def cached_self_and_descendants_ids
371
- Rails.cache.fetch("#{cache_key_with_version}/descendant-ids") do
378
+ @cached_self_and_descendants_ids ||= Rails.cache.fetch("#{cache_key_with_version}/descendant-ids") do
372
379
  self_and_descendants.ids
373
380
  end
374
381
  end
@@ -155,7 +155,7 @@ module Spree
155
155
  end
156
156
  end
157
157
 
158
- # Returns an array of available layout section classes for the theme
158
+ # Returns an array of available layout section classes for the theme, eg. header, footer, newsletter, etc.
159
159
  #
160
160
  # @return [Array<Class>]
161
161
  def available_layout_sections
@@ -82,7 +82,7 @@ module Spree
82
82
 
83
83
  after_touch :clear_in_stock_cache
84
84
 
85
- scope :in_stock, -> { left_joins(:stock_items).where("#{Spree::StockItem.table_name}.count_on_hand > ? OR #{Spree::Variant.table_name}.track_inventory = ?", 0, false) }
85
+ scope :in_stock, -> { left_joins(:stock_items).where("#{Spree::Variant.table_name}.track_inventory = ? OR #{Spree::StockItem.table_name}.count_on_hand > ?", false, 0) }
86
86
  scope :backorderable, -> { left_joins(:stock_items).where(spree_stock_items: { backorderable: true }) }
87
87
  scope :in_stock_or_backorderable, -> { in_stock.or(backorderable) }
88
88
 
@@ -1,6 +1,8 @@
1
1
  module Spree
2
2
  module CSV
3
3
  class ProductVariantPresenter
4
+ include Spree::ImagesHelper
5
+
4
6
  CSV_HEADERS = [
5
7
  'product_id',
6
8
  'sku',
@@ -99,9 +101,9 @@ module Spree
99
101
  variant.backorderable?,
100
102
  variant.tax_category&.name,
101
103
  variant.digital?,
102
- variant.images[0]&.original_url,
103
- variant.images[1]&.original_url,
104
- variant.images[2]&.original_url,
104
+ spree_image_url(variant.images[0], image_url_options),
105
+ spree_image_url(variant.images[1], image_url_options),
106
+ spree_image_url(variant.images[2], image_url_options),
105
107
  index.positive? ? option_type(0)&.presentation : nil,
106
108
  index.positive? ? option_value(option_type(0)) : nil,
107
109
  index.positive? ? option_type(1)&.presentation : nil,
@@ -125,6 +127,15 @@ module Spree
125
127
  def option_value(option_type)
126
128
  variant.option_values.find { |ov| ov.option_type == option_type }&.presentation
127
129
  end
130
+
131
+ private
132
+
133
+ def image_url_options
134
+ {
135
+ width: 1000,
136
+ height: 1000
137
+ }
138
+ end
128
139
  end
129
140
  end
130
141
  end
@@ -0,0 +1,20 @@
1
+ # This validator can be overridden to implement custom phone validation logic.
2
+ # Currently uses the phonelib gem to validate phone numbers and ensure they
3
+ # belong to the address country.
4
+
5
+ require 'phonelib'
6
+
7
+ module Spree
8
+ module Addresses
9
+ class PhoneValidator < ActiveModel::Validator
10
+ def validate(address)
11
+ return if !Spree::Config[:address_requires_phone] || address.phone.blank? || address.country.blank? || address.country_iso.blank?
12
+
13
+ phone = Phonelib.parse(address.phone)
14
+ unless phone.valid_for_country?(address.country_iso)
15
+ address.errors.add(:phone, :invalid)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -14,7 +14,7 @@ module Spree
14
14
  private
15
15
 
16
16
  def check_if_can_be_destroyed(order:)
17
- return failure(false, Spree.t(:cannot_be_destroyed)) unless order&.can_be_destroyed?
17
+ return failure(false, Spree.t(:cannot_be_destroyed)) unless order&.can_be_deleted?
18
18
 
19
19
  success(order: order)
20
20
  end
@@ -4,6 +4,7 @@ module Spree
4
4
  prepend Spree::ServiceModule::Base
5
5
 
6
6
  def call(order:, line_item:, line_item_created: false, options: {})
7
+ order.remove_gift_card if order.gift_card.present?
7
8
  order_updater = ::Spree::OrderUpdater.new(order)
8
9
 
9
10
  order.payments.store_credits.checkout.destroy_all
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+ # Applies a gift card to an order
3
+ # under the hood it creates a store credit payment record and updates the gift card amount used
4
+ # @param gift_card [Spree::GiftCard] the gift card to apply
5
+ # @param order [Spree::Order] the order to apply the gift card to
6
+ # @return [Spree::Order] the order with the gift card applied
7
+ module Spree
8
+ module GiftCards
9
+ class Apply
10
+ prepend Spree::ServiceModule::Base
11
+
12
+ def call(gift_card:, order:)
13
+ # we shouldn't mix an order with a gift card and a store credit
14
+ return failure(:gift_card_using_store_credit_error) if order.using_store_credit?
15
+
16
+ # we shouldn't allow a gift card to be applied to an order with a different currency
17
+ return failure(:gift_card_mismatched_currency) if gift_card.currency != order.currency
18
+
19
+ amount = [gift_card.amount_remaining, order.total].min
20
+ store = order.store
21
+
22
+ return failure(:gift_card_no_amount_remaining) unless amount.positive? || order.total.zero?
23
+
24
+ payment_method = ensure_store_credit_payment_method!(store)
25
+
26
+ gift_card.lock!
27
+ order.with_lock do
28
+ store_credit = gift_card.store_credits.create!(
29
+ store: store,
30
+ user: order.user,
31
+ amount: amount,
32
+ currency: order.currency,
33
+ originator: gift_card,
34
+ action_originator: gift_card
35
+ )
36
+ gift_card.amount_used += amount
37
+ gift_card.save!
38
+
39
+ order.update!(gift_card: gift_card)
40
+ order.payments.create!(
41
+ source: store_credit,
42
+ payment_method: payment_method,
43
+ amount: amount,
44
+ state: 'checkout',
45
+ response_code: store_credit.generate_authorization_code
46
+ )
47
+ end
48
+
49
+ success(order.reload)
50
+ end
51
+
52
+ private
53
+
54
+ def ensure_store_credit_payment_method!(store)
55
+ payment_method = store.payment_methods.find_or_initialize_by(
56
+ type: 'Spree::PaymentMethod::StoreCredit'
57
+ )
58
+ payment_method.name ||= Spree.t(:store_credit_name)
59
+ payment_method.active = true
60
+ payment_method.save! if payment_method.new_record?
61
+
62
+ payment_method
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,17 @@
1
+ module Spree
2
+ module GiftCards
3
+ class Redeem
4
+ prepend Spree::ServiceModule::Base
5
+
6
+ def call(gift_card:)
7
+ if gift_card.amount_remaining.zero?
8
+ gift_card.redeem!
9
+ else
10
+ gift_card.partial_redeem!
11
+ end
12
+
13
+ success(gift_card)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,38 @@
1
+ module Spree
2
+ module GiftCards
3
+ class Remove
4
+ prepend Spree::ServiceModule::Base
5
+
6
+ def call(order:)
7
+ return failure(:remove_gift_card_on_completed_order_error) if order.completed?
8
+ return success(true) if order.gift_card.nil?
9
+
10
+ gift_card = order.gift_card
11
+
12
+ return failure(:gift_card_not_found) if gift_card.nil?
13
+
14
+ order.with_lock do
15
+ payments = order.payments.checkout.store_credits.where(source: gift_card.store_credits)
16
+ payment_total = payments.sum(:amount)
17
+ payments.each(&:invalidate!)
18
+
19
+ gift_card.with_lock do
20
+ gift_card.amount_used -= payment_total
21
+ gift_card.save!
22
+ end
23
+
24
+ # we need to destroy the store credits here because they are not associated with the order
25
+ # and we need to remove them from the gift card
26
+ # TODO: rather than destroying the store credits, we should void them
27
+ payments.each do |payment|
28
+ payment.source.destroy!
29
+ end
30
+
31
+ order.update!(gift_card: nil)
32
+ end
33
+
34
+ success(true)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -141,10 +141,10 @@ module Spree
141
141
  opt[:option_value_name]
142
142
  else
143
143
  opt[:option_value_presentation].parameterize
144
- end
144
+ end.strip
145
145
 
146
146
  option_value = option_type.option_values.where(name: option_value_identificator).first_or_initialize do |o|
147
- o.name = o.presentation = opt[:option_value_presentation]
147
+ o.presentation = opt[:option_value_presentation]
148
148
  o.save!
149
149
  end
150
150
 
@@ -5,7 +5,7 @@
5
5
  data-controller="address-form address-autocomplete"
6
6
  data-address-form-countries-value="<%= available_countries.to_json %>"
7
7
  data-address-form-states-value="<%= available_states.to_json %>"
8
- data-address-form-current-state-id-value="<%= address.state_id %>"
8
+ data-address-form-current-state-id-value="<%= address.state_id || address_form.object.state_id %>"
9
9
  >
10
10
  <div id="<%= "#{address_id}country" %>">
11
11
  <div class="form-group mb-0">
@@ -12,11 +12,11 @@
12
12
  <p class="mb-0"><%= Spree.t(:expiration) %> <%= source.month %>/<%= source.year %></p>
13
13
  </div>
14
14
  <% elsif source.is_a?(Spree::StoreCredit) %>
15
- <% if source.respond_to?(:gift_card) && source.gift_card.present? %>
16
- <div class="d-flex flex rounded bg-light px-3 py-2 mb-3 align-items-center">
17
- <%= icon('gift', height: 30, class: 'mr-3 text-muted') %>
15
+ <% if source.originator_type == 'Spree::GiftCard' %>
16
+ <div class="d-flex align-items-center">
17
+ <%= icon('gift', height: 30, class: 'mr-3 text-muted opacity-50') %>
18
18
  <div>
19
- <p class="mb-1"><%= Spree.t(:gift_card) %>: <strong><%= source.gift_card.code.upcase %></strong></p>
19
+ <p class="mb-1"><%= Spree.t(:gift_card) %>: <strong><%= source.originator.code.upcase %></strong></p>
20
20
  <%= @order.display_gift_card_total %>
21
21
  </div>
22
22
  </div>