Skip to content

Instantly share code, notes, and snippets.

@yashodhank
Last active July 28, 2023 18:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yashodhank/30e076788b4fb874d5cdd159570f395b to your computer and use it in GitHub Desktop.
Save yashodhank/30e076788b4fb874d5cdd159570f395b to your computer and use it in GitHub Desktop.
DRSApp development Document Related to Order & OrderBOM
@using KlausBOM.DTOs.Orders
@using KlausBOM.DTOs.ProductConfigs
@using KlausBOM.DTOs.Products
@using KlausBOM.Domain
@using KlausBOM.Domain.Enumerations
@using KlausBOM.Models
@using KlausBOM.Utility
@using System.Security.Claims

@model OrderCreateEditDto

@{
    ViewData["Title"] = "Create new Order";
    Layout = "~/Views/Shared/_Layout.cshtml";

    ViewData["Breadcrumb"] = new Breadcrumb
            {
                PreviousPages = new Dictionary<string, string>()
                {
                    { "Orders", "/Orders"}
                },
                CurrentPage = "Create new Order"
            };
}

@section Styles {
    <link type="text/css" rel="stylesheet" href="~/css/order/order.create.css">
}

@if (ViewData["Error"] != null)
{
    <div class="alert alert-danger border-0 fade show">
        @ViewData["Error"]
    </div>
}
else
{

    <div class="details border-left border-bottom">
        <form method="post" id="create-order-form">
            @Html.AntiForgeryToken()
            <ul class="nav nav-tabs m-0" id="order-details-tab" role="tablist">
                <li class="nav-item" role="presentation">
                    <span class="nav-link active" id="details-tab"
                      aria-selected="true">Details</span>
                </li>
            </ul>
            <div class="tab-content card p-2 mb-0 border-right" id="order-details-content">
                <div class="tab-pane fade show active" id="details" role="tabpanel"
                 aria-labelledby="details-tab">

                    <div class="row">
                        <div class="col-md-8 pr-5">

                            <div class="row">
                                <div class="col-md-4 mt-2 mt-md-0">
                                    <label for="@nameof(Model.OrderNumber)" class="form-label required-symbol">
                                        Order Number
                                    </label>
                                    <input type="text" required class="form-control"
                                       name="@nameof(Model.OrderNumber)" id="@nameof(Model.OrderNumber)"
                                       value="@Model.OrderNumber" maxlength="15" />
                                </div>

                                <div class="col-md-8 mt-2 mt-md-0">
                                    <label for="@nameof(Model.ProjectName)" class="form-label required-symbol">
                                        Project Name
                                    </label>
                                    <input type="text" required class="form-control"
                                       name="@nameof(Model.ProjectName)" id="@nameof(Model.ProjectName)"
                                       value="@Model.ProjectName" maxlength="255" />
                                </div>
                            </div>

                            <div class="row">
                                <div class="col-md-4 mt-2 mt-md-0">
                                    <label for="@nameof(Model.City)" class="form-label required-symbol">
                                        Project City
                                    </label>
                                    <input type="text" required class="form-control"
                                       name="@nameof(Model.City)" id="@nameof(Model.City)"
                                       value="@Model.City" maxlength="255" />
                                </div>

                                <div class="col-md-8 mt-2 mt-md-0">
                                    <label for="@nameof(Model.Customer)" class="form-label required-symbol">
                                        Customer Name
                                    </label>
                                    <input type="text" required class="form-control"
                                       name="@nameof(Model.Customer)" id="@nameof(Model.Customer)"
                                       value="@Model.Customer" maxlength="255" />
                                </div>
                            </div>

                            <div class="row">
                                <div class="col-md-4 mt-2 mt-md-0">
                                    <label for="@nameof(Model.CarSpacesQuantity)" class="form-label required-symbol">
                                        Car Space Quantity
                                    </label>
                                    <input type="number" required class="form-control"
                                       name="@nameof(Model.CarSpacesQuantity)" id="@nameof(Model.CarSpacesQuantity)"
                                       value="@Model.CarSpacesQuantity" min="0" />
                                </div>

                            </div>
                        </div>

                        <div class="col-md-2 offset-md-1 pr-5 mt-2 mt-md-0">
                            <div class="row">
                                <div class="col-md-12 mt-2 mt-md-0">
                                    <label for="PreparedBy" class="form-label">
                                        Prepared By
                                    </label>
                                    <input type="text" readonly class="form-control"
                                       name="PreparedBy" id="PreparedBy"
                                       value="@User.GetUserClaim(ClaimTypes.Name)" />
                                </div>

                            </div>

                            <div class="row">
                                <div class="col-md-12 mt-2 mt-md-0">
                                    <label for="@nameof(Model.ReleaseDate)" class="form-label">
                                        Release Date
                                    </label>
                                    <input type="datetime-local" readonly class="form-control"
                                       name="@nameof(Model.ReleaseDate)" id="@nameof(Model.ReleaseDate)"
                                       value="@DateTimeUtilities.GetDateTimeIstNow().ToInputLocalDateTime()" />
                                </div>

                            </div>
                        </div>
                    </div>

                    <div class="row select-order-criteria mt-4">
                        <div class="col-12">
                            <ul class="nav nav-tabs m-0" id="select-order-tab" role="tablist">
                                <li class="nav-item" role="presentation">
                                    <span class="nav-link active" id="select-tab"
                                      aria-selected="true">Select Order Criteria</span>
                                </li>
                            </ul>
                            <div class="tab-content card p-2 mb-0 border-right border-bottom border-left"
                             id="select-order-content">
                                <div class="tab-pane fade show active" id="select-order" role="tabpanel"
                                 aria-labelledby="select-tab">
                                    <div class="row align-items-center">
                                        <div class="col-lg-2 col-md-4">
                                            <label for="Product"
                                               class="form-label">Product</label>
                                            @* Dropdown *@

                                            <select id="Product"
                                                name="Product" class="custom-select">
                                                @foreach (Product product in ViewData["Products"] as IEnumerable<Product>)
                                                {
                                                    <option value="@product.ProductId" 
                                                    data-value-text="@product.Name"
                                                    >@product.Name</option>
                                                }
                                            </select>
                                        </div>

                                        <div class="col-lg-2 col-md-4 mt-3 mt-md-0">
                                            <label for="SubProduct"
                                               class="form-label">Sub-Product</label>
                                            @* Dropdown *@

                                            <select id="SubProduct"
                                                name="SubProduct" class="custom-select">
                                            </select>
                                        </div>

                                        <div class="col-lg-1 col-md-2 mt-3 mt-md-0">
                                            <label for="Width"
                                               class="form-label">Width</label>
                                            @* Dropdown *@

                                            <select id="Width"
                                                name="Width" class="custom-select">
                                                @*@foreach (Width width in ViewData["Widths"] as IEnumerable<Width>)
                                                {
                                                    <option value="@width.WidthId"
                                                    data-value-text="@width.WidthValue">@width.WidthValue</option>
                                                }*@
                                            </select>
                                        </div>

                                        <div class="col-lg-1 col-md-2 mt-3 mt-md-0">
                                            <label for="Length"
                                               class="form-label">Length</label>
                                            @* Dropdown *@

                                            <select id="Length"
                                                name="Length" class="custom-select">
                                                @*@foreach (Length length in ViewData["Lengths"] as IEnumerable<Length>)
                                                {
                                                    <option value="@length.LengthId"
                                                    data-value-text="@length.LengthValue">@length.LengthValue</option>
                                                }*@
                                            </select>
                                        </div>

                                        <div class="col-lg-1 col-md-2 mt-3 mt-lg-0">
                                            <label for="Level"
                                               class="form-label">Level</label>
                                            @* Dropdown *@

                                            <select id="Level"
                                                name="Level" class="custom-select">
                                                @*@foreach (Level level in ViewData["Levels"] as IEnumerable<Level>)
                                                {
                                                    <option value="@level.LevelId"
                                                    data-value-text="@level.LevelValue">@level.LevelValue</option>
                                                }*@
                                            </select>
                                        </div>

                                        <div class="col-lg-1 col-md-2 mt-3 mt-lg-0">
                                            <label for="Grid"
                                               class="form-label">Grid</label>
                                            @* Dropdown *@

                                            <select id="Grid"
                                                name="Grid" class="custom-select">
                                                @*@foreach (Grid grid in ViewData["Grids"] as IEnumerable<Grid>)
                                                {
                                                    <option value="@grid.GridId"
                                                    data-value-text="@grid.GridValue">@grid.GridValue</option>
                                                }*@
                                            </select>
                                        </div>

                                        <div class="col-lg-2 col-md-4 mt-3 mt-lg-0">
                                            <label for="Quantity"
                                               class="form-label">Model Qty.</label>

                                            <input id="Quantity" type="number" min="0"
                                               name="Quantity" class="form-control" />

                                        </div>

                                        <div class="col-lg-2 col-md-4 mt-3 mt-lg-0">
                                            <button class="btn btn-success"
                                                type="button" id="btnAddProduct" name="btnAddProduct">
                                                <i class="fas fa-plus mr-2"></i> Add
                                            </button>

                                        </div>
                                    </div>

                                </div>
                            </div>
                        </div>
                    </div>

                    <div class="row select-order-list mt-4 d-block">
                        <div>
                            <table class="table table-responsive-lg table-hover datatable-show-all"
                               id="orders-list" style="white-space: nowrap;">
                                <thead>
                                    <tr>
                                        <th>Id</th>
                                        <th>Product</th>
                                        <th>Sub-Product</th>
                                        <th>Width</th>
                                        <th>Length</th>
                                        <th>Level</th>
                                        <th>Grid</th>
                                        <th>Quantity</th>
                                        <th>Delete</th>
                                    </tr>
                                </thead>
                                <tbody>
                                    
                                </tbody>
                            </table>
                        </div>
                    </div>

                    <div class="row mt-3 align-items-center">
                        <div class="col-lg-2 col-md-4">
                            <button type="button" class="btn btn-danger" id="btnSubmit">
                                Create <i class="far fa-paper-plane ml-2"></i>
                            </button>
                        </div>
                        <div class="col-lg-10 col-md-6">
                            @if (ViewData["CreateError"] != null)
                            {
                                <div class="alert alert-danger border-0 fade show mb-0">
                                    @ViewData["CreateError"]
                                </div>
                            }
                        </div>

                    </div>
                </div>
            </div>
        </form>
    </div>

    @section Scripts {

    <script type="text/javascript">
        var productConfigList = @Html.Raw(Json.Serialize((ViewData["SubProducts"] as IEnumerable<ProductConfig>)));
    </script>

    <script asp-append-version="true" src="~/js/order/order.create.js"></script>
    }

}

The provided Razor view is designed to create a new order in an MVC structure using ASP.NET Core. Let's break it down:

1. Directives and Model Declaration:

  • The view starts by importing necessary namespaces with the @using directive.
  • The @model directive is used to specify the expected data type, which is OrderCreateEditDto.

2. ViewData and Layout:

  • ViewData is used to set metadata for the page, including the title and breadcrumb navigation.

3. Styles Section:

  • There's a section titled Styles that imports a specific CSS stylesheet for styling the page.

4. Content Display:

  • If an error exists (as determined by ViewData["Error"]), an error message is shown.
  • The main content starts with a form that collects information about the new order.

Inside the form:

  1. Tabs: The form contains a tabbed navigation. However, there's only one tab titled "Details" in this view.

  2. Order Details: The order details section captures basic order information such as order number, project name, city, customer name, and car space quantity.

  3. Prepared By & Release Date: This section captures the user preparing the order (automatically fetched from the user's claims) and the current date and time.

  4. Select Order Criteria: This is where the user can select products and their specifications. Dropdowns are provided for selecting a product, sub-product, width, length, level, and grid. There's also a field for specifying the quantity and a button to add the selected product configuration to the order.

  5. List of Selected Products: Displays a table of all selected products and their specifications. There's also an option to delete a product from the list.

  6. Submission: A button is provided to submit the form and create the order. If there was an error during creation (as determined by ViewData["CreateError"]), an error message is displayed.

5. Scripts Section:

  • This section initializes a JavaScript variable with the list of sub-products from the ViewData, making it accessible to client-side scripts.
  • It also imports a specific JavaScript file (order.create.js) presumably containing logic related to creating orders.

In-depth Analysis:

  1. Tabbed Interface: Even though there's just one tab in this view, the design suggests that the application might have been designed to accommodate more tabs in the future.

  2. Dynamic Dropdown Population: The design suggests that the dropdowns for sub-product, width, length, level, and grid might be populated dynamically based on the selected product. The view contains placeholder comments suggesting where the values might be fetched from.

  3. User Information Auto-population: The "Prepared By" field is auto-populated using the logged-in user's information, making the process seamless for the user.

  4. Dynamic Table: The table for selected products is dynamic, allowing users to add multiple products to the order before submission.

  5. Client-Side Data Initialization: The view serializes some of its data into JavaScript variables. This is a common pattern to pass data from the server side to client-side scripts, ensuring that client-side scripts have the necessary data to function.

In summary, this view provides a user-friendly interface for creating new orders. It's designed with usability in mind, allowing users to select multiple products and their specifications before submitting the order. The use of dynamic dropdowns and tables makes the process seamless and efficient. Integrated error handling ensures that users are notified of any issues during the creation process.

@using KlausBOM.DTOs.Orders
@using KlausBOM.Domain
@using KlausBOM.Models

@model OrderCreateEditDto

@{
    ViewData["Title"] = "Edit Order";
    Layout = "~/Views/Shared/_Layout.cshtml";

    ViewData["Breadcrumb"] = new Breadcrumb
    {
        PreviousPages = new Dictionary<string, string>()
                {
                    { "Orders", "/Orders"}
                },
        CurrentPage = "Edit Order",
        CurrentInfo = "" + Model.OrderNumber + " | " + Model.Customer + " ❱❱ " + Model.ProjectName
    };
}

@section Styles {
    <link type="text/css" rel="stylesheet" href="~/css/tables/datatables/dataTables.bootstrap4.min.css" />
    <link type="text/css" rel="stylesheet" href="~/css/tables/datatables/searchpanes/searchPanes.bootstrap4.min.css" />
    <link type="text/css" rel="stylesheet" href="~/css/tables/datatables/select.bootstrap4.min.css" />

    <link type="text/css" rel="stylesheet" href="~/css/order/order.edit.css">
    <link type="text/css" rel="stylesheet" href="~/css/order/order.edit.header.css">
    <link type="text/css" rel="stylesheet" href="~/css/order/order.edit.bom.css">
}

@if (ViewData["Error"] != null)
{
    <div class="alert alert-danger border-0 fade show">
        @ViewData["Error"]
    </div>
}
else
{
    <nav>
        <div class="nav nav-tabs nav-tabs-solid border rounded" id="order-nav-tabs" role="tablist">
            <button class="nav-link active" id="order-nav-header"
                data-toggle="tab" data-target="#nav-header"
                type="button" role="tab" aria-controls="header"
                aria-selected="true">
                Order Header
            </button>
            <button class="nav-link" id="order-nav-bom"
                data-toggle="tab" data-target="#nav-bom"
                type="button" role="tab" aria-controls="bom"
                aria-selected="false">
                BOM
            </button>
        </div>
    </nav>

    <div class="tab-content" id="order-nav-tab-content">
        <div class="tab-pane fade show active" id="nav-header" role="tabpanel"
         aria-labelledby="order-nav-header">
            @await Html.PartialAsync("./PartialViews/OrderHeader.Edit.cshtml", Model)
        </div>

        <div class="tab-pane fade" id="nav-bom" role="tabpanel"
         aria-labelledby="order-nav-bom">
            @await Html.PartialAsync("./PartialViews/OrderBom.Edit.cshtml", Model)
            <div id="spinner">
              <div class="spinner-icon"></div>
            </div>
        </div>

    </div>

    @section Scripts {
        @if (TempData["UpdateSuccess"] != null)
        {
            <script type="text/javascript">
                toast(
                    "Update Order BOM Part",
                    "@TempData["UpdateSuccess"]",
                    "success");
            </script>
        }
        <script type="text/javascript">
            var productConfigList = @Html.Raw(Json.Serialize((ViewData["SubProducts"] as IEnumerable<ProductConfig>)));
            var orderConfigs = @Html.Raw(Json.Serialize(Model.OrderConfig));

        </script>

        <script asp-append-version="true" src="~/js/tables/datatables/extensions/pdfmake/pdfmake.min.js"></script>
        <script asp-append-version="true" src="~/js/tables/datatables/extensions/pdfmake/vfs_fonts.min.js"></script>

        <script asp-append-version="true" src="~/js/tables/datatables/extensions/dataTables.bootstrap4.min.js"></script>
        <script asp-append-version="true" src="~/js/tables/datatables/extensions/searchpanes/dataTables.searchPanes.min.js"></script>
        <script asp-append-version="true" src="~/js/tables/datatables/extensions/searchpanes/searchPanes.bootstrap4.min.js"></script>
        <script asp-append-version="true" src="~/js/tables/datatables/extensions/dataTables.select.min.js"></script>

        <script asp-append-version="true" src="~/js/order/order.edit.js"></script>
        <script asp-append-version="true" src="~/js/order/order.edit.header.js"></script>
        <script asp-append-version="true" src="~/js/order/order.edit.bom.js"></script>

    }
}

This provided Razor view is for editing an order in the MVC structure of ASP.NET Core. It utilizes the OrderCreateEditDto model which seems to contain details of an order that can be created or edited. Here's a breakdown:

1. Directives and Model Declaration:

  • The view imports the necessary namespaces using the @using directive.
  • The @model directive is used to specify the expected data type for this view.

2. ViewData and Layout:

  • ViewData["Title"] and ViewData["Breadcrumb"] are used to set meta-data for the page. The breadcrumb data gives a hint about the page hierarchy, allowing users to navigate back to previous pages easily.

3. Styles Section:

  • The section titled Styles imports several CSS stylesheets, some of which are specific to the DataTables jQuery plugin, and some others are specific to different sections or features of the order editing process.

4. Content Display:

  • If there's an error (as determined by ViewData["Error"]), it displays the error message in a styled div.
  • If no error is present, the main content of the view is displayed. The main content features:
    • A navigation bar (tabbed interface) to toggle between "Order Header" and "BOM" (Bill of Materials).
    • Within these tabs, partial views are rendered using the Html.PartialAsync method. This method renders other Razor views (here named OrderHeader.Edit.cshtml and OrderBom.Edit.cshtml respectively) inside the current view.
    • A loading spinner is displayed inside the "BOM" tab, suggesting that some asynchronous operations may be taking place when this tab is accessed.

5. Scripts Section:

  • This section loads various JavaScript libraries and scripts.
  • A toast notification is shown if there was a successful update, based on the presence of TempData["UpdateSuccess"].
  • The script section also serializes some model data and ViewData into JavaScript variables, making them available for client-side scripts. This is a common pattern to pass data from server-side code to client-side JavaScript.
  • Several DataTables-related scripts are loaded, suggesting that the view might be using the DataTables jQuery plugin for displaying and managing tables.
  • Specific scripts related to different aspects of the order editing process (order.edit.js, order.edit.header.js, and order.edit.bom.js) are also loaded.

In-depth Analysis:

  1. Tabbed Interface: The view employs a tabbed interface, which allows users to easily navigate between different sections of the order editing process without having to load a new page.

  2. Partial Views: The use of partial views suggests a modular design. This is a good practice as it allows for reusability of views and separation of concerns. The main view acts as a container, while specific functionalities are encapsulated in separate partial views.

  3. Dynamic Content Loading: The presence of the loading spinner inside the "BOM" tab hints that some data might be loaded asynchronously when this tab is accessed. This could mean a more responsive UI as not all data needs to be loaded upfront.

  4. Error Handling: The view has built-in error handling, displaying a user-friendly error message when something goes wrong.

  5. DataTables Integration: The inclusion of several DataTables-related styles and scripts suggests that this view uses the DataTables jQuery plugin. DataTables provides functionalities like searching, paging, and sorting to tables.

  6. Client-Side Data Initialization: The view serializes some of its data into JavaScript variables. This is a common pattern to pass data from the server side to client-side scripts, ensuring that client-side scripts have the necessary data to function.

In summary, this view provides a user-friendly interface for editing orders, with a tabbed layout to separate different sections of the editing process. It employs a modular design using partial views and offers dynamic content loading for better responsiveness. Integrated error handling, toast notifications, and the DataTables plugin further enhance the user experience.

@using System.Data
@using KlausBOM.Models
@model DataTable

@{
    ViewData["Title"] = "Orders";
    Layout = "~/Views/Shared/_Layout.cshtml";
    ViewData["Breadcrumb"] = new Breadcrumb
            {
                PreviousPages = null,
                CurrentPage = "Orders"
            };
}

@section Styles {
    <link type="text/css" rel="stylesheet" href="~/css/order/order.index.css">

}
@if (ViewData["Error"] != null)
{
    <div class="alert alert-danger border-0 fade show">
        @ViewData["Error"]
    </div>
}
else
{

    <div class="card p-2">
        <div> 
            <table class="table table-responsive-lg table-hover datatable-show-all"
               id="orders-list" style="white-space: nowrap;">
                <thead>
                    <tr>
                        @foreach (DataColumn col in Model.Columns)
                        {

                            if (col.ColumnName.Equals("Selection"))
                            {
                                <th>
                                    <div class="custom-control custom-checkbox">
                                        <input type="checkbox" class="custom-control-input" id="select-product-config-all">
                                        <label class="custom-control-label" for="select-product-config-all"></label>
                                    </div>
                                </th>
                            }
                            else
                            {
                                <th>@col.ColumnName</th>

                            }
                        }
                    </tr>
                </thead>
                <tbody>
                    @foreach (DataRow row in Model.Rows)
                    {
                        <tr>
                            @foreach (DataColumn col in Model.Columns)
                            {
                                if (col.ColumnName.Equals("Status"))
                                {
                                    string status = row[col.ColumnName].ToString();
                                    string badgeClass = "badge ";
                                    switch (status)
                                    {
                                        case "ON_HOLD":
                                            badgeClass += "badge-warning";
                                            break;
                                        case "NEW":
                                            badgeClass += "badge-success";
                                            break;
                                        case "COLD":
                                            badgeClass += "badge-secondary";
                                            break;
                                        case "WARM":
                                            badgeClass += "badge-info";
                                            break;
                                        case "HOT":
                                            badgeClass += "badge-danger";
                                            break;
                                    }

                                    <td>
                                        <span class="@badgeClass">@status</span>
                                    </td>
                                } else {
                                    <td>@row[col.ColumnName]</td>

                                }
                            }
                        </tr>

                    }

                </tbody>
            </table>
        </div>


    </div>

    @section Scripts {
        @if (TempData["CreateSuccess"] != null)
        {
            <script type="text/javascript">
                toast(
                    "Create Order",
                    "@TempData["CreateSuccess"]",
                    "success");
            </script>
        }
        <script asp-append-version="true" src="~/js/tables/datatables/extensions/pdfmake/pdfmake.min.js"></script>
        <script asp-append-version="true" src="~/js/tables/datatables/extensions/pdfmake/vfs_fonts.min.js"></script>
        <script asp-append-version="true" src="~/js/order/order.index.js"></script>
    }
}

The code is a Razor View for the MVC structure in ASP.NET Core. The view is designed to display a list of orders. Let's break down the main components:

1. Directives & Model Declaration:

  • @using System.Data and @using KlausBOM.Models are directives that import necessary namespaces to be used in the Razor view.
  • @model DataTable sets the type of the model that the view expects to receive from the controller, which is a DataTable in this case.

2. ViewData and Layout:

  • ViewData["Title"] and ViewData["Breadcrumb"] are used to set page-specific data that might be used by the layout or other parts of the rendered view.
  • Layout = "~/Views/Shared/_Layout.cshtml"; sets the layout page to be used with this view.

3. Styles Section:

  • There's a section dedicated to styles, which includes a specific stylesheet for this view.

4. Content Display:

  • If there's an error (as determined by ViewData["Error"]), it displays an error message in a styled div.
  • If there's no error, it displays a table of orders.
    • The headers of the table are dynamically generated based on the columns in the DataTable.
    • The rows are also dynamically generated from the DataTable. If a column is named "Status", it adds a colored badge based on the status value, otherwise, it directly displays the value.

5. Scripts Section:

  • If the TempData["CreateSuccess"] is not null, it will display a toast notification with a success message.
  • Various scripts are added to the view, especially related to DataTables and the specific functionalities of the page.

In-depth Analysis:

  1. DataTable Rendering: The view is designed to display a dynamic table based on a DataTable. This is evident from the foreach loops iterating over the columns and rows of the DataTable. This offers flexibility in terms of what columns and data to display, as it can adapt to different DataTable structures.

  2. Dynamic Status Rendering: There's a specific check for a column named "Status". For this column, instead of just displaying the text, the view adds a badge with a color that depends on the status value (e.g., "ON_HOLD" gets a warning badge).

  3. Error Handling: Before displaying the main content, the view checks if there's an error message in ViewData["Error"]. If so, it displays that error prominently, providing feedback to the user.

  4. Toast Notifications: A toast notification is a non-intrusive, small popup that provides feedback to the user. In this view, a toast is shown if an order has been successfully created.

  5. Script Inclusions: The view includes various JavaScript files, especially those related to DataTables and order-specific functionalities. This suggests that there's dynamic behavior on the client side, like sorting or filtering the displayed orders.

  6. Styling: The view has a dedicated stylesheet, ensuring the orders are displayed in a styled and user-friendly manner.

In summary, this view is designed to display a list of orders in a table format, with dynamic rendering based on the data it receives. It provides feedback to the user through toast notifications and error messages and includes scripts to enhance its functionality on the client side.

@using KlausBOM.DTOs.Orders

@model OrderCreateEditDto 



<div class="order-bom">
    <div class="card p-2"> 
        <table class="table table-responsive-lg table-hover datatable-show-all"
               id="order-bom-list" style="white-space: nowrap;">
            <thead>
            <tr>
                <th>Level</th>
                <th>OrderBomId</th>
                <th>PartGroupName</th>
                <th>Part No.</th>
                <th>BOM Line</th>
                <th>Item Description</th>
                <th>Specification</th>
                <th>Part Position</th>
                <th>Drawing No.</th>
                <th>Ref. No.</th>
                <th>InfoWorld No.</th>
                <th>UOM</th>
                <th>Qty/Unit</th>        
                <th>Total Qty</th> 
                <th>Unit Weight</th>
                <th>Total Weight</th> 
                <th>Part Rate</th>
                <th>Total Cost</th>
                <th>Type</th>
                <th>Delete</th>
            </tr>
            </thead>
            <tbody>
            </tbody>
        </table>
    </div>
</div>

<!-- Modal Add Order BOM Child Part-->
<div class="modal fade" id="add-order-bom-child-modal" tabindex="-1"
     role="dialog" aria-labelledby="add-order-bom-child-title"
     aria-hidden="true">
    <div class="modal-dialog modal-dialog-centered" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title" id="add-order-bom-child-title">
                    Add Order BOM Part
                </h5>

                <button type="button" class="close" data-dismiss="modal"
                        aria-label="Close">
                    <span aria-hidden="true">&times;</span>
                </button>
            </div>
            <div class="modal-body">
                <div class="form-group">
                    <div class="custom-control custom-checkbox">
                        <input type="checkbox" class="custom-control-input" id="isEntireAssembly">
                        <label class="custom-control-label" for="isEntireAssembly">Add an entire assembly</label>
                    </div>
                </div>
                <div class="form-group">
                    <label for="PartId"
                           class="form-label">
                        BOM Part
                    </label> 
                    <select id="PartId" name="PartId"></select> 
                </div>
                <div class="form-group">
                    <label for="PartDescription"
                           class="form-label">
                        Part Description
                    </label> 
                    <input class="form-control" readonly type="text"
                           id="PartDescription" name="PartDescription"/>
                </div> 
                <div id="assembly" class="d-none">
                    <div class="form-group">
                        <label for="PartPosition"
                               class="form-label">
                            Part Position
                        </label> 
                        <input class="form-control" type="number"
                               id="PartPosition" name="PartPosition" min="0" step="1"/>
                    </div> 
                    <div class="form-group">
                        <label for="BOMLine"
                               class="form-label">
                            BOM Line
                        </label> 
                        <input class="form-control" type="number"
                               id="BomLine" name="BomLine" min="0" step="1"/>
                    </div> 
                    <div class="form-group">
                        <label for="PartQuantity"
                               class="form-label">
                            Quantity
                        </label> 
                        <input class="form-control" type="number"
                               id="PartQuantity" name="PartQuantity" min="0" step="1"/>
                    </div>
                    <div class="form-group">
                        <label for="PartTotalQuantity"
                               class="form-label">
                            Total Quantity
                        </label> 
                        <input class="form-control" type="number"
                               id="PartTotalQuantity" name="PartTotalQuantity" min="0" step="1"/>
                    </div>
                </div> 
                <div class="form-group" id="requireChildLevel">
                    <div class="custom-control custom-checkbox">
                        <input type="checkbox" class="custom-control-input" id="hasChild">
                        <label class="custom-control-label" for="hasChild">Require Child level</label>
                    </div>
                </div>
                <div class="form-group">
                    <span class="text-danger mt-2" id="errorBomChild"></span>
                </div>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-secondary"
                        data-dismiss="modal">
                    <i class="fas fa-times mr-2"></i> Cancel
                </button>
                <button type="button"
                        class="btn btn-outline-success"
                        id="btnSaveBomPart" onclick="onAddNewBomChild()">
                    <i class="fas fa-plus mr-2"></i> Add
                </button>
            </div>
        </div>
    </div>
</div> 


@section Scripts {

}

This is a Razor view named OrderBom.Edit.cshtml designed for editing or managing the Bill of Materials (BOM) for a specific order in an ASP.NET Core MVC application. The BOM typically lists the parts and quantities required to manufacture a product. Let's break down the key components:

1. Directives and Model Declaration:

  • Relevant namespaces are imported using the @using directive.
  • The expected model type for this view is declared as OrderCreateEditDto using the @model directive.

2. Order BOM Table:

  • A table is defined with the ID order-bom-list to list out the BOM details.
  • The table includes columns for various attributes related to the BOM, such as level, part group name, part number, BOM line, item description, specification, and many others.

3. Modal for Adding BOM Child Part:

  • A modal (a dialog box/pop-up window) is defined with the ID add-order-bom-child-modal which appears when the user wants to add a new part to the BOM.
  • Inside this modal:
    • A checkbox lets users decide if they want to add an entire assembly.
    • A dropdown lets users select a BOM part.
    • There's an input field for the part description, which is read-only.
    • If the user is adding an entire assembly, additional input fields for part position, BOM line, quantity, and total quantity are displayed.
    • There's another checkbox to decide if a child level is required for the part.
    • An error message can be displayed if there's an issue while adding the part.
    • There are buttons to either cancel the addition or save the new part.

4. JavaScript Section:

  • A Scripts section is defined at the end, which can be populated with JavaScript functions or scripts specific to this view. For instance, the onAddNewBomChild() function is invoked when the "Add" button in the modal is clicked. However, the implementation of this function isn't provided in the given view. It's likely defined in an external JavaScript file or provided elsewhere in the application.

Summary:

This view provides an interface for managing the Bill of Materials for an order. Users can view the current BOM details in a table and have the option to add new parts to the BOM using a modal. The design ensures that users have a clear and structured way to manage the BOM, with specific fields to capture all relevant information. The dynamic nature of the modal, which adjusts its fields based on user choices (like adding an entire assembly), ensures flexibility while maintaining a clean user interface.

@using KlausBOM.DTOs.Orders
@using KlausBOM.DTOs.ProductConfigs
@using KlausBOM.Domain
@using KlausBOM.Utility
@using System.Security.Claims

@model OrderCreateEditDto

<div class="details border-left border-bottom">
    <form method="post" id="edit-order-form">
        @Html.AntiForgeryToken()
        <ul class="nav nav-tabs m-0" id="order-details-tab" role="tablist">
            <li class="nav-item" role="presentation">
                <span class="nav-link active" id="details-tab"
                      aria-selected="true">Details</span>
            </li>
        </ul>
        <div class="tab-content card p-2 mb-0 border-right" id="order-details-content">
            <div class="tab-pane fade show active" id="details" role="tabpanel"
                 aria-labelledby="details-tab">

                <div class="row">
                    <div class="col-md-8 pr-5">
                        <input type="hidden" name="@nameof(Model.OrderId)"
                        id="@nameof(Model.OrderId)" value="@Model.OrderId" />

                        <div class="row">
                            <div class="col-md-4 mt-2 mt-md-0">
                                <label for="@nameof(Model.OrderNumber)" class="form-label required-symbol">
                                    Order Number
                                </label>
                                <input type="text" required class="form-control" readonly
                                       name="@nameof(Model.OrderNumber)" id="@nameof(Model.OrderNumber)"
                                       value="@Model.OrderNumber" maxlength="15" />
                            </div>

                            <div class="col-md-8 mt-2 mt-md-0">
                                <label for="@nameof(Model.ProjectName)" class="form-label required-symbol">
                                    Project Name
                                </label>
                                <input type="text" required class="form-control" readonly
                                       name="@nameof(Model.ProjectName)" id="@nameof(Model.ProjectName)"
                                       value="@Model.ProjectName" maxlength="255" />
                            </div>
                        </div>

                        <div class="row">
                            <div class="col-md-4 mt-2 mt-md-0">
                                <label for="@nameof(Model.City)" class="form-label required-symbol">
                                    Project City
                                </label>
                                <input type="text" required class="form-control" readonly
                                       name="@nameof(Model.City)" id="@nameof(Model.City)"
                                       value="@Model.City" maxlength="255" />
                            </div>

                            <div class="col-md-8 mt-2 mt-md-0">
                                <label for="@nameof(Model.Customer)" class="form-label required-symbol">
                                    Customer Name
                                </label>
                                <input type="text" required class="form-control" readonly
                                       name="@nameof(Model.Customer)" id="@nameof(Model.Customer)"
                                       value="@Model.Customer" maxlength="255" />
                            </div>
                        </div>

                        <div class="row">
                            <div class="col-md-4 mt-2 mt-md-0">
                                <label for="@nameof(Model.CarSpacesQuantity)" class="form-label required-symbol">
                                    Car Space Quantity
                                </label>
                                <input type="number" required class="form-control"
                                       name="@nameof(Model.CarSpacesQuantity)" id="@nameof(Model.CarSpacesQuantity)"
                                       value="@Model.CarSpacesQuantity" min="0" />
                            </div>

                        </div>
                    </div>

                    <div class="col-md-2 offset-md-1 pr-5 mt-2 mt-md-0">
                        <div class="row">
                            <div class="col-md-12 mt-2 mt-md-0">
                                <label for="PreparedBy" class="form-label">
                                    Prepared By
                                </label>
                                <input type="text" readonly class="form-control"
                                       name="PreparedBy" id="PreparedBy"
                                       value="@(Model.AddedByNavigation.FirstName + " " + Model.AddedByNavigation.LastName)" />
                            </div>

                        </div>

                        <div class="row">
                            <div class="col-md-12 mt-2 mt-md-0">
                                <label for="@nameof(Model.ReleaseDate)" class="form-label">
                                    Release Date
                                </label>
                                <input type="datetime-local" readonly class="form-control"
                                       name="@nameof(Model.ReleaseDate)" id="@nameof(Model.ReleaseDate)"
                                       value="@DateTimeUtilities.GetDateTimeIstNow().ToInputLocalDateTime()" />
                            </div>

                        </div>
                    </div>
                </div>

                <div class="row select-order-criteria mt-4">
                    <div class="col-12">
                        <ul class="nav nav-tabs m-0" id="select-order-tab" role="tablist">
                            <li class="nav-item" role="presentation">
                                <span class="nav-link active" id="select-tab"
                                      aria-selected="true">Select Order Criteria</span>
                            </li>
                        </ul>
                        <div class="tab-content card p-2 mb-0 border-right border-bottom border-left"
                             id="select-order-content">
                            <div class="tab-pane fade show active" id="select-order" role="tabpanel"
                                 aria-labelledby="select-tab">
                                <div class="row align-items-center">
                                    <div class="col-lg-2 col-md-4">
                                        <label for="Product"
                                               class="form-label">Product</label>
                                        @* Dropdown *@

                                        <select id="Product"
                                                name="Product" class="custom-select">
                                            @foreach (Product product in ViewData["Products"] as IEnumerable<Product>)
                                            {
                                                    <option value="@product.ProductId"
                                                    data-value-text="@product.Name">@product.Name</option>
                                            }
                                        </select>
                                    </div>

                                    <div class="col-lg-2 col-md-4 mt-3 mt-md-0">
                                        <label for="SubProduct"
                                               class="form-label">Sub-Product</label>
                                        @* Dropdown *@

                                        <select id="SubProduct"
                                                name="SubProduct" class="custom-select">
                                        </select>
                                    </div>

                                    <div class="col-lg-1 col-md-2 mt-3 mt-md-0">
                                        <label for="Width"
                                               class="form-label">Width</label>
                                        @* Dropdown *@

                                        <select id="Width"
                                                name="Width" class="custom-select">
                                            @*@foreach (Width width in ViewData["Widths"] as IEnumerable<Width>)
                                            {
                                            <option value="@width.WidthId"
                                            data-value-text="@width.WidthValue">@width.WidthValue</option>
                                            }*@
                                        </select>
                                    </div>

                                    <div class="col-lg-1 col-md-2 mt-3 mt-md-0">
                                        <label for="Length"
                                               class="form-label">Length</label>
                                        @* Dropdown *@

                                        <select id="Length"
                                                name="Length" class="custom-select">
                                            @*@foreach (Length length in ViewData["Lengths"] as IEnumerable<Length>)
                                            {
                                            <option value="@length.LengthId"
                                            data-value-text="@length.LengthValue">@length.LengthValue</option>
                                            }*@
                                        </select>
                                    </div>

                                    <div class="col-lg-1 col-md-2 mt-3 mt-lg-0">
                                        <label for="Level"
                                               class="form-label">Level</label>
                                        @* Dropdown *@

                                        <select id="Level"
                                                name="Level" class="custom-select">
                                            @*@foreach (Level level in ViewData["Levels"] as IEnumerable<Level>)
                                            {
                                            <option value="@level.LevelId"
                                            data-value-text="@level.LevelValue">@level.LevelValue</option>
                                            }*@
                                        </select>
                                    </div>

                                    <div class="col-lg-1 col-md-2 mt-3 mt-lg-0">
                                        <label for="Grid"
                                               class="form-label">Grid</label>
                                        @* Dropdown *@

                                        <select id="Grid"
                                                name="Grid" class="custom-select">
                                            @*@foreach (Grid grid in ViewData["Grids"] as IEnumerable<Grid>)
                                            {
                                            <option value="@grid.GridId"
                                            data-value-text="@grid.GridValue">@grid.GridValue</option>
                                            }*@
                                        </select>
                                    </div>

                                    <div class="col-lg-2 col-md-4 mt-3 mt-lg-0">
                                        <label for="Quantity"
                                               class="form-label">Model Qty.</label>

                                        <input id="Quantity" type="number" min="0"
                                               name="Quantity" class="form-control" />

                                    </div>

                                    <div class="col-lg-2 col-md-4 mt-3 mt-lg-0">
                                        <button class="btn btn-success"
                                                type="button" id="btnAddProduct" name="btnAddProduct">
                                            <i class="fas fa-plus mr-2"></i> Add
                                        </button>

                                    </div>
                                </div>

                            </div>
                        </div>
                    </div>
                </div>

                <div class="row select-order-list mt-4 d-block">
                    <div>
                        <table class="table table-responsive-lg table-hover datatable-show-all"
                               id="orders-list" style="white-space: nowrap;">
                            <thead>
                                <tr>
                                    <th>Id</th>
                                    <th>Product</th>
                                    <th>Sub-Product</th>
                                    <th>Width</th>
                                    <th>Length</th>
                                    <th>Level</th>
                                    <th>Grid</th>
                                    <th>Quantity</th>
                                    <th>Delete</th>
                                </tr>
                            </thead>
                            <tbody>
                                @{
                                    IEnumerable<ProductConfigOrderDto> productConfigs = ViewData["SubProducts"] as IEnumerable<ProductConfigOrderDto>;
                                    int count = 0;
                                }
                                @foreach (OrderConfig orderConfig in Model.OrderConfig)
                                {
                                    ProductConfigOrderDto productConfig = productConfigs.SingleOrDefault(
                                        pc => pc.ProductId.Equals(orderConfig.ProductId)
                                        && pc.SubProductId.Equals(orderConfig.SubProductId)
                                    );

                                    Width width = productConfig.Widths.SingleOrDefault(
                                        w => w.WidthId.Equals(orderConfig.WidthId)
                                    );

                                    Length length = productConfig.Lengths.SingleOrDefault(
                                        l => l.LengthId.Equals(orderConfig.LengthId)
                                    );

                                    Level level = productConfig.Levels.SingleOrDefault(
                                        lvl => lvl.LevelId.Equals(orderConfig.LevelId)
                                    );

                                    Grid grid = productConfig.Grids.SingleOrDefault(
                                        g => g.GridId.Equals(orderConfig.GridId)
                                    );

                                        <tr>
                                            <td>@count</td>
                                            <td>@productConfig.ProductName</td>
                                            <td>@productConfig.SubProductName</td>
                                            <td>@width.WidthValue</td>
                                            <td>@length.LengthValue</td>
                                            <td>@level.LevelValue</td>
                                            <td>@grid.GridValue</td>
                                            <td>@orderConfig.Quantity</td>
                                            <td></td>
                                        </tr>

                                    count++;
                                }
                            </tbody>
                        </table>
                    </div>
                </div>

                <div class="row mt-3 align-items-center">
                    <div class="col-lg-4 col-md-6">
                        <button type="button" class="btn btn-primary mt-2" id="btnSubmit">
                            Save Information<i class="far fa-paper-plane ml-2"></i>
                        </button>

                        <button type="button" class="btn btn-info ml-3 mt-2" id="btnRegenerate">
                            Regenerate <i class="fas fa-sync-alt ml-2"></i>
                        </button>
                    </div>
                    <div class="col-lg-8 col-md-6">
                        @if (ViewData["UpdateError"] != null)
                        {
                                <div class="alert alert-danger border-0 fade show mb-0">
                                @ViewData["UpdateError"]
                                </div>
                        }
                    </div>

                </div>
            </div>
        </div>
    </form>
</div>

The provided Razor view is designed to create a new order in an MVC structure using ASP.NET Core. Let's break it down:

1. Directives and Model Declaration:

  • The view starts by importing necessary namespaces with the @using directive.
  • The @model directive is used to specify the expected data type, which is OrderCreateEditDto.

2. ViewData and Layout:

  • ViewData is used to set metadata for the page, including the title and breadcrumb navigation.

3. Styles Section:

  • There's a section titled Styles that imports a specific CSS stylesheet for styling the page.

4. Content Display:

  • If an error exists (as determined by ViewData["Error"]), an error message is shown.
  • The main content starts with a form that collects information about the new order.

Inside the form:

  1. Tabs: The form contains a tabbed navigation. However, there's only one tab titled "Details" in this view.

  2. Order Details: The order details section captures basic order information such as order number, project name, city, customer name, and car space quantity.

  3. Prepared By & Release Date: This section captures the user preparing the order (automatically fetched from the user's claims) and the current date and time.

  4. Select Order Criteria: This is where the user can select products and their specifications. Dropdowns are provided for selecting a product, sub-product, width, length, level, and grid. There's also a field for specifying the quantity and a button to add the selected product configuration to the order.

  5. List of Selected Products: Displays a table of all selected products and their specifications. There's also an option to delete a product from the list.

  6. Submission: A button is provided to submit the form and create the order. If there was an error during creation (as determined by ViewData["CreateError"]), an error message is displayed.

5. Scripts Section:

  • This section initializes a JavaScript variable with the list of sub-products from the ViewData, making it accessible to client-side scripts.
  • It also imports a specific JavaScript file (order.create.js) presumably containing logic related to creating orders.

In-depth Analysis:

  1. Tabbed Interface: Even though there's just one tab in this view, the design suggests that the application might have been designed to accommodate more tabs in the future.

  2. Dynamic Dropdown Population: The design suggests that the dropdowns for sub-product, width, length, level, and grid might be populated dynamically based on the selected product. The view contains placeholder comments suggesting where the values might be fetched from.

  3. User Information Auto-population: The "Prepared By" field is auto-populated using the logged-in user's information, making the process seamless for the user.

  4. Dynamic Table: The table for selected products is dynamic, allowing users to add multiple products to the order before submission.

  5. Client-Side Data Initialization: The view serializes some of its data into JavaScript variables. This is a common pattern to pass data from the server side to client-side scripts, ensuring that client-side scripts have the necessary data to function.

In summary, this view provides a user-friendly interface for creating new orders. It's designed with usability in mind, allowing users to select multiple products and their specifications before submitting the order. The use of dynamic dropdowns and tables makes the process seamless and efficient. Integrated error handling ensures that users are notified of any issues during the creation process.

$(document).ready(function () {

    $("#Product").change(
        (e) => setSubGroupDropdown()
    );

    $("#SubProduct").change(
        (e) => setParameterDropdown()
    );

    setSubGroupDropdown();

    var dataTablesOptions = {
        dom: 'tr',
        //scrollY: true,
        //scrollX: true,
        scrollCollapse: true,
        stateSave: true,
        paging: false,
        responsive: true,
        columnDefs: [
            {
                targets: 0,
                visible: false,
                orderable: false
            },
            {
                targets: [3, 4, 5, 6, 7, 8],
                orderable: false
            },
            {
                targets: 8,
                data: "Delete",
                render: function (data, type, row, meta) {
                    return `
                        <button type="button" class="btn btn-outline-danger"
                            onclick="removeOrderConfig(${row[0]}, ${meta.row})">
                            <i class="fas fa-trash-alt"></i>
                        </button>
                    `;
                },
                orderable: false,
                searchable: false
            }],
        aaSorting: [],
        language: {
            paginate: {
                previous: '<i class="fas fa-long-arrow-alt-left"></i>',
                next: '<i class="fas fa-long-arrow-alt-right"></i>',
            }
        }
    }
    //dataTablesOptions.scrollY = true;
    //dataTablesOptions.scrollX = true;
    //dataTablesOptions.scrollCollapse = true;
    dataTablesOptions.responsive = true;
    orderListTable = $("#orders-list").DataTable(dataTablesOptions);
    //console.log(orderListTable);
    $("#btnAddProduct").on("click", (e) => {
        addToSelectedOrderConfig();
    });

    $("#btnSubmit").on("click", (e) => {
        onCreateOrder();
    });
});


function onCreateOrder() {
    const orderNumber = $("#OrderNumber").val();
    const projectName = $("#ProjectName").val();
    const projectCity = $("#City").val();
    const customerName = $("#Customer").val();
    const carSpaceQuantity = $("#CarSpacesQuantity").val();

    isLoadingAdd();

    fetch(
        "/api/Orders",
        {
            headers: {
                "Content-Type": "application/json"
            },
            credentials: "include",
            method: "POST",
            body: JSON.stringify({
                OrderNumber: orderNumber,
                ProjectName: projectName,
                Customer: customerName,
                City: projectCity,
                CarSpacesQuantity: carSpaceQuantity,
                OrderConfig: orderConfigs
            })
        }
    ).then(response => {
        if (!response.ok) {
            return response.text().then(
                text => { throw new Error(text) }
            )
        }
        isLoadingRemove();

        toast("Create Order",
            "Order has been created successfully!",
            "success",
            () => location.href = "/Orders"
        );
    }).catch(response => {
        isLoadingRemove();
        toast("Error", response.message, "error");
    });
}

function showError(errorMessage) {
    toast("Errpr", errorMessage, "error");
}

var orderListTable;
var orderConfigs = [];
var orderConfigsDisplay = [];
var orderConfigCount = 0;

function addToSelectedOrderConfig() {
    const productId = $("#Product").val();
    const productText = $($("#Product option:selected")[0]).attr("data-value-text");
    const subProductId = $("#SubProduct").val();
    const subProductText = $($("#SubProduct option:selected")[0]).attr("data-value-text");
    const widthId = $("#Width").val();
    const widthText = $($("#Width option:selected")[0]).attr("data-value-text");
    const lengthId = $("#Length").val();
    const lengthText = $($("#Length option:selected")[0]).attr("data-value-text");
    const gridId = $("#Grid").val();
    const gridText = $($("#Grid option:selected")[0]).attr("data-value-text");
    const levelId = $("#Level").val();
    const levelText = $($("#Level option:selected")[0]).attr("data-value-text");
    const quantity = $("#Quantity").val();

    if (!quantity) {
        toast(
            "Missing data",
            "You have to specify Model Quantity!",
            "warning");
    } else {
        orderConfigs.push({
            OrderConfigId: orderConfigCount,
            ProductId: productId,
            SubProductId: subProductId,
            WidthId: widthId,
            LengthId: lengthId,
            LevelId: levelId,
            GridId: gridId,
            Quantity: quantity
        });

        orderListTable.row.add([
            orderConfigCount,
            productText,
            subProductText,
            widthText,
            lengthText,
            levelText,
            gridText,
            quantity,
            ""
        ]).draw(false);

        orderConfigCount++;
    }
}

function removeOrderConfig(orderConfigId, rowCount) {
        let index = orderConfigs.findIndex((oc) => oc.OrderConfigId == orderConfigId);
        if (index >= 0) {
            orderConfigs.splice(index, 1);
        }
        const row = orderListTable.row(rowCount);
        row.remove().draw();
}

function setSubGroupDropdown() {
    const selectedProductId = $("#Product").val();

    const subProductList = productConfigList.filter((pc) => pc.ProductId === selectedProductId);

    $("#SubProduct").find('option').remove();

    subProductList.forEach((subProduct) => {
        $("#SubProduct").append(`
            <option value='${subProduct.SubProductId}'
                data-value-text='${subProduct.SubProductName}'
            >${subProduct.SubProductName}</option>
        `);
    });

    setParameterDropdown();
}

function setParameterDropdown() {
    const selectedProductId = $("#Product").val();
    const selectedSubProductId = $("#SubProduct").val();

    const productConfig = productConfigList.filter((pc) =>
        pc.ProductId === selectedProductId &&
        pc.SubProductId === selectedSubProductId)[0];

    setSingleParameterDropdown(productConfig.Widths, "Width");
    setSingleParameterDropdown(productConfig.Lengths, "Length");
    setSingleParameterDropdown(productConfig.Grids, "Grid");
    setSingleParameterDropdown(productConfig.Levels, "Level");
}

function setSingleParameterDropdown(data, parameter) {
    let orderedData = data.filter((prm) => prm[`${parameter}Value`] != 'COMMON')
        .sort((param1, param2) => param1[`${parameter}Value`] - param2[`${parameter}Value`]);

    $(`#${parameter}`).find('option').remove();
    orderedData.forEach((param) => {
        $(`#${parameter}`).append(`
            <option value='${param[`${parameter}Id`]}'
                data-value-text='${param[`${parameter}Value`]}'
            >${param[`${parameter}Value`]}</option>
            `);
    });
}

The order.create.js file contains JavaScript functionalities for the "Order Create" page. Here's a breakdown of its main components:

1. Document Ready Function:

When the page has finished loading:

  • Event listeners are attached to the Product and SubProduct dropdowns to update the related dropdowns when their values change.
  • DataTables library is initialized for a table with specific options.
  • Event listeners are attached to the "Add Product" and "Submit" buttons to handle their respective functionalities.

2. Order Creation Function (onCreateOrder):

  • Extracts values from various input fields.
  • Makes a POST request to the server to create an order with the given data.
  • Shows success or error messages based on the response from the server.

3. Show Error Function (showError):

Displays an error message using the toast notification.

4. Order Configuration Management:

  • Variables (orderListTable, orderConfigs, etc.) are declared to manage order configurations.
  • addToSelectedOrderConfig: Adds a selected product configuration to the DataTable and the orderConfigs array.
  • removeOrderConfig: Removes a product configuration from the DataTable and the orderConfigs array.

5. Dropdown Management Functions:

  • setSubGroupDropdown: Updates the SubProduct dropdown based on the selected product.
  • setParameterDropdown: Sets the values for the Width, Length, Grid, and Level dropdowns based on the selected product and subproduct.
  • setSingleParameterDropdown: A helper function to populate individual dropdowns (like Width, Length, etc.) based on provided data.

Summary:

This script enhances the user experience on the "Order Create" page. It dynamically updates dropdown values based on user selections, manages product configurations in a DataTable, and communicates with the server to create a new order. The script utilizes the DataTables library for table management and shows notifications using a toast system.

$(document).ready(function () {
    let isBomTabClicked = false;

    $("#order-nav-bom").on("click", () => {
        if (!isBomTabClicked) {
            loadBoms();
            isBomTabClicked = true;
        }
    });

    $("#add-order-bom-child-modal").on('shown.bs.modal', function () {
        $("#add-order-bom-child-modal #PartId").focus();
        setParentPartDescription();
    });

    $("#add-order-bom-child-modal #PartId").select2({
        dropdownParent: $("#add-order-bom-child-modal")
    });

    $("#add-order-bom-child-modal #PartId").change(
        (e) => {
            setParentPartDescription();
        });

    $("#add-order-bom-child-modal #isEntireAssembly").prop("checked", true);
    $("#add-order-bom-child-modal #isEntireAssembly").change(
        (e) => {
            const isChecked = $("#add-order-bom-child-modal #isEntireAssembly").prop("checked");
            if (isChecked) {
                $("#add-order-bom-child-modal #assembly").removeClass("d-block");
                $("#add-order-bom-child-modal #assembly").addClass("d-none");
                $("#add-order-bom-child-modal #requireChildLevel").removeClass("d-none");
                $("#add-order-bom-child-modal #requireChildLevel").addClass("form-group");

            } else {
                $("#add-order-bom-child-modal #assembly").removeClass("d-none");
                $("#add-order-bom-child-modal #assembly").addClass("d-block");
                $("#add-order-bom-child-modal #requireChildLevel").removeClass("form-group");
                $("#add-order-bom-child-modal #requireChildLevel").addClass("d-none");

            }
        });

    $("#add-order-bom-child-modal #hasChild").prop("checked", true);

});
var objecOrderConfig;

function loadAddBomChildModal() {
    isLoadingAdd();

    //$("#add-order-bom-child-modal #PartId").val(parentPartId);
    //$("#add-order-bom-child-modal #ParentPartNumber").val(parentPartNumber);
    //$("#add-order-bom-child-modal #ParentPartDescription").val(parentPartName);
    const orderId = $("#OrderId").val();
    // Get Child Part
    fetch(
        "/api/OrderBoms/AvailableBomParts/" + orderId,
        {
            headers: {
                "Content-Type": "application/json"
            },
            credentials: "include",
            method: "GET",
        }
    ).then(response => {
        if (!response.ok) {
            return response.text().then(
                text => {
                    throw new Error(text)
                }
            )
        }
        return response.json();

    }).then(json => {
        $("#add-order-bom-child-modal #PartId").find('option').remove();

        json.forEach((part) => {
            $("#add-order-bom-child-modal #PartId").append(`
                    <option
                    value='${part.PartId}'
                    data-part-description='${part.Name}'
                    >${part.PartNumber}</option>
                `);
        });

        isLoadingRemove();
        $("#add-order-bom-child-modal #errorBomChild").text('');
        $("#add-order-bom-child-modal").modal('show');
    }).catch(response => {
        isLoadingRemove();
        toast("Error", response.message, "error");
    });

}

function loadOrderCriteria() {
    fetch(
        "/api/Orders/OrderDetails/" + $("#OrderId").val(),
        {
            headers: {
                "Content-Type": "application/json"
            },
            credentials: "include"
        }
    ).then(response => {
        if (!response.ok) {
            return response.text().then(
                text => {
                    throw new Error(text)
                }
            )
        }
        return response.json();
    }).then(json => {
        objecOrderConfig = json;
    });
}

function onAddNewBomChild() {
    $("#add-order-bom-child-modal #errorBomChild").text('');
    const partId = $("#add-order-bom-child-modal #PartId").val();
    const totalWeight = $("#add-order-bom-child-modal #TotalWeight").val();
    const unitWeight = $("#add-order-bom-child-modal #UnitWeight").val();
    const partRate = $("#add-order-bom-child-modal #PartRate").val();
    const totalCost = $("#add-order-bom-child-modal #TotalCost").val();

    const partPosition = $("#add-order-bom-child-modal #PartPosition").val();
    const partQuantity = $("#add-order-bom-child-modal #PartQuantity").val();
    const bomLine = $("#add-order-bom-child-modal #BomLine").val();
    const totalQuantity = $("#add-order-bom-child-modal #PartTotalQuantity").val();
    const isAssembly = $("#add-order-bom-child-modal #isEntireAssembly").prop("checked");
    const hasChild = $("#add-order-bom-child-modal #hasChild").prop("checked");

    if (!isAssembly) {
        if (!partPosition || partPosition <= 0) {
            $("#add-order-bom-child-modal #errorBomChild").text('Part Position must be a positive integer!!');
            return;
        }

        if (!partQuantity || partQuantity <= 0) {
            $("#add-order-bom-child-modal #errorBomChild").text('Part Quantity must be a positive integer!!');
            return;
        }

        if (!bomLine || bomLine <= 0) {
            $("#add-order-bom-child-modal #errorBomChild").text('BOM Line must be a positive integer!!');
            return;
        }

        if (!totalQuantity || totalQuantity <= 0) {
            $("#add-order-bom-child-modal #errorBomChild").text('Total Quantity must be a positive integer!!');
            return;
        }
    }

    isLoadingAdd();

    const orderId = $("#OrderId").val();

    fetch(
        "/api/OrderBoms/" + orderId,
        {
            headers: {
                "Content-Type": "application/json"
            },
            credentials: "include",
            method: "POST",
            body: JSON.stringify({
                OrderId: orderId,
                PartId: partId,
                PartPosition: partPosition,
                BomLine: bomLine,
                Quantity: partQuantity,
                TotalQuantity: totalQuantity,
                UnitWeight: unitWeight,
                TotalWeight: totalWeight,
                PartRate: partRate,
                TotalCost: totalCost,
                HasChild: hasChild
            })
        }
    ).then(response => {
        if (!response.ok) {
            return response.text().then(
                text => {
                    throw new Error(text)
                }
            )
        }
        isLoadingRemove();

        toast("Add Part to Order BOM",
            "Part has been added successfully!",
            "success",
            () => location.reload()
        );
    }).catch(response => {
        isLoadingRemove();
        toast("Error", response.message, "error");
    });
}

function setParentPartDescription() {
    let selectedOption = $("#add-order-bom-child-modal #PartId option:selected")[0];
    let partName = $(selectedOption).attr("data-part-description");
    $("#add-order-bom-child-modal #PartDescription").val(partName);
}

var objectOrderBoms;

function loadBoms() {
    isLoadingAdd();

    const orderId = $("#OrderId").val();

    fetch(
        "/api/OrderBoms/" + orderId,
        {
            headers: {
                "Content-Type": "application/json"
            },
            credentials: "include"
        }
    ).then(response => {
        if (!response.ok) {
            return response.text().then(
                text => {
                    throw new Error(text)
                }
            )
        }
        return response.json();
    }).then(json => {
        // TODO Code
        objectOrderBoms = json;
        setBomDataTable(json);
        isLoadingRemove();
    }).catch(error => {
        console.log(error);

        isLoadingRemove();
        toast("Failed to fetch", "Error: " + error.message, "error");

    });

    loadOrderCriteria();
}

var orderBomTable;

function setBomDataTable(orderBoms) {

    orderBoms.forEach((orderBom) => {

        let appendRow = `
            <tr>
                <td>
                    
                </td>
                <td>
                    ${orderBom.OrderBomId}
                </td>
                <td>
                    ${orderBom.PartGroupName}
                </td>
                 <td>
                    ${orderBom.PartNumber}
                </td>
                 <td>
                    ${orderBom.BomLine ?? ""}
                </td>
                <td>
                    ${orderBom.PartDescription}
                </td>
                <td>
                    ${orderBom.Specification ?? ""}
                </td>
                <td>
                    ${orderBom.PartPosition ?? ""}
                </td>
                <td>
                    ${orderBom.DrawingNo ?? ""}
                </td>
                <td>
                    ${orderBom.RefNo ?? ""}
                </td>
                <td>
                    ${orderBom.InfoWorldNo ?? ""}
                </td>
                <td>
                    ${orderBom.Uom}
                </td>
                <td>
                    ${orderBom.QuantityPerMachine ?? ""}
                </td>    
                <td>
                    ${orderBom.TotalQuantity ?? ""}
                </td>
                <td>
                    ${orderBom.UnitWeight ?? ""}
                </td>    
                <td>
                    ${orderBom.TotalWeight ?? ""}
                </td>
               <td>
                    ${orderBom.PartRate ?? ""}
                </td>
               <td>
                    ${orderBom.TotalCost ?? ""}
                </td>
                <td>
                    ${orderBom.PartType ?? ""}
                </td>
                
                <td>
                    <button type="button" class="btn btn-outline-danger"
                    name="btnDeleteOrderBom" onclick="onConfirmDeleteBom('${orderBom.OrderBomId}')">
                            <i class="fas fa-trash-alt"></i>
                        </button>
                </td>
            </tr>
        `;
        $("table#order-bom-list tbody").append(appendRow);

    });
    var groupColumn = 2;
    var orderBomDatatableOptions = {
        dom: 'BfrPtip',
        stateSave: true,
        searchPanes: {
            layout: 'columns-4',
            initCollapsed: true
        },
        lengthMenu: [
            [10, 25, 50, -1],
            ['10 rows', '25 rows', '50 rows', 'Show all']
        ],
        order: [[groupColumn, 'asc']],
        drawCallback: function (settings) {
            var api = this.api();
            var rows = api.rows({ page: 'current' }).nodes();
            var last = null;

            api
                .column(groupColumn, { page: 'current' })
                .data()
                .each(function (group, i) {
                    if (last !== group) {
                        $(rows)
                            .eq(i)
                            .before('<tr class="bom-list-group-bg"><td colspan="18" class="bom-list-group-txt"><span>' + group + '</span></td></tr>');
                        last = group;
                    }
                });
        },
        buttons: [
            'colvis',
            'pageLength',
            {
                extend: 'collection',
                className: 'btn btn-warning ml-2',
                text: 'Export Data',
                buttons: [
                    {
                        extend: 'excelHtml5',
                        text: '<i class="fas fa-file-excel"></i> Dispatch BOM Excel',
                        action: function () {
                            ExportDispatchedToExcel(`Dispatch-BOM-${$("#OrderNumber").val()}`);
                        }
                    },
                    {
                        extend: 'excelHtml5',
                        text: '<i class="fas fa-file-excel"></i> Detailed BOM Excel',
                        action: function () {
                            ExportDetailedToExcel(`Detailed-BOM-${$("#OrderNumber").val()}`);
                        }
                    },
                    {
                        extend: 'csvHtml5',
                        text: '<i class="fas fa-file-csv"></i> CSV',
                        title: `Dispatch BOM : ${$("#OrderNumber").val()}`,
                        filename: `Dispatch-BOM-${$("#OrderNumber").val()}`,
                        extension: '.csv',
                        stateSave: true,
                        exportOptions: {
                            columns: ':not(:last-child):not(:first-child):visible',
                            rows: function (idx, data, node) {
                                if ($("table#order-bom-list tbody tr.selected").length == 0) {
                                    return true;
                                }
                                if ($(node).hasClass("selected")) {
                                    return true;
                                }
                                return false;
                            }
                        }
                    },
                ]
            }
        ],
        columnDefs: [
            {
                targets: 0,
                orderable: false,
                searchable: false,
                render: function (data, type, row, meta) {

                    return `
                        <a href='#'
                        class='datatable-row-action' name='expand-child'>
                            <i class="fas fa-plus-circle" onclick="onExpandChildClick(this)"></i>
                        </a>
                    `;
                },

            },
            {
                targets: [1, groupColumn],
                visible: false,
                orderable: false,
                searchable: false
            },
            {
                targets: [4, 5, 6, 7, 8, 9, 10, 11, 12, 14],
                orderable: false,
                searchable: false
            },

        ],
        aaSorting: [],
        language: {
            paginate: {
                previous: '<i class="fas fa-long-arrow-alt-left"></i>',
                next: '<i class="fas fa-long-arrow-alt-right"></i>',
            }
        }
    }
    orderBomTable = $('#order-bom-list').DataTable(orderBomDatatableOptions);
    //console.log(orderBomTable.searchPanes);
    //orderBomTable.searchPanes.container().prependTo(orderBomTable.table().container());
    //orderBomTable.searchPanes.resizePanes();

    let bulkActions = `
                <div class="dropdown float-md-right text-center ml-md-3 mb-3 mb-md-0 datatables-actions" id="datatables-bulk-actions">
                    <button type="button" class="btn btn-info disabled dropdown-toggle dropleft"
                        data-toggle="dropdown" aria-expanded="false" aria-haspopup="true"
                        id="btn-dropdown-order-bom-bulk-actions">
                        <i class="fas fa-bars"></i>
                    </button>
                
                    <div class="dropdown-menu dropdown-menu-right">`;
    if (currentRoleId == 1) {
        bulkActions += `<a href="#" class="dropdown-item text-danger"
                                                id="bulk-delete-order-boms">
                                                <i class="fas fa-trash-alt"></i> Delete Order BOMs
                                            </a>`;
    }
    bulkActions += `
                    </div>
                </div>

        `;


    $("#order-bom-list_wrapper > div:first-child").before(bulkActions);

    $("#bulk-delete-order-boms").on("click", () => {
        let title = "";
        let selectedOrderBomIds = Array.from(orderBomTable.rows(".selected").data().map((rowData) => rowData[1]));
        if (selectedOrderBomIds.length > 1) {
            title = `Delete ${selectedOrderBomIds.length} Order BOMs`;
        } else {
            title = `Delete Order BOM`;
        }

        confirmDelete(
            {
                title: title,
                text: "Do you really want to delete these Order BOMs? This cannot be reverted!",
                deleteConfirmCallback: (isSoftDelete) => {
                    return fetch(
                        isSoftDelete ? "/api/OrderBoms/DeleteOrderBoms"
                            : "/api/OrderBoms/HardDeleteOrderBoms",
                        {
                            headers: {
                                "Content-Type": "application/json"
                            },
                            credentials: "include",
                            method: "POST",
                            body: JSON.stringify(selectedOrderBomIds)
                        }
                    ).then(response => {
                        if (!response.ok) {
                            return response.text().then(
                                text => { throw new Error(text) }
                            )
                        }
                        return response.json();
                    }).catch(error => {
                        Swal.showValidationMessage(error.message);
                    });
                }
            },
            {
                title: "Delete Order BOMs",
                details: "Order BOMs have been deleted successfully!",
                icon: 'success',
                confirmCallback: () => location.reload()
            }
        );
    });

    let addButtons = `
        <div class="additional-buttons float-md-left text-center ml-md-5 mb-3 mb-md-2">
            <a class="btn btn-success mr-2 disabled" id="btnUpdatePart">
                <i class="fas fa-edit mr-2"></i> Update Part
            </a>

            <a class="btn btn-success" id="btnAddPart" href="#">
                <i class="fas fa-plus mr-2"></i> Add Part
            </a>
        </div>
    `;

    $("#order-bom-list_wrapper > div#order-bom-list_filter").after(addButtons);

    $("a#btnAddPart").on("click", (e) => {
        loadAddBomChildModal();
    });

    $("a#btnUpdatePart").on("click", (e) => {
        const selectedPart = orderBomTable.row(".selected").data();
        const orderBomId = selectedPart[1];
        window.location.href = "/OrderBoms/Edit/" + orderBomId;
    });

    $("table#order-bom-list tbody").on("click", "tr", function () {
        if ($(this).find("table").length == 0
            && $(this).parents("table[name='order-child-list']").length == 0
        ) {
            if ($(this).hasClass("selected")) {
                //$("#btnUpdatePart").addClass("disabled");
                $(this).removeClass("selected");
            } else {
                //orderBomTable.$("tr.selected").removeClass("selected");
                $(this).addClass("selected");
                //$("#btnUpdatePart").removeClass("disabled");
            }

            if (orderBomTable.$("tr.selected").length == 1) {
                $("#btnUpdatePart").removeClass("disabled");
            } else {
                $("#btnUpdatePart").addClass("disabled");
            }

            if (orderBomTable.$("tr.selected").length > 0) {
                $("#btn-dropdown-order-bom-bulk-actions").removeClass("disabled");
            } else {
                $("#btn-dropdown-order-bom-bulk-actions").addClass("disabled");
            }

            //console.log(orderBomTable.rows(".selected").data());
        }

    });

}

function onConfirmDeleteBom(orderBomId) {
    confirmDelete(
        {
            title: "Delete Order BOM",
            text: "Do you really want to delete this Order BOM? The child parts of this Order BOM will be also deleted. This cannot be reverted!",
            deleteConfirmCallback: () => {
                return fetch(
                    "/api/OrderBoms/Delete/" + orderBomId,
                    {
                        headers: {
                            "Content-Type": "application/json"
                        },
                        credentials: "include",
                        method: "POST",
                    }
                ).then(response => {
                    if (!response.ok) {
                        return response.text().then(
                            text => {
                                throw new Error(text)
                            }
                        )
                    }
                    return response.json();
                }).catch(error => {
                    Swal.showValidationMessage(error.message);
                });
            }
        },
        {
            title: "Delete Order BOM",
            details: "Order BOM has been deleted successfully!",
            icon: 'success',
            confirmCallback: () => location.reload()
        }
    );
}

async function onExpandChildClick(element) {
    const tr = $(element).parents("tr");
    const row = orderBomTable.row(tr);
    if (row.child.isShown()) {
        $(element).removeClass("fa-minus-circle");
        $(element).addClass("fa-plus-circle");
        row.child.hide();
        tr.removeClass('shown');
    } else {
        // Open row
        const children = await getOrderBomChildParts(row.data()[1]);
        $(element).removeClass("fa-plus-circle");
        $(element).addClass("fa-minus-circle");
        if (children.length == 0) {
            row.child("No Child Part!").show();
        } else {
            row.child(childFormat(children)).show();
        }
        tr.addClass("shown");
    }
}
ExportDispatchedToExcel
async function ExportDispatchedToExcel(name) {
    const orderId = $("#OrderId").val();
    isLoadingAdd();
    $.ajax({
        url: "/api/OrderBoms/ExportDispatchedToExcel/" + orderId,
        method: 'GET',
        xhrFields: {
            responseType: 'blob'
        },
        success: function (data, status, xhr) {
            isLoadingRemove();
            var blob = new Blob([data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
            var url = URL.createObjectURL(blob);
            var link = document.createElement('a');
            link.href = url;
            link.download = name + '.xlsx';
            link.click();
            URL.revokeObjectURL(url);
        },
        error: function (xhr, status, error) {
            console.log(error)
            isLoadingRemove();
        }
    });

}
async function ExportDetailedToExcel(name) {
    const orderId = $("#OrderId").val();
    isLoadingAdd();
    $.ajax({
        url: "/api/OrderBoms/ExportDetailedToExcel/" + orderId,
        method: 'GET',
        xhrFields: {
            responseType: 'blob'
        },
        success: function (data, status, xhr) {
            isLoadingRemove();
            var blob = new Blob([data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
            var url = URL.createObjectURL(blob);
            var link = document.createElement('a');
            link.href = url;
            link.download = name + '.xlsx';
            link.click();
            URL.revokeObjectURL(url);
        },
        error: function (xhr, status, error) {
            console.log(error)
            isLoadingRemove();
        }
    });

}

//ExportDispatchedToExcel
async function ExportDispatchedToExcel(name) {
    const orderId = $("#OrderId").val();
    isLoadingAdd();
    $.ajax({
        url: "/api/OrderBoms/ExportDispatchedToExcel/" + orderId,
        method: 'GET',
        xhrFields: {
            responseType: 'blob'
        },
        success: function (data, status, xhr) {
            isLoadingRemove();
            var blob = new Blob([data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
            var url = URL.createObjectURL(blob);
            var link = document.createElement('a');
            link.href = url;
            link.download = name + '.xlsx';
            link.click();
            URL.revokeObjectURL(url);
        },
        error: function (xhr, status, error) {
            console.log(error)
            isLoadingRemove();
        }
    });

}

async function ExportDetailedToExcel(name) {
    const orderId = $("#OrderId").val();
    isLoadingAdd();
    $.ajax({
        url: "/api/OrderBoms/ExportDetailedToExcel/" + orderId,
        method: 'GET',
        xhrFields: {
            responseType: 'blob'
        },
        success: function (data, status, xhr) {
            isLoadingRemove();
            var blob = new Blob([data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
            var url = URL.createObjectURL(blob);
            var link = document.createElement('a');
            link.href = url;
            link.download = name + '.xlsx';
            link.click();
            URL.revokeObjectURL(url);
        },
        error: function (xhr, status, error) {
            console.log(error)
            isLoadingRemove();
        }
    });

}

async function getOrderBomChildParts(orderBomId) {
    isLoadingAdd();

    return await fetch(
        "/api/OrderBoms/Children/" + orderBomId,
        {
            headers: {
                "Content-Type": "application/json"
            },
            credentials: "include"
        }
    ).then(response => {
        if (!response.ok) {
            return response.text().then(
                text => {
                    throw new Error(text)
                }
            )
        }
        return response.json();
    }).then(json => {
        // TODO Code
        //setBomDataTable(json);
        isLoadingRemove();
        return json;
    }).catch(error => {
        console.log(error);

        isLoadingRemove();
        toast("Failed to fetch", "Error: " + error.message, "error");

    });
}

function childFormat(children) {
    let table = `
        <table class="table table-responsive-lg 
            table-hover datatable-show-all ms-5"
            name="order-child-list"
               id="order-child-list-${children[0].OrderBomId}" style="white-space: nowrap;width: fit-content">
            `;
    table += `
            <tbody>`;

    children.forEach((child) => {
        table += `
                <tr>
                    <td></td>
                    <td>${child.ParentPartNumber}</td>
                    <td>${child.BomLine ?? ""}</td>
                    <td>${child.ParentPartName}</td>
                    <td>${child.ParentPartSpecification}</td>
                    <td>${child.ChildPartPosition ?? ""}</td>
                    <td>${child.ParentPartDrawingNumber}</td>
                    <td>${child.ParentPartRefNumber}</td>
                    <td>${child.ParentPartInfoWorldNumber}</td>
                    <td>${child.ParentPartUom}</td>
                    <td>${child.Quantity ?? ""}</td>
                    <td>${child.ParentPartType}</td>`;
        table += `
                </tr>
        `;
    });

    table += `</tbody>
        </table>
    `;

    return table;
}

The JavaScript file order.edit.bom.js is primarily meant to handle the Bill of Materials (BOM) tab functionality for an order. Here's a high-level overview:

  1. Initialization:

    • When the document is ready, several event listeners are set up, including for the BOM tab click, modal show, and several form elements' value changes.
    • Some default values and properties are set for form elements.
  2. Fetching and Displaying Data:

    • loadBoms(): Fetches and displays the list of BOMs for a particular order.
    • loadOrderCriteria(): Fetches order details.
    • loadAddBomChildModal(): Prepares and shows a modal to add a child part to the BOM.
    • setBomDataTable(orderBoms): Populates the BOM table using the DataTables library.
  3. Event Handlers:

    • Handlers to focus on certain input fields when the modal is shown.
    • Handlers to update the form's layout based on checkbox changes.
    • Handlers to add or update BOM child parts.
    • Handlers to expand or collapse child parts for a BOM entry.
  4. CRUD Operations:

    • onAddNewBomChild(): Adds a new child part to the BOM.
    • onConfirmDeleteBom(orderBomId): Asks for confirmation and then deletes a BOM entry.
    • ExportDispatchedToExcel(name) and ExportDetailedToExcel(name): Exports BOM data to Excel in different formats.
  5. Utilities:

    • isLoadingAdd(), isLoadingRemove(): Presumably, these would show and hide a loading spinner, but their implementations aren't provided.
    • toast(): Likely shows a notification or message to the user, but its implementation isn't given.
    • confirmDelete(): Shows a confirmation prompt to the user before deleting.
  6. DOM Manipulations:

    • A lot of dynamic content is added to the DOM, especially tables using the jQuery append method.
  7. Ajax Calls:

    • Several fetch and jQuery $.ajax calls to interact with the server, fetch data, or perform CRUD operations.
  8. UI Feedback:

    • Various parts of the code provide feedback to the user using modals, toast notifications, and changing the state of UI elements.

Possible Improvements:

  1. Code Modularization: The code can be split into smaller, more focused functions for clarity and maintainability.
  2. Error Handling: More robust error handling can be added, especially for AJAX calls.
  3. Duplication: The two functions ExportDispatchedToExcel and ExportDetailedToExcel have duplicated code, which can be refactored.
  4. Comments: More comments can be added to explain the purpose and behavior of functions and complex code segments.
  5. Use of Modern ES6+ Features: The code can benefit from ES6+ features like arrow functions, async/await, etc., for better readability and structure.
$(document).ready(function () {

    orderConfigs.forEach((orderConfig, index) => {
        orderConfig.OrderConfigId = index;
    });

    $("#Product").change(
        (e) => setSubGroupDropdown()
    );

    $("#SubProduct").change(
        (e) => setParameterDropdown()
    );

    setSubGroupDropdown();

    var dataTablesOptions = {
        dom: 'rtip',
        bScrollInfinite: true,
        bScrollCollapse: true,
        sScrollY: '50vh',
        stateSave: true,
        paging: false,
        columnDefs: [
            {
                targets: 0,
                visible: false,
                orderable: false
            },
            {
                targets: [3, 4, 5, 6, 7, 8],
                orderable: false
            },
            {
                targets: 8,
                data: "Delete",
                render: function (data, type, row, meta) {
                    return `
                        <button type="button" class="btn btn-outline-danger"
                            onclick="removeOrderConfig(this)">
                            <i class="fas fa-trash-alt"></i>
                        </button>
                    `;
                },
                orderable: false,
                searchable: false
            }],
        aaSorting: [],
        language: {
            paginate: {
                previous: '<i class="fas fa-long-arrow-alt-left"></i>',
                next: '<i class="fas fa-long-arrow-alt-right"></i>',
            }
        }
    }

    orderListTable = $("#orders-list").DataTable(dataTablesOptions);
    console.log(orderListTable);
    $("#btnAddProduct").on("click", (e) => {
        addToSelectedOrderConfig();
    });

    $("#btnSubmit").on("click", (e) => {
        onUpdateOrder();
    });

    $("#btnRegenerate").on("click", (e) => {
        onRegenerateOrder();
    });
});


function onUpdateOrder() {
    const orderId = $("#OrderId").val();
    const orderNumber = $("#OrderNumber").val();
    const projectName = $("#ProjectName").val();
    const projectCity = $("#City").val();
    const customerName = $("#Customer").val();
    const carSpaceQuantity = $("#CarSpacesQuantity").val();

    isLoadingAdd();

    fetch(
        "/api/Orders/" + orderId,
        {
            headers: {
                "Content-Type": "application/json"
            },
            credentials: "include",
            method: "POST",
            body: JSON.stringify({
                OrderId: orderId,
                OrderNumber: orderNumber,
                ProjectName: projectName,
                Customer: customerName,
                City: projectCity,
                CarSpacesQuantity: carSpaceQuantity,
                OrderConfig: orderConfigs
            })
        }
    ).then(response => {
        if (!response.ok) {
            return response.text().then(
                text => { throw new Error(text) }
            )
        }
        isLoadingRemove();

        toast("Update Order",
            "Order has been updated successfully!",
            "success",
            () => location.href = "/Orders"
        );
    }).catch(response => {
        isLoadingRemove();
        toast("Error", response.message, "error");
    });
}

function onRegenerateOrder() {
    const orderId = $("#OrderId").val();

    isLoadingAdd();

    fetch(
        "/api/Orders/Regenerate/" + orderId,
        {
            headers: {
                "Content-Type": "application/json"
            },
            credentials: "include",
            method: "POST",
            body: JSON.stringify({
                OrderId: orderId,
                OrderConfig: orderConfigs
            })
        }
    ).then(response => {
        if (!response.ok) {
            return response.text().then(
                text => { throw new Error(text) }
            )
        }
        isLoadingRemove();

        toast("Regenerate Order",
            "Order BOM has been regenerated successfully!",
            "success",
            () => location.reload()
        );
    }).catch(response => {
        isLoadingRemove();
        toast("Error", response.message, "error");
    });
}


function showError(errorMessage) {
    toast("Errpr", errorMessage, "error");
}

var orderListTable;
//var orderConfigs = [];
//var orderConfigsDisplay = [];
var orderConfigCount = orderConfigs.length;

function addToSelectedOrderConfig() {
    const productId = $("#Product").val();
    const productText = $($("#Product option:selected")[0]).attr("data-value-text");
    const subProductId = $("#SubProduct").val();
    const subProductText = $($("#SubProduct option:selected")[0]).attr("data-value-text");
    const widthId = $("#Width").val();
    const widthText = $($("#Width option:selected")[0]).attr("data-value-text");
    const lengthId = $("#Length").val();
    const lengthText = $($("#Length option:selected")[0]).attr("data-value-text");
    const gridId = $("#Grid").val();
    const gridText = $($("#Grid option:selected")[0]).attr("data-value-text");
    const levelId = $("#Level").val();
    const levelText = $($("#Level option:selected")[0]).attr("data-value-text");
    const quantity = $("#Quantity").val();

    if (!quantity) {
        toast(
            "Missing data",
            "You have to specify Model Quantity!",
            "warning");
    } else {
        let foundIndex = orderConfigs.findIndex((oc) => {
            return oc.ProductId == productId
                && oc.SubProductId == subProductId
                && oc.WidthId == widthId
                && oc.LengthId == lengthId
                && oc.LevelId == levelId
                && oc.GridId == gridId;
        });
        if (foundIndex >= 0) {
            let oldQuantity = orderConfigs[foundIndex].Quantity;
            let newQuantity = parseInt(quantity) + parseInt(oldQuantity);
            orderConfigs[foundIndex] = {
                OrderConfigId: orderConfigCount,
                ProductId: productId,
                SubProductId: subProductId,
                WidthId: widthId,
                LengthId: lengthId,
                LevelId: levelId,
                GridId: gridId,
                Quantity: newQuantity
            }

            let existedRowId = orderListTable.rows(function (idx, data, node) {
                return data[1] == productText
                    && data[2] == subProductText
                    && data[3] == widthText
                    && data[4] == lengthText
                    && data[5] == levelText
                    && data[6] == gridText

            })[0];

            orderListTable.row(existedRowId).data(
                [
                    orderConfigCount,
                    productText,
                    subProductText,
                    widthText,
                    lengthText,
                    levelText,
                    gridText,
                    newQuantity,
                    ""
                ]
            ).draw();
        }
        else {
            orderConfigs.push({
                OrderConfigId: orderConfigCount,
                ProductId: productId,
                SubProductId: subProductId,
                WidthId: widthId,
                LengthId: lengthId,
                LevelId: levelId,
                GridId: gridId,
                Quantity: quantity
            });
            orderListTable.row.add([
                orderConfigCount,
                productText,
                subProductText,
                widthText,
                lengthText,
                levelText,
                gridText,
                quantity,
                ""
            ]).draw(false);
        }
        

        orderConfigCount++;
    }
}

//function removeOrderConfig(orderConfigId, rowCount) {
//    let index = orderConfigs.findIndex((oc) => oc.OrderConfigId == orderConfigId);
//    if (index >= 0) {
//        orderConfigs.splice(index, 1);
//    }

//    const row = orderListTable.row(rowCount);
//    row.remove().draw();
//}

function setSubGroupDropdown() {
    const selectedProductId = $("#Product").val();

    const subProductList = productConfigList.filter((pc) => pc.ProductId === selectedProductId);

    $("#SubProduct").find('option').remove();

    subProductList.forEach((subProduct) => {
        $("#SubProduct").append(`
            <option value='${subProduct.SubProductId}'
                data-value-text='${subProduct.SubProductName}'
            >${subProduct.SubProductName}</option>
        `);
    });

    setParameterDropdown();
}

function removeOrderConfig(eventTarget) {
    let row = orderListTable.row($(eventTarget).parents('tr'));
    let data = row.data();
    orderConfigs.splice(orderConfigs.findIndex((oc) => oc.OrderConfigId == data[0]), 1);
    //console.log(orderConfigs);
    row.remove().draw();
}

function setParameterDropdown() {
    const selectedProductId = $("#Product").val();
    const selectedSubProductId = $("#SubProduct").val();

    const productConfig = productConfigList.filter((pc) =>
        pc.ProductId === selectedProductId &&
        pc.SubProductId === selectedSubProductId)[0];

    setSingleParameterDropdown(productConfig.Widths, "Width");
    setSingleParameterDropdown(productConfig.Lengths, "Length");
    setSingleParameterDropdown(productConfig.Grids, "Grid");
    setSingleParameterDropdown(productConfig.Levels, "Level");
}

function setSingleParameterDropdown(data, parameter) {
    let orderedData = data.filter((prm) => prm[`${parameter}Value`] != 'COMMON')
        .sort((param1, param2) => param1[`${parameter}Value`] - param2[`${parameter}Value`]);

    $(`#${parameter}`).find('option').remove();
    orderedData.forEach((param) => {
        $(`#${parameter}`).append(`
            <option value='${param[`${parameter}Id`]}'
                data-value-text='${param[`${parameter}Value`]}'
            >${param[`${parameter}Value`]}</option>
            `);
    });
}

The code in order.edit.header.js. Here's a summary and description of its main functionalities:

  1. Initialization:

    • When the document is ready, the script performs the following:
      • Assigns an index to each orderConfig in the orderConfigs array.
      • Adds event listeners to the change events of #Product and #SubProduct dropdowns to set their respective dropdowns.
      • Initializes the orderListTable with the DataTable library with specific settings.
      • Adds event listeners to the click events of #btnAddProduct, #btnSubmit, and #btnRegenerate.
  2. Update Order (onUpdateOrder function):

    • This function is responsible for sending the updated order data to the server.
    • It gathers all necessary data from the form (Order ID, Order Number, Project Name, etc.).
    • Makes an API call (POST request) to /api/Orders/ with the order ID and updates the order with the gathered data.
    • Shows a success or error message based on the response.
  3. Regenerate Order (onRegenerateOrder function):

    • This function sends a request to regenerate an order.
    • It sends an API call (POST request) to /api/Orders/Regenerate/ with the order ID.
    • Shows a success message and reloads the page or shows an error message based on the response.
  4. Add to Selected Order Config (addToSelectedOrderConfig function):

    • This function is responsible for adding a product configuration to the order.
    • It fetches data from the product form (Product, SubProduct, Width, etc.).
    • Checks if the selected configuration already exists in the orderConfigs array.
    • If it exists, it updates the quantity; if not, it pushes a new configuration to the array.
    • Updates the orderListTable accordingly.
  5. Remove Order Config (removeOrderConfig function):

    • Removes a selected product configuration from the order.
    • It finds the configuration in the orderConfigs array using the OrderConfigId and removes it.
    • Also removes the respective row from the orderListTable.
  6. Set Dropdown Functions:

    • setSubGroupDropdown: Updates the #SubProduct dropdown based on the selected product.
    • setParameterDropdown: Updates the parameters dropdowns (Width, Length, Grid, Level) based on the selected product and subproduct.
    • setSingleParameterDropdown: A utility function that updates a specific parameter dropdown.
  7. Miscellaneous:

    • There are some commented-out lines and functions (like removeOrderConfig that takes orderConfigId and rowCount as arguments).
    • Error handling is managed by the showError function, which shows an error toast.
    • The DataTable is used to manage and display the order list.

Overall, the script is structured to manage product configurations in an order. It provides functionalities to add, update, regenerate, and remove configurations, and dynamically updates the form dropdowns based on user selections.

$(document).ready(function () {
    var dataTablesOptions = {
        dom: 'Bfrtip',
        scrollY: '50vh',
        scrollX: true,
        scrollCollapse: true,
        stateSave: true,
        lengthMenu: [
            [10, 25, 50, -1],
            ['10 rows', '25 rows', '50 rows', 'Show all']
        ],
        buttons: [
            'pageLength',
            {
                extend: 'collection',
                className: 'btn btn-warning ml-2',
                text: 'Export Data',
                buttons: [
                    {
                        extend: 'pdfHtml5',
                        text: '<i class="fas fa-file-pdf"></i> PDF',
                        orientation: 'landscape',
                        pageSize: 'LEGAL',
                        exportOptions: {
                            columns: ':not(:last-child):not(:first-child):visible',
                            rows: function (idx, data, node) {
                                if (selectedOrderIds.length <= 0) {
                                    return true;
                                }
                                if ($(node).attr("data-selected") == "true") {
                                    return true;
                                }
                                return false;
                            }
                        }
                    },
                    {
                        extend: 'excelHtml5',
                        text: '<i class="fas fa-file-excel"></i> Excel',
                        exportOptions: {
                            columns: ':not(:last-child):not(:first-child):visible',
                            rows: function (idx, data, node) {
                                if (selectedOrderIds.length <= 0) {
                                    return true;
                                }
                                if ($(node).attr("data-selected") == "true") {
                                    return true;
                                }
                                return false;
                            }
                        }
                    },
                    {
                        extend: 'csvHtml5',
                        text: '<i class="fas fa-file-csv"></i> CSV',
                        exportOptions: {
                            columns: ':not(:last-child):not(:first-child):visible',
                            rows: function (idx, data, node) {
                                if (selectedOrderIds.length <= 0) {
                                    return true;
                                }
                                if ($(node).attr("data-selected") == "true") {
                                    return true;
                                }
                                return false;
                            }
                        }
                    },
                    {
                        extend: 'print',
                        text: '<i class="fas fa-print"></i> Print',
                        exportOptions: {
                            columns: ':not(:last-child):not(:first-child):visible',
                            rows: function (idx, data, node) {
                                if (selectedOrderIds.length <= 0) {
                                    return true;
                                }
                                if ($(node).attr("data-selected") == "true") {
                                    return true;
                                }
                                return false;
                            }
                        },
                        customize: function (win) {
                            let css = '@page { size: landscape; }',
                                head = win.document.head ||
                                    win.document.getElementsByTagName('head')[0],
                                style = win.document.createElement('style');

                            style.type = 'text/css';
                            style.media = 'print';

                            if (style.styleSheet) {
                                style.styleSheet.cssText = css;
                            } else {
                                style.appendChild(win.document.createTextNode(css));
                            }

                            head.appendChild(style);
                        }
                    }

                ]
            }
        ],
        columnDefs: [
            {
                targets: 0,
                data: "Selection",
                render: function (data, type, row, meta) {

                    return `
                        <div class="custom-control custom-checkbox">
                            <input type="checkbox"
                                class="custom-control-input"
                                name="select-order"
                                onclick="onSelectOrder(this)"
                                data-order-id="${row[1]}"
                                id="select-order-${row[1]}">
                            <label class="custom-control-label" for="select-order-${row[1]}"></label>
                        </div>
                    `;
                },
                orderable: false,
                searchable: false
            },
            {
                targets: 1,
                visible: false,
                searchable: false
            },
            {
                targets: [3, 6, 7],
                orderable: false
            },
            {
                targets: 3,
                render: function (data, type, row, meta) {
                    return data;
                }
            },
            {
                targets: 2,
                render: function (data, type, row, meta) {
                    return `
                        <a href='/Orders/Edit/${row[1]}'
                        class='datatable-row-action'>${data}</a>
                    `;
                }
            },
            {
                targets: 8,
            },
            {
                targets: 9,
                data: "Action",
                render: function (data, type, row, meta) {
                    return `
                        <div class="dropdown datatables-actions" id="datatables-actions-${row[1]}">
                            <a href="#" class="text-body"
                                data-toggle="dropdown" aria-expanded="false">
                                <i class="fas fa-bars"></i>
                            </a>

                            <div class="dropdown-menu dropdown-menu-right">

                                

                            </div>
                        </div>
                    `;
                },
                orderable: false,
                searchable: false
            }],
        aaSorting: [],
        language: {
            paginate: {
                previous: '<i class="fas fa-long-arrow-alt-left"></i>',
                next: '<i class="fas fa-long-arrow-alt-right"></i>',
            }
        }
    }
    $('#orders-list').dataTable(dataTablesOptions);

    let bulkActions = `
                <div class="dropdown d-inline float-right ml-3 datatables-actions" id="datatables-bulk-actions">
                    <button type="button" class="btn btn-info disabled dropdown-toggle dropleft"
                        data-toggle="dropdown" aria-expanded="false" aria-haspopup="true"
                        id="btn-dropdown-bulk-actions">
                        <i class="fas fa-bars"></i>
                    </button>

                    <div class="dropdown-menu dropdown-menu-right">`;
                        if (currentRoleId == 1) {
                            bulkActions += `<a href="#" class="dropdown-item text-danger"
                                                id="bulk-delete-orders">
                                                <i class="fas fa-trash-alt"></i> Delete Orders
                                            </a>`;
                        }
    bulkActions += `
                    </div>
                </div>
        `;

    $("#orders-list_wrapper > div:first-child").before(bulkActions);

    $("#select-product-config-all").on("click", (e) => {
        const isChecked = $(e.target).prop('checked');
        selectedOrderIds = [];

        if (!isChecked) {
            $("#btn-dropdown-bulk-actions").addClass("disabled");
        }
        else {
            $("#btn-dropdown-bulk-actions").removeClass("disabled");

        }
        $("input[name='select-order']").each((index, element) => {
            $(element).prop('checked', isChecked);
            if (isChecked) {
                const productOrder = $(element).attr("data-order-id");
                selectedOrderIds.push(productOrder);
            }
        });

    });

    $("#bulk-delete-orders").on("click", () => {
        let title = "";
        if (selectedOrderIds.length > 1) {
            title = `Delete ${selectedOrderIds.length} Orders`;
        } else {
            title = `Delete order`;
        }

        confirmDelete(
            {
                title: title,
                text: "Do you really want to delete these Orders? This cannot be reverted!",
                deleteConfirmCallback: (isSoftDelete) => {
                    return fetch(
                        isSoftDelete ? "/api/Orders/DeleteOrders"
                            : "/api/Orders/HardDeleteOrders",
                        {
                            headers: {
                                "Content-Type": "application/json"
                            },
                            credentials: "include",
                            method: "POST",
                            body: JSON.stringify(selectedOrderIds)
                        }
                    ).then(response => {
                        if (!response.ok) {
                            return response.text().then(
                                text => { throw new Error(text) }
                            )
                        }
                        return response.json();
                    }).catch(error => {
                        Swal.showValidationMessage(error.message);
                    });
                }
            },
            {
                title: "Delete Orders",
                details: "Orders have been deleted successfully!",
                icon: 'success',
                confirmCallback: () => location.reload()
            }
        );
    });

    let addButtons = `
        <div class="additional-buttons float-md-left text-center ml-md-5 mb-3 mb-md-0">
            <a class="btn btn-success" href="/Orders/Create">
                <i class="fas fa-plus mr-2"></i> Add Order
            </a>
        </div>
    `;

    $("#orders-list_wrapper > div#orders-list_filter").after(addButtons);
});

function onSelectOrder(element) {
    const isChecked = $(element).prop('checked');
    const orderId = $(element).attr("data-order-id");

    const tableRow = $(element).parents("tr");
    tableRow.attr("data-selected", isChecked);

    if (isChecked) {
        addToSelectedOrder(orderId);
    } else {
        removeSelectedOrder(orderId);
        if (
            $("#select-order-all").prop('checked')
            == true) {
            $("#select-order-all").prop('checked', false)

        }
    }
}

var selectedOrderIds = [];

function addToSelectedOrder(orderId) {
    if (orderId) {
        selectedOrderIds.push(orderId);
        $("#btn-dropdown-bulk-actions").removeClass("disabled");
    }
}

function removeSelectedOrder(orderId) {
    if (orderId) {
        const index = selectedOrderIds.indexOf(orderId);
        if (index > -1) {
            selectedOrderIds.splice(index, 1);
            if (selectedOrderIds.length == 0) {
                $("#btn-dropdown-bulk-actions").addClass("disabled");

            }

        }

    }
}

The code you've shared seems to be from the index page of an order management system. Here's a breakdown of the main functionalities:

  1. Initialization:

    • Upon document readiness, the script performs the following:
      • Setups the DataTable for the orders list with specific settings, such as scrollable viewport, state-saving, custom length menu, and export buttons (PDF, Excel, CSV, Print).
      • Defines column rendering functions. For instance, the first column shows a checkbox for selecting an order, the second column contains the order's ID (hidden), and the third column contains a link to edit the order.
      • Appends a 'bulk actions' dropdown to the DataTable. This dropdown allows users to delete multiple orders at once, and its visibility depends on the user's role.
      • Appends an 'Add Order' button next to the search filter.
  2. Export Options:

    • The DataTable provides options for exporting data. When exporting, the script checks if specific rows are selected. If no rows are selected, all visible rows are exported; otherwise, only selected rows are exported.
  3. Bulk Deletion:

    • Users can select multiple orders using the checkboxes.
    • The #bulk-delete-orders click event confirms with the user before deletion. Depending on the user's choice, it can either soft delete or hard delete the selected orders.
    • The deletion action makes an API call to either /api/Orders/DeleteOrders or /api/Orders/HardDeleteOrders based on the type of deletion.
  4. Order Selection:

    • The onSelectOrder function is triggered when a checkbox is clicked. This function manages the selectedOrderIds array, which tracks the IDs of selected orders.
    • addToSelectedOrder and removeSelectedOrder are utility functions that manage the addition and removal of order IDs from the selectedOrderIds array.
  5. Miscellaneous:

    • There's a global variable selectedOrderIds which keeps track of all the currently selected order IDs.
    • There are helper functions for manipulating this list (addToSelectedOrder and removeSelectedOrder).
    • Some dynamic UI modifications are performed, like appending the 'Add Order' button and the 'bulk actions' dropdown.

In summary, this script is focused on the display and management of orders on an index page. It provides functionalities for viewing, selecting, bulk deleting, and navigating to individual order edit pages. The DataTable library enhances the table with features like pagination, search, and export options.

using KlausBOM.CustomHandlers.Authorization;
using KlausBOM.Domain;
using KlausBOM.Domain.Enumerations;
using KlausBOM.DTOs.Orders;
using KlausBOM.DTOs.ProductConfigs;
using KlausBOM.Models;
using KlausBOM.ServerUtilities;
using KlausBOM.Services.Orders;
using KlausBOM.Services.ProductConfigs;
using Microsoft.AspNetCore.Mvc;
using Repository.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace KlausBOM.Controllers.Api.Order
{
    [Route("api/Orders")]
    [ApiController]
    public class OrderApiController : ControllerBase
    {
        private OrderServices orderServices;
        private ProductConfigServices productConfigServices;

        public OrderApiController(IUnitOfWork work)
        {
            orderServices = new OrderServices(work);
            productConfigServices = new ProductConfigServices(work);
        }

        [KlausAuthorize(module: PermissionModule.ORDER,
            feature: PermissionFeature.CREATE)]
        [HttpPost("")]
        public async Task<IActionResult> CreateOrder(OrderCreateEditDto dto)
        {
            try
            {
                Domain.Order order = await
                    orderServices.CreateOrderAsync(dto);

                return StatusCode(200, new { message = "Create Order successfully!" });
            }
            catch (Exception ex)
            {
                return StatusCode(500, ex.GenerateErrorMessage());
            }
        }

        [KlausAuthorize(module: PermissionModule.ORDER,
            feature: PermissionFeature.EDIT)]
        [HttpPost("{orderId}")]
        public async Task<IActionResult> UpdateOrder(string orderId,
            OrderCreateEditDto dto)
        {
            try
            {
                if (!dto.OrderId.Equals(orderId))
                {
                    throw new Exception("Request is not valid!!");
                }

                Domain.Order order = await
                    orderServices.UpdateOrderAsync(dto);

                return StatusCode(200, new { message = "Update Order successfully!" });
            }
            catch (Exception ex)
            {
                return StatusCode(500, ex.GenerateErrorMessage());
            }
        }

        [KlausAuthorize(module: PermissionModule.ORDER,
            feature: PermissionFeature.EDIT)]
        [HttpPost("Regenerate/{orderId}")]
        public async Task<IActionResult> RegenerateOrderBom(string orderId,
            OrderCreateEditDto dto)
        {
            try
            {
                if (!dto.OrderId.Equals(orderId))
                {
                    throw new Exception("Request is not valid!!");
                }

                await orderServices.RegenerateOrderBomAsync(dto);

                return StatusCode(200, new { message = "Regenerate Order BOM successfully!" });
            }
            catch (Exception ex)
            {
                return StatusCode(500, ex.GenerateErrorMessage());
            }
        }

        [HttpGet("OrderDetails/{orderId}")]
        [KlausAuthorize]
        public async Task<IActionResult> GetOrderDetails(string orderId)
        {
            try
            {
                IEnumerable<ProductConfigOrderDto> productConfigs = await
                   productConfigServices.GetReleaseProductConfigsAsync();

                OrderCreateEditDto order = await orderServices.GetOrderAsync(orderId);
                IEnumerable<OrderConfig> orderConfigs = order.OrderConfig;

                List<OrderConfigViewModel> model = new List<OrderConfigViewModel>();
                int counter = 1;
                foreach (var orderConfig in orderConfigs)
                {
                    ProductConfigOrderDto productConfig = productConfigs.SingleOrDefault(
                                      pc => pc.ProductId.Equals(orderConfig.ProductId)
                                      && pc.SubProductId.Equals(orderConfig.SubProductId)
                                  );

                    Grid grid = productConfig.Grids.SingleOrDefault(
                                       g => g.GridId.Equals(orderConfig.GridId)
                                   );
                    Level level = productConfig.Levels.SingleOrDefault(
                                       lvl => lvl.LevelId.Equals(orderConfig.LevelId)
                                   );

                    Width width = productConfig.Widths.SingleOrDefault(
                                       w => w.WidthId.Equals(orderConfig.WidthId)
                                   );

                    Length length = productConfig.Lengths.SingleOrDefault(
                        l => l.LengthId.Equals(orderConfig.LengthId)
                    );

                    model.Add(new OrderConfigViewModel()
                    {
                        SrNo = counter++,
                        Product = productConfig.ProductName,
                        SubProduct = productConfig.SubProductName,
                        Width = width.WidthValue,
                        Length = length.LengthValue,
                        Grid = grid.GridValue,
                        Level = level.LevelValue,
                        Quantity = orderConfig.Quantity
                    });
                }



                return StatusCode(200, model);

            }
            catch (Exception ex)
            {
                return StatusCode(500, ex.GenerateErrorMessage());
            }
        }

        [KlausAuthorize(module: PermissionModule.ORDER,
            feature: PermissionFeature.SOFT_DELETE)]
        [HttpPost("DeleteOrders")]
        public async Task<IActionResult> DeleteParts([FromBody] IEnumerable<string> orderIds)
        {
            try
            {
                if (orderIds == null ||
                    orderIds.Count() <= 0)
                {
                    throw new Exception("No Order to Delete!!");
                }

                await orderServices.DeleteBulkOrdersAsync(orderIds);

                return StatusCode(200, new { message = "Orders deleted successfully!" });
            }
            catch (Exception ex)
            {
                return StatusCode(500, ex.GenerateErrorMessage());
            }
        }

        [KlausAuthorize(module: PermissionModule.ORDER,
            feature: PermissionFeature.DELETE)]
        [HttpPost("HardDeleteOrders")]
        public async Task<IActionResult> HardDeleteOrders([FromBody] IEnumerable<string> orderIds)
        {
            try
            {
                if (orderIds == null ||
                    orderIds.Count() <= 0)
                {
                    throw new Exception("No Order to Delete!!");
                }

                await orderServices.DeleteBulkOrdersAsync(orderIds, false);

                return StatusCode(200, new { message = "Orders deleted successfully!" });
            }
            catch (Exception ex)
            {
                return StatusCode(500, ex.GenerateErrorMessage());
            }
        }
    }
}

The code represents the OrderApiController, which is an API controller responsible for operations related to Orders in the KlausBOM system. I'll break down the structure and functionality of this controller:

1. Namespaces and Dependencies:

  • using statements include dependencies for authorization, domain entities, DTOs (Data Transfer Objects), services, and utility functions.

2. Class Declaration and Attributes:

  • [Route("api/Orders")]: Sets the base route for all methods in this controller to /api/Orders.
  • [ApiController]: Specifies that the class should be treated as an API controller with some specific behaviors.

3. Fields:

  • orderServices and productConfigServices: Private instances of service classes. These services presumably contain business logic and interact with the database.

4. Constructor:

  • The constructor takes an IUnitOfWork parameter named work. This is a common pattern for dependency injection and likely represents a unit of work pattern, which is used to batch multiple operations into a single transaction.
  • The constructor initializes orderServices and productConfigServices with this unit of work.

5. API Endpoints:

  1. CreateOrder:

    • Accepts a POST request without additional parameters in the route.
    • Takes in an OrderCreateEditDto object from the request body.
    • Creates a new order using orderServices.
    • Returns a success message or a generated error message.
  2. UpdateOrder:

    • Accepts a POST request with an orderId parameter in the route.
    • Updates an existing order using orderServices.
    • Validates the OrderId in the DTO against the orderId in the route.
    • Returns a success message or a generated error message.
  3. RegenerateOrderBom:

    • Similar to UpdateOrder but focuses on regenerating an Order's Bill of Materials (BOM).
  4. GetOrderDetails:

    • Accepts a GET request with an orderId parameter.
    • Fetches product configurations and order details.
    • Iterates through order configurations to build a list of OrderConfigViewModel items.
    • Returns this list or a generated error message.
  5. DeleteParts:

    • Accepts a POST request.
    • Deletes multiple orders based on a list of orderIds from the request body.
    • Returns a success message or a generated error message.
  6. HardDeleteOrders:

    • Similar to DeleteParts, but likely performs a hard delete operation (completely removing records from the database).

6. Attributes for Authorization:

  • Many endpoints use the [KlausAuthorize] attribute, which likely provides custom authorization functionality.
  • This attribute takes parameters for module and feature, which specify the required permissions to execute the method.

7. Error Handling:

  • Each endpoint is wrapped in a try-catch block.
  • Exceptions are caught and transformed into a custom error message using ex.GenerateErrorMessage().
  • This approach ensures that detailed exception information isn't leaked to clients while still providing a helpful error message.

8. Validation:

  • Before performing operations, there's validation (e.g., checking if DTO's OrderId matches the route's orderId).
  • Validation helps in ensuring data integrity and protecting against potential misuse or incorrect API calls.

Implications:

  • Separation of Concerns: The controller delegates business logic and data operations to service classes, keeping the controller focused on handling HTTP requests and responses.

  • Error Handling: A consistent approach to error handling provides a uniform response to clients when things go wrong.

  • Authorization: The use of custom attributes suggests a role-based or permission-based authorization system in place.

In summary, OrderApiController is a well-organized API controller focusing on CRUD operations related to Orders. It efficiently handles HTTP requests, delegates heavy operations to services, and ensures data validation and user authorization.

@using KlausBOM.DTOs.Orders
@using KlausBOM.Domain
@using KlausBOM.Models
@model OrderBomCreateEditDto

@{
        ViewData["Title"] = "Order ❱❱ Update Order BOM Part";
        Layout = "~/Views/Shared/_Layout.cshtml";

        ViewData["Breadcrumb"] = new Breadcrumb
        {
            PreviousPages = new Dictionary<string, string>()
                {
                    { "Orders", "/Orders"},
                    { $"Edit Order - {Model.Order.OrderNumber}",
                      $"/Orders/Edit/{Model.OrderId}"
                    }
                },
            CurrentPage = $"Update Part",
            CurrentInfo = $" ❱❱ {Model.PartNumber} | {Model.PartName}"
    };
}

@section Styles {
    <link type="text/css" rel="stylesheet" href="~/css/orderbom/orderbom.edit.css">
}

@if (ViewData["Error"] != null)
{
    <div class="alert alert-danger border-0 fade show">
        @ViewData["Error"]
    </div>
}
else
{

    <div class="details border-left border-bottom">
        <form method="post" id="edit-order-bom-form">
            @Html.AntiForgeryToken()
            <ul class="nav nav-tabs m-0" id="order-bom-details-tab" role="tablist">
                <li class="nav-item" role="presentation">
                    <span class="nav-link active" id="details-tab"
                      aria-selected="true">Details</span>
                </li>
            </ul>
            <input type="hidden" name="@nameof(Model.OrderId)"
               id="@nameof(Model.OrderId)"
               value="@Model.OrderId" />

            <input type="hidden" name="@nameof(Model.OrderBomId)"
               id="@nameof(Model.OrderBomId)"
               value="@Model.OrderBomId" />

               <input type="hidden" name="@nameof(Model.HasChild)"
               id="@nameof(Model.HasChild)"
               value="@Model.HasChild" />

            <div class="tab-content card p-2 mb-0 border-right" id="order-bom-details-content">
                <div class="tab-pane fade show active" id="details" role="tabpanel"
                 aria-labelledby="details-tab">
                    <div class="row">
                        <div class="col-md-3 mt-2 mt-md-0">
                            <label for="@nameof(Model.PartId)" class="form-label required-symbol">
                                Part Number
                            </label>
                            @if (!Model.IsOrderSpecific && Model.HasChild)
                            {
                                @* Cannot update the part*@
                                <input type="text" readonly id="@nameof(Model.PartNumber)"
                                   name="@nameof(Model.PartNumber)" class="form-control"
                                   value="@Model.PartNumber" />
                                <input type="hidden" id="@nameof(Model.PartId)"
                                   name="@nameof(Model.PartId)" class="form-control"
                                   value="@Model.PartId" />
                            }
                            else
                            {
                                <select id="@nameof(Model.PartId)"
                                name="@nameof(Model.PartId)" class="custom-select">
                                    @foreach (Part part in ViewData["Parts"] as IEnumerable<Part>)
                                    {
                                        if (part.PartId.Equals(Model.PartId))
                                        {
                                            <option value="@part.PartId" selected
                                    data-value-text="@part.PartNumber">@part.PartNumber</option>
                                        }
                                        else
                                        {
                                            <option value="@part.PartId"
                                    data-value-text="@part.PartNumber">@part.PartNumber</option>
                                        }

                                    }
                                </select>
                            }

                        </div>

                        <div class="col-md-6 mt-2 mt-md-0">
                            <label for="@nameof(Model.PartName)" class="form-label">
                                Part Description
                            </label>
                            <input type="text" readonly class="form-control"
                               name="@nameof(Model.PartName)" id="@nameof(Model.PartName)"
                               value="@Model.PartName" />
                        </div>
                        <div class="col-md-3 mt-2 mt-md-0">
                            <label for="@nameof(Model.Specification)" class="form-label">
                                Specification
                            </label>
                            <input type="text" readonly class="form-control"
                               name="@nameof(Model.Specification)" id="@nameof(Model.Specification)"
                               value="@Model.Specification" />
                        </div>
                    </div>

                    <div class="row">
                        <div class="col-md-3 mt-2 mt-md-0">
                            <label for="@nameof(Model.DrawingNumber)" class="form-label">
                                Drawing No.
                            </label>
                            <input type="text" readonly class="form-control"
                               name="@nameof(Model.DrawingNumber)" id="@nameof(Model.DrawingNumber)"
                               value="@Model.DrawingNumber" />
                        </div>

                        <div class="col-md-3 mt-2 mt-md-0">
                            <label for="@nameof(Model.RefNumber)" class="form-label">
                                Ref. No.
                            </label>
                            <input type="text" readonly class="form-control"
                               name="@nameof(Model.RefNumber)" id="@nameof(Model.RefNumber)"
                               value="@Model.RefNumber" />
                        </div>

                        <div class="col-md-3 mt-2 mt-md-0">
                            <label for="@nameof(Model.InfoWorldNumber)" class="form-label">
                                Infoworld Number
                            </label>
                            <input type="text" readonly class="form-control"
                               name="@nameof(Model.InfoWorldNumber)" id="@nameof(Model.InfoWorldNumber)"
                               value="@Model.InfoWorldNumber" />
                        </div>

                        <div class="col-md-3 mt-2 mt-md-0">
                            <label for="@nameof(Model.Uom)" class="form-label">
                                UOM
                            </label>
                            <input type="text" readonly class="form-control"
                               name="@nameof(Model.Uom)" id="@nameof(Model.Uom)"
                               value="@Model.Uom" />
                        </div>
                    </div>

                    <div class="row">
                        <div class="col-md-3 mt-2 mt-md-0">
                            <label for="@nameof(Model.PartType)" class="form-label">
                                Type
                            </label>
                            <input type="text" readonly class="form-control"
                               name="@nameof(Model.PartType)" id="@nameof(Model.PartType)"
                               value="@Model.PartType" />
                        </div>

                        <div class="col-md-3 mt-2 mt-md-0">
                            <label for="@nameof(Model.PartPosition)" class="form-label required-symbol">
                                Part Position
                            </label>
                            <input type="number" required class="form-control"
                               name="@nameof(Model.PartPosition)" id="@nameof(Model.PartPosition)"
                               value="@Model.PartPosition" />
                        </div>

                        <div class="col-md-3 mt-2 mt-md-0">
                            <label for="@nameof(Model.Quantity)" class="form-label required-symbol">
                                Qty/Unit
                            </label>
                            <input type="number" required class="form-control"
                               name="@nameof(Model.Quantity)" id="@nameof(Model.Quantity)"
                               value="@Model.Quantity" />
                        </div>

                        <div class="col-md-3 mt-2 mt-md-0">
                            <label for="@nameof(Model.TotalQuantity)" class="form-label required-symbol">
                                Total Quantity
                            </label>
                            <input type="number" required class="form-control"
                               name="@nameof(Model.TotalQuantity)" id="@nameof(Model.TotalQuantity)"
                               value="@Model.TotalQuantity" />
                        </div>
                    </div>

                    <div class="row mt-3 align-items-center">
                        <div class="col-lg-2 col-md-4">
                            <button type="submit" class="btn btn-danger" id="btnSubmit">
                                Save Information <i class="far fa-paper-plane ml-2"></i>
                            </button>
                        </div>
                        <div class="col-lg-10 col-md-6">
                            @if (ViewData["UpdateError"] != null)
                            {
                                <div class="alert alert-danger border-0 fade show mb-0">
                                    @ViewData["UpdateError"]
                                </div>
                            }
                        </div>

                    </div>
                </div>
            </div>
        </form>
    </div>

    @section Scripts {

    <script type="text/javascript">
        var partList = @Html.Raw(Json.Serialize((ViewData["Parts"] as IEnumerable<Part>)));

    </script>

    <script asp-append-version="true" src="~/js/orderbom/orderbom.edit.js"></script>
    }

}

The OrderBom/Edit.cshtml Razor view provides a UI for editing or updating the Bill of Materials (BOM) parts for a specific order in an ASP.NET Core MVC application. Let's dissect the main elements:

1. Directives, Model Declaration, and Layout:

  • Using the @using directive, the view imports relevant namespaces.
  • The model for this view is declared as OrderBomCreateEditDto.
  • Layout for this view is set to ~/Views/Shared/_Layout.cshtml, meaning this view will inherit a master layout.
  • Breadcrumb navigation is set up using the ViewData["Breadcrumb"].

2. Styles Section:

  • A CSS file, orderbom.edit.css, is linked to style the page elements.

3. Error Handling:

  • If there's an error message stored in ViewData["Error"], it gets displayed as a red alert at the top of the page.

4. Order BOM Edit Form:

  • A form is defined with the ID edit-order-bom-form to capture updates to the BOM part details.
  • Inside the form:
    • Hidden input fields store the order ID, order BOM ID, and a boolean flag indicating if the BOM part has a child.
    • Various input fields and dropdowns allow users to view and edit attributes such as part number, part description, drawing number, unit of measure (UOM), part type, part position, quantity per unit, and total quantity.
    • If the BOM part isn't order-specific and has a child, the part number can't be updated and is displayed as read-only. Otherwise, it's a dropdown allowing selection from available parts.
    • A submit button lets users save the updates.

5. Scripts Section:

  • A JavaScript variable named partList is defined and initialized with a serialized list of parts.
  • An external JavaScript file, orderbom.edit.js, is also linked to the page. This script presumably contains the necessary JavaScript functions to manage the behavior and interactions on this page.

Summary:

This view provides an interface for users to edit details of a BOM part associated with an order. The form is laid out clearly with labeled input fields, making it user-friendly. The integration of scripts suggests that there are interactive elements on the page, perhaps to validate inputs or provide dynamic behavior based on user choices. The presence of the breadcrumb navigation helps users understand their position within the application and navigate easily.

$(document).ready(function () {

    $("#PartId").change(
        (e) => setPartDetails()
    );
});

function setPartDetails() {
    const selectedPartId = $("#PartId").val();
    const part = partList.filter(p => p.PartId == selectedPartId)[0];
    $("#PartName").val(part.Name);
    $("#Specification").val(part.Specification);
    $("#DrawingNumber").val(part.DrawingNumber);
    $("#RefNumber").val(part.RefNumber);
    $("#InfoWorldNumber").val(part.InfowordNumber);
    $("#Uom").val(part.PartUom.Name);
    $("#PartType").val(part.PartType.Name);

}

The code you've shared seems to be from the edit page of an order bill of materials (BOM) system. Here's a breakdown of its functionalities:

  1. Initialization:

    • Upon document readiness, the script sets up an event listener for the change event on the #PartId dropdown. When the value of this dropdown changes, the setPartDetails function is invoked.
  2. Setting Part Details:

    • The setPartDetails function is the main functionality of this script.
    • When a user selects a part using the #PartId dropdown, this function:
      • Retrieves the selected part's ID.
      • Finds the corresponding part details from the partList (presumably a global variable or a variable defined elsewhere in the full script).
      • Populates various input fields (#PartName, #Specification, #DrawingNumber, #RefNumber, #InfoWorldNumber, #Uom, #PartType) with the relevant details from the selected part.
      • It seems this function is designed to auto-fill the part details based on the selected part ID, thus reducing manual input and potential mistakes.

In summary, this script is focused on helping the user auto-fill details of a part when editing an order's BOM. When a part is selected, its details are fetched from the partList and populated in the corresponding fields, making the edit process faster and more accurate.

Analysis: OrderBomApiController.cs

From the initial glance at the OrderBomApiController.cs file, it appears to be a .NET Core API controller related to Order BOM (Bill of Materials). Here's what we can infer:

  1. Namespace and Dependencies:

    • The controller resides in the namespace KlausBOM.Controllers.Api.Order.
    • Several namespaces are imported which indicates the controller makes use of various services, utilities, DTOs (Data Transfer Objects), domain classes, and other functionalities. Some of the namespaces like KlausBOM.ExcelHelpers and NPOI.XSSF.UserModel suggest Excel processing capabilities.
  2. Controller Declaration:

    • The API controller is named OrderBomApiController and inherits from ControllerBase.
    • It has been decorated with the route attribute [Route("api/OrderBoms")], which means that the base URL for accessing the endpoints in this controller will likely be yourdomain.com/api/OrderBoms.
  3. Member Variables:

    • Several services seem to be defined in the controller such as OrderBomServices, OrderServices, UserServices, ProductConfigServices, and OrderBomChildPartServices.
    • There's a static variable _env of type IWebHostEnvironment, indicating possible interactions with the web hosting environment.
  4. Column Mappings:

    • A dictionary named bomChildColumnMapping maps certain keys to their display names (probably for Excel export based on the imported namespaces and the dictionary keys/values).

[Route("api/OrderBoms")]
    [ApiController]
    public class OrderBomApiController : ControllerBase
    {
        private OrderBomServices orderBomServices;
        private OrderServices orderServices;
        private UserServices userServices;
        private ProductConfigServices productConfigServices;
        private static IWebHostEnvironment _env;
        private OrderBomChildPartServices orderBomChildPartServices;

        #region MappingColumns

        // Define column mapping dictionary
        Dictionary<string, string> bomChildColumnMapping = new Dictionary<string, string>()
        {
            { "ParentPartNumber", "Part No." },
            { "BomLine", "BOM Line" },
            { "ParentPartName", "Item Description" },
            { "ParentPartSpecification", "Specification" },
            { "ChildPartPosition", "Part Position" },
            { "ParentPartDrawingNumber", "Drawing No." },
            { "ParentPartRefNumber", "Ref. No." },
            { "ParentPartInfoWorldNumber", "InfoWorld No." },
            { "ParentPartUom", "UOM" },
            //{ "QuantityPerMachine", "Qty" },
            { "Quantity", "Qty" },
            { "TotalQuantity", "Total Qty" },
            { "UnitWeight", "Unit Weight" },
            { "TotalWeight", "Total Weight" },
            { "PartRate", "Part Rate" },
            { "TotalCost", "Total Cost" },
            { "ParentPartType", "Type" }
        };

        Dictionary<string, string> bomColumnMapping = new Dictionary<string, string>()
        {
            //{ "OrderBomId", "OrderBomId" },
            { "PartNumber", "Part No." },
            { "BomLine", "BOM Line" },
            { "PartDescription", "Item Description" },
            { "Specification", "Specification" },
            { "PartPosition", "Part Position" },
            { "DrawingNo", "Drawing No." },
            { "RefNo", "Ref. No." },
            { "InfoWorldNo", "InfoWorld No." },
            { "Uom", "UOM" },
            { "QuantityPerMachine", "Qty" },
            { "TotalQuantity", "Total Qty" },
            { "UnitWeight", "Unit Weight" },
            { "TotalWeight", "Total Weight" },
            { "PartRate", "Part Rate" },
            { "TotalCost", "Total Cost" },
            { "PartType", "Type" }
        };

        #endregion

This code snippet showcases the beginning structure of the OrderBomApiController class, which serves as an API controller for managing Order Bills of Materials (BOM).

Here's a breakdown:

  • Attributes:

    • [Route("api/OrderBoms")]: This attribute defines the base route for all the methods inside this API controller, which is /api/OrderBoms.
    • [ApiController]: This attribute classifies the class as an API controller, providing it with specific behaviors suitable for a web API.
  • Fields:

    • orderBomServices, orderServices, userServices, productConfigServices, and orderBomChildPartServices: These are private fields that seem to represent services that the API controller uses to interact with the underlying data or business logic.
    • _env: This is a static field of type IWebHostEnvironment. This interface provides information about the web hosting environment an application is running in. Given that it's static, it might be set in a static context (like a static constructor or initializer) which isn't shown here. It is often used for retrieving environment-specific settings or file paths.
  • MappingColumns Region:

    • This region contains two dictionaries, bomChildColumnMapping and bomColumnMapping, which map internal or database column names to more user-friendly display names. These mappings are likely used when generating reports or output that need to be user-friendly.
    • bomChildColumnMapping: Maps columns related to child parts of a BOM.
    • bomColumnMapping: Maps columns for the main BOM.
    • The commented-out mappings (e.g., //{ "OrderBomId", "OrderBomId" }) suggest these were once considered but are currently not in use.

Implications:

  1. This controller probably deals with CRUD operations for Order BOMs.
  2. The use of services suggests a separation of concerns, where the controller handles HTTP requests and responses while the services handle data operations and business logic.
  3. The mapping dictionaries will likely be used in methods that create user-friendly reports or exports, translating internal names to more readable column names.

[Route("api/OrderBoms")]
    [ApiController]
    public class OrderBomApiController : ControllerBase
    {
        private OrderBomServices orderBomServices;
        private OrderServices orderServices;
        private UserServices userServices;
        private ProductConfigServices productConfigServices;
        private static IWebHostEnvironment _env;
        private OrderBomChildPartServices orderBomChildPartServices;

        #region MappingColumns

        // Define column mapping dictionary
        Dictionary<string, string> bomChildColumnMapping = new Dictionary<string, string>()
        {
            { "ParentPartNumber", "Part No." },
            { "BomLine", "BOM Line" },
            { "ParentPartName", "Item Description" },
            { "ParentPartSpecification", "Specification" },
            { "ChildPartPosition", "Part Position" },
            { "ParentPartDrawingNumber", "Drawing No." },
            { "ParentPartRefNumber", "Ref. No." },
            { "ParentPartInfoWorldNumber", "InfoWorld No." },
            { "ParentPartUom", "UOM" },
            //{ "QuantityPerMachine", "Qty" },
            { "Quantity", "Qty" },
            { "TotalQuantity", "Total Qty" },
            { "UnitWeight", "Unit Weight" },
            { "TotalWeight", "Total Weight" },
            { "PartRate", "Part Rate" },
            { "TotalCost", "Total Cost" },
            { "ParentPartType", "Type" }
        };

        Dictionary<string, string> bomColumnMapping = new Dictionary<string, string>()
        {
            //{ "OrderBomId", "OrderBomId" },
            { "PartNumber", "Part No." },
            { "BomLine", "BOM Line" },
            { "PartDescription", "Item Description" },
            { "Specification", "Specification" },
            { "PartPosition", "Part Position" },
            { "DrawingNo", "Drawing No." },
            { "RefNo", "Ref. No." },
            { "InfoWorldNo", "InfoWorld No." },
            { "Uom", "UOM" },
            { "QuantityPerMachine", "Qty" },
            { "TotalQuantity", "Total Qty" },
            { "UnitWeight", "Unit Weight" },
            { "TotalWeight", "Total Weight" },
            { "PartRate", "Part Rate" },
            { "TotalCost", "Total Cost" },
            { "PartType", "Type" }
        };

        #endregion

The provided code snippet is a partial definition of the OrderBomApiController class. This class inherits from ControllerBase and is annotated to serve as an API controller, primarily handling operations related to Order Bills of Materials (BOM).

Analysis:

  1. Attributes:

    • [Route("api/OrderBoms")]: This attribute specifies that the base route for all endpoints in this controller will be /api/OrderBoms.
    • [ApiController]: This attribute denotes that the class should be treated as a controller that responds to web API requests.
  2. Fields:

    • Service Fields: The class has various service fields like orderBomServices, orderServices, userServices, productConfigServices, and orderBomChildPartServices. These fields represent different services that are likely used to interact with the database or handle business logic.
    • _env: A static field representing the web hosting environment. It's often used to get environment-specific settings or paths.
  3. MappingColumns Region:

    • This region defines two dictionaries that map internal or database column names to more user-friendly or display-oriented names.
      • bomChildColumnMapping: Maps database columns for child parts in a Bill of Materials (BOM) to their user-friendly names.
      • bomColumnMapping: Maps database columns for the main BOM to their user-friendly names.

Observations:

  • The dictionaries in the MappingColumns region seem to serve as a bridge between database column names and the names that are probably used in some front-end display or export functionality (like generating an Excel sheet).
  • The commented-out lines (e.g., //{ "QuantityPerMachine", "Qty" } and //{ "OrderBomId", "OrderBomId" }) in the dictionaries suggest that these mappings were considered at some point but are currently not in use.
  • The services are likely injected through the constructor, which isn't shown in this snippet, and are used to perform various operations on data.

        #region ColumnNames Lists

        private List<string> columns = new List<string>
        {
            "Part No.", "BOM Line", "Item Description", "Specification", "Part Position",
            "Drawing No.", "Ref. No.", "InfoWorld No.", "UOM", "Qty", "Unit Weight",
            "Total Weight", "Total Qty",
            "Part Rate", "Total Cost", "Type",
        };

        #endregion

This #region ColumnNames Lists provides a definition for a private list named columns. This list is essentially a collection of column names.

Analysis:

List Name: columns

Type: List<string>

Purpose: Represents column headers or names which are likely used for a DataTable structure or an Excel sheet.

Content: The list contains the following column names in the specified order:

  1. Part No.
  2. BOM Line
  3. Item Description
  4. Specification
  5. Part Position
  6. Drawing No.
  7. Ref. No.
  8. InfoWorld No.
  9. UOM (usually stands for Unit of Measurement)
  10. Qty (short for Quantity)
  11. Unit Weight
  12. Total Weight
  13. Total Qty
  14. Part Rate
  15. Total Cost
  16. Type

Potential Usage:

Given its name and the names of the columns, the columns list is likely used when constructing a DataTable or an Excel sheet related to Bill of Materials (BOM) or some parts inventory. The columns capture a variety of attributes related to parts, from their identification numbers to their specifications, quantities, weights, costs, and other related attributes.

This list serves as a reference for creating, manipulating, or exporting data structures, ensuring consistency in the columns used across different parts of the application.


        #region Private Helper Methods

        private static void AddColumnsToDataTable(DataTable dataTable, List<string> columnNames)
        {
            foreach (string columnName in columnNames)
            {
                dataTable.Columns.Add(columnName);
            }
        }

        private static void CopyDataTableRows(DataTable sourceTable, DataTable targetTable,
            Dictionary<string, string> columnMapping)
        {
            foreach (DataRow sourceRow in sourceTable.Rows)
            {
                DataRow targetRow = targetTable.NewRow();
                foreach (KeyValuePair<string, string> kvp in columnMapping)
                {
                    targetRow[kvp.Value] = sourceRow[kvp.Key];
                }

                targetTable.Rows.Add(targetRow);
            }
        }

        private XSSFCellStyle CreateCellStyle(XSSFWorkbook workbook, byte[]? fillColor, IndexedColors fontColor,
            short fontBoldWeight, HorizontalAlignment horizontalAlignment)
        {
            XSSFCellStyle style = (XSSFCellStyle)workbook.CreateCellStyle();
            if (fillColor != null)
            {
                XSSFColor datacolor = new XSSFColor(fillColor);
                style.SetFillForegroundColor(datacolor);
                style.FillPattern = FillPattern.SolidForeground;
            }

            style.Alignment = horizontalAlignment;

            IFont font = workbook.CreateFont();
            font.Color = fontColor.Index;
            font.Boldweight = fontBoldWeight;
            style.SetFont(font);

            return style;
        }

        private static void InsertLogo(XSSFWorkbook workbook, ISheet worksheet, string imagePath)
        {
            imagePath = Path.Combine(_env.WebRootPath, "image", imagePath);

            byte[] data = System.IO.File.ReadAllBytes(imagePath);
            int pictureIndex = workbook.AddPicture(data, PictureType.JPEG);
            ICreationHelper helper = workbook.GetCreationHelper();
            IDrawing drawing = worksheet.CreateDrawingPatriarch();
            IClientAnchor anchor = helper.CreateClientAnchor();
            anchor.Col1 = 0;
            anchor.Row1 = 1;
            IPicture picture = drawing.CreatePicture(anchor, pictureIndex);
            picture.Resize(1.5, 2.8);
        }

        private static void AutoCorrectColumnSizes(ISheet worksheet, int titleNum)
        {
            for (int col = 0; col < worksheet.GetRow(titleNum).LastCellNum; col++)
            {
                int maxLength = 0;
                for (int row = 0; row <= worksheet.LastRowNum; row++)
                {
                    IRow sheetRow = worksheet.GetRow(row);
                    if (sheetRow != null && sheetRow.GetCell(col) != null)
                    {
                        int cellLength = sheetRow.GetCell(col).ToString()!.Length;
                        if (cellLength > maxLength)
                        {
                            maxLength = cellLength;
                        }
                    }
                }

                worksheet.SetColumnWidth(col, (maxLength + 1) * 256);
            }
        }

        #endregion

Let's break down the provided helper methods inside the #region Private Helper Methods.

Helper Methods Analysis:

1. AddColumnsToDataTable

Purpose: Adds columns to a DataTable based on provided column names.

Parameters:

  • DataTable dataTable: The target DataTable to which columns should be added.
  • List<string> columnNames: A list of column names to be added.

Functional Breakdown:

  • Iterates through the columnNames list.
  • For each column name, a new column is added to the dataTable.

2. CopyDataTableRows

Purpose: Copies rows from a source DataTable to a target DataTable based on a mapping between their columns.

Parameters:

  • DataTable sourceTable: The source DataTable from which rows are to be copied.
  • DataTable targetTable: The target DataTable to which rows are to be copied.
  • Dictionary<string, string> columnMapping: A mapping between source table columns and target table columns.

Functional Breakdown:

  • Iterates through the rows of sourceTable.
  • For each row, a new row is created in the targetTable.
  • Using the columnMapping, values from the source row are copied to the target row.
  • The target row is then added to the targetTable.

3. CreateCellStyle

Purpose: Creates and returns a cell style for Excel with specified properties.

Parameters:

  • Several parameters specifying the style, like fill color, font color, font weight, and alignment.

Functional Breakdown:

  • A new style for an Excel cell is created.
  • If a fill color is provided, the cell background is set to that color.
  • Font properties (color, weight) are set based on the provided parameters.
  • The cell's horizontal alignment is set.
  • The final style is returned.

4. InsertLogo

Purpose: Inserts a logo/image into a specified Excel worksheet.

Parameters:

  • XSSFWorkbook workbook: The workbook object.
  • ISheet worksheet: The worksheet where the logo should be inserted.
  • string imagePath: The path to the logo/image file.

Functional Breakdown:

  • Constructs the full path to the image file.
  • Reads the image file's byte data.
  • Adds the image to the workbook.
  • Sets the position and dimensions for the image in the worksheet.
  • Resizes the image based on specified dimensions.

5. AutoCorrectColumnSizes

Purpose: Auto-adjusts the width of columns in an Excel worksheet based on the content's width.

Parameters:

  • ISheet worksheet: The worksheet whose columns need to be adjusted.
  • int titleNum: The row number which presumably contains the title/header.

Functional Breakdown:

  • Iterates through all the columns in the worksheet.
  • For each column, it checks each cell to find the maximum content width.
  • Sets the column width based on the maximum content width found.

Summary:

  • These helper methods facilitate various operations related to DataTable manipulation and Excel sheet formatting.
  • They are designed to simplify repetitive tasks and maintain cleaner code in the main methods by abstracting away the details of specific operations.

public OrderBomApiController(IUnitOfWork work, IWebHostEnvironment env)
        {
            _env = env;
            userServices = new UserServices(work);
            productConfigServices = new ProductConfigServices(work);
            orderServices = new OrderServices(work);
            orderBomServices = new OrderBomServices(work);
            orderBomChildPartServices = new OrderBomChildPartServices(work);
        }

        [HttpGet("{orderId}")]
        [KlausAuthorize(module: PermissionModule.ORDER,
            feature: PermissionFeature.VIEW)]
        public async Task<IActionResult> GetOrderBoms(string orderId)
        {
            try
            {
                IEnumerable<OrderBomListItemDto> dtos = await
                    orderBomServices.GetOrderBomsAsync(orderId);
                return StatusCode(200, dtos);
            }
            catch (Exception ex)
            {
                return StatusCode(500, ex.GenerateErrorMessage());
            }
        }

        [HttpGet("Children/{orderBomId}")]
        [KlausAuthorize(module: PermissionModule.ORDER,
            feature: PermissionFeature.VIEW)]
        public async Task<IActionResult> GetOrderBomChildren(string orderBomId)
        {
            try
            {
                IEnumerable<OrderBomChildPartListItemDto> dtos = await
                    orderBomChildPartServices.GetOrderBomChildPartsAsync(orderBomId);

                return StatusCode(200, dtos);
            }
            catch (Exception ex)
            {
                return StatusCode(500, ex.GenerateErrorMessage());
            }
        }

        [HttpGet("AvailableBomParts/{orderId}")]
        [KlausAuthorize]
        public async Task<IActionResult> GetAvailableBomParts(string orderId)
        {
            try
            {
                IEnumerable<Domain.Part> parts = await
                    orderBomServices.GetAvailableOrderBomPartAsync(orderId);
                return StatusCode(200, parts);
            }
            catch (Exception ex)
            {
                return StatusCode(500, ex.GenerateErrorMessage());
            }
        }

Let's break down the provided code.

Class Analysis: OrderBomApiController

Constructor:

public OrderBomApiController(IUnitOfWork work, IWebHostEnvironment env)

Parameters:

  • IUnitOfWork work: An interface representing a unit of work. This pattern typically provides the ability to make multiple database operations in a single transaction.

  • IWebHostEnvironment env: Provides information about the web hosting environment an application is running in.

Body Description:

  • Initializes the _env private variable with the provided env parameter.

  • Initializes multiple service objects (userServices, productConfigServices, orderServices, orderBomServices, orderBomChildPartServices) using the provided work parameter.


Method Analysis: GetOrderBoms

[HttpGet("{orderId}")]
[KlausAuthorize(module: PermissionModule.ORDER, feature: PermissionFeature.VIEW)]
public async Task<IActionResult> GetOrderBoms(string orderId)

Purpose:

Provides an endpoint to retrieve all BOM (Bill of Materials) items associated with a specific order.

Functional Breakdown:

  1. Calls the GetOrderBomsAsync method of the orderBomServices object to retrieve all BOM items for the provided orderId.

  2. Returns the retrieved BOM items with a 200 OK status.

  3. If an exception occurs, a 500 Internal Server Error status is returned, along with an error message generated from the exception.


Method Analysis: GetOrderBomChildren

[HttpGet("Children/{orderBomId}")]
[KlausAuthorize(module: PermissionModule.ORDER, feature: PermissionFeature.VIEW)]
public async Task<IActionResult> GetOrderBomChildren(string orderBomId)

Purpose:

Provides an endpoint to retrieve all child parts of a specific BOM item.

Functional Breakdown:

  1. Calls the GetOrderBomChildPartsAsync method of the orderBomChildPartServices object to retrieve all child parts for the provided orderBomId.

  2. Returns the retrieved child parts with a 200 OK status.

  3. If an exception occurs, a 500 Internal Server Error status is returned, along with an error message generated from the exception.


Method Analysis: GetAvailableBomParts

[HttpGet("AvailableBomParts/{orderId}")]
[KlausAuthorize]
public async Task<IActionResult> GetAvailableBomParts(string orderId)

Purpose:

Provides an endpoint to retrieve all available BOM parts for a specific order.

Functional Breakdown:

  1. Calls the GetAvailableOrderBomPartAsync method of the orderBomServices object to retrieve all available BOM parts for the provided orderId.

  2. Returns the retrieved BOM parts with a 200 OK status.

  3. If an exception occurs, a 500 Internal Server Error status is returned, along with an error message generated from the exception.


Overall Summary:

  • The OrderBomApiController class provides API endpoints related to Order BOM operations.

  • The class constructor initializes multiple services that facilitate operations related to users, product configurations, orders, BOMs, and BOM child parts.

  • The methods in the class handle retrieval operations related to BOMs, such as fetching BOM items for a specific order, retrieving child parts for a specific BOM item, and getting available BOM parts for a specific order.


        [HttpPost("{orderId}")]
        [KlausAuthorize(module: PermissionModule.ORDER,
            feature: PermissionFeature.CREATE)]
        public async Task<IActionResult> AddOrderBomPart(string orderId, OrderBomCreateEditDto orderBom)
        {
            try
            {
                if (!orderId.Equals(orderBom.OrderId))
                {
                    throw new Exception("Invalid requuest! Please check again...");
                }

                await orderBomServices.AddOrderBomPartAsync(orderBom);
                return StatusCode(200, new { message = "Add Part successfully!" });
            }
            catch (Exception ex)
            {
                return StatusCode(500, ex.GenerateErrorMessage());
            }
        }

Let's dive into the analysis of the AddOrderBomPart method.

Method Analysis: AddOrderBomPart

Signature:

[HttpPost("{orderId}")]
[KlausAuthorize(module: PermissionModule.ORDER, feature: PermissionFeature.CREATE)]
public async Task<IActionResult> AddOrderBomPart(string orderId, OrderBomCreateEditDto orderBom)

Attributes:

  • [HttpPost("{orderId}")]: This attribute indicates that the method responds to HTTP POST requests and expects the orderId as part of the URL. An example request to this endpoint could look like /1234 where 1234 is the orderId.

  • [KlausAuthorize(module: PermissionModule.ORDER, feature: PermissionFeature.CREATE)]: This custom authorization attribute ensures that the user or role invoking this method has the necessary permissions, specifically for the ORDER module and the CREATE feature.

Parameters:

  • string orderId: Represents the ID of the Order to which the BOM part is to be added.
  • OrderBomCreateEditDto orderBom: Represents the data transfer object containing the details of the BOM part that is to be added.

Return Type:

  • Task<IActionResult>: Represents an asynchronous operation that returns an IActionResult, used to produce an HTTP response.

Description: The method aims to add a BOM (Bill of Materials) part to a specific order based on the given orderId.

Functional Breakdown:

  1. Input Validation:

    • It checks if the orderId parameter matches the OrderId property of the orderBom object.
    • If they don't match, an exception is thrown with the message "Invalid request! Please check again...".
  2. Add Operation:

    • Calls the AddOrderBomPartAsync method of the orderBomServices object, passing the orderBom DTO. This likely adds the BOM part to the database or updates existing records.
  3. Response Formation:

    • If the add operation is successful, the method returns a 200 OK status code with a message indicating the successful addition of the BOM part.
    • If any exception occurs during the process, a 500 Internal Server Error status code is returned, accompanied by an error message generated from the exception.

Purpose: This method provides an API endpoint to allow clients to add or update a BOM part associated with a specific order. It ensures the validity of the request, performs the add/update operation, and then returns an appropriate HTTP response to the client.

In essence, the AddOrderBomPart method is designed to facilitate the addition or update of BOM parts associated with specific orders, ensuring data integrity by checking the consistency between the provided orderId and the OrderId property of the orderBom DTO.


        [HttpPost("Delete/{orderBomId}")]
        [KlausAuthorize(module: PermissionModule.ORDER,
            feature: PermissionFeature.DELETE)]
        public async Task<IActionResult> DeleteOrderBom(string orderBomId)
        {
            try
            {
                await orderBomServices.DeleteOrderBomAsync(orderBomId, false);
                return StatusCode(200, new { message = "Delete Order BOM successfully!" });
            }
            catch (Exception ex)
            {
                return StatusCode(500, ex.GenerateErrorMessage());
            }
        }

Let's analyze the provided DeleteOrderBom method.

Method Analysis: DeleteOrderBom

Signature:

[HttpPost("Delete/{orderBomId}")]
[KlausAuthorize(module: PermissionModule.ORDER, feature: PermissionFeature.DELETE)]
public async Task<IActionResult> DeleteOrderBom(string orderBomId)

Attributes:

  • [HttpPost("Delete/{orderBomId}")]: This attribute indicates that the method responds to HTTP POST requests and expects the orderBomId as a part of the URL. For example, a request to this endpoint might look like /Delete/1234 where 1234 is the orderBomId.

  • [KlausAuthorize(module: PermissionModule.ORDER, feature: PermissionFeature.DELETE)]: This custom authorization attribute ensures that the user or role invoking this method has the necessary permissions, specifically for the ORDER module and the DELETE feature.

Parameters:

  • string orderBomId: Represents the ID of the Order BOM that is intended to be deleted.

Return Type:

  • Task<IActionResult>: Represents an asynchronous operation that returns an IActionResult, used to produce an HTTP response.

Description: The method is designed to perform a delete operation on a single Order BOM identified by its ID.

Functional Breakdown:

  1. Delete Operation:

    • Calls the DeleteOrderBomAsync method of the orderBomServices object, passing the orderBomId and a boolean flag set to false. This boolean likely indicates that it's a hard delete operation (permanently removes the data).
  2. Response Formation:

    • If the delete operation is successful, the method returns a 200 OK status code with a message indicating the successful deletion of the Order BOM.
    • If any exception occurs during the process, a 500 Internal Server Error status code is returned, along with an error message generated from the exception.

Purpose: This method provides an API endpoint to allow clients to request a hard delete operation on a specific Order BOM. It ensures the validity of the request, performs the delete operation, and then returns an appropriate HTTP response to the client.

In essence, while the previous methods (HardDeleteOrderBoms and DeleteOrderBoms) allowed for bulk deletion of Order BOMs by accepting a collection of IDs, the DeleteOrderBom method focuses on the deletion of a single Order BOM based on its specific ID.


[KlausAuthorize(module: PermissionModule.ORDER,
            feature: PermissionFeature.SOFT_DELETE)]
        [HttpPost("DeleteOrderBoms")]
        public async Task<IActionResult> DeleteOrderBoms([FromBody] IEnumerable<string> orderBomIds)
        {
            try
            {
                if (orderBomIds == null ||
                    orderBomIds.Count() <= 0)
                {
                    throw new Exception("No Order BOM to Delete!!");
                }

                await orderBomServices.DeleteBulkOrderBomsAsync(orderBomIds);

                return StatusCode(200, new { message = "Order BOMs deleted successfully!" });
            }
            catch (Exception ex)
            {
                return StatusCode(500, ex.GenerateErrorMessage());
            }
        }

Let's delve into the analysis of the DeleteOrderBoms method.

Method Analysis: DeleteOrderBoms

Signature:

[KlausAuthorize(module: PermissionModule.ORDER, feature: PermissionFeature.SOFT_DELETE)]
[HttpPost("DeleteOrderBoms")]
public async Task<IActionResult> DeleteOrderBoms([FromBody] IEnumerable<string> orderBomIds)

Attributes:

  • [KlausAuthorize(module: PermissionModule.ORDER, feature: PermissionFeature.SOFT_DELETE)]: This custom authorization attribute likely ensures that the user or role invoking this method has the necessary permissions, specifically for the ORDER module and the SOFT_DELETE feature.

  • [HttpPost("DeleteOrderBoms")]: Indicates that this method responds to HTTP POST requests at the "DeleteOrderBoms" endpoint.

Parameters:

  • IEnumerable<string> orderBomIds: Represents a collection of Order BOM IDs intended to be deleted. The [FromBody] attribute indicates that these IDs should be part of the request body.

Return Type:

  • Task<IActionResult>: Represents an asynchronous operation that returns an IActionResult, which is used to produce an HTTP response.

Description: The method is designed to perform a soft delete operation on a collection of Order BOMs identified by their IDs.

Functional Breakdown:

  1. Input Validation:

    • It checks if orderBomIds is null or if its count is zero.
    • If either condition is met, an exception is thrown with the message "No Order BOM to Delete!!".
  2. Delete Operation:

    • Calls the DeleteBulkOrderBomsAsync method of the orderBomServices object, passing only the orderBomIds. Unlike the HardDeleteOrderBoms method, this one doesn't pass the boolean flag, implying that the default behavior of the service method might be a soft delete.
  3. Response Formation:

    • If the delete operation is successful, the method returns a 200 OK status code with a message indicating the successful deletion of the Order BOMs.
    • If any exception occurs during the process, a 500 Internal Server Error status code is returned, along with an error message generated from the exception.

Purpose: This method provides an API endpoint to allow clients to request a soft delete operation on a collection of Order BOMs. It ensures the validity of the request, performs the delete operation, and then returns an appropriate HTTP response to the client.

Comparison with HardDeleteOrderBoms: Both methods are quite similar in structure and functionality. The primary distinction is in the type of delete operation they perform. While HardDeleteOrderBoms seemingly performs a hard delete (permanent removal), DeleteOrderBoms likely performs a soft delete, which typically involves marking records as deleted without actually removing them from the database. The specific behavior of the delete operations would be determined by the implementation of the DeleteBulkOrderBomsAsync method in the orderBomServices object.


        [KlausAuthorize(module: PermissionModule.ORDER,
            feature: PermissionFeature.DELETE)]
        [HttpPost("HardDeleteOrderBoms")]
        public async Task<IActionResult> HardDeleteOrderBoms([FromBody] IEnumerable<string> orderBomIds)
        {
            try
            {
                if (orderBomIds == null ||
                    orderBomIds.Count() <= 0)
                {
                    throw new Exception("No Order BOM to Delete!!");
                }

                await orderBomServices.DeleteBulkOrderBomsAsync(orderBomIds, false);

                return StatusCode(200, new { message = "Order BOMs deleted successfully!" });
            }
            catch (Exception ex)
            {
                return StatusCode(500, ex.GenerateErrorMessage());
            }
        }

Let's analyze the provided method, HardDeleteOrderBoms.

Method Analysis: HardDeleteOrderBoms

Signature:

[KlausAuthorize(module: PermissionModule.ORDER, feature: PermissionFeature.DELETE)]
[HttpPost("HardDeleteOrderBoms")]
public async Task<IActionResult> HardDeleteOrderBoms([FromBody] IEnumerable<string> orderBomIds)

Attributes:

  • [KlausAuthorize(module: PermissionModule.ORDER, feature: PermissionFeature.DELETE)]: This attribute is presumably a custom authorization attribute. It likely ensures that the user or role invoking this method has the necessary permissions, specifically for the ORDER module and the DELETE feature.
  • [HttpPost("HardDeleteOrderBoms")]: This attribute denotes that the method responds to HTTP POST requests at the "HardDeleteOrderBoms" endpoint.

Parameters:

  • IEnumerable<string> orderBomIds: This represents a collection of Order BOM IDs that are intended to be deleted. The [FromBody] attribute suggests that these IDs are expected to be part of the request body.

Return Type:

  • Task<IActionResult>: Represents an asynchronous operation that returns an IActionResult, which is used to produce an HTTP response.

Description: The method is designed to perform a hard delete operation on a collection of Order BOMs identified by their IDs.

Functional Breakdown:

  1. Input Validation:

    • Checks if orderBomIds is null or if its count is zero.
    • If either condition is met, an exception is thrown with the message "No Order BOM to Delete!!".
  2. Delete Operation:

    • Calls the DeleteBulkOrderBomsAsync method of the orderBomServices object, passing the orderBomIds and a boolean flag false (likely indicating that it's a hard delete operation, as opposed to a soft delete).
  3. Response Formation:

    • If the delete operation is successful, the method returns a 200 OK status code with a message indicating the successful deletion of the Order BOMs.
    • If any exception occurs during the process, a 500 Internal Server Error status code is returned, along with an error message generated from the exception. The method GenerateErrorMessage is presumably an extension or helper method that produces a user-friendly error message based on the exception.

Purpose: This method provides an API endpoint to allow clients to request a hard delete operation on a collection of Order BOMs. It ensures the validity of the request, performs the delete operation, and then returns an appropriate HTTP response to the client.


        private async Task<List<OrderConfigViewModel>> GetOrderDetails(string orderId)
        {
            try
            {
                IEnumerable<ProductConfigOrderDto> productConfigs = await
                    productConfigServices.GetReleaseProductConfigsAsync();

                OrderCreateEditDto order = await orderServices.GetOrderAsync(orderId);
                IEnumerable<OrderConfig> orderConfigs = order.OrderConfig;

                List<OrderConfigViewModel> model = new List<OrderConfigViewModel>();
                int counter = 1;
                foreach (var orderConfig in orderConfigs)
                {
                    ProductConfigOrderDto productConfig = productConfigs.SingleOrDefault(
                        pc => pc.ProductId.Equals(orderConfig.ProductId)
                              && pc.SubProductId.Equals(orderConfig.SubProductId)
                    );

                    Grid grid = productConfig.Grids.SingleOrDefault(
                        g => g.GridId.Equals(orderConfig.GridId)
                    );
                    Level level = productConfig.Levels.SingleOrDefault(
                        lvl => lvl.LevelId.Equals(orderConfig.LevelId)
                    );

                    Width width = productConfig.Widths.SingleOrDefault(
                        w => w.WidthId.Equals(orderConfig.WidthId)
                    );

                    Length length = productConfig.Lengths.SingleOrDefault(
                        l => l.LengthId.Equals(orderConfig.LengthId)
                    );

                    model.Add(new OrderConfigViewModel()
                    {
                        SrNo = counter++,
                        Product = productConfig.ProductName,
                        SubProduct = productConfig.SubProductName,
                        Width = width.WidthValue,
                        Length = length.LengthValue,
                        Grid = grid.GridValue,
                        Level = level.LevelValue,
                        Quantity = orderConfig.Quantity
                    });
                }


                return model;
            }
            catch (Exception ex)
            {
                return null;
            }
        }

Here's an in-depth analysis of the GetOrderDetails method:

Method Analysis: GetOrderDetails

Signature:

private async Task<List<OrderConfigViewModel>> GetOrderDetails(string orderId)

Parameters:

  • string orderId: Represents the ID of the order.

Return Type:

  • Task<List<OrderConfigViewModel>>: Represents an asynchronous task that will return a list of OrderConfigViewModel objects.

Description: This method retrieves the detailed configuration of an order and returns a list of view models (OrderConfigViewModel) that represent the order's configurations.

Functional Breakdown:

  1. Data Retrieval:

    • Retrieves all released product configurations.
    • Retrieves the order details for the given orderId.
    • Extracts the configurations associated with the order.
  2. Model Population:

    • Initializes an empty list of OrderConfigViewModel.
    • Iterates through each configuration associated with the order.
      • For each configuration, it identifies the associated product configuration, grid, level, width, and length details.
      • Populates the OrderConfigViewModel with the extracted details and appends it to the list.
    • The counter (SrNo) is incremented for each iteration, providing a sequence number for the view model.
  3. Exception Handling:

    • If any exception occurs during the process, the method returns null.

Purpose: This method is designed to fetch the detailed configurations for a specific order and map them to a view model (OrderConfigViewModel). This view model provides a structured representation of the order's configurations, making it easier to work with in other parts of the application or for display purposes.

The OrderConfigViewModel is likely a simple data structure or DTO (Data Transfer Object) that holds properties such as SrNo, Product, SubProduct, Width, Length, Grid, Level, and Quantity.


        private async Task<List<OrderConfigViewModel>> GetOrderDetails(string orderId)
        {
            try
            {
                IEnumerable<ProductConfigOrderDto> productConfigs = await
                    productConfigServices.GetReleaseProductConfigsAsync();

                OrderCreateEditDto order = await orderServices.GetOrderAsync(orderId);
                IEnumerable<OrderConfig> orderConfigs = order.OrderConfig;

                List<OrderConfigViewModel> model = new List<OrderConfigViewModel>();
                int counter = 1;
                foreach (var orderConfig in orderConfigs)
                {
                    ProductConfigOrderDto productConfig = productConfigs.SingleOrDefault(
                        pc => pc.ProductId.Equals(orderConfig.ProductId)
                              && pc.SubProductId.Equals(orderConfig.SubProductId)
                    );

                    Grid grid = productConfig.Grids.SingleOrDefault(
                        g => g.GridId.Equals(orderConfig.GridId)
                    );
                    Level level = productConfig.Levels.SingleOrDefault(
                        lvl => lvl.LevelId.Equals(orderConfig.LevelId)
                    );

                    Width width = productConfig.Widths.SingleOrDefault(
                        w => w.WidthId.Equals(orderConfig.WidthId)
                    );

                    Length length = productConfig.Lengths.SingleOrDefault(
                        l => l.LengthId.Equals(orderConfig.LengthId)
                    );

                    model.Add(new OrderConfigViewModel()
                    {
                        SrNo = counter++,
                        Product = productConfig.ProductName,
                        SubProduct = productConfig.SubProductName,
                        Width = width.WidthValue,
                        Length = length.LengthValue,
                        Grid = grid.GridValue,
                        Level = level.LevelValue,
                        Quantity = orderConfig.Quantity
                    });
                }


                return model;
            }
            catch (Exception ex)
            {
                return null;
            }
        }

Here's an in-depth analysis of the GetOrderDetails method:

Method Analysis: GetOrderDetails

Signature:

private async Task<List<OrderConfigViewModel>> GetOrderDetails(string orderId)

Parameters:

  • string orderId: Represents the ID of the order.

Return Type:

  • Task<List<OrderConfigViewModel>>: Represents an asynchronous task that will return a list of OrderConfigViewModel objects.

Description: This method retrieves the detailed configuration of an order and returns a list of view models (OrderConfigViewModel) that represent the order's configurations.

Functional Breakdown:

  1. Data Retrieval:

    • Retrieves all released product configurations.
    • Retrieves the order details for the given orderId.
    • Extracts the configurations associated with the order.
  2. Model Population:

    • Initializes an empty list of OrderConfigViewModel.
    • Iterates through each configuration associated with the order.
      • For each configuration, it identifies the associated product configuration, grid, level, width, and length details.
      • Populates the OrderConfigViewModel with the extracted details and appends it to the list.
    • The counter (SrNo) is incremented for each iteration, providing a sequence number for the view model.
  3. Exception Handling:

    • If any exception occurs during the process, the method returns null.

Purpose: This method is designed to fetch the detailed configurations for a specific order and map them to a view model (OrderConfigViewModel). This view model provides a structured representation of the order's configurations, making it easier to work with in other parts of the application or for display purposes.

The OrderConfigViewModel is likely a simple data structure or DTO (Data Transfer Object) that holds properties such as SrNo, Product, SubProduct, Width, Length, Grid, Level, and Quantity.


        [HttpGet("ExportDispatchedToExcel/{orderid}")]
        public ActionResult ExportDispatchedToExcel(string orderid)
        {
            var main = orderBomServices.GetOrderBomsAsync(orderid).Result.OrderBy(x => x.PartGroupName);
            var category = main.Select(x => x.PartGroupName).Distinct().ToList(
            );
            var orderIds = main.Select(x => x.OrderBomId).ToList();

            var orderDetails = orderServices.GetOrderAsync(orderid).Result;
            var user = userServices.GetUserAsync(orderDetails.AddedBy).Result;
            var mainTable = ConvertToDataTable.ToDataTable(main);
            var exportTable = new DataTable();
            AddColumnsToDataTable(exportTable, columns);
            CopyDataTableRows(mainTable, exportTable, bomColumnMapping);

            //Create a new Excel workbook and worksheet
            XSSFWorkbook workbook = new XSSFWorkbook();
            ISheet worksheet = workbook.CreateSheet("DispatchBOM");

            #region Styles

            InsertLogo(workbook, worksheet, "Klaus Logo.png");
            var infoStyle = workbook.CreateCellStyle();
            infoStyle.BorderTop = BorderStyle.Thick;
            infoStyle.TopBorderColor = IndexedColors.Black.Index;
            var inforRow = worksheet.CreateRow(4);
            for (int i = 0; i < 16; i++)
            {
                ICell infoCell = inforRow.CreateCell(i);
                infoCell.CellStyle = infoStyle;
            }

            var titleStyle = CreateCellStyle(workbook, null, IndexedColors.Black,
                (short)FontBoldWeight.Bold, HorizontalAlignment.Left);
            var orderDetailsStyle = CreateCellStyle(workbook, null, IndexedColors.Black,
                (short)FontBoldWeight.Bold, HorizontalAlignment.Left);
            //Create a new style for the header row      
            var headerStyle = CreateCellStyle(workbook, new byte[] { 255, 147, 1 }, IndexedColors.Black,
                (short)FontBoldWeight.Bold, HorizontalAlignment.Left);

            //Create a new style for the data rows 
            var dataStyle = CreateCellStyle(workbook, new byte[] { 252, 228, 214 }, IndexedColors.Black,
                (short)FontBoldWeight.None, HorizontalAlignment.Left);
            var childDataStyle = CreateCellStyle(workbook, new byte[] { 217, 217, 217 }, IndexedColors.Black,
                (short)FontBoldWeight.None, HorizontalAlignment.Left);
            //Create a new style for the ParentID and ChildID cells
            // ICellStyle navigationStyle = CreateCellStyle(workbook, new byte[] { 231, 231, 44 }, IndexedColors.Black,
            //     (short)FontBoldWeight.Bold, HorizontalAlignment.Center);

            // for group
            var groupStyle = CreateCellStyle(workbook, new byte[] { 243, 176, 132 }, IndexedColors.Black,
                (short)FontBoldWeight.Bold, HorizontalAlignment.Left);

            #endregion

            #region Info Order Details

            string[] orderData =
            {
                $"Order Number : {orderDetails.OrderNumber}",
                $"Project Name : {orderDetails.ProjectName}",
                $"Prepared By : {user.FirstName} {user.LastName} ",
                $"Project City : {orderDetails.City}",
                $"Customer Name : {orderDetails.Customer}",
                $"Release Date : {orderDetails.ReleaseDate}",
                $"Car Space Quantity : {orderDetails.CarSpacesQuantity}"
            };
            IRow titleRow = worksheet.CreateRow(0);
            ICell titleCell = titleRow.CreateCell(3);
            titleCell.SetCellValue($"Dispatch BOM : {orderDetails.OrderNumber}");
            titleCell.CellStyle = titleStyle;
            IRow oneInfo = worksheet.CreateRow(1);
            var infoOrderIndex = 0;
            for (int i = 2; i < 5; i++)
            {
                ICell ocell = oneInfo.CreateCell(i);
                ocell.SetCellValue(orderData[infoOrderIndex]);
                infoOrderIndex++;
                ocell.CellStyle = orderDetailsStyle;
            }

            IRow twoInfo = worksheet.CreateRow(2);
            for (int i = 2; i < 5; i++)
            {
                ICell ocell = twoInfo.CreateCell(i);
                ocell.SetCellValue(orderData[infoOrderIndex]);
                infoOrderIndex++;
                ocell.CellStyle = orderDetailsStyle;
            }

            IRow treeInfo = worksheet.CreateRow(3);
            for (int i = 2; i < 3; i++)
            {
                ICell ocell = treeInfo.CreateCell(i);
                ocell.SetCellValue(orderData[infoOrderIndex]);
                ocell.CellStyle = orderDetailsStyle;
            }

            #endregion

            #region Product List

            var products = GetOrderDetails(orderid).Result;
            var productTable = ConvertToDataTable.ToDataTable(products);
            IRow productHeaderRow = worksheet.CreateRow(6);
            int pindex = 1;
            for (int i = 0; i < productTable.Columns.Count; i++)
            {
                ICell cell = productHeaderRow.CreateCell(pindex);
                cell.SetCellValue(productTable.Columns[i].ColumnName);
                cell.CellStyle = headerStyle;
                pindex++;
            }

            pindex = 1;
            var productindex = 7;

            for (int i = 0; i < productTable.Rows.Count; i++)
            {
                IRow pRow = worksheet.CreateRow(productindex);
                DataRow pdRow = productTable.Rows[i];
                for (int j = 0; j < productTable.Columns.Count; j++)
                {
                    ICell cell = pRow.CreateCell(pindex);
                    cell.SetCellValue(pdRow[j].ToString());
                    cell.CellStyle = dataStyle;
                    pindex++;
                }

                productindex++;

                pindex = 1;
            }

            #endregion

            #region Bom Order List

            int bomorderHeaderRowIndex = productindex + 1;

            //Add header row to the worksheet
            IRow headerRow = worksheet.CreateRow(bomorderHeaderRowIndex);
            for (int i = 0; i < exportTable.Columns.Count; i++)
            {
                ICell cell = headerRow.CreateCell(i);
                cell.SetCellValue(exportTable.Columns[i].ColumnName);
                cell.CellStyle = headerStyle;
            }

            var ccounter = 0;
            string prevPartGroupName = null;
            var index = bomorderHeaderRowIndex + 1;
            for (int i = 0; i < exportTable.Rows.Count; i++)
            {
                DataRow row = exportTable.Rows[i];
                var partGroupName = mainTable.Rows[i]["PartGroupName"].ToString();

                if (prevPartGroupName != partGroupName)
                {
                    IRow groupRow = worksheet.CreateRow(index);
                    for (int j = 0; j < 16; j++)
                    {
                        ICell gcell = groupRow.CreateCell(j);
                        gcell.CellStyle = groupStyle;
                    }

                    ICell gCell = groupRow.GetCell(0) ?? groupRow.CreateCell(0);
                    gCell.SetCellValue(category[ccounter]);
                    //if (prevPartGroupName == null)
                    //{
                    ccounter++;
                    //}

                    index++;
                    prevPartGroupName = partGroupName;
                }

                IRow dataRow = worksheet.CreateRow(index);
                for (int j = 0; j < exportTable.Columns.Count; j++)
                {
                    ICell cell = dataRow.CreateCell(j);
                    cell.SetCellValue(row[j].ToString());
                    cell.CellStyle = dataStyle;
                    if (exportTable.Columns[j].ColumnName == "OrderBomId" && row["OrderBomId"] != DBNull.Value)
                    {
                        #region Commented Code

                        #endregion
                    }
                }

                index++;
            }

            #endregion

            AutoCorrectColumnSizes(worksheet, bomorderHeaderRowIndex);
            // Generate Excel File
            MemoryStream originalStream = new MemoryStream();
            workbook.Write(originalStream);
            MemoryStream ns = new MemoryStream(originalStream.ToArray());
            originalStream.Dispose();
            // download excel file
            string fileName = "DispatchBOMExport.xlsx";
            return File(ns.ToArray(), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                fileName);
        }

Here's an in-depth analysis of the ExportDispatchedToExcel method:

Method Analysis: ExportDispatchedToExcel

Signature:

[HttpGet("ExportDispatchedToExcel/{orderid}")]
public ActionResult ExportDispatchedToExcel(string orderid)

Parameters:

  • string orderid: Represents the ID of the order.

Description: This method is responsible for exporting the Bill Of Material (BOM) data for a dispatched order to an Excel file.

Functional Breakdown:

  1. Data Retrieval:

    • Retrieves the main BOM details for the given orderid and sorts them by PartGroupName.
    • Extracts distinct PartGroupName values and a list of OrderBomId values.
    • Fetches details about the order and the user who added the order.
  2. Data Table Initialization:

    • The main BOM data is converted to a DataTable named mainTable.
    • An empty DataTable named exportTable is initialized.
    • Columns are added to exportTable, and rows from mainTable are copied to it.
  3. Excel Workbook Initialization:

    • A new Excel workbook and worksheet are created.
    • Styles for various cell types (info, title, header, data, etc.) are defined. The method uses helper functions like CreateCellStyle and InsertLogo to set these styles.
  4. Order Details Insertion:

    • Specific order details like order number, project name, user details, etc., are set in specific cells of the worksheet.
  5. Product List Insertion:

    • Product details associated with the order are fetched and converted to a DataTable.
    • The product data is then inserted into the worksheet with appropriate styles.
  6. BOM Order List Insertion:

    • The BOM details from exportTable are iterated and inserted into the worksheet.
    • For each BOM item, the method checks for an associated OrderBomId, but there's no further action within this context (there's commented-out code which might have been intended for this).
  7. Auto-correction of Column Sizes:

    • The AutoCorrectColumnSizes function is invoked to adjust the column widths based on content.
  8. Excel File Generation:

    • The constructed Excel workbook is written to a MemoryStream.
    • The stream is then converted to an array, which is returned as a file with the name "DispatchBOMExport.xlsx".

Purpose: This method provides functionality to generate an Excel report of the Bill of Materials (BOM) for a dispatched order. The resulting Excel file contains information about the order, products associated with the order, and the BOM details.


[HttpGet("ExportDetailedToExcel/{orderid}")]
        public ActionResult ExportDetailedToExcel(string orderid)
        {
            var main = orderBomServices.GetOrderBomsAsync(orderid).Result.OrderBy(x => x.PartGroupName);
            var category = main.Select(x => x.PartGroupName).Distinct().ToList(
            );
            var orderIds = main.Select(x => x.OrderBomId).ToList();

            var orderDetails = orderServices.GetOrderAsync(orderid).Result;
            var user = userServices.GetUserAsync(orderDetails.AddedBy).Result;
            var mainTable = ConvertToDataTable.ToDataTable(main);
            var exportTable = new DataTable();
            AddColumnsToDataTable(exportTable, columns);
            CopyDataTableRows(mainTable, exportTable, bomColumnMapping);

            //Create a new Excel workbook and worksheet
            XSSFWorkbook workbook = new XSSFWorkbook();
            ISheet worksheet = workbook.CreateSheet("DetailedLevelBOM");

            #region Styles

            InsertLogo(workbook, worksheet, "Klaus Logo.png");
            var infoStyle = workbook.CreateCellStyle();
            infoStyle.BorderTop = BorderStyle.Thick;
            infoStyle.TopBorderColor = IndexedColors.Black.Index;
            var inforRow = worksheet.CreateRow(4);
            for (int i = 0; i < 16; i++)
            {
                ICell infoCell = inforRow.CreateCell(i);
                infoCell.CellStyle = infoStyle;
            }

            var titleStyle = CreateCellStyle(workbook, null, IndexedColors.Black,
                (short)FontBoldWeight.Bold, HorizontalAlignment.Left);
            var orderDetailsStyle = CreateCellStyle(workbook, null, IndexedColors.Black,
                (short)FontBoldWeight.Bold, HorizontalAlignment.Left);
            //Create a new style for the header row      
            var headerStyle = CreateCellStyle(workbook, new byte[] { 255, 147, 1 }, IndexedColors.Black,
                (short)FontBoldWeight.Bold, HorizontalAlignment.Left);

            //Create a new style for the data rows 
            var dataStyle = CreateCellStyle(workbook, new byte[] { 252, 228, 214 }, IndexedColors.Black,
                (short)FontBoldWeight.None, HorizontalAlignment.Left);
            var childDataStyle = CreateCellStyle(workbook, new byte[] { 217, 217, 217 }, IndexedColors.Black,
                (short)FontBoldWeight.None, HorizontalAlignment.Left);
            //Create a new style for the ParentID and ChildID cells
            // ICellStyle navigationStyle = CreateCellStyle(workbook, new byte[] { 231, 231, 44 }, IndexedColors.Black,
            //     (short)FontBoldWeight.Bold, HorizontalAlignment.Center);

            // for group
            var groupStyle = CreateCellStyle(workbook, new byte[] { 243, 176, 132 }, IndexedColors.Black,
                (short)FontBoldWeight.Bold, HorizontalAlignment.Left);

            #endregion

            #region Info Order Details

            string[] orderData =
            {
                $"Order Number : {orderDetails.OrderNumber}",
                $"Project Name : {orderDetails.ProjectName}",
                $"Prepared By : {user.FirstName} {user.LastName} ",
                $"Project City : {orderDetails.City}",
                $"Customer Name : {orderDetails.Customer}",
                $"Release Date : {orderDetails.ReleaseDate}",
                $"Car Space Quantity : {orderDetails.CarSpacesQuantity}"
            };
            IRow titleRow = worksheet.CreateRow(0);
            ICell titleCell = titleRow.CreateCell(3);
            titleCell.SetCellValue($"Detailed BOM : {orderDetails.OrderNumber}");
            titleCell.CellStyle = titleStyle;
            IRow oneInfo = worksheet.CreateRow(1);
            var infoOrderIndex = 0;
            for (int i = 2; i < 5; i++)
            {
                ICell ocell = oneInfo.CreateCell(i);
                ocell.SetCellValue(orderData[infoOrderIndex]);
                infoOrderIndex++;
                ocell.CellStyle = orderDetailsStyle;
            }

            IRow twoInfo = worksheet.CreateRow(2);
            for (int i = 2; i < 5; i++)
            {
                ICell ocell = twoInfo.CreateCell(i);
                ocell.SetCellValue(orderData[infoOrderIndex]);
                infoOrderIndex++;
                ocell.CellStyle = orderDetailsStyle;
            }

            IRow treeInfo = worksheet.CreateRow(3);
            for (int i = 2; i < 3; i++)
            {
                ICell ocell = treeInfo.CreateCell(i);
                ocell.SetCellValue(orderData[infoOrderIndex]);
                ocell.CellStyle = orderDetailsStyle;
            }

            #endregion

            #region Product List

            var products = GetOrderDetails(orderid).Result;
            var productTable = ConvertToDataTable.ToDataTable(products);
            IRow productHeaderRow = worksheet.CreateRow(6);
            int pindex = 1;
            for (int i = 0; i < productTable.Columns.Count; i++)
            {
                ICell cell = productHeaderRow.CreateCell(pindex);
                cell.SetCellValue(productTable.Columns[i].ColumnName);
                cell.CellStyle = headerStyle;
                pindex++;
            }

            pindex = 1;
            var productindex = 7;

            for (int i = 0; i < productTable.Rows.Count; i++)
            {
                IRow pRow = worksheet.CreateRow(productindex);
                DataRow pdRow = productTable.Rows[i];
                for (int j = 0; j < productTable.Columns.Count; j++)
                {
                    ICell cell = pRow.CreateCell(pindex);
                    cell.SetCellValue(pdRow[j].ToString());
                    cell.CellStyle = dataStyle;
                    pindex++;
                }

                productindex++;

                pindex = 1;
            }

            #endregion

            #region Bom Order List

            int bomorderHeaderRowIndex = productindex + 1;

            //Add header row to the worksheet
            IRow headerRow = worksheet.CreateRow(bomorderHeaderRowIndex);
            for (int i = 0; i < exportTable.Columns.Count; i++)
            {
                ICell cell = headerRow.CreateCell(i);
                cell.SetCellValue(exportTable.Columns[i].ColumnName);
                cell.CellStyle = headerStyle;
            }

            var ccounter = 0;
            string prevPartGroupName = null;
            var index = bomorderHeaderRowIndex + 1;
            for (int i = 0; i < exportTable.Rows.Count; i++)
            {
                DataRow row = exportTable.Rows[i];
                var partGroupName = mainTable.Rows[i]["PartGroupName"].ToString();

                if (prevPartGroupName != partGroupName)
                {
                    IRow groupRow = worksheet.CreateRow(index);
                    for (int j = 0; j < 16; j++)
                    {
                        ICell gcell = groupRow.CreateCell(j);
                        gcell.CellStyle = groupStyle;
                    }

                    ICell gCell = groupRow.GetCell(0) ?? groupRow.CreateCell(0);
                    gCell.SetCellValue(category[ccounter]);
                    //if (prevPartGroupName == null)
                    //{
                    ccounter++;
                    //}

                    index++;
                    prevPartGroupName = partGroupName;
                }

                IRow dataRow = worksheet.CreateRow(index);
                for (int j = 0; j < exportTable.Columns.Count; j++)
                {
                    ICell cell = dataRow.CreateCell(j);
                    cell.SetCellValue(row[j].ToString());
                    cell.CellStyle = dataStyle;
                    if (exportTable.Columns[j].ColumnName == "OrderBomId" && row["OrderBomId"] != DBNull.Value)
                    {
                        #region Commented Code

                        /*
                        if (exportTable.Columns[j].ColumnName == "OrderBomId" && row["OrderBomId"] != DBNull.Value)
                        {
    
                            // Childs My Method.
                           
                             string sheetName = row["OrderBomId"].ToString();
                             var childs = orderBomChildPartServices.GetOrderBomChildPartsAsync(row["OrderBomId"].ToString())
                                .Result;
                            if (childs.Count() > 0)
                            {
                                var childsTable = ConvertToDataTable.ToDataTable(childs);
                                var exportChildsTable = new DataTable();
                                AddColumnsToDataTable(exportChildsTable, childColumns);
                                CopyDataTableRows(childsTable, exportChildsTable, bomChildColumnMapping);
                        
                                ISheet childSheet = workbook.CreateSheet(sheetName.Split("-")[0]);
                                IRow childHeaderRow = childSheet.CreateRow(0);
                                for (int c = 0; c < exportChildsTable.Columns.Count; c++)
                                {
                                    ICell childCell = childHeaderRow.CreateCell(c);
                                    childCell.SetCellValue(exportChildsTable.Columns[c].ColumnName);
                                    childCell.CellStyle = headerStyle;
                                    childSheet.AutoSizeColumn(c);
                                }
                        
                                for (int c = 0; c < childsTable.Rows.Count; c++)
                                {
                                    DataRow childRow = exportChildsTable.Rows[c];
                                    IRow childDataRow = childSheet.CreateRow(c + 1);
                                    for (int cc = 0; cc < exportChildsTable.Columns.Count; cc++)
                                    {
                                        ICell childCell = childDataRow.CreateCell(cc);
                                        childCell.SetCellValue(childRow[cc].ToString());
                                        childCell.CellStyle = dataStyle;
                                    }
                                } 
                                cell.CellStyle = navigationStyle;
                                cell.Hyperlink = workbook.GetCreationHelper().CreateHyperlink(HyperlinkType.Document);
                                cell.Hyperlink.Address = sheetName.Split("-")[0] + "!A1";
                            } 
                        }
                    */

                        #endregion
                    }
                }

                index++;
                var childs = orderBomChildPartServices.GetOrderBomChildPartsAsync(orderIds[i])
                    .Result;

                if (childs.Count() > 0)
                {
                    var childsTable = ConvertToDataTable.ToDataTable(childs);
                    var exportChildsTable = new DataTable();
                    AddColumnsToDataTable(exportChildsTable, columns);
                    CopyDataTableRows(childsTable, exportChildsTable, bomChildColumnMapping);

                    for (int c = 0; c < childsTable.Rows.Count; c++)
                    {
                        DataRow childRow = exportChildsTable.Rows[c];
                        IRow childDataRow = worksheet.CreateRow(index);
                        for (int cc = 0; cc < exportChildsTable.Columns.Count; cc++)
                        {
                            ICell childCell = childDataRow.CreateCell(cc);
                            childCell.SetCellValue(childRow[cc].ToString());
                            childCell.CellStyle = childDataStyle;
                        }

                        index++;
                    }
                }
            }

            #endregion

            AutoCorrectColumnSizes(worksheet, bomorderHeaderRowIndex);
            // Generate Excel File
            MemoryStream originalStream = new MemoryStream();
            workbook.Write(originalStream);
            MemoryStream ns = new MemoryStream(originalStream.ToArray());
            originalStream.Dispose();
            // download excel file
            string fileName = "DetailedBOMExport.xlsx";
            return File(ns.ToArray(), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                fileName);
        }

Here's an in-depth analysis of the ExportDetailedToExcel method:

Method Analysis: ExportDetailedToExcel

Signature:

[HttpGet("ExportDetailedToExcel/{orderid}")]
public ActionResult ExportDetailedToExcel(string orderid)

Parameters:

  • string orderid: Represents the ID of the order.

Description: This method is responsible for exporting detailed Bill Of Material (BOM) data for a given order to an Excel file.

Functional Breakdown:

  1. Data Retrieval:

    • The method retrieves the main BOM details for the given orderid and sorts them by PartGroupName.
    • It also retrieves distinct PartGroupName values and a list of OrderBomId values.
    • Details about the order and the user who added the order are fetched.
  2. Data Table Initialization:

    • The main BOM data is converted to a DataTable named mainTable.
    • An empty DataTable named exportTable is initialized.
    • Columns are added to exportTable, and rows from mainTable are copied to it.
  3. Excel Workbook Initialization:

    • A new Excel workbook and worksheet are created.
    • Styles for various cell types (info, title, header, data, etc.) are defined. Notably, the method uses previously analyzed helper functions like CreateCellStyle and InsertLogo to set these styles.
  4. Order Details Insertion:

    • Specific order details like order number, project name, user details, etc., are set in specific cells of the worksheet.
  5. Product List Insertion:

    • Product details associated with the order are fetched and converted to a DataTable.
    • The product data is then inserted into the worksheet with appropriate styles.
  6. BOM Order List Insertion:

    • The BOM details from exportTable are iterated and inserted into the worksheet.
    • For each BOM item, associated child parts are retrieved and added to the worksheet.
  7. Auto-correction of Column Sizes:

    • The AutoCorrectColumnSizes function (analyzed previously) is invoked to adjust the column widths based on content.
  8. Excel File Generation:

    • The constructed Excel workbook is written to a MemoryStream.
    • The stream is then converted to an array, which is returned as a file with the name "DetailedBOMExport.xlsx".

Purpose: This method provides functionality to generate a detailed Excel report of the Bill of Materials (BOM) for a specific order. The resulting Excel file contains information about the order, products associated with the order, and the BOM details, including child parts.

using KlausBOM.CustomHandlers.Authorization;
using KlausBOM.Domain;
using KlausBOM.Domain.Enumerations;
using KlausBOM.DTOs.Orders;
using KlausBOM.Services.Orders;
using Microsoft.AspNetCore.Mvc;
using Repository.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace KlausBOM.Controllers.Orders
{
    [Route("OrderBoms")]

    public class OrderBomController : Controller
    {

        private OrderBomServices orderBomServices;
        private OrderServices orderServices;

        public OrderBomController(IUnitOfWork work)
        {
            orderBomServices = new OrderBomServices(work);
            orderServices = new OrderServices(work);
        }

        public IActionResult Index()
        {
            return View();
        }

        [HttpGet("Edit/{orderBomId}")]
        [KlausAuthorize(module: PermissionModule.ORDER,
            feature: PermissionFeature.EDIT)]
        public async Task<IActionResult> Edit(string orderBomId)
        {
            try
            {

                OrderBomCreateEditDto dto = await orderBomServices
                    .GetOrderBomAsync(orderBomId);

                if (dto == null)
                {
                    throw new Exception("Order BOM does not exist!!");
                }

                IEnumerable<Part> parts = await orderBomServices
                    .GetAvailableOrderBomPartAsync(dto.OrderId, true);
                parts = parts.Append(new Part
                {
                    PartId = dto.PartId,
                    PartNumber = dto.PartNumber,
                    Name = dto.PartName,
                    Specification = dto.Specification,
                    DrawingNumber = dto.DrawingNumber,
                    RefNumber = dto.RefNumber,
                    InfowordNumber = dto.InfoWorldNumber,
                    PartUom = new PartUom { Name = dto.Uom },
                    PartType = new PartType { Name = dto.PartType }
                });

                ViewData["Parts"] = parts;

                return View(dto);
            }
            catch (Exception ex)
            {
                ViewData["Error"] = ex.Message;
                if (ex.InnerException != null)
                {
                    ViewData["Error"] += "\n" +
                        "Details: " + ex.InnerException.Message;
                }

                return View();
            }
        }

        [HttpPost("Edit/{orderBomId}")]
        [KlausAuthorize(module: PermissionModule.ORDER,
            feature: PermissionFeature.EDIT)]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Edit(string orderBomId,
            OrderBomCreateEditDto dto)
        {
            try
            {
                IEnumerable<Part> parts = await orderBomServices
                    .GetAvailableOrderBomPartAsync(dto.OrderId, true);
                parts = parts.Append(new Part
                {
                    PartId = dto.PartId,
                    PartNumber = dto.PartNumber,
                    Name = dto.PartName,
                    Specification = dto.Specification,
                    DrawingNumber = dto.DrawingNumber,
                    RefNumber = dto.RefNumber,
                    InfowordNumber = dto.InfoWorldNumber,
                    PartUom = new PartUom { Name = dto.Uom },
                    PartType = new PartType { Name = dto.PartType }
                });

                ViewData["Parts"] = parts;

                if (!orderBomId.Equals(dto.OrderBomId))
                {
                    throw new Exception("Invalid request!!!");
                }

                OrderBom updatedOrderBom = await orderBomServices.UpdateOrderBomAsync(dto);
                TempData["UpdateSuccess"] = "Order BOM Part has been updated successfully!!";
                return Redirect("/Orders/Edit/" + updatedOrderBom.OrderId);
            }
            catch (Exception ex)
            {
                ViewData["UpdateError"] = ex.Message;
                if (ex.InnerException != null)
                {
                    ViewData["UpdateError"] += "\n" +
                        "Details: " + ex.InnerException.Message;
                }

                Order order = await orderServices.GetOrderAsync(dto.OrderId);
                dto.Order = order;
                return View(dto);
            }
        }
    }
}

The OrderBomController is an MVC controller in the KlausBOM system. It deals with operations related to the OrderBom entity. Here's an in-depth analysis:

1. Dependencies:

  • Various namespaces are imported to enable functionalities like custom authorization, domain entities, DTOs (Data Transfer Objects), and services.

2. Class Declaration and Attributes:

  • [Route("OrderBoms")]: Specifies the base route for all methods in this controller as /OrderBoms.
  • This controller inherits from the Controller base class, which means it is an MVC controller and not an API controller.

3. Fields:

  • orderBomServices and orderServices: Private service instances. These services likely contain business logic and interact with the database.

4. Constructor:

  • Accepts an IUnitOfWork parameter named work. This is a unit of work pattern, which batches multiple operations into a single transaction.
  • The constructor initializes orderBomServices and orderServices using this unit of work.

5. Actions:

  1. Index:

    • The simplest action that returns the default view for the controller.
  2. Edit (GET):

    • Accepts a GET request with an orderBomId parameter.
    • Retrieves the OrderBom with the specified ID.
    • If the OrderBom does not exist, it throws an exception.
    • Retrieves a list of parts available for the order, appending the current part to the list.
    • Populates the ViewData with the parts list and then returns a View populated with the OrderBomCreateEditDto.
  3. Edit (POST):

    • Accepts a POST request with an orderBomId and an OrderBomCreateEditDto.
    • Again, retrieves a list of parts available for the order and appends the current part to the list.
    • Validates the orderBomId against the DTO's OrderBomId.
    • Updates the OrderBom using orderBomServices.
    • If successful, sets a success message in TempData and redirects to the Edit view of the parent Order.
    • If there's an exception, it sets an error message in ViewData and returns the Edit view populated with the DTO.

6. Authorization:

  • Custom authorization is used in the form of the [KlausAuthorize] attribute. This attribute specifies the required permissions for executing the method.

7. Error Handling:

  • Both Edit actions use try-catch blocks to handle exceptions.
  • If an error occurs, the error message is stored in ViewData and displayed on the View.

8. ViewData and TempData:

  • ViewData: A dictionary that can pass data from the controller to the view.
  • TempData: A dictionary that retains information for the duration of a single request. Useful for passing data between actions (e.g., success or error messages).

Implications:

  • MVC Structure: Unlike an API controller, this controller is designed to interact with views in the MVC framework. It retrieves data, populates models, and returns views.

  • Error Handling: The consistent approach ensures a smooth user experience by displaying meaningful error messages on the views.

  • Authorization: Role-based or permission-based authorization is applied to ensure only authorized users can perform certain actions.

  • Data Retrieval and Manipulation: The controller focuses heavily on data operations, especially related to OrderBom and its associated parts. This includes reading, updating, and organizing data before passing it to the view.

In summary, OrderBomController is a well-structured MVC controller dedicated to operations on OrderBom entities. It integrates smoothly with views, ensuring a seamless user experience.

using KlausBOM.CustomHandlers.Authorization;
using KlausBOM.Domain;
using KlausBOM.Domain.Enumerations;
using KlausBOM.DTOs.Orders;
using KlausBOM.DTOs.ProductConfigs;
using KlausBOM.Services.Orders;
using KlausBOM.Services.Parameters;
using KlausBOM.Services.ProductConfigs;
using Microsoft.AspNetCore.Mvc;
using Repository.Core;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading.Tasks;

namespace KlausBOM.Controllers.Orders
{
    [Route("Orders")]
    public class OrderController : Controller
    {
        private OrderServices orderServices;
        private ProductConfigServices productConfigServices;
        private WidthServices widthServices;
        private GridServices gridServices;
        private LengthServices lengthServices;
        private LevelServices levelServices;

        public OrderController(IUnitOfWork work)
        {
            orderServices = new OrderServices(work);
            productConfigServices = new ProductConfigServices(work);
            widthServices = new WidthServices(work);
            lengthServices = new LengthServices(work);
            gridServices = new GridServices(work);
            levelServices = new LevelServices(work);
        }

        [HttpGet]
        [KlausAuthorize(module: PermissionModule.ORDER,
            feature: PermissionFeature.VIEW)]
        public async Task<IActionResult> Index()
        {
            try
            {
                DataTable tableOrder = new DataTable();
                tableOrder.Columns.Add("Selection");
                tableOrder.Columns.Add("OrderId");
                tableOrder.Columns.Add("Order Number");
                tableOrder.Columns.Add("Release Date");
                tableOrder.Columns.Add("Project Name");
                tableOrder.Columns.Add("Customer");
                tableOrder.Columns.Add("City");
                tableOrder.Columns.Add("Car Spaces Qty.");
                tableOrder.Columns.Add("Status");
                tableOrder.Columns.Add("Action");

                IEnumerable<OrderListItemDto> orders =
                    await orderServices.GetOrdersAsync();
                foreach (OrderListItemDto order in orders)
                {
                    DataRow row = tableOrder.NewRow();
                    row["OrderId"] = order.OrderId;
                    row["Order Number"] = order.OrderNumber;
                    row["Release Date"] = order.ReleaseDate;
                    row["Project Name"] = order.ProjectName;
                    row["Customer"] = order.Customer;
                    row["City"] = order.City;
                    row["Car Spaces Qty."] = order.CarSpacesQuantity;
                    row["Status"] = order.Status.ToString();

                    tableOrder.Rows.Add(row);
                }

                return View(tableOrder);
            }
            catch (Exception ex)
            {
                ViewData["Error"] = ex.Message;
                if (ex.InnerException != null)
                {
                    ViewData["Error"] += "\n" +
                        "Details: " + ex.InnerException.Message;
                }

                return View();
            }
        }

        [HttpGet("Create")]
        [KlausAuthorize(module: PermissionModule.ORDER,
            feature: PermissionFeature.CREATE)]
        public async Task<IActionResult> Create()
        {
            try
            {
                IEnumerable<ProductConfigOrderDto> dtos = await
                    productConfigServices.GetReleaseProductConfigsAsync();

                ViewData["Products"] = dtos
                    .GroupBy(pc => pc.ProductId)
                    .Select(pc => pc.First())
                    .Select(pc => new Product
                    {
                        ProductId = pc.ProductId,
                        Name = pc.ProductName
                    })
                    .OrderBy(p => p.Name);

                ViewData["SubProducts"] = dtos
                    .OrderBy(sp => sp.SubProductName);

            }
            catch (Exception ex)
            {
                ViewData["Error"] = ex.Message;
                if (ex.InnerException != null)
                {
                    ViewData["Error"] += "\n" +
                        "Details: " + ex.InnerException.Message;
                }

            }
            return View(new OrderCreateEditDto());
        }

        [HttpGet("Edit/{orderId}")]
        [KlausAuthorize(module: PermissionModule.ORDER,
            feature: PermissionFeature.EDIT)]
        public async Task<IActionResult> Edit(string orderId)
        {
            try
            {
                IEnumerable<ProductConfigOrderDto> productConfigDtos = await
                    productConfigServices.GetReleaseProductConfigsAsync();

                ViewData["Products"] = productConfigDtos
                    .GroupBy(pc => pc.ProductId)
                    .Select(pc => pc.First())
                    .Select(pc => new Product
                    {
                        ProductId = pc.ProductId,
                        Name = pc.ProductName
                    })
                    .OrderBy(p => p.Name);

                ViewData["SubProducts"] = productConfigDtos
                    .OrderBy(sp => sp.SubProductName);

                OrderCreateEditDto dto = await orderServices.GetOrderAsync(orderId);
                return View(dto);
            }
            catch (Exception ex)
            {
                ViewData["Error"] = ex.Message;
                if (ex.InnerException != null)
                {
                    ViewData["Error"] += "\n" +
                        "Details: " + ex.InnerException.Message;
                }

                return View(new OrderCreateEditDto());
            }
        }
    }
}

The OrderController class is an MVC controller in the KlausBOM system that handles operations related to orders. Let's break it down:

1. Dependencies:

  • Several namespaces are imported, granting functionalities such as custom authorization, domain entities, DTOs (Data Transfer Objects), services, and system utilities like DataTable.

2. Class Declaration and Attributes:

  • [Route("Orders")]: Specifies the base route for all methods in this controller as /Orders.
  • This controller inherits from the Controller base class, which means it's an MVC controller and not an API controller.

3. Fields:

The controller contains multiple service instances:

  • orderServices, productConfigServices, widthServices, gridServices, lengthServices, and levelServices. These services probably contain business logic and database interactions.

4. Constructor:

  • Accepts an IUnitOfWork parameter named work. This is a unit of work pattern, which batches multiple operations into a single transaction.
  • The constructor initializes the service fields using this unit of work.

5. Actions:

  1. Index (GET):

    • Retrieves a list of orders and constructs a DataTable with various columns to display the orders.
    • Populates the DataTable with the retrieved orders.
    • Returns the constructed DataTable to the View.
  2. Create (GET):

    • Retrieves a list of product configurations.
    • Organizes the data into two ViewData lists: Products and SubProducts.
    • Returns a new OrderCreateEditDto to the View.
  3. Edit (GET):

    • Accepts an orderId parameter.
    • Retrieves the corresponding order and the list of product configurations.
    • Organizes the data into two ViewData lists: Products and SubProducts.
    • Returns the retrieved OrderCreateEditDto to the View.

6. Authorization:

  • Custom authorization is in place through the [KlausAuthorize] attribute. This ensures that only users with the necessary permissions can access the related actions.

7. Error Handling:

  • Each action contains a try-catch block for error handling.
  • If an error occurs, the error message is stored in ViewData and displayed on the View.

8. ViewData:

  • ViewData: A dictionary that can pass data from the controller to the view. It's used to provide additional data to the view that isn't part of the model, like the Products and SubProducts lists.

Implications:

  • MVC Structure: This is an MVC controller, meaning it's designed to interact with views. It retrieves data, populates models, and returns views.

  • Data Construction: The DataTable construction in the Index action suggests that the system might be using a custom view or grid component to display data in a tabular format.

  • Data Retrieval and Manipulation: The controller is focused heavily on data operations, especially related to orders and product configurations.

  • Error Handling: There's a consistent approach to error handling, ensuring a smooth user experience by displaying meaningful error messages.

In summary, the OrderController class provides endpoints for operations related to orders, with a particular focus on displaying and editing them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment