public virtual async Task <ShoppingCartSubTotal> GetShoppingCartSubTotalAsync(IList <OrganizedShoppingCartItem> cart, bool?includeTax = null) { Guard.NotNull(cart, nameof(cart)); var includingTax = includeTax ?? (_workContext.TaxDisplayType == TaxDisplayType.IncludingTax); var result = new ShoppingCartSubTotal(); if (!(cart?.Any() ?? false)) { return(result); } var customer = cart.GetCustomer(); var subTotalExclTaxWithoutDiscount = new Money(_primaryCurrency); var subTotalInclTaxWithoutDiscount = new Money(_primaryCurrency); foreach (var cartItem in cart) { if (cartItem.Item.Product == null) { continue; } var item = cartItem.Item; decimal taxRate = decimal.Zero; Money itemExclTax, itemInclTax; await _productAttributeMaterializer.MergeWithCombinationAsync(item.Product, item.AttributeSelection); if (_primaryCurrency.RoundOrderItemsEnabled) { // Gross > Net RoundFix. var unitPrice = await _priceCalculationService.GetUnitPriceAsync(cartItem, true); // Adaption to eliminate rounding issues. (itemExclTax, taxRate) = await _taxService.GetProductPriceAsync(item.Product, unitPrice, false, customer : customer); itemExclTax = _primaryCurrency.AsMoney(itemExclTax.Amount * item.Quantity); (itemInclTax, taxRate) = await _taxService.GetProductPriceAsync(item.Product, unitPrice, true, customer : customer); itemInclTax = _primaryCurrency.AsMoney(itemInclTax.Amount * item.Quantity); } else { var itemSubTotal = await _priceCalculationService.GetSubTotalAsync(cartItem, true); (itemExclTax, taxRate) = await _taxService.GetProductPriceAsync(item.Product, itemSubTotal, false, customer : customer); (itemInclTax, taxRate) = await _taxService.GetProductPriceAsync(item.Product, itemSubTotal, true, customer : customer); } subTotalExclTaxWithoutDiscount += itemExclTax; subTotalInclTaxWithoutDiscount += itemInclTax; result.TaxRates.Add(taxRate, itemInclTax.Amount - itemExclTax.Amount); } // Checkout attributes. if (customer != null) { var values = await _checkoutAttributeMaterializer.MaterializeCheckoutAttributeValuesAsync(customer.GenericAttributes.CheckoutAttributes); foreach (var value in values) { var(attributePriceExclTax, _) = await _taxService.GetCheckoutAttributePriceAsync(value, false, customer); var(attributePriceInclTax, taxRate) = await _taxService.GetCheckoutAttributePriceAsync(value, true, customer); subTotalExclTaxWithoutDiscount += attributePriceExclTax.Amount; subTotalInclTaxWithoutDiscount += attributePriceInclTax.Amount; result.TaxRates.Add(taxRate, attributePriceInclTax.Amount - attributePriceExclTax.Amount); } } // Subtotal without discount. result.SubTotalWithoutDiscount = _primaryCurrency.AsMoney(includingTax ? subTotalInclTaxWithoutDiscount.Amount : subTotalExclTaxWithoutDiscount.Amount, true, true); // We calculate discount amount on order subtotal excl tax (discount first). var(discountAmountExclTax, appliedDiscount) = await this.GetOrderSubtotalDiscountAsync(subTotalExclTaxWithoutDiscount, customer); result.AppliedDiscount = appliedDiscount; if (subTotalExclTaxWithoutDiscount < discountAmountExclTax) { discountAmountExclTax = subTotalExclTaxWithoutDiscount; } var discountAmountInclTax = discountAmountExclTax; // Subtotal with discount (excl tax). var subTotalExclTaxWithDiscount = subTotalExclTaxWithoutDiscount - discountAmountExclTax; var subTotalInclTaxWithDiscount = subTotalExclTaxWithDiscount; // Add tax for shopping items & checkout attributes. var tempTaxRates = new Dictionary <decimal, decimal>(result.TaxRates); foreach (var kvp in tempTaxRates) { var taxRate = kvp.Key; var taxAmount = kvp.Value; if (taxAmount != decimal.Zero) { // Discount the tax amount that applies to subtotal items. if (subTotalExclTaxWithoutDiscount > decimal.Zero) { var discountTax = result.TaxRates[taxRate] * (discountAmountExclTax.Amount / subTotalExclTaxWithoutDiscount.Amount); discountAmountInclTax += discountTax; taxAmount = _primaryCurrency.RoundIfEnabledFor(result.TaxRates[taxRate] - discountTax); result.TaxRates[taxRate] = taxAmount; } // Subtotal with discount (incl tax). subTotalInclTaxWithDiscount += taxAmount; } } discountAmountInclTax = _primaryCurrency.AsMoney(discountAmountInclTax.Amount); result.DiscountAmount = _primaryCurrency.AsMoney(includingTax ? discountAmountInclTax.Amount : discountAmountExclTax.Amount, false); result.SubTotalWithDiscount = _primaryCurrency.AsMoney(includingTax ? subTotalInclTaxWithDiscount.Amount : subTotalExclTaxWithDiscount.Amount, true, true); return(result); }
/// <summary> /// Add order items to the request query parameters /// </summary> /// <param name="parameters">Query parameters</param> /// <param name="postProcessPaymentRequest">Payment info required for an order processing</param> private async Task AddItemsParametersAsync(IDictionary <string, string> parameters, PostProcessPaymentRequest postProcessPaymentRequest) { //upload order items parameters.Add("cmd", "_cart"); parameters.Add("upload", "1"); var cartTotal = decimal.Zero; var roundedCartTotal = decimal.Zero; var itemCount = 1; //add shopping cart items foreach (var item in await _orderService.GetOrderItemsAsync(postProcessPaymentRequest.Order.Id)) { var roundedItemPrice = Math.Round(item.UnitPriceExclTax, 2); var product = await _productService.GetProductByIdAsync(item.ProductId); //add query parameters parameters.Add($"item_name_{itemCount}", product.Name); parameters.Add($"amount_{itemCount}", roundedItemPrice.ToString("0.00", CultureInfo.InvariantCulture)); parameters.Add($"quantity_{itemCount}", item.Quantity.ToString()); cartTotal += item.PriceExclTax; roundedCartTotal += roundedItemPrice * item.Quantity; itemCount++; } //add checkout attributes as order items var checkoutAttributeValues = _checkoutAttributeParser.ParseCheckoutAttributeValues(postProcessPaymentRequest.Order.CheckoutAttributesXml); var customer = await _customerService.GetCustomerByIdAsync(postProcessPaymentRequest.Order.CustomerId); await foreach (var(attribute, values) in checkoutAttributeValues) { await foreach (var attributeValue in values) { var(attributePrice, _) = await _taxService.GetCheckoutAttributePriceAsync(attribute, attributeValue, false, customer); var roundedAttributePrice = Math.Round(attributePrice, 2); //add query parameters if (attribute == null) { continue; } parameters.Add($"item_name_{itemCount}", attribute.Name); parameters.Add($"amount_{itemCount}", roundedAttributePrice.ToString("0.00", CultureInfo.InvariantCulture)); parameters.Add($"quantity_{itemCount}", "1"); cartTotal += attributePrice; roundedCartTotal += roundedAttributePrice; itemCount++; } } //add shipping fee as a separate order item, if it has price var roundedShippingPrice = Math.Round(postProcessPaymentRequest.Order.OrderShippingExclTax, 2); if (roundedShippingPrice > decimal.Zero) { parameters.Add($"item_name_{itemCount}", "Shipping fee"); parameters.Add($"amount_{itemCount}", roundedShippingPrice.ToString("0.00", CultureInfo.InvariantCulture)); parameters.Add($"quantity_{itemCount}", "1"); cartTotal += postProcessPaymentRequest.Order.OrderShippingExclTax; roundedCartTotal += roundedShippingPrice; itemCount++; } //add payment method additional fee as a separate order item, if it has price var roundedPaymentMethodPrice = Math.Round(postProcessPaymentRequest.Order.PaymentMethodAdditionalFeeExclTax, 2); if (roundedPaymentMethodPrice > decimal.Zero) { parameters.Add($"item_name_{itemCount}", "Payment method fee"); parameters.Add($"amount_{itemCount}", roundedPaymentMethodPrice.ToString("0.00", CultureInfo.InvariantCulture)); parameters.Add($"quantity_{itemCount}", "1"); cartTotal += postProcessPaymentRequest.Order.PaymentMethodAdditionalFeeExclTax; roundedCartTotal += roundedPaymentMethodPrice; itemCount++; } //add tax as a separate order item, if it has positive amount var roundedTaxAmount = Math.Round(postProcessPaymentRequest.Order.OrderTax, 2); if (roundedTaxAmount > decimal.Zero) { parameters.Add($"item_name_{itemCount}", "Tax amount"); parameters.Add($"amount_{itemCount}", roundedTaxAmount.ToString("0.00", CultureInfo.InvariantCulture)); parameters.Add($"quantity_{itemCount}", "1"); cartTotal += postProcessPaymentRequest.Order.OrderTax; roundedCartTotal += roundedTaxAmount; } if (cartTotal > postProcessPaymentRequest.Order.OrderTotal) { //get the difference between what the order total is and what it should be and use that as the "discount" var discountTotal = Math.Round(cartTotal - postProcessPaymentRequest.Order.OrderTotal, 2); roundedCartTotal -= discountTotal; //gift card or rewarded point amount applied to cart in nopCommerce - shows in PayPal as "discount" parameters.Add("discount_amount_cart", discountTotal.ToString("0.00", CultureInfo.InvariantCulture)); } //save order total that actually sent to PayPal (used for PDT order total validation) await _genericAttributeService.SaveAttributeAsync(postProcessPaymentRequest.Order, PayPalHelper.OrderTotalSentToPayPal, roundedCartTotal); }
/// <summary> /// Formats attributes /// </summary> /// <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="allowHyperlinks">A value indicating whether to HTML hyperlink 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(string attributesXml, Customer customer, string separator = "<br />", bool htmlEncode = true, bool renderPrices = true, bool allowHyperlinks = true) { var result = new StringBuilder(); var attributes = await _checkoutAttributeParser.ParseCheckoutAttributesAsync(attributesXml); for (var i = 0; i < attributes.Count; i++) { var attribute = attributes[i]; var valuesStr = _checkoutAttributeParser.ParseValues(attributesXml, attribute.Id); for (var j = 0; j < valuesStr.Count; j++) { var valueStr = valuesStr[j]; var formattedAttribute = string.Empty; if (!attribute.ShouldHaveValues()) { //no values if (attribute.AttributeControlType == AttributeControlType.MultilineTextbox) { //multiline textbox var attributeName = await _localizationService.GetLocalizedAsync(attribute, a => a.Name, (await _workContext.GetWorkingLanguageAsync()).Id); //encode (if required) if (htmlEncode) { attributeName = WebUtility.HtmlEncode(attributeName); } formattedAttribute = $"{attributeName}: {HtmlHelper.FormatText(valueStr, false, true, false, false, false, false)}"; //we never encode multiline textbox input } else if (attribute.AttributeControlType == AttributeControlType.FileUpload) { //file upload Guid.TryParse(valueStr, out var downloadGuid); var download = await _downloadService.GetDownloadByGuidAsync(downloadGuid); if (download != null) { string attributeText; var fileName = $"{download.Filename ?? download.DownloadGuid.ToString()}{download.Extension}"; //encode (if required) if (htmlEncode) { fileName = WebUtility.HtmlEncode(fileName); } if (allowHyperlinks) { //hyperlinks are allowed var downloadLink = $"{_webHelper.GetStoreLocation(false)}download/getfileupload/?downloadId={download.DownloadGuid}"; attributeText = $"<a href=\"{downloadLink}\" class=\"fileuploadattribute\">{fileName}</a>"; } else { //hyperlinks aren't allowed attributeText = fileName; } var attributeName = await _localizationService.GetLocalizedAsync(attribute, a => a.Name, (await _workContext.GetWorkingLanguageAsync()).Id); //encode (if required) if (htmlEncode) { attributeName = WebUtility.HtmlEncode(attributeName); } formattedAttribute = $"{attributeName}: {attributeText}"; } } else { //other attributes (textbox, datepicker) formattedAttribute = $"{await _localizationService.GetLocalizedAsync(attribute, a => a.Name, (await _workContext.GetWorkingLanguageAsync()).Id)}: {valueStr}"; //encode (if required) if (htmlEncode) { formattedAttribute = WebUtility.HtmlEncode(formattedAttribute); } } } else { if (int.TryParse(valueStr, out var attributeValueId)) { var attributeValue = await _checkoutAttributeService.GetCheckoutAttributeValueByIdAsync(attributeValueId); if (attributeValue != null) { formattedAttribute = $"{await _localizationService.GetLocalizedAsync(attribute, a => a.Name, (await _workContext.GetWorkingLanguageAsync()).Id)}: {await _localizationService.GetLocalizedAsync(attributeValue, a => a.Name, (await _workContext.GetWorkingLanguageAsync()).Id)}"; if (renderPrices) { var priceAdjustmentBase = (await _taxService.GetCheckoutAttributePriceAsync(attribute, attributeValue, customer)).price; var priceAdjustment = await _currencyService.ConvertFromPrimaryStoreCurrencyAsync(priceAdjustmentBase, await _workContext.GetWorkingCurrencyAsync()); if (priceAdjustmentBase > 0) { formattedAttribute += string.Format( await _localizationService.GetResourceAsync("FormattedAttributes.PriceAdjustment"), "+", await _priceFormatter.FormatPriceAsync(priceAdjustment), string.Empty); } } } //encode (if required) if (htmlEncode) { formattedAttribute = WebUtility.HtmlEncode(formattedAttribute); } } } if (string.IsNullOrEmpty(formattedAttribute)) { continue; } if (i != 0 || j != 0) { result.Append(separator); } result.Append(formattedAttribute); } } return(result.ToString()); }
public async Task <string> FormatAttributesAsync( CheckoutAttributeSelection selection, Customer customer = null, string serapator = "<br />", bool htmlEncode = true, bool renderPrices = true, bool allowHyperlinks = true) { Guard.NotNull(selection, nameof(selection)); customer ??= _workContext.CurrentCustomer; using var psb = StringBuilderPool.Instance.Get(out var sb); var attributesList = await _checkoutAttributeMaterializer.MaterializeCheckoutAttributesAsync(selection); if (attributesList.IsNullOrEmpty()) { return(null); } var attributeValues = attributesList .Where(x => x.IsListTypeAttribute) .SelectMany(x => x.CheckoutAttributeValues); var language = _workContext.WorkingLanguage; for (var i = 0; i < attributesList.Count; i++) { var currentAttribute = attributesList[i]; var currentAttributeValues = selection.GetAttributeValues(currentAttribute.Id).ToList(); for (var j = 0; j < currentAttributeValues.Count; j++) { var currentValue = currentAttributeValues[j].ToString(); var attributeStr = string.Empty; if (!currentAttribute.IsListTypeAttribute) { if (currentAttribute.AttributeControlType is AttributeControlType.MultilineTextbox) { // Multiline textbox input gets never encoded var attributeName = currentAttribute.GetLocalized(a => a.Name, language).ToString(); if (htmlEncode) { attributeName = HttpUtility.HtmlEncode(attributeName); } attributeStr = string.Format( "{0}: {1}", attributeName, HtmlUtils.ConvertPlainTextToHtml(currentValue.EmptyNull().Replace(":", "").HtmlEncode())); } else if (currentAttribute.AttributeControlType is AttributeControlType.FileUpload) { Guid.TryParse(currentValue, out var downloadGuid); var download = await _db.Downloads .Include(x => x.MediaFile) .Where(x => x.DownloadGuid == downloadGuid) .FirstOrDefaultAsync(); if (download?.MediaFile != null) { // TODO: (ms) (core) add a method for getting URL (use routing because it handles all SEO friendly URLs) ? //var genratedUrl = _mediaService.GenerateFileDownloadUrl(download.MediaFileId, 0); var attributeText = string.Empty; var fileName = download.MediaFile.Name; if (htmlEncode) { fileName = HttpUtility.HtmlEncode(fileName); } if (allowHyperlinks) { var downloadLink = string.Format( "{0}download/getfileupload/?downloadId={1}", _webHelper.GetStoreLocation(false), download.DownloadGuid); attributeText = string.Format( "<a href=\"{0}\" class=\"fileuploadattribute\">{1}</a>", downloadLink, fileName); } else { attributeText = fileName; } var attributeName = currentAttribute.GetLocalized(a => a.Name, language).ToString(); if (htmlEncode) { attributeName = HttpUtility.HtmlEncode(attributeName); } attributeStr = string.Format("{0}: {1}", attributeName, attributeText); } } else { // Other attributes (textbox, datepicker...) attributeStr = string.Format( "{0}: {1}", currentAttribute.GetLocalized(a => a.Name, language), currentValue); if (htmlEncode) { attributeStr = HttpUtility.HtmlEncode(attributeStr); } } } else { if (int.TryParse(currentValue, out var id)) { var attributeValue = attributeValues.Where(x => x.Id == id).FirstOrDefault(); if (attributeValue != null) { attributeStr = string.Format( "{0}: {1}", currentAttribute.GetLocalized(x => x.Name, language), attributeValue.GetLocalized(x => x.Name, language)); if (renderPrices) { var(priceAdjustment, _) = await _taxService.GetCheckoutAttributePriceAsync(attributeValue, customer : customer); var priceAdjustmentConverted = _currencyService.ConvertFromPrimaryStoreCurrency(priceAdjustment.Amount, _workContext.WorkingCurrency); if (priceAdjustment > 0) { attributeStr += string.Format(" [+{0}]", _currencyService.CreateMoney(priceAdjustmentConverted).ToString()); } } } if (htmlEncode) { attributeStr = HttpUtility.HtmlEncode(attributeStr); } } } if (attributeStr.HasValue()) { if (i != 0 || j != 0) { sb.Append(serapator); } sb.Append(attributeStr); } } } return(sb.ToString()); }
public override async Task <(decimal discountAmount, List <Discount> appliedDiscounts, decimal subTotalWithoutDiscount, decimal subTotalWithDiscount, SortedDictionary <decimal, decimal> taxRates)> GetShoppingCartSubTotalAsync( IList <ShoppingCartItem> cart, bool includingTax) { var discountAmount = decimal.Zero; var appliedDiscounts = new List <Discount>(); var subTotalWithoutDiscount = decimal.Zero; var subTotalWithDiscount = decimal.Zero; var taxRates = new SortedDictionary <decimal, decimal>(); if (!cart.Any()) { return(discountAmount, appliedDiscounts, subTotalWithoutDiscount, subTotalWithDiscount, taxRates); } //get the customer var customer = await _customerService.GetShoppingCartCustomerAsync(cart); //sub totals var subTotalExclTaxWithoutDiscount = decimal.Zero; var subTotalInclTaxWithoutDiscount = decimal.Zero; foreach (var shoppingCartItem in cart) { var sciSubTotal = (await _shoppingCartService.GetSubTotalAsync(shoppingCartItem, true)).subTotal; var product = await _productService.GetProductByIdAsync(shoppingCartItem.ProductId); var(sciExclTax, taxRate) = await _taxService.GetProductPriceAsync(product, sciSubTotal, false, customer); // custom var(_, sciInclTax) = await _warrantyTaxService.CalculateWarrantyTaxAsync(shoppingCartItem, customer, sciExclTax); subTotalExclTaxWithoutDiscount += sciExclTax; subTotalInclTaxWithoutDiscount += sciInclTax; //tax rates var sciTax = sciInclTax - sciExclTax; if (taxRate <= decimal.Zero || sciTax <= decimal.Zero) { continue; } if (!taxRates.ContainsKey(taxRate)) { taxRates.Add(taxRate, sciTax); } else { taxRates[taxRate] = taxRates[taxRate] + sciTax; } } //checkout attributes if (customer != null) { var checkoutAttributesXml = await _genericAttributeService.GetAttributeAsync <string>(customer, NopCustomerDefaults.CheckoutAttributes, (await _storeContext.GetCurrentStoreAsync()).Id); var attributeValues = _checkoutAttributeParser.ParseCheckoutAttributeValues(checkoutAttributesXml); if (attributeValues != null) { await foreach (var(attribute, values) in attributeValues) { await foreach (var attributeValue in values) { var(caExclTax, taxRate) = await _taxService.GetCheckoutAttributePriceAsync(attribute, attributeValue, false, customer); var(caInclTax, _) = await _taxService.GetCheckoutAttributePriceAsync(attribute, attributeValue, true, customer); subTotalExclTaxWithoutDiscount += caExclTax; subTotalInclTaxWithoutDiscount += caInclTax; //tax rates var caTax = caInclTax - caExclTax; if (taxRate <= decimal.Zero || caTax <= decimal.Zero) { continue; } if (!taxRates.ContainsKey(taxRate)) { taxRates.Add(taxRate, caTax); } else { taxRates[taxRate] = taxRates[taxRate] + caTax; } } } } } //subtotal without discount subTotalWithoutDiscount = includingTax ? subTotalInclTaxWithoutDiscount : subTotalExclTaxWithoutDiscount; if (subTotalWithoutDiscount < decimal.Zero) { subTotalWithoutDiscount = decimal.Zero; } if (_shoppingCartSettings.RoundPricesDuringCalculation) { subTotalWithoutDiscount = await _priceCalculationService.RoundPriceAsync(subTotalWithoutDiscount); } //We calculate discount amount on order subtotal excl tax (discount first) //calculate discount amount ('Applied to order subtotal' discount) decimal discountAmountExclTax; (discountAmountExclTax, appliedDiscounts) = await GetOrderSubtotalDiscountAsync(customer, subTotalExclTaxWithoutDiscount); if (subTotalExclTaxWithoutDiscount < discountAmountExclTax) { discountAmountExclTax = subTotalExclTaxWithoutDiscount; } var discountAmountInclTax = discountAmountExclTax; //subtotal with discount (excl tax) var subTotalExclTaxWithDiscount = subTotalExclTaxWithoutDiscount - discountAmountExclTax; var subTotalInclTaxWithDiscount = subTotalExclTaxWithDiscount; //add tax for shopping items & checkout attributes var tempTaxRates = new Dictionary <decimal, decimal>(taxRates); foreach (var kvp in tempTaxRates) { var taxRate = kvp.Key; var taxValue = kvp.Value; if (taxValue == decimal.Zero) { continue; } //discount the tax amount that applies to subtotal items if (subTotalExclTaxWithoutDiscount > decimal.Zero) { var discountTax = taxRates[taxRate] * (discountAmountExclTax / subTotalExclTaxWithoutDiscount); discountAmountInclTax += discountTax; taxValue = taxRates[taxRate] - discountTax; if (_shoppingCartSettings.RoundPricesDuringCalculation) { taxValue = await _priceCalculationService.RoundPriceAsync(taxValue); } taxRates[taxRate] = taxValue; } //subtotal with discount (incl tax) subTotalInclTaxWithDiscount += taxValue; } if (_shoppingCartSettings.RoundPricesDuringCalculation) { discountAmountInclTax = await _priceCalculationService.RoundPriceAsync(discountAmountInclTax); discountAmountExclTax = await _priceCalculationService.RoundPriceAsync(discountAmountExclTax); } if (includingTax) { subTotalWithDiscount = subTotalInclTaxWithDiscount; discountAmount = discountAmountInclTax; } else { subTotalWithDiscount = subTotalExclTaxWithDiscount; discountAmount = discountAmountExclTax; } if (subTotalWithDiscount < decimal.Zero) { subTotalWithDiscount = decimal.Zero; } if (_shoppingCartSettings.RoundPricesDuringCalculation) { subTotalWithDiscount = await _priceCalculationService.RoundPriceAsync(subTotalWithDiscount); } return(discountAmount, appliedDiscounts, subTotalWithoutDiscount, subTotalWithDiscount, taxRates); }
public override async Task MapAsync(IEnumerable <OrganizedShoppingCartItem> from, ShoppingCartModel to, dynamic parameters = null) { Guard.NotNull(from, nameof(from)); Guard.NotNull(to, nameof(to)); if (!from.Any()) { return; } await base.MapAsync(from, to, null); var store = _services.StoreContext.CurrentStore; var customer = _services.WorkContext.CurrentCustomer; var currency = _services.WorkContext.WorkingCurrency; var isEditable = parameters?.IsEditable == true; var validateCheckoutAttributes = parameters?.ValidateCheckoutAttributes == true; var prepareEstimateShippingIfEnabled = parameters?.PrepareEstimateShippingIfEnabled == true; var setEstimateShippingDefaultAddress = parameters?.SetEstimateShippingDefaultAddress == true; var prepareAndDisplayOrderReviewData = parameters?.PrepareAndDisplayOrderReviewData == true; #region Simple properties to.MediaDimensions = _mediaSettings.CartThumbPictureSize; to.DeliveryTimesPresentation = _shoppingCartSettings.DeliveryTimesInShoppingCart; to.DisplayBasePrice = _shoppingCartSettings.ShowBasePrice; to.DisplayWeight = _shoppingCartSettings.ShowWeight; to.DisplayMoveToWishlistButton = await _services.Permissions.AuthorizeAsync(Permissions.Cart.AccessWishlist); to.TermsOfServiceEnabled = _orderSettings.TermsOfServiceEnabled; to.DisplayCommentBox = _shoppingCartSettings.ShowCommentBox; to.DisplayEsdRevocationWaiverBox = _shoppingCartSettings.ShowEsdRevocationWaiverBox; to.IsEditable = isEditable; var measure = await _db.MeasureWeights.FindByIdAsync(_measureSettings.BaseWeightId, false); if (measure != null) { to.MeasureUnitName = measure.GetLocalized(x => x.Name); } to.CheckoutAttributeInfo = HtmlUtils.ConvertPlainTextToTable( HtmlUtils.ConvertHtmlToPlainText( await _checkoutAttributeFormatter.FormatAttributesAsync(customer.GenericAttributes.CheckoutAttributes, customer))); // Gift card and gift card boxes. to.DiscountBox.Display = _shoppingCartSettings.ShowDiscountBox; var discountCouponCode = customer.GenericAttributes.DiscountCouponCode; var discount = await _db.Discounts .AsNoTracking() .Where(x => x.CouponCode == discountCouponCode) .FirstOrDefaultAsync(); if (discount != null && discount.RequiresCouponCode && await _discountService.IsDiscountValidAsync(discount, customer)) { to.DiscountBox.CurrentCode = discount.CouponCode; } to.GiftCardBox.Display = _shoppingCartSettings.ShowGiftCardBox; // Reward points. if (_rewardPointsSettings.Enabled && !from.IncludesMatchingItems(x => x.IsRecurring) && !customer.IsGuest()) { var rewardPointsBalance = customer.GetRewardPointsBalance(); var rewardPointsAmountBase = _orderCalculationService.ConvertRewardPointsToAmount(rewardPointsBalance); var rewardPointsAmount = _currencyService.ConvertFromPrimaryCurrency(rewardPointsAmountBase.Amount, currency); if (rewardPointsAmount > decimal.Zero) { to.RewardPoints.DisplayRewardPoints = true; to.RewardPoints.RewardPointsAmount = rewardPointsAmount.ToString(true); to.RewardPoints.RewardPointsBalance = rewardPointsBalance; to.RewardPoints.UseRewardPoints = customer.GenericAttributes.UseRewardPointsDuringCheckout; } } // Cart warnings. var warnings = new List <string>(); var cartIsValid = await _shoppingCartValidator.ValidateCartItemsAsync(from, warnings, validateCheckoutAttributes, customer.GenericAttributes.CheckoutAttributes); if (!cartIsValid) { to.Warnings.AddRange(warnings); } #endregion #region Checkout attributes var checkoutAttributes = await _checkoutAttributeMaterializer.GetValidCheckoutAttributesAsync(from); foreach (var attribute in checkoutAttributes) { var caModel = new ShoppingCartModel.CheckoutAttributeModel { Id = attribute.Id, Name = attribute.GetLocalized(x => x.Name), TextPrompt = attribute.GetLocalized(x => x.TextPrompt), IsRequired = attribute.IsRequired, AttributeControlType = attribute.AttributeControlType }; if (attribute.IsListTypeAttribute) { var caValues = await _db.CheckoutAttributeValues .AsNoTracking() .Where(x => x.CheckoutAttributeId == attribute.Id) .ToListAsync(); // Prepare each attribute with image and price foreach (var caValue in caValues) { var pvaValueModel = new ShoppingCartModel.CheckoutAttributeValueModel { Id = caValue.Id, Name = caValue.GetLocalized(x => x.Name), IsPreSelected = caValue.IsPreSelected, Color = caValue.Color }; if (caValue.MediaFileId.HasValue && caValue.MediaFile != null) { pvaValueModel.ImageUrl = _mediaService.GetUrl(caValue.MediaFile, _mediaSettings.VariantValueThumbPictureSize, null, false); } caModel.Values.Add(pvaValueModel); // Display price if allowed. if (await _services.Permissions.AuthorizeAsync(Permissions.Catalog.DisplayPrice)) { var priceAdjustmentBase = await _taxService.GetCheckoutAttributePriceAsync(caValue); var priceAdjustment = _currencyService.ConvertFromPrimaryCurrency(priceAdjustmentBase.Price.Amount, currency); if (priceAdjustmentBase.Price > decimal.Zero) { pvaValueModel.PriceAdjustment = "+" + priceAdjustmentBase.Price.ToString(); } else if (priceAdjustmentBase.Price < decimal.Zero) { pvaValueModel.PriceAdjustment = "-" + priceAdjustmentBase.Price.ToString(); } } } } // Set already selected attributes. var selectedCheckoutAttributes = customer.GenericAttributes.CheckoutAttributes; switch (attribute.AttributeControlType) { case AttributeControlType.DropdownList: case AttributeControlType.RadioList: case AttributeControlType.Boxes: case AttributeControlType.Checkboxes: if (selectedCheckoutAttributes.AttributesMap.Any()) { // Clear default selection. foreach (var item in caModel.Values) { item.IsPreSelected = false; } // Select new values. var selectedCaValues = await _checkoutAttributeMaterializer.MaterializeCheckoutAttributeValuesAsync(selectedCheckoutAttributes); foreach (var caValue in selectedCaValues) { foreach (var item in caModel.Values) { if (caValue.Id == item.Id) { item.IsPreSelected = true; } } } } break; case AttributeControlType.TextBox: case AttributeControlType.MultilineTextbox: if (selectedCheckoutAttributes.AttributesMap.Any()) { var enteredText = selectedCheckoutAttributes.AttributesMap .Where(x => x.Key == attribute.Id) .SelectMany(x => x.Value) .FirstOrDefault() .ToString(); if (enteredText.HasValue()) { caModel.TextValue = enteredText; } } break; case AttributeControlType.Datepicker: { // Keep in mind my that the code below works only in the current culture. var enteredDate = selectedCheckoutAttributes.AttributesMap .Where(x => x.Key == attribute.Id) .SelectMany(x => x.Value) .FirstOrDefault() .ToString(); if (enteredDate.HasValue() && DateTime.TryParseExact(enteredDate, "D", CultureInfo.CurrentCulture, DateTimeStyles.None, out var selectedDate)) { caModel.SelectedDay = selectedDate.Day; caModel.SelectedMonth = selectedDate.Month; caModel.SelectedYear = selectedDate.Year; } } break; case AttributeControlType.FileUpload: if (selectedCheckoutAttributes.AttributesMap.Any()) { var FileValue = selectedCheckoutAttributes.AttributesMap .Where(x => x.Key == attribute.Id) .SelectMany(x => x.Value) .FirstOrDefault() .ToString(); if (FileValue.HasValue() && caModel.UploadedFileGuid.HasValue() && Guid.TryParse(caModel.UploadedFileGuid, out var guid)) { var download = await _db.Downloads .Include(x => x.MediaFile) .FirstOrDefaultAsync(x => x.DownloadGuid == guid); if (download != null && !download.UseDownloadUrl && download.MediaFile != null) { caModel.UploadedFileName = download.MediaFile.Name; } } } break; default: break; } to.CheckoutAttributes.Add(caModel); } #endregion #region Estimate shipping if (prepareEstimateShippingIfEnabled) { to.EstimateShipping.Enabled = _shippingSettings.EstimateShippingEnabled && from.Any() && from.IncludesMatchingItems(x => x.IsShippingEnabled); if (to.EstimateShipping.Enabled) { // Countries. var defaultEstimateCountryId = setEstimateShippingDefaultAddress && customer.ShippingAddress != null ? customer.ShippingAddress.CountryId : to.EstimateShipping.CountryId; var countriesForShipping = await _db.Countries .AsNoTracking() .ApplyStoreFilter(store.Id) .Where(x => x.AllowsShipping) .ToListAsync(); foreach (var countries in countriesForShipping) { to.EstimateShipping.AvailableCountries.Add(new SelectListItem { Text = countries.GetLocalized(x => x.Name), Value = countries.Id.ToString(), Selected = countries.Id == defaultEstimateCountryId }); } // States. var states = defaultEstimateCountryId.HasValue ? await _db.StateProvinces.AsNoTracking().ApplyCountryFilter(defaultEstimateCountryId.Value).ToListAsync() : new(); if (states.Any()) { var defaultEstimateStateId = setEstimateShippingDefaultAddress && customer.ShippingAddress != null ? customer.ShippingAddress.StateProvinceId : to.EstimateShipping.StateProvinceId; foreach (var s in states) { to.EstimateShipping.AvailableStates.Add(new SelectListItem { Text = s.GetLocalized(x => x.Name), Value = s.Id.ToString(), Selected = s.Id == defaultEstimateStateId }); } } else { to.EstimateShipping.AvailableStates.Add(new SelectListItem { Text = T("Address.OtherNonUS"), Value = "0" }); } if (setEstimateShippingDefaultAddress && customer.ShippingAddress != null) { to.EstimateShipping.ZipPostalCode = customer.ShippingAddress.ZipPostalCode; } } } #endregion #region Cart items foreach (var cartItem in from) { var model = new ShoppingCartModel.ShoppingCartItemModel(); await cartItem.MapAsync(model); to.AddItems(model); } #endregion #region Order review data if (prepareAndDisplayOrderReviewData) { var checkoutState = _httpContextAccessor.HttpContext?.GetCheckoutState(); to.OrderReviewData.Display = true; // Billing info. var billingAddress = customer.BillingAddress; if (billingAddress != null) { await MapperFactory.MapAsync(billingAddress, to.OrderReviewData.BillingAddress); } // Shipping info. if (from.IsShippingRequired()) { to.OrderReviewData.IsShippable = true; var shippingAddress = customer.ShippingAddress; if (shippingAddress != null) { await MapperFactory.MapAsync(shippingAddress, to.OrderReviewData.ShippingAddress); } // Selected shipping method. var shippingOption = customer.GenericAttributes.SelectedShippingOption; if (shippingOption != null) { to.OrderReviewData.ShippingMethod = shippingOption.Name; } if (checkoutState != null && checkoutState.CustomProperties.ContainsKey("HasOnlyOneActiveShippingMethod")) { to.OrderReviewData.DisplayShippingMethodChangeOption = !(bool)checkoutState.CustomProperties.Get("HasOnlyOneActiveShippingMethod"); } } if (checkoutState != null && checkoutState.CustomProperties.ContainsKey("HasOnlyOneActivePaymentMethod")) { to.OrderReviewData.DisplayPaymentMethodChangeOption = !(bool)checkoutState.CustomProperties.Get("HasOnlyOneActivePaymentMethod"); } var selectedPaymentMethodSystemName = customer.GenericAttributes.SelectedPaymentMethod; var paymentMethod = await _paymentService.LoadPaymentMethodBySystemNameAsync(selectedPaymentMethodSystemName); // TODO: (ms) (core) Wait for PluginMediator.GetLocalizedFriendlyName implementation //model.OrderReviewData.PaymentMethod = paymentMethod != null ? _pluginMediator.GetLocalizedFriendlyName(paymentMethod.Metadata) : ""; to.OrderReviewData.PaymentSummary = checkoutState.PaymentSummary; to.OrderReviewData.IsPaymentSelectionSkipped = checkoutState.IsPaymentSelectionSkipped; } #endregion var paymentTypes = new PaymentMethodType[] { PaymentMethodType.Button, PaymentMethodType.StandardAndButton }; var boundPaymentMethods = await _paymentService.LoadActivePaymentMethodsAsync( customer, from.ToList(), store.Id, paymentTypes, false); var bpmModel = new ButtonPaymentMethodModel(); foreach (var boundPaymentMethod in boundPaymentMethods) { if (from.IncludesMatchingItems(x => x.IsRecurring) && boundPaymentMethod.Value.RecurringPaymentType == RecurringPaymentType.NotSupported) { continue; } var widgetInvoker = boundPaymentMethod.Value.GetPaymentInfoWidget(); bpmModel.Items.Add(widgetInvoker); } to.ButtonPaymentMethods = bpmModel; }