/// <summary> /// Formats attributes /// </summary> /// <param name="product">Product</param> /// <param name="attributesXml">Attributes in XML format</param> /// <param name="customer">Customer</param> /// <param name="separator">Separator</param> /// <param name="htmlEncode">A value indicating whether to encode (HTML) values</param> /// <param name="renderPrices">A value indicating whether to render prices</param> /// <param name="renderProductAttributes">A value indicating whether to render product attributes</param> /// <param name="renderGiftCardAttributes">A value indicating whether to render gift card attributes</param> /// <param name="allowHyperlinks">A value indicating whether to HTML hyperink tags could be rendered (if required)</param> /// <returns> /// A task that represents the asynchronous operation /// The task result contains the attributes /// </returns> public virtual async Task <string> FormatAttributesAsync(Product product, string attributesXml, Customer customer, string separator = "<br />", bool htmlEncode = true, bool renderPrices = true, bool renderProductAttributes = true, bool renderGiftCardAttributes = true, bool allowHyperlinks = true) { var result = new StringBuilder(); //attributes if (renderProductAttributes) { foreach (var attribute in await _productAttributeParser.ParseProductAttributeMappingsAsync(attributesXml)) { var productAttribute = await _productAttributeService.GetProductAttributeByIdAsync(attribute.ProductAttributeId); var attributeName = await _localizationService.GetLocalizedAsync(productAttribute, a => a.Name, (await _workContext.GetWorkingLanguageAsync()).Id); //attributes without values if (!attribute.ShouldHaveValues()) { foreach (var value in _productAttributeParser.ParseValues(attributesXml, attribute.Id)) { var formattedAttribute = string.Empty; if (attribute.AttributeControlType == AttributeControlType.MultilineTextbox) { //encode (if required) if (htmlEncode) { attributeName = WebUtility.HtmlEncode(attributeName); } //we never encode multiline textbox input formattedAttribute = $"{attributeName}: {HtmlHelper.FormatText(value, false, true, false, false, false, false)}"; } else if (attribute.AttributeControlType == AttributeControlType.FileUpload) { //file upload Guid.TryParse(value, out var downloadGuid); var download = await _downloadService.GetDownloadByGuidAsync(downloadGuid); if (download != null) { var fileName = $"{download.Filename ?? download.DownloadGuid.ToString()}{download.Extension}"; //encode (if required) if (htmlEncode) { fileName = WebUtility.HtmlEncode(fileName); } var attributeText = allowHyperlinks ? $"<a href=\"{_webHelper.GetStoreLocation(false)}download/getfileupload/?downloadId={download.DownloadGuid}\" class=\"fileuploadattribute\">{fileName}</a>" : fileName; //encode (if required) if (htmlEncode) { attributeName = WebUtility.HtmlEncode(attributeName); } formattedAttribute = $"{attributeName}: {attributeText}"; } } else { //other attributes (textbox, datepicker) formattedAttribute = $"{attributeName}: {value}"; //encode (if required) if (htmlEncode) { formattedAttribute = WebUtility.HtmlEncode(formattedAttribute); } } if (string.IsNullOrEmpty(formattedAttribute)) { continue; } if (result.Length > 0) { result.Append(separator); } result.Append(formattedAttribute); } } //product attribute values else { foreach (var attributeValue in await _productAttributeParser.ParseProductAttributeValuesAsync(attributesXml, attribute.Id)) { var formattedAttribute = $"{attributeName}: {await _localizationService.GetLocalizedAsync(attributeValue, a => a.Name, (await _workContext.GetWorkingLanguageAsync()).Id)}"; if (renderPrices) { if (attributeValue.PriceAdjustmentUsePercentage) { if (attributeValue.PriceAdjustment > decimal.Zero) { formattedAttribute += string.Format( await _localizationService.GetResourceAsync("FormattedAttributes.PriceAdjustment"), "+", attributeValue.PriceAdjustment.ToString("G29"), "%"); } else if (attributeValue.PriceAdjustment < decimal.Zero) { formattedAttribute += string.Format( await _localizationService.GetResourceAsync("FormattedAttributes.PriceAdjustment"), string.Empty, attributeValue.PriceAdjustment.ToString("G29"), "%"); } } else { var attributeValuePriceAdjustment = await _priceCalculationService.GetProductAttributeValuePriceAdjustmentAsync(product, attributeValue, customer); var(priceAdjustmentBase, _) = await _taxService.GetProductPriceAsync(product, attributeValuePriceAdjustment, customer); var priceAdjustment = await _currencyService.ConvertFromPrimaryStoreCurrencyAsync(priceAdjustmentBase, await _workContext.GetWorkingCurrencyAsync()); if (priceAdjustmentBase > decimal.Zero) { formattedAttribute += string.Format( await _localizationService.GetResourceAsync("FormattedAttributes.PriceAdjustment"), "+", await _priceFormatter.FormatPriceAsync(priceAdjustment, false, false), string.Empty); } else if (priceAdjustmentBase < decimal.Zero) { formattedAttribute += string.Format( await _localizationService.GetResourceAsync("FormattedAttributes.PriceAdjustment"), "-", await _priceFormatter.FormatPriceAsync(-priceAdjustment, false, false), string.Empty); } } } //display quantity if (_shoppingCartSettings.RenderAssociatedAttributeValueQuantity && attributeValue.AttributeValueType == AttributeValueType.AssociatedToProduct) { //render only when more than 1 if (attributeValue.Quantity > 1) { formattedAttribute += string.Format(await _localizationService.GetResourceAsync("ProductAttributes.Quantity"), attributeValue.Quantity); } } //encode (if required) if (htmlEncode) { formattedAttribute = WebUtility.HtmlEncode(formattedAttribute); } if (string.IsNullOrEmpty(formattedAttribute)) { continue; } if (result.Length > 0) { result.Append(separator); } result.Append(formattedAttribute); } } } } //gift cards if (!renderGiftCardAttributes) { return(result.ToString()); } if (!product.IsGiftCard) { return(result.ToString()); } _productAttributeParser.GetGiftCardAttribute(attributesXml, out var giftCardRecipientName, out var giftCardRecipientEmail, out var giftCardSenderName, out var giftCardSenderEmail, out var _); //sender var giftCardFrom = product.GiftCardType == GiftCardType.Virtual ? string.Format(await _localizationService.GetResourceAsync("GiftCardAttribute.From.Virtual"), giftCardSenderName, giftCardSenderEmail) : string.Format(await _localizationService.GetResourceAsync("GiftCardAttribute.From.Physical"), giftCardSenderName); //recipient var giftCardFor = product.GiftCardType == GiftCardType.Virtual ? string.Format(await _localizationService.GetResourceAsync("GiftCardAttribute.For.Virtual"), giftCardRecipientName, giftCardRecipientEmail) : string.Format(await _localizationService.GetResourceAsync("GiftCardAttribute.For.Physical"), giftCardRecipientName); //encode (if required) if (htmlEncode) { giftCardFrom = WebUtility.HtmlEncode(giftCardFrom); giftCardFor = WebUtility.HtmlEncode(giftCardFor); } if (!string.IsNullOrEmpty(result.ToString())) { result.Append(separator); } result.Append(giftCardFrom); result.Append(separator); result.Append(giftCardFor); return(result.ToString()); }
public async override Task <(decimal unitPrice, decimal discountAmount, List <Discount> appliedDiscounts)> GetUnitPriceAsync( Product product, Customer customer, ShoppingCartType shoppingCartType, int quantity, string attributesXml, decimal customerEnteredPrice, DateTime?rentalStartDate, DateTime?rentalEndDate, bool includeDiscounts) { if (product == null) { throw new ArgumentNullException(nameof(product)); } if (customer == null) { throw new ArgumentNullException(nameof(customer)); } var discountAmount = decimal.Zero; var appliedDiscounts = new List <Discount>(); decimal finalPrice; var combination = await _productAttributeParser.FindProductAttributeCombinationAsync(product, attributesXml); if (combination?.OverriddenPrice.HasValue ?? false) { (_, finalPrice, discountAmount, appliedDiscounts) = await _priceCalculationService.GetFinalPriceAsync(product, customer, combination.OverriddenPrice.Value, decimal.Zero, includeDiscounts, quantity, product.IsRental?rentalStartDate : null, product.IsRental?rentalEndDate : null); } else { // custom // summarize price of all attributes except warranties (warranties processed separately) decimal warrantyPrice = decimal.Zero; ProductAttributeMapping warrantyPam = await _attributeUtilities.GetWarrantyAttributeMappingAsync(attributesXml); //summarize price of all attributes var attributesTotalPrice = decimal.Zero; var attributeValues = await _productAttributeParser.ParseProductAttributeValuesAsync(attributesXml); // custom - considers warranties if (attributeValues != null) { foreach (var attributeValue in attributeValues) { if (warrantyPam != null && attributeValue.ProductAttributeMappingId == warrantyPam.Id) { warrantyPrice = await _priceCalculationService.GetProductAttributeValuePriceAdjustmentAsync( product, attributeValue, customer, product.CustomerEntersPrice?(decimal?)customerEnteredPrice : null ); } else { attributesTotalPrice += await _priceCalculationService.GetProductAttributeValuePriceAdjustmentAsync( product, attributeValue, customer, product.CustomerEntersPrice?(decimal?)customerEnteredPrice : null ); } } } //get price of a product (with previously calculated price of all attributes) if (product.CustomerEntersPrice) { finalPrice = customerEnteredPrice; } else { int qty; if (_shoppingCartSettings.GroupTierPricesForDistinctShoppingCartItems) { //the same products with distinct product attributes could be stored as distinct "ShoppingCartItem" records //so let's find how many of the current products are in the cart qty = (await GetShoppingCartAsync(customer, shoppingCartType: shoppingCartType, productId: product.Id)) .Sum(x => x.Quantity); if (qty == 0) { qty = quantity; } } else { qty = quantity; } (_, finalPrice, discountAmount, appliedDiscounts) = await _priceCalculationService.GetFinalPriceAsync(product, customer, attributesTotalPrice, includeDiscounts, qty, product.IsRental?rentalStartDate : null, product.IsRental?rentalEndDate : null); finalPrice += warrantyPrice; } } //rounding if (_shoppingCartSettings.RoundPricesDuringCalculation) { finalPrice = await _priceCalculationService.RoundPriceAsync(finalPrice); } return(finalPrice, discountAmount, appliedDiscounts); }