Skip to content

Commit 747e3ab

Browse files
committed
Add organization structured data fields to Spree stores for SEO
- Added new fields to spree_stores to support organization structured data for SEO. - Fields include legal_name, contact_email, contact_phone, description, vat_id, tax_id, address1, address2, city, zipcode, state_name, country_id, and state_id. - This update enhances SEO by allowing structured data markup for better search engine visibility.
1 parent 08207da commit 747e3ab

File tree

8 files changed

+264
-10
lines changed

8 files changed

+264
-10
lines changed

api/lib/spree/api_configuration.rb

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,10 @@ def promotion_attributes=(value)
147147
deprecate "promotion_attributes=" => promotion_attributes_deprecation_message, deprecator: Spree.deprecator
148148

149149
preference :store_attributes, :array, default: [
150-
:id, :name, :url, :meta_description, :meta_keywords, :seo_title,
151-
:mail_from_address, :default_currency, :code, :default, :available_locales,
152-
:bcc_email
150+
:id, :name, :legal_name, :url, :meta_description, :meta_keywords, :seo_title,
151+
:mail_from_address, :default_currency, :code, :default,
152+
:bcc_email, :contact_phone, :contact_email, :tax_id, :vat_id, :description,
153+
:address1, :address2, :city, :zipcode, :country_id, :state_id, :state_name, :available_locales
153154
]
154155

155156
preference :store_credit_history_attributes, :array, default: [

api/spec/requests/spree/api/stores_spec.rb

Lines changed: 129 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44

55
module Spree::Api
66
describe 'Stores', type: :request do
7+
let(:country) { create :country, states_required: true }
8+
let(:country_without_states) { create :country, states_required: false }
9+
let(:state) { create :state, name: 'maryland', abbr: 'md', country: }
10+
let!(:base_attributes) { Spree::Api::Config.store_attributes }
11+
712
let!(:store) do
813
create(:store, name: "My Spree Store", url: "spreestore.example.com")
914
end
@@ -22,6 +27,58 @@ module Spree::Api
2227
default: false)
2328
end
2429

30+
describe "store state validation" do
31+
context "when store country has states_required" do
32+
it "is invalid without a state" do
33+
store = Spree::Store.new(name: "Test Store", country: country, state: nil, url: "spreestore.example.com",
34+
mail_from_address: "[email protected]", code: "test-store",)
35+
expect(store).not_to be_valid
36+
expect(store.errors[:state]).to include("can't be blank")
37+
end
38+
39+
it "is valid with a state" do
40+
store = Spree::Store.new(name: "Test Store", country: country, state: state, url: "spreestore.example.com",
41+
mail_from_address: "[email protected]", code: "test-store",)
42+
expect(store).to be_valid
43+
end
44+
end
45+
46+
context "when store country has no states" do
47+
it "is valid without a state" do
48+
store = Spree::Store.new(name: "Test Store", country: country_without_states, state: nil, url: "spreestore.example.com",
49+
mail_from_address: "[email protected]", code: "test-store",)
50+
expect(store).to be_valid
51+
end
52+
end
53+
54+
it "is valid without an address and without country/state" do
55+
expect(store).to be_valid
56+
end
57+
58+
it "is valid with only correct country and state" do
59+
store = Spree::Store.create!(
60+
name: "Test Store",
61+
url: "spreestore.example.com",
62+
mail_from_address: "spreestore.example.com",
63+
code: "test-store",
64+
address1: "123 Main St",
65+
city: "New York",
66+
zipcode: "10001",
67+
state: state,
68+
country: country,
69+
)
70+
expect(store).to be_valid
71+
end
72+
end
73+
74+
describe "#index" do
75+
it "ensures the API store attributes match the expected attributes" do
76+
get spree.api_stores_path
77+
first_store = json_response["stores"].first
78+
expect(first_store.keys).to include(*base_attributes.map(&:to_s))
79+
end
80+
end
81+
2582
it "can list the available stores" do
2683
get spree.api_stores_path
2784
expect(json_response["stores"]).to match_array([
@@ -37,7 +94,20 @@ module Spree::Api
3794
"default_currency" => nil,
3895
"code" => store.code,
3996
"default" => true,
40-
"available_locales" => ["en"]
97+
"available_locales" => ["en"],
98+
"legal_name" => nil,
99+
"contact_email" => nil,
100+
"contact_phone" => nil,
101+
"description" => nil,
102+
"tax_id" => nil,
103+
"vat_id" => nil,
104+
"address1" => nil,
105+
"address2" => nil,
106+
"city" => nil,
107+
"zipcode" => nil,
108+
"country_id" => nil,
109+
"state_id" => nil,
110+
"state_name" => nil
41111
},
42112
{
43113
"id" => non_default_store.id,
@@ -51,7 +121,20 @@ module Spree::Api
51121
"default_currency" => nil,
52122
"code" => non_default_store.code,
53123
"default" => false,
54-
"available_locales" => ["en"]
124+
"available_locales" => ["en"],
125+
"legal_name" => nil,
126+
"contact_email" => nil,
127+
"contact_phone" => nil,
128+
"description" => nil,
129+
"tax_id" => nil,
130+
"vat_id" => nil,
131+
"address1" => nil,
132+
"address2" => nil,
133+
"city" => nil,
134+
"zipcode" => nil,
135+
"country_id" => nil,
136+
"state_id" => nil,
137+
"state_name" => nil
55138
}
56139
])
57140
end
@@ -70,7 +153,20 @@ module Spree::Api
70153
"default_currency" => nil,
71154
"code" => store.code,
72155
"default" => true,
73-
"available_locales" => ["en"]
156+
"available_locales" => ["en"],
157+
"legal_name" => nil,
158+
"contact_email" => nil,
159+
"contact_phone" => nil,
160+
"description" => nil,
161+
"tax_id" => nil,
162+
"vat_id" => nil,
163+
"address1" => nil,
164+
"address2" => nil,
165+
"city" => nil,
166+
"zipcode" => nil,
167+
"country_id" => nil,
168+
"state_id" => nil,
169+
"state_name" => nil
74170
)
75171
end
76172

@@ -79,7 +175,14 @@ module Spree::Api
79175
code: "spree123",
80176
name: "Hack0rz",
81177
url: "spree123.example.com",
82-
mail_from_address: "[email protected]"
178+
mail_from_address: "[email protected]",
179+
legal_name: 'ABC Corp',
180+
address1: "123 Main St",
181+
city: 'San Francisco',
182+
country_id: country.id,
183+
state_id: state.id,
184+
phone: "123-456-7890",
185+
zipcode: "12345"
83186
}
84187
post spree.api_stores_path, params: { store: store_hash }
85188
expect(response.status).to eq(201)
@@ -89,13 +192,34 @@ module Spree::Api
89192
store_hash = {
90193
url: "spree123.example.com",
91194
mail_from_address: "[email protected]",
92-
bcc_email: "[email protected]"
195+
bcc_email: "[email protected]",
196+
legal_name: 'XYZ Corp',
197+
description: "Leading provider of high-quality tech accessories, offering the latest gadgets, peripherals, and electronics to enhance your digital lifestyle.",
198+
tax_id: "TX-987654321",
199+
vat_id: "VAT-123456789",
200+
address1: "123 Innovation Drive",
201+
address2: "Suite 456",
202+
city: "New York",
203+
country_id: country.id,
204+
state_id: state.id,
205+
contact_phone: "123-456-7888",
206+
zipcode: "10001"
93207
}
94208
put spree.api_store_path(store), params: { store: store_hash }
95209
expect(response.status).to eq(200)
96210
expect(store.reload.url).to eql "spree123.example.com"
97211
expect(store.reload.mail_from_address).to eql "[email protected]"
98212
expect(store.reload.bcc_email).to eql "[email protected]"
213+
expect(store.reload.legal_name).to eql "XYZ Corp"
214+
expect(store.reload.tax_id).to eql "TX-987654321"
215+
expect(store.reload.vat_id).to eql "VAT-123456789"
216+
expect(store.reload.address1).to eql "123 Innovation Drive"
217+
expect(store.reload.address2).to eql "Suite 456"
218+
expect(store.reload.city).to eql "New York"
219+
expect(store.reload.country_id).to eql country.id
220+
expect(store.reload.state_id).to eql state.id
221+
expect(store.reload.contact_phone).to eql "123-456-7888"
222+
expect(store.reload.zipcode).to eql "10001"
99223
end
100224

101225
context "deleting a store" do
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
2+
<% s_or_b = type.chars.first %>
3+
<hr>
4+
5+
<div class="row" id="<%= type %>" data-hook="address_fields">
6+
<div class="col-12 col-md-6">
7+
<div class="field <%= "#{type}-row" %>">
8+
<%= f.label :legal_name %>
9+
<%= f.text_field :legal_name, class: 'fullwidth' %>
10+
</div>
11+
12+
<div class="field <%= "#{type}-row" %>">
13+
<%= f.label :address1 %>
14+
<%= f.text_field :address1, class: 'fullwidth' %>
15+
</div>
16+
17+
<div class="field <%= "#{type}-row" %>">
18+
<%= f.label :address2 %>
19+
<%= f.text_field :address2, class: 'fullwidth' %>
20+
</div>
21+
22+
<div class="field <%= "#{type}-row" %>">
23+
<%= f.label :contact_phone %>
24+
<%= f.phone_field :contact_phone, class: 'fullwidth' %>
25+
</div>
26+
</div>
27+
28+
<div class="col-12 col-md-6">
29+
<div class="field <%= "#{type}-row" %>">
30+
<%= f.label :city %>
31+
<%= f.text_field :city, class: 'fullwidth' %>
32+
</div>
33+
34+
<div class="field <%= "#{type}-row" %>">
35+
<%= f.label :zipcode %>
36+
<%= f.text_field :zipcode, class: 'fullwidth' %>
37+
</div>
38+
39+
<div class="field <%= "#{type}-row" %>">
40+
<%= f.label :country_id, Spree::Country.model_name.human %>
41+
<span id="<%= s_or_b %>country">
42+
<%= f.collection_select :country_id, available_countries, :id, :name, { include_blank: true }, {class: 'custom-select fullwidth js-country_id'} %>
43+
</span>
44+
</div>
45+
46+
<div class="field <%= "#{type}-row" %>">
47+
<%= f.label :state_id, Spree::State.model_name.human %>
48+
<span id="<%= s_or_b %>state">
49+
<%= f.hidden_field :state_name, value: nil %>
50+
<% states = f.object.country.try(:states).nil? ? [] : f.object.country.states %>
51+
<%= f.text_field :state_name,
52+
style: "display: #{states.empty? ? 'block' : 'none' };",
53+
disabled: !states.empty?, class: 'fullwidth state_name js-state_name' %>
54+
<%= f.hidden_field :state_id, value: nil %>
55+
<%= f.collection_select :state_id,
56+
states.sort,
57+
:id, :name,
58+
{ include_blank: true },
59+
{ class: 'custom-select fullwidth js-state_id',
60+
style: "display: #{states.empty? ? 'none' : 'block' };",
61+
disabled: states.empty? } %>
62+
</span>
63+
</div>
64+
</div>
65+
</div>

backend/app/views/spree/admin/stores/_form.html.erb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,21 @@
3030
<%= f.text_area :meta_description, class: 'fullwidth' %>
3131
<%= f.error_message_on :meta_description %>
3232
<% end %>
33+
34+
<%= f.field_container :tax_id do %>
35+
<%= f.label :tax_id %>
36+
<%= f.text_field :tax_id, class: 'fullwidth' %>
37+
<%= f.error_message_on :tax_id %>
38+
<% end %>
39+
40+
<%= f.field_container :vat_id do %>
41+
<%= f.label :vat_id %>
42+
<%= f.text_field :vat_id, class: 'fullwidth' %>
43+
<%= f.error_message_on :vat_id %>
44+
<% end %>
45+
3346
</div>
47+
3448
<div class="col-12 col-md-6">
3549
<%= f.field_container :url do %>
3650
<%= f.label :url, class: 'required' %>
@@ -81,5 +95,17 @@
8195
{ class: 'select2 fullwidth', multiple: true } %>
8296
<%= f.error_message_on :default_currency %>
8397
<% end %>
98+
99+
<%= f.field_container :description do %>
100+
<%= f.label :description %>
101+
<%= f.text_area :description, class: 'fullwidth' %>
102+
<%= f.error_message_on :description %>
103+
<% end %>
104+
</div>
105+
106+
<div class="col-12">
107+
<div class="js-addresses-form">
108+
<%= render partial: 'address_form', locals: { f: f, type: 'store' } %>
109+
</div>
84110
</div>
85111
</div>

core/app/models/spree/store.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,16 @@ class Store < Spree::Base
1515
has_many :store_shipping_methods, inverse_of: :store
1616
has_many :shipping_methods, through: :store_shipping_methods
1717

18+
belongs_to :state, class_name: 'Spree::State', optional: true
19+
belongs_to :country, class_name: 'Spree::Country', optional: true
20+
1821
has_many :orders, class_name: "Spree::Order"
1922

2023
validates :code, presence: true, uniqueness: { allow_blank: true, case_sensitive: true }
2124
validates :name, presence: true
2225
validates :url, presence: true
2326
validates :mail_from_address, presence: true
27+
validates :state, presence: true, if: -> { country&.states_required }
2428

2529
self.allowed_ransackable_attributes = %w[name url code]
2630

core/config/locales/en.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,18 +352,31 @@ en:
352352
quantity: Quantity
353353
variant: Variant
354354
spree/store:
355+
address: Address
356+
address1: Street Address
357+
address2: Street Address (cont'd)
355358
available_locales: Locales Available in the Storefront
356359
bcc_email: BCC Email
357360
cart_tax_country_iso: Tax Country for Empty Carts
361+
city: City
358362
code: Slug
363+
contact_email: Contact Email
364+
contact_phone: Contact Phone
365+
country_id: Country
359366
default: Default
360367
default_currency: Default Currency
368+
description: Store Description
369+
legal_name: Legal Name
361370
mail_from_address: Mail From Address
362371
meta_description: Meta Description
363372
meta_keywords: Meta Keywords
364373
name: Site Name
374+
postal_code: Postal Code
365375
seo_title: Seo Title
376+
state_id: State
377+
tax_id: Tax ID
366378
url: Site URL
379+
vat_id: VAT ID
367380
spree/store_credit:
368381
amount: Amount
369382
amount_authorized: Amount Authorized
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
class AddStoreAttributesToSpreeStores < ActiveRecord::Migration[7.2]
2+
def change
3+
add_column :spree_stores, :legal_name, :string
4+
add_column :spree_stores, :contact_email, :string
5+
add_column :spree_stores, :description, :text
6+
add_column :spree_stores, :vat_id, :string
7+
add_column :spree_stores, :tax_id, :string
8+
add_column :spree_stores, :address1, :string
9+
add_column :spree_stores, :address2, :string
10+
add_column :spree_stores, :city, :string
11+
add_column :spree_stores, :zipcode, :string
12+
add_column :spree_stores, :state_name, :string
13+
add_column :spree_stores, :contact_phone, :string
14+
add_column :spree_stores, :country_id, :integer
15+
add_column :spree_stores, :state_id, :integer
16+
add_index :spree_stores, :country_id
17+
add_index :spree_stores, :state_id
18+
end
19+
end

core/lib/spree/permitted_attributes.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,12 @@ module PermittedAttributes
120120
:quantity, :stock_item, :stock_item_id, :originator, :action
121121
]
122122

123-
@@store_attributes = [:name, :url, :seo_title, :meta_keywords,
123+
@@store_attributes = [:name, :legal_name, :url, :seo_title, :meta_keywords,
124124
:meta_description, :default_currency,
125125
:mail_from_address, :cart_tax_country_iso,
126-
:bcc_email]
126+
:bcc_email, :contact_email, :contact_phone, :code,
127+
:tax_id, :vat_id, :description, :address1, :address2,
128+
:city, :zipcode, :country_id, :state_id, :state_name]
127129

128130
@@taxonomy_attributes = [:name]
129131

0 commit comments

Comments
 (0)