public virtual async Task <string> GetBasePriceInfoAsync(Product product, Customer customer = null, Currency currency = null, Money?priceAdjustment = null) { Guard.NotNull(product, nameof(product)); customer ??= _workContext.CurrentCustomer; currency ??= _workContext.WorkingCurrency; if (product.BasePriceHasValue && product.BasePriceAmount != decimal.Zero) { var currentPrice = await GetFinalPriceAsync(product, null, customer, includeDiscounts : true); if (priceAdjustment.HasValue) { currentPrice += priceAdjustment.Value; } var(price, _) = await _taxService.GetProductPriceAsync(product, currentPrice, customer : customer); var convertedPrice = _currencyService.ConvertFromPrimaryStoreCurrency(price); return(GetBasePriceInfo(product, convertedPrice, currency)); } return(string.Empty); }
private async Task <OrderItem> PrepareDefaultOrderItemFromProductAsync(Order order, Product product) { var customer = await CustomerService.GetCustomerByIdAsync(order.CustomerId); var presetQty = 1; var price = await _priceCalculationService.GetFinalPriceAsync(product, customer, decimal.Zero, true, presetQty); var(priceInclTax, _) = await _taxService.GetProductPriceAsync(product, price.finalPrice, includingTax : true, customer); var(priceExclTax, _) = await _taxService.GetProductPriceAsync(product, price.finalPrice, includingTax : false, customer); var orderItem = new OrderItem { OrderItemGuid = new Guid(), UnitPriceExclTax = priceExclTax, UnitPriceInclTax = priceInclTax, PriceInclTax = priceInclTax, PriceExclTax = priceExclTax, OriginalProductCost = await _priceCalculationService.GetProductCostAsync(product, null), Quantity = presetQty, ProductId = product.Id, OrderId = order.Id }; return(orderItem); }
public virtual async Task <string> GetBasePriceInfoAsync(Product product, Customer customer = null, Currency currency = null, Money?priceAdjustment = null) { Guard.NotNull(product, nameof(product)); // Currency is only used for formatting, not for calculation. currency ??= _workContext.WorkingCurrency; customer ??= _workContext.CurrentCustomer; if (product.BasePriceHasValue && product.BasePriceAmount != decimal.Zero) { var currentPrice = await GetFinalPriceAmountAsync(product, decimal.Zero, customer, includeDiscounts : true); if (priceAdjustment.HasValue) { currentPrice += priceAdjustment.Value.Amount; } var(price, _) = await _taxService.GetProductPriceAsync(product, new(currentPrice, _primaryCurrency), customer : customer); var convertedPrice = _currencyService.ConvertCurrency(price, currency); return(GetBasePriceInfo(product, convertedPrice, currency)); } return(string.Empty); }
public async Task <(decimal taxRate, decimal sciSubTotalInclTax, decimal sciUnitPriceInclTax, decimal warrantyUnitPriceExclTax, decimal warrantyUnitPriceInclTax)> CalculateWarrantyTaxAsync( ShoppingCartItem sci, Customer customer, decimal sciSubTotalExclTax, decimal sciUnitPriceExclTax ) { var product = await _productService.GetProductByIdAsync(sci.ProductId); var sciSubTotalInclTax = await _taxService.GetProductPriceAsync(product, sciSubTotalExclTax, true, customer); var sciUnitPriceInclTax = await _taxService.GetProductPriceAsync(product, sciUnitPriceExclTax, true, customer); var warrantyUnitPriceExclTax = decimal.Zero; (decimal price, decimal taxRate)warrantyUnitPriceInclTax; warrantyUnitPriceInclTax.price = decimal.Zero; warrantyUnitPriceInclTax.taxRate = decimal.Zero; // warranty item handling ProductAttributeMapping warrantyPam = await _attributeUtilities.GetWarrantyAttributeMappingAsync(sci.AttributesXml); if (warrantyPam != null) { warrantyUnitPriceExclTax = (await _productAttributeParser.ParseProductAttributeValuesAsync(sci.AttributesXml)) .Where(pav => pav.ProductAttributeMappingId == warrantyPam.Id) .Select(pav => pav.PriceAdjustment) .FirstOrDefault(); // get warranty "product" - this is so the warranties have a tax category Product warrProduct = _importUtilities.GetExistingProductBySku("WARRPLACE_SKU"); var isCustomerInTaxableState = await IsCustomerInTaxableStateAsync(customer); if (warrProduct == null) { // taxed warranty price warrantyUnitPriceInclTax = await _taxService.GetProductPriceAsync(product, warrantyUnitPriceExclTax, false, customer); } else { warrantyUnitPriceInclTax = await _taxService.GetProductPriceAsync(warrProduct, warrantyUnitPriceExclTax, isCustomerInTaxableState, customer); } var productUnitPriceInclTax = await _taxService.GetProductPriceAsync(product, sciUnitPriceExclTax - warrantyUnitPriceExclTax, true, customer); sciUnitPriceInclTax.price = productUnitPriceInclTax.price + warrantyUnitPriceInclTax.price; sciSubTotalInclTax.price = sciUnitPriceInclTax.price * sci.Quantity; } return(warrantyUnitPriceInclTax.taxRate, sciSubTotalInclTax.price, sciUnitPriceInclTax.price, warrantyUnitPriceExclTax, warrantyUnitPriceInclTax.price); }
/// <summary> /// Prepare paged shopping cart item list model /// </summary> /// <param name="searchModel">Shopping cart item search model</param> /// <param name="customer">Customer</param> /// <returns> /// A task that represents the asynchronous operation /// The task result contains the shopping cart item list model /// </returns> public virtual async Task <ShoppingCartItemListModel> PrepareShoppingCartItemListModelAsync(ShoppingCartItemSearchModel searchModel, Customer customer) { if (searchModel == null) { throw new ArgumentNullException(nameof(searchModel)); } if (customer == null) { throw new ArgumentNullException(nameof(customer)); } //get shopping cart items var items = (await _shoppingCartService.GetShoppingCartAsync(customer, searchModel.ShoppingCartType, searchModel.StoreId, searchModel.ProductId, searchModel.StartDate, searchModel.EndDate)).ToPagedList(searchModel); var isSearchProduct = searchModel.ProductId > 0; Product product = null; if (isSearchProduct) { product = await _productService.GetProductByIdAsync(searchModel.ProductId) ?? throw new Exception("Product is not found"); } //prepare list model var model = await new ShoppingCartItemListModel().PrepareToGridAsync(searchModel, items, () => { return(items .OrderByDescending(item => item.CreatedOnUtc) .SelectAwait(async item => { //fill in model values from the entity var itemModel = item.ToModel <ShoppingCartItemModel>(); if (!isSearchProduct) { product = await _productService.GetProductByIdAsync(item.ProductId); } //convert dates to the user time itemModel.UpdatedOn = await _dateTimeHelper.ConvertToUserTimeAsync(item.UpdatedOnUtc, DateTimeKind.Utc); //fill in additional values (not existing in the entity) itemModel.Store = (await _storeService.GetStoreByIdAsync(item.StoreId))?.Name ?? "Deleted"; itemModel.AttributeInfo = await _productAttributeFormatter.FormatAttributesAsync(product, item.AttributesXml, customer); var(unitPrice, _, _) = await _shoppingCartService.GetUnitPriceAsync(item, true); itemModel.UnitPrice = await _priceFormatter.FormatPriceAsync((await _taxService.GetProductPriceAsync(product, unitPrice)).price); var(subTotal, _, _, _) = await _shoppingCartService.GetSubTotalAsync(item, true); itemModel.Total = await _priceFormatter.FormatPriceAsync((await _taxService.GetProductPriceAsync(product, subTotal)).price); //set product name since it does not survive mapping itemModel.ProductName = product.Name; return itemModel; })); }); return(model); }
public async Task CanGetProductPricePriceIncludesTaxIncludingTaxTaxable() { var customer = new Customer(); var product = new Product(); var(price, _) = await _taxService.GetProductPriceAsync(product, 0, 1000M, true, customer, true); price.Should().Be(1000); (price, _) = await _taxService.GetProductPriceAsync(product, 0, 1000M, true, customer, false); price.Should().Be(1100); (price, _) = await _taxService.GetProductPriceAsync(product, 0, 1000M, false, customer, true); price.Should().Be(909.0909090909090909090909091M); (price, _) = await _taxService.GetProductPriceAsync(product, 0, 1000M, false, customer, false); price.Should().Be(1000); }
public virtual async Task <string> GetBasePriceInfoAsync(Product product, Customer customer = null, Currency currency = null, decimal priceAdjustment = decimal.Zero) { Guard.NotNull(product, nameof(product)); customer ??= _workContext.CurrentCustomer; currency ??= _workContext.WorkingCurrency; if (product.BasePriceHasValue && product.BasePriceAmount != decimal.Zero) { var currentPrice = await GetFinalPriceAsync(product, customer, includeDiscounts : true); var price = await _taxService.GetProductPriceAsync(product, decimal.Add(currentPrice, priceAdjustment), currency : currency, customer : customer); price = _currencyService.ConvertFromPrimaryStoreCurrency(price, currency); return(_priceFormatter.GetBasePriceInfo(product, price, currency)); } return(string.Empty); }
public async Task <List <ProductDetailsModel.TierPriceModel> > CreateTierPriceModelAsync(Product product, decimal adjustment = decimal.Zero) { var model = await product.TierPrices .OrderBy(x => x.Quantity) .FilterByStore(_services.StoreContext.CurrentStore.Id) .FilterForCustomer(_services.WorkContext.CurrentCustomer) .ToList() .RemoveDuplicatedQuantities() .SelectAsync(async(tierPrice) => { var m = new ProductDetailsModel.TierPriceModel { Quantity = tierPrice.Quantity, }; if (adjustment != 0 && tierPrice.CalculationMethod == TierPriceCalculationMethod.Percental && _catalogSettings.ApplyTierPricePercentageToAttributePriceAdjustments) { adjustment -= (adjustment / 100 * tierPrice.Price); } else { adjustment = decimal.Zero; } var adjustmentAmount = adjustment == 0 ? (Money?)null : new Money(adjustment, _currencyService.PrimaryCurrency); var priceBase = default(Money); var taxRate = decimal.Zero; var finalPriceBase = await _priceCalculationService.GetFinalPriceAsync(product, adjustmentAmount, _services.WorkContext.CurrentCustomer, _catalogSettings.DisplayTierPricesWithDiscounts, tierPrice.Quantity, null, null, true); (priceBase, taxRate) = await _taxService.GetProductPriceAsync(product, finalPriceBase); m.Price = _currencyService.ConvertToWorkingCurrency(priceBase); return(m); }) .AsyncToList(); return(model); }
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); }
public virtual async Task <string> FormatAttributesAsync( ProductVariantAttributeSelection selection, Product product, Customer customer = null, string separator = "<br />", bool htmlEncode = true, bool includePrices = true, bool includeProductAttributes = true, bool includeGiftCardAttributes = true, bool includeHyperlinks = true) { Guard.NotNull(selection, nameof(selection)); Guard.NotNull(product, nameof(product)); customer ??= _workContext.CurrentCustomer; using var pool = StringBuilderPool.Instance.Get(out var result); if (includeProductAttributes) { var languageId = _workContext.WorkingLanguage.Id; var attributes = await _productAttributeMaterializer.MaterializeProductVariantAttributesAsync(selection); var attributesDic = attributes.ToDictionary(x => x.Id); foreach (var kvp in selection.AttributesMap) { if (!attributesDic.TryGetValue(kvp.Key, out var pva)) { continue; } foreach (var value in kvp.Value) { var valueStr = value.ToString().EmptyNull(); var pvaAttribute = string.Empty; if (pva.IsListTypeAttribute()) { var pvaValue = pva.ProductVariantAttributeValues.FirstOrDefault(x => x.Id == valueStr.ToInt()); if (pvaValue != null) { pvaAttribute = "{0}: {1}".FormatInvariant( pva.ProductAttribute.GetLocalized(x => x.Name, languageId), pvaValue.GetLocalized(x => x.Name, languageId)); if (includePrices) { var attributeValuePriceAdjustment = await _priceCalculationService.GetProductVariantAttributeValuePriceAdjustmentAsync(pvaValue, product, customer, null, 1); var(priceAdjustmentBase, _) = await _taxService.GetProductPriceAsync(product, attributeValuePriceAdjustment, customer : customer); var priceAdjustment = _currencyService.ConvertToWorkingCurrency(priceAdjustmentBase); if (_shoppingCartSettings.ShowLinkedAttributeValueQuantity && pvaValue.ValueType == ProductVariantAttributeValueType.ProductLinkage && pvaValue.Quantity > 1) { pvaAttribute = pvaAttribute + " × " + pvaValue.Quantity; } if (_catalogSettings.ShowVariantCombinationPriceAdjustment) { if (priceAdjustmentBase > decimal.Zero) { pvaAttribute += $" (+{priceAdjustment})"; } else if (priceAdjustmentBase < decimal.Zero) { pvaAttribute += $" (+{priceAdjustment * -1})"; } } } if (htmlEncode) { pvaAttribute = pvaAttribute.HtmlEncode(); } } } else if (pva.AttributeControlType == AttributeControlType.MultilineTextbox) { string attributeName = pva.ProductAttribute.GetLocalized(x => x.Name, languageId); pvaAttribute = "{0}: {1}".FormatInvariant( htmlEncode ? attributeName.HtmlEncode() : attributeName, HtmlUtils.ConvertPlainTextToHtml(valueStr.HtmlEncode())); } else if (pva.AttributeControlType == AttributeControlType.FileUpload) { if (Guid.TryParse(valueStr, out var downloadGuid) && downloadGuid != Guid.Empty) { var download = await _db.Downloads .AsNoTracking() .Include(x => x.MediaFile) .Where(x => x.DownloadGuid == downloadGuid) .FirstOrDefaultAsync(); if (download?.MediaFile != null) { var attributeText = string.Empty; var fileName = htmlEncode ? download.MediaFile.Name.HtmlEncode() : download.MediaFile.Name; if (includeHyperlinks) { // TODO: (core) add a method for getting URL (use routing because it handles all SEO friendly URLs). var downloadLink = _webHelper.GetStoreLocation(false) + "download/getfileupload/?downloadId=" + download.DownloadGuid; attributeText = $"<a href=\"{downloadLink}\" class=\"fileuploadattribute\">{fileName}</a>"; } else { attributeText = fileName; } string attributeName = pva.ProductAttribute.GetLocalized(a => a.Name, languageId); pvaAttribute = "{0}: {1}".FormatInvariant( htmlEncode ? attributeName.HtmlEncode() : attributeName, attributeText); } } } else { // TextBox, Datepicker pvaAttribute = "{0}: {1}".FormatInvariant(pva.ProductAttribute.GetLocalized(x => x.Name, languageId), valueStr); if (htmlEncode) { pvaAttribute = pvaAttribute.HtmlEncode(); } } result.Grow(pvaAttribute, separator); } } } if (includeGiftCardAttributes && product.IsGiftCard) { var gci = selection.GiftCardInfo; if (gci != null) { // Sender. var giftCardFrom = product.GiftCardType == GiftCardType.Virtual ? (await _localizationService.GetResourceAsync("GiftCardAttribute.From.Virtual")).FormatInvariant(gci.SenderName, gci.SenderEmail) : (await _localizationService.GetResourceAsync("GiftCardAttribute.From.Physical")).FormatInvariant(gci.SenderName); // Recipient. var giftCardFor = product.GiftCardType == GiftCardType.Virtual ? (await _localizationService.GetResourceAsync("GiftCardAttribute.For.Virtual")).FormatInvariant(gci.RecipientName, gci.RecipientEmail) : (await _localizationService.GetResourceAsync("GiftCardAttribute.For.Physical")).FormatInvariant(gci.RecipientName); if (htmlEncode) { giftCardFrom = giftCardFrom.HtmlEncode(); giftCardFor = giftCardFor.HtmlEncode(); } result.Grow(giftCardFrom, separator); result.Grow(giftCardFor, separator); } } return(result.ToString()); }
protected async override Task MoveShoppingCartItemsToOrderItemsAsync( PlaceOrderContainer details, Order order ) { foreach (var sc in details.Cart) { var product = await _productService.GetProductByIdAsync(sc.ProductId); //prices var scUnitPrice = (await _shoppingCartService.GetUnitPriceAsync(sc, true)).unitPrice; var(scSubTotal, discountAmount, scDiscounts, _) = await _shoppingCartService.GetSubTotalAsync(sc, true); // var scUnitPriceInclTax = // await _taxService.GetProductPriceAsync(product, scUnitPrice, true, details.Customer); var scUnitPriceExclTax = await _taxService.GetProductPriceAsync(product, scUnitPrice, false, details.Customer); // var scSubTotalInclTax = // await _taxService.GetProductPriceAsync(product, scSubTotal, true, details.Customer); var scSubTotalExclTax = await _taxService.GetProductPriceAsync(product, scSubTotal, false, details.Customer); // custom - getting warranty tax var(_, scSubTotalInclTax, scUnitPriceInclTax, _, _) = await _warrantyTaxService.CalculateWarrantyTaxAsync(sc, details.Customer, scSubTotalExclTax.price, scUnitPriceExclTax.price); var discountAmountInclTax = await _taxService.GetProductPriceAsync(product, discountAmount, true, details.Customer); var discountAmountExclTax = await _taxService.GetProductPriceAsync(product, discountAmount, false, details.Customer); foreach (var disc in scDiscounts) { if (!_discountService.ContainsDiscount(details.AppliedDiscounts, disc)) { details.AppliedDiscounts.Add(disc); } } //attributes var attributeDescription = await _productAttributeFormatter.FormatAttributesAsync(product, sc.AttributesXml, details.Customer); var itemWeight = await _shippingService.GetShoppingCartItemWeightAsync(sc); //save order item var orderItem = new OrderItem { OrderItemGuid = Guid.NewGuid(), OrderId = order.Id, ProductId = product.Id, UnitPriceInclTax = scUnitPriceInclTax, UnitPriceExclTax = scUnitPriceExclTax.price, PriceInclTax = scSubTotalInclTax, PriceExclTax = scSubTotalExclTax.price, OriginalProductCost = await _priceCalculationService.GetProductCostAsync(product, sc.AttributesXml), AttributeDescription = attributeDescription, AttributesXml = sc.AttributesXml, Quantity = sc.Quantity, DiscountAmountInclTax = discountAmountInclTax.price, DiscountAmountExclTax = discountAmountExclTax.price, DownloadCount = 0, IsDownloadActivated = false, LicenseDownloadId = 0, ItemWeight = itemWeight, RentalStartDateUtc = sc.RentalStartDateUtc, RentalEndDateUtc = sc.RentalEndDateUtc }; await _orderService.InsertOrderItemAsync(orderItem); //gift cards await AddGiftCardsAsync(product, sc.AttributesXml, sc.Quantity, orderItem, scUnitPriceExclTax.price); //inventory await _productService.AdjustInventoryAsync(product, -sc.Quantity, sc.AttributesXml, string.Format(await _localizationService.GetResourceAsync("Admin.StockQuantityHistory.Messages.PlaceOrder"), order.Id)); } //clear shopping cart details.Cart.ToList().ForEach(async sci => await _shoppingCartService.DeleteShoppingCartItemAsync(sci, false)); }
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); }
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)); } }
/// <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 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); }