public void Can_save_and_load_shoppingCartItem()
        {
            var sci = new ShoppingCartItem
            {
                ShoppingCartType = ShoppingCartType.ShoppingCart,
                AttributesXml = "AttributesXml 1",
                CustomerEnteredPrice = 1.1M,
                Quantity= 2,
                CreatedOnUtc = new DateTime(2010, 01, 01),
                UpdatedOnUtc = new DateTime(2010, 01, 02),
                Customer = GetTestCustomer(),
				Product = GetTestProduct()
            };

            var fromDb = SaveAndLoadEntity(sci);
            fromDb.ShouldNotBeNull();

            fromDb.ShoppingCartType.ShouldEqual(ShoppingCartType.ShoppingCart);
            fromDb.AttributesXml.ShouldEqual("AttributesXml 1");
            fromDb.CustomerEnteredPrice.ShouldEqual(1.1M);
            fromDb.Quantity.ShouldEqual(2);
            fromDb.CreatedOnUtc.ShouldEqual(new DateTime(2010, 01, 01));
            fromDb.UpdatedOnUtc.ShouldEqual(new DateTime(2010, 01, 02));

            fromDb.Customer.ShouldNotBeNull();
			fromDb.Product.ShouldNotBeNull();
        }
        public void Can_get_shoppingCart_totalWeight_without_attributes()
        {
            var sci1 = new ShoppingCartItem()
            {
                AttributesXml = "",
                Quantity = 3,
                Product = new Product()
                {
                    Weight = 1.5M,
                    Height = 2.5M,
                    Length = 3.5M,
                    Width = 4.5M
                }
            };
            var sci2 = new ShoppingCartItem()
            {
                AttributesXml = "",
                Quantity = 4,
                Product = new Product()
                {
                    Weight = 11.5M,
                    Height = 12.5M,
                    Length = 13.5M,
                    Width = 14.5M
                }
            };

            var cart = new List<OrganizedShoppingCartItem>();
            cart.Add(new OrganizedShoppingCartItem(sci1));
            cart.Add(new OrganizedShoppingCartItem(sci2));

            _shippingService.GetShoppingCartTotalWeight(cart).ShouldEqual(50.5M);
        }
		public OrganizedShoppingCartItem(ShoppingCartItem item)
		{
			if (item == null)
				throw new ArgumentNullException("item");

			Item = item;	// must not be null
			ChildItems = new List<OrganizedShoppingCartItem>();
			BundleItemData = new ProductBundleItemData(item.BundleItem);
		}
        public void Can_get_shoppingCartItem_totalWeight_without_attributes()
        {
            var sci = new ShoppingCartItem()
            {
                AttributesXml = "",
                Quantity = 3,
                Product = new Product()
                {
                    Weight = 1.5M,
                    Height = 2.5M,
                    Length = 3.5M,
                    Width = 4.5M
                }
            };

            var item = new OrganizedShoppingCartItem(sci);

            _shippingService.GetShoppingCartItemTotalWeight(item).ShouldEqual(4.5M);
        }
        public void Can_get_shopping_cart_subTotal_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);

            _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_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);
        }
        public void Can_get_shoppingCartItem_additional_shippingCharge()
        {
            var sci1 = new ShoppingCartItem()
            {
                AttributesXml = "",
                Quantity = 3,
                Product = new Product()
                {
                    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()
                {
                    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()
                {
                    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));

            _orderTotalCalcService.GetShoppingCartAdditionalShippingCharge(cart).ShouldEqual(42.5M);
        }
        public void Can_get_shipping_total_with_fixed_shipping_rate_including_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);

            decimal taxRate = decimal.Zero;
            Discount appliedDiscount = null;
            decimal? shipping = null;

            shipping = _orderTotalCalcService.GetShoppingCartShippingTotal(cart, true, out taxRate, out appliedDiscount);
            shipping.ShouldNotBeNull();
            //10 - default fixed shipping rate, 42.5 - additional shipping change
            shipping.ShouldEqual(57.75);
            appliedDiscount.ShouldBeNull();
            //10 - default fixed tax rate
            taxRate.ShouldEqual(10);
        }
        public void Shipping_should_not_be_free_when_some_of_shoppingCartItems_are_not_marked_as_freeShipping()
        {
            var sci1 = new ShoppingCartItem()
            {
                AttributesXml = "",
                Quantity = 3,
                Product = new Product()
                {
                    Weight = 1.5M,
                    Height = 2.5M,
                    Length = 3.5M,
                    Width = 4.5M,
                    IsFreeShipping = true,
                    IsShipEnabled = true,
                }
            };
            var sci2 = new ShoppingCartItem()
            {
                AttributesXml = "",
                Quantity = 4,
                Product = new Product()
                {
                    Weight = 11.5M,
                    Height = 12.5M,
                    Length = 13.5M,
                    Width = 14.5M,
                    IsFreeShipping = false,
                    IsShipEnabled = true,
                }
            };

            var cart = new List<OrganizedShoppingCartItem>();
            cart.Add(new OrganizedShoppingCartItem(sci1));
            cart.Add(new OrganizedShoppingCartItem(sci2));

            var customer = new Customer();
            cart.ForEach(sci => sci.Item.Customer = customer);
            cart.ForEach(sci => sci.Item.CustomerId = customer.Id);

            _orderTotalCalcService.IsFreeShipping(cart).ShouldEqual(false);
        }
        public void Shipping_should_be_free_when_customer_is_in_role_with_free_shipping()
        {
            var sci1 = new ShoppingCartItem()
            {
                AttributesXml = "",
                Quantity = 3,
                Product = new Product()
                {
                    Weight = 1.5M,
                    Height = 2.5M,
                    Length = 3.5M,
                    Width = 4.5M,
                    IsFreeShipping = false,
                    IsShipEnabled = true,
                }
            };
            var sci2 = new ShoppingCartItem()
            {
                AttributesXml = "",
                Quantity = 4,
                Product = new Product()
                {
                    Weight = 11.5M,
                    Height = 12.5M,
                    Length = 13.5M,
                    Width = 14.5M,
                    IsFreeShipping = false,
                    IsShipEnabled = true,
                }
            };

            var cart = new List<OrganizedShoppingCartItem>();
            cart.Add(new OrganizedShoppingCartItem(sci1));
            cart.Add(new OrganizedShoppingCartItem(sci2));

            var customer = new Customer();
            var customerRole1 = new CustomerRole()
            {
                Active = true,
                FreeShipping = true,
            };
            var customerRole2 = new CustomerRole()
            {
                Active = true,
                FreeShipping = false,
            };
            customer.CustomerRoles.Add(customerRole1);
            customer.CustomerRoles.Add(customerRole2);
            cart.ForEach(sci => sci.Item.Customer = customer);
            cart.ForEach(sci => sci.Item.CustomerId = customer.Id);

            _orderTotalCalcService.IsFreeShipping(cart).ShouldEqual(true);
        }
        /// <summary>
		/// Add a product to shopping cart
        /// </summary>
        /// <param name="customer">Customer</param>
		/// <param name="product">Product</param>
        /// <param name="shoppingCartType">Shopping cart type</param>
		/// <param name="storeId">Store identifier</param>
        /// <param name="selectedAttributes">Selected attributes</param>
        /// <param name="customerEnteredPrice">The price enter by a customer</param>
        /// <param name="quantity">Quantity</param>
		/// <param name="automaticallyAddRequiredProductsIfEnabled">Automatically add required products if enabled</param>
		/// <param name="shoppingCartItemId">Identifier fo the new shopping cart item</param>
		/// <param name="parentItemId">Identifier of the parent shopping cart item</param>
		/// <param name="bundleItem">Product bundle item</param>
        /// <returns>Warnings</returns>
        public virtual IList<string> AddToCart(Customer customer, Product product,
			ShoppingCartType shoppingCartType, int storeId, string selectedAttributes,
			decimal customerEnteredPrice, int quantity, bool automaticallyAddRequiredProductsIfEnabled,
			out int shoppingCartItemId, int? parentItemId = null, ProductBundleItem bundleItem = null)
        {
			shoppingCartItemId = 0;

            if (customer == null)
                throw new ArgumentNullException("customer");

            if (product == null)
                throw new ArgumentNullException("product");

            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;
            }

			if (parentItemId.HasValue && (parentItemId.Value == 0 || bundleItem == null || bundleItem.Id == 0))
			{
				warnings.Add(_localizationService.GetResource("ShoppingCart.Bundle.BundleItemNotFound").FormatWith(bundleItem.GetLocalizedName()));
				return warnings;
			}

            //reset checkout info
			_customerService.ResetCheckoutData(customer, storeId);

			var cart = customer.GetCartItems(shoppingCartType, storeId);
			OrganizedShoppingCartItem shoppingCartItem = null;

			if (bundleItem == null)
			{
				shoppingCartItem = FindShoppingCartItemInTheCart(cart, shoppingCartType, product, selectedAttributes, customerEnteredPrice);
			}

            if (shoppingCartItem != null)
            {
                //update existing shopping cart item
                int newQuantity = shoppingCartItem.Item.Quantity + quantity;

                warnings.AddRange(
					GetShoppingCartItemWarnings(customer, shoppingCartType, product, storeId, selectedAttributes, customerEnteredPrice, newQuantity, 
						automaticallyAddRequiredProductsIfEnabled, bundleItem: bundleItem)
				);

                if (warnings.Count == 0)
                {
                    shoppingCartItem.Item.AttributesXml = selectedAttributes;
                    shoppingCartItem.Item.Quantity = newQuantity;
                    shoppingCartItem.Item.UpdatedOnUtc = DateTime.UtcNow;
                    _customerService.UpdateCustomer(customer);

                    //event notification
                    _eventPublisher.EntityUpdated(shoppingCartItem.Item);
                }
            }
            else
            {
                //new shopping cart item
                warnings.AddRange(
					GetShoppingCartItemWarnings(customer, shoppingCartType, product, storeId, selectedAttributes, customerEnteredPrice, quantity,
						automaticallyAddRequiredProductsIfEnabled, bundleItem: bundleItem)
				);

                if (warnings.Count == 0)
                {
                    //maximum items validation
                    switch (shoppingCartType)
                    {
                        case ShoppingCartType.ShoppingCart:
                            if (cart.Count >= _shoppingCartSettings.MaximumShoppingCartItems)
                            {
                                warnings.Add(_localizationService.GetResource("ShoppingCart.MaximumShoppingCartItems"));
                                return warnings;
                            }
                            break;
                        case ShoppingCartType.Wishlist:
                            if (cart.Count >= _shoppingCartSettings.MaximumWishlistItems)
                            {
                                warnings.Add(_localizationService.GetResource("ShoppingCart.MaximumWishlistItems"));
                                return warnings;
                            }
                            break;
                        default:
                            break;
                    }

                    DateTime now = DateTime.UtcNow;
                    var cartItem = new ShoppingCartItem()
                    {
                        ShoppingCartType = shoppingCartType,
						StoreId = storeId,
                        Product = product,
                        AttributesXml = selectedAttributes,
                        CustomerEnteredPrice = customerEnteredPrice,
                        Quantity = quantity,
                        CreatedOnUtc = now,
                        UpdatedOnUtc = now,
						ParentItemId = parentItemId
                    };

					if (bundleItem != null)
						cartItem.BundleItemId = bundleItem.Id;

                    customer.ShoppingCartItems.Add(cartItem);
                    _customerService.UpdateCustomer(customer);

					shoppingCartItemId = cartItem.Id;

                    //event notification
                    _eventPublisher.EntityInserted(cartItem);
                }
            }

            return warnings;
        }
        /// <summary>
        /// Delete shopping cart item
        /// </summary>
        /// <param name="shoppingCartItem">Shopping cart item</param>
        /// <param name="resetCheckoutData">A value indicating whether to reset checkout data</param>
        /// <param name="ensureOnlyActiveCheckoutAttributes">A value indicating whether to ensure that only active checkout attributes are attached to the current customer</param>
		/// <param name="deleteChildCartItems">A value indicating whether to delete child cart items</param>
        public virtual void DeleteShoppingCartItem(ShoppingCartItem shoppingCartItem, bool resetCheckoutData = true, 
            bool ensureOnlyActiveCheckoutAttributes = false, bool deleteChildCartItems = true)
        {
            if (shoppingCartItem == null)
                throw new ArgumentNullException("shoppingCartItem");

            var customer = shoppingCartItem.Customer;
			var storeId = shoppingCartItem.StoreId;
			int cartItemId = shoppingCartItem.Id;

            //reset checkout data
            if (resetCheckoutData)
            {
				_customerService.ResetCheckoutData(shoppingCartItem.Customer, shoppingCartItem.StoreId);
            }

            //delete item
            _sciRepository.Delete(shoppingCartItem);

            //validate checkout attributes
            if (ensureOnlyActiveCheckoutAttributes &&
                //only for shopping cart items (ignore wishlist)
                shoppingCartItem.ShoppingCartType == ShoppingCartType.ShoppingCart)
            {
				var cart = customer.GetCartItems(ShoppingCartType.ShoppingCart, storeId);

				var checkoutAttributesXml = customer.GetAttribute<string>(SystemCustomerAttributeNames.CheckoutAttributes, _genericAttributeService);
				checkoutAttributesXml = _checkoutAttributeParser.EnsureOnlyActiveAttributes(checkoutAttributesXml, cart);
				_genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.CheckoutAttributes, checkoutAttributesXml);
            }

            //event notification
            _eventPublisher.EntityDeleted(shoppingCartItem);

			// delete child items
			if (deleteChildCartItems)
			{
				var childCartItems = _sciRepository.Table
					.Where(x => x.CustomerId == customer.Id && x.ParentItemId != null && x.ParentItemId.Value == cartItemId && x.Id != cartItemId)
					.ToList();

				foreach (var cartItem in childCartItems)
					DeleteShoppingCartItem(cartItem, resetCheckoutData, ensureOnlyActiveCheckoutAttributes, false);
			}
        }
        public void Can_get_shopping_cart_item_unitPrice()
        {
            //customer
            var 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()
            {
                Customer = customer,
                CustomerId = customer.Id,
                Product = product1,
                ProductId = product1.Id,
                Quantity = 2,
            };

            var item = new OrganizedShoppingCartItem(sci1);

            _priceCalcService.GetUnitPrice(item, false).ShouldEqual(12.34);
        }
		/// <summary>
		/// Add product to cart
		/// </summary>
		/// <param name="customer">The customer</param>
		/// <param name="product">The product</param>
		/// <param name="cartType">Cart type</param>
		/// <param name="storeId">Store identifier</param>
		/// <param name="selectedAttributes">Selected attributes</param>
		/// <param name="customerEnteredPrice">Price entered by customer</param>
		/// <param name="quantity">Quantity</param>
		/// <param name="automaticallyAddRequiredProductsIfEnabled">Whether to add required products</param>
		/// <param name="ctx">Add to cart context</param>
		/// <returns>List with warnings</returns>
		public virtual List<string> AddToCart(Customer customer, Product product, ShoppingCartType cartType, int storeId, string selectedAttributes,
			decimal customerEnteredPrice, int quantity, bool automaticallyAddRequiredProductsIfEnabled,	AddToCartContext ctx = null)
		{
			if (customer == null)
				throw new ArgumentNullException("customer");

			if (product == null)
				throw new ArgumentNullException("product");

			var warnings = new List<string>();
			var bundleItem = (ctx == null ? null : ctx.BundleItem);

			if (ctx != null && bundleItem != null && ctx.Warnings.Count > 0)
				return ctx.Warnings;	// warnings while adding bundle items to cart -> no need for further processing

			if (cartType == ShoppingCartType.ShoppingCart && !_permissionService.Authorize(StandardPermissionProvider.EnableShoppingCart, customer))
			{
				warnings.Add("Shopping cart is disabled");
				return warnings;
			}
			if (cartType == 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;
			}

			//if (parentItemId.HasValue && (parentItemId.Value == 0 || bundleItem == null || bundleItem.Id == 0))
			//{
			//	warnings.Add(_localizationService.GetResource("ShoppingCart.Bundle.BundleItemNotFound").FormatWith(bundleItem.GetLocalizedName()));
			//	return warnings;
			//}

			//reset checkout info
			_customerService.ResetCheckoutData(customer, storeId);

			var cart = customer.GetCartItems(cartType, storeId);
			OrganizedShoppingCartItem existingCartItem = null;

			if (bundleItem == null)
			{
				existingCartItem = FindShoppingCartItemInTheCart(cart, cartType, product, selectedAttributes, customerEnteredPrice);
			}

			if (existingCartItem != null)
			{
				//update existing shopping cart item
				int newQuantity = existingCartItem.Item.Quantity + quantity;

				warnings.AddRange(
					GetShoppingCartItemWarnings(customer, cartType, product, storeId, selectedAttributes, customerEnteredPrice, newQuantity,
						automaticallyAddRequiredProductsIfEnabled, bundleItem: bundleItem)
				);

				if (warnings.Count == 0)
				{
					existingCartItem.Item.AttributesXml = selectedAttributes;
					existingCartItem.Item.Quantity = newQuantity;
					existingCartItem.Item.UpdatedOnUtc = DateTime.UtcNow;
					_customerService.UpdateCustomer(customer);

					//event notification
					_eventPublisher.EntityUpdated(existingCartItem.Item);
				}
			}
			else
			{
				//new shopping cart item
				warnings.AddRange(
					GetShoppingCartItemWarnings(customer, cartType, product, storeId, selectedAttributes, customerEnteredPrice, quantity,
						automaticallyAddRequiredProductsIfEnabled, bundleItem: bundleItem)
				);

				if (warnings.Count == 0)
				{
					//maximum items validation
					if (cartType == ShoppingCartType.ShoppingCart && cart.Count >= _shoppingCartSettings.MaximumShoppingCartItems)
					{
						warnings.Add(_localizationService.GetResource("ShoppingCart.MaximumShoppingCartItems"));
						return warnings;
					}
					else if (cartType == ShoppingCartType.Wishlist && cart.Count >= _shoppingCartSettings.MaximumWishlistItems)
					{
						warnings.Add(_localizationService.GetResource("ShoppingCart.MaximumWishlistItems"));
						return warnings;
					}

					var now = DateTime.UtcNow;
					var cartItem = new ShoppingCartItem()
					{
						ShoppingCartType = cartType,
						StoreId = storeId,
						Product = product,
						AttributesXml = selectedAttributes,
						CustomerEnteredPrice = customerEnteredPrice,
						Quantity = quantity,
						CreatedOnUtc = now,
						UpdatedOnUtc = now,
						ParentItemId = null	//parentItemId
					};

					if (bundleItem != null)
						cartItem.BundleItemId = bundleItem.Id;


					if (ctx == null)
					{
						customer.ShoppingCartItems.Add(cartItem);
						_customerService.UpdateCustomer(customer);
						_eventPublisher.EntityInserted(cartItem);
					}
					else
					{
						if (bundleItem == null)
						{
							Debug.Assert(ctx.Item == null, "Add to cart item already specified");
							ctx.Item = cartItem;
						}
						else
						{
							ctx.ChildItems.Add(cartItem);
						}
					}
				}
			}

			return warnings;
		}
        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 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");
        }
        public void Can_get_tax_total()
        {
            //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);

            _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);
        }