Tutorial: Quote Editor

Editable grids are one of the most common requirements in any XRM project. The QuoteProductEditor project demonstrates the following techniques:   

1.       Editable Grid – Quote Line Items are added via an editable grid

2.       Knockout Binding of form fields – Form fields allow editing values of the selected item not shown in the grid columns.

3.       Grid Validation – Validation that ensures only a product or a write in product is provided. Form Field validation is demonstrated in the Contact Editor tutorial.

4.       Command  Bar – Buttons that invoke commands on the View Model.

 

Note: Read how to set up a SparkleXrm project in Visual Studio 2012
The full solution can be downloaded from GitHub

You will need to install the SparkleXrm solution - SparkleXrm_0_1_0_managed.zip

View –  QuoteLineItemEditor.htm

SparkleXRM uses the MVVM pattern to separate business logic from the presentation. The view is in two parts, the first being an HTML web resource.

The styling is controlled primarily by the header css:



	

Notice the relative paths – this is so that the webresources are always cached in accordance with the standard Dynamics CRM Webresource caching. More info can be read here.

Command Bar

A Command Bar is added above the grid to provide commands that operate on the grid records:

Each of the buttons are data bound to a command in the ViewModel using the Knockout click binding.



...

	

Grid

The Quote Grid is positioned on the page simply by placing a div for the grid and a div for the pager:

Form Fields

SparkleXRM provides bindings for all CRM field types so you can quickly create forms that are bound to your viewmodel.

The following example is a lookup field that calls  the view model command ‘saelsRepSearchCommand’ to find users based on the search term entered.

	
                        
	

 

The following form field data binders are available:

1.       text

2.       numeric

3.       money

4.       datetime

5.       lookup

6.       picklist

7.       optionset

8.       duration

9.       bool

10.   multiline-text

Numeric, Money & Date/Time  bindings will automatically read the user settings and display in the correct format.

Model – QuoteDetail.cs

The model in this tutorial is simply the quotedetail entity and can be found in the Model folder of the ClientUI project. The layout of these entity classes are designed to be compatible with the SDK CrmSvcUtil generated Entity classes so that client side code may be shared with server side code:

public class QuoteDetail: Entity
    {
        public QuoteDetail()
            : base("quotedetail")
        {
            this._metaData["quantity"] = AttributeTypes.Decimal_;
            this._metaData["lineitemnumber"] = AttributeTypes.Int_;
            PropertyChanged += QuoteProduct_PropertyChanged;
        }

The constructor must provide some additional metadata for numeric attributes since all number in JavaScript are of type Number. Only numeric attributes need this metadata.

The PropertyChanged event is used to calculate the extended amount when the price or units change.

The attributes are represented as fields (properties are not fully supported in JavaScript without explicit getter/setter method calls).

[ScriptName("lineitemnumber")]
public int? LineItemNumber;
	

The ScriptName attribute tells Script# to output the field as the lowercase version so that it can be easily updated via the SOAP calls – however the SchemaName is used for the field name for compatibility with the CrmSvcUtil generated classes.

View – QuoteLineItemEditorView.cs

The View class is called at the end of the HTML via the init method.


	

The init methods responsibility is to create an instance of the view model and initialise the grids.

Grid

Each column is added to the grid using the GridDataViewBinder. Each type of attribute has it’s own Grid data binding class:

XrmLookupEditor.BindColumn(
	GridDataViewBinder.AddColumn(
		columns, 
		"Existing Product", 
		200, 
		"productid"), 
	vm.ProductSearchCommand,
	"productid", 
	"name", "");
	

The following editors are available:

1.       XrmDateEditor

2.       XrmDurationEditor

3.       XrmLookupEditor

4.       XrmMoneyEditor

5.       XrmNumberEditor

6.       XrmTextEditor

7.       XrmTimeEditor

8.       XrmOptionSetEditor

Numeric, Money and Date/Time editors will automatically read the user settings and display in the correct format.

View Model – QuoteLineItemEditorViewModel.cs

The main logic of the application is held in the root View Model. This class is initialised by the view and the constructor sets up the data that is bound to the grid.

QuoteDetail DataView

SparkleXRM provides a simple way of binding grids to a fetchxml query. It doesn’t automatically parse fetchxml and layoutxml so that you have more flexibility over exactly what is retrieved and how it is displayed in the grid. After all, you are writing an XRM application that could have some very specific requirements that departs from the way normal CRM grids work.

Lines = new EntityDataViewModel(10, typeof(QuoteDetail), false); 
Lines.OnSelectedRowsChanged += OnSelectedRowsChanged;
Lines.FetchXml = "<fetch version='1.0' output-format='xml-platform' mapping='logical' returntotalrecordcount='true' no-lock='true' distinct='false' count='{0}' paging-cookie='{1}' page='{2}'>
                  <entity name='quotedetail'>
                    <attribute name='productid'></attribute>
                    <attribute name='productdescription'></attribute>
                    ...
                    {3}
                    <link-entity name='quote' from='quoteid' to='quoteid' alias='ac'>
                      <filter type='and'>
                        <condition attribute='quoteid' operator='eq' uiname='tes' uitype='quote' value='" + GetQuoteId() + @"' />
                      </filter>
                    </link-entity>
                  </entity>
                </fetch>";
 
Lines.SortBy(new SortCol("lineitemnumber", true));
Lines.NewItemFactory = NewLineFactory;

The EntityDataViewModel class inherits from DataViewBase which you can also inherit from if you need to create custom Data Views. The Data Views responsibility is to load the data from the server, then page through it when requested by the Grid. It is a view on the data since it only supplies the data required to be displayed, and can sort if required.

The constructor accepts the page size, the type of entity to use and if the pages should be ‘lazy loaded’ or they should be loaded all at once when the first record is requested. For the quote editor application, all lines are retrieved so that client side sorting can be demonstrated.

The NewItemFactory is called when the grids requests a new item. If null, then a record is automatically created using the type supplied, but in this case we need to set the transactioncurrencyid and the lineitemnumber.

ObservableQuoteDetail

The Entity classes are designed to be light weight and be used when calling the SDK SOAP web services. Form data binding require observable versions of the entity classes - each with each attribute being of type Observable<T>. This allows knockout to data bind to the fields described in the HTML.

[ScriptName("requestdeliveryby")]
public Observable RequestDeliveryBy = Knockout.Observable();
	

The attributes are again described with the ScriptName to ensure the two types can be used interchangeably when validating input. A single instance of the ObservableQuoteDetail class is created by the ViewModel and different Entity records are loaded using the SetValue function when rows are selected on the grid. This is done using the SetValue method on the ObservableQuoteDetail class.

public void SetValue(QuoteDetail value)
{
_isSetting = true;
InnerQuoteDetail = value==null ? new QuoteDetail() : value;
this.RequestDeliveryBy.SetValue(InnerQuoteDetail.RequestDeliveryBy);
this.SalesRepId.SetValue(InnerQuoteDetail.SalesRepId);
_isSetting = false;
}
	

When the updates are made via the form, the Commit function is used to save the values to the InnerQuoteDetail. Alternatively, this approach can also be used with an ‘Apply’ button rather than immediately updating the inner value (See the Contact Editor Example).

Commands

Where ever any functionality is invoked via the user interface, it is done through a view model command. The Quote Editor application has the following commands:

1.       SaveComand – Called via the Command Bar, and saves all the new, edited or deleted records

2.       DeleteCommand – Marks all the selected rows as deleted

3.       MoveUpCommand – Moves the selected row up in the list (TODO: Re-order the lineitemnumbers!)

4.       MoveDownCommand – Moves the selected row down in the list (TODO: Re-order the lineitemnumbers!)

5.       ProductSearchCommand – Used when the user searches in the Product Lookup grid editor

6.       UoMSearchCommand – Used when the user searches in the Unit Lookup grid editor

7.       SalesRepSearchCommand – Used when the user searches in the Sales Rep Form field data binding.