Edit a Saved Order - Part 1 - Code

SQL

POLine table

USE [PurchaseOrders]
GO

ALTER TABLE [dbo].[POLine] DROP CONSTRAINT [DF_POLine_POLineTaxRate]
GO

ALTER TABLE [dbo].[POLine] DROP CONSTRAINT [DF_POLine_POLineProductUnitPrice]
GO

ALTER TABLE [dbo].[POLine] DROP CONSTRAINT [DF_POLine_POLineProductQuantity]
GO

ALTER TABLE [dbo].[POLine] DROP CONSTRAINT [DF_POLine_POLineProductID]
GO

ALTER TABLE [dbo].[POLine] DROP CONSTRAINT [DF_POLine_POLineHeaderID]
GO

/****** Object:  Table [dbo].[POLine]    Script Date: 05/04/2021 10:43:41 ******/
IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[POLine]') AND type in (N'U'))
DROP TABLE [dbo].[POLine]
GO

/****** Object:  Table [dbo].[POLine]    Script Date: 05/04/2021 10:43:41 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[POLine](
	[POLineID] [int] IDENTITY(1,1) NOT NULL,
	[POLineHeaderID] [int] NOT NULL,
	[POLineProductID] [int] NOT NULL,
	[POLineProductDescription] [nvarchar](50) NOT NULL,
	[POLineProductQuantity] [decimal](9, 3) NOT NULL,
	[POLineProductUnitPrice] [money] NOT NULL,
	[POLineTaxRate] [decimal](6, 4) NOT NULL,
	[POLineTaxID] [int] NOT NULL,
 CONSTRAINT [PK_POLine] PRIMARY KEY CLUSTERED 
(
	[POLineID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[POLine] ADD  CONSTRAINT [DF_POLine_POLineHeaderID]  DEFAULT ((0)) FOR [POLineHeaderID]
GO

ALTER TABLE [dbo].[POLine] ADD  CONSTRAINT [DF_POLine_POLineProductID]  DEFAULT ((0)) FOR [POLineProductID]
GO

ALTER TABLE [dbo].[POLine] ADD  CONSTRAINT [DF_POLine_POLineProductQuantity]  DEFAULT ((0)) FOR [POLineProductQuantity]
GO

ALTER TABLE [dbo].[POLine] ADD  CONSTRAINT [DF_POLine_POLineProductUnitPrice]  DEFAULT ((0)) FOR [POLineProductUnitPrice]
GO

ALTER TABLE [dbo].[POLine] ADD  CONSTRAINT [DF_POLine_POLineTaxRate]  DEFAULT ((0)) FOR [POLineTaxRate]
GO

POLine Stored Procedures

USE [PurchaseOrders]
GO

DROP PROCEDURE [dbo].[spPOLine_GetByPOHeader]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO


--------------  Stored Proc for SELECT by POHeader, with Product Code and Net, Tax and Gross
CREATE PROCEDURE [dbo].[spPOLine_GetByPOHeader]
-- 
@POHeaderID int
AS 
BEGIN
-- SQL Select for one table row
SELECT dbo.POLine.POLineID, dbo.POLine.POLineHeaderID, dbo.POLine.POLineProductID, dbo.Product.ProductCode as POLineProductCode, 
		dbo.POLine.POLineProductDescription, dbo.POLine.POLineProductQuantity, dbo.POLine.POLineProductUnitPrice, 
		dbo.POLine.POLineProductUnitPrice * dbo.POLine.POLineProductQuantity AS POLineNetPrice, 
        dbo.POLine.POLineTaxRate, dbo.POLine.POLineTaxID,
		dbo.POLine.POLineProductUnitPrice * dbo.POLine.POLineProductQuantity * dbo.POLine.POLineTaxRate AS POLineTaxAmount, 
		(dbo.POLine.POLineProductUnitPrice * dbo.POLine.POLineProductQuantity) * (1 + dbo.POLine.POLineTaxRate) AS POLineGrossPrice
FROM   dbo.POLine INNER JOIN
        dbo.Product ON dbo.POLine.POLineProductID = dbo.Product.ProductID
WHERE POLineHeaderID= @POHeaderID
END

GO

DROP PROCEDURE [dbo].[spPOLine_GetOne]
GO

--------------  Stored Proc for SELECT (one)
CREATE PROCEDURE [dbo].[spPOLine_GetOne]
-- Needs one parameter for primary key
@POLineID int
AS 
BEGIN
-- SQL Select for one table row
SELECT POLineID, POLineHeaderID, POLineProductID, POLineProductDescription, POLineProductQuantity, POLineProductUnitPrice, POLineTaxRate, POLineTaxID FROM POLine WHERE POLineID= @POLineID
END

GO

DROP PROCEDURE [dbo].[spPOLine_Insert]
GO

-------------- Stored Proc for INSERT
CREATE PROCEDURE [dbo].[spPOLine_Insert]
--Parameters for Insert stored procedure
@POLineHeaderID int,
@POLineProductID int,
@POLineProductDescription nvarchar(50),
@POLineProductQuantity decimal(9, 3),
@POLineProductUnitPrice money,
@POLineTaxRate decimal(6, 4),
@POLineTaxID int
AS
BEGIN
--SQL for Insert stored procedure
INSERT INTO POLine(POLineHeaderID, POLineProductID, POLineProductDescription, POLineProductQuantity, POLineProductUnitPrice, POLineTaxRate, POLineTaxID) 
VALUES (@POLineHeaderID, @POLineProductID, @POLineProductDescription, @POLineProductQuantity, @POLineProductUnitPrice, @POLineTaxRate, @POLineTaxID)
END

GO

DROP PROCEDURE [dbo].[spPOLine_List]
GO

--------------  Stored Proc for SELECT (LIST, just first six fields but you can change in final code.)
CREATE PROCEDURE [dbo].[spPOLine_List]
--No parameters required.
AS
BEGIN
--SQL for Select stored procedure.
SELECT POLineID, POLineHeaderID, POLineProductID, POLineProductDescription, POLineProductQuantity, POLineProductUnitPrice, POLineTaxRate, POLineTaxID FROM POLine ORDER BY POLineID DESC
END

GO

DROP PROCEDURE [dbo].[spPOLine_Update]
GO

--------------  Stored Proc for UPDATE
CREATE PROCEDURE [dbo].[spPOLine_Update]
-- Parameters for Update stored procedure.
@POLineID int,
@POLineHeaderID int,
@POLineProductID int,
@POLineProductDescription nvarchar(50),
@POLineProductQuantity decimal(9, 3),
@POLineProductUnitPrice money,
@POLineTaxRate decimal(6, 4),
@POLineTaxID int
AS
BEGIN
-- SQL for Update stored procedure
UPDATE POLine SET POLineHeaderID = @POLineHeaderID, POLineProductID = @POLineProductID, 
POLineProductDescription = @POLineProductDescription, POLineProductQuantity = @POLineProductQuantity, 
POLineProductUnitPrice = @POLineProductUnitPrice, POLineTaxRate = @POLineTaxRate, POLineTaxID = @POLineTaxID
WHERE POLineID = @POLineID
END

GO

C#

POLine.cs

using System;
using System.ComponentModel.DataAnnotations;

// This is the model for one row in the database table.
namespace BlazorPurchaseOrders.Data
{
    public class POLine
    {
        [Required]
        public int POLineID { get; set; }
        [Required]
        public int POLineHeaderID { get; set; }
        [Required]
        public int POLineProductID { get; set; }
        [Required(ErrorMessage = "Product Description is compulsory.")]
        [StringLength(50, MinimumLength = 2, ErrorMessage = "Product Description must be between 2 and 20 characters.")]
        public string POLineProductDescription { get; set; }
        [Required]
        public decimal POLineProductQuantity { get; set; }
        [Required]
        public decimal POLineProductUnitPrice { get; set; }
        [Required]
        public decimal POLineTaxRate { get; set; }
        public int POLineTaxID { get; set; }

        //The following are not saved to database - just for the DataGrid
        public decimal? POLineNetPrice { get; set; }
        public decimal POLineTaxAmount { get; set; }
        public decimal POLineGrossPrice { get; set; }
        public string POLineProductCode { get; set; }

    }
}

POLineService.cs

using Dapper;
using Microsoft.Data.SqlClient;
using System;
using System.Collections.Generic;
using System.Data;
using System.Threading.Tasks;

namespace BlazorPurchaseOrders.Data
{
    public class POLineService : IPOLineService
    {
        // Database connection
        private readonly SqlConnectionConfiguration _configuration;
        public POLineService(SqlConnectionConfiguration configuration)
        {
            _configuration = configuration;
        }
        // Add (create) a POLine table row (SQL Insert)
        // This only works if you're already created the stored procedure.
        public async Task<bool> POLineInsert(POLine poline)
        {
            using (var conn = new SqlConnection(_configuration.Value))
            {
                var parameters = new DynamicParameters();
                parameters.Add("POLineHeaderID", poline.POLineHeaderID, DbType.Int32);
                parameters.Add("POLineProductID", poline.POLineProductID, DbType.Int32);
                parameters.Add("POLineProductDescription", poline.POLineProductDescription, DbType.String);
                parameters.Add("POLineProductQuantity", poline.POLineProductQuantity, DbType.Decimal);
                parameters.Add("POLineProductUnitPrice", poline.POLineProductUnitPrice, DbType.Decimal);
                parameters.Add("POLineTaxRate", poline.POLineTaxRate, DbType.Decimal);
                parameters.Add("POLineTaxID", poline.POLineTaxID, DbType.Decimal);

                // Stored procedure method
                await conn.ExecuteAsync("spPOLine_Insert", parameters, commandType: CommandType.StoredProcedure);
            }
            return true;
        }
        // Get a list of poline rows (SQL Select)
        // This only works if you're already created the stored procedure.
        public async Task<IEnumerable<POLine>> POLineList()
        {
            IEnumerable<POLine> polines;
            using (var conn = new SqlConnection(_configuration.Value))
            {
                polines = await conn.QueryAsync<POLine>("spPOLine_List", commandType: CommandType.StoredProcedure);
            }
            return polines;
        }

        // Get one poline based on its POLineID (SQL Select)
        // This only works if you're already created the stored procedure.
        public async Task<POLine> POLine_GetOne(int @POLineID)
        {
            POLine poline = new POLine();
            var parameters = new DynamicParameters();
            parameters.Add("@POLineID", POLineID, DbType.Int32);
            using (var conn = new SqlConnection(_configuration.Value))
            {
                poline = await conn.QueryFirstOrDefaultAsync<POLine>("spPOLine_GetOne", parameters, commandType: CommandType.StoredProcedure);
            }
            return poline;
        }

        public async Task<IEnumerable<POLine>> POLine_GetByPOHeader(int @POHeaderID)
        {
            IEnumerable<POLine> polines;
            var parameters = new DynamicParameters();
            parameters.Add("@POHeaderID", POHeaderID, DbType.Int32);
            using (var conn = new SqlConnection(_configuration.Value))
            {
                polines = await conn.QueryAsync<POLine>("spPOLine_GetByPOHeader", parameters, commandType: CommandType.StoredProcedure);
            }
            return polines;
        }

        // Update one POLine row based on its POLineID (SQL Update)
        // This only works if you're already created the stored procedure.
        public async Task<bool> POLineUpdate(POLine poline)
        {
            using (var conn = new SqlConnection(_configuration.Value))
            {
                var parameters = new DynamicParameters();
                parameters.Add("POLineID", poline.POLineID, DbType.Int32);

                parameters.Add("POLineHeaderID", poline.POLineHeaderID, DbType.Int32);
                parameters.Add("POLineProductID", poline.POLineProductID, DbType.Int32);
                parameters.Add("POLineProductDescription", poline.POLineProductDescription, DbType.String);
                parameters.Add("POLineProductQuantity", poline.POLineProductQuantity, DbType.Decimal);
                parameters.Add("POLineProductUnitPrice", poline.POLineProductUnitPrice, DbType.Decimal);
                parameters.Add("POLineTaxRate", poline.POLineTaxRate, DbType.Decimal);
                parameters.Add("POLineTaxID", poline.POLineTaxID, DbType.Decimal);

                await conn.ExecuteAsync("spPOLine_Update", parameters, commandType: CommandType.StoredProcedure);
            }
            return true;
        }
    }
}

PurchaseOrderPage.razor

@page "/purchaseorder/{POHeaderID:int}"
@using BlazorPurchaseOrders.Data

@inject NavigationManager NavigationManager
@inject ISupplierService SupplierService
@inject IPOHeaderService POHeaderService
@inject IPOLineService POLineService
@inject IProductService ProductService
@inject ITaxService TaxService


@using Microsoft.AspNetCore.Components.Authorization
@inject AuthenticationStateProvider AuthenticationStateProvider
@using System
@using System.Collections.Generic


<h3>@pagetitle</h3>

<EditForm Model="@orderaddedit" OnValidSubmit="@OrderSave">
    <DataAnnotationsValidator />
    <div class="grid-container">
        <div class="grid-child left-column">
            <SfDropDownList DataSource="@supplier"
                            TItem="Supplier"
                            TValue="int"
                            Text="SupplierID"
                            @bind-Value="orderaddedit.POHeaderSupplierID"
                            FloatLabelType="@FloatLabelType.Auto"
                            Placeholder="Select a Supplier"
                            Enabled="@supplierEnabled">
                <DropDownListFieldSettings Text="SupplierName" Value="SupplierID"></DropDownListFieldSettings>
                <DropDownListEvents TItem="Supplier" TValue="int" ValueChange="OnChangeSupplier"></DropDownListEvents>
            </SfDropDownList>

            <SfTextBox Enabled="true" Placeholder="Address"
                       FloatLabelType="@FloatLabelType.Always"
                       @bind-Value="orderaddedit.POHeaderSupplierAddress1"></SfTextBox>
            <ValidationMessage For="@(() => orderaddedit.POHeaderSupplierAddress1)" />

            <SfTextBox Enabled="true" Placeholder=""
                       FloatLabelType="@FloatLabelType.Never"
                       @bind-Value="orderaddedit.POHeaderSupplierAddress2"></SfTextBox>
            <ValidationMessage For="@(() => orderaddedit.POHeaderSupplierAddress2)" />

            <SfTextBox Enabled="true" Placeholder=""
                       FloatLabelType="@FloatLabelType.Never"
                       @bind-Value="orderaddedit.POHeaderSupplierAddress3"></SfTextBox>
            <ValidationMessage For="@(() => orderaddedit.POHeaderSupplierAddress3)" />

            <SfTextBox Enabled="true" Placeholder="Post Code"
                       FloatLabelType="@FloatLabelType.Never"
                       @bind-Value="orderaddedit.POHeaderSupplierPostCode"></SfTextBox>
            <ValidationMessage For="@(() => orderaddedit.POHeaderSupplierPostCode)" />

            <SfTextBox Enabled="true" Placeholder="Email"
                       FloatLabelType="@FloatLabelType.Auto"
                       @bind-Value="orderaddedit.POHeaderSupplierEmail"></SfTextBox>
            <ValidationMessage For="@(() => orderaddedit.POHeaderSupplierEmail)" />
        </div>
        <div class="grid-child right-column">
            <SfNumericTextBox Enabled="false" Placeholder="Order No"
                              FloatLabelType="@FloatLabelType.Always"
                              ShowSpinButton="false"
                              @bind-Value="orderaddedit.POHeaderOrderNumber"></SfNumericTextBox>

            <SfDatePicker TValue="DateTime"
                          Placeholder='Order Date'
                          FloatLabelType="@FloatLabelType.Auto"
                          @bind-Value="orderaddedit.POHeaderOrderDate"></SfDatePicker>

            <SfTextBox Enabled="false" Placeholder="Requested by"
                       FloatLabelType="@FloatLabelType.Always"
                       @bind-Value="orderaddedit.POHeaderRequestedBy"></SfTextBox>
        </div>
    </div>
    <br />
    <SfGrid @ref="OrderLinesGrid"
            DataSource="@orderLines"
            Toolbar="@Toolbaritems"
            AllowResizing="true">
        <GridColumns>
            <GridColumn Field="@nameof(POLine.POLineProductCode)"
                        HeaderText="Product"
                        TextAlign="@TextAlign.Left"
                        Width="20">
            </GridColumn>
            <GridColumn Field="@nameof(POLine.POLineProductDescription)"
                        HeaderText="Description"
                        TextAlign="@TextAlign.Left"
                        Width="30">
            </GridColumn>
            <GridColumn Field="@nameof(POLine.POLineProductQuantity)"
                        HeaderText="Quantity"
                        TextAlign="@TextAlign.Right"
                        Format="n0"
                        Width="10">
            </GridColumn>
            <GridColumn Field="@nameof(POLine.POLineProductUnitPrice)"
                        HeaderText="Unit Price"
                        TextAlign="@TextAlign.Right"
                        Format="C2"
                        Width="10">
            </GridColumn>
            <GridColumn Field="@nameof(POLine.POLineNetPrice)"
                        HeaderText="Net Price"
                        TextAlign="@TextAlign.Right"
                        Format="C2"
                        Width="10">
            </GridColumn>
            <GridColumn Field="@nameof(POLine.POLineTaxRate)"
                        HeaderText="Tax Rate"
                        TextAlign="@TextAlign.Right"
                        Format="p2"
                        Width="10">
            </GridColumn>
            <GridColumn Field="@nameof(POLine.POLineTaxAmount)"
                        HeaderText="Tax"
                        TextAlign="@TextAlign.Right"
                        Format="C2"
                        Width="10">
            </GridColumn>
            <GridColumn Field="@nameof(POLine.POLineGrossPrice)"
                        HeaderText="Total"
                        TextAlign="@TextAlign.Right"
                        Format="C2"
                        Width="10">
            </GridColumn>
        </GridColumns>

        <GridAggregates>
            <GridAggregate>
                <GridAggregateColumns>
                    <GridAggregateColumn Field=@nameof(POLine.POLineNetPrice) Type="AggregateType.Sum" Format="C2">
                        <FooterTemplate Context="NetContext">
                            @{
                                var aggregate = NetContext as AggregateTemplateContext;
                                <div>
                                    <p>@aggregate.Sum</p>
                                </div>
                            }
                        </FooterTemplate>
                    </GridAggregateColumn>

                    <GridAggregateColumn Field=@nameof(POLine.POLineTaxAmount) Type="AggregateType.Sum" Format="C2">
                        <FooterTemplate Context="TaxContext">
                            @{
                                var aggregate = TaxContext as AggregateTemplateContext;
                                <div>
                                    <p>@aggregate.Sum</p>
                                </div>
                            }
                        </FooterTemplate>
                    </GridAggregateColumn>

                    <GridAggregateColumn Field=@nameof(POLine.POLineGrossPrice) Type="AggregateType.Sum" Format="C2">
                        <FooterTemplate Context="GrossContext">
                            @{
                                var aggregate = GrossContext as AggregateTemplateContext;
                                <div>
                                    <p>@aggregate.Sum</p>
                                </div>
                            }
                        </FooterTemplate>
                    </GridAggregateColumn>
                </GridAggregateColumns>
            </GridAggregate>

        </GridAggregates>   
        <GridEvents RowSelected="RowSelectHandler" OnToolbarClick="ToolbarClickHandler" TValue="POLine"></GridEvents>
    </SfGrid>
    <br />

    <div class="e-footer-content">
        <div class="button-container">
            <button type="submit" class="e-btn e-normal e-primary">Save</button>
            <button type="button" class="e-btn e-normal" @onclick="@Cancel">Cancel</button>
        </div>
    </div>
</EditForm>

<SfDialog @ref="DialogAddEditOrderLine" IsModal="true" Width="600px" ShowCloseIcon="true" Visible="false">
    <DialogTemplates>
        <Header> @dialogHeaderText </Header>
    </DialogTemplates>
    <EditForm Model="@addeditOrderLine" OnValidSubmit="@OrderLineSave">
        <DataAnnotationsValidator />
        <div class=flex-container>
            <SfDropDownList DataSource="@product"
                            TItem="Product"
                            TValue="int"
                            Text="ProductID"
                            @bind-Value="addeditOrderLine.POLineProductID"
                            FloatLabelType="@FloatLabelType.Always"
                            Placeholder="Select a Product"
                            Enabled="true">
                <DropDownListFieldSettings Text="ProductCode" Value="ProductID"></DropDownListFieldSettings>
                <DropDownListEvents TItem="Product" TValue="int" OnValueSelect="OnChangeProduct"></DropDownListEvents>
            </SfDropDownList>
        </div>
        <div class=flex-container>
            <SfTextBox Enabled="true" Placeholder="Product Description"
                       FloatLabelType="@FloatLabelType.Always"
                       @bind-Value="addeditOrderLine.POLineProductDescription"></SfTextBox>
        </div>
        <div class=flex-container>
            <ValidationMessage For="@(() => addeditOrderLine.POLineProductDescription)" />
        </div><div class=flex-container>
            <SfNumericTextBox Enabled="true" Placeholder="Quantity"
                              FloatLabelType="@FloatLabelType.Always"
                              ShowSpinButton="false"
                              Format="n0"
                              EnableRtl="true"
                              @bind-Value="addeditOrderLine.POLineProductQuantity"
                              @onfocusout='@POLineCalc'>
            </SfNumericTextBox>

            <SfNumericTextBox Enabled="true" Placeholder="Unit Price"
                              FloatLabelType="@FloatLabelType.Always"
                              ShowSpinButton="false"                              
                              Format="c2"                              
                              EnableRtl="true"                              
                              @bind-Value="addeditOrderLine.POLineProductUnitPrice"
                              @onfocusout='@POLineCalc'>
            </SfNumericTextBox>

            <SfNumericTextBox Enabled="false" Placeholder="Net Price"
                              FloatLabelType="@FloatLabelType.Always"
                              ShowSpinButton="false"
                              Format="c2"
                              EnableRtl="true"
                              @bind-Value="addeditOrderLine.POLineNetPrice">
            </SfNumericTextBox>
        </div>
        <div class=flex-container>
            <SfDropDownList DataSource="@tax"
                            TItem="Tax"
                            TValue="int"
                            Text="TaxID"
                            @bind-Value="addeditOrderLine.POLineTaxID"
                            FloatLabelType="@FloatLabelType.Always"
                            Placeholder="Tax Rate"
                            Enabled="true">
                <DropDownListFieldSettings Text="TaxDescription" Value="TaxID"></DropDownListFieldSettings>
                <DropDownListEvents TItem="Tax" TValue="int" OnValueSelect="OnChangeTax"></DropDownListEvents>
            </SfDropDownList>

            <SfNumericTextBox Enabled="false" Placeholder="Tax Rate %"
                              FloatLabelType="@FloatLabelType.Always"
                              ShowSpinButton="false"
                              Format="p2"
                              EnableRtl="true"
                              @bind-Value="addeditOrderLine.POLineTaxRate">
            </SfNumericTextBox>

            <SfNumericTextBox Enabled="false" Placeholder="Tax Amount"
                              FloatLabelType="@FloatLabelType.Always"
                              ShowSpinButton="false"
                              Format="c2"
                              EnableRtl="true"
                              @bind-Value="addeditOrderLine.POLineTaxAmount">
            </SfNumericTextBox>
        </div>
        <div class=flex-container>
            <SfNumericTextBox Enabled="false" Placeholder="Total Price"
                              FloatLabelType="@FloatLabelType.Always"
                              ShowSpinButton="false"
                              Format="c2"
                              EnableRtl="true"
                              @bind-Value="addeditOrderLine.POLineGrossPrice">
            </SfNumericTextBox>
        </div>

        <br />
        <div class="e-footer-content">
            <div class="button-container">
                <button type="button" class="e-btn e-normal" @onclick="@CloseDialog">Cancel</button>
                <button type="submit" class="e-btn e-normal e-primary">Save</button>
            </div>
        </div>
    </EditForm>
</SfDialog>

<SfDialog @ref="DialogDeleteOrderLine" IsModal="true" Width="500px" ShowCloseIcon="true" Visible="false">
    <DialogTemplates>
        <Header> Confirm Delete </Header>
        <Content>
            <span class="text-danger">Please confirm that you want to delete this record</span>
        </Content>
    </DialogTemplates>
    <DialogButtons>
        <DialogButton Content="Delete" IsPrimary="true" OnClick="@ConfirmDeleteYes" />
        <DialogButton Content="Cancel" IsPrimary="false" OnClick="@ConfirmDeleteNo" />
    </DialogButtons>
</SfDialog>

<WarningPage @ref="Warning" WarningHeaderMessage="@WarningHeaderMessage" WarningContentMessage="@WarningContentMessage" />

<style>
    .grid-container {
        display: grid;
        max-width: 900px; /* Maximum width of the whole container - in this case both columns */
        grid-template-columns: 1fr 1fr; /* Relative width of each column (1fr 1fr is equivalent to, say, 33fr 33fr */
        grid-gap: 75px; /* size of the gap between columns */
    }

    .flex-container {
        display: flex;
        flex-direction: row; /* Causes tab to move along row and then onto following row */
        justify-content: space-evenly; /* Equal space left and right margin and between elements */
        margin: 10px; /* This appears to be vertical margin between rows */
        column-gap: 10px; /* Tgap betwen columns */
    }
</style>


@code {
    POHeader orderaddedit = new POHeader();
    IEnumerable<Supplier> supplier;
    IEnumerable<Product> product;
    IEnumerable<Tax> tax;
    IEnumerable<POLine> orderLinesByPOHeader;

    string pagetitle = "";
    string dialogHeaderText = "";

    private string UserName;

    SfGrid<POLine> OrderLinesGrid;
    public List<POLine> orderLines = new List<POLine>();
    private List<ItemModel> Toolbaritems = new List<ItemModel>();

    SfDialog DialogAddEditOrderLine;
    SfDialog DialogDeleteOrderLine;
    public POLine addeditOrderLine = new POLine();

    WarningPage Warning;
    string WarningHeaderMessage = "";
    string WarningContentMessage = "";

    public bool supplierEnabled { get; set; } = true;

    private int tmpPOLineID { get; set; } = 0;
    private int selectedPOLineID { get; set; } = 0;

    [Parameter]
    public int POHeaderID { get; set; }

    //Executes on page open, sets headings and gets data in the case of edit
    protected override async Task OnInitializedAsync()
    {
        supplier = await SupplierService.SupplierList();
        orderaddedit.POHeaderOrderDate = DateTime.Now;
        tax = await TaxService.TaxList();

        if (POHeaderID == 0)
        {
            pagetitle = "Add an Order";
        }
        else
        {
            pagetitle = "Edit an Order";
            orderaddedit = await POHeaderService.POHeader_GetOne(POHeaderID);
            orderLinesByPOHeader = await POLineService.POLine_GetByPOHeader(POHeaderID);
            orderLines = orderLinesByPOHeader.ToList(); //Convert from IEnumable to List
            supplierEnabled = false;
        }

        //Get user if logged in and populate the 'Requested by' column
        var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
        var user = authState.User;

        if (user.Identity.IsAuthenticated)
        {
            UserName = user.Identity.Name;
        }
        else
        {
            UserName = "The user is NOT authenticated.";
        }

        orderaddedit.POHeaderRequestedBy = UserName;

        Toolbaritems.Add(new ItemModel() { Text = "Add", TooltipText = "Add a new order line", PrefixIcon = "e-add" });
        Toolbaritems.Add(new ItemModel() { Text = "Edit", TooltipText = "Edit selected order line", PrefixIcon = "e-edit" });
        Toolbaritems.Add(new ItemModel() { Text = "Delete", TooltipText = "Delete selected order line", PrefixIcon = "e-delete" });

    }

    private void OnChangeSupplier(Syncfusion.Blazor.DropDowns.ChangeEventArgs<int, Supplier> args)
    {
        this.orderaddedit.POHeaderSupplierAddress1 = args.ItemData.SupplierAddress1;
        this.orderaddedit.POHeaderSupplierAddress2 = args.ItemData.SupplierAddress2;
        this.orderaddedit.POHeaderSupplierAddress3 = args.ItemData.SupplierAddress3;
        this.orderaddedit.POHeaderSupplierPostCode = args.ItemData.SupplierPostCode;
        this.orderaddedit.POHeaderSupplierEmail = args.ItemData.SupplierEmail;

    }

    // Executes OnValidSubmit of EditForm above
    protected async Task OrderSave()
    {
        if (POHeaderID == 0)
        {
            //Save the record - 1st - the POHeader

            int HeaderID = await POHeaderService.POHeaderInsert(
                           orderaddedit.POHeaderOrderDate,
                           orderaddedit.POHeaderSupplierID,
                           orderaddedit.POHeaderSupplierAddress1,
                           orderaddedit.POHeaderSupplierAddress2,
                           orderaddedit.POHeaderSupplierAddress3,
                           orderaddedit.POHeaderSupplierPostCode,
                           orderaddedit.POHeaderSupplierEmail,
                           orderaddedit.POHeaderRequestedBy
                           );

            //2nd - the POLines
            foreach (var individualPOLine in orderLines)
            {
                individualPOLine.POLineHeaderID = HeaderID;
                bool Success = await POLineService.POLineInsert(individualPOLine);
            }

            NavigationManager.NavigateTo("/");
        }
        else
        {

            NavigationManager.NavigateTo("/");
        }
    }

    //Executes if user clicks the Cancel button.
    void Cancel()
    {
        NavigationManager.NavigateTo("/");
    }

    public async Task ToolbarClickHandler(Syncfusion.Blazor.Navigations.ClickEventArgs args)
    {

        //Refresh product to select only those products for this supplier (and products with null suppliers)
        product = await ProductService.ProductListBySupplier(orderaddedit.POHeaderSupplierID);

        if (args.Item.Text == "Add")
        {
            //Code for adding goes here
            dialogHeaderText = "Add an Order Line";
            //Check that a supplier has been selected from the drop-down list
            if (orderaddedit.POHeaderSupplierID == 0)
            {
                WarningHeaderMessage = "Warning!";
                WarningContentMessage = "Please Select a Supplier before adding order lines.";
                Warning.OpenDialog();
            }
            else
            {
                addeditOrderLine = new POLine();          // Ensures a blank form when adding
                addeditOrderLine.POLineNetPrice = 0;
                addeditOrderLine.POLineTaxID = 0;
                addeditOrderLine.POLineProductID = 0;
                await this.DialogAddEditOrderLine.Show();
            }

        }

        if (args.Item.Text == "Edit")
        {
            dialogHeaderText = "Edit an Order Line";
            //Check that an order line has been selected
            if (selectedPOLineID == 0)
            {
                WarningHeaderMessage = "Warning!";
                WarningContentMessage = "Please select an Order Line from the grid.";
                Warning.OpenDialog();
            }
            else
            {
                //populate addeditOrderLine (temporary data set used for the editing process)                
                addeditOrderLine = orderLines.Where(x => x.POLineID == selectedPOLineID).FirstOrDefault();
                StateHasChanged();
                await this.DialogAddEditOrderLine.Show();
            }
        }

        if (args.Item.Text == "Delete")
        {
            //Code for adding goes here
            if (selectedPOLineID == 0)
            {
                WarningHeaderMessage = "Warning!";
                WarningContentMessage = "Please select an Order Line from the grid.";
                Warning.OpenDialog();
            }
            else
            {
                await this.DialogDeleteOrderLine.Show();
            }
        }
    }

    private async Task OrderLineSave()
    {
        if (addeditOrderLine.POLineID == 0)
        {
            //Code to save new order line goes here
            //Check that a product has been selected from the drop-down list
            if (addeditOrderLine.POLineProductCode == null || addeditOrderLine.POLineProductCode == "")
            {
                WarningHeaderMessage = "Warning!";
                WarningContentMessage = "Please select a Product.";
                Warning.OpenDialog();
            }
            //And check that a tax rate has been selected from the drop-down list
            else if (addeditOrderLine.POLineTaxID == 0)
            {
                WarningHeaderMessage = "Warning!";
                WarningContentMessage = "Please select a Tax Rate.";
                Warning.OpenDialog();
            }
            else
            {
                POLineCalc();
                tmpPOLineID = tmpPOLineID - 1;        //used to provide a temporary ID for POLineID

                orderLines.Add(new POLine
                {
                    POLineID = tmpPOLineID,
                    POLineHeaderID = 0,
                    POLineProductID = addeditOrderLine.POLineProductID,
                    POLineProductCode = addeditOrderLine.POLineProductCode,
                    POLineProductDescription = addeditOrderLine.POLineProductDescription,
                    POLineProductQuantity = addeditOrderLine.POLineProductQuantity,
                    POLineProductUnitPrice = addeditOrderLine.POLineProductUnitPrice,
                    POLineNetPrice = addeditOrderLine.POLineNetPrice,
                    POLineTaxRate = addeditOrderLine.POLineTaxRate,
                    POLineTaxAmount = addeditOrderLine.POLineTaxAmount,
                    POLineGrossPrice = addeditOrderLine.POLineGrossPrice,
                    POLineTaxID = addeditOrderLine.POLineTaxID
                });


                OrderLinesGrid.Refresh();
                StateHasChanged();                  //<-----  THIS IS ABSOLUTELY ESSENTIAL

                //addeditOrderLine = new POLine();  //<-----  THIS gives errors (nulls)

                addeditOrderLine.POLineProductID = 0;
                addeditOrderLine.POLineProductCode = "";
                addeditOrderLine.POLineProductDescription = "";
                addeditOrderLine.POLineProductQuantity = 0;
                addeditOrderLine.POLineProductUnitPrice = 0;
                addeditOrderLine.POLineNetPrice = 0;
                //addeditOrderLine.POLineTaxID = 0;                 //Leave - highly likely to be same as previous record   
                //addeditOrderLine.POLineTaxRate = 0;               //Leave - highly likely to be same as previous record 
                addeditOrderLine.POLineTaxAmount = 0;
                addeditOrderLine.POLineGrossPrice = 0;

                //We now have order lines, so prevent user from changing the supplier
                supplierEnabled = false;
            }
        }

        else
        //An order line is being edited
        //Check that a product has been selected from the drop-down list
        if (addeditOrderLine.POLineProductCode == null || addeditOrderLine.POLineProductCode == "")
        {
            WarningHeaderMessage = "Warning!";
            WarningContentMessage = "Please select a Product.";
            Warning.OpenDialog();
        }
        //And check that a tax rate has been selected from the drop-down list
        else if (addeditOrderLine.POLineTaxID == 0)
        {
            WarningHeaderMessage = "Warning!";
            WarningContentMessage = "Please select a Tax Rate.";
            Warning.OpenDialog();
        }
        else
        {
            OrderLinesGrid.Refresh();
            StateHasChanged();                  //<-----  THIS IS ABSOLUTELY ESSENTIAL
            await CloseDialog();                //No need to keep dialog open
        }
    }


    private async Task CloseDialog()
    {
        await this.DialogAddEditOrderLine.Hide();
    }

    private void OnChangeProduct(Syncfusion.Blazor.DropDowns.SelectEventArgs<Product> args)
    {
        this.addeditOrderLine.POLineProductCode = args.ItemData.ProductCode;
        this.addeditOrderLine.POLineProductDescription = args.ItemData.ProductDescription;
        this.addeditOrderLine.POLineProductUnitPrice = args.ItemData.ProductUnitPrice;
        POLineCalc();
    }

    private void OnChangeTax(Syncfusion.Blazor.DropDowns.SelectEventArgs<Tax> args)
    {
        // int testTaxId = args.ItemData.TaxID;
        this.addeditOrderLine.POLineTaxRate = args.ItemData.TaxRate;
        POLineCalc();
    }

    private void POLineCalc()
    {
        addeditOrderLine.POLineNetPrice = addeditOrderLine.POLineProductUnitPrice * addeditOrderLine.POLineProductQuantity;
        addeditOrderLine.POLineTaxAmount = addeditOrderLine.POLineNetPrice.Value * addeditOrderLine.POLineTaxRate;
        addeditOrderLine.POLineGrossPrice = addeditOrderLine.POLineNetPrice.Value * (1 + addeditOrderLine.POLineTaxRate);
    }

    public void RowSelectHandler(RowSelectEventArgs<POLine> args)
    {
        //{args.Data} returns the current selected records.
        selectedPOLineID = args.Data.POLineID;
    }

    private void OrderLineDelete()
    {
        var itemToRemove = orderLines.Single(x => x.POLineID == selectedPOLineID);
        orderLines.Remove(itemToRemove);
        OrderLinesGrid.Refresh();
    }

    public async void ConfirmDeleteNo()
    {
        await DialogDeleteOrderLine.Hide();
        selectedPOLineID = 0;
    }

    public async void ConfirmDeleteYes()
    {
        OrderLineDelete();
        await this.DialogDeleteOrderLine.Hide();
        selectedPOLineID = 0;
    }
}