public decimal GetTax(IList <ShoppingCartItem> cart) { //load all shipping rate computation methods var shippingRateComputationMethods = _shippingPluginManager.LoadActivePlugins(_workContext.CurrentCustomer); return(_orderTotalCalculationService.GetTaxTotal(cart, shippingRateComputationMethods)); }
public void Can_load_active_shippingRateComputationMethods() { var srcm = _shippingPluginManager.LoadActivePlugins(_shippingSettings.ActiveShippingRateComputationMethodSystemNames); srcm.Should().NotBeNull(); srcm.Any().Should().BeTrue(); }
public void CanLoadActiveShippingRateComputationMethods() { var shippingRateComputationMethods = _shippingPluginManager.LoadActivePlugins(new List <string> { "FixedRateTestShippingRateComputationMethod" }); shippingRateComputationMethods.Should().NotBeNull(); shippingRateComputationMethods.Any().Should().BeTrue(); }
/// <summary> /// Get transaction amount details /// </summary> /// <param name="paymentRequest">Payment info required for an order processing</param> /// <param name="shoppingCart">Shopping cart</param> /// <param name="items">List of PayPal items</param> /// <returns>Amount details object</returns> protected DetailsAmountInfo GetAmountDetails(IList <ShoppingCartItem> shoppingCart, IList <Item> items, out decimal totalAdjust) { //get total discount amount var orderTotal = _orderTotalCalculationService.GetShoppingCartTotal(shoppingCart, out decimal discountAmount, out List <DiscountForCaching> _, out List <AppliedGiftCard> _, out int _, out decimal _); //get shipping total var shippingRateComputationMethods = _shippingPluginManager .LoadActivePlugins(_workContext.CurrentCustomer, _storeContext.CurrentStore.Id); var shipping = _orderTotalCalculationService.GetShoppingCartShippingTotal(shoppingCart, shippingRateComputationMethods); var shippingTotal = shipping ?? 0; //get tax total var taxTotal = _orderTotalCalculationService.GetTaxTotal(shoppingCart, shippingRateComputationMethods); //get subtotal decimal subTotal; if (items != null && items.Any()) { //items passed to PayPal, so calculate subtotal based on them subTotal = items.Sum(item => !decimal.TryParse(item.price, out decimal tmpPrice) || !int.TryParse(item.quantity, out int tmpQuantity) ? 0 : tmpPrice * tmpQuantity); } else { subTotal = orderTotal.Value - shippingTotal - taxTotal; } //adjust order total to avoid PayPal payment error: "Transaction amount details (subtotal, tax, shipping) must add up to specified amount total" totalAdjust = Math.Round(shippingTotal, 2) + Math.Round(subTotal, 2) + Math.Round(taxTotal, 2); //create amount details return(new DetailsAmountInfo { shipping = shippingTotal.ToString("N", new CultureInfo("en-US")), subtotal = subTotal.ToString("N", new CultureInfo("en-US")), tax = taxTotal.ToString("N", new CultureInfo("en-US")) }); }
/// <summary> /// Get transaction line items /// </summary> /// <param name="customer">Customer</param> /// <param name="storeId">Store identifier</param> /// <returns>List of transaction items</returns> private List <BasketItem> GetItems(Core.Domain.Customers.Customer customer, int storeId) { var items = new List <BasketItem>(); //get current shopping cart var shoppingCart = _shoppingCartService.GetShoppingCart(customer, ShoppingCartType.ShoppingCart, storeId); //define function to create item BasketItem createItem(decimal price, string productId, string productName, string categoryName, BasketItemType itemType = BasketItemType.PHYSICAL) { return(new BasketItem { Id = productId, Name = productName, Category1 = categoryName, ItemType = itemType.ToString(), Price = Convert.ToDecimal(price, CultureInfo.InvariantCulture).ToString("f8", CultureInfo.InvariantCulture), }); } items.AddRange(shoppingCart.Where(shoppingCartItem => shoppingCartItem.ProductId != 0).Select(shoppingCartItem => { var product = _productService.GetProductById(shoppingCartItem.ProductId); var productCategories = _categoryService.GetProductCategoriesByProductId(product.Id).FirstOrDefault(); var category = _categoryService.GetCategoryById(productCategories.CategoryId); return(createItem(product.Price * shoppingCartItem.Quantity, product.Id.ToString(), product.Name, category.Name)); })); //LoadAllShippingRateComputationMethods var shippingRateComputationMethods = _shippingPluginManager.LoadActivePlugins(_workContext.CurrentCustomer, _storeContext.CurrentStore.Id); //shipping without tax decimal taxRate = 0; var shoppingCartShipping = _orderTotalCalculationService.GetShoppingCartShippingTotal(shoppingCart, false, out taxRate /*shippingRateComputationMethods*/); if (shoppingCartShipping.HasValue) { items.Add(createItem(shoppingCartShipping ?? 0, Guid.NewGuid().ToString(), "Shipping", "Shipping", BasketItemType.VIRTUAL)); } return(items); }
/// <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 GetShippingOptionResponse GetShippingOptions(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 = CreateShippingOptionRequests(cart, shippingAddress, storeId, out var shippingFromMultipleLocations); result.ShippingFromMultipleLocations = shippingFromMultipleLocations; var shippingRateComputationMethods = _shippingPluginManager .LoadActivePlugins(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 = srcm.GetShippingOptions(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); _logger.Warning($"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 = _priceCalculationService.RoundPrice(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(_localizationService.GetResource("Checkout.ShippingOptionCouldNotBeLoaded")); } return(result); }
/// <summary> /// Prepare tax details by Avalara tax service /// </summary> /// <param name="cart">Shopping cart</param> private void PrepareTaxDetails(IList <ShoppingCartItem> cart) { //ensure that Avalara tax provider is active var taxProvider = _taxPluginManager .LoadPluginBySystemName(AvalaraTaxDefaults.SystemName, _workContext.CurrentCustomer, _storeContext.CurrentStore.Id) as AvalaraTaxProvider; if (!_taxPluginManager.IsPluginActive(taxProvider)) { return; } //create dummy order for the tax request var order = new Order { Customer = _workContext.CurrentCustomer }; //addresses order.BillingAddress = _workContext.CurrentCustomer.BillingAddress; order.ShippingAddress = _workContext.CurrentCustomer.ShippingAddress; if (_shippingSettings.AllowPickupInStore) { var pickupPoint = _genericAttributeService.GetAttribute <PickupPoint>(_workContext.CurrentCustomer, NopCustomerDefaults.SelectedPickupPointAttribute, _storeContext.CurrentStore.Id); if (pickupPoint != null) { var country = _countryService.GetCountryByTwoLetterIsoCode(pickupPoint.CountryCode); order.PickupAddress = new Address { Address1 = pickupPoint.Address, City = pickupPoint.City, Country = country, StateProvince = _stateProvinceService.GetStateProvinceByAbbreviation(pickupPoint.StateAbbreviation, country?.Id), ZipPostalCode = pickupPoint.ZipPostalCode, CreatedOnUtc = DateTime.UtcNow, }; } } //checkout attributes order.CheckoutAttributesXml = _genericAttributeService.GetAttribute <string>(_workContext.CurrentCustomer, NopCustomerDefaults.CheckoutAttributes, _storeContext.CurrentStore.Id); //shipping method var shippingRateComputationMethods = _shippingPluginManager.LoadActivePlugins(_workContext.CurrentCustomer, _storeContext.CurrentStore.Id); order.OrderShippingExclTax = _orderTotalCalculationService.GetShoppingCartShippingTotal(cart, false, shippingRateComputationMethods) ?? 0; order.ShippingMethod = _genericAttributeService.GetAttribute <ShippingOption>(_workContext.CurrentCustomer, NopCustomerDefaults.SelectedShippingOptionAttribute, _storeContext.CurrentStore.Id)?.Name; //payment method var paymentMethod = _genericAttributeService.GetAttribute <string>(_workContext.CurrentCustomer, NopCustomerDefaults.SelectedPaymentMethodAttribute, _storeContext.CurrentStore.Id); var paymentFee = _paymentService.GetAdditionalHandlingFee(cart, paymentMethod); order.PaymentMethodAdditionalFeeExclTax = _taxService.GetPaymentMethodAdditionalFee(paymentFee, false, _workContext.CurrentCustomer); order.PaymentMethodSystemName = paymentMethod; //add discount amount _orderTotalCalculationService.GetShoppingCartSubTotal(cart, false, out var orderSubTotalDiscountExclTax, out _, out _, out _); order.OrderSubTotalDiscountExclTax = orderSubTotalDiscountExclTax; //create dummy order items foreach (var cartItem in cart) { var orderItem = new OrderItem { AttributesXml = cartItem.AttributesXml, Product = cartItem.Product, Quantity = cartItem.Quantity }; var itemSubtotal = _priceCalculationService.GetSubTotal(cartItem, true, out _, out _, out _); orderItem.PriceExclTax = _taxService.GetProductPrice(cartItem.Product, itemSubtotal, false, _workContext.CurrentCustomer, out _); order.OrderItems.Add(orderItem); } //get tax details var taxTransaction = taxProvider.CreateOrderTaxTransaction(order, false); if (taxTransaction == null) { return; } //and save it for the further usage var taxDetails = new TaxDetails { TaxTotal = taxTransaction.totalTax }; foreach (var item in taxTransaction.summary) { if (!item.rate.HasValue || !item.tax.HasValue) { continue; } var taxRate = item.rate.Value * 100; var taxValue = item.tax.Value; if (!taxDetails.TaxRates.ContainsKey(taxRate)) { taxDetails.TaxRates.Add(taxRate, taxValue); } else { taxDetails.TaxRates[taxRate] = taxDetails.TaxRates[taxRate] + taxValue; } } _httpContextAccessor.HttpContext.Session.Set(AvalaraTaxDefaults.TaxDetailsSessionValue, taxDetails); }
/// <summary> /// Handle shopping cart changed event /// </summary> /// <param name="cartItem">Shopping cart item</param> public void HandleShoppingCartChangedEvent(ShoppingCartItem cartItem) { //whether marketing automation is enabled if (!_sendinBlueSettings.UseMarketingAutomation) { return; } var customer = _customerService.GetCustomerById(cartItem.CustomerId); try { //create API client var client = CreateMarketingAutomationClient(); //first, try to identify current customer client.Identify(new Identify(customer.Email)); //get shopping cart GUID var shoppingCartGuid = _genericAttributeService .GetAttribute <Guid?>(customer, SendinBlueDefaults.ShoppingCartGuidAttribute); //create track event object var trackEvent = new TrackEvent(customer.Email, string.Empty); //get current customer's shopping cart var cart = _shoppingCartService .GetShoppingCart(customer, ShoppingCartType.ShoppingCart, _storeContext.CurrentStore.Id); if (cart.Any()) { //get URL helper var urlHelper = _urlHelperFactory.GetUrlHelper(_actionContextAccessor.ActionContext); //get shopping cart amounts var shippingRateComputationMethods = _shippingPluginManager .LoadActivePlugins(customer, _storeContext.CurrentStore.Id); _orderTotalCalculationService.GetShoppingCartSubTotal(cart, _workContext.TaxDisplayType == TaxDisplayType.IncludingTax, out var cartDiscount, out _, out var cartSubtotal, out _); var cartTax = _orderTotalCalculationService.GetTaxTotal(cart, shippingRateComputationMethods, false); var cartShipping = _orderTotalCalculationService.GetShoppingCartShippingTotal(cart, shippingRateComputationMethods); var cartTotal = _orderTotalCalculationService.GetShoppingCartTotal(cart, false, false); //get products data by shopping cart items var itemsData = cart.Where(item => item.ProductId != 0).Select(item => { var product = _productService.GetProductById(item.ProductId); //try to get product attribute combination var combination = _productAttributeParser.FindProductAttributeCombination(product, item.AttributesXml); //get default product picture var picture = _pictureService.GetProductPicture(product, item.AttributesXml); //get product SEO slug name var seName = _urlRecordService.GetSeName(product); //create product data return(new { id = product.Id, name = product.Name, variant_id = combination?.Id ?? product.Id, variant_name = combination?.Sku ?? product.Name, sku = combination?.Sku ?? product.Sku, category = _categoryService.GetProductCategoriesByProductId(item.ProductId).Aggregate(",", (all, pc) => { var res = _categoryService.GetCategoryById(pc.CategoryId).Name; res = all == "," ? res : all + ", " + res; return res; }), url = urlHelper.RouteUrl("Product", new { SeName = seName }, _webHelper.CurrentRequestProtocol), image = _pictureService.GetPictureUrl(ref picture), quantity = item.Quantity, price = _shoppingCartService.GetSubTotal(item) }); }).ToArray(); //prepare cart data var cartData = new { affiliation = _storeContext.CurrentStore.Name, subtotal = cartSubtotal, shipping = cartShipping ?? decimal.Zero, total_before_tax = cartSubtotal + cartShipping ?? decimal.Zero, tax = cartTax, discount = cartDiscount, revenue = cartTotal ?? decimal.Zero, url = urlHelper.RouteUrl("ShoppingCart", null, _webHelper.CurrentRequestProtocol), currency = _currencyService.GetCurrencyById(_currencySettings.PrimaryStoreCurrencyId)?.CurrencyCode, //gift_wrapping = string.Empty, //currently we can't get this value items = itemsData }; //if there is a single item in the cart, so the cart is just created if (cart.Count == 1) { shoppingCartGuid = Guid.NewGuid(); } else { //otherwise cart is updated shoppingCartGuid ??= Guid.NewGuid(); } trackEvent.EventName = SendinBlueDefaults.CartUpdatedEventName; trackEvent.EventData = new { id = $"cart:{shoppingCartGuid}", data = cartData }; } else { //there are no items in the cart, so the cart is deleted shoppingCartGuid ??= Guid.NewGuid(); trackEvent.EventName = SendinBlueDefaults.CartDeletedEventName; trackEvent.EventData = new { id = $"cart:{shoppingCartGuid}" }; } //track event client.TrackEvent(trackEvent); //update GUID for the current customer's shopping cart _genericAttributeService.SaveAttribute(customer, SendinBlueDefaults.ShoppingCartGuidAttribute, shoppingCartGuid); } catch (Exception exception) { //log full error _logger.Error($"SendinBlue Marketing Automation error: {exception.Message}.", exception, customer); } }
/// <summary> /// Prepare details to place an order. It also sets some properties to "processPaymentRequest" /// </summary> /// <param name="processPaymentRequest">Process payment request</param> /// <returns>Details</returns> protected override PlaceOrderContainer PreparePlaceOrderDetails(ProcessPaymentRequest processPaymentRequest) { var details = new PlaceOrderContainer { //customer Customer = _customerService.GetCustomerById(processPaymentRequest.CustomerId) }; if (details.Customer == null) { throw new ArgumentException("Customer is not set"); } //affiliate var affiliate = _affiliateService.GetAffiliateById(details.Customer.AffiliateId); if (affiliate != null && affiliate.Active && !affiliate.Deleted) { details.AffiliateId = affiliate.Id; } //check whether customer is guest if (details.Customer.IsGuest() && !_orderSettings.AnonymousCheckoutAllowed) { throw new QNetException("Anonymous checkout is not allowed"); } //customer currency var currencyTmp = _currencyService.GetCurrencyById( _genericAttributeService.GetAttribute <int>(details.Customer, QNetCustomerDefaults.CurrencyIdAttribute, processPaymentRequest.StoreId)); var customerCurrency = currencyTmp != null && currencyTmp.Published ? currencyTmp : _workContext.WorkingCurrency; var primaryStoreCurrency = _currencyService.GetCurrencyById(_currencySettings.PrimaryStoreCurrencyId); details.CustomerCurrencyCode = customerCurrency.CurrencyCode; details.CustomerCurrencyRate = customerCurrency.Rate / primaryStoreCurrency.Rate; //customer language details.CustomerLanguage = _languageService.GetLanguageById( _genericAttributeService.GetAttribute <int>(details.Customer, QNetCustomerDefaults.LanguageIdAttribute, processPaymentRequest.StoreId)); if (details.CustomerLanguage == null || !details.CustomerLanguage.Published) { details.CustomerLanguage = _workContext.WorkingLanguage; } //billing address if (details.Customer.BillingAddress == null) { throw new QNetException("Billing address is not provided"); } if (!CommonHelper.IsValidEmail(details.Customer.BillingAddress.Email)) { throw new QNetException("Email is not valid"); } details.BillingAddress = (Address)details.Customer.BillingAddress.Clone(); if (details.BillingAddress.Country != null && !details.BillingAddress.Country.AllowsBilling) { throw new QNetException($"Country '{details.BillingAddress.Country.Name}' is not allowed for billing"); } //checkout attributes details.CheckoutAttributesXml = _genericAttributeService.GetAttribute <string>(details.Customer, QNetCustomerDefaults.CheckoutAttributes, processPaymentRequest.StoreId); details.CheckoutAttributeDescription = _checkoutAttributeFormatter.FormatAttributes(details.CheckoutAttributesXml, details.Customer); //load shopping cart details.Cart = _shoppingCartService.GetShoppingCart(details.Customer, ShoppingCartType.ShoppingCart, processPaymentRequest.StoreId); if (!details.Cart.Any()) { throw new QNetException("Cart is empty"); } //validate the entire shopping cart var warnings = _shoppingCartService.GetShoppingCartWarnings(details.Cart, details.CheckoutAttributesXml, true); if (warnings.Any()) { throw new QNetException(warnings.Aggregate(string.Empty, (current, next) => $"{current}{next};")); } //validate individual cart items foreach (var sci in details.Cart) { var sciWarnings = _shoppingCartService.GetShoppingCartItemWarnings(details.Customer, sci.ShoppingCartType, sci.Product, processPaymentRequest.StoreId, sci.AttributesXml, sci.CustomerEnteredPrice, sci.RentalStartDateUtc, sci.RentalEndDateUtc, sci.Quantity, false, sci.Id); if (sciWarnings.Any()) { throw new QNetException(sciWarnings.Aggregate(string.Empty, (current, next) => $"{current}{next};")); } } //min totals validation if (!ValidateMinOrderSubtotalAmount(details.Cart)) { var minOrderSubtotalAmount = _currencyService.ConvertFromPrimaryStoreCurrency(_orderSettings.MinOrderSubtotalAmount, _workContext.WorkingCurrency); throw new QNetException(string.Format(_localizationService.GetResource("Checkout.MinOrderSubtotalAmount"), _priceFormatter.FormatPrice(minOrderSubtotalAmount, true, false))); } if (!ValidateMinOrderTotalAmount(details.Cart)) { var minOrderTotalAmount = _currencyService.ConvertFromPrimaryStoreCurrency(_orderSettings.MinOrderTotalAmount, _workContext.WorkingCurrency); throw new QNetException(string.Format(_localizationService.GetResource("Checkout.MinOrderTotalAmount"), _priceFormatter.FormatPrice(minOrderTotalAmount, true, false))); } //tax display type if (_taxSettings.AllowCustomersToSelectTaxDisplayType) { details.CustomerTaxDisplayType = (TaxDisplayType)_genericAttributeService.GetAttribute <int>(details.Customer, QNetCustomerDefaults.TaxDisplayTypeIdAttribute, processPaymentRequest.StoreId); } else { details.CustomerTaxDisplayType = _taxSettings.TaxDisplayType; } //sub total (incl tax) _orderTotalCalculationService.GetShoppingCartSubTotal(details.Cart, true, out var orderSubTotalDiscountAmount, out var orderSubTotalAppliedDiscounts, out var subTotalWithoutDiscountBase, out var _); details.OrderSubTotalInclTax = subTotalWithoutDiscountBase; details.OrderSubTotalDiscountInclTax = orderSubTotalDiscountAmount; //discount history foreach (var disc in orderSubTotalAppliedDiscounts) { if (!_discountService.ContainsDiscount(details.AppliedDiscounts, disc)) { details.AppliedDiscounts.Add(disc); } } //sub total (excl tax) _orderTotalCalculationService.GetShoppingCartSubTotal(details.Cart, false, out orderSubTotalDiscountAmount, out orderSubTotalAppliedDiscounts, out subTotalWithoutDiscountBase, out _); details.OrderSubTotalExclTax = subTotalWithoutDiscountBase; details.OrderSubTotalDiscountExclTax = orderSubTotalDiscountAmount; //shipping info if (_shoppingCartService.ShoppingCartRequiresShipping(details.Cart)) { var pickupPoint = _genericAttributeService.GetAttribute <PickupPoint>(details.Customer, QNetCustomerDefaults.SelectedPickupPointAttribute, processPaymentRequest.StoreId); if (_shippingSettings.AllowPickupInStore && pickupPoint != null) { var country = _countryService.GetCountryByTwoLetterIsoCode(pickupPoint.CountryCode); var state = _stateProvinceService.GetStateProvinceByAbbreviation(pickupPoint.StateAbbreviation, country?.Id); details.PickupInStore = true; details.PickupAddress = new Address { Address1 = pickupPoint.Address, City = pickupPoint.City, County = pickupPoint.County, Country = country, StateProvince = state, ZipPostalCode = pickupPoint.ZipPostalCode, CreatedOnUtc = DateTime.UtcNow }; } else { if (details.Customer.ShippingAddress == null) { throw new QNetException("Shipping address is not provided"); } if (!CommonHelper.IsValidEmail(details.Customer.ShippingAddress.Email)) { throw new QNetException("Email is not valid"); } //clone shipping address details.ShippingAddress = (Address)details.Customer.ShippingAddress.Clone(); if (details.ShippingAddress.Country != null && !details.ShippingAddress.Country.AllowsShipping) { throw new QNetException($"Country '{details.ShippingAddress.Country.Name}' is not allowed for shipping"); } } var shippingOption = _genericAttributeService.GetAttribute <ShippingOption>(details.Customer, QNetCustomerDefaults.SelectedShippingOptionAttribute, processPaymentRequest.StoreId); if (shippingOption != null) { details.ShippingMethodName = shippingOption.Name; details.ShippingRateComputationMethodSystemName = shippingOption.ShippingRateComputationMethodSystemName; } details.ShippingStatus = ShippingStatus.NotYetShipped; } else { details.ShippingStatus = ShippingStatus.ShippingNotRequired; } //LoadAllShippingRateComputationMethods var shippingRateComputationMethods = _shippingPluginManager.LoadActivePlugins(_workContext.CurrentCustomer, _storeContext.CurrentStore.Id); //shipping total var orderShippingTotalInclTax = _orderTotalCalculationService.GetShoppingCartShippingTotal(details.Cart, true, shippingRateComputationMethods, out var _, out var shippingTotalDiscounts); var orderShippingTotalExclTax = _orderTotalCalculationService.GetShoppingCartShippingTotal(details.Cart, false, shippingRateComputationMethods); if (!orderShippingTotalInclTax.HasValue || !orderShippingTotalExclTax.HasValue) { throw new QNetException("Shipping total couldn't be calculated"); } details.OrderShippingTotalInclTax = orderShippingTotalInclTax.Value; details.OrderShippingTotalExclTax = orderShippingTotalExclTax.Value; foreach (var disc in shippingTotalDiscounts) { if (!_discountService.ContainsDiscount(details.AppliedDiscounts, disc)) { details.AppliedDiscounts.Add(disc); } } //payment total var paymentAdditionalFee = _paymentService.GetAdditionalHandlingFee(details.Cart, processPaymentRequest.PaymentMethodSystemName); details.PaymentAdditionalFeeInclTax = _taxService.GetPaymentMethodAdditionalFee(paymentAdditionalFee, true, details.Customer); details.PaymentAdditionalFeeExclTax = _taxService.GetPaymentMethodAdditionalFee(paymentAdditionalFee, false, details.Customer); //tax amount details.OrderTaxTotal = _orderTotalCalculationService.GetTaxTotal(details.Cart, shippingRateComputationMethods, out var taxRatesDictionary); //Avalara plugin changes //get previously saved tax details received from the Avalara tax service var taxDetails = _httpContextAccessor.HttpContext.Session.Get <TaxDetails>(AvalaraTaxDefaults.TaxDetailsSessionValue); if (taxDetails != null) { //adjust tax total according to received value from the Avalara if (taxDetails.TaxTotal.HasValue) { details.OrderTaxTotal = taxDetails.TaxTotal.Value; } if (taxDetails.TaxRates?.Any() ?? false) { taxRatesDictionary = new SortedDictionary <decimal, decimal>(taxDetails.TaxRates); } } //Avalara plugin changes //VAT number var customerVatStatus = (VatNumberStatus)_genericAttributeService.GetAttribute <int>(details.Customer, QNetCustomerDefaults.VatNumberStatusIdAttribute); if (_taxSettings.EuVatEnabled && customerVatStatus == VatNumberStatus.Valid) { details.VatNumber = _genericAttributeService.GetAttribute <string>(details.Customer, QNetCustomerDefaults.VatNumberAttribute); } //tax rates details.TaxRates = taxRatesDictionary.Aggregate(string.Empty, (current, next) => $"{current}{next.Key.ToString(CultureInfo.InvariantCulture)}:{next.Value.ToString(CultureInfo.InvariantCulture)}; "); //order total (and applied discounts, gift cards, reward points) var orderTotal = _orderTotalCalculationService.GetShoppingCartTotal(details.Cart, out var orderDiscountAmount, out var orderAppliedDiscounts, out var appliedGiftCards, out var redeemedRewardPoints, out var redeemedRewardPointsAmount); if (!orderTotal.HasValue) { throw new QNetException("Order total couldn't be calculated"); } details.OrderDiscountAmount = orderDiscountAmount; details.RedeemedRewardPoints = redeemedRewardPoints; details.RedeemedRewardPointsAmount = redeemedRewardPointsAmount; details.AppliedGiftCards = appliedGiftCards; details.OrderTotal = orderTotal.Value; //discount history foreach (var disc in orderAppliedDiscounts) { if (!_discountService.ContainsDiscount(details.AppliedDiscounts, disc)) { details.AppliedDiscounts.Add(disc); } } processPaymentRequest.OrderTotal = details.OrderTotal; //Avalara plugin changes //delete custom value _httpContextAccessor.HttpContext.Session.Set <TaxDetails>(AvalaraTaxDefaults.TaxDetailsSessionValue, null); //Avalara plugin changes //recurring or standard shopping cart? details.IsRecurringShoppingCart = _shoppingCartService.ShoppingCartIsRecurring(details.Cart); if (!details.IsRecurringShoppingCart) { return(details); } var recurringCyclesError = _shoppingCartService.GetRecurringCycleInfo(details.Cart, out var recurringCycleLength, out var recurringCyclePeriod, out var recurringTotalCycles); if (!string.IsNullOrEmpty(recurringCyclesError)) { throw new QNetException(recurringCyclesError); } processPaymentRequest.RecurringCycleLength = recurringCycleLength; processPaymentRequest.RecurringCyclePeriod = recurringCyclePeriod; processPaymentRequest.RecurringTotalCycles = recurringTotalCycles; return(details); }
/// <summary> /// Gets shopping cart total /// </summary> /// <param name="cart">Cart</param> /// <param name="appliedGiftCards">Applied gift cards</param> /// <param name="discountAmount">Applied discount amount</param> /// <param name="appliedDiscounts">Applied discounts</param> /// <param name="redeemedRewardPoints">Reward points to redeem</param> /// <param name="redeemedRewardPointsAmount">Reward points amount in primary store currency to redeem</param> /// <param name="useRewardPoints">A value indicating reward points should be used; null to detect current choice of the customer</param> /// <param name="usePaymentMethodAdditionalFee">A value indicating whether we should use payment method additional fee when calculating order total</param> /// <returns>Shopping cart total;Null if shopping cart total couldn't be calculated now</returns> public override decimal?GetShoppingCartTotal(IList <ShoppingCartItem> cart, out decimal discountAmount, out List <DiscountForCaching> appliedDiscounts, out List <AppliedGiftCard> appliedGiftCards, out int redeemedRewardPoints, out decimal redeemedRewardPointsAmount, bool?useRewardPoints = null, bool usePaymentMethodAdditionalFee = true) { redeemedRewardPoints = 0; redeemedRewardPointsAmount = decimal.Zero; var customer = cart.FirstOrDefault(item => item.Customer != null)?.Customer; var paymentMethodSystemName = string.Empty; if (customer != null) { paymentMethodSystemName = _genericAttributeService.GetAttribute <string>(customer, NopCustomerDefaults.SelectedPaymentMethodAttribute, _storeContext.CurrentStore.Id); } //subtotal without tax GetShoppingCartSubTotal(cart, false, out _, out _, out _, out var subTotalWithDiscountBase); //subtotal with discount var subtotalBase = subTotalWithDiscountBase; //LoadAllShippingRateComputationMethods var shippingRateComputationMethods = _shippingPluginManager.LoadActivePlugins(_workContext.CurrentCustomer, _storeContext.CurrentStore.Id); //shipping without tax var shoppingCartShipping = GetShoppingCartShippingTotal(cart, false, shippingRateComputationMethods); //todo override this method again to include the refundable price //refundable price without tax var refundablePrice = cart.Sum(x => x.Product.RefundablePrice * x.Quantity); //payment method additional fee without tax var paymentMethodAdditionalFeeWithoutTax = decimal.Zero; if (usePaymentMethodAdditionalFee && !string.IsNullOrEmpty(paymentMethodSystemName)) { var paymentMethodAdditionalFee = _paymentService.GetAdditionalHandlingFee(cart, paymentMethodSystemName); paymentMethodAdditionalFeeWithoutTax = _taxService.GetPaymentMethodAdditionalFee(paymentMethodAdditionalFee, false, customer); } //tax var shoppingCartTax = GetTaxTotal(cart, shippingRateComputationMethods, usePaymentMethodAdditionalFee); //Avalara plugin changes //adjust tax total according to received value from the Avalara shoppingCartTax = _httpContextAccessor.HttpContext.Session .Get <TaxDetails>(AvalaraTaxDefaults.TaxDetailsSessionValue)?.TaxTotal ?? shoppingCartTax; //Avalara plugin changes //order total var resultTemp = decimal.Zero; //refundable price resultTemp += refundablePrice; resultTemp += subtotalBase; if (shoppingCartShipping.HasValue) { resultTemp += shoppingCartShipping.Value; } resultTemp += paymentMethodAdditionalFeeWithoutTax; resultTemp += shoppingCartTax; if (_shoppingCartSettings.RoundPricesDuringCalculation) { resultTemp = _priceCalculationService.RoundPrice(resultTemp); } //order total discount discountAmount = GetOrderTotalDiscount(customer, resultTemp, out appliedDiscounts); //sub totals with discount if (resultTemp < discountAmount) { discountAmount = resultTemp; } //reduce subtotal resultTemp -= discountAmount; if (resultTemp < decimal.Zero) { resultTemp = decimal.Zero; } if (_shoppingCartSettings.RoundPricesDuringCalculation) { resultTemp = _priceCalculationService.RoundPrice(resultTemp); } //let's apply gift cards now (gift cards that can be used) appliedGiftCards = new List <AppliedGiftCard>(); AppliedGiftCards(cart, appliedGiftCards, customer, ref resultTemp); if (resultTemp < decimal.Zero) { resultTemp = decimal.Zero; } if (_shoppingCartSettings.RoundPricesDuringCalculation) { resultTemp = _priceCalculationService.RoundPrice(resultTemp); } if (!shoppingCartShipping.HasValue) { //we have errors return(null); } var orderTotal = resultTemp; //reward points SetRewardPoints(ref redeemedRewardPoints, ref redeemedRewardPointsAmount, useRewardPoints, customer, orderTotal); orderTotal = orderTotal - redeemedRewardPointsAmount; if (_shoppingCartSettings.RoundPricesDuringCalculation) { orderTotal = _priceCalculationService.RoundPrice(orderTotal); } //todo append the refundable price return(orderTotal); }
/// <summary> /// Prepare shipping address model /// </summary> /// <param name="selectedCountryId">Selected country identifier</param> /// <param name="prePopulateNewAddressWithCustomerFields">Pre populate new address with customer fields</param> /// <param name="overrideAttributesXml">Override attributes xml</param> /// <returns>Shipping address model</returns> public virtual CheckoutShippingAddressModel PrepareShippingAddressModel(int?selectedCountryId = null, bool prePopulateNewAddressWithCustomerFields = false, string overrideAttributesXml = "") { var model = new CheckoutShippingAddressModel { //allow pickup in store? AllowPickupInStore = _shippingSettings.AllowPickupInStore }; if (model.AllowPickupInStore) { model.DisplayPickupPointsOnMap = _shippingSettings.DisplayPickupPointsOnMap; model.GoogleMapsApiKey = _shippingSettings.GoogleMapsApiKey; var pickupPointProviders = _pickupPluginManager.LoadActivePlugins(_workContext.CurrentCustomer, _storeContext.CurrentStore.Id); if (pickupPointProviders.Any()) { var languageId = _workContext.WorkingLanguage.Id; var pickupPointsResponse = _shippingService.GetPickupPoints(_workContext.CurrentCustomer.BillingAddressId ?? 0, _workContext.CurrentCustomer, storeId: _storeContext.CurrentStore.Id); if (pickupPointsResponse.Success) { model.PickupPoints = pickupPointsResponse.PickupPoints.Select(point => { var country = _countryService.GetCountryByTwoLetterIsoCode(point.CountryCode); var state = _stateProvinceService.GetStateProvinceByAbbreviation(point.StateAbbreviation, country?.Id); var pickupPointModel = new CheckoutPickupPointModel { Id = point.Id, Name = point.Name, Description = point.Description, ProviderSystemName = point.ProviderSystemName, Address = point.Address, City = point.City, County = point.County, StateName = state != null ? _localizationService.GetLocalized(state, x => x.Name, languageId) : string.Empty, CountryName = country != null ? _localizationService.GetLocalized(country, x => x.Name, languageId) : string.Empty, ZipPostalCode = point.ZipPostalCode, Latitude = point.Latitude, Longitude = point.Longitude, OpeningHours = point.OpeningHours }; if (point.PickupFee > 0) { var amount = _taxService.GetShippingPrice(point.PickupFee, _workContext.CurrentCustomer); amount = _currencyService.ConvertFromPrimaryStoreCurrency(amount, _workContext.WorkingCurrency); pickupPointModel.PickupFee = _priceFormatter.FormatShippingPrice(amount, true); } return(pickupPointModel); }).ToList(); } else { foreach (var error in pickupPointsResponse.Errors) { model.Warnings.Add(error); } } } //only available pickup points var shippingProviders = _shippingPluginManager.LoadActivePlugins(_workContext.CurrentCustomer, _storeContext.CurrentStore.Id); if (!shippingProviders.Any()) { if (!pickupPointProviders.Any()) { model.Warnings.Add(_localizationService.GetResource("Checkout.ShippingIsNotAllowed")); model.Warnings.Add(_localizationService.GetResource("Checkout.PickupPoints.NotAvailable")); } model.PickupInStoreOnly = true; model.PickupInStore = true; return(model); } } //existing addresses var addresses = _customerService.GetAddressesByCustomerId(_workContext.CurrentCustomer.Id) .Where(a => _countryService.GetCountryByAddress(a) is Country country && (//published country.Published && //allow shipping country.AllowsShipping && //enabled for the current store _storeMappingService.Authorize(country))) .ToList(); foreach (var address in addresses) { var addressModel = new AddressModel(); _addressModelFactory.PrepareAddressModel(addressModel, address: address, excludeProperties: false, addressSettings: _addressSettings); if (_addressService.IsAddressValid(address)) { model.ExistingAddresses.Add(addressModel); } else { model.InvalidExistingAddresses.Add(addressModel); } } //new address model.ShippingNewAddress.CountryId = selectedCountryId; _addressModelFactory.PrepareAddressModel(model.ShippingNewAddress, address: null, excludeProperties: false, addressSettings: _addressSettings, loadCountries: () => _countryService.GetAllCountriesForShipping(_workContext.WorkingLanguage.Id), prePopulateWithCustomerFields: prePopulateNewAddressWithCustomerFields, customer: _workContext.CurrentCustomer, overrideAttributesXml: overrideAttributesXml); return(model); }
/// <summary> /// Prepares the checkout pickup points model /// </summary> /// <param name="cart">Cart</param> /// <returns>The checkout pickup points model</returns> protected virtual CheckoutPickupPointsModel PrepareCheckoutPickupPointsModel(IList <ShoppingCartItem> cart) { var model = new CheckoutPickupPointsModel() { AllowPickupInStore = _shippingSettings.AllowPickupInStore }; if (model.AllowPickupInStore) { model.DisplayPickupPointsOnMap = _shippingSettings.DisplayPickupPointsOnMap; model.GoogleMapsApiKey = _shippingSettings.GoogleMapsApiKey; var pickupPointProviders = _pickupPluginManager.LoadActivePlugins(_workContext.CurrentCustomer, _storeContext.CurrentStore.Id); if (pickupPointProviders.Any()) { var languageId = _workContext.WorkingLanguage.Id; var pickupPointsResponse = _shippingService.GetPickupPoints(_workContext.CurrentCustomer.BillingAddressId ?? 0, _workContext.CurrentCustomer, storeId: _storeContext.CurrentStore.Id); if (pickupPointsResponse.Success) { model.PickupPoints = pickupPointsResponse.PickupPoints.Select(point => { var country = _countryService.GetCountryByTwoLetterIsoCode(point.CountryCode); var state = _stateProvinceService.GetStateProvinceByAbbreviation(point.StateAbbreviation, country?.Id); var pickupPointModel = new CheckoutPickupPointModel { Id = point.Id, Name = point.Name, Description = point.Description, ProviderSystemName = point.ProviderSystemName, Address = point.Address, City = point.City, County = point.County, StateName = state != null ? _localizationService.GetLocalized(state, x => x.Name, languageId) : string.Empty, CountryName = country != null ? _localizationService.GetLocalized(country, x => x.Name, languageId) : string.Empty, ZipPostalCode = point.ZipPostalCode, Latitude = point.Latitude, Longitude = point.Longitude, OpeningHours = point.OpeningHours }; var cart = _shoppingCartService.GetShoppingCart(_workContext.CurrentCustomer, ShoppingCartType.ShoppingCart, _storeContext.CurrentStore.Id); var amount = _orderTotalCalculationService.IsFreeShipping(cart) ? 0 : point.PickupFee; if (amount > 0) { amount = _taxService.GetShippingPrice(amount, _workContext.CurrentCustomer); amount = _currencyService.ConvertFromPrimaryStoreCurrency(amount, _workContext.WorkingCurrency); pickupPointModel.PickupFee = _priceFormatter.FormatShippingPrice(amount, true); } //adjust rate var shippingTotal = _orderTotalCalculationService.AdjustShippingRate(point.PickupFee, cart, out var _, true); var rateBase = _taxService.GetShippingPrice(shippingTotal, _workContext.CurrentCustomer); var rate = _currencyService.ConvertFromPrimaryStoreCurrency(rateBase, _workContext.WorkingCurrency); pickupPointModel.PickupFee = _priceFormatter.FormatShippingPrice(rate, true); return(pickupPointModel); }).ToList(); } else { foreach (var error in pickupPointsResponse.Errors) { model.Warnings.Add(error); } } } //only available pickup points var shippingProviders = _shippingPluginManager.LoadActivePlugins(_workContext.CurrentCustomer, _storeContext.CurrentStore.Id); if (!shippingProviders.Any()) { if (!pickupPointProviders.Any()) { model.Warnings.Add(_localizationService.GetResource("Checkout.ShippingIsNotAllowed")); model.Warnings.Add(_localizationService.GetResource("Checkout.PickupPoints.NotAvailable")); } model.PickupInStoreOnly = true; model.PickupInStore = true; return(model); } } return(model); }