예제 #1
0
        public virtual async Task <Product> CloneProductAsync(
            Product product,
            string cloneName,
            bool isPublished,
            bool copyAssociatedProducts = true)
        {
            Guard.NotNull(product, nameof(product));
            Guard.NotEmpty(cloneName, nameof(cloneName));

            var localizedKeySelectors = new List <Expression <Func <Product, string> > >
            {
                x => x.Name,
                x => x.ShortDescription,
                x => x.FullDescription,
                x => x.MetaKeywords,
                x => x.MetaDescription,
                x => x.MetaTitle,
                x => x.BundleTitleText
            };

            var clone     = new Product();
            var utcNow    = DateTime.UtcNow;
            var languages = await _languageService.GetAllLanguagesAsync(true);

            int?sampleDownloadId = null;

            // Enable hooks for slugs cache invalidation.
            using (_chronometer.Step("Clone product " + product.Id))
                using (var scope = new DbContextScope(_db, autoDetectChanges: false, hooksEnabled: true, deferCommit: true, forceNoTracking: true))
                {
                    if (product.HasSampleDownload && product.SampleDownload != null)
                    {
                        var sampleDownloadClone = product.SampleDownload.Clone();
                        _db.Downloads.Add(sampleDownloadClone);

                        await scope.CommitAsync();

                        sampleDownloadId = sampleDownloadClone.Id;
                    }

                    var props = FastProperty.GetProperties(typeof(Product), PropertyCachingStrategy.EagerCached);

                    foreach (var prop in props.Values)
                    {
                        if (prop.IsComplexType)
                        {
                            continue;
                        }

                        if (!prop.IsPublicSettable)
                        {
                            continue;
                        }

                        prop.SetValue(clone, prop.GetValue(product));
                    }

                    clone.Id               = 0;
                    clone.Name             = cloneName;
                    clone.SampleDownloadId = sampleDownloadId;
                    clone.Published        = isPublished;
                    clone.CreatedOnUtc     = utcNow;
                    clone.UpdatedOnUtc     = utcNow;

                    // Category mappings.
                    clone.ProductCategories.AddRange(product.ProductCategories.Select(x => new ProductCategory
                    {
                        CategoryId        = x.CategoryId,
                        IsFeaturedProduct = x.IsFeaturedProduct,
                        DisplayOrder      = x.DisplayOrder
                    }));

                    // Manufacturer mappings.
                    clone.ProductManufacturers.AddRange(product.ProductManufacturers.Select(x => new ProductManufacturer
                    {
                        ManufacturerId    = x.ManufacturerId,
                        IsFeaturedProduct = x.IsFeaturedProduct,
                        DisplayOrder      = x.DisplayOrder
                    }));

                    // Media file mappings.
                    clone.ProductPictures.AddRange(product.ProductPictures.Select(x => new ProductMediaFile
                    {
                        MediaFileId  = x.MediaFileId,
                        DisplayOrder = x.DisplayOrder
                    }));
                    if (clone.MainPictureId == null)
                    {
                        clone.MainPictureId = product.ProductPictures.FirstOrDefault()?.MediaFileId;
                    }

                    // Product specification attributes.
                    clone.ProductSpecificationAttributes.AddRange(product.ProductSpecificationAttributes.Select(x => new ProductSpecificationAttribute
                    {
                        SpecificationAttributeOptionId = x.SpecificationAttributeOptionId,
                        AllowFiltering    = x.AllowFiltering,
                        ShowOnProductPage = x.ShowOnProductPage,
                        DisplayOrder      = x.DisplayOrder
                    }));

                    // Tier prices.
                    clone.TierPrices.AddRange(product.TierPrices.Select(x => new TierPrice
                    {
                        StoreId           = x.StoreId,
                        CustomerRoleId    = x.CustomerRoleId,
                        Quantity          = x.Quantity,
                        Price             = x.Price,
                        CalculationMethod = x.CalculationMethod
                    }));
                    clone.HasTierPrices = clone.TierPrices.Any();

                    // Discount mappings.
                    foreach (var discount in product.AppliedDiscounts)
                    {
                        clone.AppliedDiscounts.Add(discount);
                        clone.HasDiscountsApplied = true;
                    }

                    // Tags.
                    foreach (var tag in product.ProductTags)
                    {
                        clone.ProductTags.Add(tag);
                    }

                    // >>>>>>> Put clone to db (from here on we need the product clone's ID).
                    _db.Products.Add(clone);
                    await scope.CommitAsync();

                    // Store mappings.
                    var selectedStoreIds = await _storeMappingService.GetAuthorizedStoreIdsAsync(product);

                    selectedStoreIds.Each(id => _storeMappingService.AddStoreMapping(clone, id));

                    await ProcessPromotions(product, clone);

                    await ProcessSlugs(product, clone, languages);

                    await ProcessLocalizations(product, clone, localizedKeySelectors, languages);

                    await ProcessDownloads(product, clone);

                    // >>>>>>> Put to db.
                    await scope.CommitAsync();

                    await ProcessBundleItems(scope, product, clone, languages);

                    // Attributes and attribute combinations.
                    await ProcessAttributes(scope, product, clone, languages);

                    // Update computed properties.
                    clone.LowestAttributeCombinationPrice = await _db.ProductVariantAttributeCombinations
                                                            .ApplyLowestPriceFilter(clone.Id)
                                                            .Select(x => x.Price)
                                                            .FirstOrDefaultAsync();

                    // Associated products.
                    if (copyAssociatedProducts && product.ProductType != ProductType.BundledProduct)
                    {
                        await ProcessAssociatedProducts(product, clone, isPublished);
                    }

                    // >>>>>>> Our final commit.
                    await scope.CommitAsync();
                }

            return(clone);
        }