Пример #1
0
        /// <summary>
        /// Creates a product URL including variant query string.
        /// </summary>
        /// <param name="helper">Product URL helper.</param>
        /// <param name="productId">Product identifier.</param>
        /// <param name="productSlug">Product URL slug.</param>
        /// <param name="bundleItemId">Bundle item identifier. 0 if it's not a bundle item.</param>
        /// <param name="variantValues">Variant values<./param>
        /// <returns>Product URL.</returns>
        public static string GetProductUrl(
            this ProductUrlHelper helper,
            int productId,
            string productSlug,
            int bundleItemId,
            params ProductVariantAttributeValue[] variantValues)
        {
            Guard.NotNull(helper, nameof(helper));
            Guard.NotZero(productId, nameof(productId));

            var query = new ProductVariantQuery();

            foreach (var value in variantValues)
            {
                var attribute = value.ProductVariantAttribute;

                query.AddVariant(new ProductVariantQueryItem(value.Id.ToString())
                {
                    ProductId          = productId,
                    BundleItemId       = bundleItemId,
                    AttributeId        = attribute.ProductAttributeId,
                    VariantAttributeId = attribute.Id,
                    Alias      = attribute.ProductAttribute.Alias,
                    ValueAlias = value.Alias
                });
            }

            return(helper.GetProductUrl(productSlug, query));
        }
Пример #2
0
        /// <summary>
        /// Deserializes attributes XML into a product variant query
        /// </summary>
        /// <param name="query">Product variant query</param>
        /// <param name="productId">Product identifier</param>
        /// <param name="bundleItemId">Bundle item identifier</param>
        /// <param name="attributesXml">XML formatted attributes</param>
        public virtual void DeserializeQuery(ProductVariantQuery query, int productId, string attributesXml, int bundleItemId = 0)
        {
            Guard.NotNull(query, nameof(query));
            Guard.NotZero(productId, nameof(productId));

            if (attributesXml.IsEmpty() || productId == 0)
            {
                return;
            }

            var attributeMap = _productAttributeParser.DeserializeProductVariantAttributes(attributesXml);
            var attributes   = _productAttributeService.GetProductVariantAttributesByIds(attributeMap.Keys);

            foreach (var attribute in attributes)
            {
                foreach (var originalValue in attributeMap[attribute.Id])
                {
                    var      value = originalValue;
                    DateTime?date  = null;

                    if (attribute.AttributeControlType == AttributeControlType.Datepicker)
                    {
                        date = originalValue.ToDateTime(new string[] { "D" }, CultureInfo.CurrentCulture, DateTimeStyles.None, null);
                        if (date == null)
                        {
                            continue;
                        }

                        value = string.Join("-", date.Value.Year, date.Value.Month, date.Value.Day);
                    }

                    var queryItem = new ProductVariantQueryItem(value);
                    queryItem.ProductId          = productId;
                    queryItem.BundleItemId       = bundleItemId;
                    queryItem.AttributeId        = attribute.ProductAttributeId;
                    queryItem.VariantAttributeId = attribute.Id;
                    queryItem.Alias  = _catalogSearchQueryAliasMapper.Value.GetVariantAliasById(attribute.ProductAttributeId, _languageId);
                    queryItem.Date   = date;
                    queryItem.IsFile = attribute.AttributeControlType == AttributeControlType.FileUpload;
                    queryItem.IsText = attribute.AttributeControlType == AttributeControlType.TextBox || attribute.AttributeControlType == AttributeControlType.MultilineTextbox;

                    if (attribute.ShouldHaveValues())
                    {
                        queryItem.ValueAlias = _catalogSearchQueryAliasMapper.Value.GetVariantOptionAliasById(value.ToInt(), _languageId);
                    }

                    query.AddVariant(queryItem);
                }
            }
        }
        /// <summary>
        /// Deserializes attributes XML into a product variant query
        /// </summary>
        /// <param name="query">Product variant query</param>
        /// <param name="productId">Product identifier</param>
        /// <param name="bundleItemId">Bundle item identifier</param>
        /// <param name="attributesXml">XML formatted attributes</param>
        public virtual void DeserializeQuery(ProductVariantQuery query, int productId, string attributesXml, int bundleItemId = 0)
        {
            Guard.NotNull(query, nameof(query));
            Guard.NotZero(productId, nameof(productId));

            if (attributesXml.HasValue() && productId != 0)
            {
                var attributeValues = _productAttributeParser.ParseProductVariantAttributeValues(attributesXml).ToList();

                foreach (var value in attributeValues)
                {
                    query.AddVariant(new ProductVariantQueryItem(value.Id.ToString())
                    {
                        ProductId          = productId,
                        BundleItemId       = bundleItemId,
                        AttributeId        = value.ProductVariantAttribute.ProductAttributeId,
                        VariantAttributeId = value.ProductVariantAttributeId,
                        Alias      = value.ProductVariantAttribute.ProductAttribute.Alias,
                        ValueAlias = value.Alias
                    });
                }
            }
        }
Пример #4
0
        protected virtual decimal GetPreselectedPrice(
            Product product,
            Customer customer,
            PriceCalculationContext context,
            ProductBundleItemData bundleItem,
            IEnumerable <ProductBundleItemData> bundleItems)
        {
            var taxRate = decimal.Zero;
            var attributesTotalPriceBase       = decimal.Zero;
            var preSelectedPriceAdjustmentBase = decimal.Zero;
            var isBundle            = (product.ProductType == ProductType.BundledProduct);
            var isBundleItemPricing = (bundleItem != null && bundleItem.Item.BundleProduct.BundlePerItemPricing);
            var isBundlePricing     = (bundleItem != null && !bundleItem.Item.BundleProduct.BundlePerItemPricing);
            var bundleItemId        = (bundleItem == null ? 0 : bundleItem.Item.Id);

            var query = new ProductVariantQuery();
            var selectedAttributeValues = new List <ProductVariantAttributeValue>();
            var attributes = context.Attributes.GetOrLoad(product.Id);

            // 1. Fill query with initially selected attributes.
            foreach (var attribute in attributes.Where(x => x.ProductVariantAttributeValues.Count > 0 && x.ShouldHaveValues()))
            {
                int preSelectedValueId = 0;
                ProductVariantAttributeValue defaultValue = null;
                var selectedValueIds = new List <int>();
                var pvaValues        = attribute.ProductVariantAttributeValues;

                foreach (var pvaValue in pvaValues)
                {
                    ProductBundleItemAttributeFilter attributeFilter = null;

                    if (bundleItem.FilterOut(pvaValue, out attributeFilter))
                    {
                        continue;
                    }

                    if (preSelectedValueId == 0 && attributeFilter != null && attributeFilter.IsPreSelected)
                    {
                        preSelectedValueId = attributeFilter.AttributeValueId;
                    }

                    if (!isBundlePricing && pvaValue.IsPreSelected)
                    {
                        decimal attributeValuePriceAdjustment = GetProductVariantAttributeValuePriceAdjustment(pvaValue);
                        decimal priceAdjustmentBase           = _taxService.GetProductPrice(product, attributeValuePriceAdjustment, out taxRate);

                        preSelectedPriceAdjustmentBase = decimal.Add(preSelectedPriceAdjustmentBase, priceAdjustmentBase);
                    }
                }

                // Value pre-selected by a bundle item filter discards the default pre-selection.
                if (preSelectedValueId != 0 && (defaultValue = pvaValues.FirstOrDefault(x => x.Id == preSelectedValueId)) != null)
                {
                    //defaultValue.IsPreSelected = true;
                    selectedAttributeValues.Add(defaultValue);
                    query.AddVariant(new ProductVariantQueryItem(defaultValue.Id.ToString())
                    {
                        ProductId          = product.Id,
                        BundleItemId       = bundleItemId,
                        AttributeId        = attribute.ProductAttributeId,
                        VariantAttributeId = attribute.Id,
                        Alias      = attribute.ProductAttribute.Alias,
                        ValueAlias = defaultValue.Alias
                    });
                }
                else
                {
                    foreach (var value in pvaValues.Where(x => x.IsPreSelected))
                    {
                        selectedAttributeValues.Add(value);
                        query.AddVariant(new ProductVariantQueryItem(value.Id.ToString())
                        {
                            ProductId          = product.Id,
                            BundleItemId       = bundleItemId,
                            AttributeId        = attribute.ProductAttributeId,
                            VariantAttributeId = attribute.Id,
                            Alias      = attribute.ProductAttribute.Alias,
                            ValueAlias = value.Alias
                        });
                    }
                }
            }

            // 2. Find attribute combination for selected attributes and merge it.
            if (!isBundle && query.Variants.Count > 0)
            {
                var attributeXml = query.CreateSelectedAttributesXml(product.Id, bundleItemId, attributes, _productAttributeParser, _services.Localization,
                                                                     _downloadService, _catalogSettings, _httpRequestBase, new List <string>());

                var combinations = context.AttributeCombinations.GetOrLoad(product.Id);

                var selectedCombination = combinations.FirstOrDefault(x => _productAttributeParser.AreProductAttributesEqual(x.AttributesXml, attributeXml));

                if (selectedCombination != null && selectedCombination.IsActive && selectedCombination.Price.HasValue)
                {
                    product.MergedDataValues = new Dictionary <string, object> {
                        { "Price", selectedCombination.Price.Value }
                    };

                    if (selectedCombination.BasePriceAmount.HasValue)
                    {
                        product.MergedDataValues.Add("BasePriceAmount", selectedCombination.BasePriceAmount.Value);
                    }

                    if (selectedCombination.BasePriceBaseAmount.HasValue)
                    {
                        product.MergedDataValues.Add("BasePriceBaseAmount", selectedCombination.BasePriceBaseAmount.Value);
                    }
                }
            }

            if (_catalogSettings.EnableDynamicPriceUpdate && !isBundlePricing)
            {
                if (selectedAttributeValues.Count > 0)
                {
                    selectedAttributeValues.Each(x => attributesTotalPriceBase += GetProductVariantAttributeValuePriceAdjustment(x));
                }
                else
                {
                    attributesTotalPriceBase = preSelectedPriceAdjustmentBase;
                }
            }

            if (bundleItem != null)
            {
                bundleItem.AdditionalCharge = attributesTotalPriceBase;
            }

            var result = GetFinalPrice(product, bundleItems, customer, attributesTotalPriceBase, true, 1, bundleItem, context);

            return(result);
        }
        protected virtual async Task <decimal> GetPreselectedPriceAmountAsync(
            Product product,
            Customer customer,
            PriceCalculationContext context,
            ProductBundleItemData bundleItem,
            IEnumerable <ProductBundleItemData> bundleItems)
        {
            var attributesTotalPriceBase       = decimal.Zero;
            var preSelectedPriceAdjustmentBase = decimal.Zero;
            var isBundle            = product.ProductType == ProductType.BundledProduct;
            var isBundleItemPricing = bundleItem?.Item?.BundleProduct?.BundlePerItemPricing ?? false;
            var isBundlePricing     = bundleItem != null && !bundleItem.Item.BundleProduct.BundlePerItemPricing;
            var bundleItemId        = bundleItem?.Item?.Id ?? 0;

            var query = new ProductVariantQuery();
            var selectedAttributeValues = new List <ProductVariantAttributeValue>();
            var attributes = await context.Attributes.GetOrLoadAsync(product.Id);

            // 1. Fill query with initially selected attributes.
            foreach (var attribute in attributes.Where(x => x.ProductVariantAttributeValues.Any() && x.IsListTypeAttribute()))
            {
                await _db.LoadCollectionAsync(attribute, x => x.ProductVariantAttributeValues);

                var preSelectedValueId = 0;
                var selectedValueIds   = new List <int>();
                ProductVariantAttributeValue defaultValue = null;
                var pvaValues = attribute.ProductVariantAttributeValues;

                foreach (var pvaValue in pvaValues)
                {
                    ProductBundleItemAttributeFilter attributeFilter = null;
                    if (bundleItem?.Item?.IsFilteredOut(pvaValue, out attributeFilter) ?? false)
                    {
                        continue;
                    }

                    if (preSelectedValueId == 0 && attributeFilter != null && attributeFilter.IsPreSelected)
                    {
                        preSelectedValueId = attributeFilter.AttributeValueId;
                    }

                    if (!isBundlePricing && pvaValue.IsPreSelected)
                    {
                        var attributeValuePriceAdjustment = await GetVariantPriceAdjustmentAsync(pvaValue, product, customer, context, 1);

                        // We cannot avoid money usage in calls between interfaces.
                        var(priceAdjustmentBase, _) = await _taxService.GetProductPriceAsync(product, new(attributeValuePriceAdjustment, _primaryCurrency), customer : customer);

                        preSelectedPriceAdjustmentBase += priceAdjustmentBase.Amount;
                    }
                }

                // Value pre-selected by a bundle item filter discards the default pre-selection.
                if (preSelectedValueId != 0 && (defaultValue = pvaValues.FirstOrDefault(x => x.Id == preSelectedValueId)) != null)
                {
                    //defaultValue.IsPreSelected = true;
                    selectedAttributeValues.Add(defaultValue);
                    query.AddVariant(new ProductVariantQueryItem(defaultValue.Id.ToString())
                    {
                        ProductId          = product.Id,
                        BundleItemId       = bundleItemId,
                        AttributeId        = attribute.ProductAttributeId,
                        VariantAttributeId = attribute.Id,
                        Alias      = attribute.ProductAttribute.Alias,
                        ValueAlias = defaultValue.Alias
                    });
                }
                else
                {
                    foreach (var value in pvaValues.Where(x => x.IsPreSelected))
                    {
                        selectedAttributeValues.Add(value);
                        query.AddVariant(new ProductVariantQueryItem(value.Id.ToString())
                        {
                            ProductId          = product.Id,
                            BundleItemId       = bundleItemId,
                            AttributeId        = attribute.ProductAttributeId,
                            VariantAttributeId = attribute.Id,
                            Alias      = attribute.ProductAttribute.Alias,
                            ValueAlias = value.Alias
                        });
                    }
                }
            }

            // 2. Find attribute combination for selected attributes and merge it.
            if (!isBundle && query.Variants.Any())
            {
                var(selection, warnings) = await _productAttributeMaterializer.CreateAttributeSelectionAsync(query, attributes, product.Id, bundleItemId, true);

                var combinations = await context.AttributeCombinations.GetOrLoadAsync(product.Id);

                var selectedCombination = combinations.FirstOrDefault(x => x.AttributeSelection.Equals(selection));

                if (selectedCombination != null && selectedCombination.IsActive && selectedCombination.Price.HasValue)
                {
                    product.MergedDataValues = new Dictionary <string, object> {
                        { "Price", selectedCombination.Price.Value }
                    };

                    if (selectedCombination.BasePriceAmount.HasValue)
                    {
                        product.MergedDataValues.Add("BasePriceAmount", selectedCombination.BasePriceAmount.Value);
                    }

                    if (selectedCombination.BasePriceBaseAmount.HasValue)
                    {
                        product.MergedDataValues.Add("BasePriceBaseAmount", selectedCombination.BasePriceBaseAmount.Value);
                    }
                }
            }

            if (_catalogSettings.EnableDynamicPriceUpdate && !isBundlePricing)
            {
                if (selectedAttributeValues.Count > 0)
                {
                    foreach (var value in selectedAttributeValues)
                    {
                        attributesTotalPriceBase += await GetVariantPriceAdjustmentAsync(value, product, customer, context, 1);
                    }
                }
                else
                {
                    attributesTotalPriceBase = preSelectedPriceAdjustmentBase;
                }
            }

            if (bundleItem != null)
            {
                bundleItem.AdditionalCharge = new(attributesTotalPriceBase, _primaryCurrency);
            }

            var result = await GetFinalPriceAmountAsync(product, bundleItems, attributesTotalPriceBase, customer, true, 1, bundleItem, context);

            return(result);
        }
Пример #6
0
        /// <summary>
        /// Adds selected product variant attributes to a product variant query.
        /// </summary>
        /// <param name="query">Target product variant query.</param>
        /// <param name="source">Selected attributes.</param>
        /// <param name="productId">Product identifier.</param>
        /// <param name="bundleItemId">Bundle item identifier.</param>
        /// <param name="attributes">Product variant attributes.</param>
        public virtual async Task AddAttributesToQueryAsync(
            ProductVariantQuery query,
            ProductVariantAttributeSelection source,
            int productId,
            int bundleItemId = 0,
            ICollection <ProductVariantAttribute> attributes = null)
        {
            Guard.NotNull(query, nameof(query));

            if (productId == 0 || !(source?.AttributesMap?.Any() ?? false))
            {
                return;
            }

            if (attributes == null)
            {
                var ids = source.AttributesMap.Select(x => x.Key);
                attributes = await _db.ProductVariantAttributes.GetManyAsync(ids);
            }

            var languageId = _workContext.WorkingLanguage.Id;

            foreach (var attribute in attributes)
            {
                var item = source.AttributesMap.FirstOrDefault(x => x.Key == attribute.Id);
                if (item.Key != 0)
                {
                    foreach (var originalValue in item.Value)
                    {
                        var      value = originalValue.ToString();
                        DateTime?date  = null;

                        if (attribute.AttributeControlType == AttributeControlType.Datepicker)
                        {
                            date = value.ToDateTime(new[] { "D" }, CultureInfo.CurrentCulture, DateTimeStyles.None, null);
                            if (date == null)
                            {
                                continue;
                            }

                            value = string.Join("-", date.Value.Year, date.Value.Month, date.Value.Day);
                        }

                        var queryItem = new ProductVariantQueryItem(value)
                        {
                            ProductId          = productId,
                            BundleItemId       = bundleItemId,
                            AttributeId        = attribute.ProductAttributeId,
                            VariantAttributeId = attribute.Id,
                            Alias  = _catalogSearchQueryAliasMapper.Value.GetVariantAliasById(attribute.ProductAttributeId, languageId),
                            Date   = date,
                            IsFile = attribute.AttributeControlType == AttributeControlType.FileUpload,
                            IsText = attribute.AttributeControlType == AttributeControlType.TextBox || attribute.AttributeControlType == AttributeControlType.MultilineTextbox
                        };

                        if (attribute.IsListTypeAttribute())
                        {
                            queryItem.ValueAlias = _catalogSearchQueryAliasMapper.Value.GetVariantOptionAliasById(value.ToInt(), languageId);
                        }

                        query.AddVariant(queryItem);
                    }
                }
            }
        }
Пример #7
0
        public async Task CalculateAsync(CalculatorContext context, CalculatorDelegate next)
        {
            var options = context.Options;

            if (!options.DeterminePreselectedPrice)
            {
                // Proceed with pipeline and omit this calculator, it is made for preselected price calculation only.
                await next(context);

                return;
            }

            var selectedValues = (await context.GetPreSelectedAttributeValuesAsync())
                                 .Where(x => x.ProductVariantAttribute.IsListTypeAttribute())
                                 .ToList();

            if (selectedValues.Any())
            {
                // Create attribute selection of preselected values.
                var query        = new ProductVariantQuery();
                var product      = context.Product;
                var bundleItemId = context.BundleItem?.Item?.Id ?? 0;
                var attributes   = await options.BatchContext.Attributes.GetOrLoadAsync(product.Id);

                var combinations = await options.BatchContext.AttributeCombinations.GetOrLoadAsync(product.Id);

                foreach (var value in selectedValues)
                {
                    var productAttribute = value.ProductVariantAttribute;

                    query.AddVariant(new ProductVariantQueryItem(value.Id.ToString())
                    {
                        ProductId          = product.Id,
                        BundleItemId       = bundleItemId,
                        AttributeId        = productAttribute.ProductAttributeId,
                        VariantAttributeId = productAttribute.Id,
                        Alias      = productAttribute.ProductAttribute.Alias,
                        ValueAlias = value.Alias
                    });
                }

                var(selection, _) = await _productAttributeMaterializer.CreateAttributeSelectionAsync(query, attributes, product.Id, bundleItemId, false);

                var selectedCombination = combinations.FirstOrDefault(x => x.AttributeSelection.Equals(selection));

                // Apply attribute combination price.
                if ((selectedCombination?.IsActive ?? false) && selectedCombination.Price.HasValue)
                {
                    context.FinalPrice = selectedCombination.Price.Value;

                    // That comes too late because regular price has already been passed to child CalculatorContext:
                    //product.MergedDataValues = new Dictionary<string, object> { { "Price", selectedCombination.Price.Value } };
                }
            }

            // The product page is always loaded with the default quantity of 1.
            context.Quantity = 1;
            await next(context);

            context.PreselectedPrice = context.FinalPrice;
        }