Friday, 28 April 2023

Use temporary Blob storage for accessing files during runtime

We can learn about how to store a file in the temporary blob storage, so that the file can be used during runtime process for our scenarios. I have a scenario, where I need to get the company image and use it in my process.

In order to do this, we can get the image file from DB and upload it to the temporary Blob storage. Further we can get the URL for the uploaded file and that can be used to send or download the image as required in our process.

Below code sample help us for the scenario.

Image                             logoImage;
Binary                             binary;
System.IO.Stream        imageStream;
str                                   downloadUrl;
       
logoImage = new Image();
logoImage.setData(CompanyImage::findByRecord(CompanyInfo::find()).Image);
 
binary = Binary::constructFromContainer(logoImage.getData());
 
imageStream = binary.getMemoryStream();
 
if (imageStream)
{
    str fileName        = 'Your file name'; //Fixed name or GUID can be created for dynamic name
    str contentType  = 'image/png'; // Change according to your file type
    str fileExtension = enum2Str(ImageType::PNG); // Change according to your file type
 
    FileUploadTemporaryStorageStrategy fileUploadStrategy = new FileUploadTemporaryStorageStrategy();
    FileUploadTemporaryStorageResult fileUploadResult = fileUploadStrategy.uploadFile(imageStream, fileName + fileExtension, contentType, fileExtension);
 
    if (fileUploadResult == null || !fileUploadResult.getUploadStatus())
    {
        warning("@ApplicationPlatform:FileUploadFailed");
    }
    else
    {
        downloadUrl = fileUploadResult.getDownloadUrl();
        if (downloadUrl == "")
        {
            throw Exception::Error;
        }
    }
}

The downloadUrl hold the link for the temporarily stored file path. We can use this in our next process as required.





Tuesday, 25 April 2023

Using data files in Postman for Recursive or Repeated triggers

You can use data files to pass Postman sets of values to use in a collection run. By selecting a JSON or CSV data file in the Collection Runner, you can test your requests with multiple values as part of a single run.

Running collections with data files

·      Select Home -->File -->New Runner Tab













 ·      Now you can "drag and drop" your requests from Collection and than keep checked only request you would like to generate by a Runner setting 10 iterations (to generate 10 requests ) and delay for example to 0 (to make it as fast as possible). The requests are executed in the order it is included in the runner collection.



·       Make your file with sample data that needs to be triggered through postman

·       Below is a sample Json format File
























·      The above screenshot shows that, the file contains two sets of data that will be triggered for the each request selected.

·      Now since there are multiple triggers made to the request, the values present on the body of the request should be dynamic. So the body of the request has to be modified to get the value from the Json file.

·      Specify the variable name same as that of provided in the Json file inside {{}} in the body. Below is an example.











·      Select your data file with the Select File button.








 





·      Iterations is defaulted to 2, since we had two different sets of data in the Json file. These two sets of data will be used to trigger all the request marked in the runnable collection used.

·      Click on the button Run Trigger using Data files.

·      We can see the summary and results using the View summary / View results Tab.








 










·      Test scripts can be used in the Test area of the request if required for any specific scenarios(Optional)



 







More details can be found in Using data files in Postman

Friday, 21 April 2023

Get the List of fields and methods available on table through x++

We might get some scenarios to get the list of objects or a list of elements available in a particular object in X++. Here is a sample piece of code to retrieve the details about the table object.

Whenever we try to get the metadata of an object (Tables, Table fields, table methods) we should keep in mind to use the below namespace in our code

using Microsoft.Dynamics.AX.Metadata.MetaModel;

To get list of tables

using Microsoft.Dynamics.AX.Metadata.MetaModel;
public class TableNamesList
{
    public static void getTableNames()
    {
       var tables = Microsoft.Dynamics.Ax.Xpp.MetadataSupport::GetAllTables();
        var tablesEnumr = tables.GetEnumerator();
        while (tablesEnumr.MoveNext())
        {
            info(strfmt("%1", axTable.Name));
        }
    }
}


To get list of Fields from a table

public static void getFieldNames()
    {
        AxTable table =     Microsoft.Dynamics.Ax.Xpp.MetadataSupport::GetTable(tableId2Name(tablenum(SalesTable)));
       var fields = table.Fields;
        var fieldEnumerator = fields.GetEnumerator();
        while (fieldEnumerator.MoveNext())
        {
            info(strfmt("%1", field.Name));
        }
    }


To get list of methods available on a table

public static void getMethodNames()
    {
        AxTable table = Microsoft.Dynamics.Ax.Xpp.MetadataSupport::GetTable(tableId2Name(tablenum(SalesTable)));
        var methods = table.Methods;
        var methodEnumerator = methods.GetEnumerator();
        while (methodEnumerator.MoveNext())
        {
            AxMethod method = methodEnumerator.Current;
            if(method.IsDisplay)
            {
                info(strfmt("Display method : %1", method.Name));
            }
            else
            {
                info(strfmt("Normal method : %1", method.Name));
            }
        }
    }

Similar to the above code, we can use the metadata .dll in X++ to get all the objects and its related details available in AOT 

Friday, 14 April 2023

Inventory Visibility feature in Dynamics 365 for Supply Chain Management

The Inventory Visibility Add-in for Dynamics 365 Supply Chain Management is an independent microservice that is highly scalable, empowering high-volume retailers and manufacturers to manage millions of inventory updates every minute and gain real-time visibility to cross-channel inventory positions. Read this blog to learn more about this new add-in and how it can help your company!

Get a global view of inventory

The integrated solution lets you define your own data sources and centralize inventory data.

There are two approaches to viewing your inventory:

  • Submit a query through the high-performance API. This API can return near-real-time inventory data directly from a cached instance.
  • View the raw on-hand list. This list is periodically synced from a cached instance and is visible in Dataverse.

Soft reservations

Soft reservation applies when a business must reserve a specific quantity of products to support, for example, sales order fulfillment that avoids over-selling. When a sales order is created and confirmed in Supply Chain Management or other order management systems, a request to reserve the quantity is sent to Inventory Visibility. Inventory Visibility lets you reserve products that have dimension details and specific inventory transaction types.  After the quantity is successfully reserved, a reservation ID is returned. You can use this reservation ID to link back to the original order in Supply Chain Management or other order management systems.

The functionality is designed so that a reservation in Inventory visibility doesn’t change the total quantity. Instead, it only flags the reserved quantity. (For this reason, it’s called a soft reservation.) The soft-reserved quantity can be offset when the products are consumed in Supply Chain Management or a third-party system by calling the API again to make a quantity deduction and update the total quantity in Inventory Visibility.

How to Install the Inventory Visibility Add-in

Before you install the add-in, register an application and add a client secret to Azure Active Directory (Azure AD) under your Azure subscription. Be sure to make a note of the Application (client) IDClient secret, and Tenant ID values, because you will need them later.

After you register an application and add a client secret to Azure AD, follow these steps to install the Inventory Visibility Add-in.

  1. Sign in to LCS.
  2. On the home page, select the project where your environment is deployed.
  3. On the project page, select the environment where you want to install the add-in.
  4. On the environment page, scroll down until you find the Environment add-ins section in the Power Platform integration section. There, you can find the Dataverse environment name. Confirm that the Dataverse environment name is the one that you want to use for Inventory Visibility.
  5. In the Environment add-ins section, select Install a new add-in.                                                                                                
  6. Select the Install a new add-in link. A list of available add-ins appears.
  7. In the list, select Inventory Visibility.
  8. Set the following fields for your environment:
    • AAD application (client) ID – Enter the Azure AD application ID that you created and made a note of earlier.
    • AAD tenant ID – Enter the tenant ID that you made a note of earlier.

  1. Agree to the terms and condition by selecting the Terms and conditions checkbox.
  2. Select Install. The status of the add-in is shown as Installing. When the installation is completed, refresh the page. The status should change to Installed.
  3. In Dataverse, select the Apps section in the left navigation, and verify that the Inventory Visibility Power Apps is installed successfully.

Monday, 10 April 2023

Exception handling in X++ - D365 FO

This article describes the exception handling in X++. we can handle errors by using the  below key statements or keywords in the X++ code.

  • throw
  • try
  • catch
  • finally
  • retry

throw statements

We use the throw keyword to throw an Exception. throw statement can be used to either to throw generic error message or a custom error message. For example, the following statement throws an error exception.

throw Exception::error;

Instead of throwing an enum value, the below example shows the throw with a custom error message. The best practice is to use the output of the Global::error method as the operand for throw.

throw error("Error thrown.");  //The Hard coded values can also be replaced with labels created.

try, catch, finally, and retry statements

When an exception is thrown, it's first processed through the catch list of the innermost try block. If a catch block is found that handles the kind of exception that is being thrown, program control jumps to that catch block. If the catch list has no block that specifies the exception, the system passes the exception to the catch list of the next-innermost try block. The catch statements are processed in the same sequence as they appear in the code.

It's a common practice to have the first catch statement handle the Exception::Error enum value. One strategy is to have the last catch statement leave the exception type unspecified. In this case, the last catch statement handles all exceptions that aren't handled by any earlier catch statement. This strategy is appropriate for the outermost try...catch blocks.

An optional finally clause can be included in try...catch statements. The semantics of a finally clause are the same as they are in C#. The statements in the finally clause are executed when control leaves the try block, either normally or through an exception.

The retry statement can be written only in a catch block. The retry statement causes control to jump up to the first line of code in the associated try block. The retry statement is used when the cause of the exception can be fixed by the code in the catch block. The retry statement gives the code in the try block another opportunity to succeed. The retry statement erases all messages that have been written to the Infolog since program control entered the try block.

try

{

    // Code here.

}

catch (Exception::Numeric)

{

    info("Caught a Numeric exception.");

}

catch

{

    info("Caught an exception.");

}

finally

{

    // Executed no matter how the try block exits.

}

Exceptions inside transactions

If an exception is thrown inside a transaction, the transaction is automatically canceled (that is, a ttsAbort operation occurs). This behavior applies for both exceptions that are thrown manually and exceptions that the system throws. When an exception is thrown inside a ttsBegin-ttsCommit transaction block, no catch statement inside that transaction block can process the exception, (unless it is a UpdateConflict or a DuplicateKeyException). Instead, the innermost catch statements that are outside the transaction block are the first catch statements that are tested.

The finally clause will be executed even in transaction scope.

Examples of exception handling

The following code example shows exceptions in the Infolog

 

static void TryCatchGlobalError2Job(Args _args)

{

    /***

    The 'Global::error()' does directly add a message to the Infolog.

    The exception is caught.

    ***/

    try

    {

        info("In the 'try' block. (j2)");

        throw Global::error("Written to the Infolog.");

    }

    catch (Exception::Error)

    {

        info("Caught 'Exception::Error'.");

    }

 

/***  Infolog output

Message (03:51:44 pm)

In the 'try' block. (j2)

Written to the Infolog.

Caught 'Exception::Error'.

***/

}

 

Throwing an exception inside a transaction

The following code example throws an exception in a transaction block.

static void TryCatchTransaction5Job(Args _args)

{

    /***

    Shows an exception that is thrown inside a ttsBegin - ttsCommit

    transaction block cannot be caught inside that block.

    ***/

    try

    {

        try

        {

            ttsbegin;

            try

            {

                throw error("Throwing exception inside transaction.");

            }

            catch (Exception::Error)

            {

                info("Catch_1: Unexpected, caught in 'catch' inside the transaction block.");

            }

            ttscommit;

        }

        catch (Exception::Error)

        {

            info("Catch_2: Expected, caught in the innermost 'catch' that is outside of the transaction block.");

        }

    }

    catch (Exception::Error)

    {

        info("Catch_3: Unexpected, caught in 'catch' far outside the transaction block.");

    }

    info("End of job.");

 

/**********  Actual Infolog output

Message (04:12:34 pm)

Throwing exception inside transaction.

Catch_2: Expected, caught in the innermost 'catch' that is outside of the transaction block.

End of job.

**********/

}