/// <returns>A task that represents the asynchronous operation</returns>
        public virtual async Task <IActionResult> EstimateShipping([FromQuery] ProductDetailsModel.ProductEstimateShippingModel model, IFormCollection form)
        {
            if (model == null)
            {
                model = new ProductDetailsModel.ProductEstimateShippingModel();
            }

            var errors = new List <string>();

            if (!_shippingSettings.EstimateShippingCityNameEnabled && string.IsNullOrEmpty(model.ZipPostalCode))
            {
                errors.Add(await _localizationService.GetResourceAsync("Shipping.EstimateShipping.ZipPostalCode.Required"));
            }

            if (_shippingSettings.EstimateShippingCityNameEnabled && string.IsNullOrEmpty(model.City))
            {
                errors.Add(await _localizationService.GetResourceAsync("Shipping.EstimateShipping.City.Required"));
            }

            if (model.CountryId == null || model.CountryId == 0)
            {
                errors.Add(await _localizationService.GetResourceAsync("Shipping.EstimateShipping.Country.Required"));
            }

            if (errors.Count > 0)
            {
                return(Json(new
                {
                    Success = false,
                    Errors = errors
                }));
            }

            var product = await _productService.GetProductByIdAsync(model.ProductId);

            if (product == null || product.Deleted)
            {
                errors.Add(await _localizationService.GetResourceAsync("Shipping.EstimateShippingPopUp.Product.IsNotFound"));
                return(Json(new
                {
                    Success = false,
                    Errors = errors
                }));
            }

            var wrappedProduct = new ShoppingCartItem()
            {
                StoreId            = (await _storeContext.GetCurrentStoreAsync()).Id,
                ShoppingCartTypeId = (int)ShoppingCartType.ShoppingCart,
                CustomerId         = (await _workContext.GetCurrentCustomerAsync()).Id,
                ProductId          = product.Id,
                CreatedOnUtc       = DateTime.UtcNow
            };

            var addToCartWarnings = new List <string>();

            //customer entered price
            wrappedProduct.CustomerEnteredPrice = await _productAttributeParser.ParseCustomerEnteredPriceAsync(product, form);

            //entered quantity
            wrappedProduct.Quantity = _productAttributeParser.ParseEnteredQuantity(product, form);

            //product and gift card attributes
            wrappedProduct.AttributesXml = await _productAttributeParser.ParseProductAttributesAsync(product, form, addToCartWarnings);

            //rental attributes
            _productAttributeParser.ParseRentalDates(product, form, out var rentalStartDate, out var rentalEndDate);
            wrappedProduct.RentalStartDateUtc = rentalStartDate;
            wrappedProduct.RentalEndDateUtc   = rentalEndDate;

            var result = await _shoppingCartModelFactory.PrepareEstimateShippingResultModelAsync(new[] { wrappedProduct }, model, false);

            return(Json(result));
        }
        public override async Task <IActionResult> AddProductToCart_Details(int productId, int shoppingCartTypeId, IFormCollection form)
        {
            var product = await _productService.GetProductByIdAsync(productId);

            if (product == null)
            {
                return(Json(new
                {
                    redirect = Url.RouteUrl("Homepage")
                }));
            }

            //we can add only simple products
            if (product.ProductType != ProductType.SimpleProduct)
            {
                return(Json(new
                {
                    success = false,
                    message = "Only simple products could be added to the cart"
                }));
            }

            //update existing shopping cart item
            var updatecartitemid = 0;

            foreach (var formKey in form.Keys)
            {
                if (formKey.Equals($"addtocart_{productId}.UpdatedShoppingCartItemId", StringComparison.InvariantCultureIgnoreCase))
                {
                    int.TryParse(form[formKey], out updatecartitemid);
                    break;
                }
            }

            ShoppingCartItem updatecartitem = null;

            if (_shoppingCartSettings.AllowCartItemEditing && updatecartitemid > 0)
            {
                //search with the same cart type as specified
                var cart = await _shoppingCartService.GetShoppingCartAsync(await _workContext.GetCurrentCustomerAsync(), (ShoppingCartType)shoppingCartTypeId, (await _storeContext.GetCurrentStoreAsync()).Id);

                updatecartitem = cart.FirstOrDefault(x => x.Id == updatecartitemid);
                //not found? let's ignore it. in this case we'll add a new item
                //if (updatecartitem == null)
                //{
                //    return Json(new
                //    {
                //        success = false,
                //        message = "No shopping cart item found to update"
                //    });
                //}
                //is it this product?
                if (updatecartitem != null && product.Id != updatecartitem.ProductId)
                {
                    return(Json(new
                    {
                        success = false,
                        message = "This product does not match a passed shopping cart item identifier"
                    }));
                }
            }

            var addToCartWarnings = new List <string>();

            //customer entered price
            var customerEnteredPriceConverted = await _productAttributeParser.ParseCustomerEnteredPriceAsync(product, form);

            //entered quantity
            var quantity = _productAttributeParser.ParseEnteredQuantity(product, form);

            //product and gift card attributes
            var attributes = await _productAttributeParser.ParseProductAttributesAsync(product, form, addToCartWarnings);

            // --------------------------------
            // ABC: add Home Delivery attribute
            // --------------------------------
            attributes = await _attributeUtilities.InsertHomeDeliveryAttributeAsync(product, attributes);

            //rental attributes
            _productAttributeParser.ParseRentalDates(product, form, out var rentalStartDate, out var rentalEndDate);

            var cartType = updatecartitem == null ? (ShoppingCartType)shoppingCartTypeId :
                           //if the item to update is found, then we ignore the specified "shoppingCartTypeId" parameter
                           updatecartitem.ShoppingCartType;

            await SaveItemAsync(updatecartitem, addToCartWarnings, product, cartType, attributes, customerEnteredPriceConverted, rentalStartDate, rentalEndDate, quantity);

            //return result
            return(await GetProductToCartDetails(addToCartWarnings, cartType, product));
        }