Your Guide to Efficiently Setting Up Dynamics 365 Commerce Cloud POS

Setting Up Dynamics 365 Commerce Cloud POS: A Comprehensive Guide 

1)     Initiate the process by obtaining the Dynamics commerce scale unit from the GitHub repository. You can find it at the following address:
https://github.com/microsoft/Dynamics365Commerce.ScaleUnit

2)     Before proceeding further, verify your Finance and Operations (FnO) version to ensure compatibility with the scale unit.


3)     Download the appropriate release corresponding to your FnO version.


 

4)     To create an ideal environment for the setup, download and install the necessary SDKs and runtimes, including:

a.      sdk-2.1.202-windows-x64-installer

b.      sdk-2.1.513-windows-x64-installer

c.      runtime-2.0.9-windows-x64-installer

d.      runtime-2.1.17-windows-x64-installer

e.      .NET Core 3.1 SDK

f.       Windows SDK (10.0.10586.0)

g.      TypeScript version 2.2.2

h.   .NET 6.0 Runtime 

I.    .NET Core 6.0 Runtime 
 

5)     Unzip the downloaded repository, and move this to K drive with some meaning full name.

6)     Browse and open the scale unit in Visual Studio 19.


 

7)     Now Clean the projects by removing sample artifacts.

a.      For Chanel Database delete the SQL script

b.      For Commerce Runtime delete all the artifacts/Folder except DefinePosExtensionTrigger.cs file in the Triggers folder.

c.      For POS delete all the folders.

8)     Update DefinePosExtensionTrigger.cs file in the Commerce Runtime project with the Extension name.


9)     Update the manifest.json file in the POS project.


 

10)  In the POS project, create a folder that is named SearchExtension.

11)  In the SearchExtension folder, create a folder that is named ViewExtensions.

12)  In the ViewExtensions folder, create a folder named Search.

13)  In the Search folder, create a Typescript file that is named CustomCustomerSearchColumns.ts.

14)  In the CustomCustomerSearchColumns.ts file, add the following import statements to import the relevant entities and context.

import { ICustomerSearchColumn } from "PosApi/Extend/Views/SearchView";

import { ICustomColumnsContext } from "PosApi/Extend/Views/CustomListColumns";

import { ProxyEntities } from "PosApi/Entities";

15)  Add the existing column and the custom column to the file.

export default (context: ICustomColumnsContext): ICustomerSearchColumn[] => {

    return [

        {

            title: context.resources.getString("string_2"),

            computeValue: (row: ProxyEntities.GlobalCustomer): string => { return row.AccountNumber; },

            ratio: 15,

            collapseOrder: 5,

            minWidth: 120

         }, {

            title: context.resources.getString("string_3"),

            computeValue: (row: ProxyEntities.GlobalCustomer): string => { return row.FullName; },

            ratio: 20,

            collapseOrder: 4,

            minWidth: 200

        }, {

            title: context.resources.getString("string_4"),

            computeValue: (row: ProxyEntities.GlobalCustomer): string => { return row.FullAddress; },

            ratio: 25,

            collapseOrder: 1,

            minWidth: 200

        }, {

            title: context.resources.getString("string_5"),

            computeValue: (row: ProxyEntities.GlobalCustomer): string => { return row.Email; },

            ratio: 20,

            collapseOrder: 2,

            minWidth: 200

        }, {

            title: context.resources.getString("string_7"),

            computeValue: (row: ProxyEntities.GlobalCustomer): string => { return row.Phone; },

            ratio: 20,

            collapseOrder: 3,

            minWidth: 120

        }

    ];

};

16)  You will now add the resource file for the localization of the column name. In the SearchExtension folder, create a folder that is named Resources.

17)  In the Resources folder, create a folder named Strings.

18)  In the Strings folder, create a folder named en-US.

19)  In the en-us folder, create a file that is named resources.resjson.

20)  In the resources.resjson file, add the following code.

{

    //======================== Sample View extensions strings. ========================

    "string_0" : "Quick compare products",

    "_string_0.comment" : "Product search page app bar command label.",

    "string_1" : "View customer summary",

    "_string_1.comment" : "Customer search page app bar command label.",

    //======================== Column names. ========================

    "string_2" : "ACCOUNT NUMBER_CUSTOMIZED",

    "_string_2.comment" : "Customer search column name.",

    "string_3" : "NAME",

    "_string_3.comment" : "Customer search column name.",

    "string_4" : "ADDRESS",

    "_string_4.comment" : "Customer search column name.",

    "string_5" : "CONTACT EMAIL",

    "_string_5.comment" : "Customer search column name.",

    "string_7" : "PHONE NUMBER",

    "_string_7.comment" : "Customer search column name."

}

21)  In the SearchExtension folder, create a folder that is named DialogSample.

22)  In the DialogSample folder, create a TypeScript file that is named MessageDialog.ts.

23)  In the MessageDialog.ts file, add the following import statements to import the relevant entities and context.

import { ShowMessageDialogClientRequest, ShowMessageDialogClientResponse, IMessageDialogOptions } from "PosApi/Consume/Dialogs";

import { IExtensionContext } from "PosApi/Framework/ExtensionContext";

import { ClientEntities } from "PosApi/Entities";

24)  Create a class that is named MessageDialog.

export default class MessageDialog {}

25)  In the MessageDialog class, add the following show method.

public static show(context: IExtensionContext, message: string): Promise<void> {

    let promise: Promise<void> = new Promise<void>((resolve: () => void, reject: (reason?: any) => void) =>

    {

        let messageDialogOptions: IMessageDialogOptions = {

            title: "Extension Message Dialog",

            message: message,

            showCloseX: true, // this property will return "Close" as result when "X" is clicked to close dialog.

            button1: {

                id: "Button1Close",

                label: "OK",

                result: "OKResult"

            },

            button2: {

                id: "Button2Cancel",

                label: "Cancel",

                result: "CancelResult"

            }

        };

        let dialogRequest: ShowMessageDialogClientRequest<ShowMessageDialogClientResponse> =

            new ShowMessageDialogClientRequest<ShowMessageDialogClientResponse>(messageDialogOptions);

            context.runtime.executeAsync(dialogRequest).then((

                result: ClientEntities.ICancelableDataResult<ShowMessageDialogClientResponse>) => {

            if (!result.canceled) {

                context.logger.logInformational("MessageDialog result: " + result.data.result.dialogResult);

                resolve();

            }

        }).catch((reason: any) => {

        context.logger.logError(JSON.stringify(reason));

        reject(reason);

        });

    });

    return promise;

}

26)  You will now add a custom app bar button in the search view to open a dialog box that contains details about the selected customer. In the ViewExtensions folder, create a Typescript file that is named ViewCustomerSummaryCommand.ts.

27)  In the ViewCustomerSummaryCommand.ts file, add the following import statements to import the relevant entities and context.

import { ProxyEntities } from "PosApi/Entities";

import { ArrayExtensions, ObjectExtensions } from "PosApi/TypeExtensions";

import { IExtensionCommandContext } from "PosApi/Extend/Views/AppBarCommands";

import * as SearchView from "PosApi/Extend/Views/SearchView";

import MessageDialog from "../../Controls/DialogSample/MessageDialog";

28)  Create a class that is named ViewCustomerSummaryCommand, and extend it from CustomerSearchExtensionCommandBase.

export default class ViewCustomerSummaryCommand extends SearchView.CustomerSearchExtensionCommandBase {}

29)  In the ViewCustomerSummaryCommand class, declare a private variable to capture the results when searching for the selected customer.

private _customerSearchResults: ProxyEntities.GlobalCustomer[];

30)  Add the class constructor method to initialize and clear the search handler.

constructor(context: IExtensionCommandContext<SearchView.ICustomerSearchToExtensionCommandMessageTypeMap>) {

    super(context);

    this.id = "viewCustomerSummaryCommand";

    this.label = context.resources.getString("string_1");

    this.extraClass = "iconLightningBolt";

    this._customerSearchResults = [];

    this.searchResultsSelectedHandler = (data: SearchView.CustomerSearchSearchResultSelectedData): void => {

        this._customerSearchResults = data.customers;

        this.canExecute = true;

    };

    this.searchResultSelectionClearedHandler = (): void => {

        this._customerSearchResults = [];

        this.canExecute = false;

    };

}

31)  Add the init method to initialize the visible property.

protected init(state: SearchView.ICustomerSearchExtensionCommandState): void {

    this.isVisible = true;

}

32)  Add the execute method to handle the app button click handler. The execute method reads the data for the selected customer from the handler and shows it in a simple dialog box.

protected execute(): void {

    let customer: ProxyEntities.GlobalCustomer = ArrayExtensions.firstOrUndefined(this._customerSearchResults);

    if (!ObjectExtensions.isNullOrUndefined(customer)) {

        let message: string = "Customer Account: " + (customer.AccountNumber || "") + " | ";

        message += "Name: " + customer.FullName + " | ";

        message += "Phone Number: " + customer.Phone + " | ";

        message += "Email Address: " + customer.Email;

        MessageDialog.show(this.context, message);

    }

}

33)  The whole code sample should look like this.

import { ProxyEntities } from "PosApi/Entities";

import { ArrayExtensions, ObjectExtensions } from "PosApi/TypeExtensions";

import { IExtensionCommandContext } from "PosApi/Extend/Views/AppBarCommands";

import * as SearchView from "PosApi/Extend/Views/SearchView";

import MessageDialog from "../../DialogSample/MessageDialog";

export default class ViewCustomerSummaryCommand extends SearchView.CustomerSearchExtensionCommandBase {

    private _customerSearchResults: ProxyEntities.GlobalCustomer[];

 

    /**

    * Creates a new instance of the ViewCustomerSummaryCommand class.

    * @param {IExtensionCommandContext<CustomerDetailsView.ICustomerSearchToExtensionCommandMessageTypeMap>} context The command context.

    * @remarks The command context contains APIs through which a command can communicate with POS.

    */

    constructor(context: IExtensionCommandContext<SearchView.ICustomerSearchToExtensionCommandMessageTypeMap>) {

        super(context);

        this.id = "viewCustomerSummaryCommand";

        this.label = context.resources.getString("string_1");

        this.extraClass = "iconLightningBolt";

        this._customerSearchResults = [];

 

        this.searchResultsSelectedHandler = (data: SearchView.CustomerSearchSearchResultSelectedData): void => {

            this._customerSearchResults = data.customers;

            this.canExecute = true;

        };

 

        this.searchResultSelectionClearedHandler = (): void => {

            this._customerSearchResults = [];

            this.canExecute = false;

        };

    }

 

    /**

    * Initializes the command.

    * @param {CustomerDetailsView.ICustomerDetailsExtensionCommandState} state The state used to initialize the command.

    */

    protected init(state: SearchView.ICustomerSearchExtensionCommandState): void {

        this.isVisible = true;

    }

 

    /**

    * Executes the command.

    */

    protected execute(): void {

        let customer: ProxyEntities.GlobalCustomer = ArrayExtensions.firstOrUndefined(this._customerSearchResults);

        if (!ObjectExtensions.isNullOrUndefined(customer)) {

            let message: string = "Customer Account: " + (customer.AccountNumber || "") + " | ";

            message += "Name: " + customer.FullName + " | ";

            message += "Phone Number: " + customer.Phone + " | ";

            message += "Email Address: " + customer.Email;

            MessageDialog.show(this.context, message);

        }

    }

}

34)  In the SearchExtension folder, create a JSON file that is named manifest.json.

35)  In the manifest.json file, add the following code.

{

    "$schema": "../manifestSchema.json",

    "name": "Pos_Extensibility_Samples",

    "publisher": "Microsoft",

    "version": "7.2.0",

    "minimumPosVersion": "7.2.0.0",

    "components": {

        "resources": {

            "supportedUICultures": [ "en-US" ],

            "fallbackUICulture": "en-US",

            "culturesDirectoryPath": "Resources/Strings ",

            "stringResourcesFileName": "resources.resjson",

            "cultureInfoOverridesFilePath": "Resources/cultureInfoOverrides.json"

        },

        "extend": {

            "views": {

                "SearchView": {

                    "customerAppBarCommands": [ { "modulePath": "ViewExtensions/Search/ViewCustomerSummaryCommand" } ],

                    "customerListConfiguration": { "modulePath": "ViewExtensions/Search/CustomCustomerSearchColumns" }

                }

            }

        }

    }

}

36)  In the POS.Extensions project, open the extensions.json file, and update it with SearchExtension samples, so that the POS includes this extension at runtime.

{

    "extensionPackages": [

    {

        "baseUrl": "SampleExtensions2"

    },

    {

        "baseUrl": "SearchExtension"

    }

    ]

}

37)  In the tsconfig.json file, comment out the extension package folders in the exclude list. The POS uses this file to include or exclude the extension. By default, the list contains the whole excluded extensions list. To include an extension as part of the POS, add the name of the extension folder, and comment out the extension in the exclude list, as shown here.

"exclude": [

"SampleExtensions"

//"SampleExtensions2",

//"SearchExtension"

],

 

38)  Try to Rebuild the whole solution, this step is tricky you might need to install other runtimes.

39)  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”


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

41)  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”


42)  Browse Cloud POS and have a view of your created extension.


 

 

Comments