diff --git a/lib/spree/core/product_duplicator.rb b/lib/spree/core/product_duplicator.rb index 5000caf7cd..d34b45aaeb 100644 --- a/lib/spree/core/product_duplicator.rb +++ b/lib/spree/core/product_duplicator.rb @@ -32,9 +32,12 @@ module Spree end def duplicate_variants - product.variants.map do |variant| + # Create a hash with mapping: { : , } + mapped_variants = product.variants.index_with do |variant| duplicate_variant(variant) end + duplicate_variant_links(mapped_variants) + mapped_variants.values end def duplicate_variant(variant) @@ -47,6 +50,19 @@ module Spree end end + def duplicate_variant_links(mapped_variants) + # Find any links between orig variants (links to/from another product are ignored) + variant_links = VariantLink.where(source_variant: [mapped_variants.keys], + target_variant: [mapped_variants.keys]) + # Link the new variants + variant_links.find_each do |variant_link| + source_variant = mapped_variants[variant_link.source_variant] + target_variant = mapped_variants[variant_link.target_variant] + + target_variant.variant_links_as_target.new(source_variant:) + end + end + def duplicate_image(image) new_image = image.dup new_image.attachment.attach(image.attachment_blob) diff --git a/spec/lib/spree/core/product_duplicator_spec.rb b/spec/lib/spree/core/product_duplicator_spec.rb index 5f22636065..a7f5a4f7d7 100644 --- a/spec/lib/spree/core/product_duplicator_spec.rb +++ b/spec/lib/spree/core/product_duplicator_spec.rb @@ -36,6 +36,7 @@ RSpec.describe Spree::Core::ProductDuplicator do it "can duplicate a product" do duplicator = Spree::Core::ProductDuplicator.new(product) + allow(duplicator).to receive(:duplicate_variant_links) # tested elsewhere expect(new_product).to receive(:name=).with("COPY OF foo") expect(new_product).to receive(:sku=).with("") expect(new_product).to receive(:product_properties=).with([new_property]) @@ -65,6 +66,92 @@ RSpec.describe Spree::Core::ProductDuplicator do end end + describe "duplicating" do + subject { described_class.new(product).duplicate } + + context "with multiple variant links" do + let(:product) { create(:product) } + + before do + src_variant = product.variants.first.tap { it.update! display_name: "SRC" } + user = src_variant.supplier.owner + src_variant.create_linked_variant(user).tap { it.update! display_name: "LNK1" } + src_variant.create_linked_variant(user).tap { it.update! display_name: "LNK2" } + end + + it "duplicates variant links" do + expect(subject).to be_a Spree::Product + expect(subject.variants.count).to eq 3 + + new_src_variant = subject.variants.find { it.display_name == "SRC" } + new_lnk_variant1 = subject.variants.find { it.display_name == "LNK1" } + new_lnk_variant2 = subject.variants.find { it.display_name == "LNK2" } + + expect(new_src_variant.target_variants).to eq [new_lnk_variant1, new_lnk_variant2] + expect(new_lnk_variant1.source_variants).to eq [new_src_variant] + expect(new_lnk_variant2.source_variants).to eq [new_src_variant] + end + + it "minimises(?) database queries" do + expect { subject }.to query_database [ + "Spree::ProductProperty Load", + "Spree::Image Load", + "Spree::Variant Load", + "Spree::Image Load", + "Spree::Price Load", + "Spree::Image Load", + "Spree::Price Load", + "Spree::Image Load", + "Spree::Price Load", + "VariantLink Load", + "Spree::Variant Load", + "Spree::Variant Load", + "Spree::Variant Load", + "Spree::Variant Load", + "TRANSACTION", + "Spree::ShippingCategory Load", + "Spree::Taxon Load", + "Enterprise Load", + "Spree::ShippingCategory Load", + "Spree::Taxon Load", + "Enterprise Load", + "Spree::ShippingCategory Load", + "Spree::Taxon Load", + "Enterprise Load", + "Spree::Product Create", + "Spree::Variant Create", + "Spree::Price Create", + "Spree::StockItem Exists?", + "Spree::StockItem Exists?", + "Spree::StockItem Create", + "ActsAsTaggableOn::Tagging Load", + "Spree::Variant Update", + "Spree::Variant Create", + "Spree::Price Create", + "VariantLink Create", + "Spree::StockItem Exists?", + "Spree::StockItem Exists?", + "Spree::StockItem Create", + "ActsAsTaggableOn::Tagging Load", + "Spree::Variant Update", + "Spree::Variant Create", + "Spree::Price Create", + "VariantLink Create", + "Spree::StockItem Exists?", + "Spree::StockItem Exists?", + "Spree::StockItem Create", + "ActsAsTaggableOn::Tagging Load", + "Spree::Variant Update", + "Spree::Product Update", + "Enterprise Update", + "Enterprise Update All", + "Spree::Taxon Update", + "TRANSACTION" + ] + end + end + end + describe "errors" do context "with invalid product" do # Name has a max length of 255 char, when cloning a product the cloned product has a name