public static void MergeWithCombination(this Product product, ProductVariantAttributeCombination combination)
		{
			Guard.ArgumentNotNull(product, "product");

			if (product.MergedDataValues != null)
				product.MergedDataValues.Clear();

			if (combination == null)
				return;

			if (product.MergedDataValues == null)
				product.MergedDataValues = new Dictionary<string, object>();

            if (ManageInventoryMethod.ManageStockByAttributes == (ManageInventoryMethod)product.ManageInventoryMethodId)
            {
                product.MergedDataValues.Add("StockQuantity", combination.StockQuantity);
				product.MergedDataValues.Add("BackorderModeId", combination.AllowOutOfStockOrders ? (int)BackorderMode.AllowQtyBelow0 : (int)BackorderMode.NoBackorders);
            }

			if (combination.Sku.HasValue())
				product.MergedDataValues.Add("Sku", combination.Sku);
			if (combination.Gtin.HasValue())
				product.MergedDataValues.Add("Gtin", combination.Gtin);
			if (combination.ManufacturerPartNumber.HasValue())
				product.MergedDataValues.Add("ManufacturerPartNumber", combination.ManufacturerPartNumber);

			if (combination.Price.HasValue)
				product.MergedDataValues.Add("Price", combination.Price.Value);

			if (combination.DeliveryTimeId.HasValue && combination.DeliveryTimeId.Value > 0)
				product.MergedDataValues.Add("DeliveryTimeId", combination.DeliveryTimeId);

			if (combination.QuantityUnitId.HasValue && combination.QuantityUnitId.Value > 0)
				product.MergedDataValues.Add("QuantityUnitId", combination.QuantityUnitId);

			if (combination.Length.HasValue)
				product.MergedDataValues.Add("Length", combination.Length.Value);
			if (combination.Width.HasValue)
				product.MergedDataValues.Add("Width", combination.Width.Value);
			if (combination.Height.HasValue)
				product.MergedDataValues.Add("Height", combination.Height.Value);

			if (combination.BasePriceAmount.HasValue)
				product.MergedDataValues.Add("BasePriceAmount", combination.BasePriceAmount);
			if (combination.BasePriceBaseAmount.HasValue)
				product.MergedDataValues.Add("BasePriceBaseAmount", combination.BasePriceBaseAmount);
		}
        public void Can_save_and_load_productVariantAttributeCombination()
        {
            var pvac = new ProductVariantAttributeCombination
                       {
                           AttributesXml = "Some XML",
                           StockQuantity = 2,
						   Sku = "X1000",
						   Price = 9.80M,
                           AllowOutOfStockOrders = true,
						   Product = GetTestProduct()
                       };

            var fromDb = SaveAndLoadEntity(pvac);
            fromDb.ShouldNotBeNull();
            fromDb.AttributesXml.ShouldEqual("Some XML");
            fromDb.StockQuantity.ShouldEqual(2);
			fromDb.Sku.ShouldEqual("X1000");
			fromDb.Price.ShouldEqual(9.80M);
            fromDb.AllowOutOfStockOrders.ShouldEqual(true);
        }
        public void PrepareProductDetailsPictureModel(
			ProductDetailsPictureModel model, 
			IList<Picture> pictures, 
			string name, 
			IList<int> allCombinationImageIds,
			bool isAssociatedProduct, 
			ProductBundleItemData bundleItem = null, 
			ProductVariantAttributeCombination combination = null)
        {
            model.Name = name;
            model.DefaultPictureZoomEnabled = _mediaSettings.DefaultPictureZoomEnabled;
            model.PictureZoomType = _mediaSettings.PictureZoomType;
            model.AlternateText = T("Media.Product.ImageAlternateTextFormat", model.Name);

            Picture defaultPicture = null;
            var combiAssignedImages = (combination == null ? null : combination.GetAssignedPictureIds());
            int defaultPictureSize;

            if (isAssociatedProduct)
                defaultPictureSize = _mediaSettings.AssociatedProductPictureSize;
            else if (bundleItem != null)
                defaultPictureSize = _mediaSettings.BundledProductPictureSize;
            else
                defaultPictureSize = _mediaSettings.ProductDetailsPictureSize;

            if (pictures.Count > 0)
            {
                if (pictures.Count <= _catalogSettings.DisplayAllImagesNumber)
                {
                    // show all images
                    foreach (var picture in pictures)
                    {
                        model.PictureModels.Add(CreatePictureModel(model, picture, _mediaSettings.ProductDetailsPictureSize));

                        if (defaultPicture == null && combiAssignedImages != null && combiAssignedImages.Contains(picture.Id))
                        {
                            model.GalleryStartIndex = model.PictureModels.Count - 1;
                            defaultPicture = picture;
                        }
                    }
                }
                else
                {
                    // images not belonging to any combination...
                    allCombinationImageIds = allCombinationImageIds ?? new List<int>();
                    foreach (var picture in pictures.Where(p => !allCombinationImageIds.Contains(p.Id)))
                    {
                        model.PictureModels.Add(CreatePictureModel(model, picture, _mediaSettings.ProductDetailsPictureSize));
                    }

                    // plus images belonging to selected combination
                    if (combiAssignedImages != null)
                    {
                        foreach (var picture in pictures.Where(p => combiAssignedImages.Contains(p.Id)))
                        {
                            model.PictureModels.Add(CreatePictureModel(model, picture, _mediaSettings.ProductDetailsPictureSize));

                            if (defaultPicture == null)
                            {
                                model.GalleryStartIndex = model.PictureModels.Count - 1;
                                defaultPicture = picture;
                            }
                        }
                    }
                }

                if (defaultPicture == null)
                {
                    model.GalleryStartIndex = 0;
                    defaultPicture = pictures.First();
                }
            }

            // default picture
            if (defaultPicture == null)
            {
                model.DefaultPictureModel = new PictureModel
                {
                    Title = T("Media.Product.ImageLinkTitleFormat", model.Name),
                    AlternateText = model.AlternateText
                };

                if (!_catalogSettings.HideProductDefaultPictures)
                {
                    model.DefaultPictureModel.ThumbImageUrl = _pictureService.GetDefaultPictureUrl(_mediaSettings.ProductThumbPictureSizeOnProductDetailsPage);
                    model.DefaultPictureModel.ImageUrl = _pictureService.GetDefaultPictureUrl(defaultPictureSize);
                    model.DefaultPictureModel.FullSizeImageUrl = _pictureService.GetDefaultPictureUrl();
                }
            }
            else
            {
                model.DefaultPictureModel = CreatePictureModel(model, defaultPicture, defaultPictureSize);
            }
        }
        /// <summary>
        /// Create a copy of product with all depended data
        /// </summary>
        /// <param name="product">The product</param>
        /// <param name="newName">The name of product duplicate</param>
        /// <param name="isPublished">A value indicating whether the product duplicate should be published</param>
        /// <param name="copyImages">A value indicating whether the product images should be copied</param>
        /// <param name="copyAssociatedProducts">A value indicating whether the copy associated products</param>
        /// <returns>Product entity</returns>
        public virtual Product CopyProduct(Product product, string newName, bool isPublished, bool copyImages, bool copyAssociatedProducts = true)
        {
            if (product == null)
                throw new ArgumentNullException("product");

            if (String.IsNullOrEmpty(newName))
                throw new ArgumentException("Product name is required");

            Product productCopy = null;
            var utcNow = DateTime.UtcNow;

            // product download & sample download
            int downloadId = 0;
            int? sampleDownloadId = null;

            if (product.IsDownload)
            {
                var download = _downloadService.GetDownloadById(product.DownloadId);
                if (download != null)
                {
                    var downloadCopy = new Download
                    {
                        DownloadGuid = Guid.NewGuid(),
                        UseDownloadUrl = download.UseDownloadUrl,
                        DownloadUrl = download.DownloadUrl,
                        DownloadBinary = download.DownloadBinary,
                        ContentType = download.ContentType,
                        Filename = download.Filename,
                        Extension = download.Extension,
                        IsNew = download.IsNew,
                    };

                    _downloadService.InsertDownload(downloadCopy);
                    downloadId = downloadCopy.Id;
                }

                if (product.HasSampleDownload)
                {
                    var sampleDownload = _downloadService.GetDownloadById(product.SampleDownloadId.GetValueOrDefault());
                    if (sampleDownload != null)
                    {
                        var sampleDownloadCopy = new Download
                        {
                            DownloadGuid = Guid.NewGuid(),
                            UseDownloadUrl = sampleDownload.UseDownloadUrl,
                            DownloadUrl = sampleDownload.DownloadUrl,
                            DownloadBinary = sampleDownload.DownloadBinary,
                            ContentType = sampleDownload.ContentType,
                            Filename = sampleDownload.Filename,
                            Extension = sampleDownload.Extension,
                            IsNew = sampleDownload.IsNew
                        };

                        _downloadService.InsertDownload(sampleDownloadCopy);
                        sampleDownloadId = sampleDownloadCopy.Id;
                    }
                }
            }

            // product
            productCopy = new Product
            {
                ProductTypeId = product.ProductTypeId,
                ParentGroupedProductId = product.ParentGroupedProductId,
                VisibleIndividually = product.VisibleIndividually,
                Name = newName,
                ShortDescription = product.ShortDescription,
                FullDescription = product.FullDescription,
                ProductTemplateId = product.ProductTemplateId,
                AdminComment = product.AdminComment,
                ShowOnHomePage = product.ShowOnHomePage,
                HomePageDisplayOrder = product.HomePageDisplayOrder,
                MetaKeywords = product.MetaKeywords,
                MetaDescription = product.MetaDescription,
                MetaTitle = product.MetaTitle,
                AllowCustomerReviews = product.AllowCustomerReviews,
                LimitedToStores = product.LimitedToStores,
                Sku = product.Sku,
                ManufacturerPartNumber = product.ManufacturerPartNumber,
                Gtin = product.Gtin,
                IsGiftCard = product.IsGiftCard,
                GiftCardType = product.GiftCardType,
                RequireOtherProducts = product.RequireOtherProducts,
                RequiredProductIds = product.RequiredProductIds,
                AutomaticallyAddRequiredProducts = product.AutomaticallyAddRequiredProducts,
                IsDownload = product.IsDownload,
                DownloadId = downloadId,
                UnlimitedDownloads = product.UnlimitedDownloads,
                MaxNumberOfDownloads = product.MaxNumberOfDownloads,
                DownloadExpirationDays = product.DownloadExpirationDays,
                DownloadActivationType = product.DownloadActivationType,
                HasSampleDownload = product.HasSampleDownload,
                SampleDownloadId = sampleDownloadId,
                HasUserAgreement = product.HasUserAgreement,
                UserAgreementText = product.UserAgreementText,
                IsRecurring = product.IsRecurring,
                RecurringCycleLength = product.RecurringCycleLength,
                RecurringCyclePeriod = product.RecurringCyclePeriod,
                RecurringTotalCycles = product.RecurringTotalCycles,
                IsShipEnabled = product.IsShipEnabled,
                IsFreeShipping = product.IsFreeShipping,
                AdditionalShippingCharge = product.AdditionalShippingCharge,
                IsEsd = product.IsEsd,
                IsTaxExempt = product.IsTaxExempt,
                TaxCategoryId = product.TaxCategoryId,
                ManageInventoryMethod = product.ManageInventoryMethod,
                StockQuantity = product.StockQuantity,
                DisplayStockAvailability = product.DisplayStockAvailability,
                DisplayStockQuantity = product.DisplayStockQuantity,
                MinStockQuantity = product.MinStockQuantity,
                LowStockActivityId = product.LowStockActivityId,
                NotifyAdminForQuantityBelow = product.NotifyAdminForQuantityBelow,
                BackorderMode = product.BackorderMode,
                AllowBackInStockSubscriptions = product.AllowBackInStockSubscriptions,
                OrderMinimumQuantity = product.OrderMinimumQuantity,
                OrderMaximumQuantity = product.OrderMaximumQuantity,
                AllowedQuantities = product.AllowedQuantities,
                DisableBuyButton = product.DisableBuyButton,
                DisableWishlistButton = product.DisableWishlistButton,
                AvailableForPreOrder = product.AvailableForPreOrder,
                CallForPrice = product.CallForPrice,
                Price = product.Price,
                OldPrice = product.OldPrice,
                ProductCost = product.ProductCost,
                SpecialPrice = product.SpecialPrice,
                SpecialPriceStartDateTimeUtc = product.SpecialPriceStartDateTimeUtc,
                SpecialPriceEndDateTimeUtc = product.SpecialPriceEndDateTimeUtc,
                CustomerEntersPrice = product.CustomerEntersPrice,
                MinimumCustomerEnteredPrice = product.MinimumCustomerEnteredPrice,
                MaximumCustomerEnteredPrice = product.MaximumCustomerEnteredPrice,
                LowestAttributeCombinationPrice = product.LowestAttributeCombinationPrice,
                Weight = product.Weight,
                Length = product.Length,
                Width = product.Width,
                Height = product.Height,
                AvailableStartDateTimeUtc = product.AvailableStartDateTimeUtc,
                AvailableEndDateTimeUtc = product.AvailableEndDateTimeUtc,
                DisplayOrder = product.DisplayOrder,
                Published = isPublished,
                Deleted = product.Deleted,
                CreatedOnUtc = utcNow,
                UpdatedOnUtc = utcNow,
                DeliveryTimeId = product.DeliveryTimeId,
                QuantityUnitId = product.QuantityUnitId,
                BasePriceEnabled = product.BasePriceEnabled,
                BasePriceMeasureUnit = product.BasePriceMeasureUnit,
                BasePriceAmount = product.BasePriceAmount,
                BasePriceBaseAmount = product.BasePriceBaseAmount,
                BundleTitleText = product.BundleTitleText,
                BundlePerItemShipping = product.BundlePerItemShipping,
                BundlePerItemPricing = product.BundlePerItemPricing,
                BundlePerItemShoppingCart = product.BundlePerItemShoppingCart
            };

            _productService.InsertProduct(productCopy);

            //search engine name
            _urlRecordService.SaveSlug(productCopy, productCopy.ValidateSeName("", productCopy.Name, true), 0);

            var languages = _languageService.GetAllLanguages(true);

            //localization
            foreach (var lang in languages)
            {
                var name = product.GetLocalized(x => x.Name, lang.Id, false, false);
                if (!String.IsNullOrEmpty(name))
                    _localizedEntityService.SaveLocalizedValue(productCopy, x => x.Name, name, lang.Id);

                var shortDescription = product.GetLocalized(x => x.ShortDescription, lang.Id, false, false);
                if (!String.IsNullOrEmpty(shortDescription))
                    _localizedEntityService.SaveLocalizedValue(productCopy, x => x.ShortDescription, shortDescription, lang.Id);

                var fullDescription = product.GetLocalized(x => x.FullDescription, lang.Id, false, false);
                if (!String.IsNullOrEmpty(fullDescription))
                    _localizedEntityService.SaveLocalizedValue(productCopy, x => x.FullDescription, fullDescription, lang.Id);

                var metaKeywords = product.GetLocalized(x => x.MetaKeywords, lang.Id, false, false);
                if (!String.IsNullOrEmpty(metaKeywords))
                    _localizedEntityService.SaveLocalizedValue(productCopy, x => x.MetaKeywords, metaKeywords, lang.Id);

                var metaDescription = product.GetLocalized(x => x.MetaDescription, lang.Id, false, false);
                if (!String.IsNullOrEmpty(metaDescription))
                    _localizedEntityService.SaveLocalizedValue(productCopy, x => x.MetaDescription, metaDescription, lang.Id);

                var metaTitle = product.GetLocalized(x => x.MetaTitle, lang.Id, false, false);
                if (!String.IsNullOrEmpty(metaTitle))
                    _localizedEntityService.SaveLocalizedValue(productCopy, x => x.MetaTitle, metaTitle, lang.Id);

                var bundleTitleText = product.GetLocalized(x => x.BundleTitleText, lang.Id, false, false);
                if (!String.IsNullOrEmpty(bundleTitleText))
                    _localizedEntityService.SaveLocalizedValue(productCopy, x => x.BundleTitleText, bundleTitleText, lang.Id);

                //search engine name
                _urlRecordService.SaveSlug(productCopy, productCopy.ValidateSeName("", name, false), lang.Id);

            }

            // product pictures
            if (copyImages)
            {
                foreach (var productPicture in product.ProductPictures)
                {
                    var picture = productPicture.Picture;
                    var pictureCopy = _pictureService.InsertPicture(
                        _pictureService.LoadPictureBinary(picture),
                        picture.MimeType,
                        _pictureService.GetPictureSeName(newName),
                        true,
                        false,
                        false);

                    _productService.InsertProductPicture(new ProductPicture
                    {
                        ProductId = productCopy.Id,
                        PictureId = pictureCopy.Id,
                        DisplayOrder = productPicture.DisplayOrder
                    });
                }
            }

            // product <-> categories mappings
            foreach (var productCategory in product.ProductCategories)
            {
                var productCategoryCopy = new ProductCategory
                {
                    ProductId = productCopy.Id,
                    CategoryId = productCategory.CategoryId,
                    IsFeaturedProduct = productCategory.IsFeaturedProduct,
                    DisplayOrder = productCategory.DisplayOrder
                };

                _categoryService.InsertProductCategory(productCategoryCopy);
            }

            // product <-> manufacturers mappings
            foreach (var productManufacturers in product.ProductManufacturers)
            {
                var productManufacturerCopy = new ProductManufacturer
                {
                    ProductId = productCopy.Id,
                    ManufacturerId = productManufacturers.ManufacturerId,
                    IsFeaturedProduct = productManufacturers.IsFeaturedProduct,
                    DisplayOrder = productManufacturers.DisplayOrder
                };

                _manufacturerService.InsertProductManufacturer(productManufacturerCopy);
            }

            // product <-> releated products mappings
            foreach (var relatedProduct in _productService.GetRelatedProductsByProductId1(product.Id, true))
            {
                _productService.InsertRelatedProduct(new RelatedProduct
                {
                    ProductId1 = productCopy.Id,
                    ProductId2 = relatedProduct.ProductId2,
                    DisplayOrder = relatedProduct.DisplayOrder
                });
            }

            // product <-> cross sells mappings
            foreach (var csProduct in _productService.GetCrossSellProductsByProductId1(product.Id, true))
            {
                _productService.InsertCrossSellProduct(new CrossSellProduct
                {
                    ProductId1 = productCopy.Id,
                    ProductId2 = csProduct.ProductId2,
                });
            }

            // product specifications
            foreach (var productSpecificationAttribute in product.ProductSpecificationAttributes)
            {
                var psaCopy = new ProductSpecificationAttribute
                {
                    ProductId = productCopy.Id,
                    SpecificationAttributeOptionId = productSpecificationAttribute.SpecificationAttributeOptionId,
                    AllowFiltering = productSpecificationAttribute.AllowFiltering,
                    ShowOnProductPage = productSpecificationAttribute.ShowOnProductPage,
                    DisplayOrder = productSpecificationAttribute.DisplayOrder
                };

                _specificationAttributeService.InsertProductSpecificationAttribute(psaCopy);
            }

            //store mapping
            var selectedStoreIds = _storeMappingService.GetStoresIdsWithAccess(product);
            foreach (var id in selectedStoreIds)
            {
                _storeMappingService.InsertStoreMapping(productCopy, id);
            }

            // product <-> attributes mappings
            var associatedAttributes = new Dictionary<int, int>();
            var associatedAttributeValues = new Dictionary<int, int>();

            foreach (var productVariantAttribute in _productAttributeService.GetProductVariantAttributesByProductId(product.Id))
            {
                var productVariantAttributeCopy = new ProductVariantAttribute
                {
                    ProductId = productCopy.Id,
                    ProductAttributeId = productVariantAttribute.ProductAttributeId,
                    TextPrompt = productVariantAttribute.TextPrompt,
                    IsRequired = productVariantAttribute.IsRequired,
                    AttributeControlTypeId = productVariantAttribute.AttributeControlTypeId,
                    DisplayOrder = productVariantAttribute.DisplayOrder
                };

                _productAttributeService.InsertProductVariantAttribute(productVariantAttributeCopy);
                //save associated value (used for combinations copying)
                associatedAttributes.Add(productVariantAttribute.Id, productVariantAttributeCopy.Id);

                // product variant attribute values
                var productVariantAttributeValues = _productAttributeService.GetProductVariantAttributeValues(productVariantAttribute.Id);

                foreach (var productVariantAttributeValue in productVariantAttributeValues)
                {
                    var pvavCopy = new ProductVariantAttributeValue
                    {
                        ProductVariantAttributeId = productVariantAttributeCopy.Id,
                        Name = productVariantAttributeValue.Name,
                        ColorSquaresRgb = productVariantAttributeValue.ColorSquaresRgb,
                        PriceAdjustment = productVariantAttributeValue.PriceAdjustment,
                        WeightAdjustment = productVariantAttributeValue.WeightAdjustment,
                        IsPreSelected = productVariantAttributeValue.IsPreSelected,
                        DisplayOrder = productVariantAttributeValue.DisplayOrder,
                        ValueTypeId = productVariantAttributeValue.ValueTypeId,
                        LinkedProductId = productVariantAttributeValue.LinkedProductId,
                        Quantity = productVariantAttributeValue.Quantity,
                    };

                    _productAttributeService.InsertProductVariantAttributeValue(pvavCopy);

                    //save associated value (used for combinations copying)
                    associatedAttributeValues.Add(productVariantAttributeValue.Id, pvavCopy.Id);

                    //localization
                    foreach (var lang in languages)
                    {
                        var name = productVariantAttributeValue.GetLocalized(x => x.Name, lang.Id, false, false);
                        if (!String.IsNullOrEmpty(name))
                            _localizedEntityService.SaveLocalizedValue(pvavCopy, x => x.Name, name, lang.Id);
                    }
                }
            }

            // attribute combinations
            using (var scope = new DbContextScope(lazyLoading: false, forceNoTracking: false))
            {
                scope.LoadCollection(product, (Product p) => p.ProductVariantAttributeCombinations);
            }

            foreach (var combination in product.ProductVariantAttributeCombinations)
            {
                //generate new AttributesXml according to new value IDs
                string newAttributesXml = "";
                var parsedProductVariantAttributes = _productAttributeParser.ParseProductVariantAttributes(combination.AttributesXml);
                foreach (var oldPva in parsedProductVariantAttributes)
                {
                    if (associatedAttributes.ContainsKey(oldPva.Id))
                    {
                        int newPvaId = associatedAttributes[oldPva.Id];
                        var newPva = _productAttributeService.GetProductVariantAttributeById(newPvaId);
                        if (newPva != null)
                        {
                            var oldPvaValuesStr = _productAttributeParser.ParseValues(combination.AttributesXml, oldPva.Id);
                            foreach (var oldPvaValueStr in oldPvaValuesStr)
                            {
                                if (newPva.ShouldHaveValues())
                                {
                                    //attribute values
                                    int oldPvaValue = int.Parse(oldPvaValueStr);
                                    if (associatedAttributeValues.ContainsKey(oldPvaValue))
                                    {
                                        int newPvavId = associatedAttributeValues[oldPvaValue];
                                        var newPvav = _productAttributeService.GetProductVariantAttributeValueById(newPvavId);
                                        if (newPvav != null)
                                        {
                                            newAttributesXml = _productAttributeParser.AddProductAttribute(newAttributesXml, newPva, newPvav.Id.ToString());
                                        }
                                    }
                                }
                                else
                                {
                                    //just a text
                                    newAttributesXml = _productAttributeParser.AddProductAttribute(newAttributesXml, newPva, oldPvaValueStr);
                                }
                            }
                        }
                    }
                }
                var combinationCopy = new ProductVariantAttributeCombination
                {
                    ProductId = productCopy.Id,
                    AttributesXml = newAttributesXml,
                    StockQuantity = combination.StockQuantity,
                    AllowOutOfStockOrders = combination.AllowOutOfStockOrders,

                    // SmartStore extension
                    Sku = combination.Sku,
                    Gtin = combination.Gtin,
                    ManufacturerPartNumber = combination.ManufacturerPartNumber,
                    Price = combination.Price,
                    AssignedPictureIds = copyImages ? combination.AssignedPictureIds : null,
                    Length = combination.Length,
                    Width = combination.Width,
                    Height = combination.Height,
                    BasePriceAmount = combination.BasePriceAmount,
                    BasePriceBaseAmount = combination.BasePriceBaseAmount,
                    DeliveryTimeId = combination.DeliveryTimeId,
                    QuantityUnitId = combination.QuantityUnitId,
                    IsActive = combination.IsActive
                    //IsDefaultCombination = combination.IsDefaultCombination
                };
                _productAttributeService.InsertProductVariantAttributeCombination(combinationCopy);
            }

            // tier prices
            foreach (var tierPrice in product.TierPrices)
            {
                _productService.InsertTierPrice(new TierPrice
                {
                    ProductId = productCopy.Id,
                    StoreId = tierPrice.StoreId,
                    CustomerRoleId = tierPrice.CustomerRoleId,
                    Quantity = tierPrice.Quantity,
                    Price = tierPrice.Price
                });
            }

            // product <-> discounts mapping
            foreach (var discount in product.AppliedDiscounts)
            {
                productCopy.AppliedDiscounts.Add(discount);
                _productService.UpdateProduct(productCopy);
            }

            // update "HasTierPrices" and "HasDiscountsApplied" properties
            _productService.UpdateHasTierPricesProperty(productCopy);
            _productService.UpdateLowestAttributeCombinationPriceProperty(productCopy);
            _productService.UpdateHasDiscountsApplied(productCopy);

            // associated products
            if (copyAssociatedProducts && product.ProductType != ProductType.BundledProduct)
            {
                var searchContext = new ProductSearchContext
                {
                    OrderBy = ProductSortingEnum.Position,
                    ParentGroupedProductId = product.Id,
                    PageSize = int.MaxValue,
                    ShowHidden = true
                };

                string copyOf = _localizationService.GetResource("Admin.Common.CopyOf");
                var associatedProducts = _productService.SearchProducts(searchContext);

                foreach (var associatedProduct in associatedProducts)
                {
                    var associatedProductCopy = CopyProduct(associatedProduct, string.Format("{0} {1}", copyOf, associatedProduct.Name), isPublished, copyImages, false);
                    associatedProductCopy.ParentGroupedProductId = productCopy.Id;

                    _productService.UpdateProduct(productCopy);
                }
            }

            // bundled products
            var bundledItems = _productService.GetBundleItems(product.Id, true);

            foreach (var bundleItem in bundledItems)
            {
                var newBundleItem = bundleItem.Item.Clone();
                newBundleItem.BundleProductId = productCopy.Id;
                newBundleItem.CreatedOnUtc = utcNow;
                newBundleItem.UpdatedOnUtc = utcNow;

                _productService.InsertBundleItem(newBundleItem);

                foreach (var itemFilter in bundleItem.Item.AttributeFilters)
                {
                    var newItemFilter = itemFilter.Clone();
                    newItemFilter.BundleItemId = newBundleItem.Id;

                    _productAttributeService.InsertProductBundleItemAttributeFilter(newItemFilter);
                }
            }

            return productCopy;
        }
 public static ProductVariantAttributeCombination ToEntity(this ProductVariantAttributeCombinationModel model, ProductVariantAttributeCombination destination)
 {
     return Mapper.Map(model, destination);
 }
        public virtual void CreateAllProductVariantAttributeCombinations(Product product)
        {
            // delete all existing combinations
            foreach(var itm in product.ProductVariantAttributeCombinations)
            {
                DeleteProductVariantAttributeCombination(itm);
            }

            var attributes = GetProductVariantAttributesByProductId(product.Id);
            if (attributes == null || attributes.Count <= 0)
                return;

            var toCombine = new List<List<ProductVariantAttributeValue>>();
            var resultMatrix = new List<List<ProductVariantAttributeValue>>();
            var tmp = new List<ProductVariantAttributeValue>();

            foreach (var attr in attributes)
            {
                var attributeValues = attr.ProductVariantAttributeValues.ToList();
                if (attributeValues.Count > 0)
                    toCombine.Add(attributeValues);
            }

            if (toCombine.Count > 0)
            {
                CombineAll(toCombine, resultMatrix, 0, tmp);

                foreach (var values in resultMatrix)
                {
                    string attrXml = "";
                    foreach (var x in values)
                    {
                        attrXml = attributes[values.IndexOf(x)].AddProductAttribute(attrXml, x.Id.ToString());
                    }

                    var combination = new ProductVariantAttributeCombination
                    {
                        ProductId = product.Id,
                        AttributesXml = attrXml,
                        StockQuantity = 10000,
                        AllowOutOfStockOrders = true,
                        IsActive = true
                    };

                    _productVariantAttributeCombinationRepository.Insert(combination);
                    _eventPublisher.EntityInserted(combination);
                }
            }

            //foreach (var y in resultMatrix) {
            //	StringBuilder sb = new StringBuilder();
            //	foreach (var x in y) {
            //		sb.AppendFormat("{0} ", x.Name);
            //	}
            //	sb.ToString().Dump();
            //}
        }
        public virtual void UpdateProductVariantAttributeCombination(ProductVariantAttributeCombination combination)
        {
            if (combination == null)
                throw new ArgumentNullException("combination");

            //if (combination.IsDefaultCombination)
            //{
            //	EnsureSingleDefaultVariant(combination);
            //}
            //else
            //{
            //	// check if it was default before modification...
            //	// but make it Type-Safe (resistant to code refactoring ;-))
            //	Expression<Func<ProductVariantAttributeCombination, bool>> expr = x => x.IsDefaultCombination;
            //	string propertyToCheck = expr.ExtractPropertyInfo().Name;

            //	object originalValue = null;
            //	if (_productVariantAttributeCombinationRepository.GetModifiedProperties(combination).TryGetValue(propertyToCheck, out originalValue))
            //	{
            //		bool wasDefault = (bool)originalValue;
            //		if (wasDefault)
            //		{
            //			// we can't uncheck the default variant within a combination list,
            //			// we would't have a default combination anymore.
            //			combination.IsDefaultCombination = true;
            //		}
            //	}
            //}

            _productVariantAttributeCombinationRepository.Update(combination);

            //event notification
            _eventPublisher.EntityUpdated(combination);
        }
        public virtual void InsertProductVariantAttributeCombination(ProductVariantAttributeCombination combination)
        {
            if (combination == null)
                throw new ArgumentNullException("combination");

            //if (combination.IsDefaultCombination)
            //{
            //	EnsureSingleDefaultVariant(combination);
            //}

            _productVariantAttributeCombinationRepository.Insert(combination);

            //event notification
            _eventPublisher.EntityInserted(combination);
        }
        public virtual void DeleteProductVariantAttributeCombination(ProductVariantAttributeCombination combination)
        {
            if (combination == null)
                throw new ArgumentNullException("combination");

            _productVariantAttributeCombinationRepository.Delete(combination);

            //event notification
            _eventPublisher.EntityDeleted(combination);
        }
        public virtual IList<string> GetShoppingCartItemAttributeWarnings(
			Customer customer, 
			ShoppingCartType shoppingCartType,
			Product product, 
			string selectedAttributes, 
			int quantity = 1, 
			ProductBundleItem bundleItem = null,
			ProductVariantAttributeCombination combination = null)
        {
            Guard.ArgumentNotNull(() => product);

            var warnings = new List<string>();

            if (product.ProductType == ProductType.BundledProduct)
                return warnings;	// customer cannot select anything cause bundles have no attributes

            if (bundleItem != null && !bundleItem.BundleProduct.BundlePerItemPricing)
                return warnings;	// customer cannot select anything... selectedAttribute is always empty

            //selected attributes
            var pva1Collection = _productAttributeParser.ParseProductVariantAttributes(selectedAttributes);
            foreach (var pva1 in pva1Collection)
            {
                var pv1 = pva1.Product;

                if (pv1 == null || pv1.Id != product.Id)
                {
                    warnings.Add(_localizationService.GetResource("ShoppingCart.AttributeError"));
                    return warnings;
                }
            }

            //existing product attributes
            var pva2Collection = product.ProductVariantAttributes;
            foreach (var pva2 in pva2Collection)
            {
                if (pva2.IsRequired)
                {
                    bool found = false;
                    //selected product attributes
                    foreach (var pva1 in pva1Collection)
                    {
                        if (pva1.Id == pva2.Id)
                        {
                            var pvaValuesStr = _productAttributeParser.ParseValues(selectedAttributes, pva1.Id);
                            foreach (string str1 in pvaValuesStr)
                            {
                                if (!String.IsNullOrEmpty(str1.Trim()))
                                {
                                    found = true;
                                    break;
                                }
                            }
                        }
                    }

                    if (!found && bundleItem != null && bundleItem.FilterAttributes && !bundleItem.AttributeFilters.Any(x => x.AttributeId == pva2.ProductAttributeId))
                    {
                        found = true;	// attribute is filtered out on bundle item level... it cannot be selected by customer
                    }

                    if (!found)
                    {
                        warnings.Add(string.Format(_localizationService.GetResource("ShoppingCart.SelectAttribute"),
                            pva2.TextPrompt.HasValue() ? pva2.TextPrompt : pva2.ProductAttribute.GetLocalized(a => a.Name)));
                    }
                }
            }

            // check if there is a selected attribute combination and if it is active
            if (warnings.Count == 0 && selectedAttributes.HasValue())
            {
                if (combination == null)
                {
                    combination = _productAttributeParser.FindProductVariantAttributeCombination(product.Id, selectedAttributes, pva1Collection);
                }

                if (combination != null && !combination.IsActive)
                {
                    warnings.Add(_localizationService.GetResource("ShoppingCart.NotAvailable"));
                }
            }

            if (warnings.Count == 0)
            {
                var pvaValues = _productAttributeParser.ParseProductVariantAttributeValues(selectedAttributes);
                foreach (var pvaValue in pvaValues)
                {
                    if (pvaValue.ValueType ==  ProductVariantAttributeValueType.ProductLinkage)
                    {
                        var linkedProduct = _productService.GetProductById(pvaValue.LinkedProductId);
                        if (linkedProduct != null)
                        {
                            var linkageWarnings = GetShoppingCartItemWarnings(customer, shoppingCartType, linkedProduct, _storeContext.CurrentStore.Id,
                                "", decimal.Zero, quantity * pvaValue.Quantity, false, true, true, true, true);

                            foreach (var linkageWarning in linkageWarnings)
                            {
                                string msg = _localizationService.GetResource("ShoppingCart.ProductLinkageAttributeWarning").FormatWith(
                                    pvaValue.ProductVariantAttribute.ProductAttribute.GetLocalized(a => a.Name),
                                    pvaValue.GetLocalized(a => a.Name),
                                    linkageWarning);

                                warnings.Add(msg);
                            }
                        }
                        else
                        {
                            string msg = _localizationService.GetResource("ShoppingCart.ProductLinkageProductNotLoading").FormatWith(pvaValue.LinkedProductId);
                            warnings.Add(msg);
                        }
                    }
                }
            }

            return warnings;
        }