Wednesday, July 26, 2023

Code to get individual financial dimensions from default dimension in D365 X++

 

public static void main(Args _args)

   {

       DimensionAttributeValueSetStorage           dimStorage;

 

       dimStorage = DimensionAttributeValueSetStorage::find(68719498720);

       for (int i= 1 ; i<= dimStorage.elements() ; i++)

       {

           if(DimensionAttribute::find(dimStorage.getAttributeByIndex(i)).Name == "BusinessUnit")

           {

               info(strFmt("Business Unit %1",dimStorage.getDisplayValueByIndex(i)));

           }

           if(DimensionAttribute::find(dimStorage.getAttributeByIndex(i)).Name == "CostCenter")

           {

               info(strFmt("cost center %1",dimStorage.getDisplayValueByIndex(i)));

           }

           if(DimensionAttribute::find(dimStorage.getAttributeByIndex(i)).Name == "Department")

           {

               info(strFmt("Department %1",dimStorage.getDisplayValueByIndex(i)));

           }

           if(DimensionAttribute::find(dimStorage.getAttributeByIndex(i)).Name == "ItemGroup")

           {

               info(strFmt("Item Group %1",dimStorage.getDisplayValueByIndex(i)));

           }

           if(DimensionAttribute::find(dimStorage.getAttributeByIndex(i)).Name == "Project")

           {

               info(strFmt("project %1",dimStorage.getDisplayValueByIndex(i)));

           }

       }

   }

................

Other Way :

    public static void main(Args _args)

   {

       DefaultDimensionView defaultDimensinView;

       while select Name, DisplayValue from defaultDimensinView

           where defaultDimensinView.DefaultDimension == 52565465999

       {

           Info (defaultDimensinView.Name +" "+ defaultDimensinView.DisplayValue);

       }

   }

Output :








Code to get Validations in D365 X++

 public str captureInfoMessage()

    {

        SysInfologEnumerator    sysInfologEnumerator;

        SysInfologMessageStruct infoMessageStruct;

        ErrorMsg                logMessage;

        str                           logString;

        str                          ret;

        int                          i;

        #Define.NewLine('\n')

    sysInfologEnumerator = SysInfologEnumerator::newData(infolog.infologData());

        while (sysInfologEnumerator.moveNext())

        {

            i = 1;

            if (logMessage)

            {

                logMessage += #Newline;

            }

            infoMessageStruct = SysInfologMessageStruct::construct(sysInfologEnumerator.currentMessage());

            while (i <= infoMessageStruct.prefixDepth())

            {

                logString = logString + infoMessageStruct.preFixTextElement(i) + '. ';

                i++;

            }

            logString = logString + infoMessageStruct.message();

            logMessage = logMessage + infoMessageStruct.message();

        }

         ret = logMessage;

        return ret;

    }

How to create a Custom Business Event in D365 X++

 To create a custom business event, we would need following three artifacts.

·       BusinessEventsContract class

·       BusinessEventsBase class 

·       A Trigger Class to send the business Event

     


     BusinessEventContract Class :

    

[DataContract]

internal final class DaxCustomerBusinessEventContract extends BusinessEventsContract

{

   private CustAccount   custAccount;

   Private CustName      customerName;

   /// <summary>

   /// Initializes the field values.

   /// </summary>

   private void initialize(CustTable _custTable)

   {

       custAccount = _custTable.AccountNum;

   }

   /// <summary>

   /// Creates a <c>DaxCustomerBusinessEventContract</c> from a <c>CustTable</c> record.

   /// </summary>

   /// <param name = "_CustTable">A <c>CustTable</c> record.</param>

   /// <returns>A <c>CustomerCreatedBusinessEvent</c>.</returns>

   public static DaxCustomerBusinessEventContract newFromCustTable(CustTable _custTable)

   {

       var contract =  DaxCustomerBusinessEventContract::construct();

       contract.initialize(_custTable);

       contract.parmCustAccount(_custTable.AccountNum);

       contract.parmCustomerName(_custTable.name());

       return contract;

   }

   [DataMember('AccountNumber'), BusinessEventsDataMember("@Dev:AccountNumber")]

   public CustAccount parmCustAccount(CustAccount _custAccount = custAccount)

   {

       custAccount = _custAccount;

       return custAccount;

   }

   [DataMember(identifierStr(CustomerName)),

   BusinessEventsDataMember("Customer name")]

   public Name parmCustomerName(Name _customerName = customerName)

   {

       customerName = _customerName;

       return customerName;

   }

   private void new()

   {

   }

   public static DaxCustomerBusinessEventContract construct()

   {

       DaxCustomerBusinessEventContract retVal = new DaxCustomerBusinessEventContract();

       return retVal;

   }

}

BusinessEventsBase class :

[BusinessEvents(classStr(DaxCustomerBusinessEventContract),

"Dev:DaxCustomerCreatedEvent","Dev:DaxCustomerCreatedEventDescription",ModuleAxapta::Customer)]

public final class DaxCustomerBusinessEvent extends BusinessEventsBase

{

   private CustTable custTable;

   private CustTable parmCustTable(CustTable _custTable = custTable)

   {

       custTable = _custTable;

       return custTable;

   }

   private void new()

   {

       super();

   }

   public static DaxCustomerBusinessEvent construct()

   {

       DaxCustomerBusinessEvent retVal = new DaxCustomerBusinessEvent();

       return retVal;

   }

   [Wrappable(true), Replaceable(true)]

   public BusinessEventsContract buildContract()

   {

       return DaxCustomerBusinessEventContract::newFromCustTable(custTable);

   }

   static public DaxCustomerBusinessEvent newFromCustTable(CustTable _custTable)

   {

       DaxCustomerBusinessEvent businessEvent =  DaxCustomerBusinessEvent::construct();

       businessEvent.parmCustTable(_custTable);

       return businessEvent;

   } 

}

A Trigger Class to send the business Event :

[ExtensionOf(tableStr(CustTable))]

public final class DaxCustTable_Extension

{

   public void insert(DirPartyType _partyType, Name _name,boolean _updateCRM)

   {

       next insert(_partyType, _name, _updateCRM);

 

       if (BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(DaxCustomerBusinessEvent)))

       {

           DaxCustomerBusinessEvent::newFromCustTable(this).send();

       }

   }

}

Activate the custom Dynamics 365 FO business event

The Business Event catalog doesnt get automatically refreshed. To refresh the Business Event catalog, go to
System Administration -> Business Event Catalog -> Manage -> Rebuild business event catalog
Once rebuild is complete, the new business event would be added to the list
 .

To Activate the BE you need a endpoint. Before Activation create a end point.

System Administration >Setup>Business Event catalog>Endpoints


Activate the Business Event and assign the end point to the business event. Once it is activated, the business event should appear in the “Active events” tab.

Power Automate flow : 

·       Go to the Power Automate portal.

·       On the left pane click Create to start a new flow, select Automated cloud flow.

·       Name the flow and find the following trigger, click Create.



In the Instance setting provide a link to your Dynamics 365 F&O environment.

After the one is provided, Power Automate will connect to the environment via the link and gather available business event information. After the Category is selected, user may select the exact Business event with binding a company. In case a company is not set, the event is active for all companies.


Parse business event data from JSON :

 

Previous step makes customer data available in JSON format. To use it further it makes sense to parse it using "Parse JSON" step.

 

Click "New step" and find the appropriate step, select "body" in Content field (this is the customer information JSON string that we have received from Dynamics 365 F&O on the previous step).

 

Now we need to provide a JSON schema so the step is able to parse the data correctly. To generate a schema from sample, go to the System administration -> Setup -> Business event catalog form, find the created business event, go to details at the right of the form, click Download schema button.

 

The button will generate a JSON sample string for this particular business event. The string may be used for "Generate schema from sample" function while setting up Parse JSON step.




Send a message via Teams :

Finally, we were asked to send a message via Teams containing customer information.

Click New step to add "Post message in a chatbot or channel" step. Enter a channel or a user that should receive the message on new customer creation and save the flow.


Test Power Automate flow :

In Dynamics 365 F&O go to Accounts receivable -> Customers -> All customers and create a new customer. After the customer is created, the user or channel you specified at the Teams setup step should receive a message via Teams that looks like the following:



If we click the link, the system will open "All customers" form showing the newly created customer only.
























Export of vendor payment journal info message to JSON format in D365 X++

 My requirement is to export the vendor payment journal to JSON format through custom button.

And to generate the response in JSON file based on validations occured.

Custom button clicked Event Handler :

internal final class DaxLedgerJournalTable_EventHandler

{

   /// </summary>

   /// <param name="sender"></param>

   /// <param name="e"></param>

   [FormControlEventHandler(formControlStr(LedgerJournalTable, Json), FormControlEventType::Clicked)]

   public static void Json_OnClicked(FormControl sender, FormControlEventArgs e)

   {

       MenuFunction                    menuFunction;

       FormRun formRun                 = sender.formRun();

       FormDataSource                  LedgerJournalTable_ds = formRun.dataSource(1);

       LedgerJournalCheckpost          _ledgerJournalCheckpost;

       str                             message,msg,info,setvalue;

       container                       con;

       Set                             setOne;

       SetEnumerator                   enumerator;

       setOne                          = new Set(types::String);

       LedgerJournalTable              ledgerJournalTable;

       ledgerJournalTable              = LedgerJournalTable_ds.cursor();

       LedgerJournalTrans              ledgerJournalTrans;

       System.IO.StringWriter          stringWriter;

       Newtonsoft.Json.JsonTextWriter  jsonWriter;

       stringWriter                    = new System.IO.StringWriter();

//validation

       ledgerJournalTable              = LedgerJournalTable::find(ledgerjournaltable.JournalNum);

       _LedgerJournalCheckPost         = LedgerJournalCheckPost::newLedgerJournalTable(ledgerJournalTable,NoYes::No);

       _LedgerJournalCheckPost.run();

       message                         = _LedgerJournalCheckPost.parmErrorMsg();

       if(message)

       {

           msg             = Global::parmSysInfologStr();

       }

       info                = Global::parminfo();

       if(info != "Journal is OK.")

       {

           con = str2con(info);

       }

       setOne.add(message);

       setOne.add(msg);

       enumerator = setOne.getEnumerator();

       while (enumerator.moveNext())

       {

           setvalue += enumerator.current();

       }

       con = conIns(con,1,setvalue);

       jsonWriter                      = new Newtonsoft.Json.JsonTextWriter(stringWriter);

       jsonWriter.WriteStartObject();// add '{'

       jsonWriter.WritePropertyName("Journal Num");

       jsonWriter.WriteValue(ledgerJournalTable.JournalNum);

       jsonWriter.WritePropertyName("Journal Name");

       jsonWriter.WriteValue(ledgerJournalTable.JournalName);

       jsonWriter.WritePropertyName("Lines");

       jsonWriter.WriteStartArray();

       while select ledgerJournalTrans

           where ledgerJournalTrans.JournalNum == ledgerjournaltable.JournalNum

       {

           jsonWriter.WriteStartObject();// add '{'

           jsonWriter.WritePropertyName("Voucher");

           jsonWriter.WriteValue(ledgerJournalTrans.Voucher);

           jsonWriter.WritePropertyName("Vendor Name");

           jsonWriter.WriteValue(ledgerJournalTrans.LedgerDimensionName);

           jsonWriter.WriteEndObject(); // add '}'

       }

       jsonWriter.WriteEndArray(); //add ']'

       jsonWriter.WriteEndObject(); //add '}'

       if(info == "Journal is OK." && !msg && !message)

       {

           jsonWriter.WriteStartObject();

           jsonWriter.WritePropertyName("Response");

           jsonWriter.WriteStartArray();

           jsonWriter.WriteStartObject();// add '{'

           jsonWriter.WritePropertyName("Status");

           jsonWriter.WriteValue("Success");

           jsonWriter.WritePropertyName("Remark");

           jsonWriter.WriteValue(info);

       }

       else

       {

           jsonWriter.WriteStartObject();

           jsonWriter.WritePropertyName("Response");

           jsonWriter.WriteStartArray();

           jsonWriter.WriteStartObject();// add '{'

           jsonWriter.WritePropertyName("Status");

           jsonWriter.WriteValue("Failed");

           jsonWriter.WritePropertyName("Remark");

           jsonWriter.WriteValue(con2Str(con));

       }

       jsonWriter.WriteEndObject();

       jsonWriter.WriteEndArray();

       jsonWriter.WriteEndObject();

       info(strFmt("%1,",stringWriter.ToString()));

       //info(strFmt("my message %1", message)); //Warnings

       //info(strFmt("my msg %1", msg)); //warnings

       //info(strFmt("my info %1", info)); // info's

   }

}

Code to get warnings and info messages :

[ExtensionOf(classStr(Global))]

static final class DaxGlobal_Extension

{

   public static SysInfologStr  message;

   public static SysInfologStr  info;

   static boolean checkFailed(SysInfoLogStr txt, URL helpURL, SysInfoAction _sysInfoAction)

   {

       boolean     ret;

       ret = next checkFailed(txt,helpURL,_sysInfoAction);

       Global::parmSysInfologStr(txt);

       return ret;

   }

   static SysInfologStr parmSysInfologStr(SysInfologStr  _message = message)

   {

       message = getPrefix()+_message;

       return message;

   }

   static SysInfologStr parminfo(SysInfologStr  _info = info)

   {

       info = getPrefix()+_info;

       return info;

   }

   static Exception info(SysInfoLogStr txt, URL helpUrl, SysInfoAction _sysInfoAction)

   {

       boolean     ret;

       ret =next info(txt,helpUrl,_sysInfoAction);

       Global::parminfo(txt);

       return ret;

   }

}

Code to get the current record validation message :

[ExtensionOf(classStr(LedgerPostingMessageLog))]

internal final class DaxLedgerPostingMessageLog_Extension

{

   public SysInfologStr message;

   public boolean logCheckFailed(SysInfologStr _message, URL _helpURL)

   {

       boolean ret;

       ret= next logCheckFailed(_message,_helpURL);

       this.parmSysInfologStr(_message);

       return ret;

   }

   public SysInfologStr parmSysInfologStr(SysInfologStr  _message = message)

   {

       message = _message;

       return message;

   } 

}

Code to get the above class message in ledgerjournalcheckpost for use in custom button :

[ExtensionOf(classstr(LedgerJournalCheckPost))]

internal final class Dax_LedgerJournalCheckpost_Extension

{

   public SysInfologStr message;

   protected boolean checkJournalStatus()

   {

       boolean ret;

       ret = next checkJournalStatus();

       message = getPrefix() + ledgerPostingMessageCollection.parmSysInfologStr();

       return ret;

   }

   public SysInfologStr parmErrorMsg()

   {

       return message;

   }

}


Output :





Other Simplest way to get Info Message is :

Validation(info message)  is saved in Log field of Ledger Journal Table.