How to Add Custom Control in Customer with Custom Field & Add/Update Data in D365 Commerce POS


 

Microsoft Dynamics 365

Retail & Commerce

POS (Point of Sales)

How to Add Custom Control in Customer with Custom Field & Add/Update Data in D365 Commerce POS

Pre-Requisite

            You must have a custom field in the channel table.

               If you want to add a custom field. Please follow the link

https://sajidshaikhblog.blogspot.com/2023/08/how-to-extend-customer-list-view-adding.html

Scale Unit Project


Commerce Runtime (CRT)

K:\RetailSDK\SampleExtensions\CommerceRuntime


Development

Open Scale Unit Project from Service Volume (K) and build it.

After the successful build add new folder in ViewExtension with the name of CustomerAddEdit



After that add class CustomerCustomField.html


Code - CustomerCustomField.html

<!DOCTYPE html>

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">

<head>

    <meta charset="utf-8" />

    <title></title>

</head>

<body>

    <!-- Note: The element id is different than the id generated by the POS extensibility framework. This

    'template' id is not used by the POS extensibility framework. -->

    <div id="Contoso_Pos_Extensibility_Samples_CustomerCustomField" class="height100Percent">

        <div class="gutter20x20"></div>

        <div id="Contoso_Pos_Extensibility_Samples_REFNOEXT" class="width300 grow col">

            <label for="REFNOEXT">REF No EXT</label>

            <input type="text" id="IDREFNOEXT" name="REFNOEXT" />

        </div>

    </div>

</body>

</html>

After that add class CustomerCustomField.ts





Code - CustomerCustomField.ts

import {

    CustomerAddEditCustomControlBase,

    ICustomerAddEditCustomControlState,

    ICustomerAddEditCustomControlContext,

    CustomerAddEditCustomerUpdatedData

} from "PosApi/Extend/Views/CustomerAddEditView";

import { ObjectExtensions } from "PosApi/TypeExtensions";

import { ProxyEntities } from "PosApi/Entities";

import * as Controls from "PosApi/Consume/Controls";

export default class CustomerCustomField extends CustomerAddEditCustomControlBase {

 

    public refnoext: string;

    public customerIsPerson: boolean;

    public dataList: Controls.IDataList<ProxyEntities.Customer>;

    public readonly title: string;

    private static readonly TEMPLATE_ID: string = "Contoso_Pos_Extensibility_Samples_CustomerCustomField";

    private _state: ICustomerAddEditCustomControlState;

    constructor(id: string, context: ICustomerAddEditCustomControlContext) {

        super(id, context);

        this.refnoext = "";

        this.customerIsPerson = false;

        this.customerUpdatedHandler = (data: CustomerAddEditCustomerUpdatedData) => {

            this.customerIsPerson = data.customer.CustomerTypeValue == ProxyEntities.CustomerType.Person ?

                true : false;

        };

    }

    /**

    * Binds the control to the specified element.

    * @param {HTMLElement} element The element to which the control should be bound.

    */

    public onReady(element: HTMLElement): void {

 

        let templateElement: HTMLElement = document.getElementById(CustomerCustomField.TEMPLATE_ID);

        let templateClone: Node = templateElement.cloneNode(true);

        element.appendChild(templateClone);

 

        let idRefNoExt: HTMLInputElement = element.querySelector("#IDREFNOEXT") as HTMLInputElement;

        idRefNoExt.onchange = () => { this.updateExtensionField(idRefNoExt.value); };

 

        let sampleExtensionPropertyValue: string;

        if (!ObjectExtensions.isNullOrUndefined(this.customer.ExtensionProperties)) {

            let SampleCommerceProperties: ProxyEntities.CommerceProperty[] =

                this.customer.ExtensionProperties.filter(

                    (extensionProperty: ProxyEntities.CommerceProperty) => {

                        return extensionProperty.Key === "REFNOEXT";

                    });

            sampleExtensionPropertyValue = SampleCommerceProperties.length > 0 ? SampleCommerceProperties[0].Value.StringValue : "";

        }

        else {

            sampleExtensionPropertyValue = "";

        }

        idRefNoExt.value = sampleExtensionPropertyValue;

    }

    /**

    * Initializes the control.

    * @param {ICustomerDetailCustomControlState} state The initial state of the page used to initialize the

    control.

*/

    public init(state: ICustomerAddEditCustomControlState): void {

        this._state = state;

        if (!this._state.isSelectionMode) {

            this.isVisible = true;

            this.customerIsPerson = state.customer.CustomerTypeValue === ProxyEntities.CustomerType.Person ?

                true : false;

        }

    }

 

    public updateExtensionField(RefNoExt: string): void {

        this._addOrUpdateExtensionProperty("REFNOEXT", <ProxyEntities.CommercePropertyValue>{

            StringValue:RefNoExt

        });

    }

 

    /**

    * Gets the property value from the property bag, by its key. Optionally creates the property value on the bag,

    if it does not exist.

    */

    private _addOrUpdateExtensionProperty(key: string, newValue: ProxyEntities.CommercePropertyValue):

        void {

        let customer: ProxyEntities.Customer = this.customer;

        let extensionProperty: ProxyEntities.CommerceProperty =

            Commerce.ArrayExtensions.firstOrUndefined(customer.ExtensionProperties, (property:

                ProxyEntities.CommerceProperty) => {

                return property.Key === key;

            });

        if (ObjectExtensions.isNullOrUndefined(extensionProperty)) {

            let newProperty: ProxyEntities.CommerceProperty = {

                Key: key,

                Value: newValue

            };

            if (ObjectExtensions.isNullOrUndefined(customer.ExtensionProperties)) {

                customer.ExtensionProperties = [];

            }

            customer.ExtensionProperties.push(newProperty);

        } else {

            extensionProperty.Value = newValue;

        }

        this.customer = customer;

    }

}

 

Register class in manifest.json

Code - manifest.json

"CustomerAddEditView": {

          "controlsConfig": {

            "customControls": [

              {

                "controlName": "CustomerCustomField",

                "htmlPath": "ViewExtensions/CustomerAddEdit/CustomerCustomField.html",

                "modulePath": "ViewExtensions/CustomerAddEdit/CustomerCustomField"

              }

            ]

          }

 }

Rebuild the whole solution.

Once the build is compiled successfully Now copy the commerce runtime library.

CommerceRuntime.dll

From: “..\..\CommerceRuntime\bin\Debug\netstandard2.0”

To: “K:\RetailServer\WebRoot\bin\Ext”


Open CommerceRuntime.Ext.config file and add the following line in the composition tag.
<add source="assembly" value="CommerceRuntime" />

Now deploy the extension on the dev machine by copying the code.

 From:..\..\ ScaleUnit\bin\Debug\netstandard2.0\CloudScaleUnitExtensionPackage\RetailCloudPOS\Code\Extensions”

To: “K:\RetailCloudPos\WebRoot\Extensions”



Now login to pos and go to setting to check package

 



 Now search with customer and select one customer and open detail customer form


 

Now you can see custom control. But data not showing.

Now open Commerce Runtime (CRT)


Open class GetCustomerTrigger.cs

 


Code – GetCustomerTriggers.cs

namespace Contoso

{

    namespace Commerce.Runtime.EmailPreferenceSample

    {

        using System;

        using System.Collections.Generic;

        using System.Threading.Tasks;

        using Microsoft.Dynamics.Commerce.Runtime;

        using Microsoft.Dynamics.Commerce.Runtime.Data;

        using Microsoft.Dynamics.Commerce.Runtime.DataModel;

        using Microsoft.Dynamics.Commerce.Runtime.DataServices.Messages;

        using Microsoft.Dynamics.Commerce.Runtime.Messages;

 

        /// <summary>

        /// Class that implements a post trigger for the GetCustomerDataRequest request type.

        /// </summary>

        public class GetCustomerTriggers : IRequestTriggerAsync

        {

            /// <summary>

            /// Gets the supported requests for this trigger.

            /// </summary>

            public IEnumerable<Type> SupportedRequestTypes

            {

                get

                {

                    return new[] { typeof(GetCustomerDataRequest),

                                  typeof(GetCustomersDataRequest) };

                }

            }

 

            /// <summary>

            /// Post trigger code to retrieve extension properties.

            /// </summary>

            /// <param name="request">The request.</param>

            /// <param name="response">The response.</param>

            public async Task OnExecuted(Request request, Response response)

            {

                ThrowIf.Null(request, "request");

                ThrowIf.Null(response, "response");

 

                switch (request)

                {

                    case GetCustomerDataRequest getCustomerDataRequests:

                        var custom = ((SingleEntityDataServiceResponse<Customer>)response).Entity;

                        if (custom != null)

                        {

                            var query = new SqlPagedQuery(QueryResultSettings.SingleRecord)

                            {

                                DatabaseSchema = "ax",

                                Select = new ColumnSet(new string[] { "REFNOEXT" }),

                                From = "CustTable",

                                Where = "ACCOUNTNUM = @accountNum"

                            };

                            query.Parameters["@accountNum"] = custom.AccountNumber;

                            using (var databaseContext = new DatabaseContext(request.RequestContext))

                            {

                                var extensionsResponse = await

                                databaseContext.ReadEntityAsync<ExtensionsEntity>(query).ConfigureAwait(false);

                                ExtensionsEntity extensions = extensionsResponse.FirstOrDefault();

                                var refNOEXT = extensions != null ? extensions.GetProperty("REFNOEXT") : null;

                                if (refNOEXT != null)

                                {

                                    //customer.SetProperty("EMAILOPTIN", emailOptIn);

                                    custom.ExtensionProperties.Add(new CommerceProperty()

                                    {

                                        Key = "REFNOEXT",

                                        Value = refNOEXT.ToString()

                                    });

                                }

                            }

                        }

                        break;

                    case GetCustomersDataRequest getCustomersDataRequests:

                        var customers = (EntityDataServiceResponse<Customer>)response;

                        PagedResult<Customer> customer = customers.PagedEntityCollection;

                        foreach (Customer item in customer)

                        {

                            var query = new SqlPagedQuery(QueryResultSettings.SingleRecord)

                            {

                                DatabaseSchema = "ax",

                                Select = new ColumnSet(new string[] { "REFNOEXT" }),

                                From = "CustTable",

                                Where = "ACCOUNTNUM = @accountNum"

                            };

                            query.Parameters["@accountNum"] = item.AccountNumber;

                            using (var databaseContext = new DatabaseContext(request.RequestContext))

                            {

                                var extensionsResponse = await

                                databaseContext.ReadEntityAsync<ExtensionsEntity>(query).ConfigureAwait(false);

                                ExtensionsEntity extensions = extensionsResponse.FirstOrDefault();

                                var refNOEXT = extensions != null ? extensions.GetProperty("REFNOEXT") : null;

                                if (refNOEXT != null)

                                {

                                    item.ExtensionProperties.Add(new CommerceProperty()

                                    {

                                        Key = "REFNOEXT",

                                        Value = refNOEXT.ToString()

                                    });

                                }

                            }

                        }

                        break;

                    default:

                        break;

                }

            }

 

            /// <summary>

            /// Pre trigger code.

            /// </summary>

            /// <param name="request">The request.</param>

            public async Task OnExecuting(Request request)

            {

                // It's only stub to handle async signature

                await Task.CompletedTask;

            }

        }

    }

}

Open class - CreateOrUpdateCustomerDataRequestHandler


Code – CreateOrUpdateCustomerDataRequestHandler.cs

namespace Contoso

{

    namespace Commerce.Runtime.EmailPreferenceSample

    {

        using System.Threading.Tasks;

        using System.Transactions;

        using Microsoft.Dynamics.Commerce.Runtime;

        using Microsoft.Dynamics.Commerce.Runtime.Data;

        using Microsoft.Dynamics.Commerce.Runtime.DataAccess.SqlServer;

        using Microsoft.Dynamics.Commerce.Runtime.DataModel;

        using Microsoft.Dynamics.Commerce.Runtime.DataServices.Messages;

        using Microsoft.Dynamics.Commerce.Runtime.Messages;

 

        /// <summary>

        /// Create or update customer data request handler.

        /// </summary>

        public sealed class CreateOrUpdateCustomerDataRequestHandler : SingleAsyncRequestHandler<CreateOrUpdateCustomerDataRequest>

        {

            /// <summary>

            /// Executes the workflow to create or update a customer.

            /// </summary>

            /// <param name="request">The request.</param>

            /// <returns>The response.</returns>

            protected override async Task<Response> Process(CreateOrUpdateCustomerDataRequest request)

            {

                ThrowIf.Null(request, "request");

 

                using (var databaseContext = new SqlServerDatabaseContext(request.RequestContext))

                {

                    // Execute original functionality to save the customer.

                    var response = await this.ExecuteNextAsync<SingleEntityDataServiceResponse<Customer>>(request).ConfigureAwait(false);

 

                    // Execute additional functionality to save the customer's extension properties.

                    if (!request.Customer.ExtensionProperties.IsNullOrEmpty())

                    {

                        string RefNoExt = "";

                        var custExt = request.Customer.ExtensionProperties;

 

                        foreach (var item in custExt)

                        {

                            if (item.Key == "REFNOEXT")

                            {

                                RefNoExt = item.Value.StringValue.ToString();

                                break;

                            }

                        }

 

                        ParameterSet parameters = new ParameterSet();

                        parameters.Add("AccountNum", request.Customer.AccountNumber);

                        parameters.Add("REFNOEXT", RefNoExt);

                        await databaseContext.ExecuteStoredProcedureNonQueryAsync("[ext].[UPDATECUSTOMEREXTENEDPROPERTIES]", parameters, resultSettings: null).ConfigureAwait(true);

 

 

                        // The stored procedure will determine which extension properties are saved to which tables.

                        // ParameterSet parameters = new ParameterSet();

                        // parameters["@TVP_EXTENSIONPROPERTIESTABLETYPE"] = new ExtensionPropertiesExtTableType(request.Customer.RecordId, request.Customer.ExtensionProperties).DataTable;

                        // await databaseContext.ExecuteStoredProcedureNonQueryAsync("[ext].UPDATECUSTOMEREXTENSIONPROPERTIES", parameters, resultSettings: null).ConfigureAwait(false);

                    }

 

                   

                    return response;

                }

            }

        }

    } 

}

       Create store procedure in sql for update extension field data

      

CREATE PROCEDURE [ext].[UPDATECUSTOMEREXTENEDPROPERTIES] @AccountNum nvarchar(255), @REFNOEXT nvarchar(255)

AS

BEGIN

       Update ax.CUSTTABLE Set REFNOEXT = @REFNOEXT Where ACCOUNTNUM = @AccountNum

       Update dbo.CUSTTABLE Set REFNOEXT = @REFNOEXT Where ACCOUNTNUM = @AccountNum

END

 

Build the project Runtime.Extensions.EmailPreferenceSample.

  Copy dll from

K:\RetailSDK\SampleExtensions\CommerceRuntime\Extensions.EmailPreferenceSample\binDebug\netstandard2.0


Paste dll to

K:\RetailServer\WebRoot\bin\Ext


               Open CommerceRuntime.Ext.config file and add the following line in the composition tag.

<add source="type" value="Contoso.Commerce.Runtime.EmailPreferenceSample.GetCustomerTriggers, Contoso.Commerce.Runtime.EmailPreferenceSample" />

<add source="type" value="Contoso.Commerce.Runtime.EmailPreferenceSample.CreateOrUpdateCustomerDataRequestHandler, Contoso.Commerce.Runtime.EmailPreferenceSample" />


Now refresh, log in and go to customer detail view form

 

The End

Comments

Popular posts from this blog

How to Extend Customer List View adding Custom Field on Dynamics 365 Retail & Commerce POS

How to Search Custom Field from Customer and Num Pad in Dynamics 365 Retail & Commerce POS