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
Post a Comment