/// <summary> /// Gets an additional handling fee of a payment method /// </summary> /// <param name="cart">Shopping cart</param> /// <param name="paymentMethodSystemName">Payment method system name</param> /// <returns> /// A task that represents the asynchronous operation /// The task result contains the additional handling fee /// </returns> public virtual async Task <decimal> GetAdditionalHandlingFeeAsync(IList <ShoppingCartItem> cart, string paymentMethodSystemName) { if (string.IsNullOrEmpty(paymentMethodSystemName)) { return(decimal.Zero); } var customer = await _customerService.GetCustomerByIdAsync(cart.FirstOrDefault()?.CustomerId ?? 0); var paymentMethod = await _paymentPluginManager.LoadPluginBySystemNameAsync(paymentMethodSystemName, customer, cart.FirstOrDefault()?.StoreId ?? 0); if (paymentMethod == null) { return(decimal.Zero); } var result = await paymentMethod.GetAdditionalHandlingFeeAsync(cart); if (result < decimal.Zero) { result = decimal.Zero; } if (!_shoppingCartSettings.RoundPricesDuringCalculation) { return(result); } result = await _priceCalculationService.RoundPriceAsync(result); return(result); }
/// <summary> /// Formats the price /// </summary> /// <param name="price">Price</param> /// <param name="showCurrency">A value indicating whether to show a currency</param> /// <param name="targetCurrency">Target currency</param> /// <param name="languageId">Language</param> /// <param name="priceIncludesTax">A value indicating whether price includes tax</param> /// <param name="showTax">A value indicating whether to show tax suffix</param> /// <returns> /// A task that represents the asynchronous operation /// The task result contains the price /// </returns> public virtual async Task <string> FormatPriceAsync(decimal price, bool showCurrency, Currency targetCurrency, int languageId, bool priceIncludesTax, bool showTax) { //we should round it no matter of "ShoppingCartSettings.RoundPricesDuringCalculation" setting price = await _priceCalculationService.RoundPriceAsync(price, targetCurrency); var currencyString = GetCurrencyString(price, showCurrency, targetCurrency); if (!showTax) { return(currencyString); } //show tax suffix string formatStr; if (priceIncludesTax) { formatStr = await _localizationService.GetResourceAsync("Products.InclTaxSuffix", languageId, false); if (string.IsNullOrEmpty(formatStr)) { formatStr = "{0} incl tax"; } } else { formatStr = await _localizationService.GetResourceAsync("Products.ExclTaxSuffix", languageId, false); if (string.IsNullOrEmpty(formatStr)) { formatStr = "{0} excl tax"; } } return(string.Format(formatStr, currencyString)); }
/// <summary> /// Gets available shipping options /// </summary> /// <param name="cart">Shopping cart</param> /// <param name="shippingAddress">Shipping address</param> /// <param name="customer">Load records allowed only to a specified customer; pass null to ignore ACL permissions</param> /// <param name="allowedShippingRateComputationMethodSystemName">Filter by shipping rate computation method identifier; null to load shipping options of all shipping rate computation methods</param> /// <param name="storeId">Load records allowed only in a specified store; pass 0 to load all records</param> /// <returns>Shipping options</returns> public virtual async Task <GetShippingOptionResponse> GetShippingOptionsAsync(IList <ShoppingCartItem> cart, Address shippingAddress, Customer customer = null, string allowedShippingRateComputationMethodSystemName = "", int storeId = 0) { if (cart == null) { throw new ArgumentNullException(nameof(cart)); } var result = new GetShippingOptionResponse(); //create a package var(shippingOptionRequests, shippingFromMultipleLocations) = await CreateShippingOptionRequestsAsync(cart, shippingAddress, storeId); result.ShippingFromMultipleLocations = shippingFromMultipleLocations; var shippingRateComputationMethods = await _shippingPluginManager .LoadActivePluginsAsync(customer, storeId, allowedShippingRateComputationMethodSystemName); if (!shippingRateComputationMethods.Any()) { return(result); } //request shipping options from each shipping rate computation methods foreach (var srcm in shippingRateComputationMethods) { //request shipping options (separately for each package-request) IList <ShippingOption> srcmShippingOptions = null; foreach (var shippingOptionRequest in shippingOptionRequests) { var getShippingOptionResponse = await srcm.GetShippingOptionsAsync(shippingOptionRequest); if (getShippingOptionResponse.Success) { //success if (srcmShippingOptions == null) { //first shipping option request srcmShippingOptions = getShippingOptionResponse.ShippingOptions; } else { //get shipping options which already exist for prior requested packages for this scrm (i.e. common options) srcmShippingOptions = srcmShippingOptions .Where(existingso => getShippingOptionResponse.ShippingOptions.Any(newso => newso.Name == existingso.Name)) .ToList(); //and sum the rates foreach (var existingso in srcmShippingOptions) { existingso.Rate += getShippingOptionResponse .ShippingOptions .First(newso => newso.Name == existingso.Name) .Rate; } } } else { //errors foreach (var error in getShippingOptionResponse.Errors) { result.AddError(error); await _logger.WarningAsync($"Shipping ({srcm.PluginDescriptor.FriendlyName}). {error}"); } //clear the shipping options in this case srcmShippingOptions = new List <ShippingOption>(); break; } } //add this scrm's options to the result if (srcmShippingOptions == null) { continue; } foreach (var so in srcmShippingOptions) { //set system name if not set yet if (string.IsNullOrEmpty(so.ShippingRateComputationMethodSystemName)) { so.ShippingRateComputationMethodSystemName = srcm.PluginDescriptor.SystemName; } if (_shoppingCartSettings.RoundPricesDuringCalculation) { so.Rate = await _priceCalculationService.RoundPriceAsync(so.Rate); } result.ShippingOptions.Add(so); } } if (_shippingSettings.ReturnValidOptionsIfThereAreAny) { //return valid options if there are any (no matter of the errors returned by other shipping rate computation methods). if (result.ShippingOptions.Any() && result.Errors.Any()) { result.Errors.Clear(); } } //no shipping options loaded if (!result.ShippingOptions.Any() && !result.Errors.Any()) { result.Errors.Add(await _localizationService.GetResourceAsync("Checkout.ShippingOptionCouldNotBeLoaded")); } return(result); }
public async override Task <(decimal unitPrice, decimal discountAmount, List <Discount> appliedDiscounts)> GetUnitPriceAsync( Product product, Customer customer, ShoppingCartType shoppingCartType, int quantity, string attributesXml, decimal customerEnteredPrice, DateTime?rentalStartDate, DateTime?rentalEndDate, bool includeDiscounts) { if (product == null) { throw new ArgumentNullException(nameof(product)); } if (customer == null) { throw new ArgumentNullException(nameof(customer)); } var discountAmount = decimal.Zero; var appliedDiscounts = new List <Discount>(); decimal finalPrice; var combination = await _productAttributeParser.FindProductAttributeCombinationAsync(product, attributesXml); if (combination?.OverriddenPrice.HasValue ?? false) { (_, finalPrice, discountAmount, appliedDiscounts) = await _priceCalculationService.GetFinalPriceAsync(product, customer, combination.OverriddenPrice.Value, decimal.Zero, includeDiscounts, quantity, product.IsRental?rentalStartDate : null, product.IsRental?rentalEndDate : null); } else { // custom // summarize price of all attributes except warranties (warranties processed separately) decimal warrantyPrice = decimal.Zero; ProductAttributeMapping warrantyPam = await _attributeUtilities.GetWarrantyAttributeMappingAsync(attributesXml); //summarize price of all attributes var attributesTotalPrice = decimal.Zero; var attributeValues = await _productAttributeParser.ParseProductAttributeValuesAsync(attributesXml); // custom - considers warranties if (attributeValues != null) { foreach (var attributeValue in attributeValues) { if (warrantyPam != null && attributeValue.ProductAttributeMappingId == warrantyPam.Id) { warrantyPrice = await _priceCalculationService.GetProductAttributeValuePriceAdjustmentAsync( product, attributeValue, customer, product.CustomerEntersPrice?(decimal?)customerEnteredPrice : null ); } else { attributesTotalPrice += await _priceCalculationService.GetProductAttributeValuePriceAdjustmentAsync( product, attributeValue, customer, product.CustomerEntersPrice?(decimal?)customerEnteredPrice : null ); } } } //get price of a product (with previously calculated price of all attributes) if (product.CustomerEntersPrice) { finalPrice = customerEnteredPrice; } else { int qty; if (_shoppingCartSettings.GroupTierPricesForDistinctShoppingCartItems) { //the same products with distinct product attributes could be stored as distinct "ShoppingCartItem" records //so let's find how many of the current products are in the cart qty = (await GetShoppingCartAsync(customer, shoppingCartType: shoppingCartType, productId: product.Id)) .Sum(x => x.Quantity); if (qty == 0) { qty = quantity; } } else { qty = quantity; } (_, finalPrice, discountAmount, appliedDiscounts) = await _priceCalculationService.GetFinalPriceAsync(product, customer, attributesTotalPrice, includeDiscounts, qty, product.IsRental?rentalStartDate : null, product.IsRental?rentalEndDate : null); finalPrice += warrantyPrice; } } //rounding if (_shoppingCartSettings.RoundPricesDuringCalculation) { finalPrice = await _priceCalculationService.RoundPriceAsync(finalPrice); } return(finalPrice, discountAmount, appliedDiscounts); }
public async Task <IList <YahooHeaderRow> > GetYahooHeaderRowsAsync(Order order) { var result = new List <YahooHeaderRow>(); var orderItems = await _customOrderService.GetOrderItemsAsync(order.Id); if (!orderItems.Any()) { return(result); } var splitItems = orderItems.SplitByPickupAndShipping(); var address = await _addressService.GetAddressByIdAsync(order.BillingAddressId); var stateAbbv = (await _stateProvinceService.GetStateProvinceByAddressAsync(address)).Abbreviation; var country = (await _countryService.GetCountryByAddressAsync(address)).Name; var cardName = _encryptionService.DecryptText(order.CardName, _securitySettings.EncryptionKey); var cardNumber = _encryptionService.DecryptText(order.CardNumber, _securitySettings.EncryptionKey); var cardMonth = _encryptionService.DecryptText(order.CardExpirationMonth, _securitySettings.EncryptionKey); var cardYear = _encryptionService.DecryptText(order.CardExpirationYear, _securitySettings.EncryptionKey); var cardCvv2 = _encryptionService.DecryptText(order.CardCvv2, _securitySettings.EncryptionKey); var customValues = _paymentService.DeserializeCustomValues(order); var ccRefNo = customValues != null? customValues.Where(cv => cv.Key == "CC_REFNO") .Select(cv => cv.Value?.ToString()) .FirstOrDefault() : null; var pickupItems = splitItems.pickupItems; if (pickupItems.Any()) { decimal backendOrderTax, backendOrderTotal; CalculateValues(pickupItems, out backendOrderTax, out backendOrderTotal); var giftCard = await CalculateGiftCardAsync(order, backendOrderTotal); result.Add(new YahooHeaderRow( _settings.OrderIdPrefix, order, address, stateAbbv, country, cardName, cardNumber, cardMonth, cardYear, cardCvv2, await _priceCalculationService.RoundPriceAsync(backendOrderTax), await _priceCalculationService.RoundPriceAsync(backendOrderTotal), giftCard.GiftCardCode, giftCard.GiftCardUsage, ccRefNo )); } var shippingItems = splitItems.shippingItems; if (shippingItems.Any()) { decimal homeDeliveryCost = 0; decimal shippingCost = 0; homeDeliveryCost = await _homeDeliveryCostService.GetHomeDeliveryCostAsync(shippingItems); shippingCost = order.OrderShippingExclTax - homeDeliveryCost; decimal backendOrderTax, backendOrderTotal; CalculateValues(shippingItems, out backendOrderTax, out backendOrderTotal); decimal shippingTax = order.OrderShippingInclTax - order.OrderShippingExclTax; backendOrderTax += shippingTax; backendOrderTotal += order.OrderShippingInclTax; var giftCard = await CalculateGiftCardAsync(order, backendOrderTotal); result.Add(new YahooHeaderRowShipping( _settings.OrderIdPrefix, order, address, stateAbbv, country, cardName, cardNumber, cardMonth, cardYear, cardCvv2, await _priceCalculationService.RoundPriceAsync(backendOrderTax), await _priceCalculationService.RoundPriceAsync(shippingCost), await _priceCalculationService.RoundPriceAsync(homeDeliveryCost), await _priceCalculationService.RoundPriceAsync(backendOrderTotal), giftCard.GiftCardCode, giftCard.GiftCardUsage, ccRefNo )); } return(result); }
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); }