public virtual async Task <ShoppingCartTotal> GetShoppingCartTotalAsync( IList <OrganizedShoppingCartItem> cart, bool includeRewardPoints = true, bool includePaymentAdditionalFee = true, bool includeCreditBalance = true) { Guard.NotNull(cart, nameof(cart)); var store = _storeContext.CurrentStore; var customer = cart.GetCustomer(); var paymentMethodSystemName = customer != null ? customer.GenericAttributes.SelectedPaymentMethod : string.Empty; var subTotal = await GetShoppingCartSubTotalAsync(cart, false); // Subtotal with discount. var subtotalBase = subTotal.SubTotalWithDiscount; // Shipping without tax. Money?shoppingCartShipping = await GetShoppingCartShippingTotalAsync(cart, false); // Payment method additional fee without tax. var paymentFeeWithoutTax = new Money(_primaryCurrency); if (includePaymentAdditionalFee && paymentMethodSystemName.HasValue()) { var provider = _providerManager.GetProvider <IPaymentMethod>(paymentMethodSystemName); var paymentFee = await provider?.Value?.GetAdditionalHandlingFeeAsync(cart); (paymentFeeWithoutTax, _) = await _taxService.GetPaymentMethodAdditionalFeeAsync(paymentFee, false, customer : customer); } // Tax. var(shoppingCartTax, _) = await GetTaxTotalAsync(cart, includePaymentAdditionalFee); // Order total. var resultTemp = subtotalBase; if (shoppingCartShipping.HasValue) { resultTemp += shoppingCartShipping.Value; } resultTemp += paymentFeeWithoutTax; resultTemp += shoppingCartTax; // Round: resultTemp = _primaryCurrency.AsMoney(resultTemp.Amount); // Order total discount. var(discountAmount, appliedDiscount) = await GetDiscountAmountAsync(resultTemp, DiscountType.AssignedToOrderTotal, customer); // Subtotal with discount. if (resultTemp < discountAmount) { discountAmount = resultTemp; } // Reduce subtotal. resultTemp = _primaryCurrency.AsMoney((resultTemp - discountAmount).Amount, true, true); // Applied gift cards. var appliedGiftCards = new List <AppliedGiftCard>(); if (!cart.IncludesMatchingItems(x => x.IsRecurring)) { // TODO: (ms) (core) Gift card usage in OrderCalculationService needs to be tested extensively as the gift card code has been fundamentally changed. var giftCards = await _giftCardService.GetValidGiftCardsAsync(store.Id, customer); foreach (var gc in giftCards) { if (resultTemp > decimal.Zero) { var usableAmount = resultTemp > gc.UsableAmount ? gc.UsableAmount : resultTemp; // Reduce subtotal. resultTemp -= usableAmount; appliedGiftCards.Add(new() { GiftCard = gc.GiftCard, UsableAmount = usableAmount }); } } } // Reward points. var redeemedRewardPoints = 0; var redeemedRewardPointsAmount = new Money(_primaryCurrency); if (_rewardPointsSettings.Enabled && includeRewardPoints && resultTemp > decimal.Zero && customer != null && customer.GenericAttributes.UseRewardPointsDuringCheckout) { var rewardPointsBalance = customer.GetRewardPointsBalance(); var rewardPointsBalanceAmount = ConvertRewardPointsToAmount(rewardPointsBalance); if (resultTemp > rewardPointsBalanceAmount) { redeemedRewardPointsAmount = rewardPointsBalanceAmount; redeemedRewardPoints = rewardPointsBalance; } else { redeemedRewardPointsAmount = resultTemp; redeemedRewardPoints = ConvertAmountToRewardPoints(redeemedRewardPointsAmount); } } resultTemp = _primaryCurrency.AsMoney(resultTemp.Amount, true, true); // Return null if we have errors: Money?orderTotal = shoppingCartShipping.HasValue ? resultTemp : null; var orderTotalConverted = orderTotal; var appliedCreditBalance = new Money(_primaryCurrency); var toNearestRounding = new Money(_primaryCurrency); var toNearestRoundingConverted = new Money(_primaryCurrency); if (orderTotal.HasValue) { orderTotal = orderTotal.Value - redeemedRewardPointsAmount; // Credit balance. if (includeCreditBalance && customer != null && orderTotal > decimal.Zero) { var creditBalance = _primaryCurrency.AsMoney(customer.GenericAttributes.UseCreditBalanceDuringCheckout, false); if (creditBalance > decimal.Zero) { if (creditBalance > orderTotal) { // Normalize used amount. appliedCreditBalance = orderTotal.Value; customer.GenericAttributes.UseCreditBalanceDuringCheckout = orderTotal.Value.Amount; await _db.SaveChangesAsync(); } else { appliedCreditBalance = creditBalance; } } } orderTotal = _primaryCurrency.AsMoney(orderTotal.Value.Amount - appliedCreditBalance.Amount); orderTotalConverted = _currencyService.ConvertFromPrimaryStoreCurrency(orderTotal.Value, store); // Round order total to nearest (cash rounding). if (_primaryCurrency.RoundOrderTotalEnabled && paymentMethodSystemName.HasValue()) { var paymentMethod = await _db.PaymentMethods.AsNoTracking().FirstOrDefaultAsync(x => x.PaymentMethodSystemName == paymentMethodSystemName); if (paymentMethod?.RoundOrderTotalEnabled ?? false) { orderTotal = _primaryCurrency.RoundToNearest(orderTotal.Value, out toNearestRounding); orderTotalConverted = _primaryCurrency.RoundToNearest(orderTotalConverted.Value, out toNearestRoundingConverted); } } } var result = new ShoppingCartTotal(orderTotal) { ToNearestRounding = toNearestRounding, DiscountAmount = discountAmount, AppliedDiscount = appliedDiscount, RedeemedRewardPoints = redeemedRewardPoints, RedeemedRewardPointsAmount = redeemedRewardPointsAmount, CreditBalance = appliedCreditBalance, AppliedGiftCards = appliedGiftCards, ConvertedAmount = new ShoppingCartTotal.ConvertedAmounts { Total = orderTotalConverted, ToNearestRounding = toNearestRoundingConverted } }; return(result); }
public virtual async Task <ShoppingCartTotal> GetShoppingCartTotalAsync( IList <OrganizedShoppingCartItem> cart, bool includeRewardPoints = true, bool includePaymentFee = true, bool includeCreditBalance = true) { Guard.NotNull(cart, nameof(cart)); var store = _storeContext.CurrentStore; var customer = cart.GetCustomer(); var paymentMethodSystemName = customer != null ? customer.GenericAttributes.SelectedPaymentMethod : string.Empty; var subtotal = await GetCartSubtotalAsync(cart, false); var subtotalBase = subtotal.SubtotalWithDiscount; // Shipping without tax. var shipping = await GetCartShippingTotalAsync(cart, false); // Payment method additional fee without tax. var paymentFeeWithoutTax = decimal.Zero; if (includePaymentFee && paymentMethodSystemName.HasValue()) { var paymentFee = await GetShoppingCartPaymentFeeAsync(cart, paymentMethodSystemName); if (paymentFee != decimal.Zero) { var paymentFeeExclTax = await _taxCalculator.CalculatePaymentFeeTaxAsync(paymentFee.Amount, false, customer : customer); paymentFeeWithoutTax = paymentFeeExclTax.Price; } } // Tax. var(shoppingCartTax, _) = await GetCartTaxTotalAsync(cart, includePaymentFee); // Order total. var resultTemp = subtotalBase; if (shipping.ShippingTotal.HasValue) { resultTemp += shipping.ShippingTotal.Value; } resultTemp = _workingCurrency.RoundIfEnabledFor(resultTemp + paymentFeeWithoutTax + shoppingCartTax); // Order total discount. var(discountAmount, appliedDiscount) = await GetDiscountAmountAsync(resultTemp, DiscountType.AssignedToOrderTotal, customer); // Subtotal with discount. if (resultTemp < discountAmount) { discountAmount = resultTemp; } // Reduce subtotal. resultTemp = _workingCurrency.RoundIfEnabledFor(Math.Max(resultTemp - discountAmount, decimal.Zero)); // Applied gift cards. var appliedGiftCards = new List <AppliedGiftCard>(); if (!cart.IncludesMatchingItems(x => x.IsRecurring)) { var giftCards = await _giftCardService.GetValidGiftCardsAsync(store.Id, customer); foreach (var gc in giftCards) { if (resultTemp > decimal.Zero) { var usableAmount = resultTemp > gc.UsableAmount.Amount ? gc.UsableAmount.Amount : resultTemp; // Reduce subtotal. resultTemp -= usableAmount; appliedGiftCards.Add(new() { GiftCard = gc.GiftCard, UsableAmount = new(usableAmount, _primaryCurrency) }); } } } // Reward points. var redeemedRewardPoints = 0; var redeemedRewardPointsAmount = decimal.Zero; if (_rewardPointsSettings.Enabled && includeRewardPoints && resultTemp > decimal.Zero && customer != null && customer.GenericAttributes.UseRewardPointsDuringCheckout) { var rewardPointsBalance = customer.GetRewardPointsBalance(); var rewardPointsBalanceAmount = ConvertRewardPointsToAmountCore(rewardPointsBalance); if (resultTemp > rewardPointsBalanceAmount) { redeemedRewardPointsAmount = rewardPointsBalanceAmount; redeemedRewardPoints = rewardPointsBalance; } else { redeemedRewardPointsAmount = resultTemp; redeemedRewardPoints = ConvertAmountToRewardPoints(redeemedRewardPointsAmount); } } resultTemp = _workingCurrency.RoundIfEnabledFor(Math.Max(resultTemp, decimal.Zero)); // Return null if we have errors: decimal?orderTotal = shipping.ShippingTotal.HasValue ? resultTemp : null; var orderTotalConverted = orderTotal; var appliedCreditBalance = decimal.Zero; var toNearestRounding = decimal.Zero; var toNearestRoundingConverted = decimal.Zero; if (orderTotal.HasValue) { orderTotal = orderTotal.Value - redeemedRewardPointsAmount; // Credit balance. if (includeCreditBalance && customer != null && orderTotal > decimal.Zero) { var creditBalance = customer.GenericAttributes.UseCreditBalanceDuringCheckout; if (creditBalance > decimal.Zero) { if (creditBalance > orderTotal) { // Normalize used amount. appliedCreditBalance = orderTotal.Value; customer.GenericAttributes.UseCreditBalanceDuringCheckout = orderTotal.Value; await _db.SaveChangesAsync(); } else { appliedCreditBalance = creditBalance; } } } orderTotal = _workingCurrency.RoundIfEnabledFor(orderTotal.Value - appliedCreditBalance); orderTotalConverted = _currencyService.ConvertToWorkingCurrency(orderTotal.Value).Amount; // Round order total to nearest (cash rounding). if (_workingCurrency.RoundOrderTotalEnabled && paymentMethodSystemName.HasValue()) { var paymentMethod = await _db.PaymentMethods.AsNoTracking().FirstOrDefaultAsync(x => x.PaymentMethodSystemName == paymentMethodSystemName); if (paymentMethod?.RoundOrderTotalEnabled ?? false) { orderTotal = _workingCurrency.RoundToNearest(orderTotal.Value, out toNearestRounding); orderTotalConverted = _workingCurrency.RoundToNearest(orderTotalConverted.Value, out toNearestRoundingConverted); } } } var result = new ShoppingCartTotal { Total = orderTotal.HasValue ? new(orderTotal.Value, _primaryCurrency) : null, ToNearestRounding = new(toNearestRounding, _primaryCurrency), DiscountAmount = new(discountAmount, _primaryCurrency), AppliedDiscount = appliedDiscount, RedeemedRewardPoints = redeemedRewardPoints, RedeemedRewardPointsAmount = new(redeemedRewardPointsAmount, _primaryCurrency), CreditBalance = new(appliedCreditBalance, _primaryCurrency), AppliedGiftCards = appliedGiftCards, LineItems = subtotal.LineItems, ConvertedAmount = new ShoppingCartTotal.ConvertedAmounts { Total = orderTotalConverted.HasValue ? new(orderTotalConverted.Value, _workingCurrency) : null, ToNearestRounding = new(toNearestRoundingConverted, _workingCurrency) } }; return(result); }