spree_core 5.0.0 → 5.0.1
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 +9 -0
- data/app/helpers/spree/images_helper.rb +71 -0
- data/app/javascript/spree/core/controllers/address_autocomplete_controller.js +1 -4
- data/app/javascript/spree/core/helpers/address_autocomplete/google_places_suggestions_provider.js +101 -119
- data/app/models/spree/credit_card.rb +1 -1
- data/app/models/spree/order.rb +7 -14
- data/app/models/spree/theme.rb +2 -2
- data/app/services/spree/checkout/update.rb +14 -2
- data/app/services/spree/payments/create.rb +1 -1
- data/app/services/spree/products/prepare_nested_attributes.rb +3 -1
- data/app/views/active_storage/blobs/_blob.html.erb +1 -7
- data/app/views/spree/shared/_mailer_logo.html.erb +1 -1
- data/config/locales/en.yml +18 -1
- data/db/migrate/20241005093437_add_multi_code_to_spree_promotions.rb +5 -3
- data/lib/spree/core/version.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5a60d5a6d4ba0fd4d7e223de06835389a8c4c39572dabe07e29a4fb5312d2548
|
4
|
+
data.tar.gz: 1163cb99eaca807157669cc6ae77f3c2eae58c8b63de520b27c329244c870d22
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d69a80bb1326c8f17f29c299701c20b372736a16002688a51cb0f9cef9f95a650ef3f2eb860e5d671e3fda54745fc2eace0b54a534e746d0b12e0337ea7cb547
|
7
|
+
data.tar.gz: 20864a616e90144196cf5b3d8a4b4d8ef35bed9bf85415a1ed07361b1c4451fd77716adfdfbb9a122ac0284eee6b2b00e2d9b24f5dddf825167eb7fe2f5e97c7
|
@@ -163,6 +163,15 @@ module Spree
|
|
163
163
|
Spree::Core::Engine.frontend_available?
|
164
164
|
end
|
165
165
|
|
166
|
+
# returns the URL of an object on the storefront
|
167
|
+
# @param resource [Spree::Product, Spree::Post, Spree::Taxon, Spree::Page] the resource to get the URL for
|
168
|
+
# @param options [Hash] the options for the URL
|
169
|
+
# @option options [String] :locale the locale of the resource, defaults to I18n.locale
|
170
|
+
# @option options [String] :store the store of the resource, defaults to current_store
|
171
|
+
# @option options [String] :relative whether to use the relative URL, defaults to false
|
172
|
+
# @option options [String] :preview_id the preview ID of the resource, usually the ID of the resource
|
173
|
+
# @option options [String] :variant_id the variant ID of the resource, usually the ID of the variant (only used for products)
|
174
|
+
# @return [String] the URL of the resource
|
166
175
|
def spree_storefront_resource_url(resource, options = {})
|
167
176
|
options.merge!(locale: locale_param) if defined?(locale_param) && locale_param.present?
|
168
177
|
|
@@ -1,6 +1,77 @@
|
|
1
1
|
module Spree
|
2
2
|
module ImagesHelper
|
3
|
+
# render an image tag with a spree image variant
|
4
|
+
# it also automatically scales the width and height by 2x to look great on retina screens
|
5
|
+
#
|
6
|
+
# @param image [ActiveStorage::Attachment] the image to render
|
7
|
+
# @param options [Hash] options for the image tag
|
8
|
+
# @option options [Integer] :width the width of the image
|
9
|
+
# @option options [Integer] :height the height of the image
|
10
|
+
def spree_image_tag(image, options = {})
|
11
|
+
image_tag(
|
12
|
+
spree_image_url(image, { width: options[:width], height: options[:height] }),
|
13
|
+
options
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
def spree_image_url(image, options = {})
|
18
|
+
width = options[:width]
|
19
|
+
height = options[:height]
|
20
|
+
|
21
|
+
width = width * 2 if width.present?
|
22
|
+
height = height * 2 if height.present?
|
23
|
+
|
24
|
+
return unless image.attached?
|
25
|
+
return unless image.variable?
|
26
|
+
|
27
|
+
if width.present? && height.present?
|
28
|
+
main_app.cdn_image_url(
|
29
|
+
image.variant(spree_image_variant_options(resize_to_fill: [width, height]))
|
30
|
+
)
|
31
|
+
else
|
32
|
+
main_app.cdn_image_url(
|
33
|
+
image.variant(spree_image_variant_options(resize_to_limit: [width, height]))
|
34
|
+
)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def spree_asset_aspect_ratio(attachment)
|
39
|
+
return unless attachment.present?
|
40
|
+
return unless attachment.analyzed?
|
41
|
+
|
42
|
+
metadata = attachment.metadata
|
43
|
+
aspect_ratio = metadata['aspect_ratio'].presence
|
44
|
+
|
45
|
+
return aspect_ratio if aspect_ratio
|
46
|
+
|
47
|
+
width = metadata['width']&.to_f
|
48
|
+
return unless width
|
49
|
+
|
50
|
+
height = metadata['height']&.to_f
|
51
|
+
return unless height
|
52
|
+
return if height.zero?
|
53
|
+
|
54
|
+
w, h = width.to_f, height.to_f
|
55
|
+
|
56
|
+
# Always return width / height, flipping if needed
|
57
|
+
if h > w
|
58
|
+
ratio = h / w
|
59
|
+
elsif h < w
|
60
|
+
ratio = w / h
|
61
|
+
else
|
62
|
+
# h == w, square image
|
63
|
+
ratio = 1.0
|
64
|
+
end
|
65
|
+
|
66
|
+
ratio.round(3)
|
67
|
+
end
|
68
|
+
|
3
69
|
# convert images to webp with some sane optimization defaults
|
70
|
+
# it also automatically scales the width and height by 2x to look great on retina screens
|
71
|
+
#
|
72
|
+
# @param options [Hash] options for the image variant
|
73
|
+
# @option options [Integer] :width the width of the image
|
74
|
+
# @option options [Integer] :height the height of the image
|
4
75
|
def spree_image_variant_options(options = {})
|
5
76
|
{
|
6
77
|
saver: {
|
@@ -121,10 +121,7 @@ export default class extends Controller {
|
|
121
121
|
|
122
122
|
async fillAddress(placeID) {
|
123
123
|
const placeDetails =
|
124
|
-
await this.googlePlacesSuggestionsProvider.getPlaceDetails(
|
125
|
-
placeID,
|
126
|
-
this.address1Target
|
127
|
-
)
|
124
|
+
await this.googlePlacesSuggestionsProvider.getPlaceDetails(placeID)
|
128
125
|
|
129
126
|
if (placeDetails) {
|
130
127
|
this.address1Target.value = placeDetails.fullAddress
|
data/app/javascript/spree/core/helpers/address_autocomplete/google_places_suggestions_provider.js
CHANGED
@@ -6,139 +6,115 @@ export default class {
|
|
6
6
|
}
|
7
7
|
this.placesApi = await google.maps.importLibrary('places')
|
8
8
|
this.googleSessionToken = new this.placesApi.AutocompleteSessionToken()
|
9
|
-
this.autocompleteSevice = new this.placesApi.AutocompleteService()
|
10
9
|
}
|
11
10
|
|
12
11
|
async getSuggestions(input, country) {
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
return
|
17
|
-
}
|
12
|
+
if (this.placesApi === undefined) {
|
13
|
+
throw new Error('You must call connect() before getSuggestions()')
|
14
|
+
}
|
18
15
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
resolve(predictions.map(this.parsePrediction.bind(this)))
|
36
|
-
}
|
37
|
-
}
|
38
|
-
)
|
39
|
-
})
|
16
|
+
const request = {
|
17
|
+
input,
|
18
|
+
// We can use `address`, `street_address`
|
19
|
+
// `street_address` is the most accurate and specific, but sometimes it returns no results, even if the address is valid
|
20
|
+
// Since we are validating the address on the server, we can use `address`
|
21
|
+
includedRegionCodes: [country],
|
22
|
+
sessionToken: this.googleSessionToken
|
23
|
+
}
|
24
|
+
|
25
|
+
try {
|
26
|
+
const response = await this.placesApi.AutocompleteSuggestion.fetchAutocompleteSuggestions(request)
|
27
|
+
return response.suggestions.map((element, index) => this.parsePrediction(element.placePrediction, index))
|
28
|
+
} catch (error) {
|
29
|
+
console.error('Error fetching autocomplete suggestions', error)
|
30
|
+
return []
|
31
|
+
}
|
40
32
|
}
|
41
33
|
|
42
|
-
async getPlaceDetails(placeID
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
return
|
47
|
-
}
|
34
|
+
async getPlaceDetails(placeID) {
|
35
|
+
if (this.placesApi === undefined) {
|
36
|
+
throw new Error('You must call connect() before getPlaceDetails()')
|
37
|
+
}
|
48
38
|
|
49
|
-
|
50
|
-
service.
|
51
|
-
{
|
52
|
-
placeId: placeID,
|
53
|
-
fields: ['address_components'],
|
54
|
-
sessionToken: this.googleSessionToken
|
55
|
-
},
|
56
|
-
(place, status) => {
|
57
|
-
if (status !== this.placesApi.PlacesServiceStatus.OK) {
|
58
|
-
resolve(null)
|
59
|
-
return
|
60
|
-
}
|
39
|
+
try {
|
40
|
+
const service = new this.placesApi.Place({ id: placeID })
|
61
41
|
|
62
|
-
|
42
|
+
const { place } = await service.fetchFields({ fields: ["addressComponents"] })
|
63
43
|
|
64
|
-
|
65
|
-
|
44
|
+
const fullAddress = []
|
45
|
+
let hasStreetNumber = false
|
66
46
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
47
|
+
const details = {
|
48
|
+
fullAddress: '',
|
49
|
+
city: '',
|
50
|
+
stateAbbr: '',
|
51
|
+
zipcode: '',
|
52
|
+
hasStreetNumber: false
|
53
|
+
}
|
74
54
|
|
75
|
-
|
76
|
-
let postal_town = undefined
|
77
|
-
let sublocality = undefined
|
78
|
-
let administrative_area_level_3 = undefined
|
79
|
-
let premise = undefined
|
80
|
-
let subpremise = undefined
|
55
|
+
let locality, postal_town, sublocality, administrative_area_level_3, premise, subpremise
|
81
56
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
}
|
87
|
-
if (component.types.includes('subpremise')) {
|
88
|
-
subpremise = component.long_name
|
89
|
-
}
|
90
|
-
if (component.types.includes('premise')) {
|
91
|
-
premise = component.long_name
|
92
|
-
}
|
93
|
-
if (component.types.includes('route')) {
|
94
|
-
fullAddress.push(component.long_name)
|
95
|
-
}
|
96
|
-
if (component.types.includes('locality')) {
|
97
|
-
locality = component.long_name
|
98
|
-
}
|
99
|
-
if (component.types.includes('postal_town')) {
|
100
|
-
postal_town = component.long_name
|
101
|
-
}
|
102
|
-
if (component.types.includes('sublocality')) {
|
103
|
-
sublocality = component.long_name
|
104
|
-
}
|
105
|
-
if (component.types.includes('administrative_area_level_1')) {
|
106
|
-
details.stateAbbr = component.short_name
|
107
|
-
}
|
108
|
-
if (component.types.includes('postal_code')) {
|
109
|
-
details.zipcode = component.long_name
|
110
|
-
}
|
111
|
-
if (component.types.includes('administrative_area_level_3')) {
|
112
|
-
administrative_area_level_3 = component.long_name
|
113
|
-
}
|
114
|
-
})
|
115
|
-
// City is tricky, because it can be in different fields
|
116
|
-
details.city =
|
117
|
-
locality ||
|
118
|
-
postal_town ||
|
119
|
-
sublocality ||
|
120
|
-
administrative_area_level_3 ||
|
121
|
-
''
|
122
|
-
if (premise && !hasStreetNumber) {
|
123
|
-
fullAddress.unshift(premise)
|
124
|
-
hasStreetNumber = true
|
125
|
-
}
|
126
|
-
if (subpremise) {
|
127
|
-
fullAddress.push(subpremise)
|
128
|
-
}
|
129
|
-
details.hasStreetNumber = hasStreetNumber
|
130
|
-
details.fullAddress = fullAddress.join(' ')
|
131
|
-
resolve(details)
|
57
|
+
place.addressComponents.forEach((component) => {
|
58
|
+
if (component.types.includes('street_number')) {
|
59
|
+
fullAddress.push(component.longText)
|
60
|
+
hasStreetNumber = true
|
132
61
|
}
|
133
|
-
|
134
|
-
|
62
|
+
if (component.types.includes('subpremise')) {
|
63
|
+
subpremise = component.longText
|
64
|
+
}
|
65
|
+
if (component.types.includes('premise')) {
|
66
|
+
premise = component.longText
|
67
|
+
}
|
68
|
+
if (component.types.includes('route')) {
|
69
|
+
fullAddress.push(component.longText)
|
70
|
+
}
|
71
|
+
if (component.types.includes('locality')) {
|
72
|
+
locality = component.longText
|
73
|
+
}
|
74
|
+
if (component.types.includes('postal_town')) {
|
75
|
+
postal_town = component.longText
|
76
|
+
}
|
77
|
+
if (component.types.includes('sublocality')) {
|
78
|
+
sublocality = component.longText
|
79
|
+
}
|
80
|
+
if (component.types.includes('administrative_area_level_1')) {
|
81
|
+
details.stateAbbr = component.shortText
|
82
|
+
}
|
83
|
+
if (component.types.includes('postal_code')) {
|
84
|
+
details.zipcode = component.longText
|
85
|
+
}
|
86
|
+
if (component.types.includes('administrative_area_level_3')) {
|
87
|
+
administrative_area_level_3 = component.longText
|
88
|
+
}
|
89
|
+
})
|
90
|
+
// City is tricky, because it can be in different fields
|
91
|
+
details.city =
|
92
|
+
locality ||
|
93
|
+
postal_town ||
|
94
|
+
sublocality ||
|
95
|
+
administrative_area_level_3 ||
|
96
|
+
''
|
97
|
+
if (premise && !hasStreetNumber) {
|
98
|
+
fullAddress.unshift(premise)
|
99
|
+
hasStreetNumber = true
|
100
|
+
}
|
101
|
+
if (subpremise) {
|
102
|
+
fullAddress.push(subpremise)
|
103
|
+
}
|
104
|
+
details.hasStreetNumber = hasStreetNumber
|
105
|
+
details.fullAddress = fullAddress.join(' ')
|
106
|
+
return details
|
107
|
+
} catch (error) {
|
108
|
+
console.error('Error fetching autocomplete details', error)
|
109
|
+
return null
|
110
|
+
}
|
135
111
|
}
|
136
112
|
|
137
113
|
parsePrediction(prediction, index) {
|
138
114
|
const suggestion = {
|
139
115
|
index: index,
|
140
|
-
placeID: prediction.
|
141
|
-
description: prediction.
|
116
|
+
placeID: prediction.placeId,
|
117
|
+
description: prediction.text.text
|
142
118
|
}
|
143
119
|
|
144
120
|
const splittedText = this.splitTextForMarks(prediction)
|
@@ -155,12 +131,18 @@ export default class {
|
|
155
131
|
|
156
132
|
// This function takes the prediction and splits the description into an array of objects with the text and a boolean indicating if it should be highlighted
|
157
133
|
splitTextForMarks(prediction) {
|
158
|
-
|
134
|
+
const description = prediction.text.text;
|
135
|
+
let markedAreas = prediction.text.matches
|
136
|
+
.map(({ startOffset, endOffset }) => ({
|
137
|
+
offset: startOffset,
|
138
|
+
length: endOffset - startOffset
|
139
|
+
}))
|
140
|
+
.sort((a, b) => a.offset - b.offset);
|
159
141
|
const textAreas = []
|
160
|
-
for (let i = 0; i <
|
142
|
+
for (let i = 0; i < description.length; i++) {
|
161
143
|
if (markedAreas[0]?.offset === i) {
|
162
144
|
textAreas.push({
|
163
|
-
text:
|
145
|
+
text: description.substring(i, i + markedAreas[0].length),
|
164
146
|
marked: true
|
165
147
|
})
|
166
148
|
i += markedAreas[0].length - 1
|
@@ -168,7 +150,7 @@ export default class {
|
|
168
150
|
markedAreas = [...markedAreas.sort((m) => m.offset)]
|
169
151
|
} else {
|
170
152
|
textAreas.push({
|
171
|
-
text:
|
153
|
+
text: description.charAt(i),
|
172
154
|
marked: false
|
173
155
|
})
|
174
156
|
}
|
@@ -12,7 +12,7 @@ module Spree
|
|
12
12
|
acts_as_paranoid
|
13
13
|
|
14
14
|
belongs_to :payment_method
|
15
|
-
belongs_to :user, class_name:
|
15
|
+
belongs_to :user, class_name: Spree.user_class.to_s, foreign_key: 'user_id', optional: true
|
16
16
|
belongs_to :gateway_customer, class_name: 'Spree::GatewayCustomer', optional: true
|
17
17
|
|
18
18
|
has_many :payments, as: :source
|
data/app/models/spree/order.rb
CHANGED
@@ -504,6 +504,7 @@ module Spree
|
|
504
504
|
end
|
505
505
|
|
506
506
|
def available_payment_methods(store = nil)
|
507
|
+
Spree::Deprecation.warn('`Order#available_payment_methods` is deprecated and will be removed in Spree 6. Use `collect_frontend_payment_methods` instead.')
|
507
508
|
if store.present?
|
508
509
|
Spree::Deprecation.warn('The `store` parameter is deprecated and will be removed in Spree 5. Order is already associated with Store')
|
509
510
|
end
|
@@ -754,7 +755,11 @@ module Spree
|
|
754
755
|
end
|
755
756
|
|
756
757
|
def collect_backend_payment_methods
|
757
|
-
|
758
|
+
store.payment_methods.active.available_on_back_end.select { |pm| pm.available_for_order?(self) }
|
759
|
+
end
|
760
|
+
|
761
|
+
def collect_frontend_payment_methods
|
762
|
+
store.payment_methods.active.available_on_front_end.select { |pm| pm.available_for_order?(self) }
|
758
763
|
end
|
759
764
|
|
760
765
|
# determines whether the inventory is fully discounted
|
@@ -771,19 +776,6 @@ module Spree
|
|
771
776
|
Spree::CouponCode.find_by(order: self, promotion: promotions).try(:code) || promotions.pluck(:code).compact.first
|
772
777
|
end
|
773
778
|
|
774
|
-
def validate_payments_attributes(attributes)
|
775
|
-
Spree::Deprecation.warn('`Order#validate_payments_attributes` is deprecated and will be removed in Spree 5')
|
776
|
-
|
777
|
-
# Ensure the payment methods specified are allowed for this user
|
778
|
-
payment_method_ids = available_payment_methods.map(&:id).map(&:to_s)
|
779
|
-
|
780
|
-
attributes.each do |payment_attributes|
|
781
|
-
payment_method_id = payment_attributes[:payment_method_id].to_s
|
782
|
-
|
783
|
-
raise ActiveRecord::RecordNotFound unless payment_method_ids.include?(payment_method_id)
|
784
|
-
end
|
785
|
-
end
|
786
|
-
|
787
779
|
def valid_promotions
|
788
780
|
order_promotions.where(promotion_id: valid_promotion_ids).uniq(&:promotion_id)
|
789
781
|
end
|
@@ -887,6 +879,7 @@ module Spree
|
|
887
879
|
end
|
888
880
|
|
889
881
|
def collect_payment_methods(store = nil)
|
882
|
+
Spree::Deprecation.warn('`Order#collect_payment_methods` is deprecated and will be removed in Spree 6. Use `collect_frontend_payment_methods` instead.')
|
890
883
|
if store.present?
|
891
884
|
Spree::Deprecation.warn('The `store` parameter is deprecated and will be removed in Spree 5. Order is already associated with Store')
|
892
885
|
end
|
data/app/models/spree/theme.rb
CHANGED
@@ -98,7 +98,7 @@ module Spree
|
|
98
98
|
end
|
99
99
|
|
100
100
|
def create_default_pages
|
101
|
-
Rails.application.config.spree.pages.each do |page_class|
|
101
|
+
Rails.application.config.spree.pages.map(&:to_s).map(&:constantize).each do |page_class|
|
102
102
|
next if page_class == Spree::Pages::Custom
|
103
103
|
|
104
104
|
page_class.where(pageable: self).first_or_create!
|
@@ -107,7 +107,7 @@ module Spree
|
|
107
107
|
|
108
108
|
def create_layout_sections
|
109
109
|
ApplicationRecord.transaction do
|
110
|
-
available_layout_sections.each do |section_class|
|
110
|
+
available_layout_sections.map(&:to_s).map(&:constantize).each do |section_class|
|
111
111
|
section_class.where(pageable: self).first_or_create!
|
112
112
|
end
|
113
113
|
end
|
@@ -9,8 +9,16 @@ module Spree
|
|
9
9
|
bill_changed = address_with_country_iso_present?(params, 'bill')
|
10
10
|
params[:order][:ship_address_attributes] = replace_country_iso_with_id(params[:order][:ship_address_attributes]) if ship_changed
|
11
11
|
params[:order][:bill_address_attributes] = replace_country_iso_with_id(params[:order][:bill_address_attributes]) if bill_changed
|
12
|
-
|
13
|
-
|
12
|
+
|
13
|
+
# for quick checkouts we cannot revert to previous states
|
14
|
+
# we already have the address and delivery steps completed
|
15
|
+
# however we need to update the shipping address with missing data
|
16
|
+
# as previously we didn't have access to first/last name and street
|
17
|
+
unless params[:do_not_change_state]
|
18
|
+
order.state = 'address' if (ship_changed || bill_changed || quick_checkout_cancelled?(params)) && order.has_checkout_step?('address')
|
19
|
+
order.state = 'delivery' if selected_shipping_rate_present?(params) && order.has_checkout_step?('delivery')
|
20
|
+
end
|
21
|
+
|
14
22
|
return success(order) if order.update_from_params(params, permitted_attributes, request_env)
|
15
23
|
|
16
24
|
failure(order)
|
@@ -31,6 +39,10 @@ module Spree
|
|
31
39
|
|
32
40
|
shipments_attributes.any? { |s| s.dig(:selected_shipping_rate_id) }
|
33
41
|
end
|
42
|
+
|
43
|
+
def quick_checkout_cancelled?(params)
|
44
|
+
params.dig(:order, :ship_address_id) == 'CLEAR'
|
45
|
+
end
|
34
46
|
end
|
35
47
|
end
|
36
48
|
end
|
@@ -14,7 +14,7 @@ module Spree
|
|
14
14
|
protected
|
15
15
|
|
16
16
|
def prepare_payment_attributes(order:, params:)
|
17
|
-
payment_method = order.
|
17
|
+
payment_method = order.collect_frontend_payment_methods.find { |pm| pm.id.to_s == params[:payment_method_id]&.to_s }
|
18
18
|
|
19
19
|
payment_attributes = {
|
20
20
|
amount: params[:amount] || order.order_total_after_store_credit,
|
@@ -130,8 +130,10 @@ module Spree
|
|
130
130
|
option_value_variant_params = {}
|
131
131
|
|
132
132
|
option_value_params.each_with_index do |opt, index|
|
133
|
-
option_type = Spree::OptionType.
|
133
|
+
option_type = Spree::OptionType.find_by(id: opt[:id]) if opt.fetch(:id)
|
134
|
+
option_type ||= Spree::OptionType.where(name: opt[:name].parameterize).first_or_initialize do |o|
|
134
135
|
o.name = o.presentation = opt[:name]
|
136
|
+
o.position = opt[:position]
|
135
137
|
o.save!
|
136
138
|
end
|
137
139
|
option_value = option_type.option_values.where(name: opt[:value].parameterize).first_or_initialize do |o|
|
@@ -1,12 +1,6 @@
|
|
1
1
|
<figure class="attachment attachment--<%= blob.representable? ? "preview" : "file" %> attachment--<%= blob.filename.extension %>">
|
2
2
|
<% if blob.representable? %>
|
3
|
-
<%=
|
4
|
-
blob.variant(
|
5
|
-
spree_image_variant_options(
|
6
|
-
resize_to_limit: local_assigns[:in_gallery] ? [ 800, 600 ] : [ 1024, 768 ]
|
7
|
-
)
|
8
|
-
)
|
9
|
-
) %>
|
3
|
+
<%= spree_image_tag(blob, width: local_assigns[:in_gallery] ? 400 : 512, height: local_assigns[:in_gallery] ? 300 : 400) %>
|
10
4
|
<% end %>
|
11
5
|
|
12
6
|
<figcaption class="attachment__caption">
|
@@ -5,7 +5,7 @@
|
|
5
5
|
<%= link_to current_store.url_or_custom_domain, 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
|
-
<% aspect_ratio =
|
8
|
+
<% aspect_ratio = spree_asset_aspect_ratio(logo) %>
|
9
9
|
<% logo_style = "aspect-ratio: #{aspect_ratio};" %>
|
10
10
|
|
11
11
|
<% if aspect_ratio.to_f > 10 %>
|
data/config/locales/en.yml
CHANGED
@@ -310,6 +310,9 @@ en:
|
|
310
310
|
spree/address:
|
311
311
|
one: Address
|
312
312
|
other: Addresses
|
313
|
+
spree/classification:
|
314
|
+
one: Classification
|
315
|
+
other: Classifications
|
313
316
|
spree/cms_page:
|
314
317
|
one: Page
|
315
318
|
other: Pages
|
@@ -328,6 +331,9 @@ en:
|
|
328
331
|
spree/digital:
|
329
332
|
one: Digital asset
|
330
333
|
other: Digital assets
|
334
|
+
spree/export:
|
335
|
+
one: Export
|
336
|
+
other: Exports
|
331
337
|
spree/inventory_unit:
|
332
338
|
one: Inventory Unit
|
333
339
|
other: Inventory Units
|
@@ -349,6 +355,9 @@ en:
|
|
349
355
|
spree/payment_method:
|
350
356
|
one: Payment Method
|
351
357
|
other: Payment Methods
|
358
|
+
spree/price:
|
359
|
+
one: Price
|
360
|
+
other: Prices
|
352
361
|
spree/product:
|
353
362
|
one: Product
|
354
363
|
other: Products
|
@@ -397,6 +406,9 @@ en:
|
|
397
406
|
spree/state_change:
|
398
407
|
one: State Change
|
399
408
|
other: State Changes
|
409
|
+
spree/stock_item:
|
410
|
+
one: Stock Item
|
411
|
+
other: Stock Items
|
400
412
|
spree/stock_location:
|
401
413
|
one: Stock Location
|
402
414
|
other: Stock Locations
|
@@ -406,6 +418,9 @@ en:
|
|
406
418
|
spree/stock_transfer:
|
407
419
|
one: Stock Transfer
|
408
420
|
other: Stock Transfers
|
421
|
+
spree/store:
|
422
|
+
one: Store
|
423
|
+
other: Stores
|
409
424
|
spree/store_credit:
|
410
425
|
one: Store Credit
|
411
426
|
other: Store Credits
|
@@ -424,6 +439,9 @@ en:
|
|
424
439
|
spree/taxonomy:
|
425
440
|
one: Taxonomy
|
426
441
|
other: Taxonomies
|
442
|
+
spree/theme:
|
443
|
+
one: Theme
|
444
|
+
other: Themes
|
427
445
|
spree/tracker:
|
428
446
|
one: Tracker
|
429
447
|
other: Trackers
|
@@ -1741,7 +1759,6 @@ en:
|
|
1741
1759
|
sales_totals: Sales Totals
|
1742
1760
|
same_as_shipping_address: Same as shipping address
|
1743
1761
|
save_and_continue: Save and Continue
|
1744
|
-
save_changes: Save changes
|
1745
1762
|
save_my_address: Save my address
|
1746
1763
|
saving: Saving
|
1747
1764
|
say_no: 'No'
|
@@ -7,8 +7,10 @@ class AddMultiCodeToSpreePromotions < ActiveRecord::Migration[6.1]
|
|
7
7
|
|
8
8
|
add_index :spree_promotions, :kind, if_not_exists: true
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
unless Rails.env.test?
|
11
|
+
Spree::Promotion.reset_column_information
|
12
|
+
# set all promotions without a code to automatic
|
13
|
+
Spree::Promotion.where(code: [nil, '']).update_all(kind: 1)
|
14
|
+
end
|
13
15
|
end
|
14
16
|
end
|
data/lib/spree/core/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: spree_core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.0.
|
4
|
+
version: 5.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sean Schofield
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2025-04-
|
13
|
+
date: 2025-04-09 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: i18n-tasks
|
@@ -1308,9 +1308,9 @@ licenses:
|
|
1308
1308
|
- BSD-3-Clause
|
1309
1309
|
metadata:
|
1310
1310
|
bug_tracker_uri: https://github.com/spree/spree/issues
|
1311
|
-
changelog_uri: https://github.com/spree/spree/releases/tag/v5.0.
|
1311
|
+
changelog_uri: https://github.com/spree/spree/releases/tag/v5.0.1
|
1312
1312
|
documentation_uri: https://docs.spreecommerce.org/
|
1313
|
-
source_code_uri: https://github.com/spree/spree/tree/v5.0.
|
1313
|
+
source_code_uri: https://github.com/spree/spree/tree/v5.0.1
|
1314
1314
|
post_install_message:
|
1315
1315
|
rdoc_options: []
|
1316
1316
|
require_paths:
|