protected virtual async Task <List <ProductVariantAttributeValue> > GetSelectedAttributeValuesAsync(CalculatorContext context, IEnumerable <ProductVariantAttribute> attributes) { var result = new List <ProductVariantAttributeValue>(); var bundleItem = context?.BundleItem?.Item; // Apply attributes selected by customer. var selections = context.SelectedAttributes .Where(x => x.ProductId == context.Product.Id && x.BundleItemId == bundleItem?.Id) .Select(x => x.Selection) .ToList(); foreach (var selection in selections) { var selectedValues = selection.MaterializeProductVariantAttributeValues(attributes); // Ignore attributes that are filtered out for a bundle item. if (bundleItem?.FilterAttributes ?? false) { var filteredValues = selectedValues .Where(x => bundleItem.AttributeFilters.Any(af => af.AttributeId == x.ProductVariantAttributeId && af.AttributeValueId == x.Id)); result.AddRange(filteredValues); } else { result.AddRange(selectedValues); } } // Apply attributes preselected by merchant. if (context.Options.ApplyPreselectedAttributes) { // Ignore already applied values. var appliedValueIds = result.Select(x => x.Id).ToArray(); var preSelectedValues = await context.GetPreSelectedAttributeValuesAsync(); result.AddRange(preSelectedValues.Where(x => !appliedValueIds.Contains(x.Id))); } return(result); }
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; }