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.
- checksums.yaml +4 -4
- data/app/helpers/spree/base_helper.rb +5 -7
- data/app/jobs/spree/payments/handle_webhook_job.rb +22 -0
- data/app/mailers/spree/base_mailer.rb +1 -1
- data/app/models/concerns/spree/user_methods.rb +16 -0
- data/app/models/spree/allowed_origin.rb +44 -0
- data/app/models/spree/asset.rb +121 -3
- data/app/models/spree/cart_promotion.rb +7 -0
- data/app/models/spree/checkout/default_requirements.rb +51 -0
- data/app/models/spree/checkout/registry.rb +112 -0
- data/app/models/spree/checkout/requirement.rb +49 -0
- data/app/models/spree/checkout/requirements.rb +56 -0
- data/app/models/spree/checkout/step.rb +52 -0
- data/app/models/spree/image/configuration/active_storage.rb +2 -14
- data/app/models/spree/image.rb +2 -78
- data/app/models/spree/legacy_user.rb +1 -0
- data/app/models/spree/line_item.rb +3 -3
- data/app/models/spree/order/checkout.rb +18 -0
- data/app/models/spree/order.rb +3 -0
- data/app/models/spree/order_promotion.rb +2 -0
- data/app/models/spree/payment_method.rb +34 -0
- data/app/models/spree/payment_session.rb +18 -0
- data/app/models/spree/product.rb +45 -34
- data/app/models/spree/shipment.rb +1 -0
- data/app/models/spree/store.rb +32 -0
- data/app/models/spree/variant.rb +21 -12
- data/app/services/spree/cart/create.rb +3 -30
- data/app/services/spree/carts/complete.rb +46 -0
- data/app/services/spree/carts/create.rb +32 -0
- data/app/services/spree/carts/update.rb +115 -0
- data/app/services/spree/{cart → carts}/upsert_items.rb +19 -23
- data/app/services/spree/payments/handle_webhook.rb +58 -0
- data/app/services/spree/seeds/all.rb +1 -0
- data/app/services/spree/seeds/allowed_origins.rb +14 -0
- data/app/views/spree/shared/_mailer_logo.html.erb +1 -1
- data/config/locales/en.yml +23 -2
- data/db/migrate/20260315000000_create_spree_allowed_origins.rb +14 -0
- data/db/migrate/20260315100000_add_product_media_support.rb +21 -0
- data/lib/spree/core/configuration.rb +3 -0
- data/lib/spree/core/dependencies.rb +4 -2
- data/lib/spree/core/version.rb +1 -1
- data/lib/spree/core.rb +1 -0
- data/lib/spree/permitted_attributes.rb +5 -1
- data/lib/spree/testing_support/factories/allowed_origin_factory.rb +8 -0
- data/lib/spree/testing_support/factories/asset_factory.rb +6 -9
- data/lib/spree/testing_support/factories/image_factory.rb +3 -1
- data/lib/spree/testing_support/factories/order_factory.rb +3 -0
- data/lib/tasks/images.rake +11 -11
- data/lib/tasks/products.rake +4 -2
- metadata +21 -6
- 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
|
|
3
|
-
# Bulk upsert line items on
|
|
2
|
+
module Carts
|
|
3
|
+
# Bulk upsert line items on a cart.
|
|
4
4
|
#
|
|
5
|
-
# For each entry in +
|
|
6
|
-
# - If a line item for the variant already exists
|
|
7
|
-
# - If no line item exists
|
|
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
|
|
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::
|
|
17
|
-
#
|
|
18
|
-
#
|
|
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(
|
|
28
|
-
|
|
29
|
-
return success(
|
|
23
|
+
def call(cart:, items:)
|
|
24
|
+
items = Array(items)
|
|
25
|
+
return success(cart) if items.empty?
|
|
30
26
|
|
|
31
|
-
store =
|
|
27
|
+
store = cart.store || Spree::Current.store
|
|
32
28
|
|
|
33
29
|
ApplicationRecord.transaction do
|
|
34
|
-
|
|
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 #{
|
|
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:
|
|
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 =
|
|
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
|
-
|
|
52
|
+
cart.update_with_updater!
|
|
57
53
|
end
|
|
58
54
|
|
|
59
|
-
success(
|
|
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
|
|
@@ -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.
|
|
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) %>
|
data/config/locales/en.yml
CHANGED
|
@@ -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',
|
data/lib/spree/core/version.rb
CHANGED
data/lib/spree/core.rb
CHANGED
|
@@ -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,
|
|
@@ -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::
|
|
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')
|
data/lib/tasks/images.rake
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
namespace :spree do
|
|
2
|
-
namespace :
|
|
3
|
-
desc 'Backfill
|
|
4
|
-
task
|
|
5
|
-
puts 'Backfilling variant
|
|
6
|
-
Spree::Variant.where(
|
|
7
|
-
|
|
8
|
-
variant.update_column(:
|
|
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
|
|
12
|
-
Spree::Product.where(
|
|
13
|
-
|
|
14
|
-
product.update_column(:
|
|
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!'
|
data/lib/tasks/products.rake
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
namespace :spree do
|
|
2
2
|
namespace :products do
|
|
3
|
-
desc 'Reset counter caches (variant_count, classification_count,
|
|
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
|
-
|
|
13
|
+
media_count: total_media,
|
|
12
14
|
updated_at: Time.current
|
|
13
15
|
)
|
|
14
16
|
print '.'
|