public async Task HandleEventAsync(ValidatingCartEvent message) { // Order total validation. var roleMappings = _workContext.CurrentImpersonator?.CustomerRoleMappings ?? message.Customer.CustomerRoleMappings; var result = await _orderProcessingService.ValidateOrderTotalAsync(message.Cart, roleMappings.Select(x => x.CustomerRole).ToArray()); if (!result.IsAboveMinimum) { var convertedMin = _currencyService.ConvertFromPrimaryCurrency(result.OrderTotalMinimum, _workContext.WorkingCurrency); message.Warnings.Add(_localizationService.GetResource("Checkout.MinOrderSubtotalAmount").FormatInvariant(convertedMin.ToString(true))); } if (!result.IsBelowMaximum) { var convertedMax = _currencyService.ConvertFromPrimaryCurrency(result.OrderTotalMaximum, _workContext.WorkingCurrency); message.Warnings.Add(_localizationService.GetResource("Checkout.MaxOrderSubtotalAmount").FormatInvariant(convertedMax.ToString(true))); } }
public async Task <IViewComponentResult> InvokeAsync(bool isEditable = false) { var orderTotalsEvent = new RenderingOrderTotalsEvent(); await Services.EventPublisher.PublishAsync(orderTotalsEvent); var currency = Services.WorkContext.WorkingCurrency; var customer = orderTotalsEvent.Customer ?? Services.WorkContext.CurrentCustomer; var storeId = orderTotalsEvent.StoreId ?? Services.StoreContext.CurrentStore.Id; var cart = await _shoppingCartService.GetCartItemsAsync(customer, ShoppingCartType.ShoppingCart, storeId); var model = new OrderTotalsModel { IsEditable = isEditable }; if (!cart.Any()) { return(View(model)); } model.Weight = decimal.Zero; foreach (var cartItem in cart) { model.Weight += cartItem.Item.Product.Weight * cartItem.Item.Quantity; } var measureWeight = await _db.MeasureWeights.FindByIdAsync(_measureSettings.BaseWeightId, false); if (measureWeight != null) { model.WeightMeasureUnitName = measureWeight.GetLocalized(x => x.Name); } // SubTotal var cartSubTotal = await _orderCalculationService.GetShoppingCartSubtotalAsync(cart); var subTotalConverted = _currencyService.ConvertFromPrimaryCurrency(cartSubTotal.SubtotalWithoutDiscount.Amount, currency); model.SubTotal = subTotalConverted; if (cartSubTotal.DiscountAmount > decimal.Zero) { var subTotalDiscountAmountConverted = _currencyService.ConvertFromPrimaryCurrency(cartSubTotal.DiscountAmount.Amount, currency); model.SubTotalDiscount = subTotalDiscountAmountConverted * -1; model.AllowRemovingSubTotalDiscount = cartSubTotal.AppliedDiscount != null && cartSubTotal.AppliedDiscount.RequiresCouponCode && cartSubTotal.AppliedDiscount.CouponCode.HasValue() && model.IsEditable; } // Shipping info // TODO: (ms) (core) Re-implement when any payment method has been implemented model.RequiresShipping = false;// cart.IsShippingRequired(); if (model.RequiresShipping) { var shippingTotal = await _orderCalculationService.GetShoppingCartShippingTotalAsync(cart); if (shippingTotal.ShippingTotal.HasValue) { var shippingTotalConverted = _currencyService.ConvertFromPrimaryCurrency(shippingTotal.ShippingTotal.Value.Amount, currency); model.Shipping = shippingTotalConverted.ToString(); // Selected shipping method var shippingOption = customer.GenericAttributes.SelectedShippingOption; if (shippingOption != null) { model.SelectedShippingMethod = shippingOption.Name; } } } // Payment method fee var paymentFee = await _orderCalculationService.GetShoppingCartPaymentFeeAsync(cart, customer.GenericAttributes.SelectedPaymentMethod); var paymentFeeTax = await _taxCalculator.CalculatePaymentFeeTaxAsync(paymentFee.Amount, customer : customer); if (paymentFeeTax.Price != 0m) { var convertedPaymentFeeTax = _currencyService.ConvertFromPrimaryCurrency(paymentFeeTax.Price, currency); model.PaymentMethodAdditionalFee = convertedPaymentFeeTax; } // Tax var displayTax = true; var displayTaxRates = true; if (_taxSettings.HideTaxInOrderSummary && Services.WorkContext.TaxDisplayType == TaxDisplayType.IncludingTax) { displayTax = false; displayTaxRates = false; } else { (Money Price, TaxRatesDictionary TaxRates)cartTaxBase = (new Money(1.19m, currency), new TaxRatesDictionary()); //await _orderCalculationService.GetShoppingCartTaxTotalAsync(cart); var cartTax = _currencyService.ConvertFromPrimaryCurrency(cartTaxBase.Price.Amount, currency); if (cartTaxBase.Price == decimal.Zero && _taxSettings.HideZeroTax) { displayTax = false; displayTaxRates = false; } else { displayTaxRates = _taxSettings.DisplayTaxRates && cartTaxBase.TaxRates.Count > 0; displayTax = !displayTaxRates; model.Tax = cartTax.ToString(true); foreach (var taxRate in cartTaxBase.TaxRates) { var rate = _taxService.FormatTaxRate(taxRate.Key); var labelKey = "ShoppingCart.Totals.TaxRateLine" + (Services.WorkContext.TaxDisplayType == TaxDisplayType.IncludingTax ? "Incl" : "Excl"); model.TaxRates.Add(new OrderTotalsModel.TaxRate { Rate = rate, Value = _currencyService.ConvertFromPrimaryCurrency(taxRate.Value, currency), Label = _localizationService.GetResource(labelKey).FormatCurrent(rate) }); } } } model.DisplayTaxRates = displayTaxRates; model.DisplayTax = displayTax; model.DisplayWeight = _shoppingCartSettings.ShowWeight; model.ShowConfirmOrderLegalHint = _shoppingCartSettings.ShowConfirmOrderLegalHint; // Cart total // TODO: (ms) (core) Re-implement when any payment method has been implemented //var cartTotal = await _orderCalculationService.GetShoppingCartTotalAsync(cart); var cartTotal = new ShoppingCartTotal { Total = new(decimal.Zero, currency), ToNearestRounding = new(decimal.Zero, currency), DiscountAmount = new(15, currency), AppliedDiscount = (await _db.Discounts.FirstOrDefaultAsync()) ?? new(), RedeemedRewardPoints = 10, RedeemedRewardPointsAmount = new(10, currency), CreditBalance = new(decimal.Zero, currency), AppliedGiftCards = new() { new() { GiftCard = (await _db.GiftCards.Include(x => x.GiftCardUsageHistory).FirstOrDefaultAsync()) ?? new(), UsableAmount = new(50m, _currencyService.PrimaryCurrency) } }, ConvertedAmount = new ShoppingCartTotal.ConvertedAmounts { Total = new(decimal.Zero, currency), ToNearestRounding = new(decimal.Zero, currency) } }; if (cartTotal.ConvertedAmount.Total.HasValue) { model.OrderTotal = cartTotal.ConvertedAmount.Total.Value; if (cartTotal.ConvertedAmount.ToNearestRounding != decimal.Zero) { model.OrderTotalRounding = cartTotal.ConvertedAmount.ToNearestRounding; } } // Discount if (cartTotal.DiscountAmount > decimal.Zero) { var orderTotalDiscountAmount = _currencyService.ConvertFromPrimaryCurrency(cartTotal.DiscountAmount.Amount, currency); model.OrderTotalDiscount = orderTotalDiscountAmount * -1; model.AllowRemovingOrderTotalDiscount = cartTotal.AppliedDiscount != null && cartTotal.AppliedDiscount.RequiresCouponCode && cartTotal.AppliedDiscount.CouponCode.HasValue() && model.IsEditable; } // Gift cards if (!cartTotal.AppliedGiftCards.IsNullOrEmpty()) { foreach (var appliedGiftCard in cartTotal.AppliedGiftCards) { if (appliedGiftCard?.GiftCard == null) { continue; } var gcModel = new OrderTotalsModel.GiftCard { Id = appliedGiftCard.GiftCard.Id, CouponCode = appliedGiftCard.GiftCard.GiftCardCouponCode, }; var amountCanBeUsed = _currencyService.ConvertFromPrimaryCurrency(appliedGiftCard.UsableAmount.Amount, currency); gcModel.Amount = amountCanBeUsed * -1; var remainingAmountBase = _giftCardService.GetRemainingAmount(appliedGiftCard.GiftCard) - appliedGiftCard.UsableAmount; var remainingAmount = _currencyService.ConvertFromPrimaryCurrency(remainingAmountBase.Amount, currency); gcModel.Remaining = remainingAmount; model.GiftCards.Add(gcModel); } } // Reward points if (cartTotal.RedeemedRewardPointsAmount > decimal.Zero) { var redeemedRewardPointsAmountInCustomerCurrency = _currencyService.ConvertFromPrimaryCurrency(cartTotal.RedeemedRewardPointsAmount.Amount, currency); model.RedeemedRewardPoints = cartTotal.RedeemedRewardPoints; model.RedeemedRewardPointsAmount = (redeemedRewardPointsAmountInCustomerCurrency * -1).ToString(true); } // Credit balance. if (cartTotal.CreditBalance > decimal.Zero) { var convertedCreditBalance = _currencyService.ConvertFromPrimaryCurrency(cartTotal.CreditBalance.Amount, currency); model.CreditBalance = (convertedCreditBalance * -1).ToString(true); } return(View(model)); }
private async Task <CalculatedPrice> CreateCalculatedPrice(CalculatorContext context, Product product = null, int subtotalQuantity = 1) { product ??= context.Product; var options = context.Options; // Calculate the subtotal price instead of the unit price. if (subtotalQuantity > 1 && context.FinalPrice > 0) { context.FinalPrice = options.RoundingCurrency.RoundIfEnabledFor(context.FinalPrice) * subtotalQuantity; context.DiscountAmount = options.RoundingCurrency.RoundIfEnabledFor(context.DiscountAmount) * subtotalQuantity; } // Determine tax rate for product. var taxRate = await _taxService.GetTaxRateAsync(product, null, options.Customer); // Prepare result by converting price amounts. var result = new CalculatedPrice(context) { Product = product, RegularPrice = ConvertAmount(context.RegularPrice, context, taxRate, false, out _).Value, OfferPrice = ConvertAmount(context.OfferPrice, context, taxRate, false, out _), PreselectedPrice = ConvertAmount(context.PreselectedPrice, context, taxRate, false, out _), LowestPrice = ConvertAmount(context.LowestPrice, context, taxRate, false, out _), DiscountAmount = ConvertAmount(context.DiscountAmount, context, taxRate, false, out _).Value, FinalPrice = ConvertAmount(context.FinalPrice, context, taxRate, true, out var tax).Value, Tax = tax }; if (tax.HasValue && _primaryCurrency != options.TargetCurrency) { // Exchange tax amounts. // TODO: (mg) (core) Check for rounding issues thoroughly! result.Tax = new Tax( tax.Value.Rate, // Amount _currencyService.ConvertFromPrimaryCurrency(tax.Value.Amount, options.TargetCurrency).Amount, // Price result.FinalPrice.Amount, tax.Value.IsGrossPrice, tax.Value.Inclusive); } // Convert attribute price adjustments. context.AttributePriceAdjustments.Each(x => x.Price = ConvertAmount(x.RawPriceAdjustment, context, taxRate, false, out _).Value); // Calculate price saving. // The final price without discounts has priority over the old price. // This avoids differing percentage discount in product lists and detail page. var priceWithoutDiscount = result.FinalPrice + result.DiscountAmount; var savingPrice = result.FinalPrice < priceWithoutDiscount ? priceWithoutDiscount : ConvertAmount(product.OldPrice, context, taxRate, false, out _).Value; var hasSaving = savingPrice > 0 && result.FinalPrice < savingPrice; result.PriceSaving = new PriceSaving { HasSaving = hasSaving, SavingPrice = savingPrice, SavingPercent = hasSaving ? (float)((savingPrice - result.FinalPrice) / savingPrice) * 100 : 0f, SavingAmount = hasSaving ? (savingPrice - result.FinalPrice).WithPostFormat(null) : null }; return(result); }
private async Task <WishlistModel.ShoppingCartItemModel> PrepareWishlistCartItemModelAsync(OrganizedShoppingCartItem cartItem) { Guard.NotNull(cartItem, nameof(cartItem)); var item = cartItem.Item; var product = item.Product; var customer = item.Customer; var currency = Services.WorkContext.WorkingCurrency; await _productAttributeMaterializer.MergeWithCombinationAsync(product, item.AttributeSelection); var productSeName = await SeoExtensions.GetActiveSlugAsync(product); var model = new WishlistModel.ShoppingCartItemModel { Id = item.Id, Sku = product.Sku, ProductId = product.Id, ProductName = product.GetLocalized(x => x.Name), ProductSeName = productSeName, ProductUrl = await _productUrlHelper.GetProductUrlAsync(productSeName, cartItem), EnteredQuantity = item.Quantity, MinOrderAmount = product.OrderMinimumQuantity, MaxOrderAmount = product.OrderMaximumQuantity, QuantityStep = product.QuantityStep > 0 ? product.QuantityStep : 1, ShortDesc = product.GetLocalized(x => x.ShortDescription), ProductType = product.ProductType, VisibleIndividually = product.Visibility != ProductVisibility.Hidden, CreatedOnUtc = item.UpdatedOnUtc, DisableBuyButton = product.DisableBuyButton, }; if (item.BundleItem != null) { model.BundleItem.Id = item.BundleItem.Id; model.BundleItem.DisplayOrder = item.BundleItem.DisplayOrder; model.BundleItem.HideThumbnail = item.BundleItem.HideThumbnail; model.BundlePerItemPricing = item.BundleItem.BundleProduct.BundlePerItemPricing; model.BundlePerItemShoppingCart = item.BundleItem.BundleProduct.BundlePerItemShoppingCart; model.AttributeInfo = await _productAttributeFormatter.FormatAttributesAsync(item.AttributeSelection, product, customer, includePrices : false, includeGiftCardAttributes : false, includeHyperlinks : false); var bundleItemName = item.BundleItem.GetLocalized(x => x.Name); if (bundleItemName.HasValue()) { model.ProductName = bundleItemName; } var bundleItemShortDescription = item.BundleItem.GetLocalized(x => x.ShortDescription); if (bundleItemShortDescription.HasValue()) { model.ShortDesc = bundleItemShortDescription; } if (model.BundlePerItemPricing && model.BundlePerItemShoppingCart) { (var bundleItemPriceBase, var bundleItemTaxRate) = await _taxService.GetProductPriceAsync(product, await _priceCalculationService.GetSubTotalAsync(cartItem, true)); var bundleItemPrice = _currencyService.ConvertFromPrimaryCurrency(bundleItemPriceBase.Amount, currency); model.BundleItem.PriceWithDiscount = bundleItemPrice.ToString(); } } else { model.AttributeInfo = await _productAttributeFormatter.FormatAttributesAsync(item.AttributeSelection, product, customer); } var allowedQuantities = product.ParseAllowedQuantities(); foreach (var qty in allowedQuantities) { model.AllowedQuantities.Add(new SelectListItem { Text = qty.ToString(), Value = qty.ToString(), Selected = item.Quantity == qty }); } var quantityUnit = await _db.QuantityUnits.FindByIdAsync(product.QuantityUnitId ?? 0); if (quantityUnit != null) { model.QuantityUnitName = quantityUnit.GetLocalized(x => x.Name); } if (product.IsRecurring) { model.RecurringInfo = string.Format(T("ShoppingCart.RecurringPeriod"), product.RecurringCycleLength, product.RecurringCyclePeriod.GetLocalizedEnum()); } if (product.CallForPrice) { model.UnitPrice = T("Products.CallForPrice"); } else { var unitPriceWithDiscount = await _priceCalculationService.GetUnitPriceAsync(cartItem, true); var unitPriceBaseWithDiscount = await _taxService.GetProductPriceAsync(product, unitPriceWithDiscount); unitPriceWithDiscount = _currencyService.ConvertFromPrimaryCurrency(unitPriceBaseWithDiscount.Price.Amount, currency); model.UnitPrice = unitPriceWithDiscount.ToString(); } // Subtotal and discount. if (product.CallForPrice) { model.SubTotal = T("Products.CallForPrice"); } else { var cartItemSubTotalWithDiscount = await _priceCalculationService.GetSubTotalAsync(cartItem, true); var cartItemSubTotalWithDiscountBase = await _taxService.GetProductPriceAsync(product, cartItemSubTotalWithDiscount); cartItemSubTotalWithDiscount = _currencyService.ConvertFromPrimaryCurrency(cartItemSubTotalWithDiscountBase.Price.Amount, currency); model.SubTotal = cartItemSubTotalWithDiscount.ToString(); // Display an applied discount amount. var cartItemSubTotalWithoutDiscount = await _priceCalculationService.GetSubTotalAsync(cartItem, false); var cartItemSubTotalWithoutDiscountBase = await _taxService.GetProductPriceAsync(product, cartItemSubTotalWithoutDiscount); var cartItemSubTotalDiscountBase = cartItemSubTotalWithoutDiscountBase.Price - cartItemSubTotalWithDiscountBase.Price; if (cartItemSubTotalDiscountBase > decimal.Zero) { var shoppingCartItemDiscount = _currencyService.ConvertFromPrimaryCurrency(cartItemSubTotalDiscountBase.Amount, currency); model.Discount = shoppingCartItemDiscount.ToString(); } } if (item.BundleItem != null) { if (_shoppingCartSettings.ShowProductBundleImagesOnShoppingCart) { model.Image = await PrepareCartItemPictureModelAsync(product, _mediaSettings.CartThumbBundleItemPictureSize, model.ProductName, item.AttributeSelection); } } else { if (_shoppingCartSettings.ShowProductImagesOnShoppingCart) { model.Image = await PrepareCartItemPictureModelAsync(product, _mediaSettings.CartThumbPictureSize, model.ProductName, item.AttributeSelection); } } var itemWarnings = new List <string>(); var itemIsValid = await _shoppingCartValidator.ValidateCartAsync(new List <OrganizedShoppingCartItem> { cartItem }, itemWarnings); if (!itemIsValid) { model.Warnings.AddRange(itemWarnings); } if (cartItem.ChildItems != null) { foreach (var childItem in cartItem.ChildItems.Where(x => x.Item.Id != item.Id)) { var childModel = await PrepareWishlistCartItemModelAsync(childItem); model.ChildItems.Add(childModel); } } return(model); }
private async Task <OrderDetailsModel.OrderItemModel> PrepareOrderItemModelAsync( Order order, OrderItem orderItem, CatalogSettings catalogSettings, ShoppingCartSettings shoppingCartSettings, MediaSettings mediaSettings, Currency customerCurrency) { var language = _services.WorkContext.WorkingLanguage; var attributeCombination = await _productAttributeMaterializer.FindAttributeCombinationAsync(orderItem.ProductId, orderItem.AttributeSelection); if (attributeCombination != null) { orderItem.Product.MergeWithCombination(attributeCombination); } var model = new OrderDetailsModel.OrderItemModel { Id = orderItem.Id, Sku = orderItem.Product.Sku, ProductId = orderItem.Product.Id, ProductName = orderItem.Product.GetLocalized(x => x.Name), ProductSeName = await orderItem.Product.GetActiveSlugAsync(), ProductType = orderItem.Product.ProductType, Quantity = orderItem.Quantity, AttributeInfo = orderItem.AttributeDescription }; var quantityUnit = await _db.QuantityUnits.FindByIdAsync(orderItem.Product.QuantityUnitId ?? 0, false); model.QuantityUnit = quantityUnit == null ? string.Empty : quantityUnit.GetLocalized(x => x.Name); if (orderItem.Product.ProductType == ProductType.BundledProduct && orderItem.BundleData.HasValue()) { var bundleData = orderItem.GetBundleData(); var bundleProducts = await _db.ProductBundleItem .AsNoTracking() .Include(x => x.Product) .ApplyBundledProductsFilter(new int[] { orderItem.ProductId }) .Select(x => new ProductBundleItemData(x)) .ToListAsync(); var bundleItems = shoppingCartSettings.ShowProductBundleImagesOnShoppingCart ? bundleProducts.ToDictionarySafe(x => x.Item.ProductId) : new Dictionary <int, ProductBundleItemData>(); model.BundlePerItemPricing = orderItem.Product.BundlePerItemPricing; model.BundlePerItemShoppingCart = bundleData.Any(x => x.PerItemShoppingCart); foreach (var bid in bundleData) { var bundleItemModel = new OrderDetailsModel.BundleItemModel { Sku = bid.Sku, ProductName = bid.ProductName, ProductSeName = bid.ProductSeName, VisibleIndividually = bid.VisibleIndividually, Quantity = bid.Quantity, DisplayOrder = bid.DisplayOrder, AttributeInfo = bid.AttributesInfo }; bundleItemModel.ProductUrl = await _productUrlHelper.GetProductUrlAsync(bid.ProductId, bundleItemModel.ProductSeName, bid.AttributeSelection); if (model.BundlePerItemShoppingCart) { var priceWithDiscount = _currencyService.ConvertFromPrimaryCurrency(bid.PriceWithDiscount, customerCurrency); //bundleItemModel.PriceWithDiscount = _priceFormatter.FormatPrice(priceWithDiscount, true, order.CustomerCurrencyCode, language, false, false); bundleItemModel.PriceWithDiscount = priceWithDiscount.ToString(); } // Bundle item picture. if (shoppingCartSettings.ShowProductBundleImagesOnShoppingCart && bundleItems.TryGetValue(bid.ProductId, out var bundleItem)) { bundleItemModel.HideThumbnail = bundleItem.Item.HideThumbnail; bundleItemModel.Image = await PrepareOrderItemImageModelAsync( bundleItem.Item.Product, mediaSettings.CartThumbBundleItemPictureSize, bid.ProductName, bid.AttributeSelection, catalogSettings); } model.BundleItems.Add(bundleItemModel); } } // Unit price, subtotal. switch (order.CustomerTaxDisplayType) { case TaxDisplayType.ExcludingTax: { var unitPriceExclTaxInCustomerCurrency = _currencyService.ConvertFromPrimaryCurrency(orderItem.UnitPriceExclTax, customerCurrency); //model.UnitPrice = _priceFormatter.FormatPrice(unitPriceExclTaxInCustomerCurrency, true, order.CustomerCurrencyCode, language, false, false); model.UnitPrice = unitPriceExclTaxInCustomerCurrency.ToString(); var priceExclTaxInCustomerCurrency = _currencyService.ConvertFromPrimaryCurrency(orderItem.PriceExclTax, customerCurrency); //model.SubTotal = _priceFormatter.FormatPrice(priceExclTaxInCustomerCurrency, true, order.CustomerCurrencyCode, language, false, false); model.SubTotal = priceExclTaxInCustomerCurrency.ToString(); } break; case TaxDisplayType.IncludingTax: { var unitPriceInclTaxInCustomerCurrency = _currencyService.ConvertFromPrimaryCurrency(orderItem.UnitPriceInclTax, customerCurrency); //model.UnitPrice = _priceFormatter.FormatPrice(unitPriceInclTaxInCustomerCurrency, true, order.CustomerCurrencyCode, language, true, false); model.UnitPrice = unitPriceInclTaxInCustomerCurrency.ToString(); var priceInclTaxInCustomerCurrency = _currencyService.ConvertFromPrimaryCurrency(orderItem.PriceInclTax, customerCurrency); //model.SubTotal = _priceFormatter.FormatPrice(priceInclTaxInCustomerCurrency, true, order.CustomerCurrencyCode, language, true, false); model.SubTotal = priceInclTaxInCustomerCurrency.ToString(); } break; } model.ProductUrl = await _productUrlHelper.GetProductUrlAsync(orderItem.ProductId, model.ProductSeName, orderItem.AttributeSelection); if (shoppingCartSettings.ShowProductImagesOnShoppingCart) { model.Image = await PrepareOrderItemImageModelAsync( orderItem.Product, mediaSettings.CartThumbPictureSize, model.ProductName, orderItem.AttributeSelection, catalogSettings); } return(model); }
public override async Task MapAsync(IEnumerable <OrganizedShoppingCartItem> from, MiniShoppingCartModel to, dynamic parameters = null) { Guard.NotNull(from, nameof(from)); Guard.NotNull(to, nameof(to)); var customer = _services.WorkContext.CurrentCustomer; to.ShowProductImages = _shoppingCartSettings.ShowProductImagesInMiniShoppingCart; to.ThumbSize = _mediaSettings.MiniCartThumbPictureSize; to.CurrentCustomerIsGuest = customer.IsGuest(); to.AnonymousCheckoutAllowed = _orderSettings.AnonymousCheckoutAllowed; to.DisplayMoveToWishlistButton = await _services.Permissions.AuthorizeAsync(Permissions.Cart.AccessWishlist); to.ShowBasePrice = _shoppingCartSettings.ShowBasePrice; to.TotalProducts = from.GetTotalQuantity(); if (!from.Any()) { return; } var taxFormat = _currencyService.GetTaxFormat(); var batchContext = _productService.CreateProductBatchContext(from.Select(x => x.Item.Product).ToArray(), null, customer, false); var subtotal = await _orderCalculationService.GetShoppingCartSubtotalAsync(from.ToList(), null, batchContext); var lineItems = subtotal.LineItems.ToDictionarySafe(x => x.Item.Item.Id); var currency = _services.WorkContext.WorkingCurrency; var subtotalWithoutDiscount = _currencyService.ConvertFromPrimaryCurrency(subtotal.SubtotalWithoutDiscount.Amount, currency); to.SubTotal = subtotalWithoutDiscount.WithPostFormat(taxFormat); // A customer should visit the shopping cart page before going to checkout if: //1. There is at least one checkout attribute that is reqired //2. Min order sub total is OK var checkoutAttributes = await _checkoutAttributeMaterializer.GetValidCheckoutAttributesAsync(from); to.DisplayCheckoutButton = !checkoutAttributes.Any(x => x.IsRequired); // Products sort descending (recently added products) foreach (var cartItem in from) { var item = cartItem.Item; var product = cartItem.Item.Product; var productSeName = await product.GetActiveSlugAsync(); var cartItemModel = new MiniShoppingCartModel.ShoppingCartItemModel { Id = item.Id, ProductId = product.Id, ProductName = product.GetLocalized(x => x.Name), ShortDesc = product.GetLocalized(x => x.ShortDescription), ProductSeName = productSeName, EnteredQuantity = item.Quantity, MaxOrderAmount = product.OrderMaximumQuantity, MinOrderAmount = product.OrderMinimumQuantity, QuantityStep = product.QuantityStep > 0 ? product.QuantityStep : 1, CreatedOnUtc = item.UpdatedOnUtc, ProductUrl = await _productUrlHelper.GetProductUrlAsync(productSeName, cartItem), QuantityUnitName = null, AttributeInfo = await _productAttributeFormatter.FormatAttributesAsync( item.AttributeSelection, product, null, ", ", includePrices : false, includeGiftCardAttributes : false, includeHyperlinks : false, batchContext : batchContext) }; if (cartItem.ChildItems != null && _shoppingCartSettings.ShowProductBundleImagesOnShoppingCart) { var bundleItems = cartItem.ChildItems.Where(x => x.Item.Id != item.Id && x.Item.BundleItem != null && !x.Item.BundleItem.HideThumbnail); foreach (var bundleItem in bundleItems) { var bundleItemModel = new MiniShoppingCartModel.ShoppingCartItemBundleItem { ProductName = bundleItem.Item.Product.GetLocalized(x => x.Name), ProductSeName = await bundleItem.Item.Product.GetActiveSlugAsync(), }; bundleItemModel.ProductUrl = await _productUrlHelper.GetProductUrlAsync( bundleItem.Item.ProductId, bundleItemModel.ProductSeName, bundleItem.Item.AttributeSelection); var file = await _db.ProductMediaFiles .AsNoTracking() .Include(x => x.MediaFile) .ApplyProductFilter(bundleItem.Item.ProductId) .FirstOrDefaultAsync(); if (file?.MediaFile != null) { var fileInfo = await _mediaService.GetFileByIdAsync(file.MediaFileId, MediaLoadFlags.AsNoTracking); bundleItemModel.ImageModel = new ImageModel { File = fileInfo, ThumbSize = MediaSettings.ThumbnailSizeXxs, Title = file.MediaFile.GetLocalized(x => x.Title)?.Value.NullEmpty() ?? T("Media.Manufacturer.ImageLinkTitleFormat", bundleItemModel.ProductName), Alt = file.MediaFile.GetLocalized(x => x.Alt)?.Value.NullEmpty() ?? T("Media.Manufacturer.ImageAlternateTextFormat", bundleItemModel.ProductName), NoFallback = _catalogSettings.HideProductDefaultPictures, }; } cartItemModel.BundleItems.Add(bundleItemModel); } } // Unit prices. if (product.CallForPrice) { cartItemModel.UnitPrice = T("Products.CallForPrice"); } else if (lineItems.TryGetValue(item.Id, out var lineItem)) { var unitPrice = _currencyService.ConvertFromPrimaryCurrency(lineItem.UnitPrice.FinalPrice.Amount, currency); cartItemModel.UnitPrice = unitPrice.WithPostFormat(taxFormat).ToString(true); if (unitPrice != 0 && to.ShowBasePrice) { cartItemModel.BasePriceInfo = _priceCalculationService.GetBasePriceInfo(item.Product, unitPrice, currency); } } // Image. if (_shoppingCartSettings.ShowProductImagesInMiniShoppingCart) { await cartItem.MapAsync(cartItemModel.Image, _mediaSettings.MiniCartThumbPictureSize, cartItemModel.ProductName); } to.Items.Add(cartItemModel); } }
public override async Task MapAsync(OrganizedShoppingCartItem from, TModel to, dynamic parameters = null) { Guard.NotNull(from, nameof(from)); // TODO: (ms) (core) Be certain that product is null if it is removed from cart. if (from.Item.Product == null) { return; } var item = from.Item; var product = from.Item.Product; var customer = item.Customer; var currency = _services.WorkContext.WorkingCurrency; var shoppingCartType = item.ShoppingCartType; await _productAttributeMaterializer.MergeWithCombinationAsync(product, item.AttributeSelection); var productSeName = await product.GetActiveSlugAsync(); // General model data to.Id = item.Id; to.Sku = product.Sku; to.ProductId = product.Id; to.ProductName = product.GetLocalized(x => x.Name); to.ProductSeName = productSeName; to.ProductUrl = await _productUrlHelper.GetProductUrlAsync(productSeName, from); to.EnteredQuantity = item.Quantity; to.MinOrderAmount = product.OrderMinimumQuantity; to.MaxOrderAmount = product.OrderMaximumQuantity; to.QuantityStep = product.QuantityStep > 0 ? product.QuantityStep : 1; to.ShortDesc = product.GetLocalized(x => x.ShortDescription); to.ProductType = product.ProductType; to.VisibleIndividually = product.Visibility != ProductVisibility.Hidden; to.CreatedOnUtc = item.UpdatedOnUtc; if (item.BundleItem != null) { to.BundleItem.Id = item.BundleItem.Id; to.BundleItem.DisplayOrder = item.BundleItem.DisplayOrder; to.BundleItem.HideThumbnail = item.BundleItem.HideThumbnail; to.BundlePerItemPricing = item.BundleItem.BundleProduct.BundlePerItemPricing; to.BundlePerItemShoppingCart = item.BundleItem.BundleProduct.BundlePerItemShoppingCart; to.AttributeInfo = await _productAttributeFormatter.FormatAttributesAsync( item.AttributeSelection, product, customer, includePrices : false, includeGiftCardAttributes : true, includeHyperlinks : true); var bundleItemName = item.BundleItem.GetLocalized(x => x.Name); if (bundleItemName.Value.HasValue()) { to.ProductName = bundleItemName; } var bundleItemShortDescription = item.BundleItem.GetLocalized(x => x.ShortDescription); if (bundleItemShortDescription.Value.HasValue()) { to.ShortDesc = bundleItemShortDescription; } if (to.BundlePerItemPricing && to.BundlePerItemShoppingCart) { var bundleItemSubTotalWithDiscountBase = await _taxService.GetProductPriceAsync(product, await _priceCalculationService.GetSubTotalAsync(from, true)); var bundleItemSubTotalWithDiscount = _currencyService.ConvertFromPrimaryCurrency(bundleItemSubTotalWithDiscountBase.Price.Amount, currency); to.BundleItem.PriceWithDiscount = bundleItemSubTotalWithDiscount.ToString(); } } else { to.AttributeInfo = await _productAttributeFormatter.FormatAttributesAsync(item.AttributeSelection, product, customer); } var allowedQuantities = product.ParseAllowedQuantities(); foreach (var quantity in allowedQuantities) { to.AllowedQuantities.Add(new SelectListItem { Text = quantity.ToString(), Value = quantity.ToString(), Selected = item.Quantity == quantity }); } var quantityUnit = await _db.QuantityUnits.GetQuantityUnitByIdAsync(product.QuantityUnitId ?? 0, _catalogSettings.ShowDefaultQuantityUnit); if (quantityUnit != null) { to.QuantityUnitName = quantityUnit.GetLocalized(x => x.Name); } if (product.IsRecurring) { to.RecurringInfo = T("ShoppingCart.RecurringPeriod", product.RecurringCycleLength, product.RecurringCyclePeriod.GetLocalizedEnum()); } if (product.CallForPrice) { to.UnitPrice = T("Products.CallForPrice"); } else { var unitPriceWithDiscountBase = await _taxService.GetProductPriceAsync(product, await _priceCalculationService.GetUnitPriceAsync(from, true)); var unitPriceWithDiscount = _currencyService.ConvertFromPrimaryCurrency(unitPriceWithDiscountBase.Price.Amount, currency); to.UnitPrice = unitPriceWithDiscount.ToString(); } // Subtotal and discount. if (product.CallForPrice) { to.SubTotal = T("Products.CallForPrice"); } else { var cartItemSubTotalWithDiscount = await _priceCalculationService.GetSubTotalAsync(from, true); var cartItemSubTotalWithDiscountBase = await _taxService.GetProductPriceAsync(product, cartItemSubTotalWithDiscount); cartItemSubTotalWithDiscount = _currencyService.ConvertFromPrimaryCurrency(cartItemSubTotalWithDiscountBase.Price.Amount, currency); to.SubTotal = cartItemSubTotalWithDiscount.ToString(); // Display an applied discount amount. var cartItemSubTotalWithoutDiscount = await _priceCalculationService.GetSubTotalAsync(from, false); var cartItemSubTotalWithoutDiscountBase = await _taxService.GetProductPriceAsync(product, cartItemSubTotalWithoutDiscount); var cartItemSubTotalDiscountBase = cartItemSubTotalWithoutDiscountBase.Price - cartItemSubTotalWithDiscountBase.Price; if (cartItemSubTotalDiscountBase > decimal.Zero) { var itemDiscount = _currencyService.ConvertFromPrimaryCurrency(cartItemSubTotalDiscountBase.Amount, currency); to.Discount = itemDiscount.ToString(); } } if (item.BundleItem != null) { if (_shoppingCartSettings.ShowProductBundleImagesOnShoppingCart) { await from.MapAsync(to.Image, _mediaSettings.CartThumbBundleItemPictureSize, to.ProductName); } } else { if (_shoppingCartSettings.ShowProductImagesOnShoppingCart) { await from.MapAsync(to.Image, _mediaSettings.CartThumbPictureSize, to.ProductName); } } var itemWarnings = new List <string>(); var isItemValid = await _shoppingCartValidator.ValidateCartItemsAsync(new[] { from }, itemWarnings); if (!isItemValid) { itemWarnings.Each(x => to.Warnings.Add(x)); } }
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 taxFormat = _currencyService.GetTaxFormat(null, null, PricingTarget.Product); 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 _taxCalculator.CalculateCheckoutAttributeTaxAsync(caValue); var priceAdjustment = _currencyService.ConvertFromPrimaryCurrency(priceAdjustmentBase.Price, currency); if (priceAdjustment > 0) { pvaValueModel.PriceAdjustment = "+" + priceAdjustment.WithPostFormat(taxFormat).ToString(); } else if (priceAdjustment < 0) { pvaValueModel.PriceAdjustment = "-" + (priceAdjustment * -1).WithPostFormat(taxFormat).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.GetAttributeValues(attribute.Id)? .Select(x => x.ToString()) .FirstOrDefault(); 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) .Select(x => x.Value.ToString()) .FirstOrDefault(); 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 var allProducts = from .Select(x => x.Item.Product) .Union(from.Select(x => x.ChildItems).SelectMany(child => child.Select(x => x.Item.Product))) .ToArray(); var batchContext = _productService.CreateProductBatchContext(allProducts, null, customer, false); var subtotal = await _orderCalculationService.GetShoppingCartSubtotalAsync(from.ToList(), null, batchContext); dynamic itemParameters = new ExpandoObject(); itemParameters.TaxFormat = _currencyService.GetTaxFormat(); itemParameters.BatchContext = batchContext; itemParameters.CartSubtotal = subtotal; foreach (var cartItem in from) { var model = new ShoppingCartModel.ShoppingCartItemModel(); await cartItem.MapAsync(model, (object)itemParameters); 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; }