Skip to content

Commit

Permalink
Add Primary Taxon feature to Spree products
Browse files Browse the repository at this point in the history
- Introduced a new `primary_taxon_id` reference field in the `spree_products` table to associate a primary breadcrumb taxon with products.
- Updated the `Spree::Product` model to include a `belongs_to :primary_taxon` association, allowing optional linkage to a taxon.
- Modified the `Spree::Api::ApiConfiguration` to include `primary_taxon_id` in `product_attributes`, enabling API support for this attribute.
- Enhanced API tests to cover creation and updating of products with a primary taxon.
- Updated the admin product form to allow selection of a primary taxon.
- Improved frontend JavaScript for autocomplete functionality when selecting a primary taxon.
- Added `primary_taxon_id` to the list of permitted attributes for safe updates via the admin interface and API.
- Included test cases to verify the functionality of the `primary_taxon` association, ensuring proper model validation and API behavior.

This change is backward-compatible, allowing products to exist without a primary breadcrumb taxon while enhancing the product management capabilities in both the admin interface and API.
  • Loading branch information
shahmayur001 committed Feb 12, 2025
1 parent efe8b97 commit f7f91c0
Show file tree
Hide file tree
Showing 10 changed files with 111 additions and 22 deletions.
2 changes: 1 addition & 1 deletion api/lib/spree/api_configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class ApiConfiguration < Preferences::Configuration
preference :product_attributes, :array, default: [
:id, :name, :description, :available_on,
:slug, :meta_description, :meta_keywords, :shipping_category_id,
:taxon_ids, :total_on_hand, :meta_title
:taxon_ids, :total_on_hand, :meta_title, :primary_taxon_id
]

preference :product_property_attributes, :array, default: [:id, :product_id, :property_id, :value, :property_name]
Expand Down
14 changes: 14 additions & 0 deletions api/spec/requests/spree/api/products_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,13 @@ module Spree::Api
expect(json_response["taxon_ids"]).to eq([taxon_1.id])
end

it "puts primary taxon for the product" do
product_data[:primary_taxon_id] = taxon_1.id.to_s
post spree.api_products_path, params: { product: product_data }

expect(json_response["primary_taxon_id"]).to eq(taxon_1.id)
end

# Regression test for https://github.com/spree/spree/issues/4123
it "puts the created product in the given taxons" do
product_data[:taxon_ids] = [taxon_1.id, taxon_2.id].join(',')
Expand Down Expand Up @@ -404,6 +411,13 @@ module Spree::Api
expect(json_response["taxon_ids"]).to eq([taxon_1.id])
end

it "puts primary taxon for the updated product" do
product.primary_taxon_id = taxon_2.id
put spree.api_product_path(product), params: { product: { primary_taxon_id: taxon_1.id } }

expect(json_response["primary_taxon_id"]).to eq(taxon_1.id)
end

# Regression test for https://github.com/spree/spree/issues/4123
it "puts the created product in the given taxons" do
put spree.api_product_path(product), params: { product: { taxon_ids: [taxon_1.id, taxon_2.id].join(',') } }
Expand Down
68 changes: 49 additions & 19 deletions backend/app/assets/javascripts/spree/backend/taxon_autocomplete.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,49 @@
$.fn.taxonAutocomplete = function () {
$.fn.autocompleteTaxon = function (options) {
'use strict';

var defaultOptions = {
multiple: true,
placeholder: Spree.translations.taxon_placeholder
};

options = $.extend({}, defaultOptions, options);

this.select2({
placeholder: Spree.translations.taxon_placeholder,
multiple: true,
placeholder: options.placeholder,
multiple: options.multiple,
initSelection: function (element, callback) {
var ids = element.val(),
count = ids.split(",").length;

Spree.ajax({
type: "GET",
url: Spree.pathFor('api/taxons'),
data: {
ids: ids,
per_page: count,
without_children: true
},
success: function (data) {
callback(data['taxons']);
}
});
var ids = element.val();

if (options.multiple) {
var count = ids.split(",").length;

Spree.ajax({
type: "GET",
url: Spree.pathFor('api/taxons'),
data: {
ids: ids,
per_page: count,
without_children: true
},
success: function (data) {
callback(data['taxons']);
}
});
} else {

Spree.ajax({
type: "GET",
url: Spree.pathFor('api/taxons'),
data: {
ids: ids,
per_page: 1,
without_children: true
},
success: function (data) {
callback(data['taxons'][0]);
}
});
}
},
ajax: {
url: Spree.pathFor('api/taxons'),
Expand Down Expand Up @@ -53,5 +77,11 @@ $.fn.taxonAutocomplete = function () {
};

Spree.ready(function () {
$('#product_taxon_ids, .taxon_picker').taxonAutocomplete();
$('#product_taxon_ids, .taxon_picker').autocompleteTaxon({
multiple: true,
});

$('#product_primary_taxon_id').autocompleteTaxon({
multiple: false,
});
});
7 changes: 7 additions & 0 deletions backend/app/views/spree/admin/products/_form.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@
<% end %>
</div>

<div data-hook="admin_product_form_primary_taxons">
<%= f.field_container :primary_taxon do %>
<%= f.label :primary_taxon_id %><br>
<%= f.hidden_field :primary_taxon_id, value: @product.primary_taxon_id %>
<% end %>
</div>

<div data-hook="admin_product_form_option_types">
<%= f.field_container :option_types do %>
<%= f.label :option_type_ids, plural_resource_name(Spree::OptionType) %>
Expand Down
5 changes: 4 additions & 1 deletion backend/spec/features/admin/products/edit/taxons_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ def assert_selected_taxons(taxons)
visit spree.edit_admin_product_path(product)

assert_selected_taxons([taxon_1])
select2_search "Clothing", from: "Taxon"
within("[data-hook='admin_product_form_taxons']") do
select2_search "Clothing", from: "Taxon"
end

assert_selected_taxons([taxon_1, taxon_2])

# Without this line we have a flaky spec probably due to select2 not
Expand Down
1 change: 1 addition & 0 deletions core/app/models/spree/product.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class Product < Spree::Base

belongs_to :tax_category, class_name: 'Spree::TaxCategory', optional: true
belongs_to :shipping_category, class_name: 'Spree::ShippingCategory', inverse_of: :products, optional: true
belongs_to :primary_taxon, class_name: 'Spree::Taxon', optional: true

has_one :master,
-> { where(is_master: true).with_discarded },
Expand Down
2 changes: 2 additions & 0 deletions core/config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,8 @@ en:
name: Name
on_hand: On Hand
price: Master Price
primary_taxon_id: Primary Taxon
primary_taxon: Primary Taxon
promotionable: Promotable
shipping_category: Shipping Category
sku: Master SKU
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class AddPrimaryTaxonToProducts < ActiveRecord::Migration[7.0]
def change
change_table :spree_products do |t|
t.references :primary_taxon, { to_table: :spree_taxons }
end
end
end
2 changes: 1 addition & 1 deletion core/lib/spree/permitted_attributes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ module PermittedAttributes
:meta_keywords, :price, :sku, :deleted_at,
:option_values_hash, :weight, :height, :width, :depth,
:shipping_category_id, :tax_category_id,
:taxon_ids, :option_type_ids, :cost_currency, :cost_price
:taxon_ids, :option_type_ids, :cost_currency, :cost_price, :primary_taxon_id
]

@@property_attributes = [:name, :presentation]
Expand Down
25 changes: 25 additions & 0 deletions core/spec/models/spree/product_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,22 @@ class Extension < Spree::Base
end
end
end

describe "primary_taxon" do
it 'should belong to primary_taxon' do
expect(Spree::Product.reflect_on_association(:primary_taxon).macro).to eq(:belongs_to)
end

it 'should be optional' do
association = Spree::Product.reflect_on_association(:primary_taxon)
expect(association.options[:optional]).to be(true)
end

it 'should have a class_name of Spree::Taxon' do
association = Spree::Product.reflect_on_association(:primary_taxon)
expect(association.class_name).to eq('Spree::Taxon')
end
end
end
end

Expand Down Expand Up @@ -709,4 +725,13 @@ class Extension < Spree::Base
end
end
end

it 'is valid with or without a primary_taxon' do
product_with_taxon = create(:product, primary_taxon: create(:taxon))

product_without_taxon = create(:product, primary_taxon: nil)

expect(product_with_taxon).to be_valid
expect(product_without_taxon).to be_valid
end
end

0 comments on commit f7f91c0

Please sign in to comment.