Sunday, 9 June 2024

Import and Export file from BLOB storage Account(Azure) in D365 F&O using X++

 

Import and Export file from BLOB storage Account in D365 F&O using X++

Import:

/// <summary>

/// MKInventQualityOrderLineService

/// </summary>

using Microsoft.Azure;

using Microsoft.WindowsAzure.Storage;

using Microsoft.WindowsAzure.Storage.Blob;

using Microsoft.WindowsAzure.Storage.File;

using System.Net;

/// <summary>

/// MKInventQualityOrderLineService

/// </summary>

class MKInventQualityOrderLineService   extends SysOperationServiceBase

{

    #File

    #define.delimiterField(',')

    InventParameters    inventParameters;

    

     /// <summary>

    ///  processMultipleFiles

    /// </summary>

   /// <param name = "_contract">MKInventQualityOrderLineContract</param>

    public void processMultipleFiles(MKInventQualityOrderLineContract _contract)

    {

        #OCCRetryCount

 

        System.IO.MemoryStream memoryStream;

        System.String          storageAccountName;

        System.String          keyValue;

 

        CloudStorageAccount    storageAccount;

        CloudFileClient        fileClient;

        CloudFileShare         fileShare;

        CloudFileDirectory     fileDirectoryRoot;

        CloudFileDirectory     fileDirectory;

        CloudFile              file;

        TextStreamIo           textStreamIo;

        inventParameters = InventParameters::find();

        storageAccountName     = inventParameters.MKStorageAccountName;

        keyValue               = inventParameters.MKAccountKeyValue;

        var storageCredentials = new Microsoft.WindowsAzure.Storage.Auth.StorageCredentials(storageAccountName, keyValue);

        storageAccount         = new Microsoft.WindowsAzure.Storage.CloudStorageAccount(storageCredentials, true);

        fileClient             = storageAccount.CreateCloudFileClient();

        fileShare              = fileClient.GetShareReference(inventParameters.MKFileShare);

        if (fileShare.Exists(null, null))

        {

            fileDirectoryRoot = fileShare.GetRootDirectoryReference();

            fileDirectory     = fileDirectoryRoot.GetDirectoryReference(inventParameters.MKLineSourcePath);

            if (fileDirectory.Exists(null, null))

            {

                // Get a reference to the root directory for the share.

                CloudFileDirectory      rootDirectory   = fileShare.GetRootDirectoryReference();

                // Get a reference to the directory we created previously.

                CloudFileDirectory      readFolder = rootDirectory.GetDirectoryReference(inventParameters.MKLineSourcePath);

                Microsoft.WindowsAzure.Storage.File.FileRequestOptions      fileRequestOptions;

                var     files       = readFolder.ListFilesAndDirectories(fileRequestOptions, null);

                var     enumerator  = files.getEnumerator();

                while (enumerator.moveNext())

                {

                    File= enumerator.Current as CloudFile;

                    if (file.Exists(null, null))

                    {

                        try

                        {

                            memoryStream = new System.IO.MemoryStream();

                            file.DownloadToStream(memoryStream, null, null, null);

                            this.importFromBlob(memoryStream, File.Name) ;

                            textStreamIo = TextStreamIo::constructForRead(memoryStream);

                            DMFDefinitionGroupExecution definitionGroupExecution;

                            select firstonly TargetStatus from definitionGroupExecution

                                order by RecId desc

                                where definitionGroupExecution.DEFINITIONGROUP == inventParameters.MKLineDefinationGroupName;

                            //upload

                            if(definitionGroupExecution.TargetStatus == DMFBatchJobStatus::Finished)

                            {

                                this.writeFile(File.Name, inventParameters.MKLineDestinationPath, memoryStream);

                            }

                            else

                            {

                                this.writeFile(File.Name, inventParameters.MKLineErrorPath, memoryStream);

                            }

                         

                            this.deleteFile(File.Name, inventParameters.MKLineSourcePath);

                            InventQualityOrderTable qualityOrder;

                            Args args = new Args();

                            while select qualityOrder 

                                where qualityOrder.OrderStatus == InventTestOrderStatus::Open

                            {

                                args.record(qualityOrder) ;

                                InventQualityOrderValidate  validate =    InventQualityOrderValidate::newQualityOrderTable(qualityOrder);

                                validate.run();

                            }

                        

                        }

                        catch 

                        {

                            continue;

                        }

                    }

                }

            }

        }

    }


    /// <summary>

    ///  deleteFile meathod

    /// </summary>

    /// <param name = "_fileName">Filename</param>

    /// <param name = "_deleteLocation">str</param>

    public void deleteFile(Filename  _fileName, str _deleteLocation = 'Outbound')

    {

        str    connectStr = strFmt('DefaultEndpointsProtocol=https;AccountName=%1;AccountKey=%2;EndpointSuffix=core.windows.net', inventParameters.MKStorageAccountName, inventParameters.MKAccountKeyValue);

        str    deleteLocationPath = _deleteLocation;     

        str exceptionMessage;


        try

        {

            CloudStorageAccount     storageAccount  = CloudStorageAccount::Parse(connectStr);

            CloudFileClient         fileClient      = storageAccount.CreateCloudFileClient();

 

            //Processed is a folder that I have in my Azure file share

            CloudFileShare          fileShare;

            CloudFile               tempCloudFile;

            

 

            //Folder in Azure file share

            fileShare = fileClient.GetShareReference(inventParameters.MKFileShare);

            if (fileShare.Exists(null, null) == false)

            {

                throw error (strFmt("@BAE:Filesharewasnotfound", inventParameters.MKFileShare));

            }

            

            if (fileShare.Exists(null, null))

            {

                // Get a reference to the root directory for the share.

                CloudFileDirectory      rootDirectory   = fileShare.GetRootDirectoryReference();

                  

                // Get a reference to the directory we created previously.

                CloudFileDirectory      sampleDir = rootDirectory.GetDirectoryReference(deleteLocationPath);

 

                tempCloudFile = sampleDir.GetFileReference(_fileName);

                if (tempCloudFile.Exists(null, null) == true)

                {

                    tempCloudFile.Delete(null, null, null);

                }

            }

        }

        catch (Exception::CLRError)

        {

            System.Exception interopException = CLRInterop::getLastException();

 

            if (interopException != null)

            {

                if (interopException is System.Reflection.TargetInvocationException)

                {

                    System.Exception innerException = interopException.InnerException;

                    if (innerException != null)

                    {

                        exceptionMessage = innerException.Message;

                    }

                }

                else

                {

                    exceptionMessage = interopException.Message;

                }

                throw error (exceptionMessage);

            }

        }

    }


     /// <summary>

    ///  writeFile

     /// </summary>

    /// <param name = "_fileName">Filename</param>

    /// <param name = "_writeLocation">str</param>

    /// <param name = "_stream">System.IO.Stream</param>

    /// <param name = "overwrite">boolean</param>

    public void writeFile(Filename  _fileName = 'dummyTestFile', str _writeLocation = 'Outbound', System.IO.Stream _stream = null,boolean overwrite=false)

    {

        str     connectStr = strFmt('DefaultEndpointsProtocol=https;AccountName=%1;AccountKey=%2;EndpointSuffix=core.windows.net', inventParameters.MKStorageAccountName, inventParameters.MKAccountKeyValue);

        str     writeLocationPath = _writeLocation;

        ;

        System.IO.Stream         memoryStream;

        str exceptionMessage;

        try

        {

            CloudStorageAccount     storageAccount  = CloudStorageAccount::Parse(connectStr);

            CloudFileClient         fileClient      = storageAccount.CreateCloudFileClient();

 

            //Processed is a folder that I have in my Azure file share

            CloudFileShare          fileShare;

            CloudFile               tempCloudFile;

            

            if (_stream)

            {

                memoryStream = _stream;

            }


            fileShare = fileClient.GetShareReference(inventParameters.MKFileShare);

            if (fileShare.Exists(null, null) == false)

            {

                throw error (strFmt("@MK:Filesharewasnotfound", inventParameters.MKFileShare));

            }


            if (fileShare.Exists(null, null))

            {

                // Get a reference to the root directory for the share.

                CloudFileDirectory      rootDirectory   = fileShare.GetRootDirectoryReference();

                // Get a reference to the directory we created previously.

                CloudFileDirectory      sampleDir = rootDirectory.GetDirectoryReference(writeLocationPath);

 

                //tempCloudFile = sampleDir.GetFileReference(strFmt("%1_%2.txt", _fileName, timeNow()));

                tempCloudFile = sampleDir.GetFileReference(strFmt('%1', _fileName));

                if(overwrite)

                {

                    memoryStream.Position = 0;  // have to reset position, else you will get an empty file

                    tempCloudFile.UploadFromStream(memoryStream, null, null, null);

                }

                else

                {

                    if (tempCloudFile.Exists(null, null) == false)

                    {

                        memoryStream.Position = 0;  // have to reset position, else you will get an empty file

                        tempCloudFile.UploadFromStream(memoryStream, null, null, null);

                    }

                }

                

                if (tempCloudFile.Exists(null, null) == false)

                {

                    memoryStream.Position = 0;  // have to reset position, else you will get an empty file

                    tempCloudFile.UploadFromStream(memoryStream, null, null, null);

                }

                memoryStream.Flush();

                memoryStream = null;

            }

        }

        catch (Exception::CLRError)

        {

            System.Exception interopException = CLRInterop::getLastException();

 

            if (interopException != null)

            {

                if (interopException is System.Reflection.TargetInvocationException)

                {

                    System.Exception innerException = interopException.InnerException;

                    if (innerException != null)

                    {

                        exceptionMessage = innerException.Message;

                    }

                }

                else

                {

                    exceptionMessage = interopException.Message;

                }

                throw error (exceptionMessage);

            }

        }

    }


     /// <summary>

    /// DMFDefinitionGroupEntity

     /// </summary>

    /// <param name = "_uploadedStatement">SharedServiceUnitFileID</param>

    /// <param name = "definitionGroup">DMFDefinitionGroup</param>

    /// <returns>filePath</returns>

    public DMFLocalFilePath applyTransforms(SharedServiceUnitFileID _uploadedStatement, DMFDefinitionGroup definitionGroup)

    {

        DMFDefinitionGroupEntity    definitionGroupEntity =  DMFDefinitionGroupEntity::find(inventParameters.MKLineDefinationGroupName, 'Quality order line results');// //this.findDMFDefinitionGroupEntity(definitionGroup);

        DMFExecutionId              executionId = DMFUtil::setupNewExecution(definitionGroup.DefinitionGroupName);


        DMFDefinitionGroupExecution execution = DMFDefinitionGroupExecution::find(

            definitionGroup.DefinitionGroupName,

            definitionGroupEntity.Entity,

            executionId,

            true);


        execution.IsTransformed = NoYes::No;

        DMFLocalFilePath filePath = execution.applyTransforms(_uploadedStatement);


        DMFExecution e = DMFExecution::find(executionId, true);

        e.delete();


        return filePath;

    }


     /// <summary>

    ///  importFromBlob meathod

     /// </summary>

    /// <param name = "_memory">System.IO.Stream</param>

    /// <param name = "_fileName">str</param>

    public void importFromBlob(System.IO.Stream _memory, str _fileName)

    {

        SharedServiceUnitFileID fileId;

        DMFDefinitionGroup definitionGroup;

        DMFDefinitionGroupEntity definitionGroupEntity;

        DMFExecutionId executionId;

        DMFDefinitionGroupExecution execution;


        // Should be used to get file into FileUploadTemporaryStorageResult object

        FileUploadTemporaryStorageResult result

            = File::SendFileToTempStore_GetResult(_memory, _fileName);


        if (result && result.getUploadStatus())

        {

            fileId = result.getFileId();

            definitionGroup = this.findDefinitionGroup(_fileName);

            this.applyTransforms(fileId, definitionGroup);


            definitionGroupEntity = DMFDefinitionGroupEntity::find(inventParameters.BAELineDefinationGroupName, 'Quality order line results');//  this.findDMFDefinitionGroupEntity(definitionGroup);

            executionId = DMFUtil::setupNewExecution(definitionGroup.DefinitionGroupName);


            // Find execution

            execution = DMFDefinitionGroupExecution::find(

                definitionGroup.DefinitionGroupName,

                definitionGroupEntity.Entity,

                executionId,

                true

            );


            execution.FilePath = fileId;

            execution.IsTransformed = NoYes::Yes;

            execution.IsSelected = NoYes::Yes;

            execution.ExecuteTargetStep = NoYes::Yes;

            execution.update();


            setPrefix(strFmt("@SYS73667", _fileName));


            // Import the file via quick import DMF

            DMFQuickImportExport::doPGImport(definitionGroup.DefinitionGroupName, executionId, true);


            // Deletes file

            result.deleteResult();

        }

    }


     /// <summary>

    /// DMFDefinitionGroup

     /// </summary>

    /// <param name = "_fileName">Filename</param>

    /// <returns>definitionGroup</returns>

    public DMFDefinitionGroup findDefinitionGroup(Filename _fileName)

    {

        DMFDefinitionGroup definitionGroup;

        

        select firstonly definitionGroup

             where definitionGroup.DefinitionGroupName ==  inventParameters.BAELineDefinationGroupName;

     

        return definitionGroup;

    }


}



Export:

/// <summary>

///  MKQualityOrdersLineExportService

/// </summary>

using Microsoft.Azure;

using Microsoft.WindowsAzure.Storage;

using Microsoft.WindowsAzure.Storage.Blob;

using Microsoft.WindowsAzure.Storage.File;

using System.Net;

 /// <summary>

///  MKQualityOrdersLineExportService

 /// </summary>


class MKQualityOrdersLineExportService             extends SysOperationServiceBase

{

    #File

    #define.delimiterField(',')

    InventParameters    inventParameters;

    /// <summary>

    /// writeFile meathod

    /// </summary>

    /// <param name = "_fileName">Filename</param>

    /// <param name = "_writeLocation">str</param>

    /// <param name = "_stream">System.IO.Stream</param>

    /// <param name = "overwrite">boolean</param>

    public void writeFile(Filename  _fileName = 'dummyTestFile', str _writeLocation = 'Outbound', System.IO.Stream _stream = null,boolean overwrite=false)

    {

        str     connectStr = strFmt('DefaultEndpointsProtocol=https;AccountName=%1;AccountKey=%2;EndpointSuffix=core.windows.net',

                            inventParameters.MKStorageAccountName, inventParameters.MKAccountKeyValue);

        str     writeLocationPath = _writeLocation;   

        ;

        System.IO.Stream         memoryStream;

        str exceptionMessage;

        try

        {

            CloudStorageAccount     storageAccount  = CloudStorageAccount::Parse(connectStr);

            CloudFileClient         fileClient      = storageAccount.CreateCloudFileClient();

 

            //Processed is a folder that I have in my Azure file share

            CloudFileShare          fileShare;

            CloudFile               tempCloudFile;

            

            if (_stream)

            {

                memoryStream = _stream;

            }


            fileShare = fileClient.GetShareReference(inventParameters.MKFileShare);

            if (fileShare.Exists(null, null) == false)

            {

                throw error (strFmt("@MK:Filesharewasnotfound", inventParameters.MKFileShare));

            }


            if (fileShare.Exists(null, null))

            {

                // Get a reference to the root directory for the share.

                CloudFileDirectory      rootDirectory   = fileShare.GetRootDirectoryReference();

                // Get a reference to the directory we created previously.

                CloudFileDirectory      sampleDir = rootDirectory.GetDirectoryReference(writeLocationPath);

 

                //tempCloudFile = sampleDir.GetFileReference(strFmt("%1_%2.txt", _fileName, timeNow()));

                tempCloudFile = sampleDir.GetFileReference(strFmt('%1', _fileName));

                if(overwrite)

                {

                    memoryStream.Position = 0;  // have to reset position, else you will get an empty file

                    tempCloudFile.UploadFromStream(memoryStream, null, null, null);

                }

                else

                {

                    if (tempCloudFile.Exists(null, null) == false)

                    {

                        memoryStream.Position = 0;  // have to reset position, else you will get an empty file

                        tempCloudFile.UploadFromStream(memoryStream, null, null, null);

                    }

                }

                

                if (tempCloudFile.Exists(null, null) == false)

                {

                    memoryStream.Position = 0;  // have to reset position, else you will get an empty file

                    tempCloudFile.UploadFromStream(memoryStream, null, null, null);

                }

                memoryStream.Flush();

                memoryStream = null;

            }

        }

        catch (Exception::CLRError)

        {

            System.Exception interopException = CLRInterop::getLastException();

 

            if (interopException != null)

            {

                if (interopException is System.Reflection.TargetInvocationException)

                {

                    System.Exception innerException = interopException.InnerException;

                    if (innerException != null)

                    {

                        exceptionMessage = innerException.Message;

                    }

                }

                else

                {

                    exceptionMessage = interopException.Message;

                }

                throw error (exceptionMessage);

            }

        }

    }


     /// <summary>

    /// processMultipleFiles meathod

     /// </summary>

    /// <param name = "_contract">MKQualityOrdersLineExportContract</param>

    public void processMultipleFiles(MKQualityOrdersLineExportContract _contract)

    {

        #DMF

        SharedServiceUnitFileID fileId;

        inventParameters = inventParameters::find();

        DMFDefinitionGroupName definitionGroupName = InventParameters.MKExportLineDefinitionGroupName;  //"lineexport";

       

        try

        {

            EntityName           entityName = DMFEntity::findFirstByTableId(tableNum(InventQualityOrderLineEntity)).EntityName;

            // Start:Optional if you want to filter data while exporting

            Query                query = new Query(DMFUtil::getDefaultQueryForEntityV3(entityName));

            QueryBuildDataSource qbds = query.dataSourceTable(tableNum(InventQualityOrderLineEntity));


            // End

            DMFEntityExporter exporter = new DMFEntityExporter();

            fileId = exporter.exportToFile(entityName,

                                    definitionGroupName,

                                    '', //Optional: ExecutionID

                                    'CSV', //Optional::SourceName

                                    #FieldGroupName_AllFields, //Optional field selection

                                    query.pack(), //Optional: Filtered Query

                                    curExt() //Optional: DataAReaId

                                    );

            if (fileId != '')

            {

                str downloadUrl = DMFDataPopulation::getAzureBlobReadUrl(str2Guid(fileId));

                System.Uri uri = new System.Uri(downloadUrl);

                str fileExt;

                if (uri != null)

                {

                    fileExt = System.IO.Path::GetExtension(uri.LocalPath);

                }

                Filename filename = strFmt('QualityOrderLineExport%1',fileExt);

                System.IO.Stream stream = File::UseFileFromURL(downloadUrl);

                File::SendFileToUser(stream, filename);


                DMFDefinitionGroupExecution definitionGroupExecution;


                select firstonly TargetStatus from definitionGroupExecution

                                order by RecId desc

                                where definitionGroupExecution.DEFINITIONGROUP == inventParameters.MKExportLineDefinitionGroupName;

                //upload

                if(definitionGroupExecution.TargetStatus == DMFBatchJobStatus::Finished)

                {

                    this.writeFile(filename, inventParameters.MKExportLineDestinationPath, stream);

                }

                else

                {

                    this.writeFile(filename, inventParameters.MKExportLineErrorPath, stream);

                }

                         

                

            }

            else

            {

                throw error("@MK:DMFexecutionfailedanddetailswerewrittentotheexecutionlog");

            }


        }

        catch

        {

            error("@MK:Erroroccurredwhileexporting");

        }

    }


}



Wednesday, 7 February 2024

Update NuGet package to new MS D365FO version

1. Import the NuGet package files from LCS for that particular version please take the PU version files only.

a. Goto LCS-->Asset Library-->NuGet Packages-->Import

b. Pick the 4 PU files 

      






2.  In DevOps Goto Artifacts and click on Connect to Feed


3. Click on NuGet.exe and Get the tool and download the exe file to C:\Temp folder










Click on NuGet.exe and Get the tool and download the exe file to C:\Temp folder

Edit the below two files and change the path according to and run in power shell as administrator
---
1. AddSource

C:\temp\nuget.exe sources Add -Name "NuGet.exe Project setup Key =" -Source "https:NuGet.exe Project setup Key value =/index.json" -Username "mallikarjun" -password "DevOps Token"

-----
2. Script2Push

$Token = "DevOps token"
$FeedURL = "https:NuGet.exe Project setup Key value =/index.json"
$Username = "mallikarjun"

$LocalFileApplicationSuiteBuild  = "C:\_Col\NuGetPackages\Microsoft.Dynamics.AX.ApplicationSuite.DevALM.BuildXpp.nupkg"
$LocalFileApplicationBuild  = "C:\_Col\NuGetPackages\Microsoft.Dynamics.AX.Application.DevALM.BuildXpp.nupkg"
$LocalFilePlatformBuild  = "C:\_Col\NuGetPackages\Microsoft.Dynamics.AX.Platform.DevALM.BuildXpp.nupkg"
$LocalFileCompilerTools  = "C:\_Col\NuGetPackages\Microsoft.Dynamics.AX.Platform.CompilerPackage.nupkg"

C:\temp\nuget.exe sources push -Name "NuGet.exe Project setup Key =" -Source $FeedUrl -UserName "mallikarjun" -password $Token

C:\temp\nuget.exe push -Source "Baettr" -ApiKey Az $LocalFileApplicationSuiteBuild -Timeout 3000                                    
C:\temp\nuget.exe push -Source "Baettr" -ApiKey Az $LocalFileApplicationBuild -Timeout 3000  
C:\temp\nuget.exe push -Source "Baettr" -ApiKey Az $LocalFilePlatformBuild -Timeout 3000                                   
C:\temp\nuget.exe push -Source "Baettr" -ApiKey Az $LocalFileCompilerTools -Timeout 3000  


5. After running the above commands, it will update the  Artifacts version. Copy the version and update in TFS 



 






Wednesday, 3 January 2024

A Guide to Finding Table Field Types Enum in D365FO Using X++

 Certainly! Enum fields in Dynamics 365 Finance and Operations (D365FO) are treated as Int64 data types. When comparing enum values, you need to use the enum2int function to convert the enum value to its corresponding integer representation. Let's extend the previous code to demonstrate how to compare enum values using the DictField.baseType() method:


static void FindFieldTypeAndCompareEnum(Args _args) { DictTable dictTable; DictField dictField; int fieldType; // Table name str tableName = 'CustTable'; // Field name str fieldName = 'CustClassification'; // Get the table dictionary dictTable = new DictTable(tableName2id(tableName)); if (dictTable) { // Get the field dictionary dictField = dictTable.fieldName2Id(fieldName); if (dictField) { // Get the field type fieldType = dictField.baseType(); // Display the field type info(strFmt("Field Type of %1.%2: %3", tableName, fieldName, fieldType)); // Check if the field is an Enum if (fieldType == Types::Enum) { CustClassification custClassification = CustClassification::High; // Convert the enum value to integer using enum2int int enumIntValue = enum2int(custClassification); // Retrieve the field's enum type str enumTypeName = dictField.typeName(); // Display the enum comparison result info(strFmt("Comparing %1.%2 with %3: %4", tableName, fieldName, custClassification, enumIntValue == enum2int(enumTypeName, dictField.enumType()))); } else { warning(strFmt("Field '%1' is not an Enum in table '%2'", fieldName, tableName)); } } else { warning(strFmt("Field '%1' not found in table '%2'", fieldName, tableName)); } } else { warning(strFmt("Table '%1' not found", tableName)); } }

Explanation:

  1. enum2int: The enum2int function is used to convert an enum value to its corresponding integer representation.

  2. typeName: The typeName method of DictField returns the type name of the field, which is useful for obtaining information about the enum type.

  3. enum2int(enumTypeName, enumValue): This is used to convert an enum value to its integer representation based on the enum type name.

  4. Comparing Enum Values: The code checks if the field type is an Enum and then compares an enum value (e.g., CustClassification::High) with the stored value in the table.

Switch (dictField.baseType())

{

Case Type::Enum :

dictEnum = new DictEnum(dictField.enumID());

for (i = 0; i < dictEnum.values(); i++)

{

info (strfmt(“Enum value :  %1“,dictEnum.value2Name(i))); 

}

}

Usage:

  1. Copy the extended code into the X++ editor in the Dynamics 365 Finance and Operations development environment.

  2. Replace the values of tableName and fieldName with the desired table and enum field names.

  3. Run the job, and the output will display the field type and the result of comparing the enum value with the stored value in the table.

This extended code provides a way to determine if a field is of enum type and how to compare enum values using the DictField.baseType() method in D365FO X++.

A Guide to Finding Table Field Types in D365FO Using X++

Introduction:


In Microsoft Dynamics 365 Finance and Operations (D365FO), developers often need to determine the field types of various table fields for customization or reporting purposes. In this blog post, we'll explore how to find the table field type using X++ code.


Code Snippet:


Let's start with a simple X++ code snippet that you can use to find the field type of a specific field in a table. In this example, we'll use the CustTable table and the AccountNum field:


static void FindFieldTypeExample(Args _args)

{

    DictTable dictTable;

    DictField dictField;

    int fieldType;


    // Table name

    str tableName = 'CustTable';

    

    // Field name

    str fieldName = 'AccountNum';


    // Get the table dictionary

    dictTable = new DictTable(tableName2id(tableName));


    if (dictTable)

    {

        // Get the field dictionary

        dictField = dictTable.fieldName2Id(fieldName);


        if (dictField)

        {

            // Get the field type

            fieldType = dictField.baseType();


            // Display the field type

            info(strFmt("Field Type of %1.%2: %3", tableName, fieldName, fieldType));

        }

        else

        {

            warning(strFmt("Field '%1' not found in table '%2'", fieldName, tableName));

        }

    }

    else

    {

        warning(strFmt("Table '%1' not found", tableName));

    }

}



Explanation:

  • DictTable and DictField classes: These classes are part of the X++ dictionary framework and are used to access metadata information about tables and fields.
  • tableName2id: This method converts the table name to its corresponding ID, which is used to create a DictTable instance.
  • fieldName2Id: This method is used to get the field dictionary based on the field name.
  • baseType: The baseType method of DictField returns the base data type of the field.
  • info and warning: These are methods used to display messages to the user. info is used for general information messages, while warning is used for non-fatal issues.

Usage:

  • Copy the provided code into the X++ editor in the Dynamics 365 Finance and Operations development environment.
  • Replace the values of tableName and fieldName with the desired table and field names.
  • Run the job, and the output will display the field type of the specified field in the specified table.

Conclusion:

With this X++ code snippet, you can easily find the field type of any table field in Dynamics 365 Finance and Operations. This information is valuable for developers working on customizations, integrations, or reporting solutions within the D365FO environment. Feel free to customize the code further based on your specific requirements.

Wednesday, 18 October 2023

Making API POST Requests with JSON and Authentication Token using C# Class Library as reference in D365FO

 In today's interconnected world, integrating with external APIs is a common requirement for many software projects. Whether you're building a web application, mobile app, or any other software that needs to communicate with external services, understanding how to make API POST requests with JSON data and authentication tokens in a C# class library can be incredibly valuable. In this blog post, we'll walk you through the process of creating a C# class library that can be used as a reference for making such requests.

Step 1: Create a C# Class Library Project

To create a C# class library project in Visual Studio that compiles into a .dll (Dynamic-Link Library) file, you can follow these steps:

1. Open Visual Studio:

If you don't have Visual Studio installed, you can download the Community edition for free from the official Microsoft website.

2. Create a New Project:

Go to "File" > "New" > "Project..."

In the "Create a new project" window, search for "Class Library" or "Class Library (.NET Core)" depending on the version of .NET you want to target.

Select the appropriate project template and click "Next."

Add the below C# code:

-----------------------------------------------------------------------------------------------------------------------

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

using System.Net.Http;

using System.Net.Http.Headers;


namespace CSharpAPIClassLibrary

{

    public class AXApiService

    {

        private readonly HttpClient httpClient;

        public AXApiService(string baseUri)

        {

            httpClient = new HttpClient { BaseAddress = new Uri(baseUri) };

        }

        public async Task<string> PostDataAsync(string endpoint, string jsonContent, string authToken)

        {

            try

            {

                httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authToken);

                httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

                var content = new StringContent(jsonContent, Encoding.UTF8, "application/json");

                HttpResponseMessage response = await httpClient.PostAsync(endpoint, content);

                if (response.IsSuccessStatusCode)

                {

                    return await response.Content.ReadAsStringAsync();

                }

                throw new Exception($"API request failed with status code {response.StatusCode}");

            }

            catch (Exception ex)

            {

                throw new Exception($"API request failed: {ex.Message}");

            }

        }

    }

}

-------------------------------------------------------------------------------------------------------------------------

3. Build the Project:

        a)Build your class library project by clicking "Build" > "Build Solution" or using the keyboard shortcut (usually F6).
        b)This action will compile your code into a .dll file.

4. Locate the .dll File:

        a)The compiled .dll file can be found in the "bin" folder of your project. By default, it's in a subfolder named after your target framework (e.g., "bin\Debug\netstandard2.0" for a .NET Standard 2.0 project).
Your C# class library is now compiled into a .dll file and is ready to be used in other projects or applications. You can reference this .dll in other C# projects, such as console applications, Windows Forms applications, ASP.NET applications, or any other .NET application type.

Note: 

This ApiClient class allows you to set the base URL, make POST requests with JSON payloads, and include an authentication token in the headers.

Step 2: Use the API Client in Your Application

    a)To use this library in an Application Object Tree (AOT) project, add a reference to your class library by right-clicking on the AOT project, selecting "Add Reference," and then browsing for your class library's DLL file in the bin/Debug (or bin/Release) folder of your class library project.

    b)In your AOT project, you can now use the ApiService class to make API calls with the desired content type and authorization token.

Here's an example of how you can use this class in your AOT project:

using CSharpAPIClassLibrary;
internal final class CallCshrpLibAPIPostEmp
{
    /// <summary>
    /// Class entry point. The system will call this method when a designated menu 
    /// is selected or when execution starts and this class is set as the startup class.
    /// </summary>
    /// <param name = "_args">The specified arguments.</param>
    public static void main(Args _args)
    {
        var axApiService = new AXApiService("https://api.example.com"); // Base URL
        str endpoint = "/api/resource"; //endPoint POST URL       
        str jsonContent = "{ \"key\": \"value\" }";
        str authToken = = "your-auth-token";

        try
        {
            str response = axApiService.PostDataAsync(endpoint, jsonContent, authToken).Result;
            info("API response: " + response);
        }
        catch (Exception::Error)
        {
            // Handle exceptions that may occur during the request
            error("An error occurred during the HTTP request.");
        }
    }

}


You've now created a C# class library that can be used as a reference for making API POST requests with JSON data and authentication tokens. This library can be reused in various C# applications, making it easier to integrate with external APIs securely and efficiently.

Remember to handle errors and edge cases according to your application's specific requirements and consult the API's documentation for any additional details. Happy coding! ---Mallik

Tuesday, 17 October 2023

Calling APIs from D365 Using Content Type Text "application/x-www-form-urlencoded" POST request

 

In the world of enterprise resource planning (ERP), Microsoft Dynamics 365 (D365) stands out as a powerful and versatile solution. To unlock the full potential of D365, you often need to integrate it with other systems or automate certain processes. One common way to achieve this is by calling D365 APIs. In this blog post, we'll walk you through the process of calling D365 APIs using the "application/x-www-form-urlencoded" content type.

Understanding the "application/x-www-form-urlencoded" Content Type

Before we dive into calling D365 APIs, let's first understand what the "application/x-www-form-urlencoded" content type is. This content type is commonly used for sending data to web servers, especially when dealing with HTML forms. It represents key-value pairs in the URL-encoded format, where data is sent as a series of name-value pairs separated by the '&' character.

Prerequisites

To start calling D365 APIs, you'll need the following prerequisites:

1. Microsoft Dynamics 365 Account: You should have access to a D365 instance and the required permissions to call APIs.

2. API Endpoint: You should know the API endpoint you want to call. Consult D365's API documentation to find the specific endpoint URL.

3. API Key or Authentication Token: Depending on your D365 instance's security settings, you might need an API key or an authentication token.

Calling D365 APIs with "application/x-www-form-urlencoded" Content Type

Now, let's walk through the steps to call a D365 API using the "application/x-www-form-urlencoded" content type:

Step 1: Assemble Your Data

Prepare the data you want to send in the request in the "application/x-www-form-urlencoded" format. This means you need to create a string that contains key-value pairs separated by '&' and encode them properly.

Step 2: Set Up Your HTTP Request below is the sample code 

using Newtonsoft.Json.Linq;

using Newtonsoft.Json.JsonSerializer;

using System.Text.Json;

using System.IO.FileAccess;

using System.IO.Directory;

using System.IO;

using Newtonsoft.Json.Linq.JObject;

using System.Net.Http.HttpClient;

using System.Net.Http.HttpRequestMessage ;

using System.Net.Http.HttpResponseMessage;

internal final class AuthenticationRunnableClass

{

    /// <summary>

    /// Class entry point. The system will call this method when a designated menu 

    /// is selected or when execution starts and this class is set as the startup class.

    /// </summary>

    /// <param name = "_args">The specified arguments.</param>

    public static void main(Args _args)

    {

        System.Net.Http.HttpClient clientlocal;

        System.Net.Http.HttpRequestMessage requestlocal;

        System.Net.Http.HttpResponseMessage responselocal;

        System.Text.Encoding encoding;

        try

        {

            // Create an instance of the HttpClient

            clientlocal = new System.Net.Http.HttpClient();

            // Define the request URL

            str requestUrl = 'https://your-d365-api-endpoint.com';

    // Create the content in application/x-www-form-urlencoded format

            str formData = 'client_id=xx-xxxx&client_secret=xxxxx&grant_type=client_credentials&scope=alf.scope.integration.api&acr_values=tenant:xxxx'; // Replace with your form data //This is the text which is used to send with URL

            encoding = System.Text.Encoding::UTF8;

            // Create the content as StringContent

            System.Net.Http.StringContent content = new System.Net.Http.StringContent(formData, encoding, 'application/x-www-form-urlencoded');

            // Send the POST request with the form data

            responselocal = clientlocal.postAsync(requestUrl, content).Result;

            // Handle the response as needed

            int statusCode = any2Int(responselocal.get_StatusCode());

            str responseContent = responselocal.get_Content().readAsStringAsync().Result;

            // Handle the response data and potential errors

            if (statusCode == 200)

            {

                // Request was successful; process the response content

                info(responseContent);

            }

            else

            {

                // Handle the response based on the status code (e.g., log an error)

                error("HTTP request failed with status code: " + int2str(statusCode));

            }

        }

        catch (Exception::Error)

        {

            // Handle exceptions that may occur during the request

            error("An error occurred during the HTTP request.");

        }

    }

}

Step 3: Handle the Response

Once you send the request, you can handle the response from the D365 API. Be sure to check the status code and parse the response data accordingly. The code below show how to handle the response 

// Handle the response data and potential errors

            if (statusCode == 200)

            {

                // Request was successful; process the response content

                info(responseContent);

            }

            else

            {

                // Handle the response based on the status code (e.g., log an error)

                error("HTTP request failed with status code: " + int2str(statusCode));

            }


Conclusion

Calling D365 APIs using the "application/x-www-form-urlencoded" content type is a common way to integrate D365 with other systems or automate processes. Understanding the format and following the steps outlined in this blog post can help you make successful API requests and leverage the full power of Microsoft Dynamics 365. Make sure to refer to D365's API documentation for the specific endpoints and data you need to interact with in your integration projects.






How to Get Trade Agreement Line Discount Percentage by Item and Date in D365FO Using X++

  In Microsoft Dynamics 365 Finance and Operations (D365FO), trade agreements are used to manage prices and discounts for customers, vendor...