/// <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="appliedDiscount">Applied discount</param> /// <param name="redeemedRewardPoints">Reward points to redeem</param> /// <param name="redeemedRewardPointsAmount">Reward points amount in primary store currency to redeem</param> /// <param name="ignoreRewardPonts">A value indicating whether we should ignore reward points (if enabled and a customer is going to use them)</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 virtual decimal? GetShoppingCartTotal(IList<ShoppingCartItem> cart, out decimal discountAmount, out Discount appliedDiscount, out List<AppliedGiftCard> appliedGiftCards, out int redeemedRewardPoints, out decimal redeemedRewardPointsAmount, bool ignoreRewardPonts = false, bool usePaymentMethodAdditionalFee = true) { redeemedRewardPoints = 0; redeemedRewardPointsAmount = decimal.Zero; var customer = cart.GetCustomer(); string paymentMethodSystemName = ""; if (customer != null) { paymentMethodSystemName = customer.GetAttribute<string>( SystemCustomerAttributeNames.SelectedPaymentMethod, _genericAttributeService, _storeContext.CurrentStore.Id); } //subtotal without tax decimal subtotalBase = decimal.Zero; decimal orderSubTotalDiscountAmount = decimal.Zero; Discount orderSubTotalAppliedDiscount = null; decimal subTotalWithoutDiscountBase = decimal.Zero; decimal subTotalWithDiscountBase = decimal.Zero; GetShoppingCartSubTotal(cart, false, out orderSubTotalDiscountAmount, out orderSubTotalAppliedDiscount, out subTotalWithoutDiscountBase, out subTotalWithDiscountBase); //subtotal with discount subtotalBase = subTotalWithDiscountBase; //shipping without tax decimal? shoppingCartShipping = GetShoppingCartShippingTotal(cart, false); //payment method additional fee without tax decimal paymentMethodAdditionalFeeWithoutTax = decimal.Zero; if (usePaymentMethodAdditionalFee && !String.IsNullOrEmpty(paymentMethodSystemName)) { decimal paymentMethodAdditionalFee = _paymentService.GetAdditionalHandlingFee(cart, paymentMethodSystemName); paymentMethodAdditionalFeeWithoutTax = _taxService.GetPaymentMethodAdditionalFee(paymentMethodAdditionalFee, false, customer); } //tax decimal shoppingCartTax = GetTaxTotal(cart, usePaymentMethodAdditionalFee); //order total decimal resultTemp = decimal.Zero; resultTemp += subtotalBase; if (shoppingCartShipping.HasValue) { resultTemp += shoppingCartShipping.Value; } resultTemp += paymentMethodAdditionalFeeWithoutTax; resultTemp += shoppingCartTax; if (_shoppingCartSettings.RoundPricesDuringCalculation) resultTemp = Math.Round(resultTemp, 2); #region Order total discount discountAmount = GetOrderTotalDiscount(customer, resultTemp, out appliedDiscount); //sub totals with discount if (resultTemp < discountAmount) discountAmount = resultTemp; //reduce subtotal resultTemp -= discountAmount; if (resultTemp < decimal.Zero) resultTemp = decimal.Zero; if (_shoppingCartSettings.RoundPricesDuringCalculation) resultTemp = Math.Round(resultTemp, 2); #endregion #region Applied gift cards //let's apply gift cards now (gift cards that can be used) appliedGiftCards = new List<AppliedGiftCard>(); if (!cart.IsRecurring()) { //we don't apply gift cards for recurring products var giftCards = _giftCardService.GetActiveGiftCardsAppliedByCustomer(customer); if (giftCards!=null) foreach (var gc in giftCards) if (resultTemp > decimal.Zero) { decimal remainingAmount = gc.GetGiftCardRemainingAmount(); decimal amountCanBeUsed = decimal.Zero; if (resultTemp > remainingAmount) amountCanBeUsed = remainingAmount; else amountCanBeUsed = resultTemp; //reduce subtotal resultTemp -= amountCanBeUsed; var appliedGiftCard = new AppliedGiftCard(); appliedGiftCard.GiftCard = gc; appliedGiftCard.AmountCanBeUsed = amountCanBeUsed; appliedGiftCards.Add(appliedGiftCard); } } #endregion if (resultTemp < decimal.Zero) resultTemp = decimal.Zero; if (_shoppingCartSettings.RoundPricesDuringCalculation) resultTemp = Math.Round(resultTemp, 2); decimal? orderTotal = null; if (!shoppingCartShipping.HasValue) { //return null if we have errors orderTotal = null; return orderTotal; } else { //return result if we have no errors orderTotal = resultTemp; } #region Reward points if (_rewardPointsSettings.Enabled && !ignoreRewardPonts && customer.GetAttribute<bool>(SystemCustomerAttributeNames.UseRewardPointsDuringCheckout, _genericAttributeService, _storeContext.CurrentStore.Id)) { int rewardPointsBalance = customer.GetRewardPointsBalance(); if (CheckMinimumRewardPointsToUseRequirement(rewardPointsBalance)) { decimal rewardPointsBalanceAmount = ConvertRewardPointsToAmount(rewardPointsBalance); if (orderTotal.HasValue && orderTotal.Value > decimal.Zero) { if (orderTotal.Value > rewardPointsBalanceAmount) { redeemedRewardPoints = rewardPointsBalance; redeemedRewardPointsAmount = rewardPointsBalanceAmount; } else { redeemedRewardPointsAmount = orderTotal.Value; redeemedRewardPoints = ConvertAmountToRewardPoints(redeemedRewardPointsAmount); } } } } #endregion if (orderTotal.HasValue) { orderTotal = orderTotal.Value - redeemedRewardPointsAmount; if (_shoppingCartSettings.RoundPricesDuringCalculation) orderTotal = Math.Round(orderTotal.Value, 2); return orderTotal; } else return null; }
/// <summary> /// Gets shopping cart shipping total /// </summary> /// <param name="cart">Cart</param> /// <param name="includingTax">A value indicating whether calculated price should include tax</param> /// <param name="taxRate">Applied tax rate</param> /// <param name="appliedDiscount">Applied discount</param> /// <returns>Shipping total</returns> public virtual decimal? GetShoppingCartShippingTotal(IList<ShoppingCartItem> cart, bool includingTax, out decimal taxRate, out Discount appliedDiscount) { decimal? shippingTotal = null; decimal? shippingTotalTaxed = null; appliedDiscount = null; taxRate = decimal.Zero; var customer = cart.GetCustomer(); bool isFreeShipping = IsFreeShipping(cart); if (isFreeShipping) return decimal.Zero; ShippingOption shippingOption = null; if (customer != null) shippingOption = customer.GetAttribute<ShippingOption>(SystemCustomerAttributeNames.SelectedShippingOption, _genericAttributeService, _storeContext.CurrentStore.Id); if (shippingOption != null) { //use last shipping option (get from cache) //adjust shipping rate shippingTotal = AdjustShippingRate(shippingOption.Rate, cart, out appliedDiscount); } else { //use fixed rate (if possible) Address shippingAddress = null; if (customer != null) shippingAddress = customer.ShippingAddress; var shippingRateComputationMethods = _shippingService.LoadActiveShippingRateComputationMethods(_storeContext.CurrentStore.Id); if (shippingRateComputationMethods == null || shippingRateComputationMethods.Count == 0) throw new NopException("Shipping rate computation method could not be loaded"); if (shippingRateComputationMethods.Count == 1) { var shippingRateComputationMethod = shippingRateComputationMethods[0]; var shippingOptionRequests = _shippingService.CreateShippingOptionRequests(cart, shippingAddress); decimal? fixedRate = null; foreach (var shippingOptionRequest in shippingOptionRequests) { //calculate fixed rates for each request-package var fixedRateTmp = shippingRateComputationMethod.GetFixedRate(shippingOptionRequest); if (fixedRateTmp.HasValue) { if (!fixedRate.HasValue) fixedRate = decimal.Zero; fixedRate += fixedRateTmp.Value; } } if (fixedRate.HasValue) { //adjust shipping rate shippingTotal = AdjustShippingRate(fixedRate.Value, cart, out appliedDiscount); } } } if (shippingTotal.HasValue) { if (shippingTotal.Value < decimal.Zero) shippingTotal = decimal.Zero; //round if (_shoppingCartSettings.RoundPricesDuringCalculation) shippingTotal = Math.Round(shippingTotal.Value, 2); shippingTotalTaxed = _taxService.GetShippingPrice(shippingTotal.Value, includingTax, customer, out taxRate); //round if (_shoppingCartSettings.RoundPricesDuringCalculation) shippingTotalTaxed = Math.Round(shippingTotalTaxed.Value, 2); } return shippingTotalTaxed; }
public void Can_save_and_load_discount() { var discount = new Discount { DiscountType = DiscountType.AssignedToCategories, Name = "Discount 1", UsePercentage = true, DiscountPercentage = 1.1M, DiscountAmount = 2.1M, MaximumDiscountAmount = 3.1M, StartDateUtc = new DateTime(2010, 01, 01), EndDateUtc = new DateTime(2010, 01, 02), RequiresCouponCode = true, CouponCode = "SecretCode", DiscountLimitation = DiscountLimitationType.Unlimited, LimitationTimes = 3, MaximumDiscountedQuantity = 4, }; var fromDb = SaveAndLoadEntity(discount); fromDb.ShouldNotBeNull(); fromDb.DiscountType.ShouldEqual(DiscountType.AssignedToCategories); fromDb.Name.ShouldEqual("Discount 1"); fromDb.UsePercentage.ShouldEqual(true); fromDb.DiscountPercentage.ShouldEqual(1.1M); fromDb.DiscountAmount.ShouldEqual(2.1M); fromDb.MaximumDiscountAmount.ShouldEqual(3.1M); fromDb.StartDateUtc.ShouldEqual(new DateTime(2010, 01, 01)); fromDb.EndDateUtc.ShouldEqual(new DateTime(2010, 01, 02)); fromDb.RequiresCouponCode.ShouldEqual(true); fromDb.CouponCode.ShouldEqual("SecretCode"); fromDb.DiscountLimitation.ShouldEqual(DiscountLimitationType.Unlimited); fromDb.LimitationTimes.ShouldEqual(3); fromDb.MaximumDiscountedQuantity.ShouldEqual(4); }
/// <summary> /// Checks discount limitation for customer /// </summary> /// <param name="discount">Discount</param> /// <param name="customer">Customer</param> /// <returns>Value indicating whether discount can be used</returns> protected virtual bool CheckDiscountLimitations(Discount discount, Customer customer) { if (discount == null) throw new ArgumentNullException("discount"); switch (discount.DiscountLimitation) { case DiscountLimitationType.Unlimited: { return true; } case DiscountLimitationType.NTimesOnly: { var totalDuh = GetAllDiscountUsageHistory(discount.Id, null, 0, 1).TotalCount; return totalDuh < discount.LimitationTimes; } case DiscountLimitationType.NTimesPerCustomer: { if (customer != null && !customer.IsGuest()) { //registered customer var totalDuh = GetAllDiscountUsageHistory(discount.Id, customer.Id, 0, 1).TotalCount; return totalDuh < discount.LimitationTimes; } else { //guest return true; } } default: break; } return false; }
public void Can_save_and_load_discount_with_discountRequirements() { var discount = new Discount { Name = "Discount 1", DiscountType = DiscountType.AssignedToCategories, UsePercentage = true, DiscountPercentage = 1, DiscountAmount = 2, StartDateUtc = new DateTime(2010, 01, 01), EndDateUtc = new DateTime(2010, 01, 02), RequiresCouponCode = true, CouponCode = "SecretCode", DiscountLimitation = DiscountLimitationType.Unlimited, LimitationTimes = 3 }; discount.DiscountRequirements.Add ( new DiscountRequirement() { DiscountRequirementRuleSystemName = "BillingCountryIs" } ); var fromDb = SaveAndLoadEntity(discount); fromDb.ShouldNotBeNull(); fromDb.Name.ShouldEqual("Discount 1"); fromDb.DiscountRequirements.ShouldNotBeNull(); (fromDb.DiscountRequirements.Count == 1).ShouldBeTrue(); fromDb.DiscountRequirements.First().DiscountRequirementRuleSystemName.ShouldEqual("BillingCountryIs"); }
public void Can_validate_discount_dateRange() { var discount = new Discount { DiscountType = DiscountType.AssignedToSkus, Name = "Discount 2", UsePercentage = false, DiscountPercentage = 0, DiscountAmount = 5, StartDateUtc = DateTime.UtcNow.AddDays(-1), EndDateUtc = DateTime.UtcNow.AddDays(1), RequiresCouponCode = false, DiscountLimitation = DiscountLimitationType.Unlimited, }; var customer = new Customer { CustomerGuid = Guid.NewGuid(), AdminComment = "", Active = true, Deleted = false, CreatedOnUtc = new DateTime(2010, 01, 01), LastActivityDateUtc = new DateTime(2010, 01, 02) }; var result1 = _discountService.IsDiscountValid(discount, customer); result1.ShouldEqual(true); discount.StartDateUtc = DateTime.UtcNow.AddDays(1); var result2 = _discountService.IsDiscountValid(discount, customer); result2.ShouldEqual(false); }
protected virtual void PrepareDiscountModel(DiscountModel model, Discount discount) { if (model == null) throw new ArgumentNullException("model"); model.PrimaryStoreCurrencyCode = _currencyService.GetCurrencyById(_currencySettings.PrimaryStoreCurrencyId).CurrencyCode; model.AvailableDiscountRequirementRules.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Promotions.Discounts.Requirements.DiscountRequirementType.Select"), Value = "" }); var discountRules = _discountService.LoadAllDiscountRequirementRules(); foreach (var discountRule in discountRules) model.AvailableDiscountRequirementRules.Add(new SelectListItem() { Text = discountRule.PluginDescriptor.FriendlyName, Value = discountRule.PluginDescriptor.SystemName }); if (discount != null) { //applied to categories foreach (var category in discount.AppliedToCategories) { if (category != null && !category.Deleted) { model.AppliedToCategoryModels.Add(new DiscountModel.AppliedToCategoryModel() { CategoryId = category.Id, Name = category.Name }); } } //applied to product variants foreach (var product in discount.AppliedToProducts) { if (product != null && !product.Deleted) { var appliedToProductModel = new DiscountModel.AppliedToProductModel() { ProductId = product.Id, ProductName = product.Name }; model.AppliedToProductModels.Add(appliedToProductModel); } } //requirements foreach (var dr in discount.DiscountRequirements.OrderBy(dr=>dr.Id)) { var drr = _discountService.LoadDiscountRequirementRuleBySystemName(dr.DiscountRequirementRuleSystemName); if (drr != null) { model.DiscountRequirementMetaInfos.Add(new DiscountModel.DiscountRequirementMetaInfo() { DiscountRequirementId = dr.Id, RuleName = drr.PluginDescriptor.FriendlyName, ConfigurationUrl = GetRequirementUrlInternal(drr, discount, dr.Id) }); } } } }
protected virtual string GetRequirementUrlInternal(IDiscountRequirementRule discountRequirementRule, Discount discount, int? discountRequirementId) { if (discountRequirementRule == null) throw new ArgumentNullException("discountRequirementRule"); if (discount == null) throw new ArgumentNullException("discount"); string url = string.Format("{0}{1}", _webHelper.GetStoreLocation(), discountRequirementRule.GetConfigurationUrl(discount.Id, discountRequirementId)); return url; }
/// <summary> /// Delete discount /// </summary> /// <param name="discount">Discount</param> public virtual void DeleteDiscount(Discount discount) { if (discount == null) throw new ArgumentNullException("discount"); _discountRepository.Delete(discount); _cacheManager.RemoveByPattern(DISCOUNTS_PATTERN_KEY); //event notification _eventPublisher.EntityDeleted(discount); }
public void Can_calculate_discount_amount_fixed() { var discount = new Discount() { UsePercentage = false, DiscountAmount = 10 }; discount.GetDiscountAmount(100).ShouldEqual(10); discount.DiscountAmount = 20; discount.GetDiscountAmount(200).ShouldEqual(20); }
public void Can_calculate_discount_amount_percentage() { var discount = new Discount() { UsePercentage = true, DiscountPercentage= 30 }; discount.GetDiscountAmount(100).ShouldEqual(30); discount.DiscountPercentage = 60; discount.GetDiscountAmount(200).ShouldEqual(120); }
public static bool ContainsDiscount(this IList<Discount> discounts, Discount discount) { if (discounts == null) throw new ArgumentNullException("discounts"); if (discount == null) throw new ArgumentNullException("discount"); foreach (var dis1 in discounts) if (discount.Id == dis1.Id) return true; return false; }
public new void SetUp() { _discountRepo = MockRepository.GenerateMock<IRepository<Discount>>(); var discount1 = new Discount { Id = 1, DiscountType = DiscountType.AssignedToCategories, Name = "Discount 1", UsePercentage = true, DiscountPercentage = 10, DiscountAmount =0, DiscountLimitation = DiscountLimitationType.Unlimited, LimitationTimes = 0, }; var discount2 = new Discount { Id = 2, DiscountType = DiscountType.AssignedToSkus, Name = "Discount 2", UsePercentage = false, DiscountPercentage = 0, DiscountAmount = 5, RequiresCouponCode = true, CouponCode = "SecretCode", DiscountLimitation = DiscountLimitationType.NTimesPerCustomer, LimitationTimes = 3, }; _discountRepo.Expect(x => x.Table).Return(new List<Discount> { discount1, discount2 }.AsQueryable()); _eventPublisher = MockRepository.GenerateMock<IEventPublisher>(); _eventPublisher.Expect(x => x.Publish(Arg<object>.Is.Anything)); _storeContext = MockRepository.GenerateMock<IStoreContext>(); var cacheManager = new NopNullCache(); _discountRequirementRepo = MockRepository.GenerateMock<IRepository<DiscountRequirement>>(); _discountUsageHistoryRepo = MockRepository.GenerateMock<IRepository<DiscountUsageHistory>>(); var pluginFinder = new PluginFinder(); _genericAttributeService = MockRepository.GenerateMock<IGenericAttributeService>(); _localizationService = MockRepository.GenerateMock<ILocalizationService>(); _discountService = new DiscountService(cacheManager, _discountRepo, _discountRequirementRepo, _discountUsageHistoryRepo, _storeContext, _genericAttributeService, _localizationService, pluginFinder, _eventPublisher); }
public void Maximum_discount_amount_is_used() { var discount = new Discount { UsePercentage = true, DiscountPercentage = 30, MaximumDiscountAmount = 3.4M }; discount.GetDiscountAmount(100).ShouldEqual(3.4M); discount.DiscountPercentage = 60; discount.GetDiscountAmount(200).ShouldEqual(3.4M); discount.GetDiscountAmount(100).ShouldEqual(3.4M); discount.DiscountPercentage = 1; discount.GetDiscountAmount(200).ShouldEqual(2); }
public void Can_save_and_load_discount_with_appliedCategories() { var discount = new Discount { Name = "Discount 1", DiscountType = DiscountType.AssignedToCategories, UsePercentage = true, DiscountPercentage = 1, DiscountAmount = 2, StartDateUtc = new DateTime(2010, 01, 01), EndDateUtc = new DateTime(2010, 01, 02), RequiresCouponCode = true, CouponCode = "SecretCode", DiscountLimitation = DiscountLimitationType.Unlimited, LimitationTimes = 3 }; discount.AppliedToCategories.Add(GetTestCategory()); var fromDb = SaveAndLoadEntity(discount); fromDb.ShouldNotBeNull(); fromDb.AppliedToCategories.ShouldNotBeNull(); (fromDb.AppliedToCategories.Count == 1).ShouldBeTrue(); fromDb.AppliedToCategories.First().Name.ShouldEqual("Books"); }
/// <summary> /// Check discount requirements /// </summary> /// <param name="discount">Discount</param> /// <param name="customer">Customer</param> /// <param name="couponCodeToValidate">Coupon code to validate</param> /// <returns>true - requirement is met; otherwise, false</returns> public virtual bool IsDiscountValid(Discount discount, Customer customer, string couponCodeToValidate) { if (discount == null) throw new ArgumentNullException("discount"); //check coupon code if (discount.RequiresCouponCode) { if (String.IsNullOrEmpty(discount.CouponCode)) return false; if (!discount.CouponCode.Equals(couponCodeToValidate, StringComparison.InvariantCultureIgnoreCase)) return false; } //check date range DateTime now = DateTime.UtcNow; if (discount.StartDateUtc.HasValue) { DateTime startDate = DateTime.SpecifyKind(discount.StartDateUtc.Value, DateTimeKind.Utc); if (startDate.CompareTo(now) > 0) return false; } if (discount.EndDateUtc.HasValue) { DateTime endDate = DateTime.SpecifyKind(discount.EndDateUtc.Value, DateTimeKind.Utc); if (endDate.CompareTo(now) < 0) return false; } if (!CheckDiscountLimitations(discount, customer)) return false; //discount requirements var requirements = discount.DiscountRequirements; foreach (var req in requirements) { var requirementRule = LoadDiscountRequirementRuleBySystemName(req.DiscountRequirementRuleSystemName); if (requirementRule == null) continue; if (!_pluginFinder.AuthenticateStore(requirementRule.PluginDescriptor, _storeContext.CurrentStore.Id)) continue; var request = new CheckDiscountRequirementRequest() { DiscountRequirement = req, Customer = customer, Store = _storeContext.CurrentStore }; if (!requirementRule.CheckRequirement(request)) return false; } return true; }
/// <summary> /// Check discount requirements /// </summary> /// <param name="discount">Discount</param> /// <param name="customer">Customer</param> /// <returns>true - requirement is met; otherwise, false</returns> public virtual bool IsDiscountValid(Discount discount, Customer customer) { if (discount == null) throw new ArgumentNullException("discount"); var couponCodeToValidate = ""; if (customer != null) couponCodeToValidate = customer.GetAttribute<string>(SystemCustomerAttributeNames.DiscountCouponCode, _genericAttributeService); return IsDiscountValid(discount, customer, couponCodeToValidate); }
protected virtual void PrepareDiscountModel(DiscountModel model, Discount discount) { if (model == null) throw new ArgumentNullException("model"); model.PrimaryStoreCurrencyCode = _currencyService.GetCurrencyById(_currencySettings.PrimaryStoreCurrencyId).CurrencyCode; model.AvailableDiscountRequirementRules.Add(new SelectListItem { Text = _localizationService.GetResource("Admin.Promotions.Discounts.Requirements.DiscountRequirementType.Select"), Value = "" }); var discountRules = _discountService.LoadAllDiscountRequirementRules(); foreach (var discountRule in discountRules) model.AvailableDiscountRequirementRules.Add(new SelectListItem { Text = discountRule.PluginDescriptor.FriendlyName, Value = discountRule.PluginDescriptor.SystemName }); if (discount != null) { //requirements foreach (var dr in discount.DiscountRequirements.OrderBy(dr=>dr.Id)) { var drr = _discountService.LoadDiscountRequirementRuleBySystemName(dr.DiscountRequirementRuleSystemName); if (drr != null) { model.DiscountRequirementMetaInfos.Add(new DiscountModel.DiscountRequirementMetaInfo { DiscountRequirementId = dr.Id, RuleName = drr.PluginDescriptor.FriendlyName, ConfigurationUrl = GetRequirementUrlInternal(drr, discount, dr.Id) }); } } } }
public static Discount ToEntity(this DiscountModel model, Discount destination) { return Mapper.Map(model, destination); }
/// <summary> /// Gets discount amount /// </summary> /// <param name="productVariant">Product variant</param> /// <param name="customer">The customer</param> /// <param name="additionalCharge">Additional charge</param> /// <param name="quantity">Product quantity</param> /// <param name="appliedDiscount">Applied discount</param> /// <returns>Discount amount</returns> public virtual decimal GetDiscountAmount(ProductVariant productVariant, Customer customer, decimal additionalCharge, int quantity, out Discount appliedDiscount) { appliedDiscount = null; decimal appliedDiscountAmount = decimal.Zero; //we don't apply discounts to products with price entered by a customer if (productVariant.CustomerEntersPrice) return appliedDiscountAmount; appliedDiscount = GetPreferredDiscount(productVariant, customer, additionalCharge, quantity); if (appliedDiscount != null) { decimal finalPriceWithoutDiscount = GetFinalPrice(productVariant, customer, additionalCharge, false, quantity); appliedDiscountAmount = appliedDiscount.GetDiscountAmount(finalPriceWithoutDiscount); } return appliedDiscountAmount; }
public void Can_get_shopping_cart_subTotal_discount_including_tax() { //customer Customer customer = new Customer(); //shopping cart var productVariant1 = new ProductVariant { Id = 1, Name = "Product variant name 1", Price = 12.34M, CustomerEntersPrice = false, Published = true, Product = new Product() { Id = 1, Name = "Product name 1", Published = true } }; var sci1 = new ShoppingCartItem() { ProductVariant = productVariant1, ProductVariantId = productVariant1.Id, Quantity = 2, }; var productVariant2 = new ProductVariant { Id = 2, Name = "Product variant name 2", Price = 21.57M, CustomerEntersPrice = false, Published = true, Product = new Product() { Id = 2, Name = "Product name 2", Published = true } }; var sci2 = new ShoppingCartItem() { ProductVariant = productVariant2, ProductVariantId = productVariant2.Id, Quantity = 3 }; var cart = new List<ShoppingCartItem>() { sci1, sci2 }; cart.ForEach(sci => sci.Customer = customer); cart.ForEach(sci => sci.CustomerId = customer.Id); //discounts var discount1 = new Discount() { Id = 1, Name = "Discount 1", DiscountType = DiscountType.AssignedToOrderSubTotal, DiscountAmount = 3, DiscountLimitation = DiscountLimitationType.Unlimited, }; _discountService.Expect(ds => ds.IsDiscountValid(discount1, customer)).Return(true); _discountService.Expect(ds => ds.GetAllDiscounts(DiscountType.AssignedToOrderSubTotal)).Return(new List<Discount>() { discount1 }); _discountService.Expect(ds => ds.GetAllDiscounts(DiscountType.AssignedToCategories)).Return(new List<Discount>()); decimal discountAmount; Discount appliedDiscount; decimal subTotalWithoutDiscount; decimal subTotalWithDiscount; SortedDictionary<decimal, decimal> taxRates; _orderTotalCalcService.GetShoppingCartSubTotal(cart, true, out discountAmount, out appliedDiscount, out subTotalWithoutDiscount, out subTotalWithDiscount, out taxRates); //TODO strange. Why does the commented test fail? discountAmount.ShouldEqual(3.3); //discountAmount.ShouldEqual(3.3); appliedDiscount.ShouldNotBeNull(); appliedDiscount.Name.ShouldEqual("Discount 1"); subTotalWithoutDiscount.ShouldEqual(98.329); subTotalWithDiscount.ShouldEqual(95.029); taxRates.Count.ShouldEqual(1); taxRates.ContainsKey(10).ShouldBeTrue(); taxRates[10].ShouldEqual(8.639); }
/// <summary> /// Gets discount amount /// </summary> /// <param name="productVariant">Product variant</param> /// <param name="customer">The customer</param> /// <param name="additionalCharge">Additional charge</param> /// <param name="appliedDiscount">Applied discount</param> /// <returns>Discount amount</returns> public virtual decimal GetDiscountAmount(ProductVariant productVariant, Customer customer, decimal additionalCharge, out Discount appliedDiscount) { return GetDiscountAmount(productVariant, customer, additionalCharge, 1, out appliedDiscount); }
/// <summary> /// Gets discount amount /// </summary> /// <param name="shoppingCartItem">The shopping cart item</param> /// <param name="appliedDiscount">Applied discount</param> /// <returns>Discount amount</returns> public virtual decimal GetDiscountAmount(ShoppingCartItem shoppingCartItem, out Discount appliedDiscount) { if (shoppingCartItem == null) throw new ArgumentNullException("shoppingCartItem"); var customer = shoppingCartItem.Customer; appliedDiscount = null; decimal totalDiscountAmount = decimal.Zero; var product = shoppingCartItem.Product; if (product != null) { decimal attributesTotalPrice = decimal.Zero; var pvaValues = _productAttributeParser.ParseProductVariantAttributeValues(shoppingCartItem.AttributesXml); foreach (var pvaValue in pvaValues) { attributesTotalPrice += GetProductVariantAttributeValuePriceAdjustment(pvaValue); } decimal productDiscountAmount = GetDiscountAmount(product, customer, attributesTotalPrice, shoppingCartItem.Quantity, out appliedDiscount); totalDiscountAmount = productDiscountAmount * shoppingCartItem.Quantity; } if (_shoppingCartSettings.RoundPricesDuringCalculation) totalDiscountAmount = Math.Round(totalDiscountAmount, 2); return totalDiscountAmount; }
/// <summary> /// Gets an order discount (applied to order total) /// </summary> /// <param name="customer">Customer</param> /// <param name="orderTotal">Order total</param> /// <param name="appliedDiscount">Applied discount</param> /// <returns>Order discount</returns> protected virtual decimal GetOrderTotalDiscount(Customer customer, decimal orderTotal, out Discount appliedDiscount) { appliedDiscount = null; decimal discountAmount = decimal.Zero; if (_catalogSettings.IgnoreDiscounts) return discountAmount; var allDiscounts = _discountService.GetAllDiscounts(DiscountType.AssignedToOrderTotal); var allowedDiscounts = new List<Discount>(); if (allDiscounts != null) foreach (var discount in allDiscounts) if (_discountService.IsDiscountValid(discount, customer) && discount.DiscountType == DiscountType.AssignedToOrderTotal && !allowedDiscounts.ContainsDiscount(discount)) allowedDiscounts.Add(discount); appliedDiscount = allowedDiscounts.GetPreferredDiscount(orderTotal); if (appliedDiscount != null) discountAmount = appliedDiscount.GetDiscountAmount(orderTotal); if (discountAmount < decimal.Zero) discountAmount = decimal.Zero; if (_shoppingCartSettings.RoundPricesDuringCalculation) discountAmount = Math.Round(discountAmount, 2); return discountAmount; }
/// <summary> /// Gets discount amount /// </summary> /// <param name="shoppingCartItem">The shopping cart item</param> /// <param name="appliedDiscount">Applied discount</param> /// <returns>Discount amount</returns> public virtual decimal GetDiscountAmount(ShoppingCartItem shoppingCartItem, out Discount appliedDiscount) { var customer = shoppingCartItem.Customer; appliedDiscount = null; decimal discountAmount = decimal.Zero; var productVariant = shoppingCartItem.ProductVariant; if (productVariant != null) { decimal attributesTotalPrice = decimal.Zero; var pvaValues = _productAttributeParser.ParseProductVariantAttributeValues(shoppingCartItem.AttributesXml); foreach (var pvaValue in pvaValues) { attributesTotalPrice += pvaValue.PriceAdjustment; } decimal productVariantDiscountAmount = GetDiscountAmount(productVariant, customer, attributesTotalPrice, shoppingCartItem.Quantity, out appliedDiscount); discountAmount = productVariantDiscountAmount * shoppingCartItem.Quantity; } if (_shoppingCartSettings.RoundPricesDuringCalculation) discountAmount = Math.Round(discountAmount, 2); return discountAmount; }
/// <summary> /// Gets shopping cart subtotal /// </summary> /// <param name="cart">Cart</param> /// <param name="includingTax">A value indicating whether calculated price should include tax</param> /// <param name="discountAmount">Applied discount amount</param> /// <param name="appliedDiscount">Applied discount</param> /// <param name="subTotalWithoutDiscount">Sub total (without discount)</param> /// <param name="subTotalWithDiscount">Sub total (with discount)</param> public virtual void GetShoppingCartSubTotal(IList<ShoppingCartItem> cart, bool includingTax, out decimal discountAmount, out Discount appliedDiscount, out decimal subTotalWithoutDiscount, out decimal subTotalWithDiscount) { SortedDictionary<decimal, decimal> taxRates = null; GetShoppingCartSubTotal(cart, includingTax, out discountAmount, out appliedDiscount, out subTotalWithoutDiscount, out subTotalWithDiscount, out taxRates); }
public void Can_get_shipping_total_discount_including_tax() { var sci1 = new ShoppingCartItem() { AttributesXml = "", Quantity = 3, ProductVariant = new ProductVariant() { Id = 1, Weight = 1.5M, Height = 2.5M, Length = 3.5M, Width = 4.5M, AdditionalShippingCharge = 5.5M, IsShipEnabled = true, } }; var sci2 = new ShoppingCartItem() { AttributesXml = "", Quantity = 4, ProductVariant = new ProductVariant() { Id = 2, Weight = 11.5M, Height = 12.5M, Length = 13.5M, Width = 14.5M, AdditionalShippingCharge = 6.5M, IsShipEnabled = true, } }; //sci3 is not shippable var sci3 = new ShoppingCartItem() { AttributesXml = "", Quantity = 5, ProductVariant = new ProductVariant() { Id = 3, Weight = 11.5M, Height = 12.5M, Length = 13.5M, Width = 14.5M, AdditionalShippingCharge = 7.5M, IsShipEnabled = false, } }; var cart = new List<ShoppingCartItem>() { sci1, sci2, sci3 }; var customer = new Customer(); cart.ForEach(sci => sci.Customer = customer); cart.ForEach(sci => sci.CustomerId = customer.Id); //discounts var discount1 = new Discount() { Id = 1, Name = "Discount 1", DiscountType = DiscountType.AssignedToShipping, DiscountAmount = 3, DiscountLimitation = DiscountLimitationType.Unlimited, }; _discountService.Expect(ds => ds.IsDiscountValid(discount1, customer)).Return(true); _discountService.Expect(ds => ds.GetAllDiscounts(DiscountType.AssignedToShipping)).Return(new List<Discount>() { discount1 }); decimal taxRate = decimal.Zero; Discount appliedDiscount = null; decimal? shipping = null; shipping = _orderTotalCalcService.GetShoppingCartShippingTotal(cart, true, out taxRate, out appliedDiscount); appliedDiscount.ShouldNotBeNull(); appliedDiscount.Name.ShouldEqual("Discount 1"); shipping.ShouldNotBeNull(); //10 - default fixed shipping rate, 42.5 - additional shipping change, -3 - discount shipping.ShouldEqual(54.45); //10 - default fixed tax rate taxRate.ShouldEqual(10); }
/// <summary> /// Gets shopping cart subtotal /// </summary> /// <param name="cart">Cart</param> /// <param name="includingTax">A value indicating whether calculated price should include tax</param> /// <param name="discountAmount">Applied discount amount</param> /// <param name="appliedDiscount">Applied discount</param> /// <param name="subTotalWithoutDiscount">Sub total (without discount)</param> /// <param name="subTotalWithDiscount">Sub total (with discount)</param> /// <param name="taxRates">Tax rates (of order sub total)</param> public virtual void GetShoppingCartSubTotal(IList<ShoppingCartItem> cart, bool includingTax, out decimal discountAmount, out Discount appliedDiscount, out decimal subTotalWithoutDiscount, out decimal subTotalWithDiscount, out SortedDictionary<decimal, decimal> taxRates) { discountAmount = decimal.Zero; appliedDiscount = null; subTotalWithoutDiscount = decimal.Zero; subTotalWithDiscount = decimal.Zero; taxRates = new SortedDictionary<decimal, decimal>(); if (cart.Count == 0) return; //get the customer Customer customer = cart.GetCustomer(); //sub totals decimal subTotalExclTaxWithoutDiscount = decimal.Zero; decimal subTotalInclTaxWithoutDiscount = decimal.Zero; foreach (var shoppingCartItem in cart) { decimal taxRate = decimal.Zero; decimal sciSubTotal = _priceCalculationService.GetSubTotal(shoppingCartItem, true); decimal sciExclTax = _taxService.GetProductPrice(shoppingCartItem.Product, sciSubTotal, false, customer, out taxRate); decimal sciInclTax = _taxService.GetProductPrice(shoppingCartItem.Product, sciSubTotal, true, customer, out taxRate); subTotalExclTaxWithoutDiscount += sciExclTax; subTotalInclTaxWithoutDiscount += sciInclTax; //tax rates decimal sciTax = sciInclTax - sciExclTax; if (taxRate > decimal.Zero && sciTax > decimal.Zero) { if (!taxRates.ContainsKey(taxRate)) { taxRates.Add(taxRate, sciTax); } else { taxRates[taxRate] = taxRates[taxRate] + sciTax; } } } //checkout attributes if (customer != null) { var checkoutAttributesXml = customer.GetAttribute<string>(SystemCustomerAttributeNames.CheckoutAttributes, _genericAttributeService, _storeContext.CurrentStore.Id); var caValues = _checkoutAttributeParser.ParseCheckoutAttributeValues(checkoutAttributesXml); if (caValues!=null) { foreach (var caValue in caValues) { decimal taxRate = decimal.Zero; decimal caExclTax = _taxService.GetCheckoutAttributePrice(caValue, false, customer, out taxRate); decimal caInclTax = _taxService.GetCheckoutAttributePrice(caValue, true, customer, out taxRate); subTotalExclTaxWithoutDiscount += caExclTax; subTotalInclTaxWithoutDiscount += caInclTax; //tax rates decimal caTax = caInclTax - caExclTax; if (taxRate > decimal.Zero && caTax > decimal.Zero) { if (!taxRates.ContainsKey(taxRate)) { taxRates.Add(taxRate, caTax); } else { taxRates[taxRate] = taxRates[taxRate] + caTax; } } } } } //subtotal without discount if (includingTax) subTotalWithoutDiscount = subTotalInclTaxWithoutDiscount; else subTotalWithoutDiscount = subTotalExclTaxWithoutDiscount; if (subTotalWithoutDiscount < decimal.Zero) subTotalWithoutDiscount = decimal.Zero; if (_shoppingCartSettings.RoundPricesDuringCalculation) subTotalWithoutDiscount = Math.Round(subTotalWithoutDiscount, 2); /*We calculate discount amount on order subtotal excl tax (discount first)*/ //calculate discount amount ('Applied to order subtotal' discount) decimal discountAmountExclTax = GetOrderSubtotalDiscount(customer, subTotalExclTaxWithoutDiscount, out appliedDiscount); if (subTotalExclTaxWithoutDiscount < discountAmountExclTax) discountAmountExclTax = subTotalExclTaxWithoutDiscount; decimal discountAmountInclTax = discountAmountExclTax; //subtotal with discount (excl tax) decimal subTotalExclTaxWithDiscount = subTotalExclTaxWithoutDiscount - discountAmountExclTax; decimal subTotalInclTaxWithDiscount = subTotalExclTaxWithDiscount; //add tax for shopping items & checkout attributes Dictionary<decimal, decimal> tempTaxRates = new Dictionary<decimal, decimal>(taxRates); foreach (KeyValuePair<decimal, decimal> kvp in tempTaxRates) { decimal taxRate = kvp.Key; decimal taxValue = kvp.Value; if (taxValue != decimal.Zero) { //discount the tax amount that applies to subtotal items if (subTotalExclTaxWithoutDiscount > decimal.Zero) { decimal discountTax = taxRates[taxRate] * (discountAmountExclTax / subTotalExclTaxWithoutDiscount); discountAmountInclTax += discountTax; taxValue = taxRates[taxRate] - discountTax; if (_shoppingCartSettings.RoundPricesDuringCalculation) taxValue = Math.Round(taxValue, 2); taxRates[taxRate] = taxValue; } //subtotal with discount (incl tax) subTotalInclTaxWithDiscount += taxValue; } } if (_shoppingCartSettings.RoundPricesDuringCalculation) { discountAmountInclTax = Math.Round(discountAmountInclTax, 2); discountAmountExclTax = Math.Round(discountAmountExclTax, 2); } if (includingTax) { subTotalWithDiscount = subTotalInclTaxWithDiscount; discountAmount = discountAmountInclTax; } else { subTotalWithDiscount = subTotalExclTaxWithDiscount; discountAmount = discountAmountExclTax; } if (subTotalWithDiscount < decimal.Zero) subTotalWithDiscount = decimal.Zero; if (_shoppingCartSettings.RoundPricesDuringCalculation) subTotalWithDiscount = Math.Round(subTotalWithDiscount, 2); }
public void Can_get_shopping_cart_total_discount() { //customer var customer = new Customer() { Id = 10, }; //shopping cart var productVariant1 = new ProductVariant { Id = 1, Name = "Product variant name 1", Price = 10M, Published = true, IsShipEnabled = true, Product = new Product() { Id = 1, Name = "Product name 1", Published = true } }; var sci1 = new ShoppingCartItem() { ProductVariant = productVariant1, ProductVariantId = productVariant1.Id, Quantity = 2, }; var productVariant2 = new ProductVariant { Id = 2, Name = "Product variant name 2", Price = 12M, Published = true, IsShipEnabled = true, Product = new Product() { Id = 2, Name = "Product name 2", Published = true } }; var sci2 = new ShoppingCartItem() { ProductVariant = productVariant2, ProductVariantId = productVariant2.Id, Quantity = 3 }; var cart = new List<ShoppingCartItem>() { sci1, sci2 }; cart.ForEach(sci => sci.Customer = customer); cart.ForEach(sci => sci.CustomerId = customer.Id); //discounts var discount1 = new Discount() { Id = 1, Name = "Discount 1", DiscountType = DiscountType.AssignedToOrderTotal, DiscountAmount = 3, DiscountLimitation = DiscountLimitationType.Unlimited, }; _discountService.Expect(ds => ds.IsDiscountValid(discount1, customer)).Return(true); _discountService.Expect(ds => ds.GetAllDiscounts(DiscountType.AssignedToOrderTotal)).Return(new List<Discount>() { discount1 }); _discountService.Expect(ds => ds.GetAllDiscounts(DiscountType.AssignedToCategories)).Return(new List<Discount>()); _genericAttributeService.Expect(x => x.GetAttributesForEntity(customer.Id, "Customer")) .Return(new List<GenericAttribute>() { new GenericAttribute() { StoreId = _store.Id, EntityId = customer.Id, Key = SystemCustomerAttributeNames.SelectedPaymentMethod, KeyGroup = "Customer", Value = "test1" } }); _paymentService.Expect(ps => ps.GetAdditionalHandlingFee(cart, "test1")).Return(20); decimal discountAmount; Discount appliedDiscount; List<AppliedGiftCard> appliedGiftCards; int redeemedRewardPoints; decimal redeemedRewardPointsAmount; //shipping is taxable, payment fee is taxable _taxSettings.ShippingIsTaxable = true; _taxSettings.PaymentMethodAdditionalFeeIsTaxable = true; //56 - items, 10 - shipping (fixed), 20 - payment fee, 8.6 - tax, [-3] - discount _orderTotalCalcService.GetShoppingCartTotal(cart, out discountAmount, out appliedDiscount, out appliedGiftCards, out redeemedRewardPoints, out redeemedRewardPointsAmount) .ShouldEqual(91.6M); discountAmount.ShouldEqual(3); appliedDiscount.ShouldNotBeNull(); appliedDiscount.Name.ShouldEqual("Discount 1"); }
/// <summary> /// Adjust shipping rate (free shipping, additional charges, discounts) /// </summary> /// <param name="shippingRate">Shipping rate to adjust</param> /// <param name="cart">Cart</param> /// <param name="appliedDiscount">Applied discount</param> /// <returns>Adjusted shipping rate</returns> public virtual decimal AdjustShippingRate(decimal shippingRate, IList<ShoppingCartItem> cart, out Discount appliedDiscount) { appliedDiscount = null; //free shipping if (IsFreeShipping(cart)) return decimal.Zero; //additional shipping charges decimal additionalShippingCharge = GetShoppingCartAdditionalShippingCharge(cart); var adjustedRate = shippingRate + additionalShippingCharge; //discount var customer = cart.GetCustomer(); decimal discountAmount = GetShippingDiscount(customer, adjustedRate, out appliedDiscount); adjustedRate = adjustedRate - discountAmount; if (adjustedRate < decimal.Zero) adjustedRate = decimal.Zero; if (_shoppingCartSettings.RoundPricesDuringCalculation) adjustedRate = Math.Round(adjustedRate, 2); return adjustedRate; }