Friday, January 22, 2021

D365FO data entities with financial dimension requires special field names

 While creating data entities using financial dimensions, do not forget that those are backed in background by special classes via SysDataEntityPersister class




Those latter help resolving selected values (similar to what we see in segmented entry in forms). 



Each class is meant to serve a particular EDT, for example


/// <summary>
/// The <c>LedgerAccountDimensionDataEntityResolver</c> class resolves data entity ledger account values.
/// </summary>
[
System.ComponentModel.Composition.ExportMetadataAttribute("DimensionSFKType", identifierstr(LedgerDimensionAccount)),
System.ComponentModel.Composition.ExportAttribute("Microsoft.Dynamics.AX.DimensionDataEntitySFKFieldResolver")
]
public class LedgerAccountDimensionDataEntityResolver extends DimensionDataEntitySFKFieldResolver
{
    Name accountStructure;
...


In order to let it work correctly, some fields in your data entity must have particular names.


public class DimensionDataEntityConstants
{
    public static const str HasDimensionDataSourcesCustomPropertyName = '__HasDimensionDataSources__';
    public static const str ProviderCacheName = 'DimensionDataEntitySFKProviderCacheName';
    public static const str AccountStructureSuffix = 'AccountStructure';
    public static const str DisplayValueSuffix = 'DisplayValue';
    public static const str DisplayValueTypeName = 'DimensionDisplayValue';
}




Friday, January 15, 2021

How to delete obsolete values in ENUMVALUETABLE table X++

Following up this old issue with extensions to enums, I propose to run this class, if you cannot delete added values (grr* in my case) directly in SQL Studio. I am not sure if it is going to work in PROD. Please use it at your own risk.


class ENUMVALUETABLEDelete
{
   
    public static void main(Args _args)
    {
        if(!Box::confirm("Would you like to delete all grr* values of SysPolicyRuleTypeEnum type in ENUMVALUETABLE? (If not, just print them"))
        {
            ENUMVALUETABLEDelete::printRecords();
            return;
        }
        ENUMVALUETABLEDelete::deleteRecords();
        info("all related records have been deleted. Synchonize DB now!");
    }

    private static void deleteRecords()
    {
        Connection                      conn;
        SqlStatementExecutePermission   permission;

        str sqlSelect = 
@"SELECT
t.RECID
 from ENUMVALUETABLE as t
 join ENUMIDTABLE
 on enumid = id
 and ENUMIDTABLE.NAME = 'SysPolicyRuleTypeEnum'
 and t.NAME like 'grr%'";
        str sqlDelete = strFmt("%1 (%2)", "DELETE FROM ENUMVALUETABLE WHERE RECID IN ", sqlSelect);

        permission  = new SqlStatementExecutePermission(sqlDelete);
        conn        = new Connection();
        permission.assert();
        //conn.transactionScopeBegin();
        Statement statement = conn.createStatement();
        
        int result  = statement.executeUpdate(sqlDelete);
        str errText = statement.getLastErrorText();
        if(errText)
        {
            Info(errText);
        }

        // the permissions needs to be reverted back to original condition.
        CodeAccessPermission::revertAssert();
    }

    private static void printRecords()
    {
        Connection                      conn;
        SqlStatementExecutePermission   permission;

        str sqlSelect =
@"SELECT
t.RECID,
t.ENUMID,
t.ENUMVALUE,
t.NAME
 from ENUMVALUETABLE as t
 join ENUMIDTABLE
 on enumid = id
 and ENUMIDTABLE.NAME = 'SysPolicyRuleTypeEnum'
 and t.NAME like 'grr%'";

        permission  = new SqlStatementExecutePermission(sqlSelect);
        conn        = new Connection();
        permission.assert();

        Statement statement = conn.createStatement();
        
        ResultSet results = statement.executeQuery(sqlSelect);
        str errText = statement.getLastErrorText();
        if(errText)
        {
            Info(errText);
        }

        int enumId;
        str name;
        int i = 1;
        while (results.next())
        {
            enumId  = results.getInt(3);
            name    = results.getString(4);
            Info(strFmt("Found %1) %2 : %3", i, enumId, name));
            i++;
        }
        // the permissions needs to be reverted back to original condition.
        CodeAccessPermission::revertAssert();
    }

}

Do not forget to build your models and full DB sync after!