Skip to content

Commit

Permalink
Add Primary Breadcrumb Taxon feature to Spree products
Browse files Browse the repository at this point in the history
- Introduced a new `primary_breadcrumb_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_breadcrumb_taxon` association, allowing optional linkage to a taxon.
- Modified the `Spree::Api::ApiConfiguration` to include `primary_breadcrumb_taxon_id` in `product_attributes`, enabling API support for this attribute.
- Enhanced API tests to cover creation and updating of products with a primary breadcrumb taxon.
- Updated the admin product form to allow selection of a primary breadcrumb taxon.
- Improved frontend JavaScript for autocomplete functionality when selecting a primary breadcrumb taxon.
- Added `primary_breadcrumb_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_breadcrumb_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 10, 2025
1 parent dfe87ae commit fc7a95d
Show file tree
Hide file tree
Showing 9 changed files with 109 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_breadcrumb_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 breadcrumb taxon for the product" do
product_data[:primary_breadcrumb_taxon_id] = taxon_1.id.to_s
post spree.api_products_path, params: { product: product_data }

expect(json_response["primary_breadcrumb_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 breadcrumb taxon for the updated product" do
product.primary_breadcrumb_taxon_id = taxon_2.id
put spree.api_product_path(product), params: { product: { primary_breadcrumb_taxon_id: taxon_1.id } }

expect(json_response["primary_breadcrumb_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_breadcrumb_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_breadcrumb_taxons">
<%= f.field_container :primary_breadcrumb_taxon do %>
<%= f.label :primary_breadcrumb_taxon_id, "Primary Breadcrumb Taxon" %><br>
<%= f.hidden_field :primary_breadcrumb_taxon_id, value: @product.primary_breadcrumb_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_breadcrumb_taxon, class_name: 'Spree::Taxon', optional: true

has_one :master,
-> { where(is_master: true).with_discarded },
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class AddPrimaryBreadcrumbTaxonToProducts < ActiveRecord::Migration[7.0]
def change
add_column :spree_products, :primary_breadcrumb_taxon_id, :integer, null: true
add_foreign_key :spree_products, :spree_taxons, column: :primary_breadcrumb_taxon_id
add_index :spree_products, :primary_breadcrumb_taxon_id
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_breadcrumb_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_breadcrumb_taxon" do
it 'should belong to primary_breadcrumb_taxon' do
expect(Spree::Product.reflect_on_association(:primary_breadcrumb_taxon).macro).to eq(:belongs_to)
end

it 'should be optional' do
association = Spree::Product.reflect_on_association(:primary_breadcrumb_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_breadcrumb_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_breadcrumb_taxon' do
product_with_taxon = create(:product, primary_breadcrumb_taxon: create(:taxon))

product_without_taxon = create(:product, primary_breadcrumb_taxon: nil)

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

0 comments on commit fc7a95d

Please sign in to comment.