spree_core 5.4.0.rc4 → 5.4.0.rc4.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/models/concerns/spree/metadata.rb +10 -0
- data/app/models/concerns/spree/search_indexable.rb +0 -2
- data/app/models/spree/data_feed/google.rb +19 -0
- data/app/models/spree/data_feed.rb +47 -0
- data/app/models/spree/metafield.rb +1 -1
- data/app/models/spree/metafield_definition.rb +1 -1
- data/app/models/spree/order/checkout.rb +0 -5
- data/app/models/spree/store.rb +2 -0
- data/app/models/spree/variant.rb +3 -1
- data/app/presenters/spree/data_feeds/base_presenter.rb +23 -0
- data/app/presenters/spree/data_feeds/google_presenter.rb +89 -0
- data/app/services/spree/sample_data/loader.rb +7 -0
- data/app/services/spree/taxons/add_products.rb +3 -1
- data/app/services/spree/taxons/regenerate_products.rb +3 -1
- data/app/services/spree/taxons/remove_products.rb +3 -1
- data/config/locales/en.yml +10 -10
- data/db/migrate/20221229132350_create_spree_data_feed_settings.rb +14 -0
- data/db/migrate/20230415155958_rename_data_feed_settings_table.rb +5 -0
- data/db/migrate/20230415160828_rename_data_feed_table_columns.rb +7 -0
- data/db/migrate/20230415161226_add_indexes_to_data_feeds_table.rb +5 -0
- data/db/migrate/20230512094803_rename_data_feeds_column_provider_to_type.rb +5 -0
- data/db/sample_data/products.csv +1083 -1083
- data/lib/spree/core/engine.rb +5 -0
- data/lib/spree/core/version.rb +1 -1
- data/lib/spree/core.rb +8 -0
- data/lib/spree/testing_support/factories/google_data_feed_factory.rb +7 -0
- data/lib/spree/testing_support/factories/product_factory.rb +1 -0
- data/spec/fixtures/files/products_import.csv +2 -2
- metadata +14 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0445fa1595f6f1abf4bc08b4f961ce9be32e84085e27cf64d0f85ffda00eec7f
|
|
4
|
+
data.tar.gz: 98519e65a1214f7b9a27e95c1cb29ecfcd5faea8b07f453bee23a96be92e2f29
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cf023f4b0a51304733d7a502bc59df945e3b09c67124f7c8b0578deb8d5f29a1ec3b92bd6cbdbcb3681b818189eb8615342fb25d1c6051e9c85ea5c667af0c53
|
|
7
|
+
data.tar.gz: 8c3a926ca0ac60e7ca2f4e99eae97d2686175095ec414066e04d9d5631d92ca94bf28fb4e6678499cdf7b28994e9d05744eae34c737fd369cfff7fce43ebbbdf
|
|
@@ -22,6 +22,16 @@ module Spree
|
|
|
22
22
|
self.private_metadata = value
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
+
def public_metadata=(value)
|
|
26
|
+
unless value.blank? || value == {}
|
|
27
|
+
Spree::Deprecation.warn(
|
|
28
|
+
'public_metadata is deprecated and will be removed in Spree 6.0. ' \
|
|
29
|
+
'Use metadata instead. For customer-visible structured data, use metafields with display_on: \'both\'.'
|
|
30
|
+
)
|
|
31
|
+
end
|
|
32
|
+
super
|
|
33
|
+
end
|
|
34
|
+
|
|
25
35
|
# https://nandovieira.com/using-postgresql-and-jsonb-with-ruby-on-rails
|
|
26
36
|
class HashSerializer
|
|
27
37
|
def self.dump(hash)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require_dependency 'spree/data_feed'
|
|
2
|
+
|
|
3
|
+
module Spree
|
|
4
|
+
class DataFeed::Google < DataFeed
|
|
5
|
+
class << self
|
|
6
|
+
def label
|
|
7
|
+
'Google Merchant Center Feed'
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def provider_name
|
|
11
|
+
'google'
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def presenter_class
|
|
15
|
+
Spree::DataFeeds::GooglePresenter
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
class DataFeed < Spree.base_class
|
|
3
|
+
has_prefix_id :df
|
|
4
|
+
|
|
5
|
+
belongs_to :store, class_name: 'Spree::Store', foreign_key: 'store_id'
|
|
6
|
+
|
|
7
|
+
scope :for_store, ->(store) { where(store: store) }
|
|
8
|
+
scope :active, -> { where(active: true) }
|
|
9
|
+
|
|
10
|
+
before_validation :generate_slug
|
|
11
|
+
|
|
12
|
+
with_options presence: true do
|
|
13
|
+
validates :store
|
|
14
|
+
validates :name, uniqueness: true
|
|
15
|
+
validates :slug, uniqueness: { scope: :store_id }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def formatted_url
|
|
19
|
+
"#{store.formatted_url}/api/v3/store/feeds/#{slug}.xml"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def generate_slug
|
|
25
|
+
new_slug = slug.blank? ? SecureRandom.uuid : slug.parameterize
|
|
26
|
+
write_attribute(:slug, new_slug)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
class << self
|
|
30
|
+
def label
|
|
31
|
+
raise NotImplementedError
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def provider_name
|
|
35
|
+
raise NotImplementedError
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def presenter_class
|
|
39
|
+
raise NotImplementedError
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def available_types
|
|
43
|
+
Spree.data_feed_types
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -122,7 +122,6 @@ module Spree
|
|
|
122
122
|
after_transition to: :complete, do: :use_all_coupon_codes
|
|
123
123
|
after_transition to: :complete, do: :redeem_gift_card
|
|
124
124
|
after_transition to: :complete, do: :subscribe_to_newsletter
|
|
125
|
-
after_transition to: :complete, do: :publish_order_completed_event
|
|
126
125
|
|
|
127
126
|
after_transition from: any - :cart, to: any - [:confirm, :complete] do |order|
|
|
128
127
|
order.update_totals
|
|
@@ -133,10 +132,6 @@ module Spree
|
|
|
133
132
|
alias_method :save_state, :save
|
|
134
133
|
end
|
|
135
134
|
|
|
136
|
-
def publish_order_completed_event
|
|
137
|
-
publish_event('order.completed')
|
|
138
|
-
end
|
|
139
|
-
|
|
140
135
|
def subscribe_to_newsletter
|
|
141
136
|
return unless accept_marketing?
|
|
142
137
|
|
data/app/models/spree/store.rb
CHANGED
data/app/models/spree/variant.rb
CHANGED
|
@@ -292,9 +292,11 @@ module Spree
|
|
|
292
292
|
end
|
|
293
293
|
|
|
294
294
|
# Returns first Image for Variant.
|
|
295
|
+
# @deprecated Use #primary_media instead.
|
|
295
296
|
# @return [Spree::Image, nil]
|
|
296
297
|
def primary_image
|
|
297
|
-
|
|
298
|
+
Spree::Deprecation.warn('Spree::Variant#primary_image is deprecated and will be removed in Spree 6.0. Please use Spree::Variant#primary_media instead.')
|
|
299
|
+
primary_media
|
|
298
300
|
end
|
|
299
301
|
|
|
300
302
|
# Returns second Image for Variant (for hover effects).
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module DataFeeds
|
|
3
|
+
class BasePresenter
|
|
4
|
+
def initialize(data_feed)
|
|
5
|
+
@data_feed = data_feed
|
|
6
|
+
@store = data_feed.store
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
attr_reader :data_feed, :store
|
|
10
|
+
|
|
11
|
+
# @return [String] the feed content (XML, CSV, etc.)
|
|
12
|
+
def call
|
|
13
|
+
raise NotImplementedError
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def products
|
|
19
|
+
store.products.active
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
require 'nokogiri'
|
|
2
|
+
|
|
3
|
+
module Spree
|
|
4
|
+
module DataFeeds
|
|
5
|
+
class GooglePresenter < BasePresenter
|
|
6
|
+
# @return [String] RSS XML feed for Google Merchant Center
|
|
7
|
+
def call
|
|
8
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
|
9
|
+
xml.rss('xmlns:g' => 'http://base.google.com/ns/1.0', 'version' => '2.0') do
|
|
10
|
+
xml.channel do
|
|
11
|
+
build_store_info(xml)
|
|
12
|
+
build_items(xml)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
builder.to_xml
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def build_store_info(xml)
|
|
23
|
+
xml.title store.name
|
|
24
|
+
xml.link store.storefront_url
|
|
25
|
+
xml.description store.meta_description
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def build_items(xml)
|
|
29
|
+
products.includes(:primary_media, public_metafields: :metafield_definition, variants_including_master: [:primary_media, option_values: :option_type]).find_each do |product|
|
|
30
|
+
product.variants_including_master.active.each do |variant|
|
|
31
|
+
next if variant.is_master? && product.has_variants?
|
|
32
|
+
|
|
33
|
+
build_item(xml, product, variant)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def build_item(xml, product, variant)
|
|
39
|
+
image_url = image_link(variant, product)
|
|
40
|
+
return if image_url.nil?
|
|
41
|
+
|
|
42
|
+
xml.item do
|
|
43
|
+
build_required_attributes(xml, product, variant, image_url)
|
|
44
|
+
build_optional_attributes(xml, product)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def build_required_attributes(xml, product, variant, image_url)
|
|
49
|
+
xml['g'].id variant.id
|
|
50
|
+
xml['g'].item_group_id product.id
|
|
51
|
+
xml['g'].title format_title(product, variant)
|
|
52
|
+
xml['g'].description product.description || format_title(product, variant)
|
|
53
|
+
xml['g'].link "#{store.storefront_url}/products/#{product.slug}"
|
|
54
|
+
xml['g'].image_link image_url
|
|
55
|
+
xml['g'].price "#{variant.price} #{variant.cost_currency}"
|
|
56
|
+
xml['g'].availability availability(product)
|
|
57
|
+
xml['g'].availability_date product.available_on.xmlschema if product.available_on.present?
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def build_optional_attributes(xml, product)
|
|
61
|
+
product.public_metafields.each do |metafield|
|
|
62
|
+
xml['g'].send(metafield.metafield_definition.key.parameterize.underscore, metafield.value)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def format_title(product, variant)
|
|
67
|
+
parts = [product.name]
|
|
68
|
+
variant.option_values.each do |option_value|
|
|
69
|
+
parts << option_value.name
|
|
70
|
+
end
|
|
71
|
+
parts.join(' - ')
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def image_link(variant, product)
|
|
75
|
+
image = variant.primary_media || product.primary_media
|
|
76
|
+
return if image.nil?
|
|
77
|
+
|
|
78
|
+
Rails.application.routes.url_helpers.cdn_image_url(image.attachment.variant(:xlarge))
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def availability(product)
|
|
82
|
+
return 'in stock' if product.available? && (product.available_on.nil? || product.available_on.past?)
|
|
83
|
+
return 'backorder' if product.backorderable? && product.backordered? && product.available_on&.future?
|
|
84
|
+
|
|
85
|
+
'out of stock'
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -26,6 +26,8 @@ module Spree
|
|
|
26
26
|
puts 'Loading sample posts...'
|
|
27
27
|
load_ruby_file('posts')
|
|
28
28
|
|
|
29
|
+
clear_jobs_queue
|
|
30
|
+
|
|
29
31
|
puts 'Sample data loaded successfully!'
|
|
30
32
|
end
|
|
31
33
|
end
|
|
@@ -73,6 +75,11 @@ module Spree
|
|
|
73
75
|
ensure
|
|
74
76
|
Spree::Config[:geocode_addresses] = previous
|
|
75
77
|
end
|
|
78
|
+
|
|
79
|
+
def clear_jobs_queue
|
|
80
|
+
adapter = ActiveJob::Base.queue_adapter
|
|
81
|
+
adapter.enqueued_jobs.clear if adapter.respond_to?(:enqueued_jobs)
|
|
82
|
+
end
|
|
76
83
|
end
|
|
77
84
|
end
|
|
78
85
|
end
|
|
@@ -34,8 +34,10 @@ module Spree
|
|
|
34
34
|
taxon_ids.each { |id| Spree::Taxon.reset_counters(id, :classifications) }
|
|
35
35
|
product_ids.each { |id| Spree::Product.reset_counters(id, :classifications) }
|
|
36
36
|
|
|
37
|
-
#
|
|
37
|
+
# clear cache & index products
|
|
38
38
|
Spree::Product.where(id: product_ids).touch_all
|
|
39
|
+
products.each(&:enqueue_search_index)
|
|
40
|
+
|
|
39
41
|
Spree::Taxon.where(id: taxon_ids).touch_all
|
|
40
42
|
Spree::Taxons::TouchFeaturedSections.call(taxon_ids: taxon_ids) if defined?(Spree::Taxons::TouchFeaturedSections)
|
|
41
43
|
|
|
@@ -32,7 +32,9 @@ module Spree
|
|
|
32
32
|
Spree::Classification.insert_all(records_to_insert)
|
|
33
33
|
|
|
34
34
|
# expire product cache
|
|
35
|
-
Spree::Product.where(id: (previous_products_ids + product_ids_to_insert).uniq)
|
|
35
|
+
products = Spree::Product.where(id: (previous_products_ids + product_ids_to_insert).uniq)
|
|
36
|
+
products.touch_all
|
|
37
|
+
products.each(&:enqueue_search_index)
|
|
36
38
|
end
|
|
37
39
|
|
|
38
40
|
# update counter caches
|
|
@@ -49,8 +49,10 @@ module Spree
|
|
|
49
49
|
taxon_ids.each { |id| Spree::Taxon.reset_counters(id, :classifications) }
|
|
50
50
|
product_ids.each { |id| Spree::Product.reset_counters(id, :classifications) }
|
|
51
51
|
|
|
52
|
-
# clear cache
|
|
52
|
+
# clear cache & index products
|
|
53
53
|
Spree::Product.where(id: product_ids).touch_all
|
|
54
|
+
products.each(&:enqueue_search_index)
|
|
55
|
+
|
|
54
56
|
Spree::Taxon.where(id: taxon_ids).touch_all
|
|
55
57
|
Spree::Taxons::TouchFeaturedSections.call(taxon_ids: taxon_ids) if defined?(Spree::Taxons::TouchFeaturedSections)
|
|
56
58
|
|
data/config/locales/en.yml
CHANGED
|
@@ -532,11 +532,11 @@ en:
|
|
|
532
532
|
one: Tax Rate
|
|
533
533
|
other: Tax Rates
|
|
534
534
|
spree/taxon:
|
|
535
|
-
one:
|
|
536
|
-
other:
|
|
535
|
+
one: Category
|
|
536
|
+
other: Categories
|
|
537
537
|
spree/taxonomy:
|
|
538
|
-
one:
|
|
539
|
-
other:
|
|
538
|
+
one: Category
|
|
539
|
+
other: Categories
|
|
540
540
|
spree/theme:
|
|
541
541
|
one: Theme
|
|
542
542
|
other: Themes
|
|
@@ -1451,7 +1451,7 @@ en:
|
|
|
1451
1451
|
new_image: New Image
|
|
1452
1452
|
new_line_item: Add line item
|
|
1453
1453
|
new_metafield_definition: New Metafield Definition
|
|
1454
|
-
new_nested_taxon: New nested
|
|
1454
|
+
new_nested_taxon: New nested subcategory
|
|
1455
1455
|
new_option_type: New Option Type
|
|
1456
1456
|
new_order: New Order
|
|
1457
1457
|
new_order_completed: New Order Completed
|
|
@@ -1486,8 +1486,8 @@ en:
|
|
|
1486
1486
|
new_store_credit_category: New Store Credit Category
|
|
1487
1487
|
new_tax_category: New Tax Category
|
|
1488
1488
|
new_tax_rate: New Tax Rate
|
|
1489
|
-
new_taxon: New
|
|
1490
|
-
new_taxonomy: New
|
|
1489
|
+
new_taxon: New Subcategory
|
|
1490
|
+
new_taxonomy: New Category
|
|
1491
1491
|
new_tracker: New Tracker
|
|
1492
1492
|
new_user: New User
|
|
1493
1493
|
new_variant: New Variant
|
|
@@ -2332,15 +2332,15 @@ en:
|
|
|
2332
2332
|
label: Order must contain x amount of these taxons
|
|
2333
2333
|
match_all: all
|
|
2334
2334
|
match_any: at least one
|
|
2335
|
-
taxonomies:
|
|
2336
|
-
taxonomy:
|
|
2335
|
+
taxonomies: Categories
|
|
2336
|
+
taxonomy: Category
|
|
2337
2337
|
taxonomy_brands_name: Brands
|
|
2338
2338
|
taxonomy_categories_name: Categories
|
|
2339
2339
|
taxonomy_collections_name: Collections
|
|
2340
2340
|
taxonomy_edit: Edit taxonomy
|
|
2341
2341
|
taxonomy_tree_error: The requested change has not been accepted and the tree has been returned to its previous state, please try again.
|
|
2342
2342
|
taxonomy_tree_instruction: "* Right click (or double tap on iOS device) a child in the tree to access the menu for adding, deleting or sorting a child."
|
|
2343
|
-
taxons:
|
|
2343
|
+
taxons: Categories
|
|
2344
2344
|
terms_of_service: Terms of Service
|
|
2345
2345
|
test: Test
|
|
2346
2346
|
test_mode: Test Mode
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
class CreateSpreeDataFeedSettings < ActiveRecord::Migration[6.0]
|
|
2
|
+
def change
|
|
3
|
+
create_table :spree_data_feed_settings do |t|
|
|
4
|
+
t.references :spree_store
|
|
5
|
+
|
|
6
|
+
t.string :name
|
|
7
|
+
t.string :provider
|
|
8
|
+
t.string :uuid, unique: true
|
|
9
|
+
t.boolean :enabled, default: true
|
|
10
|
+
|
|
11
|
+
t.timestamps
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|