Edit & Delete Order Lines

(Whilst Adding an Order)

Introduction

Currently we can add purchase orders, but in the process we cannot edit or delete order lines individually; either we cancel the whole order or we accept whatever we entered.  In this post we will add the ability to edit or delete order lines before saving the order.

Edit an Order Line

We will be following the general pattern used for editing tax rates.  The first thing to do is to add a 'RowSelectHandler' to the Syncfusion DataGrid to identify the order line being selected.

And this is where we hit our first problem:

For Tax Rates we had already written the tax rate to the SQL database and therefore could use the TaxID (primary key and identity column from the Tax table) to identify the row from the grid being selected.  But the order hasn't been written to the SQL database so the identity column for POLIne has not been established - in fact it is the default 0.

Digression

To check these suspicions, I added the 'RowSelectHandler' to the <GridEvents> and a method.  I placed a break-point in the method so I could see the values being held and ran the project.

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

As I suspected, all rows returned a value for POLineID = 0.  This meant that POLineID, as such, would not be suitable to identify a particular row for editing or deletion.

Solution

I reasoned that although POLineID was defaulting to 0 for all order lines I could probably assign a value, temporarily, that would then get over-written when the order line was eventually written to the SQL database.  The tactic is to declare an integer variable and set it to 0 and on the 'OrderLineSave' method, for every save, to decrease this by one and assign it to POLIneID.  Each order line will therefore have a temporary unique POLineID with a negative number.  (I chose a negative number just to be sure there could be no clash with a previously saved order line - I haven't worked out yet how to handle adding a new order line to an existing order!)

Back to Coding

Declare a integer to be used for the temporary POLineID, and another to be used in the RowSelectHandler.  Enter the following near the top of the code section, along with the other declarations.

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

To decrease the value of tmpPOLineID and assign it to POLIneID, in the OrderLineSave method, after the checks that a product and tax rate have been selected, insert the following:

Amend the RowSelectHandler to use the new variable and delete the test one; replace with:

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

When the user has selected a row and clicked the 'Edit' button on the toolbar we want the Order Line dialog to open and for it to be populated with the data for the selected row.  To achieve this, insert the following code in the 'Edit' section of the 'ToolbarClickHandler'.

        if (args.Item.Text == "Edit")
        {
            //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();
            }
        }

The first part of the above code checks that the user has selected a row and if not uses the existing 'Warning' component and passes a suitable message to it.

The second part of the code gets the data from 'orderLines' where the POLineID matches the (temporary) POLineID of the selected order line and populates the 'DialogAddEditOrderLine' and displays it.  The data is then ready to be edited.

Once edited it must be saved, in this case there is no need to add a new record, it just needs saving and the data grid refreshing.  The code for this is shown below.  The existing code for 'OrderLineSave' starts with a check to ensure that POLineID = 0 and if so adds a new record to the list.  In this case this is not necessary, so the following code needs to be added to 'OrderLineSave' as the alternative to the initial check that POLineID = 0.  Insert below the closing } for the 'if' clause.

        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();
                await CloseDialog();                //No need to keep dialog open
                selectedPOLineID = 0;
            }
        }

As for adding a new order line, it checks that a Product and Tax Rate has been selected and if so refreshes the order lines data grid, forces the save by 'StateHasChanged' and closes the dialog, resetting the selectedPOLineID to 0.

Two last changes; 

  • the 'CloseDialog(); is 'awaited'. We therefore need to change the 'OrderLineSave' method from 'void' to 'async Task'.
private async Task OrderLineSave()
  • When I ran the code up to this point, I noticed that when the dialog to edit the order line opened, the Tax Rate drop-down list was blank. This is because the TaxID was not being saved to 'orderLines'. To correct this, add a comma after the last line in orderLines.Add and add the following:
POLineTaxID = addeditOrderLine.POLineTaxID              //Added 1.15

Run the code to check that edit an order line is working correctly.

Delete an Order Line

To delete an order line the user needs to click the 'Delete' button.  This will trigger an event on the ToolbarClickHandler.  The method to handle the toolbar click will respond by checking that an order line has been selected.  If an order line has been selected it would be good practice to get the user to confirm that they do, in fact, want to delete the order line.  To do this we will display a dialog asking them to confirm the deletion.

The code to respond to the toolbar click for 'Delete' is shown below:

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

To add the 'DialogDeleteOrderLine' add the dialog in the html section; above the 'Warning' component would be fine.

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

Declare the dialog by inserting the declaration near the top of the code (below the other SfDialog declaration):

SfDialog DialogDeleteOrderLine;

This dialog asks the user to confirm that they want to delete the record with buttons for 'Delete' and 'Cancel', with corresponding OnClick methods.  To handle these events, add the following methods to the foot of the code section.

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

    public async void ConfirmDeleteYes()
    {
        OrderLineDelete();
        await this.DialogDeleteOrderLine.Hide();
        selectedPOLineID = 0;
    }
    
    private void OrderLineDelete()
    {
        var itemToRemove = orderLines.Single(x => x.POLineID == selectedPOLineID);
        orderLines.Remove(itemToRemove);
        OrderLinesGrid.Refresh();
    }

The 'ConfirmDeleteNo' simply closes the dialog and resets 'selectedPOLineID' to 0 (to prevent the user inadvertently clicking the 'Delete' or 'Edit' buttons without selecting an order line.)

The 'ConfirmDeleteYes' calls another method, 'OrderLineDelete' (which deletes the order line), closes the dialog and resets 'selectedPOLineID' to 0.

The 'OrderLineDelete' method deletes the order line; it sets a variable to the order line to be deleted, calls the .Remove action and refreshes the OrderLinesGrid.

Run the code to check that deleting an order line is working correctly.

Sundry Improvements

There are a couple of minor changes that are cosmetic and/or should improve the usability of the purchase order form.

Dialog Header Text

The dialog for adding and editing an order line is currently always showing a header of 'Add an Order Line'.  To improve this we can vary the heading according to the action.  Declare a string variable for the header text and set it to an empty string.  As always, place the declaration at the top of the code.

string dialogHeaderText = "";

Change the <Header> tags for 'DialogAddEditOrderLine' to:

<Header> @dialogHeaderText </Header>

In the ToolbarClickHandler for the Add option insert the following before the rest of the code

dialogHeaderText = "Add an Order Line";

Similarly, add the following at the top of the Edit section:

dialogHeaderText = "Edit an Order Line";

Default Tax Rate

I noticed, when adding more than one order line at a time, that on the second and subsequent lines the user had to click in the Tax Rate drop-down list whereas for the first line it was visited by the tab sequence.  In many cases it is highly likely that once an order line has been entered that further lines will use the same tax rate.  To avoid the user having repeatedly to select the tax rate from the drop-down list it can be defaulted to the rate chosen on the first occasion by commenting out two lines from the 'OrderLineSave' method so that POLineTaxID and POLineTaxRate are not reset to 0, as shown below:

                //addeditOrderLine.POLineTaxID = 0;                 //Leave - highly likely to be same as previous record   
                //addeditOrderLine.POLineTaxRate = 0;               //Leave - highly likely to be same as previous record 

Project Code

The only file to be changed in this post has been the PurchaseOrderPage.raxor.  The code can be found here.

YouTube Video

Blazor Purchase Orders - Part 15 - Edit & Delete Order Lines