Wednesday, 3 May 2023

POSTMAN D365

 Postman is useful to test the behavior of different OData class from/to D365FO. In this post we will see the steps to setup Postman with D365FO.

Step 1: Register D365FO in Azure Platform. Please refer to previous post to view the steps for App registration.

 Application registration of D365FO with Azure 

Step 2: Launch Postman and create a new collection and give a name (say) Devbox.

Step 3: Click on the three dots and click on Add request of type POST and name the request as Authorization.

Step 4: Capture the TenantId from the Azure portal and POST command would be

https://login.microsoftonline.com/tenantId/oauth2/token

Step 5: Select tab Body and click on form-data radio button.

Step 6: Provide below details in the Body tab

KeyValue
client_Id                          ClientID value from Azure portal after App registration.
grant_typeclient_credentials
resourceD365FO URL
client_secretClient secret value from Azure portal after App registration

Step 7: Click on Send button to receive the access token with status as 200.

This completes the Postman setup with D365FO.

Reference: https://dynamics365musings.com/setup-postman-to-call-d365-data-entities/

Wednesday, 13 July 2022

Debug the Sandbox/Test environment in D365FO

 Above mentioned steps can be reduced to only one step i.e refresh of production DB to sandbox via LCS and later we can connect to sandbox environment directly from the development environment for debugging using below guide

Time required 20 minutes.

Steps

  1. Enable access for your IP address

    Go to lcs ->sandbox environment page. To connect to test environment RDP, you need to create Whitelist rule for your IP address enable access

  2. Connect RDP and open SSMS

    Connect to SQL server using Server name from LCS and add the prefix to SQL server name – servername.database.windows.netSSMS RDP

  3. Create a new query to test DB

    Below query will create a new user that will be used for debugging
    CREATE USER devtempuser WITH PASSWORD = ‘pass@word1’
    EXEC sp_addrolemember ‘db_owner’, ‘devtempuser’


    SQL

  4. Whitelist your IP address

    Select new query against Master DB
    exec sp_set_firewall_rule N’DEVNAME’, ‘IP’, ‘IP’ Whitelist IP

  5. Stop IIS, WWW service and Batch Service

    Open IIS and stop the service. Open run and enter services.msc and stop WWW and Microsoft D365 batch service

  6. Edit Web config

    Go to C:\AosService\webroot\web.config and save the original file somewhere else as backup. Modify 4 Keys as per the below screenshot.
    You can either comment original configuration or delete it and add new configuration of the sandbox environmentWEB config

  7. Start IIS and WWW services

    Start IIS and WWW service stopped in step 5. Don’t start batch service.

  8. Open the development environment AX URL

    If you get 503 unavailable error, go to CMD(as admin) and type IISRESET

  9. Debug

    Now sandbox environment is connected to your development environment. You can simply open Visual studio to add breakpoints to X++ object and attach w3wp.exe service to debug.

  10. Thanks Pawan Deep SinghI 

  11. Reference: https://axparadise.com/how-to-debug-the-sandbox-test-environment-in-d365fo/

Monday, 11 October 2021

cannot create a record in customers (custtable the record already exists)

Go to SQL as Admin, AXDB database:

pass your table(Custable) and run below query:

DECLARE @MaxRecID BIGINT
DECLARE @NextVal BIGINT
 
SELECT @MaxRecID = MAX(RECID)
FROM custtable
 
SELECT @NextVal = NEXTVAL
FROM SYSTEMSEQUENCES
INNER JOIN SQLDICTIONARY
ON SQLDICTIONARY.FIELDID = 0
AND SQLDICTIONARY.name = 'custtable'
AND SQLDICTIONARY.TABLEID = SYSTEMSEQUENCES.TABID
 
IF (@NextVal > @MaxRecID)
BEGIN
PRINT 'custtable did not need to be updated.'
END
ELSE
BEGIN
PRINT 'Updated custtable from ' + CONVERT(VARCHAR(MAX), @NextVal) + '' to '' + CONVERT(VARCHAR(MAX), @MaxRecID + 1)
UPDATE SYSTEMSEQUENCES
SET NEXTVAL = @MaxRecID + 1
FROM SYSTEMSEQUENCES
INNER JOIN SQLDICTIONARY
ON SQLDICTIONARY.FIELDID = 0
AND SQLDICTIONARY.name = 'custtable'
AND SQLDICTIONARY.TABLEID = SYSTEMSEQUENCES.TABID
END

Sunday, 10 October 2021

Creating new D365FO Business Event for Customer & CRUD notifications using Power Automate.

Main contract class: 

// This is a BusinessEventContract class for Customer

/// </summary>
[DataContract]
class VKCustTableBusinessEventContract extends BusinessEventsContract
{
    SysAppCRUDOperation                action;
    CustAccount                        custAccount;
    DataAreaId                        dataAreaId;
 
    private void initialize(CustTable _custTable, SysAppCRUDOperation _action)
    {
        custAccount    = _custTable.AccountNum;
        dataAreaId    = _custTable.DataAreaId;
        action        = _action;
    }
 
    /// <summary>
    /// Create new VKCustTableBusinessEventContract from CustTable and action
    /// </summary>
    /// <param name = "_custTable">CustTable table</param>
    /// <param name = "_action">Action</param>
    /// <returns>a new VKCustTableBusinessEventContract</returns>
    public static VKCustTableBusinessEventContract newFromCustTable(CustTable _custTable, SysAppCRUDOperation _action)
    {
        VKCustTableBusinessEventContract    contract    = new VKCustTableBusinessEventContract();
        contract.initialize(_custTable, _action);
        return contract;
    }
 
    protected void new()
    {
    }
 
    [DataMember('CustAccount'), BusinessEventsDatamember("CustAccount")]
    public CustAccount parmCustAccount(CustAccount _custAccount = custAccount)
    {
        custAccount = _custAccount;
        return custAccount;
    }
 
    [DataMember('Action'), BusinessEventsDatamember("Action")]
    public SysAppCRUDOperation parmAction(SysAppCRUDOperation _action = action)
    {
        action = _action;
        return action;
    }
 
    [DataMember('DataAreaId'), BusinessEventsDatamember("DataAreaId")]
    public DataAreaId parmDataAreaId(DataAreaId _dataAreaId = dataAreaId)
    {
        dataAreaId = _dataAreaId;
        return dataAreaId;
    }
 
}

Business event class:

/// <summary>
/// This is a BusinessEvent class for Customer
/// </summary>
[BusinessEvents(classStr(VKCustTableBusinessEventContract),
    "Customers CU",
    "Event for create, update",
    ModuleAxapta::General)]
class VKCustTableBusinessEvent extends BusinessEventsBase
{
    private CustTable custTable;
    SysAppCRUDOperation action;
 
    /// <summary>
    /// Create new VKCustTableBusinessEvent from CustTable and action
    /// </summary>
    /// <param name = "_custTable">CustTable table</param>
    /// <param name = "_action">Action</param>
    /// <returns>a new VKCustTableBusinessEvent</returns>
    public static VKCustTableBusinessEvent newFromCustTable(CustTable _custTable, SysAppCRUDOperation _action)
    {
        VKCustTableBusinessEvent businessEvent = new VKCustTableBusinessEvent();
        businessEvent.parmAction(_action);
        businessEvent.parmCustTable(_custTable);
        return businessEvent;
    }
 
    private CustTable parmCustTable(CustTable _custTable = custTable)
    {
        custTable = _custTable;
        return custTable;
    }
 
    private SysAppCRUDOperation parmAction(SysAppCRUDOperation _action = action)
    {
        action = _action;
        return action;
    }
 
    protected void new()
    {
    }
 
    [Wrappable(true), Replaceable(true)]
    public BusinessEventsContract buildContract()
    {
        return VKCustTableBusinessEventContract::newFromCustTable(custTable, action);
    }
 
}

Extension to CustTable table to trigger insert/update events:

/// <summary>
/// Extension class for Custtable table
/// </summary>
[ExtensionOf(tableStr(CustTable))]
final class VKCustTable_Extension
{
    public void insert(DirPartyType _partyType, Name _name,boolean _updateCRM)
    {
        next insert(_partyType, _name, _updateCRM);
 
        if (BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(VKCustTableBusinessEvent)) && !CustTable::vkSkipByUserFilter())
        {
            VKCustTableBusinessEvent::newFromCustTable(this, SysAppCRUDOperation::Create).send();
        }
    }
 
    public void update(boolean _updateSmmBusRelTable, boolean _updateParty)
    {
        next update(_updateSmmBusRelTable, _updateParty);
 
        if (BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(VKCustTableBusinessEvent)) && !CustTable::vkSkipByUserFilter())
        {
            VKCustTableBusinessEvent::newFromCustTable(this, SysAppCRUDOperation::Update).send();
        }
    }
 
    /// <summary>
    /// Whether to skip sending business event or not
    /// </summary>
    /// <returns>true to skip sending BE</returns>
    public static boolean vkSkipByUserFilter()
    {
        boolean ret = false;
 
        // Use there real user id, which will be used for integration to avoid triggering update business event when external system updates the entity
        if ('Admin' == curUserId())
        {
            ret = true;
        }
 
        return ret;
    }
 
}

Configuration:

- Build
- System administration / Setup / Business events / Business events catalogue
- Manage / Rebuild business event catalogue
You should be able to find your Business Event in the list in specified category 

Power Automate:

- Create new
- Automated cloud flow
- Set Flow name and click Skip button
- In Search input type "Dynamics"
- Select Fin & Ops Apps
- Select Whan a Business Event occurs
- Select three dots in the right top corner and in My connecions section select "Add new connection"
- Select Connect with service principal
- Fill in Connection name, Client ID, Client Secret and Tenant (which is Licensed to in D365FO About)
- Select three dots in the right top cornerand choose corrrect connection
- Select right Instance
- Select General as Category
- Select Customer CU as Business event

- Add new step
- Type "Parse JSON" in search input
- Select "Parse JSON"
- Set body of previouse step as a Content
- Click Generate from sample button and pase there Schema from Business event (System administration / Setup / Business events / Business events catalogue / find the record / click Download schema button)

- Add additional steps with required actions


Troubleshooting:

There is a bug in Power Automate that it is not possible to select your business event if you place it in a category with existing standard business events, for example "Accounts Receivable". It shows duplicating record in the category dropdown and then there is no option to select your newly created business event:
Category dropdown:





POSTMAN D365

  Postman is useful to test the behavior of different OData class from/to D365FO. In this post we will see the steps to setup Postman with D...