Wednesday, 27 September 2023

Service update failure due to Globalupdate script for service model: AOSService on VM failed due to report deployment

Hello Folks, 

I am writing this post specifically to target the issue where step fails due to report deployment. For other issues, you can refer to

https://learn.microsoft.com/en-us/dynamics365/fin-ops-core/dev-itpro/deployment/deployable-package-troubleshooting

While applying recent service update 10.0.36 to my cloud VM, I have received an issue as 

GlobalUpdate script for service model: AOSService Failed on <VM Name>

To identify the exact issue, just navigate to 

C:\DynamicsAX\RunbookExecution-ExecuteParallelRunbook-<PackageId>\Output 

folder in your VM. 

Look for latest AxUpdateInstaller file, scroll through and you will find the error reason. 




Look for the failed step in the AxupdateInstaller file or mentioned failed step over LCS. In my case, it was step 25. Navigate to the folder with the same name. 

Since the issue was on report, look for latest UpdateReports or UpdateReportsOutput file. Scroll through to find the exact object the deployment has failed on 



As it says, In my case the issue was on CustProvisionalBalance.Report under ApplicationSuite model. 

To resolve this, we can just deploy this report through powershell command under VM. 

To deploy the report, run powershell as Admin and Navigate to   J:\AosService\PackagesLocalDirectory\Plugins\AxReportVmRoleStartupTask>

Then, execute the below command

.\DeployAllReportsToSsrs.ps1 -Module ApplicationSuite -PackageInstallLocation "J:\AosService\PackagesLocalDirectory" -ReportName CustProvisionalBalance.Report

You can change the report or Model name as needed. 

Thats it, Just resume the deployment from LCS. It should then go through with no issues.

Thanks!


Thursday, 19 January 2023

Perform operation at Entity level post all Target data Insert in D365 FnO

There are often requirements where we need to perform some operation(s) Just after target data is inserted/updated fully for all records. 

There is a method that can be used at Entity level known as postTargetProcess

Drawback of this method is that It only can be used with Data management framework in non-set based entities. 

It can also not be used with Odata operations. 

Method can be implemented as below

public class SaddafDemoEntity extends common
{   
    /// <summary>
    /// Post insert into target
    /// </summary>
    /// <param name = "_dmfDefinitionGroupExecution">DMFDefinitionGroupExecution</param>
    public static void postTargetProcess(DMFDefinitionGroupExecution _dmfDefinitionGroupExecution)
    {
       TestHeaderTable      headerTable;
       SaddafDemoStaging    staging;

        select firstonly forupdate integrationImportTable
            where integrationImportTable.DMFExecutionId == _dmfDefinitionGroupExecution.ExecutionId;

        select count(Recid) from staging
            where staging.DefinitionGroup   == _dmfDefinitionGroupExecution.DefinitionGroup
            && staging.ExecutionId          == _dmfDefinitionGroupExecution.ExecutionId
            && staging.TransferStatus       == DMFTransferStatus::Completed;

        ttsbegin;
        headerTable.RecordCount  = staging.RecId
        headerTable.update();
        ttscommit;
    }
}
For Entities using set based operations, we have a tricky workaround by using an Post event handler for a write method of DmfEntityWriter class. 

Point to be noted is, this Class and method is called for all the entities wo we need to make sure of using correct entity and logic inside that to avoid messing whole framework. 

Sample implemention is as below
   [PostHandlerFor(classStr(DmfEntityWriter), methodStr(DmfEntityWriter, write))]
   public static void DmfEntityWriter_Post_write(XppPrePostArgs args)
   {
       	DMFDefinitionGroupExecution  dmfDefinitionGroupExecution = args.getArg('_definitionGroupExecution');
       
       	str entityName = dmfDefinitionGroupExecution.EntityXMLName;

        switch (entityName)
        {
          case 'SaddafDemoEntity':
            TestHeaderTable      headerTable;
       		SaddafDemoStaging    staging;

        	select firstonly forupdate integrationImportTable
            	where integrationImportTable.DMFExecutionId == dmfDefinitionGroupExecution.ExecutionId;

        	select count(Recid) from staging
            	where staging.DefinitionGroup   == dmfDefinitionGroupExecution.DefinitionGroup
            	&& staging.ExecutionId          == dmfDefinitionGroupExecution.ExecutionId
            	&& staging.TransferStatus       == DMFTransferStatus::Completed;

        	ttsbegin;
        	headerTable.RecordCount  = staging.RecId
        	headerTable.update();
        	ttscommit;
          break;
        }
   }
Unfortunately there is nothing similar for Odata, but Odata has its own set of capabilities to supersede all shortcomings.
Thanks for reading!!

Perform operation at Entity level post all Staging data Insert or Before Target insert starts in D365 FnO

As the title suggests, there are sometimes requirements where we need to perform some operation(s) Just before target data insert operation triggers (or in between the whole DMF cycle) but not at the any datasource event or at the Entity insert method. 

One of such requirement can where the Any Header data to be populated before line insertion. 

Good news is we have a way to achieve it via postGetStagingData method at Entity level, Bad news is it works only after staging is inserted and works even if all target data fails validation.

Below is the implementation way for that 

public class SaddafDemoEntity extends common
{
    /// <summary>
    /// Post staging data
    /// </summary>
    /// <param name = "_dmfDefinitionGroupExecution">DMFDefinitionGroupExecution</param>
    public static void postGetStagingData(DMFDefinitionGroupExecution _dmfDefinitionGroupExecution)
    {
        TestHeaderTable      headerTable;
        SaddafDemoStaging    staging;

        select firstonly staging
            where staging.DefinitionGroup   == _dmfDefinitionGroupExecution.DefinitionGroup
            && staging.ExecutionId          == _dmfDefinitionGroupExecution.ExecutionId;

        headerTable.DMFExecutionId =  staging.ExecutionId; //Just the example, you can use any field needed
        headerTable.insert();
ttsbegin; staging = null; update_recordset staging setting ParentRefRecId = headerTable.RecId
where staging.DefinitionGroup == _dmfDefinitionGroupExecution.DefinitionGroup && staging.ExecutionId == _dmfDefinitionGroupExecution.ExecutionId; ttscommit; } }
For Entities using set based operations, we have another method known as preTargetProcessSetbased that can be implemented in similar way as above and is available to use just before Target process initiates (which is obvious by name😃). 

Happy Coding!!!