public void Can_save_and_load_productVariantAttributeCombination()
        {
            var pvac = new ProductVariantAttributeCombination
                       {
                           AttributesXml = "Some XML",
                           StockQuantity = 2,
                           AllowOutOfStockOrders = true,
                           ProductVariant = new ProductVariant
                                            {
                                                Name = "Product variant name 1",
                                                CreatedOnUtc = new DateTime(2010, 01, 03),
                                                UpdatedOnUtc = new DateTime(2010, 01, 04),
                                                Product = new Product()
                                                          {
                                                              Name = "Name 1",
                                                              Published = true,
                                                              Deleted = false,
                                                              CreatedOnUtc = new DateTime(2010, 01, 01),
                                                              UpdatedOnUtc = new DateTime(2010, 01, 02)
                                                          }
                                            }
                       };

            var fromDb = SaveAndLoadEntity(pvac);
            fromDb.ShouldNotBeNull();
            fromDb.AttributesXml.ShouldEqual("Some XML");
            fromDb.StockQuantity.ShouldEqual(2);
            fromDb.AllowOutOfStockOrders.ShouldEqual(true);
        }
        /// <summary>
        /// Updates a product variant attribute combination
        /// </summary>
        /// <param name="combination">Product variant attribute combination</param>
        public virtual void UpdateProductVariantAttributeCombination(ProductVariantAttributeCombination combination)
        {
            if (combination == null)
                throw new ArgumentNullException("combination");

            _productVariantAttributeCombinationRepository.Update(combination);

            //event notification
            _eventPublisher.EntityUpdated(combination);
        }
        /// <summary>
        /// Create a copy of product variant with all depended data
        /// </summary>
        /// <param name="productVariant">The product variant to copy</param>
        /// <param name="productId">The product identifier</param>
        /// <param name="newName">The name of product variant duplicate</param>
        /// <param name="isPublished">A value indicating whether the product variant duplicate should be published</param>
        /// <param name="copyImage">A value indicating whether the product variant image should be copied</param>
        /// <returns>Product variant copy</returns>
        public virtual ProductVariant CopyProductVariant(ProductVariant productVariant, int productId,
            string newName, bool isPublished, bool copyImage)
        {
            if (productVariant == null)
                throw new ArgumentNullException("productVariant");

            var languages = _languageService.GetAllLanguages(true);

            // product variant picture
            int pictureId = 0;
            if (copyImage)
            {
                var picture = _pictureService.GetPictureById(productVariant.PictureId);
                if (picture != null)
                {
                    var pictureCopy = _pictureService.InsertPicture(
                        _pictureService.LoadPictureBinary(picture),
                        picture.MimeType,
                        _pictureService.GetPictureSeName(productVariant.Name),
                        true);
                    pictureId = pictureCopy.Id;
                }
            }

            // product variant download & sample download
            int downloadId = productVariant.DownloadId;
            int sampleDownloadId = productVariant.SampleDownloadId;
            if (productVariant.IsDownload)
            {
                var download = _downloadService.GetDownloadById(productVariant.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 (productVariant.HasSampleDownload)
                {
                    var sampleDownload = _downloadService.GetDownloadById(productVariant.SampleDownloadId);
                    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 variant
            var productVariantCopy = new ProductVariant()
            {
                ProductId = productId,
                Name = newName,
                Sku = productVariant.Sku,
                Description = productVariant.Description,
                AdminComment = productVariant.AdminComment,
                ManufacturerPartNumber = productVariant.ManufacturerPartNumber,
                Gtin = productVariant.Gtin,
                IsGiftCard = productVariant.IsGiftCard,
                GiftCardType = productVariant.GiftCardType,
                RequireOtherProducts = productVariant.RequireOtherProducts,
                RequiredProductVariantIds = productVariant.RequiredProductVariantIds,
                AutomaticallyAddRequiredProductVariants = productVariant.AutomaticallyAddRequiredProductVariants,
                IsDownload = productVariant.IsDownload,
                DownloadId = downloadId,
                UnlimitedDownloads = productVariant.UnlimitedDownloads,
                MaxNumberOfDownloads = productVariant.MaxNumberOfDownloads,
                DownloadExpirationDays = productVariant.DownloadExpirationDays,
                DownloadActivationType = productVariant.DownloadActivationType,
                HasSampleDownload = productVariant.HasSampleDownload,
                SampleDownloadId = sampleDownloadId,
                HasUserAgreement = productVariant.HasUserAgreement,
                UserAgreementText = productVariant.UserAgreementText,
                IsRecurring = productVariant.IsRecurring,
                RecurringCycleLength = productVariant.RecurringCycleLength,
                RecurringCyclePeriod = productVariant.RecurringCyclePeriod,
                RecurringTotalCycles = productVariant.RecurringTotalCycles,
                IsShipEnabled = productVariant.IsShipEnabled,
                IsFreeShipping = productVariant.IsFreeShipping,
                AdditionalShippingCharge = productVariant.AdditionalShippingCharge,
                IsTaxExempt = productVariant.IsTaxExempt,
                TaxCategoryId = productVariant.TaxCategoryId,
                ManageInventoryMethod = productVariant.ManageInventoryMethod,
                StockQuantity = productVariant.StockQuantity,
                DisplayStockAvailability = productVariant.DisplayStockAvailability,
                DisplayStockQuantity = productVariant.DisplayStockQuantity,
                MinStockQuantity = productVariant.MinStockQuantity,
                LowStockActivityId = productVariant.LowStockActivityId,
                NotifyAdminForQuantityBelow = productVariant.NotifyAdminForQuantityBelow,
                BackorderMode = productVariant.BackorderMode,
                AllowBackInStockSubscriptions = productVariant.AllowBackInStockSubscriptions,
                OrderMinimumQuantity = productVariant.OrderMinimumQuantity,
                OrderMaximumQuantity = productVariant.OrderMaximumQuantity,
                AllowedQuantities = productVariant.AllowedQuantities,
                DisableBuyButton = productVariant.DisableBuyButton,
                DisableWishlistButton = productVariant.DisableWishlistButton,
                CallForPrice = productVariant.CallForPrice,
                Price = productVariant.Price,
                OldPrice = productVariant.OldPrice,
                ProductCost = productVariant.ProductCost,
                SpecialPrice = productVariant.SpecialPrice,
                SpecialPriceStartDateTimeUtc = productVariant.SpecialPriceStartDateTimeUtc,
                SpecialPriceEndDateTimeUtc = productVariant.SpecialPriceEndDateTimeUtc,
                CustomerEntersPrice = productVariant.CustomerEntersPrice,
                MinimumCustomerEnteredPrice = productVariant.MinimumCustomerEnteredPrice,
                MaximumCustomerEnteredPrice = productVariant.MaximumCustomerEnteredPrice,
                Weight = productVariant.Weight,
                Length = productVariant.Length,
                Width = productVariant.Width,
                Height = productVariant.Height,
                PictureId = pictureId,
                AvailableStartDateTimeUtc = productVariant.AvailableStartDateTimeUtc,
                AvailableEndDateTimeUtc = productVariant.AvailableEndDateTimeUtc,
                Published = isPublished,
                Deleted = productVariant.Deleted,
                DisplayOrder = productVariant.DisplayOrder,
                CreatedOnUtc = DateTime.UtcNow,
                UpdatedOnUtc = DateTime.UtcNow
            };

            _productService.InsertProductVariant(productVariantCopy);

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

                var description = productVariant.GetLocalized(x => x.Description, lang.Id, false, false);
                if (!String.IsNullOrEmpty(description))
                    _localizedEntityService.SaveLocalizedValue(productVariantCopy, x => x.Description, description, lang.Id);
            }

            // product variant <-> attributes mappings
            var associatedAttributes = new Dictionary<int, int>();
            var associatedAttributeValues = new Dictionary<int, int>();
            foreach (var productVariantAttribute in _productAttributeService.GetProductVariantAttributesByProductVariantId(productVariant.Id))
            {
                var productVariantAttributeCopy = new ProductVariantAttribute()
                {
                    ProductVariantId = productVariantCopy.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
                    };
                    _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);
                    }
                }
            }
            foreach (var combination in _productAttributeService.GetAllProductVariantAttributeCombinations(productVariant.Id))
            {
                //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()
                {
                    ProductVariantId = productVariantCopy.Id,
                    AttributesXml = newAttributesXml,
                    StockQuantity = combination.StockQuantity,
                    AllowOutOfStockOrders = combination.AllowOutOfStockOrders,
                    Sku = combination.Sku,
                    ManufacturerPartNumber = combination.ManufacturerPartNumber,
                    Gtin = combination.Gtin
                };
                _productAttributeService.InsertProductVariantAttributeCombination(combinationCopy);
            }

            // product variant tier prices
            foreach (var tierPrice in productVariant.TierPrices)
            {
                _productService.InsertTierPrice(
                    new TierPrice()
                    {
                        ProductVariantId = productVariantCopy.Id,
                        CustomerRoleId = tierPrice.CustomerRoleId,
                        Quantity = tierPrice.Quantity,
                        Price = tierPrice.Price
                    });
            }

            // product variant <-> discounts mapping
            foreach (var discount in productVariant.AppliedDiscounts)
            {
                productVariantCopy.AppliedDiscounts.Add(discount);
                _productService.UpdateProductVariant(productVariantCopy);
            }

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

            return productVariantCopy;
        }