Sunday, 24 September 2017

Bulk Product Manage And Update

What we want to achieve?

  • We want to update all product details and its features along with its Image (In this example we taking 5 product at once) like as shown in figures below.
  • Product Features are sortable and can be reordered using drag and drop list up or down.






Step 1 : In controller we have action methods to update products, its features, delete product and delete its feature. 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using SampleMvc.Helpers;
using SampleMvc.Models;
using SampleMvc.DA;
using Newtonsoft.Json;
using System.IO;

namespace SampleMvc.Controllers
{
    public class ManageProductController : Controller
    {
        public ActionResult Products()
        {
            ListProductCategoryAndFeature listProductCategoryAndFeature = new ListProductCategoryAndFeature();
            List<Product> product = new List<Product>();
            List<ProductFeature> productfeatures = new List<ProductFeature>();
            ProductDA productDA = new ProductDA();
            product = productDA.ReadAllProducts();
            listProductCategoryAndFeature = productDA.ReadAllProductFeaturesAndCategory();
            ViewBag.ProductFeatures = listProductCategoryAndFeature.productfeatures;
            ViewBag.ProductCategories = listProductCategoryAndFeature.productcategories;
            return View(product);
        }

        [HttpPost]
        public ActionResult Products(string product)
        {
            var listProducts = JsonConvert.DeserializeObject<List<Product>>(product);

            foreach (var prod in listProducts)
            {
                ProductDA productDA = new ProductDA();
                productDA.UpdateProductAndFeatures(prod);
            }
            return Json(new { Success = true });
        }
        public ActionResult UpdateImage()
        {
            string imgName = string.Empty;
            ProductDA productDA = new ProductDA();
            if (System.Web.HttpContext.Current.Request.Files.AllKeys.Any())
            {
                for (int i = 0; i < System.Web.HttpContext.Current.Request.Files.Count; i++)
                {
                    var pic = System.Web.HttpContext.Current.Request.Files[i];
                    var pid = System.Web.HttpContext.Current.Request.Form[i];

                    if (pic.ContentLength > 0)
                    {
                        var fileName = Path.GetFileName(pic.FileName);
                        var ext = Path.GetExtension(pic.FileName);

                        if (ext == ".jpg" || ext == ".png" || ext == ".jpeg" || ext == ".gif")
                        {
                            string imgPath = "";
                            imgPath = "/ProductImages/" + pid + ext;

                            Product prod = new Product();
                            prod.ProductId = Convert.ToInt32(pid);
                            prod.ImagePath = imgPath;
                            productDA.UpdateProductImage(prod);

                            // TODO : Crop Image before saving
                            string path = Server.MapPath(imgPath);
                            pic.SaveAs(path);
                        }
                    }
                }
            }
            return Json(new { Success = true });
        }

        [HttpPost]
        public ActionResult DeleteProduct(Product product)
        {
            ProductDA productDA = new ProductDA();
            productDA.DeleteProduct(product);
            return Json(new { Success = true });
        }
        [HttpPost]
        public ActionResult DeleteProductFeature(int fid)
        {
            ProductDA productDA = new ProductDA();
            productDA.DeleteProductFeature(fid);
            return Json(new { Success = true });
        }      
    }

}


Step 2 : In Views :

@{
    ViewBag.Title = "Manage Product";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

@model List<SampleMvc.Models.Product>
@using SampleMvc.Models;


<div class="container-fluid">
    <div class="row content">
        <div class="col-sm-3 sidenav">
            <h4>Manage Products</h4>
            <ul class="nav nav-pills nav-stacked">
                <li class="active"><a href="#section1">Home</a></li>
                <li><a href="/Product/Product">Bulk Product Upload</a></li>
                <li><a href="/ManageProduct/Products">Manage Products</a></li>
                <li><a href="#section3">Photos</a></li>
            </ul>
            <br>
            <div class="input-group">
                <input type="text" class="form-control" placeholder="Search Blog..">
                <span class="input-group-btn">
                    <button class="btn btn-default" type="button">
                        <span class="glyphicon glyphicon-search"></span>
                    </button>
                </span>
            </div>
        </div>

        <div class="col-sm-9">
            <h4><small>Manage Products and its features.</small></h4>
            <hr>
            <div class="col-sm-12">
                @foreach (var product in Model)
                {
                    <div id="@string.Format("product{0}", product.ProductId)" class="manageProduct" pid="@product.ProductId">
                        <div class="row">
                            <div class="col-sm-3">
                                @(Model.IndexOf(product) + 1) )
                                    Product Name :
                            </div>
                            <div class="col-sm-6">
                                <input type="text" class="form-control" id="@string.Format("txtProductName{0}", product.ProductId)" value="@product.ProductName" />
                                <div id="@string.Format("errProductName{0}", product.ProductId)" class="divErr"></div>
                            </div>

                            <div class="col-sm-3">
                                <a href="javascript:;" class ="btn btn-danger delProduct" title="Delete Product" pid="@product.ProductId">Delete</a>
                            </div>
                        </div>
                        <br />
                        <div class="row">
                            <div class="col-sm-3">
                                Product Features :
                            </div>
                            <div class="col-sm-6">
                                <ul class="list-group" id="@string.Format("choiceList{0}", product.ProductId)">
                                    @foreach (var feature in (List<ProductFeature>)ViewBag.ProductFeatures)
                                    {
                                        if (feature.ProductId == product.ProductId)
                                        {
                                        <li class="list-group-item list-group-item-info" id="@string.Format("li{0}", feature.FeatureId)">
                                            <div class="divOption fwidth">
                                                <div class="divAnsOption hwidth">
                                                    <label class="rdAnsOption" type="text" name="choice" fid="@feature.FeatureId" value="@feature.ProdFeature" /> @feature.ProdFeature
                                                </div>
                                                <div class="divOrderBy">
                                                    <input type="text" class="rdAnsOrder form-control" value="@feature.OrderBy" style="width:44px"/>
                                                </div>
                                                <div class="divRemoveOpt"><a href="javascript:;" class="delChoice" fid="@feature.FeatureId" title="Remove Choice">X</a></div>
                                            </div>
                                        </li>
                                        }
                                    }
                                </ul>

                                <input type="button" id="@string.Format("btnAddFeature{0}", @product.ProductId)" class="btn btn-primary" value="Add Feature"/>
                                <div class="divAddOption" id="@string.Format("divAddOpt{0}", @product.ProductId)">
                                    Enter Feature :
                                                <input id="@string.Format("txtFeature{0}", @product.ProductId)" type="text" class="form-control" required="required" />
                                    <div id="@string.Format("errFeature{0}", @product.ProductId)" class="divErr"></div><br />
                                    <input type="button" id="@string.Format("btnAddOpt", @product.ProductId)" class="btn btn-primary" value="Add" />
                                </div>
                            </div>
                            <div class="col-sm-3">
                            </div>
                        </div>
                        <br />
                        <div class="row">
                            <div class="col-sm-3">
                                Product Category :
                            </div>
                            <div class="col-sm-6">
                                <select id="@string.Format("drpProductCategory{0}", product.ProductId)" class="form-control">
                                    <option value="0">Select Category</option>
                                    @foreach (var m in @ViewBag.ProductCategories)
                                    {
                                        if (m.CategoryId == product.CategoryId)
                                        {
                                        <option value= "@m.CategoryId" selected>@m.CategoryName </option>
                                        }
                                        else
                                        {
                                        <option value= "@m.CategoryId">@m.CategoryName </option>
                                        }


                                    }
                                </select>
                                 <div id="@string.Format("errProductCategory{0}", product.ProductId)" class="divErr"></div>
                            </div>
                            <div class="col-sm-3">
                            </div>

                        </div>
                        <br />
                        <div class="row">
                            <div class="col-sm-3">
                                Quantity Per Unit :
                            </div>
                            <div class="col-sm-6">
                                <input type="text" class="form-control" id="@string.Format("txtQuantityPerUnit{0}", product.ProductId)" value="@Convert.ToInt32(product.QuantityPerUnit)" />
                            </div>
                            <div class="col-sm-3">
                            </div>
                        </div>
                        <br />
                        <div class="row">
                            <div class="col-sm-3">
                                Unit Price :
                            </div>
                            <div class="col-sm-6">
                                <input type="text" class="form-control" id="@string.Format("txtUnitPrice{0}", product.ProductId)" value="@Convert.ToInt32(product.UnitPrice)" />
                            </div>
                            <div class="col-sm-3">
                            </div>
                        </div>
                        <br />
                        <div class="row">
                            <div class="col-sm-3">
                                Product Image :
                            </div>
                            <div class="col-sm-6">
                                <input type="file" name="postedFile" id="@string.Format("imgProductImageUrl{0}", product.ProductId)" />
                            </div>
                            <div class="col-sm-3">
                                <img src="@Url.Content((string.IsNullOrEmpty(product.ImagePath) == true ? "/Images/noimage.png" : product.ImagePath))" alt="@product.ProductName" width="150px" />
                            </div>
                        </div>
                        <br />
                    </div>
                }
                <input type="button" value="Save" id="btnSaveProduct" class="btn btn-primary" />
            </div>
        </div>
    </div>
</div>


@section scripts {
    <script src="~/Scripts/ManageProducts.js"></script>
}



Step 3 : In Javascript File ManageProducts.js :

var productManage = {

    init: function () {
        $(".manageProduct").each(function () {
            var id = $(this).attr("pid");
            var divQues = '#ques' + id;

            $("#choiceList" + id).sortable({
                stop: function (event, ui) {
                    var sort = $('#choiceList' + id).find('input[type="text"]');
                    sort.each(function (index) {
                        $(this).val((index + 1));
                    });
                }
            });

            $("#btnAddFeature" + id).click(function () {
                $("#divAddOpt" + id).show();
                $("#btnAddFeature" + id).hide();
            });

            $("#txtBaseValue" + id).keypress(function (event) {
                $("#errBaseValue" + id).html("");
                var key = event.which;
                if (!(key >= 48 && key <= 57)) {
                    event.preventDefault();
                    $("#errBaseValue" + id).html("Only numeric value is allow.");
                }
            });

            // Add choice
            var addChoice = function (input) {
                var val = $.trim($(input).val());
                $("#choiceList" + id).append('<li class="list-group-item list-group-item-info" id="li' + id + '"><div class="divOption fwidth"><div class="divAnsOption hwidth"><label class="rdAnsOption" type="text" name="choice" fid="0" value="' + val + '" t="N" >' + val + '</label></div><div class="divOrderBy"><input type="text" class="rdAnsOrder form-control" style="width:44px" value="' + ($("#choiceList" + id + " li").length + 1) + '"/></div><div class="divRemoveOpt"><a href="javascript:;" class="delChoice" title="Remove Choice">X</a></div></div></li>');

                $(input).val("");

                $(".delChoice").click(function () {
                    var fid = $(this).attr("fid");
                    if (!fid) {
                        $(this).parent().parent().parent().remove();
                        productManage.sortOptions(id);
                    }
                });
            };

            $(".delChoice").click(function () {
                var fid = $(this).attr("fid");
                if ($("#dialogdelete")) $("#dialogdelete").remove();
                $("body").append("<div id='dialogdelete'>Do you want to delete this feature?</div>");
                $("#dialogdelete").dialog({
                    modal: true,
                    title: 'Delete Feature',
                    width: 400,
                    buttons: {
                        Delete: function () {
                            alert(fid);
                            $(this).dialog("close");
                            $.ajax({
                                data: { fid: fid },
                                type: "POST",
                                url: "/ManageProduct/DeleteProductFeature",
                                success: function (data) {
                                    if (data.Success) {
                                        $("#li" + fid).remove();
                                        productManage.sortOptions(id);
                                    }
                                }
                            });
                        },
                        Cancel: function () {
                            $(this).dialog("close");
                        }
                    }
                });

            });

            $("#txtFeature" + id).change(function () {
                $("#errFeature" + id).html("");

                if ($("#choiceList" + id + " li").length == 0) {
                    addChoice(this);
                }
                else {
                    var count = 0;
                    $("#choiceList" + id + " label").each(function (i) {
                        var addFeature = $.trim($(this).text());                    
                        if ($.trim($("#txtFeature" + id).val()).toLowerCase() == addFeature.toLowerCase()) {
                            count = count + 1;
                        }
                        else {
                        }
                    });
                    if (count == 0) {
                        addChoice($("#txtFeature" + id));
                    }
                    else {
                        $("#errFeature" + id).html("Feature already added.");
                        $("#txtFeature" + id).focus();
                    }
                }
            });

         // This section has not been implemented here.
            $("#drpFeatureType" + id).change(function () {
                if ($("#drpFeatureType" + id + " option:selected").text() == "Text") {
                    if ($("#dialogdelete")) $("#dialogdelete").remove();
                    $("body").append("<div id='dialogdelete'>Do you want to delete all Feature?</div>");
                    $("#dialogdelete").dialog({
                        modal: true,
                        title: 'Delete All Feature',
                        width: 400,
                        buttons: {
                            Delete: function () {
                                $(this).dialog("close");
                                $.ajax({
                                    data: { pid: id },
                                    type: "POST",
                                    url: "/ManageProduct/DeleteAllFeature",
                                    success: function (data) {
                                        if (data.Success) {
                                            $("#choiceList" + id).empty();
                                            $("#btnAddFeature" + id).hide();
                                            productManage.sortOptions(id);
                                        }
                                    }
                                });
                            },
                            Cancel: function () {
                                $(this).dialog("close");
                                $("#drpQuestionType" + id).val("R");
                            }
                        }
                    });
                }
                else {
                    $("#btnAddFeature" + id).show();
                }
            });
        });
    },

    validateProduct: function () {
        var counterr = 0;
        $(".manageProduct").each(function () {
            counterr = 0;
            var id = $(this).attr("pid");
            $('span[id^="spnMsg"]').html("");
            $('div[id^="err"]').html("");
            if ($("#txtProductName" + id).val() == "") {
                $("#txtProductName" + id).focus();
                $("#errProductName" + id).html("Please enter Product Name.");
                counterr++;
                return false;
            }

            else if ($("#drpProductCategory" + id + " option:selected").val() == 0) {
                $("#drpProductCategory" + id).focus();
                $("#errProductCategory" + id).html("Please select Product Category.");
                counterr++;
                return false;
            }
        });

        if (counterr > 0)
            return false;
        else
            return true;
    },

    sortOptions: function (id) {
        var sort = $('#choiceList' + id).find('input[type="text"]');
        sort.each(function (index) {
            $(this).val((index + 1));
        });
    }
};

$("#btnSaveProduct").click(function () {
    if (productManage.validateProduct()) {
        jsonProductObj = [];
        var data = new FormData();

        $(".manageProduct").each(function () {
            var id = $(this).attr("pid");

            product = {}
            product["ProductId"] = id;
            product["ProductName"] = $("#txtProductName" + id).val();
            product["CategoryId"] = $("#drpProductCategory" + id + " option:selected").val();
            product["QuantityPerUnit"] = $("#txtQuantityPerUnit" + id).val();
            product["UnitPrice"] = $("#txtUnitPrice" + id).val();
            //product["Discounted"] = $("#txtDiscount" + id).is(":checked");

            var count = 0;
            var ProductFeature = "";
            $('#choiceList' + id + ' label').each(function (index) {
                ProductFeature = ProductFeature + $(this).attr('fid') + "+|+" + $(this).attr('value') + "+|+" + $(this).is(':checked') + "+|+" + $(this).parent().parent().find('input:text').val() + "*|*";

                if ($(this).is(':checked'))
                    count = count + 1;
            });

            product["ProductFeature"] = ProductFeature;
            jsonProductObj.push(product);

            // Adding  images to formdata
            var files = $("#imgProductImageUrl" + id).get(0).files;
            if (files.length > 0) {
                data.append("MyImages" + id, files[0]);
                data.append("Pid" + id, id);

            }
        });

        $.ajax({
            type: 'post',
            dataType: 'json',
            url: '/ManageProduct/Products',
            data: { "product": JSON.stringify(jsonProductObj) },
            success: function (json) {
                $.ajax({
                    url: "/ManageProduct/UpdateImage",
                    type: "POST",
                    processData: false,
                    contentType: false,
                    data: data,
                    success: function (response) {
                        window.location.href = '/ManageProduct/Products';
                    }
                });
            },
        });
    }
});

$(".delProduct").click(function () {
    var product = {};
    product.ProductId = $(this).attr("pid");
    if ($("#dialogdelete")) $("#dialogdelete").remove();
    $("body").append("<div id='dialogdelete'>Do you want to delete this Product?</div>");
    $("#dialogdelete").dialog({
        modal: true,
        title: 'Delete Product',
        width: 400,
        buttons: {
            Delete: function () {
                $(this).dialog("close");
                alert(product.ProductId);
                $.ajax({
                    url: '/ManageProduct/DeleteProduct',
                    method: 'post',
                    data: '{product: ' + JSON.stringify(product) + '}',
                    contentType: "application/json; charset=utf-8",
                    success: function () {
                        window.location.reload(true);
                    },
                    error: function (err) {

                    }
                });
            },
            Cancel: function () {
                $(this).dialog("close");
            }
        }
    });
});

$(document).ready(function () {
    productManage.init();

});

Step 4 : In DataAccess Class Library :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SampleMvc.Models;
using System.Data.SqlClient;

namespace SampleMvc.DA
{
    public class ProductDA : SQLHelper
    {
        public void AddProductAndFeatures(Product product, string productFeature)
        {
            try
            {
                int i = ExecNonQueryProc("Usp_InsertProductAndFeatures",
                new SqlParameter("@ProductName", product.ProductName),
                new SqlParameter("@CategoryId", product.CategoryId),
                new SqlParameter("@ProductFeatures", productFeature));
            }
            catch
            {
                throw;
            }
            finally
            {
                CloseConnection();
            }
        }

        public List<Product> ReadAllProducts()
        {
            List<Product> products = new List<Product>();
            SqlDataReader rdr = ExecDataReaderProc("Sp_ReadAllProducts");
            while (rdr.Read())
            {
                Product product = new Product();
                product.ProductId = Convert.ToInt32(rdr["ProductId"]);
                product.ProductName = Convert.ToString(rdr["ProductName"]);
                product.CategoryId = Convert.ToInt32(rdr["CategoryId"]);
                //product.QuantityPerUnit = Convert.ToInt32(rdr["QuantityPerUnit"]);
                //product.UnitPrice = Convert.ToInt32(rdr["UnitPrice"]);
                product.ImagePath = Convert.ToString(rdr["ImagePath"]);
                //product.UnitsInStock = Convert.ToInt32(rdr["UnitsInStock"]);
                //product.Discounted = Convert.ToBoolean(rdr["Discounted"]);
                //product.CreateOn = Convert.ToDateTime(rdr["CreateOn"]);

                products.Add(product);
            }
            CloseConnection();
            return products;

        }

       // Returning multiple result set from procedure.
        public ListProductCategoryAndFeature ReadAllProductFeaturesAndCategory()
        {
            ListProductCategoryAndFeature listProductCategoryAndFeature = new ListProductCategoryAndFeature();
            List<ProductFeature> productfeatures = new List<ProductFeature>();
            List<ProductCategory> productcategories = new List<ProductCategory>();
            SqlDataReader rdr = ExecDataReaderProc("Sp_ReadAllProductFeatures");
            while (rdr.Read())
            {
                ProductFeature productfeature = new ProductFeature();
                productfeature.ProductId = Convert.ToInt32(rdr["ProductId"]);
                productfeature.ProdFeature = Convert.ToString(rdr["ProductFeature"]);
                productfeature.FeatureId = Convert.ToInt32(rdr["FeatureId"]);

                productfeatures.Add(productfeature);
            }
         
           // Next Result
            rdr.NextResult();
            while (rdr.Read())
            {
                ProductCategory productcategory = new ProductCategory();
                productcategory.CategoryId = Convert.ToInt32(rdr["CategoryId"]);
                productcategory.CategoryName = Convert.ToString(rdr["CategoryName"]);

                productcategories.Add(productcategory);
            }

            listProductCategoryAndFeature.productfeatures = productfeatures;
            listProductCategoryAndFeature.productcategories = productcategories;
            CloseConnection();
            return listProductCategoryAndFeature;
        }

        public void UpdateProductAndFeatures(Product product)
        {
            try
            {
                int i = ExecNonQueryProc("Usp_UpdateProductsAndFeatures",
                new SqlParameter("@ProductId", product.ProductId),
                new SqlParameter("@ProductName", product.ProductName),
                new SqlParameter("@CategoryId", product.CategoryId),
                new SqlParameter("@QuantityPerUnit", product.QuantityPerUnit),
                new SqlParameter("@UnitPrice", product.UnitPrice),
                new SqlParameter("@ProductFeatures", product.ProductFeature));
            }
            catch
            {
                throw;
            }
            finally
            {
                CloseConnection();
            }
        }


        public void UpdateProductImage(Product product)
        {
            try
            {
                int i = ExecNonQueryProc("SP_UpdateProductImage",
                new SqlParameter("@ProductId", product.ProductId),
                new SqlParameter("@ImagePath", product.ImagePath));
            }
            catch
            {
                throw;
            }
            finally
            {
                CloseConnection();
            }
        }

        public void DeleteProduct(Product product)
        {
            try
            {
                int i = ExecNonQueryProc("SP_DeleteProduct",
                new SqlParameter("@ProductId", product.ProductId));
            }
            catch
            {
                throw;
            }
            finally
            {
                CloseConnection();
            }
        }
         public void DeleteProductFeature(int fid)
        {
            try
            {
                int i = ExecNonQueryProc("SP_DeleteProductFeature",
                new SqlParameter("@FeatureId", fid));
            }
            catch
            {
                throw;
            }
            finally
            {
                CloseConnection();
            }
        }    
    }

}


Step 5 : In Models :

Product.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SampleMvc.Models
{
    public class Product
    {
        public int ProductId { get; set; }
        public int CategoryId { get; set; }
        public string ProductName { get; set; }
        public string ProductFeature { get; set; }
        public DateTime CreateOn { get; set; }

        public bool Discounted { get; set; }
        public int UnitsInStock { get; set; }
        public int QuantityPerUnit { get; set; }
        public int UnitPrice { get; set; }
        public string ImagePath { get; set; }
    }

}

ProductFeature.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SampleMvc.Models
{
    public class ProductFeature
    {
        public int FeatureId { get; set; }
        public int ProductId { get; set; }
        public string ProdFeature { get; set; }
        public int OrderBy { get; set; }
    }
}

ListProductCategoryAndFeature.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SampleMvc.Models
{
    public class ListProductCategoryAndFeature
    {
        public List<ProductFeature> productfeatures { get; set; }
        public List<ProductCategory> productcategories { get; set; }
    }

}

Step 6: In Database Procedures and Functions along with tables :

Table 01 : 

CREATE TABLE [dbo].[ProductFeatures](
[FeatureId] [int] IDENTITY(1,1) NOT NULL,
[ProductId] [int] NULL,
[ProductFeature] [nvarchar](max) NULL,
[Status] [bit] NOT NULL,
[IsDeleted] [bit] NOT NULL,
[CreatedOn] [date] NULL,
[OrderBy] [int] NULL,
 CONSTRAINT [PK_ProductFeatures] PRIMARY KEY CLUSTERED
(
[FeatureId] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

ALTER TABLE [dbo].[ProductFeatures] ADD  CONSTRAINT [DF_ProductFeatures_Status]  DEFAULT ((1)) FOR [Status]
GO


ALTER TABLE [dbo].[ProductFeatures] ADD  CONSTRAINT [DF_ProductFeatures_IsDeleted]  DEFAULT ((0)) FOR [IsDeleted]



Table 02 : 

CREATE TABLE [dbo].[Products](
[ProductID] [int] IDENTITY(1,1) NOT NULL,
[ProductName] [nvarchar](40) NOT NULL,
[SupplierID] [int] NULL,
[CategoryID] [int] NULL,
[QuantityPerUnit] [nvarchar](20) NULL,
[UnitPrice] [money] NULL,
[UnitsInStock] [smallint] NULL,
[UnitsOnOrder] [smallint] NULL,
[ReorderLevel] [smallint] NULL,
[Discontinued] [bit] NOT NULL,
[CreatedOn] [date] NULL,
[ImagePath] [nvarchar](max) NULL,
 CONSTRAINT [PK_Products] PRIMARY KEY CLUSTERED
(
[ProductID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

) ON [PRIMARY]



Function 01 :

CREATE FUNCTION [dbo].[func_Split]
    (  
    @DelimitedString    varchar(8000),
    @Delimiter              varchar(100)
    )
RETURNS @tblArray TABLE
    (
    ElementID   int IDENTITY(1,1),  -- Array index
    Element     varchar(1000)               -- Array element contents
    )
AS
BEGIN

    -- Local Variable Declarations
    -- ---------------------------
    DECLARE @Index      smallint,
                    @Start      smallint,
                    @DelSize    smallint

    SET @DelSize = LEN(@Delimiter)

    -- Loop through source string and add elements to destination table array
    -- ----------------------------------------------------------------------
    WHILE LEN(@DelimitedString) > 0
    BEGIN

        SET @Index = CHARINDEX(@Delimiter, @DelimitedString)

        IF @Index = 0
            BEGIN

                INSERT INTO
                    @tblArray
                    (Element)
                VALUES
                    (LTRIM(RTRIM(@DelimitedString)))

                BREAK
            END
        ELSE
            BEGIN

                INSERT INTO
                    @tblArray
                    (Element)
                VALUES
                    (LTRIM(RTRIM(SUBSTRING(@DelimitedString, 1,@Index - 1))))

                SET @Start = @Index + @DelSize
                SET @DelimitedString = SUBSTRING(@DelimitedString, @Start , LEN(@DelimitedString) - @Start + 1)

            END
    END

    RETURN

END

Procedure 01 :  
   
CREATE PROCEDURE [dbo].[SP_UpdateProductFeatures]    
    (  
   @PId INT,  
   @CreatedDate DATETIME,  
   @element    VARCHAR(MAX),  
   @delimiter VARCHAR(MAX)    
    )    
AS    
BEGIN    
 DECLARE @result BIT = 0;    
 DECLARE @fId INT;
   
   SET @fId= (SELECT Element FROM dbo.func_split(@element, '+|+') where ElementID = 1)

   IF (@fId = 0)
   BEGIN  
  ;WITH opt AS(    
   SELECT * FROM dbo.func_split(@element, '+|+')    
   )    
 
  INSERT INTO ProductFeatures              
  (    
   ProductId,  
   CreatedOn,  
   ProductFeature,        
   OrderBy    
  )              
    VALUES              
    (    
 @PId,
 GETDATE(),            
 (select Element from opt where elementId = 2),                  
 (select Element from opt where elementId = 4)    
    )      
    END
 ELSE
 BEGIN
  ;WITH opt AS(    
   SELECT * FROM dbo.func_split(@element, '+|+')    
   )    
  UPDATE ProductFeatures
  SET ProductFeature = (select Element from opt where elementId = 2),
  OrderBy = (select Element from opt where elementId = 4)
  WHERE FeatureId=@fId

 END
    RETURN @result    
END

Procedure 02 :

CREATE PROC [dbo].[Usp_UpdateProductsAndFeatures]
@ProductId INT,
@ProductName NVARCHAR(MAX),
@CategoryId INT,
@QuantityPerUnit INT,
@UnitPrice INT,
@ProductFeatures VARCHAR(MAX)
AS
BEGIN
   DECLARE @PId INT
   DECLARE @Feature VARCHAR(MAX)

   Update Products
   Set
   ProductName = @ProductName,
   CategoryID = @CategoryId,
   QuantityPerUnit = @QuantityPerUnit,
   UnitPrice = @UnitPrice
   Where ProductID=@ProductId

  SET @PId= @ProductId;

  DECLARE @ExecQuery NVARCHAR(MAX);
  SELECT @ExecQuery  = COALESCE(@ExecQuery +';' ,'') + 'EXEC [dbo].[SP_UpdateProductFeatures] '+CAST(@PId AS VARCHAR)+', '''+ CAST(GETDATE() AS VARCHAR) +''', ''' + Element+''', ''+|+'''
  FROM dbo.func_split(@ProductFeatures, '*|*');

  EXECUTE sp_executesql @ExecQuery;


END


Procedure 03 :

Create Proc [dbo].[SP_DeleteProductFeature]
@FeatureId INT
AS
BEGIN
Delete ProductFeatures
Where FeatureId=@FeatureId

END


Friday, 15 September 2017

Bulk Product Upload Using Excel Sheet

What we want to achieve?
  • We want to upload product and its features using Excel Sheet. 
  • Excel sheet should be validated as per our sample data. 
  • If the Excel sheet is invalid list of errors should be display with link to that particular error so that user can reach there by clicking on link itself. 
  • Also we should be able to correct invalid excel sheet on the screen upload it again.
Sample Excel : 


Screen 01 : When we upload Invalid Excel Sheet.



Screen 02 : When we click on error link then that particular error will be focused.


Screen 03 : When we click on error link then that particular error will be focused.



Screen 04: When we correct all errors and upload again.


Step 01 : We have two tables : Products and ProductFeatures

Table Products 
CREATE TABLE [dbo].[Products](
[ProductID] [int] IDENTITY(1,1) NOT NULL,
[ProductName] [nvarchar](40) NOT NULL,
[SupplierID] [int] NULL,
[CategoryID] [int] NULL,
[QuantityPerUnit] [nvarchar](20) NULL,
[UnitPrice] [money] NULL,
[UnitsInStock] [smallint] NULL,
[UnitsOnOrder] [smallint] NULL,
[ReorderLevel] [smallint] NULL,
[Discontinued] [bit] NOT NULL,
[CreatedOn] [date] NULL,
 CONSTRAINT [PK_Products] PRIMARY KEY CLUSTERED
(
[ProductID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

Table ProductFeatures
CREATE TABLE [dbo].[ProductFeatures](
[FeatureId] [int] IDENTITY(1,1) NOT NULL,
[ProductId] [int] NULL,
[ProductFeature] [nvarchar](max) NULL,
[Status] [bit] NOT NULL,
[IsDeleted] [bit] NOT NULL,
[CreatedOn] [date] NULL,
 CONSTRAINT [PK_ProductFeatures] PRIMARY KEY CLUSTERED
(
[FeatureId] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

Step 02 : We have got function and Procedures

Function to Split string using delimiter.
CREATE FUNCTION [dbo].[func_Split]
    (  
    @DelimitedString    varchar(8000),
    @Delimiter              varchar(100)
    )
RETURNS @tblArray TABLE
    (
    ElementID   int IDENTITY(1,1),  -- Array index
    Element     varchar(1000)               -- Array element contents
    )
AS
BEGIN

    -- Local Variable Declarations
    -- ---------------------------
    DECLARE @Index      smallint,
                    @Start      smallint,
                    @DelSize    smallint

    SET @DelSize = LEN(@Delimiter)

    -- Loop through source string and add elements to destination table array
    -- ----------------------------------------------------------------------
    WHILE LEN(@DelimitedString) > 0
    BEGIN

        SET @Index = CHARINDEX(@Delimiter, @DelimitedString)

        IF @Index = 0
            BEGIN

                INSERT INTO
                    @tblArray
                    (Element)
                VALUES
                    (LTRIM(RTRIM(@DelimitedString)))

                BREAK
            END
        ELSE
            BEGIN

                INSERT INTO
                    @tblArray
                    (Element)
                VALUES
                    (LTRIM(RTRIM(SUBSTRING(@DelimitedString, 1,@Index - 1))))

                SET @Start = @Index + @DelSize
                SET @DelimitedString = SUBSTRING(@DelimitedString, @Start , LEN(@DelimitedString) - @Start + 1)

            END
    END

    RETURN
END

Procedure 01:

CREATE PROC [dbo].[Usp_InsertProductAndFeatures]
@ProductName NVARCHAR(MAX),
@CategoryId INT,
@ProductFeatures VARCHAR(MAX)
AS
BEGIN
   DECLARE @PId INT
   DECLARE @Feature VARCHAR(MAX)

   INSERT Products
   (
   ProductName,
   CategoryID,
   CreatedOn
    )
   VALUES
   (
   @ProductName,
   @CategoryId,
   GETDATE()
    );

  SET @PId= @@IDENTITY;

  DECLARE @ExecQuery NVARCHAR(MAX);
  SELECT @ExecQuery  = COALESCE(@ExecQuery +';' ,'') + 'EXEC [dbo].[Usp_AddProductFeatures] '+CAST(@PId AS VARCHAR)+', '''+ CAST(GETDATE() AS VARCHAR) +''', ''' + Element+''', ''+|+'''
  FROM dbo.func_split(@ProductFeatures, '*|*');

  EXECUTE sp_executesql @ExecQuery;

END

Procedure 02:

CREATE PROCEDURE [dbo].[Usp_AddProductFeatures]  
     (  
   @PId INT,  
   @CreatedDate DATETIME,  
   @element    VARCHAR(MAX),  
   @delimiter VARCHAR(MAX)  
     )  
AS  
BEGIN  
  DECLARE @result BIT = 0;  
 
  ;WITH prodFeature AS(  
   SELECT * FROM dbo.func_split(@element, '+|+')  
  )  
 
     INSERT INTO ProductFeatures  
  (  
   ProductId,  
   CreatedOn,  
   ProductFeature  
  )  
    VALUES  
    (  
     @PId,  
     @createdDate,  
     (select Element from prodFeature where elementId = 1)  
    )  
     RETURN @result  

END

Step 03 : We have got models in SampleMvc.Models :

1.) Product.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SampleMvc.Models
{
    public class Product
    {
        public int ProductId { get; set; }
        public int CategoryId { get; set; }
        public string ProductName { get; set; }
        public string ProductFeature { get; set; }
        public DateTime CreateOn { get; set; }
    }

}

2.) ProductExcel.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SampleMvc.Models
{
    public class ProductExcel
    {
        public int ProductId { get; set; }
        public string PNameAndFeature { get; set; }
    }
}

3) ProductExcelRowCheck.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SampleMvc.Models
{
    public class ProductExcelRowCheck
    {
        public int ProductId { get; set; }
        public string ProductName { get; set; }
        public List<ExcelProductFeatureChk> PFeature { get; set; }
        public bool IsBlockDone { get; set; }
    }
}

4) ExcelProductFeatureChk.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SampleMvc.Models
{
    public class ExcelProductFeatureChk
    {
        public int FID { get; set; }
        public string ProductFreature { get; set; }
    }
}

Step 04 : We have got methods in SampleMvc.DA:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SampleMvc.Models;
using System.Data.SqlClient;

namespace SampleMvc.DA
{
    public class ProductDA : SQLHelper
    {
        public void AddProductAndFeatures(Product product, string productFeature)
        {
            try
            {
                int i = ExecNonQueryProc("Usp_InsertProductAndFeatures",
                new SqlParameter("@ProductName", product.ProductName),
                new SqlParameter("@CategoryId", product.CategoryId),
                new SqlParameter("@ProductFeatures", productFeature));
            }
            catch
            {
                throw;
            }
            finally
            {
                CloseConnection();
            }
        }
    }

}

Step 05 : We have got methods in SampleMvc.Helpers :

FlowHelper.cs :

using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Script.Serialization;

namespace SampleMvc.Helpers
{
    public static class FlowHelper
    {
        public static bool CreateDirectoryIfNotExists(string folderPath)
        {
            try
            {
                bool folderExists = Directory.Exists((folderPath));
                if (!folderExists)
                {
                    Directory.CreateDirectory((folderPath));
                    return true;
                }
                else
                {
                    return true;
                }
            }
            catch
            {
                throw;
            }
        }
        public static bool TryToDeleteFile(string filePath)
        {
            try
            {
                if (File.Exists(filePath))
                {
                    File.Delete(filePath);
                    return true;
                }
                else
                {
                    return false;
                }
            }
            catch
            {
                throw;
            }
        }
    }
}

SessionHelper.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using SampleMvc.Models;

namespace SampleMvc.Helpers
{
    public class SessionHelper
    {
        public Product Product
        {
            get
            {
                return HttpContext.Current.Session["Productinfo"] as Product;
            }
            set
            {
                HttpContext.Current.Session["Productinfo"] = value;
            }
        }
    }
}

Step 06 : In ProductController Class :

using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using SampleMvc.Helpers;
using SampleMvc.Models;
using SampleMvc.DA;
using ExcelDataReader;

namespace SampleMvc.Controllers
{
    public class ProductController : Controller
    {
        public ActionResult Product()
        {
            return View();
        }
        [HttpPost]
        public ActionResult Product(HttpPostedFileBase postedFile)
        {
            string ext = Path.GetExtension(postedFile.FileName);
            if (ext == ".xls" || ext == ".xlsx")
            {
                string folderPath = Server.MapPath("/BulkUpload");
                FlowHelper.CreateDirectoryIfNotExists(folderPath);

                string filePath = "";
                filePath = "/BulkUpload/" + "QA" + ext;
                string path = Server.MapPath(filePath);
                postedFile.SaveAs(path);
                // Install ExcelDataReader 3.1.0 and ExcelDataReader.DataSet 3.1.0 : Use command to do so as given below.
                // Install-Package ExcelDataReader -Version 3.1.0
                // Install-Package ExcelDataReader.DataSet -Version 3.1.0
                // Reading excel file using excel data reader.
                using (var stream = System.IO.File.Open(path, FileMode.Open, FileAccess.Read))
                {
                    using (var reader = ExcelReaderFactory.CreateReader(stream))
                    {
                        DataSet result = reader.AsDataSet();
                        DataTable dt = result.Tables[0];

                        if (ValidateExcelFile(dt))
                        {
                            // Save Product and features.
                            CreateValidatedExcelFile(dt);
                        }
                        else
                        {
                            // Invalid excel action.
                            return View(dt);
                        }
                    }
                }
                // Delete uploaded excel file after bulk upload.
                bool fileDeleted = FlowHelper.TryToDeleteFile(path);
                TempData["ErrorLists"] = null;
            }
            return View();
        }

        [HttpPost]
        public JsonResult ValidatedProductExcelUpload(List<ProductExcel> bulkExcelList)
        {
            DataTable dt = new DataTable();
            dt = ConvertListToDataTable(bulkExcelList);
            if (ValidateExcelFile(dt))
            {
                // Save Product and features.
                CreateValidatedExcelFile(dt);
            }

            string json = string.Empty;
            List<string> errList = TempData["ErrorLists"] as List<string>;
            if (errList != null && errList.Count > 0)
            {
                json = string.Format("<h3 id=\"errList\"><i class=\"fa fa-minus-square\"></i> List(s) of Errors in Excel Sheet.</h3><ul class=\"ulerrlist\">{0}</ul>", string.Join(string.Empty, errList.Select(i => string.Concat("<li>", i, "</li>")).ToList()));
            }
            return Json(json);
        }

        /// <summary>
        /// Method to validate excel data
        /// </summary>
        /// <param name="dt"></param>
        /// <returns></returns>
        ///
        private bool ValidateExcelFile(DataTable dt)
        {
            List<ProductExcelRowCheck> eRowCheck = new List<ProductExcelRowCheck>();
            List<string> errList = new List<string>();
            int excelColumnCount = dt.Columns.Count;
            int excelRowCount = dt.Rows.Count;

            for (int row = 0; row < excelRowCount; row++)
            {
                // Id Check
                if (IsNumber(Convert.ToString(dt.Rows[row][0])))
                {
                    int id = Convert.ToInt32(dt.Rows[row][0]);
                    var lastExcelRow = eRowCheck.LastOrDefault();

                    ProductExcelRowCheck ExcelRow = eRowCheck.FirstOrDefault(q => q.ProductId == id && q.IsBlockDone == true);
                    if (ExcelRow == null)
                    {

                        if (lastExcelRow != null && lastExcelRow.IsBlockDone == false)
                        {
                            // Title Check
                            if (Convert.ToString(dt.Rows[row][1]) == string.Empty)
                            {
                                errList.Add("<a href='javascript:;' class='exErrLnk' rc='" + row + "1'>Product name or Product feature can not be blank at [" + row + "][B]</a>");
                            }
                            else
                            {
                                if (lastExcelRow != null)
                                {
                                    if (lastExcelRow.ProductId == id)
                                    {
                                        if (lastExcelRow.PFeature == null)
                                        {
                                            lastExcelRow.PFeature = new List<ExcelProductFeatureChk>();
                                        }
                                        // Adding Options
                                        lastExcelRow.PFeature.Add(new ExcelProductFeatureChk
                                        {
                                            ProductFreature = Convert.ToString(dt.Rows[row][1])
                                        });

                                        // Duplicate Options Check
                                        List<ExcelProductFeatureChk> dublicateFeature = lastExcelRow.PFeature.GroupBy(o => o.ProductFreature).SelectMany(grp => grp.Skip(1)).ToList();
                                        if (dublicateFeature != null && dublicateFeature.Count > 0)
                                        {
                                            errList.Add("<a href='javascript:;' class='exErrLnk' t='f' pid='" + lastExcelRow.ProductId + "' col='1'>Duplicate features</a>");
                                            //lastExcelRow.IsBlockDone = true;
                                        }
                                    }
                                    else
                                    {
                                        lastExcelRow.IsBlockDone = true;

                                        // Adding Question
                                        eRowCheck.Add(new ProductExcelRowCheck
                                        {
                                            ProductId = Convert.ToInt32(dt.Rows[row][0]),
                                            ProductName = Convert.ToString(dt.Rows[row][1]),
                                            IsBlockDone = false
                                        });

                                        // Duplicate Question Check
                                        List<ProductExcelRowCheck> dublicateProduct = eRowCheck.GroupBy(o => o.ProductName)
                                                                                 .Where(c => c.Count() > 1)
                                                                                 .SelectMany(grp => grp.Skip(1)).ToList();

                                        if (dublicateProduct != null && dublicateProduct.Count > 0)
                                        {
                                            var lastdublicateProduct = dublicateProduct.LastOrDefault();
                                            errList.Add("<a href='javascript:;' class='exErrLnk' t='p' pid='" + lastdublicateProduct.ProductId + "' col='1'>Duplicate Products</a>");
                                        }
                                    }
                                }
                            }
                        }
                        else
                        {
                            eRowCheck.Add(new ProductExcelRowCheck
                            {
                                ProductId = Convert.ToInt32(dt.Rows[row][0]),
                                ProductName = Convert.ToString(dt.Rows[row][1]),
                                IsBlockDone = false
                            });
                        }
                    }
                    else
                    {
                        errList.Add("<a href='javascript:;' class='exErrLnk' t='p' tr='" + row + "'>Duplicate ProductId[" + ExcelRow.ProductId + "] at [" + row + "][A]</a>");
                    }
                }
                else
                {
                    errList.Add("Product Id must be numberic value at <a href='javascript:;' class='exErrLnk' rc='" + row + "0'>[" + row + "][A]</a>");
                }
            }

            TempData["ErrorLists"] = errList;
            return !(errList.Count > 0);
            //return false;
        }
        private bool IsNumber(string v)
        {
            int result;
            bool isNumeric = int.TryParse(v, out result);
            return isNumeric;
        }

        private static DataTable ConvertListToDataTable(List<ProductExcel> list)
        {
            DataTable table = new DataTable();
            // Get max columns.
            int columns = 2;

            // Add columns.
            for (int i = 0; i < columns; i++)
            {
                table.Columns.Add();
            }
            // Add rows.          
            foreach (var array in list)
            {
                table.Rows.Add(array.ProductId, array.PNameAndFeature);
            }
            return table;
        }
        private void CreateValidatedExcelFile(DataTable dt)
        {
            //SessionHelper sessionHelper = new SessionHelper();
            //if (sessionHelper.Product != null)
            //{
            int prodColumnCount = dt.Columns.Count;
            int prodRowCount = dt.Rows.Count;
            int prodID = Convert.ToInt32(dt.Rows[0][0]);
            Product product = new Product();
            ProductDA productDA = new ProductDA();
            bool isFeature = false;
            string ProductFeature = string.Empty;

            for (int row = 0; row < prodRowCount; row++)
            {
                if (prodID == Convert.ToInt32(dt.Rows[row][0]))
                {
                    if (!isFeature)
                    {
                        product.ProductName = Convert.ToString(dt.Rows[row][1]);
                        //product.CategoryId = sessionHelper.Product.CategoryId;
                        //Get CategoryId from session or Where ever you want.
                        product.CategoryId = 2;
                        isFeature = true;
                    }
                    else
                    {
                        ProductFeature += Convert.ToString(dt.Rows[row][1]) + "*|*";
                        if (row == prodRowCount - 1)
                        {
                            //Call function to save last set of record before exiting loop.
                            ProductFeature = ProductFeature.Substring(0, ProductFeature.Length - 3);
                            productDA.AddProductAndFeatures(product, ProductFeature);
                            TempData["message"] = "Bulk upload successful.";
                        }
                    }
                }
                else
                {
                    //If Pid differs then call function to save data.
                    ProductFeature = ProductFeature.Substring(0, ProductFeature.Length - 3);
                    productDA.AddProductAndFeatures(product, ProductFeature);
                    prodID = Convert.ToInt32(dt.Rows[row][0]);
                    ProductFeature = "";
                    row--;
                    isFeature = false;
                }
            }
            //}
        }
    }

}

Step 07 : In Views :

@{
    Layout = null;
}

@model System.Data.DataTable
@using System.Data

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Product and feature upload in bulk using Excel Sheet.</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <link href="~/Contents/Bootstrap/bootstrap.min.css" rel="stylesheet" />
    <script src="~/Scripts/Bootstrap/jquery.min.js"></script>
    <script src="~/Scripts/Bootstrap/bootstrap.min.js"></script>
    <link href="~/Contents/CSS/Site.css" rel="stylesheet" />
    <link href="~/Contents/CSS/ProductExcel.css" rel="stylesheet" />
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
    <script type="text/javascript">
        $(document).ready(function () {
            $('div[id^="err"]').html("");

            var columns = ["A", "B", "C", "D", "E", "F", "G"];

            $(".readExcel table th").each(function () {
                $(this).html(columns[parseInt($(this).text())]);
            });

            ApplyEvents();        
        });

        function ApplyEvents() {
            $("#btnUpload").click(function () {
                if ($("#flExcel").val() != "") {
                }
                else {
                    $("#errflExcel").html("Please select file for bulk upload.");
                    return false;
                }
            });

            $('input[type="text"]').on('focus', function () {
                $(this).parent().addClass('activeCell');
            });
            $('input[type="text"]').on('blur', function () {
                $(this).parent().removeClass('activeCell');
            });

            $("#btnVExcelUpload").on('click', function () {
                $(".successMsg").html("");
                var bulkExcelList = [];
                var counter = 0;
                $(".readExcel table tr").each(function (i, tr) {
                    var bulkExcel = { ProductId: 0, PNameAndFeature: "" };
                    if (counter++ > 0) {
                        bulkExcel.ProductId = $(tr).find("td:eq(0) input:text").val();
                        bulkExcel.PNameAndFeature = $(tr).find("td:eq(1) input:text").val();
                        bulkExcelList.push(bulkExcel);
                    }
                });

                bulkExcelList = JSON.stringify({ 'bulkExcelList': bulkExcelList });

                $.ajax({
                    contentType: 'application/json; charset=utf-8',
                    dataType: 'json',
                    type: 'POST',
                    url: '/Product/ValidatedProductExcelUpload',
                    data: bulkExcelList,
                    success: function (data) {
                        $("#divErrLists").html(data);
                        if (!data) {
                            $("#readExcel").hide();                      
                            $(".successMsg").html("Bulk upload successful.");
                        }
                        ApplyErrorLinks();
                    }
                });
            });
            ApplyErrorLinks();
        }

        function ApplyErrorLinks() {
            $(".exErrLnk").on('click', function () {
                $('.activeCell').removeClass("activeCell");
                var exEle = $(this).attr("rc");
                if (exEle) {
                    $("#" + exEle).focus();
                    $("#txt" + exEle).focus();
                } else {
                    var type = $(this).attr("t");
                    var pid = $(this).attr("pid");
                    var col = $(this).attr("col");
                    var tr = $(this).attr("tr");

                    if (type == "f") {
                        if (pid && col) {
                            var counter = 0;
                            $(".readExcel table tr").each(function (i, tr) {
                                if ($(tr).find("td:eq(0) input:text").val() == pid) {
                                    if (counter++ > 0) {
                                        $(tr).find("td:eq(" + col + ")").addClass('activeCell');
                                    }
                                }
                            });
                        }
                    }
                    if (type == "p") {
                        if (pid && col) {
                            $(".readExcel table tr").each(function (i, tr) {
                                if ($(tr).find("td:eq(0) input:text").val() == pid) {
                                    $(tr).find("td:eq(" + col + ")").addClass('activeCell');

                                    var product = $(tr).find("td:eq(" + col + ") input:text").val();

                                    $(".readExcel table tr").each(function (i, tr) {
                                        if ($(tr).find("td:eq(1) input:text").val() == product)
                                            $(tr).find("td:eq(1)").addClass('activeCell');

                                    });
                                }
                            });
                        }
                    }
                    if (tr) {
                        alert("");
                        tr++
                        $(".readExcel table tr:eq(" + tr + ")").addClass('activeCell');
                    }
                }
            });

            $("#errList").on('click', function () {
                if ($(".ulerrlist").css("display") == "none") {
                    $(this).find("i").attr("class", "fa fa-minus-square");
                    $(".ulerrlist").slideDown();
                } else {
                    $(this).find("i").attr("class", "fa fa-plus-square");
                    $(".ulerrlist").slideUp();
                }
            });
        }
    </script>
</head>
<body>
    <div class="container-fluid">
        <div class="row content">
            <div class="col-sm-3 sidenav">
                <h4>Add Product</h4>
                <ul class="nav nav-pills nav-stacked">
                    <li class="active"><a href="#section1">Home</a></li>
                    <li><a href="/Product/Product">Bulk Product Upload</a></li>
                    <li><a href="#section3">Family</a></li>
                    <li><a href="#section3">Photos</a></li>
                </ul>
                <br>
                <div class="input-group">
                    <input type="text" class="form-control" placeholder="Search Blog..">
                    <span class="input-group-btn">
                        <button class="btn btn-default" type="button">
                            <span class="glyphicon glyphicon-search"></span>
                        </button>
                    </span>
                </div>
            </div>

            <div class="col-sm-9">
                <h4><small>Product Bulk Upload with its features.</small></h4>
                <hr>
                <div class="col-sm-12">
                    <div class="col-sm-3">
                        Select File :
                    </div>
                    <div class="col-sm-9">
                        <div id="divExcel" class="divExcelUP">
                            @using (Html.BeginForm("Product", "Product", FormMethod.Post, new { enctype = "multipart/form-data" }))
                            {
                             
                                <div class="editor-field">
                                    <input type="file" id="flExcel" name="postedFile" />
                                    <div id="errflExcel" class="divErr"></div>
                                </div>
                                <br />
                                <div class="editor-field">
                                    <input class="Button" id="btnUpload" type="submit" value="Upload" />
                                </div>
                                <br />
                                <div class="editor-field">
                                    <div class="successMsg">
                                        @TempData["Msg"]
                                    </div>
                                </div>
                            }
                        </div>
                    </div>
                </div>

                <div class="col-sm-12">
                    <div class="clearfix"></div>
                    <div id="divErrLists" class="errlists">
                        @if (TempData["ErrorLists"] != null)
                        {
                            <h3 id="errList"><i class="fa fa-minus-square"></i>List(s) of Errors in Excel Sheet.</h3>
                            <ul class="ulerrlist">
                                @foreach (var errlist in (List<string>)TempData["ErrorLists"])
                                {
                                    <li>
                                        @Html.Raw(errlist)
                                    </li>
                                }
                            </ul>
                        }
                    </div>
                    <div class="clearfix"></div>
                    <div id="readExcel" class="readExcel">
                        @if (Model != null)
                        {
                            <input class="Button floatright marginbottom" id="btnVExcelUpload" type="submit" value="Upload Excel" />
                            <table class="table table-responsive table-bordered">
                                <thead>
                                    <tr>
                                        @foreach (DataColumn column in Model.Columns)
                                        {
                                            <th>@column.Ordinal</th>
                                        }
                                    </tr>
                                </thead>
                                <tbody>
                                    @{
                                        for (int r = 0; r < Model.Rows.Count; r++)
                                        {
                                            DataRow row = Model.Rows[r];
                                        <tr class="beRow" id="@r">
                                            @for (int c = 0; c < Model.Columns.Count; c++)
                                            {
                                                <td id="@string.Concat(r, c)">
                                                    <input type="text" id="@string.Concat("txt", r, c)" class="txtExcel" value="@row[c]" />
                                                </td>
                                            }
                                        </tr>
                                        }
                                    }
                                </tbody>
                            </table>
                        }
                    </div>
                    <div class="clearfix"></div>
                </div>
            </div>
        </div>
    </div>
</body>

</html>