Modifying a DMF data entity query dynamically, from another query, for filteration in D365FO

 


Problem statement 

Have you ever came across a situation where you needed to run your data entity through a batch, and in your batch you have a very complex composite query (where the query ranges could be anything -- the user can keep adding new ranges to the query) and you need pass on the query range values to your data entity query. I bet you must have faced this: say, your data contract is having an AOT query called PurchContractQry where you have PurchTable, Purchline, InventDim, PurchParmLine and a litter of related tables. You have an entity that does have a different query (as you know Data entities store queries as packed data in DMFDefinitionGroupEntity)-- how could you possibly make the ranges flow to your Entity, then?

The following blog explains the same.

As an example: we have created a Data entity called D365DataEntityPurchExport, which exports records based on the ranges supplied in a query coming from a contract Class, called PurchContractQry. We need to get the ranges which user is selecting/enetering and pass them on to the entity and run it accordingly. 

Step 1: in your service class, you can get query from dataContract:

_contract.getQuery();

You can also get the query of your entity by simply saying:

Query    purchEnttQuery = new Query(DMFUtil::getDefaultQueryForEntityV3(_entityName));

Where _entityName is the name of the Entity you are using (in our case, it's DMFDefinitionGroupEntity).

Hence you can create a new query for your entity and get the contents of your query like this:

purchEnttQuery = this.addAdditionalRanges(_contract.getQuery(), purchEnttQuery ) ;

We are going to create a new method to get the ranges from our contract class, like this:

private Query addAdditionalRanges(Query _contractQry, Query    _entityQuery)

    {

        Counter totalDSources = _contractQry.dataSourceCount();

        QueryBuildDataSource qbdsPurch = _entityQuery.dataSourceTable(tableNum(D365DataEntityPurchExport));

        for(int ds =1; ds<= totalDSources ; ds++)

        {

            QueryBuildDataSource    qbds = _contractQry.dataSourceNo(ds);

            Counter totalRanges = qbds.rangeCount();

            for(int r =1; r<= totalRanges; r++)

            {

                QueryBuildRange qbr =  qbds.range(r);

                SysQuery::findOrCreateRange(qbdsPurch , fieldName2Id(tableNum(D365DataEntityPurchExport), qbr.fieldName())).value(qbr.value());

            }

        }

        return _entityQuery;

    }

Where dynamically your are setting the name of the query ranges in your data entity, by getting the values from your contract class Query.

Step2: We can execute an entity like this very easily:

            DMFEntityExporter exporter = new DMFEntityExporter();

            SharedServiceUnitFileID fileId = exporter.exportToFile(

                                           entityName,

                                           _dataContract.parmDefinitionGroupName(),

                                           '',

                                           DXCMVTExportMacros::csvFile,

                                           #FieldGroupName_AllFields,

                                           conNull(),                                           

                                           curExt(),

                                           null,               

                                           true,                

                                           false

                                           );

We can add this in the process method of our Service class. But the problem with this method is: it doesn't update the query you want it to execute -- it will make the entity to execute default version of the query. 

Why?

Well, look at the following code inside the above method:



Which means it will update the query of the entity, only when the definition group provided is blank. But what if, your definiton group is not blank -- purposely you want to execute your entity against any defintion group? 

Step 3: For this -- you can force the Definiton group entity to accept the packed query like this, before hand:

DMFDefinitionGroupEntity definitionGroupEntity = DMFDefinitionGroupEntity::find(_contract.parmDefinitionGroupName(), 

                                                                                        _entityName, 

                                                                                        true);

        if (definitionGroupEntity)

        {

            ttsbegin;

            definitionGroupEntity.QueryData = _query.pack();

            definitionGroupEntity.update();

            ttsCommit;

        }

As a result, the defintion group will be updated with the query value and be assigned to the entity:



 


Comments

Popular posts from this blog

Make your menu items visible on main menu, conditionally,, using this cool feature of D365FO

How to handle the error "Could not grant admin consent. Your organization does not have a subscription (or service principal) for the following API(s): Dynamics ERP" in Azure Portal