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", SpentAmount = 1, BillingCountryId = 2, ShippingCountryId = 3, } ); 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_save_and_load_discount() { var discount = new Discount { DiscountType = DiscountType.AssignedToCategories, Name = "Discount 1", UsePercentage = true, DiscountPercentage = 1.1M, DiscountAmount = 2.1M, StartDateUtc = new DateTime(2010, 01, 01), EndDateUtc = new DateTime(2010, 01, 02), RequiresCouponCode = true, CouponCode = "SecretCode", DiscountLimitation = DiscountLimitationType.Unlimited, LimitationTimes = 3, }; 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.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); }
/// <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_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); }
public 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; }
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>(); _storeContext.Expect(x => x.CurrentStore).Return(new Store { Id = 1, Name = "MyStore" }); _settingService = MockRepository.GenerateMock<ISettingService>(); var cacheManager = new NullCache(); _discountRequirementRepo = MockRepository.GenerateMock<IRepository<DiscountRequirement>>(); _discountUsageHistoryRepo = MockRepository.GenerateMock<IRepository<DiscountUsageHistory>>(); var pluginFinder = new PluginFinder(); _genericAttributeService = MockRepository.GenerateMock<IGenericAttributeService>(); _discountService = new DiscountService(cacheManager, _discountRepo, _discountRequirementRepo, _discountUsageHistoryRepo, _storeContext, _genericAttributeService, pluginFinder, _eventPublisher, _settingService, base.ProviderManager); }
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 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"); }
public void Can_get_shopping_cart_subTotal_discount_including_tax() { //customer Customer customer = new Customer(); //shopping cart var product1 = new Product { Id = 1, Name = "Product name 1", Price = 12.34M, CustomerEntersPrice = false, Published = true, }; var sci1 = new ShoppingCartItem() { Product = product1, ProductId = product1.Id, Quantity = 2, }; var product2 = new Product { Id = 2, Name = "Product name 2", Price = 21.57M, CustomerEntersPrice = false, Published = true, }; var sci2 = new ShoppingCartItem() { Product = product2, ProductId = product2.Id, Quantity = 3 }; var cart = new List<OrganizedShoppingCartItem>(); cart.Add(new OrganizedShoppingCartItem(sci1)); cart.Add(new OrganizedShoppingCartItem(sci2)); cart.ForEach(sci => sci.Item.Customer = customer); cart.ForEach(sci => sci.Item.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 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<OrganizedShoppingCartItem> 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)) { var provider = _providerManager.GetProvider<IPaymentMethod>(paymentMethodSystemName); decimal paymentMethodAdditionalFee = provider.GetAdditionalHandlingFee(cart, _shoppingCartSettings.RoundPricesDuringCalculation); 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; ////// (VATFIX) ////resultTemp += shoppingCartTax; //if (_taxService.IsVatExempt(null, customer)) //{ // // add nothing to total //} //else //{ 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 != null && customer.GetAttribute<bool>(SystemCustomerAttributeNames.UseRewardPointsDuringCheckout, _genericAttributeService, _storeContext.CurrentStore.Id)) { int rewardPointsBalance = customer.GetRewardPointsBalance(); 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; } return null; }
/// <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<OrganizedShoppingCartItem> 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) { if (shoppingCartItem.Item.Product == null) continue; decimal taxRate, sciSubTotal, sciExclTax, sciInclTax = decimal.Zero; shoppingCartItem.Item.Product.MergeWithCombination(shoppingCartItem.Item.AttributesXml, _productAttributeParser); if (_shoppingCartSettings.RoundPricesDuringCalculation) { // Gross > Net RoundFix int qty = shoppingCartItem.Item.Quantity; sciSubTotal = _priceCalculationService.GetUnitPrice(shoppingCartItem, true); // Adaption to eliminate rounding issues sciExclTax = _taxService.GetProductPrice(shoppingCartItem.Item.Product, sciSubTotal, false, customer, out taxRate); sciExclTax = Math.Round(sciExclTax, 2) * qty; sciInclTax = _taxService.GetProductPrice(shoppingCartItem.Item.Product, sciSubTotal, true, customer, out taxRate); sciInclTax = Math.Round(sciInclTax, 2) * qty; } else { sciSubTotal = _priceCalculationService.GetSubTotal(shoppingCartItem, true); sciExclTax = _taxService.GetProductPrice(shoppingCartItem.Item.Product, sciSubTotal, false, customer, out taxRate); sciInclTax = _taxService.GetProductPrice(shoppingCartItem.Item.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); 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); if (includingTax) { subTotalWithDiscount = subTotalInclTaxWithDiscount; discountAmount = discountAmountInclTax; } else { subTotalWithDiscount = subTotalExclTaxWithDiscount; discountAmount = discountAmountExclTax; } //round if (subTotalWithDiscount < decimal.Zero) subTotalWithDiscount = decimal.Zero; if (_shoppingCartSettings.RoundPricesDuringCalculation) subTotalWithDiscount = Math.Round(subTotalWithDiscount, 2); }
/// <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<OrganizedShoppingCartItem> 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 Ensure_discount_is_not_applied_to_products_with_prices_entered_by_customer() { var product = new Product { Id = 1, Name = "Product name 1", Price = 12.34M, CustomerEntersPrice = true, Published = true, }; //customer Customer customer = null; //discounts var discount1 = new Discount() { Id = 1, Name = "Discount 1", DiscountType = DiscountType.AssignedToSkus, DiscountAmount = 3, DiscountLimitation = DiscountLimitationType.Unlimited }; discount1.AppliedToProducts.Add(product); product.AppliedDiscounts.Add(discount1); _discountService.Expect(ds => ds.IsDiscountValid(discount1, customer)).Return(true); Discount appliedDiscount; _priceCalcService.GetDiscountAmount(product, customer, 0, 1, out appliedDiscount).ShouldEqual(0); appliedDiscount.ShouldBeNull(); }
/// <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(OrganizedShoppingCartItem shoppingCartItem, out Discount appliedDiscount) { var customer = shoppingCartItem.Item.Customer; appliedDiscount = null; decimal totalDiscountAmount = decimal.Zero; var product = shoppingCartItem.Item.Product; if (product != null) { decimal attributesTotalPrice = decimal.Zero; var pvaValues = _productAttributeParser.ParseProductVariantAttributeValues(shoppingCartItem.Item.AttributesXml); foreach (var pvaValue in pvaValues) { attributesTotalPrice += GetProductVariantAttributeValuePriceAdjustment(pvaValue); } decimal productDiscountAmount = GetDiscountAmount(product, customer, attributesTotalPrice, shoppingCartItem.Item.Quantity, out appliedDiscount); totalDiscountAmount = productDiscountAmount * shoppingCartItem.Item.Quantity; } if (_shoppingCartSettings.RoundPricesDuringCalculation) totalDiscountAmount = Math.Round(totalDiscountAmount, 2); return totalDiscountAmount; }
/// <summary> /// Gets discount amount /// </summary> /// <param name="product">Product</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(Product product, Customer customer, decimal additionalCharge, out Discount appliedDiscount) { return GetDiscountAmount(product, customer, additionalCharge, 1, out appliedDiscount); }
public void Can_get_shipping_total_discount_excluding_tax() { var sci1 = new ShoppingCartItem() { AttributesXml = "", Quantity = 3, Product = new Product() { 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, Product = new Product() { 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, Product = new Product() { Id = 3, Weight = 11.5M, Height = 12.5M, Length = 13.5M, Width = 14.5M, AdditionalShippingCharge = 7.5M, IsShipEnabled = false, } }; var cart = new List<OrganizedShoppingCartItem>(); cart.Add(new OrganizedShoppingCartItem(sci1)); cart.Add(new OrganizedShoppingCartItem(sci2)); cart.Add(new OrganizedShoppingCartItem(sci3)); var customer = new Customer(); cart.ForEach(sci => sci.Item.Customer = customer); cart.ForEach(sci => sci.Item.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, false, 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(49.5); //10 - default fixed tax rate taxRate.ShouldEqual(10); }
public static Discount ToEntity(this DiscountModel model, Discount destination) { return Mapper.Map(model, destination); }
public void Can_get_final_product_price_with_discount() { var product = new Product { Id = 1, Name = "Product name 1", Price = 12.34M, CustomerEntersPrice = false, Published = true, }; //customer Customer customer = null; //discounts var discount1 = new Discount() { Id = 1, Name = "Discount 1", DiscountType = DiscountType.AssignedToSkus, DiscountAmount = 3, DiscountLimitation = DiscountLimitationType.Unlimited }; discount1.AppliedToProducts.Add(product); product.AppliedDiscounts.Add(discount1); //set HasDiscountsApplied property product.HasDiscountsApplied = true; _discountService.Expect(ds => ds.IsDiscountValid(discount1, customer)).Return(true); _discountService.Expect(ds => ds.GetAllDiscounts(DiscountType.AssignedToCategories)).Return(new List<Discount>()); _priceCalcService.GetFinalPrice(product, customer, 0, true, 1).ShouldEqual(9.34M); }
public void Can_get_shopping_cart_total_discount() { //customer var customer = new Customer() { Id = 10, }; //shopping cart var product1 = new Product { Id = 1, Name = "Product name 1", Price = 10M, Published = true, IsShipEnabled = true, }; var sci1 = new ShoppingCartItem() { Product = product1, ProductId = product1.Id, Quantity = 2, }; var product2 = new Product { Id = 2, Name = "Product name 2", Price = 12M, Published = true, IsShipEnabled = true, }; var sci2 = new ShoppingCartItem() { Product = product2, ProductId = product2.Id, Quantity = 3 }; var cart = new List<OrganizedShoppingCartItem>(); cart.Add(new OrganizedShoppingCartItem(sci1)); cart.Add(new OrganizedShoppingCartItem(sci2)); cart.ForEach(sci => sci.Item.Customer = customer); cart.ForEach(sci => sci.Item.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<OrganizedShoppingCartItem> cart, string shippingMethodName, IList<ShippingMethod> shippingMethods, out Discount appliedDiscount) { appliedDiscount = null; //free shipping if (IsFreeShipping(cart)) return decimal.Zero; decimal adjustedRate = decimal.Zero; decimal bundlePerItemShipping = decimal.Zero; bool ignoreAdditionalShippingCharge = false; ShippingMethod shippingMethod; foreach (var sci in cart) { if (sci.Item.Product != null && sci.Item.Product.ProductType == ProductType.BundledProduct && sci.Item.Product.BundlePerItemShipping) { if (sci.ChildItems != null) { foreach (var childItem in sci.ChildItems.Where(x => x.Item.IsShipEnabled && !x.Item.IsFreeShipping)) bundlePerItemShipping += shippingRate; } } else if (adjustedRate == decimal.Zero) { adjustedRate = shippingRate; } } adjustedRate += bundlePerItemShipping; if (shippingMethodName.HasValue() && shippingMethods != null && (shippingMethod = shippingMethods.FirstOrDefault(x => x.Name.IsCaseInsensitiveEqual(shippingMethodName))) != null) { ignoreAdditionalShippingCharge = shippingMethod.IgnoreCharges; } //additional shipping charges if (!ignoreAdditionalShippingCharge) { decimal additionalShippingCharge = GetShoppingCartAdditionalShippingCharge(cart); adjustedRate += 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; }
/// <summary> /// Gets discount amount /// </summary> /// <param name="product">Product</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> /// <param name="bundleItem">A product bundle item</param> /// <returns>Discount amount</returns> public virtual decimal GetDiscountAmount(Product product, Customer customer, decimal additionalCharge, int quantity, out Discount appliedDiscount, ProductBundleItemData bundleItem = null) { appliedDiscount = null; decimal appliedDiscountAmount = decimal.Zero; if (bundleItem.IsValid()) { if (bundleItem.Item.Discount.HasValue && bundleItem.Item.BundleProduct.BundlePerItemPricing) { appliedDiscount = new Discount() { UsePercentage = bundleItem.Item.DiscountPercentage, DiscountPercentage = bundleItem.Item.Discount.Value, DiscountAmount = bundleItem.Item.Discount.Value }; } } else { //we don't apply discounts to products with price entered by a customer if (product.CustomerEntersPrice) return appliedDiscountAmount; appliedDiscount = GetPreferredDiscount(product, customer, additionalCharge, quantity); } if (appliedDiscount != null) { decimal finalPriceWithoutDiscount = GetFinalPrice(product, customer, additionalCharge, false, quantity, bundleItem); appliedDiscountAmount = appliedDiscount.GetDiscountAmount(finalPriceWithoutDiscount); } return appliedDiscountAmount; }
/// <summary> /// Gets an order discount (applied to order subtotal) /// </summary> /// <param name="customer">Customer</param> /// <param name="orderSubTotal">Order subtotal</param> /// <param name="appliedDiscount">Applied discount</param> /// <returns>Order discount</returns> public virtual decimal GetOrderSubtotalDiscount(Customer customer, decimal orderSubTotal, out Discount appliedDiscount) { appliedDiscount = null; decimal discountAmount = decimal.Zero; if (_catalogSettings.IgnoreDiscounts) return discountAmount; var allDiscounts = _discountService.GetAllDiscounts(DiscountType.AssignedToOrderSubTotal); var allowedDiscounts = new List<Discount>(); if (allDiscounts != null) { foreach (var discount in allDiscounts) { if (discount.DiscountType == DiscountType.AssignedToOrderSubTotal && !allowedDiscounts.Any(x => x.Id == discount.Id) && _discountService.IsDiscountValid(discount, customer)) { allowedDiscounts.Add(discount); } } } appliedDiscount = allowedDiscounts.GetPreferredDiscount(orderSubTotal); if (appliedDiscount != null) discountAmount = appliedDiscount.GetDiscountAmount(orderSubTotal); if (discountAmount < decimal.Zero) discountAmount = decimal.Zero; return discountAmount; }
public virtual decimal GetDiscountAmount( Product product, Customer customer, decimal additionalCharge, int quantity, out Discount appliedDiscount, ProductBundleItemData bundleItem = null, PriceCalculationContext context = null) { appliedDiscount = null; decimal appliedDiscountAmount = decimal.Zero; decimal finalPriceWithoutDiscount = decimal.Zero; if (bundleItem.IsValid()) { if (bundleItem.Item.Discount.HasValue && bundleItem.Item.BundleProduct.BundlePerItemPricing) { appliedDiscount = new Discount { UsePercentage = bundleItem.Item.DiscountPercentage, DiscountPercentage = bundleItem.Item.Discount.Value, DiscountAmount = bundleItem.Item.Discount.Value }; finalPriceWithoutDiscount = GetFinalPrice(product, customer, additionalCharge, false, quantity, bundleItem, context); appliedDiscountAmount = appliedDiscount.GetDiscountAmount(finalPriceWithoutDiscount); } } else { // dont't apply when customer entered price or discounts should be ignored completely if (product.CustomerEntersPrice || _catalogSettings.IgnoreDiscounts) { return appliedDiscountAmount; } var allowedDiscounts = GetAllowedDiscounts(product, customer, context); if (allowedDiscounts.Count == 0) { return appliedDiscountAmount; } finalPriceWithoutDiscount = GetFinalPrice(product, customer, additionalCharge, false, quantity, bundleItem, context); appliedDiscount = allowedDiscounts.GetPreferredDiscount(finalPriceWithoutDiscount); if (appliedDiscount != null) { appliedDiscountAmount = appliedDiscount.GetDiscountAmount(finalPriceWithoutDiscount); } } return appliedDiscountAmount; }
/// <summary> /// Gets a shipping discount /// </summary> /// <param name="customer">Customer</param> /// <param name="shippingTotal">Shipping total</param> /// <param name="appliedDiscount">Applied discount</param> /// <returns>Shipping discount</returns> public virtual decimal GetShippingDiscount(Customer customer, decimal shippingTotal, out Discount appliedDiscount) { appliedDiscount = null; decimal shippingDiscountAmount = decimal.Zero; if (_catalogSettings.IgnoreDiscounts) return shippingDiscountAmount; var allDiscounts = _discountService.GetAllDiscounts(DiscountType.AssignedToShipping); var allowedDiscounts = new List<Discount>(); if (allDiscounts != null) { foreach (var discount in allDiscounts) { if (discount.DiscountType == DiscountType.AssignedToShipping && !allowedDiscounts.Any(x => x.Id == discount.Id) && _discountService.IsDiscountValid(discount, customer)) { allowedDiscounts.Add(discount); } } } appliedDiscount = allowedDiscounts.GetPreferredDiscount(shippingTotal); if (appliedDiscount != null) { shippingDiscountAmount = appliedDiscount.GetDiscountAmount(shippingTotal); } if (shippingDiscountAmount < decimal.Zero) shippingDiscountAmount = decimal.Zero; if (_shoppingCartSettings.RoundPricesDuringCalculation) shippingDiscountAmount = Math.Round(shippingDiscountAmount, 2); return shippingDiscountAmount; }
public void Should_not_accept_wrong_discount_code() { var discount = new Discount { DiscountType = DiscountType.AssignedToSkus, Name = "Discount 2", UsePercentage = false, DiscountPercentage = 0, DiscountAmount = 5, RequiresCouponCode = true, CouponCode = "CouponCode 1", 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) }; _genericAttributeService.Expect(x => x.GetAttributesForEntity(customer.Id, "Customer")) .Return(new List<GenericAttribute>() { new GenericAttribute() { EntityId = customer.Id, Key = SystemCustomerAttributeNames.DiscountCouponCode, KeyGroup = "Customer", Value = "CouponCode 2" } }); var result2 = _discountService.IsDiscountValid(discount, customer); result2.ShouldEqual(false); }
/// <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<OrganizedShoppingCartItem> 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 var shippingMethods = _shippingService.GetAllShippingMethods(); shippingTotal = AdjustShippingRate(shippingOption.Rate, cart, shippingOption.Name, shippingMethods, 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.Any()) throw new SmartException("Shipping rate computation method could not be loaded"); if (shippingRateComputationMethods.Count() == 1) { var getShippingOptionRequest = _shippingService.CreateShippingOptionRequest(cart, shippingAddress); var shippingRateComputationMethod = shippingRateComputationMethods.First(); decimal? fixedRate = shippingRateComputationMethod.Value.GetFixedRate(getShippingOptionRequest); if (fixedRate.HasValue) { //adjust shipping rate shippingTotal = AdjustShippingRate(fixedRate.Value, cart, null, null, 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 IList<Discount> Discounts() { var sampleDiscountWithCouponCode = new Discount() { Name = "Sample discount with coupon code", DiscountType = DiscountType.AssignedToSkus, DiscountLimitation = DiscountLimitationType.Unlimited, UsePercentage = false, DiscountAmount = 10, RequiresCouponCode = true, CouponCode = "123", }; var sampleDiscounTwentyPercentTotal = new Discount() { Name = "'20% order total' discount", DiscountType = DiscountType.AssignedToOrderTotal, DiscountLimitation = DiscountLimitationType.Unlimited, UsePercentage = true, DiscountPercentage = 20, StartDateUtc = new DateTime(2013, 1, 1), EndDateUtc = new DateTime(2020, 1, 1), RequiresCouponCode = true, CouponCode = "456", }; var entities = new List<Discount> { sampleDiscountWithCouponCode, sampleDiscounTwentyPercentTotal }; this.Alter(entities); return entities; }
/// <summary> /// Gets shopping cart subtotal /// </summary> /// <param name="cart">Cart</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<OrganizedShoppingCartItem> cart, out decimal discountAmount, out Discount appliedDiscount, out decimal subTotalWithoutDiscount, out decimal subTotalWithDiscount) { bool includingTax = false; switch (_workContext.TaxDisplayType) { case TaxDisplayType.ExcludingTax: includingTax = false; break; case TaxDisplayType.IncludingTax: includingTax = true; break; } GetShoppingCartSubTotal(cart, includingTax, out discountAmount, out appliedDiscount, out subTotalWithoutDiscount, out subTotalWithDiscount); }