spree_core 5.4.0.beta6 → 5.4.0.beta8

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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/app/helpers/spree/base_helper.rb +5 -7
  3. data/app/jobs/spree/payments/handle_webhook_job.rb +22 -0
  4. data/app/mailers/spree/base_mailer.rb +1 -1
  5. data/app/models/concerns/spree/user_methods.rb +16 -0
  6. data/app/models/spree/allowed_origin.rb +44 -0
  7. data/app/models/spree/asset.rb +121 -3
  8. data/app/models/spree/cart_promotion.rb +7 -0
  9. data/app/models/spree/checkout/default_requirements.rb +51 -0
  10. data/app/models/spree/checkout/registry.rb +112 -0
  11. data/app/models/spree/checkout/requirement.rb +49 -0
  12. data/app/models/spree/checkout/requirements.rb +56 -0
  13. data/app/models/spree/checkout/step.rb +52 -0
  14. data/app/models/spree/image/configuration/active_storage.rb +2 -14
  15. data/app/models/spree/image.rb +2 -78
  16. data/app/models/spree/legacy_user.rb +1 -0
  17. data/app/models/spree/line_item.rb +3 -3
  18. data/app/models/spree/order/checkout.rb +18 -0
  19. data/app/models/spree/order.rb +3 -0
  20. data/app/models/spree/order_promotion.rb +2 -0
  21. data/app/models/spree/payment_method.rb +34 -0
  22. data/app/models/spree/payment_session.rb +18 -0
  23. data/app/models/spree/product.rb +45 -34
  24. data/app/models/spree/shipment.rb +1 -0
  25. data/app/models/spree/store.rb +32 -0
  26. data/app/models/spree/variant.rb +21 -12
  27. data/app/services/spree/cart/create.rb +3 -30
  28. data/app/services/spree/carts/complete.rb +46 -0
  29. data/app/services/spree/carts/create.rb +32 -0
  30. data/app/services/spree/carts/update.rb +115 -0
  31. data/app/services/spree/{cart → carts}/upsert_items.rb +19 -23
  32. data/app/services/spree/payments/handle_webhook.rb +58 -0
  33. data/app/services/spree/seeds/all.rb +1 -0
  34. data/app/services/spree/seeds/allowed_origins.rb +14 -0
  35. data/app/views/spree/shared/_mailer_logo.html.erb +1 -1
  36. data/config/locales/en.yml +23 -2
  37. data/db/migrate/20260315000000_create_spree_allowed_origins.rb +14 -0
  38. data/db/migrate/20260315100000_add_product_media_support.rb +21 -0
  39. data/lib/spree/core/configuration.rb +3 -0
  40. data/lib/spree/core/dependencies.rb +4 -2
  41. data/lib/spree/core/version.rb +1 -1
  42. data/lib/spree/core.rb +1 -0
  43. data/lib/spree/permitted_attributes.rb +5 -1
  44. data/lib/spree/testing_support/factories/allowed_origin_factory.rb +8 -0
  45. data/lib/spree/testing_support/factories/asset_factory.rb +6 -9
  46. data/lib/spree/testing_support/factories/image_factory.rb +3 -1
  47. data/lib/spree/testing_support/factories/order_factory.rb +3 -0
  48. data/lib/tasks/images.rake +11 -11
  49. data/lib/tasks/products.rake +4 -2
  50. metadata +21 -6
  51. data/app/services/spree/orders/update.rb +0 -121
@@ -0,0 +1,115 @@
1
+ module Spree
2
+ module Carts
3
+ class Update
4
+ prepend Spree::ServiceModule::Base
5
+
6
+ def call(cart:, params:)
7
+ @cart = cart
8
+ @params = params.to_h.deep_symbolize_keys
9
+
10
+ ApplicationRecord.transaction do
11
+ assign_cart_attributes
12
+ assign_address(:ship_address)
13
+ assign_address(:bill_address)
14
+
15
+ cart.save!
16
+
17
+ process_items
18
+ end
19
+
20
+ try_advance
21
+
22
+ success(cart)
23
+ rescue ActiveRecord::RecordNotFound
24
+ raise
25
+ rescue ActiveRecord::RecordInvalid => e
26
+ failure(cart, e.record.errors.full_messages.to_sentence)
27
+ rescue StandardError => e
28
+ failure(cart, e.message)
29
+ end
30
+
31
+ private
32
+
33
+ attr_reader :cart, :params
34
+
35
+ def assign_cart_attributes
36
+ cart.email = params[:email] if params[:email].present?
37
+ cart.special_instructions = params[:special_instructions] if params.key?(:special_instructions)
38
+ cart.currency = params[:currency].upcase if params[:currency].present?
39
+ cart.locale = params[:locale] if params[:locale].present?
40
+ cart.metadata = cart.metadata.merge(params[:metadata].to_h) if params[:metadata].present?
41
+ end
42
+
43
+ def assign_address(address_type)
44
+ address_id_param = params[:"#{address_type}_id"]
45
+ address_params = params[address_type]
46
+
47
+ if address_id_param.present?
48
+ address_id = resolve_address_id(address_id_param)
49
+ cart.public_send(:"#{address_type}_id=", address_id) if address_id
50
+ return
51
+ end
52
+
53
+ return unless address_params.is_a?(Hash)
54
+
55
+ if address_params[:id].present?
56
+ address_id = resolve_address_id(address_params[:id])
57
+ cart.public_send(:"#{address_type}_id=", address_id) if address_id
58
+ else
59
+ # Only revert to address state when shipping address changes.
60
+ # Billing address updates (e.g. during payment) should not
61
+ # destroy shipments and reset the checkout flow.
62
+ revert_to_address_state if address_type == :ship_address && cart.has_checkout_step?('address')
63
+ cart.public_send(:"#{address_type}_attributes=", address_params)
64
+ end
65
+ end
66
+
67
+ def process_items
68
+ return unless params[:items].is_a?(Array)
69
+
70
+ result = Spree::Carts::UpsertItems.call(
71
+ cart: cart,
72
+ items: params[:items]
73
+ )
74
+
75
+ raise StandardError, result.error.to_s if result.failure?
76
+ end
77
+
78
+ def resolve_address_id(prefixed_id)
79
+ return unless cart.user
80
+
81
+ decoded = Spree::Address.decode_prefixed_id(prefixed_id)
82
+ decoded ? cart.user.addresses.find_by(id: decoded)&.id : nil
83
+ end
84
+
85
+ def revert_to_address_state
86
+ return if ['cart', 'address'].include?(cart.state)
87
+
88
+ cart.state = 'address'
89
+ end
90
+
91
+ # Auto-advance as far as the checkout state machine allows.
92
+ # Loops cart.next until the cart can't progress further (e.g. missing
93
+ # payment) or reaches confirm/complete. Stops at the first step whose
94
+ # before_transition guard fails — the `requirements` array in the
95
+ # serialized response tells the frontend what's still missing.
96
+ # Failure is swallowed — the update itself already succeeded.
97
+ def try_advance
98
+ return if cart.complete? || cart.canceled?
99
+
100
+ loop do
101
+ break unless cart.next
102
+ break if cart.confirm? || cart.complete?
103
+ end
104
+ rescue StandardError => e
105
+ Rails.error.report(e, context: { order_id: cart.id, state: cart.state }, source: 'spree.checkout')
106
+ ensure
107
+ begin
108
+ cart.reload
109
+ rescue StandardError # rubocop:disable Lint/SuppressedException
110
+ # reload failure must not mask the original result
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
@@ -1,21 +1,17 @@
1
1
  module Spree
2
- module Cart
3
- # Bulk upsert line items on an order.
2
+ module Carts
3
+ # Bulk upsert line items on a cart.
4
4
  #
5
- # For each entry in +line_items+:
6
- # - If a line item for the variant already exists sets its quantity
7
- # - If no line item exists creates one with the given quantity
5
+ # For each entry in +items+:
6
+ # - If a line item for the variant already exists -> sets its quantity
7
+ # - If no line item exists -> creates one with the given quantity
8
8
  #
9
- # After all items are processed the order is recalculated once.
10
- #
11
- # Price calculation and tax adjustments are handled by LineItem model callbacks
12
- # (copy_price, update_adjustments, update_tax_charge), so we only need to
13
- # save each item and run a single order recalculation at the end.
9
+ # After all items are processed the cart is recalculated once.
14
10
  #
15
11
  # @example
16
- # Spree::Cart::UpsertItems.call(
17
- # order: order,
18
- # line_items: [
12
+ # Spree::Carts::UpsertItems.new.call(
13
+ # cart: cart,
14
+ # items: [
19
15
  # { variant_id: "variant_k5nR8xLq", quantity: 2 },
20
16
  # { variant_id: "variant_m3Rp9wXz", quantity: 1 }
21
17
  # ]
@@ -24,39 +20,39 @@ module Spree
24
20
  class UpsertItems
25
21
  prepend Spree::ServiceModule::Base
26
22
 
27
- def call(order:, line_items:)
28
- line_items = Array(line_items)
29
- return success(order) if line_items.empty?
23
+ def call(cart:, items:)
24
+ items = Array(items)
25
+ return success(cart) if items.empty?
30
26
 
31
- store = order.store || Spree::Current.store
27
+ store = cart.store || Spree::Current.store
32
28
 
33
29
  ApplicationRecord.transaction do
34
- line_items.each do |item_params|
30
+ items.each do |item_params|
35
31
  item_params = item_params.to_h.deep_symbolize_keys
36
32
  variant = resolve_variant(store, item_params[:variant_id])
37
33
  next unless variant
38
34
 
39
35
  quantity = (item_params[:quantity] || 1).to_i
40
36
 
41
- return failure(variant, "#{variant.name} is not available in #{order.currency}") if variant.amount_in(order.currency).nil?
37
+ return failure(variant, "#{variant.name} is not available in #{cart.currency}") if variant.amount_in(cart.currency).nil?
42
38
 
43
- line_item = Spree.line_item_by_variant_finder.new.execute(order: order, variant: variant)
39
+ line_item = Spree.line_item_by_variant_finder.new.execute(order: cart, variant: variant)
44
40
 
45
41
  if line_item
46
42
  line_item.quantity = quantity
47
43
  line_item.metadata = line_item.metadata.merge(item_params[:metadata].to_h) if item_params[:metadata].present?
48
44
  else
49
- line_item = order.line_items.new(quantity: quantity, variant: variant, options: { currency: order.currency })
45
+ line_item = cart.items.new(quantity: quantity, variant: variant, options: { currency: cart.currency })
50
46
  line_item.metadata = item_params[:metadata].to_h if item_params[:metadata].present?
51
47
  end
52
48
 
53
49
  return failure(line_item) unless line_item.save
54
50
  end
55
51
 
56
- order.update_with_updater!
52
+ cart.update_with_updater!
57
53
  end
58
54
 
59
- success(order)
55
+ success(cart)
60
56
  end
61
57
 
62
58
  private
@@ -0,0 +1,58 @@
1
+ module Spree
2
+ module Payments
3
+ class HandleWebhook
4
+ prepend Spree::ServiceModule::Base
5
+
6
+ # @param payment_method [Spree::PaymentMethod] the payment method that received the webhook
7
+ # @param action [Symbol] normalized action (:captured, :authorized, :failed, :canceled)
8
+ # @param payment_session [Spree::PaymentSession] the payment session associated with the webhook
9
+ # @param metadata [Hash] gateway-specific metadata (e.g. charge data, psp reference)
10
+ def call(payment_method:, action:, payment_session:, metadata: {})
11
+ return success(nil) if payment_session.nil?
12
+
13
+ order = payment_session.order
14
+
15
+ case action
16
+ when :captured, :authorized
17
+ handle_success(payment_session, order, metadata)
18
+ when :failed
19
+ payment_session.fail if payment_session.can_fail?
20
+ success(payment_session)
21
+ when :canceled
22
+ payment_session.cancel if payment_session.can_cancel?
23
+ success(payment_session)
24
+ else
25
+ failure(payment_session, "Unknown webhook action: #{action}")
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def handle_success(payment_session, order, metadata)
32
+ order.with_lock do
33
+ # Ensure payment record exists
34
+ payment = payment_session.find_or_create_payment!(metadata)
35
+
36
+ # Mark payment as completed — the webhook confirms the gateway processed it
37
+ if payment.present? && !payment.completed?
38
+ payment.started_processing! if payment.checkout?
39
+ payment.complete! if payment.can_complete?
40
+ end
41
+
42
+ # Mark session as completed
43
+ payment_session.complete if payment_session.can_complete?
44
+
45
+ # Complete order if not already done
46
+ unless order.reload.completed?
47
+ Spree::Dependencies.carts_complete_service.constantize.call(cart: order)
48
+ end
49
+ end
50
+
51
+ success(payment_session)
52
+ rescue StandardError => e
53
+ Rails.error.report(e, context: { payment_session_id: payment_session.id, order_id: order.id }, source: 'spree.payments.webhook')
54
+ failure(payment_session, e.message)
55
+ end
56
+ end
57
+ end
58
+ end
@@ -28,6 +28,7 @@ module Spree
28
28
  # add store resources
29
29
  PaymentMethods.call
30
30
  ApiKeys.call
31
+ AllowedOrigins.call
31
32
  end
32
33
  end
33
34
  end
@@ -0,0 +1,14 @@
1
+ module Spree
2
+ module Seeds
3
+ class AllowedOrigins
4
+ prepend Spree::ServiceModule::Base
5
+
6
+ def call
7
+ store = Spree::Store.default
8
+ return unless store&.persisted?
9
+
10
+ store.allowed_origins.find_or_create_by!(origin: 'http://localhost')
11
+ end
12
+ end
13
+ end
14
+ end
@@ -2,7 +2,7 @@
2
2
  <% height ||= 64 %>
3
3
  <% logo_css ||= '' %>
4
4
 
5
- <%= link_to current_store.url_or_custom_domain, id: 'site-logo', style: "text-decoration: none;" do %>
5
+ <%= link_to current_store.storefront_url, id: 'site-logo', style: "text-decoration: none;" do %>
6
6
  <span style="display: none;"><%= current_store.name %></span>
7
7
  <% if logo.present? && logo.attached? && logo.variable? %>
8
8
  <% aspect_ratio = spree_asset_aspect_ratio(logo) %>
@@ -240,6 +240,10 @@ en:
240
240
  messages:
241
241
  blank: can't be blank
242
242
  models:
243
+ spree/allowed_origin:
244
+ attributes:
245
+ origin:
246
+ must_be_origin_only: must be an origin (scheme and host) without path, query, or fragment
243
247
  spree/calculator/tiered_flat_rate:
244
248
  attributes:
245
249
  base:
@@ -742,6 +746,8 @@ en:
742
746
  all_products: All products
743
747
  all_rights_reserved: All rights reserved
744
748
  all_time: All time
749
+ allowed_origin: Allowed Origin
750
+ allowed_origins: Allowed Origins
745
751
  already_have_account: Already have an account?
746
752
  alt_text: Alternative Text
747
753
  alternative_phone: Alternative Phone
@@ -837,6 +843,7 @@ en:
837
843
  card_type: Brand
838
844
  card_type_is: Card type is
839
845
  cart: Cart
846
+ cart_already_updated: The cart has already been updated.
840
847
  cart_line_item:
841
848
  discontinued: "%{li_name} was removed because it was discontinued"
842
849
  out_of_stock: "%{li_name} was removed because it was sold out"
@@ -864,6 +871,12 @@ en:
864
871
  charged: Charged
865
872
  checkout: Checkout
866
873
  checkout_message: Checkout message
874
+ checkout_requirements:
875
+ email_required: Email address is required
876
+ line_items_required: Add at least one item to your cart
877
+ payment_required: Add a payment method
878
+ ship_address_required: Shipping address is required
879
+ shipping_method_required: Select a shipping method for all shipments
867
880
  choose_a_customer: Choose a customer
868
881
  choose_a_taxon_to_sort_products_for: Choose a taxon to sort products for
869
882
  choose_currency: Choose Currency
@@ -969,6 +982,14 @@ en:
969
982
  customer_group_rule:
970
983
  choose_customer_groups: 'Select the customer group(s) to which this promotion should apply:'
971
984
  customer_groups: Customer Groups
985
+ customer_mailer:
986
+ password_reset_email:
987
+ action: Reset Password
988
+ expiry_notice: This password reset link will expire shortly. If you did not request this, no action is needed.
989
+ greeting: Hi %{name},
990
+ ignore_notice: If you did not request a password reset, you can safely ignore this email. Your password will not be changed.
991
+ instructions: We received a request to reset your password. Click the button below to choose a new password.
992
+ subject: Password Reset
972
993
  customer_removed_from_group: Customer removed from group
973
994
  customer_return: Customer Return
974
995
  customer_returns: Customer Returns
@@ -1097,6 +1118,7 @@ en:
1097
1118
  blank: can't be blank
1098
1119
  cannot_remove_icon: Cannot remove image
1099
1120
  could_not_create_taxon: Could not create taxon
1121
+ must_be_origin_only: must be an origin (scheme and host) without path, query, or fragment
1100
1122
  no_shipping_methods_available: No shipping methods available for selected location, please change your address and try again.
1101
1123
  store_association_can_not_be_changed: The store association can not be changed
1102
1124
  store_is_already_set: Store is already set
@@ -1411,6 +1433,7 @@ en:
1411
1433
  new: New
1412
1434
  new_address: New address
1413
1435
  new_adjustment: New Adjustment
1436
+ new_allowed_origin: New Allowed Origin
1414
1437
  new_api_key: New API Key
1415
1438
  new_balance: New balance
1416
1439
  new_billing_address: New Billing Address
@@ -1559,8 +1582,6 @@ en:
1559
1582
  order: Order
1560
1583
  order_adjustments: Order adjustments
1561
1584
  order_again: Order again
1562
- order_already_completed: Order already completed
1563
- order_already_updated: The order has already been updated.
1564
1585
  order_approved: Order approved
1565
1586
  order_canceled: Order canceled
1566
1587
  order_details: Order Details
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateSpreeAllowedOrigins < ActiveRecord::Migration[7.2]
4
+ def change
5
+ create_table :spree_allowed_origins do |t|
6
+ t.references :store, null: false
7
+ t.string :origin, null: false
8
+ t.timestamps
9
+ end
10
+
11
+ add_index :spree_allowed_origins, [:store_id, :origin], unique: true,
12
+ name: 'index_spree_allowed_origins_on_store_id_and_origin'
13
+ end
14
+ end
@@ -0,0 +1,21 @@
1
+ class AddProductMediaSupport < ActiveRecord::Migration[7.2]
2
+ def change
3
+ add_column :spree_assets, :media_type, :string
4
+ add_column :spree_assets, :focal_point_x, :decimal, precision: 5, scale: 4
5
+ add_column :spree_assets, :focal_point_y, :decimal, precision: 5, scale: 4
6
+ add_column :spree_assets, :external_video_url, :string
7
+
8
+ add_index :spree_assets, :media_type
9
+
10
+ rename_column :spree_variants, :image_count, :media_count
11
+ rename_column :spree_products, :total_image_count, :media_count
12
+ rename_column :spree_variants, :thumbnail_id, :primary_media_id
13
+ rename_column :spree_products, :thumbnail_id, :primary_media_id
14
+
15
+ reversible do |dir|
16
+ dir.up do
17
+ Spree::Asset.unscoped.where(media_type: nil).update_all(media_type: 'image')
18
+ end
19
+ end
20
+ end
21
+ end
@@ -107,6 +107,9 @@ module Spree
107
107
  preference :coupon_codes_web_limit, :integer, default: 500 # number of coupon codes to be generated in the web process, more than this will be generated in a background job
108
108
  preference :coupon_codes_total_limit, :integer, default: 5000 # the maximum number of coupon codes to be generated
109
109
 
110
+ # password reset
111
+ preference :customer_password_reset_expires_in, :integer, default: 15 # password reset token expiration time in minutes
112
+
110
113
  # gift cards
111
114
  preference :gift_card_batch_web_limit, :integer, default: 500 # number of gift card codes to be generated in the web process, more than this will be generated in a background job
112
115
  preference :gift_card_batch_limit, :integer, default: 50_000
@@ -16,7 +16,6 @@ module Spree
16
16
  cart_remove_item_service: 'Spree::Cart::RemoveItem',
17
17
  cart_remove_line_item_service: 'Spree::Cart::RemoveLineItem',
18
18
  cart_set_item_quantity_service: 'Spree::Cart::SetQuantity',
19
- cart_upsert_items_service: 'Spree::Cart::UpsertItems',
20
19
  cart_estimate_shipping_rates_service: 'Spree::Cart::EstimateShippingRates',
21
20
  cart_empty_service: 'Spree::Cart::Empty',
22
21
  cart_destroy_service: 'Spree::Cart::Destroy',
@@ -24,6 +23,9 @@ module Spree
24
23
  cart_change_currency_service: 'Spree::Cart::ChangeCurrency',
25
24
  cart_remove_out_of_stock_items_service: 'Spree::Cart::RemoveOutOfStockItems',
26
25
 
26
+ # carts
27
+ carts_complete_service: 'Spree::Carts::Complete',
28
+
27
29
  # checkout
28
30
  checkout_next_service: 'Spree::Checkout::Next',
29
31
  checkout_advance_service: 'Spree::Checkout::Advance',
@@ -42,7 +44,6 @@ module Spree
42
44
  # order
43
45
  order_approve_service: 'Spree::Orders::Approve',
44
46
  order_cancel_service: 'Spree::Orders::Cancel',
45
- order_update_service: 'Spree::Orders::Update',
46
47
  order_updater: 'Spree::OrderUpdater',
47
48
 
48
49
  # shipment
@@ -87,6 +88,7 @@ module Spree
87
88
  line_item_destroy_service: 'Spree::LineItems::Destroy',
88
89
 
89
90
  payment_create_service: 'Spree::Payments::Create',
91
+ payments_handle_webhook_service: 'Spree::Payments::HandleWebhook',
90
92
 
91
93
  # finders
92
94
  address_finder: 'Spree::Addresses::Find',
@@ -1,5 +1,5 @@
1
1
  module Spree
2
- VERSION = '5.4.0.beta6'.freeze
2
+ VERSION = '5.4.0.beta8'.freeze
3
3
 
4
4
  def self.version
5
5
  VERSION
data/lib/spree/core.rb CHANGED
@@ -109,6 +109,7 @@ module Spree
109
109
  addresses: :default,
110
110
  gift_cards: :default,
111
111
  webhooks: :default,
112
+ payment_webhooks: :default,
112
113
  api_keys: :default
113
114
  )
114
115
  end
@@ -2,6 +2,7 @@ module Spree
2
2
  module PermittedAttributes
3
3
  ATTRIBUTES = [
4
4
  :address_attributes,
5
+ :allowed_origin_attributes,
5
6
  :api_key_attributes,
6
7
  :asset_attributes,
7
8
  :checkout_attributes,
@@ -87,9 +88,12 @@ module Spree
87
88
  state: [:name, :abbr] }
88
89
  ]
89
90
 
91
+ @@allowed_origin_attributes = [:origin]
92
+
90
93
  @@api_key_attributes = [:name, :key_type]
91
94
 
92
- @@asset_attributes = [:type, :viewable_id, :viewable_type, :attachment, :alt, :position]
95
+ @@asset_attributes = [:type, :viewable_id, :viewable_type, :attachment, :alt, :position,
96
+ :media_type, :focal_point_x, :focal_point_y, :external_video_url]
93
97
 
94
98
  @@checkout_attributes = [
95
99
  :coupon_code, :email, :shipping_method_id, :special_instructions, :use_billing, :use_shipping,
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.define do
4
+ factory :allowed_origin, class: Spree::AllowedOrigin do
5
+ store
6
+ sequence(:origin) { |n| "https://storefront#{n}.example.com" }
7
+ end
8
+ end
@@ -1,15 +1,12 @@
1
1
  FactoryBot.define do
2
2
  factory :asset, class: Spree::Asset do
3
- viewable_type {}
4
- viewable_id {}
5
- attachment_width { 340 }
6
- attachment_height { 280 }
7
- attachment_file_size { 128 }
8
3
  position { 1 }
9
- attachment_content_type { '.jpg' }
10
- attachment_file_name { 'attachment.jpg' }
11
- type {}
12
- attachment_updated_at {}
13
4
  alt {}
5
+
6
+ after(:build) do |asset|
7
+ if asset.media_type == 'image' && !asset.attachment.attached?
8
+ asset.attachment.attach(io: File.new(Spree::Core::Engine.root + 'spec/fixtures' + 'thinking-cat.jpg'), filename: 'thinking-cat.jpg')
9
+ end
10
+ end
14
11
  end
15
12
  end
@@ -1,5 +1,7 @@
1
1
  FactoryBot.define do
2
- factory :image, class: Spree::Image do
2
+ factory :image, class: Spree::Asset do
3
+ media_type { 'image' }
4
+
3
5
  before(:create) do |image|
4
6
  if image.class.method_defined?(:attachment)
5
7
  image.attachment.attach(io: File.new(Spree::Core::Engine.root + 'spec/fixtures' + 'thinking-cat.jpg'), filename: 'thinking-cat.jpg')
@@ -22,6 +22,9 @@ FactoryBot.define do
22
22
  end
23
23
  end
24
24
 
25
+ factory :cart, class: Spree::Order do
26
+ end
27
+
25
28
  factory :order_with_totals do
26
29
  after(:create) do |order, evaluator|
27
30
  create(:line_item, order: order, price: evaluator.line_items_price)
@@ -1,17 +1,17 @@
1
1
  namespace :spree do
2
- namespace :images do
3
- desc 'Backfill thumbnail_id for all variants and products'
4
- task backfill_thumbnails: :environment do
5
- puts 'Backfilling variant thumbnails...'
6
- Spree::Variant.where(thumbnail_id: nil).where.not(image_count: 0).find_each do |variant|
7
- first_image = variant.images.order(:position).first
8
- variant.update_column(:thumbnail_id, first_image.id) if first_image
2
+ namespace :media do
3
+ desc 'Backfill primary_media_id for all variants and products'
4
+ task backfill_primary_media: :environment do
5
+ puts 'Backfilling variant primary_media...'
6
+ Spree::Variant.where(primary_media_id: nil).where.not(media_count: 0).find_each do |variant|
7
+ first_media = variant.gallery_media.first
8
+ variant.update_column(:primary_media_id, first_media.id) if first_media
9
9
  end
10
10
 
11
- puts 'Backfilling product thumbnails...'
12
- Spree::Product.where(thumbnail_id: nil).where.not(total_image_count: 0).find_each do |product|
13
- first_image = product.variant_images.order(:position).first
14
- product.update_column(:thumbnail_id, first_image.id) if first_image
11
+ puts 'Backfilling product primary_media...'
12
+ Spree::Product.where(primary_media_id: nil).where.not(media_count: 0).find_each do |product|
13
+ first_media = product.gallery_media.first
14
+ product.update_column(:primary_media_id, first_media.id) if first_media
15
15
  end
16
16
 
17
17
  puts 'Done!'
@@ -1,14 +1,16 @@
1
1
  namespace :spree do
2
2
  namespace :products do
3
- desc 'Reset counter caches (variant_count, classification_count, total_image_count) on products'
3
+ desc 'Reset counter caches (variant_count, classification_count, media_count) on products'
4
4
  task reset_counter_caches: :environment do |_t, _args|
5
5
  puts 'Resetting product counter caches...'
6
6
 
7
7
  Spree::Product.find_each do |product|
8
+ total_media = product.media.count + product.variant_images.where.not(id: product.media.select(:id)).count
9
+
8
10
  product.update_columns(
9
11
  variant_count: product.variants.count,
10
12
  classification_count: product.classifications.count,
11
- total_image_count: product.variant_images.count,
13
+ media_count: total_media,
12
14
  updated_at: Time.current
13
15
  )
14
16
  print '.'