diff --git a/api/openapi/solidus-api.oas.yml b/api/openapi/solidus-api.oas.yml index a129f9b8c1e..b9631b91683 100644 --- a/api/openapi/solidus-api.oas.yml +++ b/api/openapi/solidus-api.oas.yml @@ -7168,6 +7168,8 @@ components: type: boolean product_id: type: integer + shipping_category_id: + type: integer tax_category_id: type: integer weight: diff --git a/backend/app/controllers/spree/admin/variants_controller.rb b/backend/app/controllers/spree/admin/variants_controller.rb index 149be67f70c..069192b1fc1 100644 --- a/backend/app/controllers/spree/admin/variants_controller.rb +++ b/backend/app/controllers/spree/admin/variants_controller.rb @@ -32,6 +32,7 @@ def collection def load_data @tax_categories = Spree::TaxCategory.order(:name) + @shipping_categories = Spree::ShippingCategory.order(:name) end def variant_includes diff --git a/backend/app/views/spree/admin/shared/_rebuild_vat_prices_checkbox.html.erb b/backend/app/views/spree/admin/shared/_rebuild_vat_prices_checkbox.html.erb index 6ceb1220930..60feaf97007 100644 --- a/backend/app/views/spree/admin/shared/_rebuild_vat_prices_checkbox.html.erb +++ b/backend/app/views/spree/admin/shared/_rebuild_vat_prices_checkbox.html.erb @@ -1,4 +1,4 @@ -
+
+ <% if show_rebuild_vat_checkbox? %> + <%= render "spree/admin/shared/rebuild_vat_prices_checkbox", form: f, model_name: "variant" %> + <% end %>
- <% if show_rebuild_vat_checkbox? %> -
- <%= render "spree/admin/shared/rebuild_vat_prices_checkbox", form: f, model_name: "variant", wrapper_class: "field" %> -
- <% end %> -
<%= f.label :cost_price %> @@ -97,6 +94,19 @@ { class: 'custom-select fullwidth' } %>
+ +
+
+ <%= f.label :shipping_category %> + <%= f.field_hint :shipping_category %> + <%= f.collection_select :shipping_category_id, + @shipping_categories, + :id, + :name, + { include_blank: t('.use_product_shipping_category') }, + { class: 'custom-select fullwidth' } %> +
+
diff --git a/backend/spec/features/admin/products/variant_spec.rb b/backend/spec/features/admin/products/variant_spec.rb index c6f68fcbb46..b3872f557fe 100644 --- a/backend/spec/features/admin/products/variant_spec.rb +++ b/backend/spec/features/admin/products/variant_spec.rb @@ -25,6 +25,7 @@ expect(page).to have_field('variant_width', with: "1.00") expect(page).to have_field('variant_depth', with: "1.50") expect(page).to have_select('variant[tax_category_id]') + expect(page).to have_select('variant[shipping_category_id]') end end diff --git a/core/app/models/spree/variant.rb b/core/app/models/spree/variant.rb index 0e2f54972c4..cebe46a583b 100644 --- a/core/app/models/spree/variant.rb +++ b/core/app/models/spree/variant.rb @@ -30,11 +30,14 @@ class Variant < Spree::Base belongs_to :product, -> { with_discarded }, touch: true, class_name: 'Spree::Product', inverse_of: :variants_including_master, optional: false belongs_to :tax_category, class_name: 'Spree::TaxCategory', optional: true + belongs_to :shipping_category, class_name: "Spree::ShippingCategory", optional: true delegate :name, :description, :slug, :available_on, :discontinue_on, :discontinued?, - :shipping_category_id, :meta_description, :meta_keywords, :shipping_category, + :meta_description, :meta_keywords, to: :product delegate :tax_category, to: :product, prefix: true + delegate :shipping_category, :shipping_category_id, + to: :product, prefix: true delegate :tax_rates, to: :tax_category has_many :inventory_units, inverse_of: :variant @@ -142,6 +145,23 @@ def tax_category super || product_tax_category end + # @return [Spree::ShippingCategory] the variant's shipping category + # + # This returns the product's shipping category if the shipping category ID on the variant is nil. It looks + # like an association, but really is an override. + # + def shipping_category + super || product_shipping_category + end + + # @return [Integer] the variant's shipping category id + # + # This returns the product's shipping category if if the shipping category ID on the variant is nil. + # + def shipping_category_id + super || product_shipping_category_id + end + # Sets the cost_price for the variant. # # @param price [Any] the price to set diff --git a/core/config/locales/en.yml b/core/config/locales/en.yml index 042b7354109..ce0212cf0cf 100644 --- a/core/config/locales/en.yml +++ b/core/config/locales/en.yml @@ -415,6 +415,7 @@ en: depth: Depth height: Height price: Price + shipping_category: Variant Shipping Category sku: SKU tax_category: Variant Tax Category weight: Weight @@ -993,6 +994,7 @@ en: pricing: Pricing pricing_hint: These values are populated from the product details page and can be overridden below properties: Properties + use_product_shipping_category: Use Product Shipping Category use_product_tax_category: Use Product Tax Category new: new_variant: New Variant @@ -1641,6 +1643,7 @@ en: deleted: Deleted Variant deleted_explanation: This variant was deleted on %{date}. deleted_explanation_with_replacement: This variant was deleted on %{date}. It has since been replaced by another with the same SKU. + shipping_category: 'This determines what kind of shipping this variant requires.
Default: Use shipping category of the product associated with this variant' tax_category: 'This determines what kind of taxation is applied to this variant.
Default: Use tax category of the product associated with this variant' home: Home i18n: diff --git a/core/db/migrate/20221123152807_add_shipping_category_to_spree_variants.rb b/core/db/migrate/20221123152807_add_shipping_category_to_spree_variants.rb new file mode 100644 index 00000000000..85c3ed8ed8c --- /dev/null +++ b/core/db/migrate/20221123152807_add_shipping_category_to_spree_variants.rb @@ -0,0 +1,5 @@ +class AddShippingCategoryToSpreeVariants < ActiveRecord::Migration[5.2] + def change + add_reference :spree_variants, :shipping_category, index: true + end +end diff --git a/core/lib/spree/permitted_attributes.rb b/core/lib/spree/permitted_attributes.rb index 5169280508c..7a9d6360f5c 100644 --- a/core/lib/spree/permitted_attributes.rb +++ b/core/lib/spree/permitted_attributes.rb @@ -132,7 +132,10 @@ module PermittedAttributes :name, :presentation, :cost_price, :lock_version, :position, :track_inventory, :product_id, :product, :price, - :weight, :height, :width, :depth, :sku, :cost_currency, :tax_category_id, option_value_ids: [], options: [:name, :value] + :weight, :height, :width, :depth, :sku, :cost_currency, + :tax_category_id, :shipping_category_id, + option_value_ids: [], + options: [:name, :value] ] @@checkout_address_attributes = [ diff --git a/core/spec/models/spree/variant_spec.rb b/core/spec/models/spree/variant_spec.rb index 05f06cf00d2..cc0fe73c15c 100644 --- a/core/spec/models/spree/variant_spec.rb +++ b/core/spec/models/spree/variant_spec.rb @@ -773,6 +773,44 @@ end end + describe '#shipping_category' do + context 'when shipping_category is nil' do + let(:shipping_category) { build(:shipping_category) } + let(:product) { build(:product, shipping_category: shipping_category) } + let(:variant) { build(:variant, product: product, shipping_category_id: nil) } + it 'returns the parent products shipping_category' do + expect(variant.shipping_category).to eq(shipping_category) + end + end + + context 'when shipping_category is set' do + let(:shipping_category) { create(:shipping_category) } + let(:variant) { build(:variant, shipping_category: shipping_category) } + it 'returns the shipping_category set on itself' do + expect(variant.shipping_category).to eq(shipping_category) + end + end + end + + describe '#shipping_category_id' do + context 'when shipping_category_id is nil' do + let(:shipping_category) { build(:shipping_category) } + let(:product) { build(:product, shipping_category: shipping_category) } + let(:variant) { build(:variant, product: product, shipping_category_id: nil) } + it 'returns the parent products shipping_category_id' do + expect(variant.shipping_category_id).to eq(shipping_category.id) + end + end + + context 'when shipping_category_id is set' do + let(:shipping_category) { create(:shipping_category) } + let(:variant) { build(:variant, shipping_category_id: shipping_category.id) } + it 'returns the shipping_category_id set on itself' do + expect(variant.shipping_category_id).to eq(shipping_category.id) + end + end + end + describe "touching" do it "updates a product" do variant.product.update_column(:updated_at, 1.day.ago)