/// <summary> /// Gets discount amount /// </summary> /// <param name="productVariant">Product variant</param> /// <param name="customer">The customer</param> /// <param name="additionalCharge">Additional charge</param> /// <returns>Discount amount</returns> public virtual decimal GetDiscountAmount(ProductVariant productVariant, Customer customer, decimal additionalCharge) { Discount appliedDiscount = null; return GetDiscountAmount(productVariant, customer, additionalCharge, out appliedDiscount); }
public void Can_check_taxExempt_productVariant() { var productVariant = new ProductVariant(); productVariant.IsTaxExempt = true; _taxService.IsTaxExempt(productVariant, null).ShouldEqual(true); productVariant.IsTaxExempt = false; _taxService.IsTaxExempt(productVariant, null).ShouldEqual(false); }
public void Can_parse_required_productvariant_ids() { var productVariant = new ProductVariant { RequiredProductVariantIds = "1, 4,7 ,a," }; var ids = productVariant.ParseRequiredProductVariantIds(); ids.Length.ShouldEqual(3); ids[0].ShouldEqual(1); ids[1].ShouldEqual(4); ids[2].ShouldEqual(7); }
public void Can_parse_allowed_quantities() { var pv = new ProductVariant() { AllowedQuantities = "1, 5,4,10,sdf" }; var result = pv.ParseAllowedQuatities(); result.Length.ShouldEqual(4); result[0].ShouldEqual(1); result[1].ShouldEqual(5); result[2].ShouldEqual(4); result[3].ShouldEqual(10); }
public void Can_get_final_product_price_with_additionalFee() { var productVariant = 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 } }; //customer Customer customer = null; _priceCalcService.GetFinalPrice(productVariant, customer, 5, false, 1).ShouldEqual(17.34M); }
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); }
public void Can_get_tax_total() { //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); _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); _discountService.Expect(ds => ds.GetAllDiscounts(DiscountType.AssignedToCategories)).Return(new List<Discount>()); //56 - items, 10 - shipping (fixed), 20 - payment fee //1. shipping is taxable, payment fee is taxable _taxSettings.ShippingIsTaxable = true; _taxSettings.PaymentMethodAdditionalFeeIsTaxable = true; SortedDictionary<decimal, decimal> taxRates; _orderTotalCalcService.GetTaxTotal(cart, out taxRates).ShouldEqual(8.6); taxRates.ShouldNotBeNull(); taxRates.Count.ShouldEqual(1); taxRates.ContainsKey(10).ShouldBeTrue(); taxRates[10].ShouldEqual(8.6); //2. shipping is taxable, payment fee is not taxable _taxSettings.ShippingIsTaxable = true; _taxSettings.PaymentMethodAdditionalFeeIsTaxable = false; _orderTotalCalcService.GetTaxTotal(cart, out taxRates).ShouldEqual(6.6); taxRates.ShouldNotBeNull(); taxRates.Count.ShouldEqual(1); taxRates.ContainsKey(10).ShouldBeTrue(); taxRates[10].ShouldEqual(6.6); //3. shipping is not taxable, payment fee is taxable _taxSettings.ShippingIsTaxable = false; _taxSettings.PaymentMethodAdditionalFeeIsTaxable = true; _orderTotalCalcService.GetTaxTotal(cart, out taxRates).ShouldEqual(7.6); taxRates.ShouldNotBeNull(); taxRates.Count.ShouldEqual(1); taxRates.ContainsKey(10).ShouldBeTrue(); taxRates[10].ShouldEqual(7.6); //3. shipping is not taxable, payment fee is not taxable _taxSettings.ShippingIsTaxable = false; _taxSettings.PaymentMethodAdditionalFeeIsTaxable = false; _orderTotalCalcService.GetTaxTotal(cart, out taxRates).ShouldEqual(5.6); taxRates.ShouldNotBeNull(); taxRates.Count.ShouldEqual(1); taxRates.ContainsKey(10).ShouldBeTrue(); taxRates[10].ShouldEqual(5.6); }
/// <summary> /// Sends a "quantity below" notification to a store owner /// </summary> /// <param name="productVariant">Product variant</param> /// <param name="languageId">Message language identifier</param> /// <returns>Queued email identifier</returns> public virtual int SendQuantityBelowStoreOwnerNotification(ProductVariant productVariant, int languageId) { if (productVariant == null) throw new ArgumentNullException("productVariant"); languageId = EnsureLanguageIsActive(languageId); var messageTemplate = GetLocalizedActiveMessageTemplate("QuantityBelow.StoreOwnerNotification", languageId); if (messageTemplate == null) return 0; var productVariantTokens = GenerateTokens(productVariant); //event notification _eventPublisher.MessageTokensAdded(messageTemplate, productVariantTokens); var emailAccount = GetEmailAccountOfMessageTemplate(messageTemplate, languageId); var toEmail = emailAccount.Email; var toName = emailAccount.DisplayName; return SendNotification(messageTemplate, emailAccount, languageId, productVariantTokens, toEmail, toName); }
/// <summary> /// Gets the final price /// </summary> /// <param name="productVariant">Product variant</param> /// <param name="includeDiscounts">A value indicating whether include discounts or not for final price computation</param> /// <returns>Final price</returns> public virtual decimal GetFinalPrice(ProductVariant productVariant, bool includeDiscounts) { var customer = _workContext.CurrentCustomer; return GetFinalPrice(productVariant, customer, includeDiscounts); }
/// <summary> /// Validates shopping cart item attributes /// </summary> /// <param name="shoppingCartType">Shopping cart type</param> /// <param name="productVariant">Product variant</param> /// <param name="selectedAttributes">Selected attributes</param> /// <returns>Warnings</returns> public virtual IList<string> GetShoppingCartItemAttributeWarnings(ShoppingCartType shoppingCartType, ProductVariant productVariant, string selectedAttributes) { if (productVariant == null) throw new ArgumentNullException("productVariant"); var warnings = new List<string>(); //selected attributes var pva1Collection = _productAttributeParser.ParseProductVariantAttributes(selectedAttributes); foreach (var pva1 in pva1Collection) { var pv1 = pva1.ProductVariant; if (pv1 != null) { if (pv1.Id != productVariant.Id) { warnings.Add("Attribute error"); } } else { warnings.Add("Attribute error"); return warnings; } } //existing product attributes var pva2Collection = productVariant.ProductVariantAttributes; foreach (var pva2 in pva2Collection) { if (pva2.IsRequired) { bool found = false; //selected product attributes foreach (var pva1 in pva1Collection) { if (pva1.Id == pva2.Id) { var pvaValuesStr = _productAttributeParser.ParseValues(selectedAttributes, pva1.Id); foreach (string str1 in pvaValuesStr) { if (!String.IsNullOrEmpty(str1.Trim())) { found = true; break; } } } } //if not found if (!found) { if (!string.IsNullOrEmpty(pva2.TextPrompt)) { warnings.Add(pva2.TextPrompt); } else { warnings.Add(string.Format(_localizationService.GetResource("ShoppingCart.SelectAttribute"), pva2.ProductAttribute.GetLocalized(a => a.Name))); } } } } return warnings; }
/// <summary> /// Finds a shopping cart item in the cart /// </summary> /// <param name="shoppingCart">Shopping cart</param> /// <param name="shoppingCartType">Shopping cart type</param> /// <param name="productVariant">Product variant</param> /// <param name="selectedAttributes">Selected attributes</param> /// <param name="customerEnteredPrice">Price entered by a customer</param> /// <returns>Found shopping cart item</returns> public virtual ShoppingCartItem FindShoppingCartItemInTheCart(IList<ShoppingCartItem> shoppingCart, ShoppingCartType shoppingCartType, ProductVariant productVariant, string selectedAttributes = "", decimal customerEnteredPrice = decimal.Zero) { if (shoppingCart == null) throw new ArgumentNullException("shoppingCart"); if (productVariant == null) throw new ArgumentNullException("productVariant"); foreach (var sci in shoppingCart.Where(a => a.ShoppingCartType == shoppingCartType)) { if (sci.ProductVariantId == productVariant.Id) { //attributes bool attributesEqual = _productAttributeParser.AreProductAttributesEqual(sci.AttributesXml, selectedAttributes); //gift cards bool giftCardInfoSame = true; if (sci.ProductVariant.IsGiftCard) { string giftCardRecipientName1 = string.Empty; string giftCardRecipientEmail1 = string.Empty; string giftCardSenderName1 = string.Empty; string giftCardSenderEmail1 = string.Empty; string giftCardMessage1 = string.Empty; _productAttributeParser.GetGiftCardAttribute(selectedAttributes, out giftCardRecipientName1, out giftCardRecipientEmail1, out giftCardSenderName1, out giftCardSenderEmail1, out giftCardMessage1); string giftCardRecipientName2 = string.Empty; string giftCardRecipientEmail2 = string.Empty; string giftCardSenderName2 = string.Empty; string giftCardSenderEmail2 = string.Empty; string giftCardMessage2 = string.Empty; _productAttributeParser.GetGiftCardAttribute(sci.AttributesXml, out giftCardRecipientName2, out giftCardRecipientEmail2, out giftCardSenderName2, out giftCardSenderEmail2, out giftCardMessage2); if (giftCardRecipientName1.ToLowerInvariant() != giftCardRecipientName2.ToLowerInvariant() || giftCardSenderName1.ToLowerInvariant() != giftCardSenderName2.ToLowerInvariant()) giftCardInfoSame = false; } //price is the same (for products which require customers to enter a price) bool customerEnteredPricesEqual = true; if (sci.ProductVariant.CustomerEntersPrice) customerEnteredPricesEqual = Math.Round(sci.CustomerEnteredPrice, 2) == Math.Round(customerEnteredPrice, 2); //found? if (attributesEqual && giftCardInfoSame && customerEnteredPricesEqual) return sci; } } return null; }
public void Can_get_shopping_cart_total_with_applied_reward_points() { //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); _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" }, new GenericAttribute() { StoreId = 1, EntityId = customer.Id, Key = SystemCustomerAttributeNames.UseRewardPointsDuringCheckout, KeyGroup = "Customer", Value = true.ToString() } }); _paymentService.Expect(ps => ps.GetAdditionalHandlingFee(cart, "test1")).Return(20); _discountService.Expect(ds => ds.GetAllDiscounts(DiscountType.AssignedToCategories)).Return(new List<Discount>()); decimal discountAmount; Discount appliedDiscount; List<AppliedGiftCard> appliedGiftCards; int redeemedRewardPoints; decimal redeemedRewardPointsAmount; //shipping is taxable, payment fee is taxable _taxSettings.ShippingIsTaxable = true; _taxSettings.PaymentMethodAdditionalFeeIsTaxable = true; //reward points _rewardPointsSettings.Enabled = true; _rewardPointsSettings.ExchangeRate = 2; //1 reward point = 2 customer.AddRewardPointsHistoryEntry(15); //15*2=30 //56 - items, 10 - shipping (fixed), 20 - payment fee, 8.6 - tax, -30 (reward points) _orderTotalCalcService.GetShoppingCartTotal(cart, out discountAmount, out appliedDiscount, out appliedGiftCards, out redeemedRewardPoints, out redeemedRewardPointsAmount) .ShouldEqual(64.6M); }
/// <summary> /// Gets a preferred discount /// </summary> /// <param name="productVariant">Product variant</param> /// <param name="customer">Customer</param> /// <param name="additionalCharge">Additional charge</param> /// <param name="quantity">Product quantity</param> /// <returns>Preferred discount</returns> protected virtual Discount GetPreferredDiscount(ProductVariant productVariant, Customer customer, decimal additionalCharge = decimal.Zero, int quantity = 1) { if (_catalogSettings.IgnoreDiscounts) return null; var allowedDiscounts = GetAllowedDiscounts(productVariant, customer); decimal finalPriceWithoutDiscount = GetFinalPrice(productVariant, customer, additionalCharge, false, quantity); var preferredDiscount = allowedDiscounts.GetPreferredDiscount(finalPriceWithoutDiscount); return preferredDiscount; }
/// <summary> /// Gets a tier price /// </summary> /// <param name="productVariant">Product variant</param> /// <param name="customer">Customer</param> /// <param name="quantity">Quantity</param> /// <returns>Price</returns> protected virtual decimal? GetMinimumTierPrice(ProductVariant productVariant, Customer customer, int quantity) { if (!productVariant.HasTierPrices) return decimal.Zero; var tierPrices = productVariant.TierPrices .OrderBy(tp => tp.Quantity) .ToList() .FilterForCustomer(customer) .RemoveDuplicatedQuantities(); int previousQty = 1; decimal? previousPrice = null; foreach (var tierPrice in tierPrices) { //check quantity if (quantity < tierPrice.Quantity) continue; if (tierPrice.Quantity < previousQty) continue; //save new price previousPrice = tierPrice.Price; previousQty = tierPrice.Quantity; } return previousPrice; }
/// <summary> /// Gets allowed discounts /// </summary> /// <param name="productVariant">Product variant</param> /// <param name="customer">Customer</param> /// <returns>Discounts</returns> protected virtual IList<Discount> GetAllowedDiscounts(ProductVariant productVariant, Customer customer) { var allowedDiscounts = new List<Discount>(); if (_catalogSettings.IgnoreDiscounts) return allowedDiscounts; if (productVariant.HasDiscountsApplied) { //we use this property ("HasDiscountsApplied") for performance optimziation to avoid unnecessary database calls foreach (var discount in productVariant.AppliedDiscounts) { if (_discountService.IsDiscountValid(discount, customer) && discount.DiscountType == DiscountType.AssignedToSkus && !allowedDiscounts.ContainsDiscount(discount)) allowedDiscounts.Add(discount); } } var productCategories = _categoryService.GetProductCategoriesByProductId(productVariant.ProductId); if (productCategories != null) { foreach (var productCategory in productCategories) { var category = productCategory.Category; if (category.HasDiscountsApplied) { //we use this property ("HasDiscountsApplied") for performance optimziation to avoid unnecessary database calls var categoryDiscounts = category.AppliedDiscounts; foreach (var discount in categoryDiscounts) { if (_discountService.IsDiscountValid(discount, customer) && discount.DiscountType == DiscountType.AssignedToCategories && !allowedDiscounts.ContainsDiscount(discount)) allowedDiscounts.Add(discount); } } } } return allowedDiscounts; }
/// <summary> /// Get product variant special price (is valid) /// </summary> /// <param name="productVariant">Product variant</param> /// <returns>Product variant special price</returns> public virtual decimal? GetSpecialPrice(ProductVariant productVariant) { if (productVariant == null) throw new ArgumentNullException("productVariant"); if (!productVariant.SpecialPrice.HasValue) return null; //check date range DateTime now = DateTime.UtcNow; if (productVariant.SpecialPriceStartDateTimeUtc.HasValue) { DateTime startDate = DateTime.SpecifyKind(productVariant.SpecialPriceStartDateTimeUtc.Value, DateTimeKind.Utc); if (startDate.CompareTo(now) > 0) return null; } if (productVariant.SpecialPriceEndDateTimeUtc.HasValue) { DateTime endDate = DateTime.SpecifyKind(productVariant.SpecialPriceEndDateTimeUtc.Value, DateTimeKind.Utc); if (endDate.CompareTo(now) < 0) return null; } return productVariant.SpecialPrice.Value; }
/// <summary> /// Gets the final price /// </summary> /// <param name="productVariant">Product variant</param> /// <param name="customer">The customer</param> /// <param name="additionalCharge">Additional charge</param> /// <param name="includeDiscounts">A value indicating whether include discounts or not for final price computation</param> /// <param name="quantity">Shopping cart item quantity</param> /// <returns>Final price</returns> public virtual decimal GetFinalPrice(ProductVariant productVariant, Customer customer, decimal additionalCharge, bool includeDiscounts, int quantity) { //initial price decimal result = productVariant.Price; //special price var specialPrice = GetSpecialPrice(productVariant); if (specialPrice.HasValue) result = specialPrice.Value; //tier prices if (productVariant.HasTierPrices) { decimal? tierPrice = GetMinimumTierPrice(productVariant, customer, quantity); if (tierPrice.HasValue) result = Math.Min(result, tierPrice.Value); } //discount + additional charge if (includeDiscounts) { Discount appliedDiscount = null; decimal discountAmount = GetDiscountAmount(productVariant, customer, additionalCharge, quantity, out appliedDiscount); result = result + additionalCharge - discountAmount; } else { result = result + additionalCharge; } if (result < decimal.Zero) result = decimal.Zero; return result; }
/// <summary> /// Gets the final price /// </summary> /// <param name="productVariant">Product variant</param> /// <param name="customer">The customer</param> /// <param name="additionalCharge">Additional charge</param> /// <param name="includeDiscounts">A value indicating whether include discounts or not for final price computation</param> /// <returns>Final price</returns> public virtual decimal GetFinalPrice(ProductVariant productVariant, Customer customer, decimal additionalCharge, bool includeDiscounts) { return GetFinalPrice(productVariant, customer, additionalCharge, includeDiscounts, 1); }
/// <summary> /// Gets the final price /// </summary> /// <param name="productVariant">Product variant</param> /// <param name="customer">The customer</param> /// <param name="includeDiscounts">A value indicating whether include discounts or not for final price computation</param> /// <returns>Final price</returns> public virtual decimal GetFinalPrice(ProductVariant productVariant, Customer customer, bool includeDiscounts) { return GetFinalPrice(productVariant, customer, decimal.Zero, includeDiscounts); }
public void Can_get_shopping_cart_subTotal_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); _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); discountAmount.ShouldEqual(0); appliedDiscount.ShouldBeNull(); subTotalWithoutDiscount.ShouldEqual(98.329); subTotalWithDiscount.ShouldEqual(98.329); taxRates.Count.ShouldEqual(1); taxRates.ContainsKey(10).ShouldBeTrue(); taxRates[10].ShouldEqual(8.939); }
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> /// Gets discount amount /// </summary> /// <param name="productVariant">Product variant</param> /// <returns>Discount amount</returns> public virtual decimal GetDiscountAmount(ProductVariant productVariant) { var customer = _workContext.CurrentCustomer; return GetDiscountAmount(productVariant, customer, decimal.Zero); }
/// <summary> /// Add a product variant to shopping cart /// </summary> /// <param name="customer">Customer</param> /// <param name="productVariant">Product variant</param> /// <param name="shoppingCartType">Shopping cart type</param> /// <param name="selectedAttributes">Selected attributes</param> /// <param name="customerEnteredPrice">The price enter by a customer</param> /// <param name="quantity">Quantity</param> /// <param name="automaticallyAddRequiredProductVariantsIfEnabled">Automatically add required product variants if enabled</param> /// <returns>Warnings</returns> public virtual IList<string> AddToCart(Customer customer, ProductVariant productVariant, ShoppingCartType shoppingCartType, string selectedAttributes, decimal customerEnteredPrice, int quantity, bool automaticallyAddRequiredProductVariantsIfEnabled) { if (customer == null) throw new ArgumentNullException("customer"); if (productVariant == null) throw new ArgumentNullException("productVariant"); var warnings = new List<string>(); if (shoppingCartType == ShoppingCartType.ShoppingCart && !_permissionService.Authorize(StandardPermissionProvider.EnableShoppingCart, customer)) { warnings.Add("Shopping cart is disabled"); return warnings; } if (shoppingCartType == ShoppingCartType.Wishlist && !_permissionService.Authorize(StandardPermissionProvider.EnableWishlist, customer)) { warnings.Add("Wishlist is disabled"); return warnings; } if (quantity <= 0) { warnings.Add(_localizationService.GetResource("ShoppingCart.QuantityShouldPositive")); return warnings; } //reset checkout info _customerService.ResetCheckoutData(customer); var cart = customer.ShoppingCartItems.Where(sci=>sci.ShoppingCartType == shoppingCartType).ToList(); var shoppingCartItem = FindShoppingCartItemInTheCart(cart, shoppingCartType, productVariant, selectedAttributes, customerEnteredPrice); if (shoppingCartItem != null) { //update existing shopping cart item int newQuantity = shoppingCartItem.Quantity + quantity; warnings.AddRange(GetShoppingCartItemWarnings(customer, shoppingCartType, productVariant, selectedAttributes, customerEnteredPrice, newQuantity, automaticallyAddRequiredProductVariantsIfEnabled)); if (warnings.Count == 0) { shoppingCartItem.AttributesXml = selectedAttributes; shoppingCartItem.Quantity = newQuantity; shoppingCartItem.UpdatedOnUtc = DateTime.UtcNow; _customerService.UpdateCustomer(customer); //event notification _eventPublisher.EntityUpdated(shoppingCartItem); } } else { //new shopping cart item warnings.AddRange(GetShoppingCartItemWarnings(customer, shoppingCartType, productVariant, selectedAttributes, customerEnteredPrice, quantity, automaticallyAddRequiredProductVariantsIfEnabled)); if (warnings.Count == 0) { //maximum items validation switch (shoppingCartType) { case ShoppingCartType.ShoppingCart: { if (cart.Count >= _shoppingCartSettings.MaximumShoppingCartItems) return warnings; } break; case ShoppingCartType.Wishlist: { if (cart.Count >= _shoppingCartSettings.MaximumWishlistItems) return warnings; } break; default: break; } DateTime now = DateTime.UtcNow; shoppingCartItem = new ShoppingCartItem() { ShoppingCartType = shoppingCartType, ProductVariant = productVariant, AttributesXml = selectedAttributes, CustomerEnteredPrice = customerEnteredPrice, Quantity = quantity, CreatedOnUtc = now, UpdatedOnUtc = now }; customer.ShoppingCartItems.Add(shoppingCartItem); _customerService.UpdateCustomer(customer); //event notification _eventPublisher.EntityInserted(shoppingCartItem); } } return warnings; }
/// <summary> /// Gets discount amount /// </summary> /// <param name="productVariant">Product variant</param> /// <param name="customer">The customer</param> /// <returns>Discount amount</returns> public virtual decimal GetDiscountAmount(ProductVariant productVariant, Customer customer) { return GetDiscountAmount(productVariant, customer, decimal.Zero); }
/// <summary> /// Validates required product variants (product variants which require other variant to be added to the cart) /// </summary> /// <param name="customer">Customer</param> /// <param name="shoppingCartType">Shopping cart type</param> /// <param name="productVariant">Product variant</param> /// <param name="automaticallyAddRequiredProductVariantsIfEnabled">Automatically add required product variants if enabled</param> /// <returns>Warnings</returns> public virtual IList<string> GetRequiredProductVariantWarnings(Customer customer, ShoppingCartType shoppingCartType, ProductVariant productVariant, bool automaticallyAddRequiredProductVariantsIfEnabled) { if (customer == null) throw new ArgumentNullException("customer"); if (productVariant == null) throw new ArgumentNullException("productVariant"); var cart = customer.ShoppingCartItems.Where(sci => sci.ShoppingCartType == shoppingCartType).ToList(); var warnings = new List<string>(); if (productVariant.RequireOtherProducts) { var requiredProductVariants = new List<ProductVariant>(); foreach (var id in productVariant.ParseRequiredProductVariantIds()) { var rpv = _productService.GetProductVariantById(id); if (rpv != null) requiredProductVariants.Add(rpv); } foreach (var rpv in requiredProductVariants) { //ensure that product is in the cart bool alreadyInTheCart = false; foreach (var sci in cart) { if (sci.ProductVariantId == rpv.Id) { alreadyInTheCart = true; break; } } //not in the cart if (!alreadyInTheCart) { string fullProductName = ""; if (!String.IsNullOrEmpty(rpv.GetLocalized(x => x.Name))) fullProductName = string.Format("{0} ({1})", rpv.Product.GetLocalized(x => x.Name), rpv.GetLocalized(x => x.Name)); else fullProductName = rpv.Product.GetLocalized(x => x.Name); if (productVariant.AutomaticallyAddRequiredProductVariants) { //add to cart (if possible) if (automaticallyAddRequiredProductVariantsIfEnabled) { //pass 'false' for 'automaticallyAddRequiredProductVariantsIfEnabled' to prevent circular references var addToCartWarnings = AddToCart(customer, rpv, shoppingCartType, "", decimal.Zero, 1, false); if (addToCartWarnings.Count > 0) { //a product wasn't atomatically added for some reasons //don't display specific errors from 'addToCartWarnings' variable //display only generic error warnings.Add(string.Format(_localizationService.GetResource("ShoppingCart.RequiredProductWarning"), fullProductName)); } } else { warnings.Add(string.Format(_localizationService.GetResource("ShoppingCart.RequiredProductWarning"), fullProductName)); } } else { warnings.Add(string.Format(_localizationService.GetResource("ShoppingCart.RequiredProductWarning"), fullProductName)); } } } } return warnings; }
/// <summary> /// Validates shopping cart item /// </summary> /// <param name="customer">Customer</param> /// <param name="shoppingCartType">Shopping cart type</param> /// <param name="productVariant">Product variant</param> /// <param name="selectedAttributes">Selected attributes</param> /// <param name="customerEnteredPrice">Customer entered price</param> /// <param name="quantity">Quantity</param> /// <param name="automaticallyAddRequiredProductVariantsIfEnabled">Automatically add required product variants if enabled</param> /// <param name="getStandardWarnings">A value indicating whether we should validate a product variant for standard properties</param> /// <param name="getAttributesWarnings">A value indicating whether we should validate product attributes</param> /// <param name="getGiftCardWarnings">A value indicating whether we should validate gift card properties</param> /// <param name="getRequiredProductVariantWarnings">A value indicating whether we should validate required product variants (product variants which require other variant to be added to the cart)</param> /// <returns>Warnings</returns> public virtual IList<string> GetShoppingCartItemWarnings(Customer customer, ShoppingCartType shoppingCartType, ProductVariant productVariant, string selectedAttributes, decimal customerEnteredPrice, int quantity, bool automaticallyAddRequiredProductVariantsIfEnabled, bool getStandardWarnings = true, bool getAttributesWarnings = true, bool getGiftCardWarnings = true, bool getRequiredProductVariantWarnings = true) { if (productVariant == null) throw new ArgumentNullException("productVariant"); var warnings = new List<string>(); //standard properties if (getStandardWarnings) warnings.AddRange(GetStandardWarnings(customer, shoppingCartType, productVariant, selectedAttributes, customerEnteredPrice, quantity)); //selected attributes if (getAttributesWarnings) warnings.AddRange(GetShoppingCartItemAttributeWarnings(shoppingCartType, productVariant, selectedAttributes)); //gift cards if (getGiftCardWarnings) warnings.AddRange(GetShoppingCartItemGiftCardWarnings(shoppingCartType, productVariant, selectedAttributes)); //required product variants if (getRequiredProductVariantWarnings) warnings.AddRange(GetRequiredProductVariantWarnings(customer, shoppingCartType, productVariant, automaticallyAddRequiredProductVariantsIfEnabled)); return warnings; }
/// <summary> /// Validates shopping cart item (gift card) /// </summary> /// <param name="shoppingCartType">Shopping cart type</param> /// <param name="productVariant">Product variant</param> /// <param name="selectedAttributes">Selected attributes</param> /// <returns>Warnings</returns> public virtual IList<string> GetShoppingCartItemGiftCardWarnings(ShoppingCartType shoppingCartType, ProductVariant productVariant, string selectedAttributes) { if (productVariant == null) throw new ArgumentNullException("productVariant"); var warnings = new List<string>(); //gift cards if (productVariant.IsGiftCard) { string giftCardRecipientName = string.Empty; string giftCardRecipientEmail = string.Empty; string giftCardSenderName = string.Empty; string giftCardSenderEmail = string.Empty; string giftCardMessage = string.Empty; _productAttributeParser.GetGiftCardAttribute(selectedAttributes, out giftCardRecipientName, out giftCardRecipientEmail, out giftCardSenderName, out giftCardSenderEmail, out giftCardMessage); if (String.IsNullOrEmpty(giftCardRecipientName)) warnings.Add(_localizationService.GetResource("ShoppingCart.RecipientNameError")); if (productVariant.GiftCardType == GiftCardType.Virtual) { //validate for virtual gift cards only if (String.IsNullOrEmpty(giftCardRecipientEmail) || !CommonHelper.IsValidEmail(giftCardRecipientEmail)) warnings.Add(_localizationService.GetResource("ShoppingCart.RecipientEmailError")); } if (String.IsNullOrEmpty(giftCardSenderName)) warnings.Add(_localizationService.GetResource("ShoppingCart.SenderNameError")); if (productVariant.GiftCardType == GiftCardType.Virtual) { //validate for virtual gift cards only if (String.IsNullOrEmpty(giftCardSenderEmail) || !CommonHelper.IsValidEmail(giftCardSenderEmail)) warnings.Add(_localizationService.GetResource("ShoppingCart.SenderEmailError")); } } return warnings; }
/// <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; }
/// <summary> /// Validates a product variant for standard properties /// </summary> /// <param name="customer">Customer</param> /// <param name="shoppingCartType">Shopping cart type</param> /// <param name="productVariant">Product variant</param> /// <param name="selectedAttributes">Selected attributes</param> /// <param name="customerEnteredPrice">Customer entered price</param> /// <param name="quantity">Quantity</param> /// <returns>Warnings</returns> public virtual IList<string> GetStandardWarnings(Customer customer, ShoppingCartType shoppingCartType, ProductVariant productVariant, string selectedAttributes, decimal customerEnteredPrice, int quantity) { if (customer == null) throw new ArgumentNullException("customer"); if (productVariant == null) throw new ArgumentNullException("productVariant"); var warnings = new List<string>(); var product = productVariant.Product; if (product == null) { warnings.Add(string.Format(_localizationService.GetResource("ShoppingCart.CannotLoadProduct"), productVariant.ProductId)); return warnings; } //deleted? if (product.Deleted || productVariant.Deleted) { warnings.Add(_localizationService.GetResource("ShoppingCart.ProductDeleted")); return warnings; } //published? if (!product.Published || !productVariant.Published) { warnings.Add(_localizationService.GetResource("ShoppingCart.ProductUnpublished")); } //ACL if (!_aclService.Authorize(product, customer)) { warnings.Add(_localizationService.GetResource("ShoppingCart.ProductUnpublished")); } //disabled "add to cart" button if (shoppingCartType == ShoppingCartType.ShoppingCart && productVariant.DisableBuyButton) { warnings.Add(_localizationService.GetResource("ShoppingCart.BuyingDisabled")); } //disabled "add to wishlist" button if (shoppingCartType == ShoppingCartType.Wishlist && productVariant.DisableWishlistButton) { warnings.Add(_localizationService.GetResource("ShoppingCart.WishlistDisabled")); } //call for price if (shoppingCartType == ShoppingCartType.ShoppingCart && productVariant.CallForPrice) { warnings.Add(_localizationService.GetResource("Products.CallForPrice")); } //customer entered price if (productVariant.CustomerEntersPrice) { if (customerEnteredPrice < productVariant.MinimumCustomerEnteredPrice || customerEnteredPrice > productVariant.MaximumCustomerEnteredPrice) { decimal minimumCustomerEnteredPrice = _currencyService.ConvertFromPrimaryStoreCurrency(productVariant.MinimumCustomerEnteredPrice, _workContext.WorkingCurrency); decimal maximumCustomerEnteredPrice = _currencyService.ConvertFromPrimaryStoreCurrency(productVariant.MaximumCustomerEnteredPrice, _workContext.WorkingCurrency); warnings.Add(string.Format(_localizationService.GetResource("ShoppingCart.CustomerEnteredPrice.RangeError"), _priceFormatter.FormatPrice(minimumCustomerEnteredPrice, false, false), _priceFormatter.FormatPrice(maximumCustomerEnteredPrice, false, false))); } } //quantity validation var hasQtyWarnings = false; if (quantity < productVariant.OrderMinimumQuantity) { warnings.Add(string.Format(_localizationService.GetResource("ShoppingCart.MinimumQuantity"), productVariant.OrderMinimumQuantity)); hasQtyWarnings = true; } if (quantity > productVariant.OrderMaximumQuantity) { warnings.Add(string.Format(_localizationService.GetResource("ShoppingCart.MaximumQuantity"), productVariant.OrderMaximumQuantity)); hasQtyWarnings = true; } var allowedQuantities = productVariant.ParseAllowedQuatities(); if (allowedQuantities.Length > 0 && !allowedQuantities.Contains(quantity)) { warnings.Add(string.Format(_localizationService.GetResource("ShoppingCart.AllowedQuantities"), string.Join(", ", allowedQuantities))); } var validateOutOfStock = shoppingCartType == ShoppingCartType.ShoppingCart || !_shoppingCartSettings.AllowOutOfStockItemsToBeAddedToWishlist; if (validateOutOfStock && !hasQtyWarnings) { switch (productVariant.ManageInventoryMethod) { case ManageInventoryMethod.DontManageStock: { } break; case ManageInventoryMethod.ManageStock: { if ((BackorderMode)productVariant.BackorderMode == BackorderMode.NoBackorders) { if (productVariant.StockQuantity < quantity) { int maximumQuantityCanBeAdded = productVariant.StockQuantity; if (maximumQuantityCanBeAdded <= 0) warnings.Add(_localizationService.GetResource("ShoppingCart.OutOfStock")); else warnings.Add(string.Format(_localizationService.GetResource("ShoppingCart.QuantityExceedsStock"), maximumQuantityCanBeAdded)); } } } break; case ManageInventoryMethod.ManageStockByAttributes: { var combination = productVariant .ProductVariantAttributeCombinations .FirstOrDefault(x => _productAttributeParser.AreProductAttributesEqual(x.AttributesXml, selectedAttributes)); if (combination != null && !combination.AllowOutOfStockOrders && combination.StockQuantity < quantity) { int maximumQuantityCanBeAdded = combination.StockQuantity; if (maximumQuantityCanBeAdded <= 0) warnings.Add(_localizationService.GetResource("ShoppingCart.OutOfStock")); else warnings.Add(string.Format(_localizationService.GetResource("ShoppingCart.QuantityExceedsStock"), maximumQuantityCanBeAdded)); } } break; default: break; } } //availability dates bool availableStartDateError = false; if (productVariant.AvailableStartDateTimeUtc.HasValue) { DateTime now = DateTime.UtcNow; DateTime availableStartDateTime = DateTime.SpecifyKind(productVariant.AvailableStartDateTimeUtc.Value, DateTimeKind.Utc); if (availableStartDateTime.CompareTo(now) > 0) { warnings.Add(_localizationService.GetResource("ShoppingCart.NotAvailable")); availableStartDateError = true; } } if (productVariant.AvailableEndDateTimeUtc.HasValue && !availableStartDateError) { DateTime now = DateTime.UtcNow; DateTime availableEndDateTime = DateTime.SpecifyKind(productVariant.AvailableEndDateTimeUtc.Value, DateTimeKind.Utc); if (availableEndDateTime.CompareTo(now) < 0) { warnings.Add(_localizationService.GetResource("ShoppingCart.NotAvailable")); } } return warnings; }
private IList<Token> GenerateTokens(ProductVariant productVariant) { var tokens = new List<Token>(); _messageTokenProvider.AddStoreTokens(tokens); _messageTokenProvider.AddProductVariantTokens(tokens, productVariant); return tokens; }