diff --git a/app/models/spree/product_decorator.rb b/app/models/spree/product_decorator.rb index 5150a760c8..3f570bdd2f 100644 --- a/app/models/spree/product_decorator.rb +++ b/app/models/spree/product_decorator.rb @@ -1,4 +1,10 @@ Spree::Product.class_eval do + # We have an after_destroy callback on Spree::ProductOptionType. However, if we + # don't specify dependent => destroy on this association, it is not called. See: + # https://github.com/rails/rails/issues/7618 + has_many :option_types, :through => :product_option_types, :dependent => :destroy + + belongs_to :supplier, :class_name => 'Enterprise' has_many :product_distributions, :dependent => :destroy diff --git a/app/models/spree/product_option_type_decorator.rb b/app/models/spree/product_option_type_decorator.rb new file mode 100644 index 0000000000..e091b62efa --- /dev/null +++ b/app/models/spree/product_option_type_decorator.rb @@ -0,0 +1,10 @@ +Spree::ProductOptionType.class_eval do + after_destroy :remove_option_values + + def remove_option_values + self.product.variants_including_master.each do |variant| + option_values = variant.option_values.where(option_type_id: self.option_type) + variant.option_values.destroy *option_values + end + end +end diff --git a/spec/models/spree/product_spec.rb b/spec/models/spree/product_spec.rb index 6c7dfb9091..7e8f679f26 100644 --- a/spec/models/spree/product_spec.rb +++ b/spec/models/spree/product_spec.rb @@ -412,6 +412,35 @@ module Spree end end + describe "option types" do + describe "removing an option type" do + it "removes the associated option values from all variants" do + # Given a product with a variant unit option type and values + p = create(:simple_product, variant_unit: 'weight', variant_unit_scale: 1) + v1 = create(:variant, product: p, unit_value: 100, option_values: []) + v2 = create(:variant, product: p, unit_value: 200, option_values: []) + + # And a custom option type and values + ot = create(:option_type, name: 'foo', presentation: 'foo') + p.option_types << ot + ov1 = create(:option_value, option_type: ot, name: 'One', presentation: 'One') + ov2 = create(:option_value, option_type: ot, name: 'Two', presentation: 'Two') + v1.option_values << ov1 + v2.option_values << ov2 + + # When we remove the custom option type + p.option_type_ids = p.option_type_ids.reject { |id| id == ot.id } + + # Then the associated option values should have been removed from the variants + v1.option_values(true).should_not include ov1 + v2.option_values(true).should_not include ov2 + + # And the option values themselves should still exist + Spree::OptionValue.where(id: [ov1.id, ov2.id]).count.should == 2 + end + end + end + describe "Stock filtering" do it "considers products that are on_demand as being in stock" do product = create(:simple_product, on_demand: true)