public async Task <IViewComponentResult> InvokeAsync(bool isEditable = false)
        {
            var orderTotalsEvent = new RenderingOrderTotalsEvent();
            await Services.EventPublisher.PublishAsync(orderTotalsEvent);

            var currency = Services.WorkContext.WorkingCurrency;
            var customer = orderTotalsEvent.Customer ?? Services.WorkContext.CurrentCustomer;
            var storeId  = orderTotalsEvent.StoreId ?? Services.StoreContext.CurrentStore.Id;

            var cart = await _shoppingCartService.GetCartItemsAsync(customer, ShoppingCartType.ShoppingCart, storeId);

            var model = new OrderTotalsModel
            {
                IsEditable = isEditable
            };

            if (!cart.Any())
            {
                return(View(model));
            }

            model.Weight = decimal.Zero;

            foreach (var cartItem in cart)
            {
                model.Weight += cartItem.Item.Product.Weight * cartItem.Item.Quantity;
            }

            var measureWeight = await _db.MeasureWeights.FindByIdAsync(_measureSettings.BaseWeightId, false);

            if (measureWeight != null)
            {
                model.WeightMeasureUnitName = measureWeight.GetLocalized(x => x.Name);
            }

            // SubTotal
            var cartSubTotal = await _orderCalculationService.GetShoppingCartSubtotalAsync(cart);

            var subTotalConverted = _currencyService.ConvertFromPrimaryCurrency(cartSubTotal.SubtotalWithoutDiscount.Amount, currency);

            model.SubTotal = subTotalConverted;

            if (cartSubTotal.DiscountAmount > decimal.Zero)
            {
                var subTotalDiscountAmountConverted = _currencyService.ConvertFromPrimaryCurrency(cartSubTotal.DiscountAmount.Amount, currency);

                model.SubTotalDiscount = subTotalDiscountAmountConverted * -1;
                model.AllowRemovingSubTotalDiscount = cartSubTotal.AppliedDiscount != null &&
                                                      cartSubTotal.AppliedDiscount.RequiresCouponCode &&
                                                      cartSubTotal.AppliedDiscount.CouponCode.HasValue() &&
                                                      model.IsEditable;
            }

            // Shipping info
            // TODO: (ms) (core) Re-implement when any payment method has been implemented
            model.RequiresShipping = false;// cart.IsShippingRequired();
            if (model.RequiresShipping)
            {
                var shippingTotal = await _orderCalculationService.GetShoppingCartShippingTotalAsync(cart);

                if (shippingTotal.ShippingTotal.HasValue)
                {
                    var shippingTotalConverted = _currencyService.ConvertFromPrimaryCurrency(shippingTotal.ShippingTotal.Value.Amount, currency);
                    model.Shipping = shippingTotalConverted.ToString();

                    // Selected shipping method
                    var shippingOption = customer.GenericAttributes.SelectedShippingOption;
                    if (shippingOption != null)
                    {
                        model.SelectedShippingMethod = shippingOption.Name;
                    }
                }
            }

            // Payment method fee
            var paymentFee = await _orderCalculationService.GetShoppingCartPaymentFeeAsync(cart, customer.GenericAttributes.SelectedPaymentMethod);

            var paymentFeeTax = await _taxCalculator.CalculatePaymentFeeTaxAsync(paymentFee.Amount, customer : customer);

            if (paymentFeeTax.Price != 0m)
            {
                var convertedPaymentFeeTax = _currencyService.ConvertFromPrimaryCurrency(paymentFeeTax.Price, currency);
                model.PaymentMethodAdditionalFee = convertedPaymentFeeTax;
            }

            // Tax
            var displayTax      = true;
            var displayTaxRates = true;

            if (_taxSettings.HideTaxInOrderSummary && Services.WorkContext.TaxDisplayType == TaxDisplayType.IncludingTax)
            {
                displayTax      = false;
                displayTaxRates = false;
            }
            else
            {
                (Money Price, TaxRatesDictionary TaxRates)cartTaxBase = (new Money(1.19m, currency), new TaxRatesDictionary()); //await _orderCalculationService.GetShoppingCartTaxTotalAsync(cart);
                var cartTax = _currencyService.ConvertFromPrimaryCurrency(cartTaxBase.Price.Amount, currency);

                if (cartTaxBase.Price == decimal.Zero && _taxSettings.HideZeroTax)
                {
                    displayTax      = false;
                    displayTaxRates = false;
                }
                else
                {
                    displayTaxRates = _taxSettings.DisplayTaxRates && cartTaxBase.TaxRates.Count > 0;
                    displayTax      = !displayTaxRates;
                    model.Tax       = cartTax.ToString(true);

                    foreach (var taxRate in cartTaxBase.TaxRates)
                    {
                        var rate     = _taxService.FormatTaxRate(taxRate.Key);
                        var labelKey = "ShoppingCart.Totals.TaxRateLine" + (Services.WorkContext.TaxDisplayType == TaxDisplayType.IncludingTax ? "Incl" : "Excl");
                        model.TaxRates.Add(new OrderTotalsModel.TaxRate
                        {
                            Rate  = rate,
                            Value = _currencyService.ConvertFromPrimaryCurrency(taxRate.Value, currency),
                            Label = _localizationService.GetResource(labelKey).FormatCurrent(rate)
                        });
                    }
                }
            }

            model.DisplayTaxRates = displayTaxRates;
            model.DisplayTax      = displayTax;

            model.DisplayWeight             = _shoppingCartSettings.ShowWeight;
            model.ShowConfirmOrderLegalHint = _shoppingCartSettings.ShowConfirmOrderLegalHint;

            // Cart total
            // TODO: (ms) (core) Re-implement when any payment method has been implemented
            //var cartTotal = await _orderCalculationService.GetShoppingCartTotalAsync(cart);

            var cartTotal = new ShoppingCartTotal
            {
                Total                      = new(decimal.Zero, currency),
                ToNearestRounding          = new(decimal.Zero, currency),
                DiscountAmount             = new(15, currency),
                AppliedDiscount            = (await _db.Discounts.FirstOrDefaultAsync()) ?? new(),
                RedeemedRewardPoints       = 10,
                RedeemedRewardPointsAmount = new(10, currency),
                CreditBalance              = new(decimal.Zero, currency),
                AppliedGiftCards           = new() { new() { GiftCard = (await _db.GiftCards.Include(x => x.GiftCardUsageHistory).FirstOrDefaultAsync()) ?? new(), UsableAmount = new(50m, _currencyService.PrimaryCurrency) } },
                ConvertedAmount            = new ShoppingCartTotal.ConvertedAmounts
                {
                    Total             = new(decimal.Zero, currency),
                    ToNearestRounding = new(decimal.Zero, currency)
                }
            };

            if (cartTotal.ConvertedAmount.Total.HasValue)
            {
                model.OrderTotal = cartTotal.ConvertedAmount.Total.Value;
                if (cartTotal.ConvertedAmount.ToNearestRounding != decimal.Zero)
                {
                    model.OrderTotalRounding = cartTotal.ConvertedAmount.ToNearestRounding;
                }
            }

            // Discount
            if (cartTotal.DiscountAmount > decimal.Zero)
            {
                var orderTotalDiscountAmount = _currencyService.ConvertFromPrimaryCurrency(cartTotal.DiscountAmount.Amount, currency);
                model.OrderTotalDiscount = orderTotalDiscountAmount * -1;
                model.AllowRemovingOrderTotalDiscount = cartTotal.AppliedDiscount != null &&
                                                        cartTotal.AppliedDiscount.RequiresCouponCode &&
                                                        cartTotal.AppliedDiscount.CouponCode.HasValue() &&
                                                        model.IsEditable;
            }

            // Gift cards
            if (!cartTotal.AppliedGiftCards.IsNullOrEmpty())
            {
                foreach (var appliedGiftCard in cartTotal.AppliedGiftCards)
                {
                    if (appliedGiftCard?.GiftCard == null)
                    {
                        continue;
                    }

                    var gcModel = new OrderTotalsModel.GiftCard
                    {
                        Id         = appliedGiftCard.GiftCard.Id,
                        CouponCode = appliedGiftCard.GiftCard.GiftCardCouponCode,
                    };

                    var amountCanBeUsed = _currencyService.ConvertFromPrimaryCurrency(appliedGiftCard.UsableAmount.Amount, currency);
                    gcModel.Amount = amountCanBeUsed * -1;

                    var remainingAmountBase = _giftCardService.GetRemainingAmount(appliedGiftCard.GiftCard) - appliedGiftCard.UsableAmount;
                    var remainingAmount     = _currencyService.ConvertFromPrimaryCurrency(remainingAmountBase.Amount, currency);
                    gcModel.Remaining = remainingAmount;

                    model.GiftCards.Add(gcModel);
                }
            }

            // Reward points
            if (cartTotal.RedeemedRewardPointsAmount > decimal.Zero)
            {
                var redeemedRewardPointsAmountInCustomerCurrency = _currencyService.ConvertFromPrimaryCurrency(cartTotal.RedeemedRewardPointsAmount.Amount, currency);
                model.RedeemedRewardPoints       = cartTotal.RedeemedRewardPoints;
                model.RedeemedRewardPointsAmount = (redeemedRewardPointsAmountInCustomerCurrency * -1).ToString(true);
            }

            // Credit balance.
            if (cartTotal.CreditBalance > decimal.Zero)
            {
                var convertedCreditBalance = _currencyService.ConvertFromPrimaryCurrency(cartTotal.CreditBalance.Amount, currency);
                model.CreditBalance = (convertedCreditBalance * -1).ToString(true);
            }

            return(View(model));
        }
Exemplo n.º 2
0
        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(_, subTotalWithDiscount, _, _, _) = await GetCartSubTotalAsync(cart, false);

            // Subtotal with discount.
            var subTotalBase = subTotalWithDiscount;

            // Shipping without tax.
            var(shoppingCartShipping, _, _) = 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 (shoppingCartShipping.HasValue)
            {
                resultTemp += shoppingCartShipping.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))
            {
                // 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.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                 = shoppingCartShipping.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,
                ConvertedAmount            = new ShoppingCartTotal.ConvertedAmounts
                {
                    Total             = orderTotalConverted.HasValue ? new(orderTotalConverted.Value, _workingCurrency) : null,
                    ToNearestRounding = new(toNearestRoundingConverted, _workingCurrency)
                }
            };

            return(result);
        }