Showing posts with label Integration. Show all posts
Showing posts with label Integration. Show all posts

January 24, 2025

Custom web service in D365FO - Part-I

Introduction

Custom services in Dynamics 365 Finance and Operations (D365FO) allow you to expose custom business logic and data to external systems. This first part of the blog post will walk you through the basics of creating and consuming custom services in D365FO. Custom services are used to expose X++ business logic to external systems via SOAP or RESTful endpoints. They are ideal for scenarios where you need to implement complex business processes that are not covered by standard services.

In later posts, we will discuss some complex scenarios and best practices around web services.

Architecture

Service Request Contract

The service contract defines the operations that the service will expose. This is done by creating a new class with the [DataContractAttribute] and [DataMemberAttribute] attributes. There must be two contract classes: one for the request message and another for the response message. (We will cover this in detail in future posts).

Example:

[DataContractAttribute]
public class TheAxaptaRequestContract
{
    private boolean isShipped;
    private String255 dataAreaId;
    private String255 storeNumber;
    private String255 trackingNumber1;

    [DataMember("TrackingNumber1")]
    public String255 parmTrackingNumber1(String255 _trackingNumber1 = trackingNumber1)
    {
        trackingNumber1 = _trackingNumber1;
        return trackingNumber1;
    }

    [DataMember("IsShipped")]
    public boolean parmIsShipped(boolean _isShipped = isShipped)
    {
        isShipped = _isShipped;
        return isShipped;
    }

    [DataMember("DataareaId")]
    public String255 parmdataAreaId(String255 _dataAreaId = dataAreaId)
    {
        dataAreaId = _dataAreaId;
        return dataAreaId;
    }

    [DataMember("StoreId")]
    public String255 parmStoreNumber(String255 _storeNumber = storeNumber)
    {
        storeNumber = _storeNumber;
        return storeNumber;
    }
}

Service Response Contract

Example:

[DataContractAttribute]
public class TheAxaptaResponseContract
{
    private boolean success;
    private str errorMessage;
    private str debugMessage;
    private System.String salesOrder;

    [DataMember("Message")]
    public str parmMessage(str _value = errorMessage)
    {
        if (!prmIsDefault(_value))
        {
            errorMessage = _value;
        }

        return errorMessage;
    }

    [DataMember("Success")]
    public Boolean parmSuccess(Boolean _value = success)
    {
        if (!prmIsDefault(_value))
        {
            success = _value;
        }

        return success;
    }

    [DataMember("DebugMessage")]
    public str parmDebugMessage(str _value = debugMessage)
    {
        if (!prmIsDefault(_value))
        {
            debugMessage = _value;
        }

        return debugMessage;
    }
}

Service Class

This is the class where the actual business operation executes. It takes the request message from the respective contract class and processes it to create a response message. Create a new class to implement the service. This class should extend the SysOperationServiceBase class and include the business logic for the service operations.

Example:

public class TheAxaptaService extends SysOperationServiceBase
{
    TheAxaptaResponseContract response = new TheAxaptaResponseContract ();
[AifCollectionType('salesOrder', Types::Class, classStr(TheAxaptaRequestContract)),
AifCollectionType('return', Types::Class, classStr(TheAxaptaResponseContract ))]
public processMsg() { TheAxaptaRequestContract request;
// Now you can access the parm methods of the request class to get different values from the request Str StoreId = request.paramStoreNumber(); response.parmSuccess(True); response.parmMessage("Message read"); } }

Register the Service

Register the service in the AOT (Application Object Tree) by creating a new service node. Set the class and method properties to point to your service implementation.

  1. In the AOT, right-click Services and select New Service. (e.g. TheAxaptaInterface)
  2. Set the Class property to your service class (e.g., TheAxaptaService).
  3. Set the Method property to the service method (e.g., processMsg).

Deploy the Service

Deploy the service to make it available for consumption. This can be done by right-clicking the service node in the AOT and selecting Deploy Service Group.

Consume the Service

A third part can consume this service using the below syntax

<EnvironmentURL>/API/services/TheAxaptaInterface/TheAxaptaService/processMsg

and send the request message in body.

If you want to setup postman to test your services by yourself, I would recommend to checkout this blog post, 

April 30, 2023

Azure Key vault parameter setup in D365FO

Hi Folks, 

In this post, I am going to share how to configure Azure key vault parameters in Dynamics 365 Finance and Operations  (Let's call it FinOps until we have a new name from Microsoft :) ).

First, let's understand what the use of this form is, This is primarily used for integration scenarios where a business needs to save sensitive data like security keys or certifications and a functionality or application working with this data must support data encryption, working with certificates, etc. As the cloud version of Microsoft Dynamics 365 for Finance and Operations doesn't support local storage of certificates, customers need to use key vault storage in this case. The Azure Key Vault provides the opportunity to import cryptographic keys, and certificates to Azure, and to manage them.


Now let's see some prerequisite steps, 

1. Create a key value on the Azure portal and note the Value URI. This is available on the overview tab.

2. Add your certificate, Secrate, and keys.
3. On the Azure portal, do an app registration and store the client Id and secret key.  
4. Now navigate to D365FO > System admin > Setup > Key Vault Parameters
5. Create a new record and fill below details


6. On the certification tab, add below for each certificate 
Name
Description
Secret – Enter a secret reference to the certificate in the below format
vault://<KeyVaultName>/<SecretName>/(Version if any)
Secret Type: Certificate

7. Click on Validate button to check the setting. 

That is all, now you should be able to access this certificate in your code, here is a sample code to access the certificate, 

public class TheAxaptaAccessKeyVault
{
    public static void main(Args _args)
    {
        KeyVaultCertificateTable    kvcTable;
        str                                        value

         kvcTable  = KeyVaultCertificateTable::findByName("TestKeyVault");
        value         = KeyVaultCertificateHelper::getManualSecretValue(certTable.RecId);

        info(value); //This will give you stored in the certificate. 
}

Cheers!!!

-Harry Follow us on Facebook to keep in rhythm with us. https:fb.com/theaxapta

February 25, 2023

Best way to do Postman setup with D365FO

Hi Folks, 

There are very simple steps to do initial setup between Postman and D365FO environment. Please follow below steps.

(Make sure you have added a record in 'Azure Active Directory applications' in D365FO under Sys admin > setup > Azure Active Directory applications )

1. Download postman from here and install on you machine.

2. Do app registration on Azure portal, and make sure you copy all details from app registration to a safe place as not all information will available for later use.

3. Go to environment in left pane and create a new environment, you can name it as same as your D365FO environment like DEV01, UAT, Test etc. This also help when you are working with multiple environment and you keep using same get/post script to access different environments. 

4. Add all the variables here as below

Client_ID: You will get it from Azure app registration. 
grant_Type: client_credentials
resource: D365FO environment URL i<https://D365FOUrl/>
client_secret: You will get it from Azure app registration. 
tenant_id: You will get it from Azure app registration. 
access_token: To generate access token follow step 9.


5. Now create new collection and name it with your environment name eg. Dev01



6. Next, click on 3 dots next to collection name and select 'add request'


7. Name this request as 'Authorization', you can name it as per your use like getting public entities details or get a specific entity data or metadata. In the post request paste below as is.

https://login.microsoftonline.com/{{tenant_id}}/oauth2/token


8. Select 'From-data' in body and set below details.



Now you see we parameterize most of things and you don't need to create multiple request for different environments, you can simple change the environment from right top corner. 

9. Now,  Click on send button and you should get a status 200 message with access token. 
You can add the access token to your environment variable. 

Here we have completed the Postman setup. Now you can try few things to explore this further

10. Add one more request in your collection, and add details as below to get list of public entities. 



-Harry Follow us on Facebook to keep in rhythm with us. https:fb.com/theaxapta

December 26, 2022

More about Bicep

Hi Folks, 

Hope you had a great Christmas time and enjoying your holiday time to get ready for the New year. 
Sometime last year Microsoft started promoting Bicep as a new tool (language) for Logic app development, if you haven't read my post on that, pl follow the below link. 

Now its been enough time and Microsoft really come up with a lot of good documentation and real-time example to use bicep to design your solution. Below are some useful links to explore more about bicep, 


-Harry Follow us on Facebook to keep in rhythm with us. https:fb.com/theaxapta

November 11, 2022

Useful links for OData and Rest API integration

Here are some handy links for D365FO integration with OData or using Rest API, 




-Harry Follow us on Facebook to keep in rhythm with us. https:fb.com/theaxapta

July 30, 2020

Say Hello to Microsoft DataFlex

Hi Folks,

Here we go again with a rebranding around CDS which is now known as DataFlex, yes that's right, CDS is now DataFlex - a new low-code data platform for Microsoft Teams. You can create and run thousands of applications, flows, and intelligent agents with a smart, secure, and now with scalable low-code data platform.

What it offers:

DataFlex Pro: Its more or less the same as what we have in CDS today including same licensing i.e.

            $10/app/User/Month

            $40/User/Month – unlimited apps

 

DataFlex: Its basically a new simplified version or I would say lightweight of CDS for building an app on top of Microsoft Team. It includes as free with your Microsoft office 365 license which includes Teams. 

Below is a screenshot for reference, hoe easily you can create different tables and columns to meet business requirements.

 

 

Why DataFlex?

 

i.                    Provides the ability to use model-driven power apps in Teams with no extra licensing cost.

ii.                 Currently, to build an app for Team you need to use the SharePoint list to save some licensing cost that too not a relation Database and without scalability. While DataFlex provides a free relation DB to build a team-based PowerApps

iii.               A low code tool that enables you to build Team apps and bots similar to native apps.

iv.               Streamline identity protection and secure guest access with identity management and multifactor authentication.

v.                  Reduce data management stress and let Dataflex Pro determine your storage needs for relational data, file and blob storage, logs, and search indexing.

vi.               Quickly develop applications your way—either with custom code or using no or low-code—across Azure, Office 365, and Dynamics 365, and with more than 300 connectors.

vii.            Create a solid data foundation using automatic duplication detection and more than 300 data transformations that clean and reshape data

Further references:

Introducing Project Oakdale, a new low-code data platform for Microsoft Teams

> CDS Doc




-Harry Follow us on Facebook to keep in rhythm with us. https:fb.com/theaxapta

September 02, 2019

Set up MT940 format for bank reconciliation #MSD365FO

Hi Folks,

In D365FO advance bank reconciliation is a feature to import bank statement file and automatically reconcile with related bank accounts. There are many formats which a bank commonly used i.e. ISO20022, MT940, BAI2.

In this post, we will see how to set up the MT940 format which is commonly used in most banks nowadays.

Let's get started.

Step 1: Get sample entity template and transformation files
To transform the source file into FnO format, you need few files and these are available under 'Resources' node of AOT. Files names are as below



Step 2: Create an import project
Under Data management workspace, create an import project with name MT940. Add a new file with below details
I. Entity Name: Bank statements
II. Upload file name: SampleBankCompositeEntity (which you got from Resource node)



Once file successfully uploaded, click on view map. On next screen select BankStatementDocumentEntity from the list and click on 'View Map' and go to 'Transformation' tab. Click new and click upload file, select different XLST file one by one, in the sequence shown in below image.



Step 3: Setup Bank statement format
Navigate to Cash and Bank management > Setup > Advance bank reconciliation setup > Bank statement format.
Here create a new record as below



Step 4: Configure the bank account to use Advance reconciliation option
Navigate to Cash and Bank management  > Bank accounts. Select a Bank account to view details. Under Reconciliation tab,
I. Set the 'Advance bank reconciliation' option to 'yes'. This is a one-time setup, the system doesn't allow to undo/change once set to yes.


II. Set Statement format field to format which we have created in step 3 i.e. MT940




Step 5: Testing
Navigate to Cash and Bank management  > Bank accounts. On Reconcile tab click on Bank statements.
On the next screen click on Import statement. A dialog will pop up. Fill/select details as below

I. Import statement for multiple bank account in all entities. : Set this as Yes if your file contains more than one bank accounts.
II. Bank Account: If the source file contains a single bank, select that bank here.
III. Statement Format: Select your statement format here it must be MT940.
IV: Bank statement file import: Select source file and click upload.
V: Click ok and it must import transaction in the current form.

Note: After every DB refresh you need to redo import project. DB will break the links and you need to remove the entity from your import project and add upload the transformation files accordingly.

-Harry Follow us on Facebook to keep in rhythm with us. https:fb.com/theaxapta

PS: This post referred to MS documentation. 

May 13, 2019

How to add 'Actions' on Odata entities


Hi Folks,


[Updated on Jan 27, 2020
I got one feedback about Odata action on entity extension are not supported. It worked for me for one odd case, I yet to test myself if its not working now. I would request all readers to take necessary caution while trying below code. Thanks to Ashish for sharing his feedback.]


In this post, I'll show how to add a new action to D365FO Odata entities which you may want to access in Logic apps or Microsoft Flow. There could be two possible scenarios, either you have custom data entity where you can directly add a method or you have standard data entity for that you need to create extension class.  

First, let's see how to add on the custom entity (yes cause, it's straight forward 😉 ), 

Add your method on data entity and add below attribute
 [SysODataActionAttribute("<MethodName>", false)]
public static void <methodName>(<parameters(optional)>)
{
}

Save, Synch and Build your changes. This method should be available now on OData action in the Logic app or Flow. 

Now the other part, how to add the same in Data entity extension. Create a new class and add below attribute.

[ExtensionOf(tableStr(<data entity name>))]
final class <data entity name>_Extension
{
         [SysODataActionAttribute("<MethodName>", false)]
         public static void <methodName>(<parameters(optional)>)
        {
         }
}

Pl make sure you use '_Extension' keyword for above class, it's mandatory.

That's all for today. Try it and share your feedback. 

[Updated May 18, 2020]

 If you want to return any value use this syntax

 [SysODataActionAttribute("<MethodName>", false), SysODataCollectionAttribute("return", Types::Record, "CarColor")]

         public static void <methodName>(<parameters(optional)>)

        {

         }


Check below link for more details on Odata


Related topics:

-Harry Follow us on Facebook to keep in rhythm with us. https:fb.com/theaxapta


October 02, 2013

Connecting to Databases through X++ PART -IV

Connecting to Databases through X++ PART -IV


Connection Class:
Connection class is mainly used for accessing the database in which a user has logged into AX i.e. Current Database and carry out the operations. This class is exetensively used in ReleaseUpdateDB classes, the classes used in data upgrades. This class cannot be run on client and should always be run on server. One more unique thing that I noticed is that the statements that you want to execute should be asserted first for permissions and then passed on to other method where they are executed. Create a class with following methods and set its RunOn property to Server.
class TestSQLExecuteClass
{
}




//This method tests the permissions for statement and then calls the method that will execute the statement
static void dbConnectionClass()
{
    ResultSet   rs;
    SqlStatementExecutePermission perm;
    ;
    perm = new SQLStatementExecutePermission("select * from CustTable where DATAAREAID = ‘CEU’");
    perm.assert();
    rs = TestSQLExecuteClass::statementExeQuery("select * from CustTable where DATAAREAID = ‘CEU’");
    while (rs.next())
    {
        info(rs.getString(1));
    }
    CodeAccessPermission::revertAssert();
}
//Executes the passed statement
private static ResultSet statementExeQuery(str _sql, Connection _con = null)
{
    ResultSet   resultSet;
    Statement   statement;
    ;
    try
    {
        if(!_con)
        {
            _con = new Connection();
        }
        statement = _con.createStatement();
// Do not call assert() here, do it in the caller
        // BP deviation documented
        resultSet = statement.executeQuery(_sql);
    }
    catch (Exception::Error)
    {
        throw error("@SYS99562");
    }
    return resultSet;
}
Now you can call the method in a job as shown below:
static void dbConnectionClass(Args _args)
{
    ;
    TestSQLExecuteClass::dbConnectionClass();
}
These examples shown here are pretty simple and easy to understand and start with. Hope it helps you in building ‘connections’

Related posts, 


-Harry

September 16, 2013

Connecting to Databases through X++ PART -III

Connecting to Databases through X++ PART -III

      OLEDB Connection:

OLEDB is a set of APIs designed by Microsoft and used for accessing different types of data stored in a uniform manner. Dynamics AX as such doesn’t have any specific classes built for this purpose. But one can make use of .Net Framework’s System.Data.OleDb namespace through AX’s COM Interoperability feature and use it in AX.
Below is an example code that depicts this scenario:




static void dbOLEDBConnection(Args _args)
{
    System.Exception                    e;
    System.Data.OleDb.OleDbConnection   objConn;
    System.Data.OleDb.OleDbCommand      cmdSelect;
    System.Data.OleDb.OleDbDataReader   reader;
    InteropPermission                   perm;
    str connectStr = "Provider=SQLNCLI.1;Integrated Security=SSPI;"+
                     "Persist Security Info=False;Initial Catalog=AX2009;Data Source= theAxapta ";
    str exceptionStr;
    ;
    try
    {
        perm = new InteropPermission(InteropKind::ClrInterop);
        if (perm == null)
        {
            throw error("Error with file permissions");
        }
        perm.assert();
        objConn = new System.Data.OleDb.OleDbConnection(connectStr);
        objConn.Open();
        cmdSelect   = objConn.CreateCommand();
        cmdSelect.set_CommandText("SELECT * FROM CustTable where DATAAREAID = ‘CEU’");
        reader      = cmdSelect.ExecuteReader();
        while (reader.Read())
        {
            info(reader.GetString(0));
        }
    }
    catch(Exception::CLRError)
    {
        CodeAccessPermission::revertAssert();
        perm = new InteropPermission(InteropKind::ClrInterop);
        if (perm == null)
        {
            return;
        }
        perm.assert();
        e = ClrInterop::getLastException();
        CodeAccessPermission::revertAssert();
        while( e )
        {
            exceptionStr += e.get_Message();
            e = e.get_InnerException();
        }
        info(exceptionStr);
    }
    catch
    {
        error("An Exception has occurred");
    }
    if(objConn)
        objConn.Close();
}


Other related posts:



-Harry

September 11, 2013

Connecting to Databases through X++ PART -II

Connecting to Databases through X++ PART -II


    ADO Connection:
ADO is a set of COM objects for accessing databases or data stores. In AX we have following Classes/Objects which help to implementing ADO concept.

Class Name
Description
Helps in establishing a connection to the target database.
Helps in executing a command (a Text type or a Stored procedure)
Stores the data
A collection of all fields in CCADORecordSet
A single field from the collection of fields
A class that helps in passing parameters that a command needs or demands

Here is an example:

static void dbCCADOConnection(Args _args)
{
    CCADOConnection connection = new CCADOConnection();
    CCADOCommand    ccADOCommand;
    CCADORecordSet  record;
    str connectStr = "Provider=SQLNCLI.1;Integrated Security=SSPI;"+
                     "Persist Security Info=False;Initial Catalog=AXDEVDB;Data Source= theAxapta";
    COM     recordSet;  /*This is required to call moveNext method to parse the record set. In AX 4.0 this method was there in the CCADORecordSet class but in AX 2009 this has been deleted*/
    ;
    // Executing a SQL Statement
    try
    {
        connection.open(connectStr);
        ccADOCommand = new CCADOCommand();
        ccADOCommand.commandText("Select * from CustTable where DataAreaId = ‘CEU’");
        ccADOCommand.activeConnection(connection);
        record = ccADOCommand.execute();
        recordSet = record.recordSet();
        while (!record.EOF())
        {
            info(any2str(record.fields().itemIdx(0).value()));
            recordSet.moveNext();
        }
    }
    catch
    {
        error("An Exception has occurred");
    }
    connection.close();
}





Previous Post:


-Harry

September 09, 2013

Connecting to Databases through X++ PART -I

Connecting to Databases through X++ PART -I






In This series of post we will discuss about all possible ways through which we can connect to different databases.

     1.    ODBC Connection (Open Data Base Connection)
     2.    ADO Connection (ActiveX Data Objects)
      3. OLEDB Connection (Object Linking and Embedding, Database)
      4. Connection class

    1.  ODBC Connection:

ODBC used to define a connection between a computer and a database stored on another system. The ODBC connection allows computer user to access the information stored in a database that is not local to that computer. In Dynamics AX, we  have ODBCConnection class to carry out this type of database connection need. This class further uses LoginProperty class for login information and uses Statement and ResultSet classes for carrying out DML operations.
Here is an example of how to use this class.

static void theAxapta_ODBCConnection(Args _args)
{
    LoginProperty   loginProp;
    ODBCConnection  conn;
    Resultset       resultSet, resultSetCount;
    Statement       statement1, statement2;
    ;
    loginProp = new LoginProperty();
    loginProp.setServer(‘theAxapta’);//you can use IP address as well
    loginProp.setDatabase(‘AXDEVDB
);
    conn = new ODBCConnection(loginProp);
    statement1  = conn.createStatement();
    resultSet   = statement1.executeQuery("SELECT * from CustTable where DATAAREAID = ‘CEU’");
    while (resultSet.next())
    {
        info(resultSet.getString(1));
    }
}


Other related posts:



-Harry

May 22, 2013

How to Read/Write an Excel file through X++ code


How to write an Excel file through X++ code

In This Post you will found two code sample
1.Write data in excel through X++ code.
2. Read from an Excel through X++ code

1.Write data in excel through X++ code.





static void thaAxapta_Write2Excel(Args _args)
{

InventTable inventTable;
SysExcelApplication application;
SysExcelWorkbooks workbooks;
SysExcelWorkbook workbook;
SysExcelWorksheets worksheets;
SysExcelWorksheet worksheet;
SysExcelCells cells;
SysExcelCell cell;
int row;
;
application = SysExcelApplication::construct();
workbooks = application.workbooks();
workbook = workbooks.add();
worksheets = workbook.worksheets();
worksheet = worksheets.itemFromNum(1);
cells = worksheet.cells();
cells.range('A:A').numberFormat('@');
cell = cells.item(1,1);
cell.value("Item");
cell = cells.item(1,2);
cell.value("Name");
row = 1;
while select inventTable
{
    row++;
    cell = cells.item(row, 1);
    cell.value(inventTable.ItemId);
    cell = cells.item(row, 2);
    cell.value(inventTable.ItemName);
}
application.visible(true);
}


2. Read from an Excel through X++ code

static void theAxapta_ReadExcel(Args _args)
{

SysExcelApplication application;
SysExcelWorkbooks workbooks;
SysExcelWorkbook workbook;
SysExcelWorksheets worksheets;
SysExcelWorksheet worksheet;
SysExcelCells cells;
COMVariantType type;
int row;
ItemId itemid;
Name name;
FileName filename;
;
application = SysExcelApplication::construct();
workbooks = application.workbooks();
//specify the file path that you want to read
filename = "C:\\item.xls";
try
{
    workbooks.open(filename);
}
catch (Exception::Error)
{
    throw error("File cannot be opened.");
}
workbook = workbooks.item(1);
worksheets = workbook.worksheets();
worksheet = worksheets.itemFromNum(1);
cells = worksheet.cells();
do
{
    row++;
    itemId = cells.item(row, 1).value().bStr();
    name = cells.item(row, 2).value().bStr();
    info(strfmt('%1 - %2', itemId, name));
    type = cells.item(row+1, 1).value().variantType();
}
while (type != COMVariantType::VT_EMPTY);
application.quit();
}