Friday, 11 February 2022

Custom lookup on Form in D365

 [Form]

public class MKCustomLookupForm extends FormRun

{

    [DataSource]

    class MKCustomDSTable

    {

        [DataField]

        class FieldIdCust 

        {

            /// <summary>

            /// </summary>

            /// <param name = "_formControl">Form control</param>

            /// <param name = "_filterStr">Fileter condition, if any</param>

            public void lookup(FormControl _formControl, str _filterStr)

            {

                CustTable   custTable;

                 SysTableLookup sysTableLookup = SysTableLookup::newParameters(tablenum(MKTable), _formControl);

        Query query = new Query();

        QueryBuildDataSource queryBuildDataSource;


        queryBuildDataSource = query.addDataSource(tablenum(MKTable));

        queryBuildDataSource.addSortField(fieldNum(MKTable, FieldId), SortOrder::Ascending);


        sysTableLookup.addLookupfield(fieldnum(MKTable, FieldId), true);

        sysTableLookup.addLookupfield(fieldnum(MKTable, FieldName));


        sysTableLookup.parmQuery(query);

        //logic

        sysTableLookup.parmTmpBuffer(fieldList);

        sysTableLookup.performFormLookup();

            }

        }

    }

}

Thursday, 27 January 2022

Validate field method for Form DataSource FormDataFieldEventType::Validating

   /// <summary>

    ///

    /// </summary>

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

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

    [FormDataFieldEventHandler(formDataFieldStr(VendTable, VendTable, ProcessToProphet), FormDataFieldEventType::Validated)]

    public static void ProcessToProphet_OnValidated(FormDataObject sender, FormDataFieldEventArgs e)

    {

        var dataObject = sender as FormDataObject;

        var args = e as FormDataFieldCancelEventArgs;

        DirPartyBaseType        dirPartyBaseTypeOrg;

        FormDataSource         dataSource = sender.datasource();

        FormRun    element = dataSource.formRun();

        FormControl     dirtype = element.design(0).controlName("type");

        str  dirPartyBaseTypestr =   dirtype.valueStr();

        dirPartyBaseTypeOrg = str2Enum(dirPartyBaseTypeOrg , dirPartyBaseTypestr);

       // info (strFmt ("%1 ", dirtype.valueStr()) )    ;

      //  DirPartyBaseType selected = any2Enum(dirtype.valueStr());

        if (args != null && dataObject != null)

        {

            var vendTable_ds = dataObject.datasource() as FormDataSource;

            if (vendTable_ds != null)

            {

                VendTable vendRec = vendTable_ds.cursor() as VendTable;

                if (vendRec.RecId > 0)

                {

                    if (dirPartyBaseTypeOrg != DirPartyBaseType::Organization)

                    {

                        boolean doCancel = !checkFailed("Not Organization");

                        args.cancel(doCancel);

                    }

                }

            }

        }

    }

Tuesday, 18 January 2022

FormDataSourceEventHandler to enable field

 class IPLVendTableTriggerForm_EventHandler

{

    /// <summary>

    ///

    /// </summary>

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

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

    [FormDataSourceEventHandler(formDataSourceStr(VendTable, VendTable), FormDataSourceEventType::Activated)]

    public static void VendTable_OnActivated(FormDataSource sender, FormDataSourceEventArgs e)

    {

        VendTable       vendTable = sender.cursor();

        FormDataSource  vendTable_ds = sender.formRun().dataSource("VendTable");

        FormRun         element = sender.formRun();

        FormControl     processTo = element.design(0).controlName("BISTrigger_ProcessTo");


        if(vendTable.ProcessTo == NoYes::Yes)

        {

            processTo.enabled(false);

        }

        else

        {

            processTo.enabled(true);

        }

    }


}

Thursday, 15 April 2021

Find Missing Indexes SQL query

 USE YourDatabase

GO

 

SELECT db.[name] AS [DatabaseName]

    ,id.[object_id] AS [ObjectID]

,OBJECT_NAME(id.[object_id], db.[database_id]) AS [ObjectName]

    ,id.[statement] AS [FullyQualifiedObjectName]

    ,id.[equality_columns] AS [EqualityColumns]

    ,id.[inequality_columns] AS [InEqualityColumns]

    ,id.[included_columns] AS [IncludedColumns]

    ,gs.[unique_compiles] AS [UniqueCompiles]

    ,gs.[user_seeks] AS [UserSeeks]

    ,gs.[user_scans] AS [UserScans]

    ,gs.[last_user_seek] AS [LastUserSeekTime]

    ,gs.[last_user_scan] AS [LastUserScanTime]

    ,gs.[avg_total_user_cost] AS [AvgTotalUserCost]  -- Average cost of the user queries that could be reduced by the index in the group.

    ,gs.[avg_user_impact] AS [AvgUserImpact]  -- The value means that the query cost would on average drop by this percentage if this missing index group was implemented.

    ,gs.[system_seeks] AS [SystemSeeks]

    ,gs.[system_scans] AS [SystemScans]

    ,gs.[last_system_seek] AS [LastSystemSeekTime]

    ,gs.[last_system_scan] AS [LastSystemScanTime]

    ,gs.[avg_total_system_cost] AS [AvgTotalSystemCost]

    ,gs.[avg_system_impact] AS [AvgSystemImpact]  -- Average percentage benefit that system queries could experience if this missing index group was implemented.

    ,gs.[user_seeks] * gs.[avg_total_user_cost] * (gs.[avg_user_impact] * 0.01) AS [IndexAdvantage]

    ,'CREATE INDEX [IX_' + OBJECT_NAME(id.[object_id], db.[database_id]) + '_' + REPLACE(REPLACE(REPLACE(ISNULL(id.[equality_columns], ''), ', ', '_'), '[', ''), ']', '') + CASE

        WHEN id.[equality_columns] IS NOT NULL

            AND id.[inequality_columns] IS NOT NULL

            THEN '_'

        ELSE ''

        END + REPLACE(REPLACE(REPLACE(ISNULL(id.[inequality_columns], ''), ', ', '_'), '[', ''), ']', '') + '_' + LEFT(CAST(NEWID() AS [nvarchar](64)), 5) + ']' + ' ON ' + id.[statement] + ' (' + ISNULL(id.[equality_columns], '') + CASE

        WHEN id.[equality_columns] IS NOT NULL

            AND id.[inequality_columns] IS NOT NULL

            THEN ','

        ELSE ''

        END + ISNULL(id.[inequality_columns], '') + ')' + ISNULL(' INCLUDE (' + id.[included_columns] + ')', '') AS [ProposedIndex]

    ,CAST(CURRENT_TIMESTAMP AS [smalldatetime]) AS [CollectionDate]

FROM [sys].[dm_db_missing_index_group_stats] gs WITH (NOLOCK)

INNER JOIN [sys].[dm_db_missing_index_groups] ig WITH (NOLOCK) ON gs.[group_handle] = ig.[index_group_handle]

INNER JOIN [sys].[dm_db_missing_index_details] id WITH (NOLOCK) ON ig.[index_handle] = id.[index_handle]

INNER JOIN [sys].[databases] db WITH (NOLOCK) ON db.[database_id] = id.[database_id]

WHERE  db.[database_id] = DB_ID()

--AND OBJECT_NAME(id.[object_id], db.[database_id]) = 'YourTableName'

ORDER BY ObjectName, [IndexAdvantage] DESC

OPTION (RECOMPILE);

Friday, 12 March 2021

Add financial dimensions as separate columns to Data Entity.

 This is just for my reference: 


Add dimensions to Excel templates - Finance & Operations | Dynamics 365 | Microsoft Docs

AX 7. How to add financial dimensions as separate columns to Data Entity. - Dynamics 365 Finance Community


Everyone who is working with financial dimensions in AX would like to see them in separate columns in Excel and today we will look at how to achieve this for all Data Entities.

Image

It’s also awesome because we have a wizard that will do all the job for us!

In the Visual Studio under Dynamics AX > Addins select “Add financial dimensions for OData…”

Image

Now we need to enter the dimensions we want to expose. As you can see from a help text these dimensions should be set up in AX on the Data Entities tab under General ledger > Chart of accounts > Dimensions > Financial dimension configuration for integrating applications.

Image

Also, we need to specify a model for new objects. Please note that the model should have reference to the Dimensions model, however you can add reference afterward.

After clicking “Apply”, VS will show a new project creation dialog. A new project will be created. It will contain two Data Entity Extensions:

Image

Please note that you cannot rename them because internal code works only with entity extensions that have name = Data Entity Name + “DimensionIntegration”.

Both entities have identical changes, the only difference is that one shows default dimensions and another one ledger dimensions.

Let’s looks at DimensionSetEntity extension:

Image

It has 3 new fields, one for each financial dimension we specified in the wizard.

These fields are computed columns, each of them uses the same data method “getValueColumnForDimensionAttributeSql”.

Image

It is quite interesting and has some smarties inside but hardcoded values as well.

That all we need to do. After compilation and synchronization, we can open Data Entity in Excel and will see new fields in the data connector designer.

Image

How does it work? That the most exciting part. All the magic is inside the DimensionAttributeValueSet table. If we look at it in AOT – nothing is really changed since AX 2012, but if we go to SQL management studio we will see a bunch of new columns:

Image

For each financial dimensions we have 2 columns: one contains the value and another RecId.

Each computed column in Dimensions entity select value from this table.

So, if you are doing BI with 3rd party tools, now you can use  DimensionAttributeValueSet and DimensionAttributeValueCombination tables to query all financial dimensions directly.



Monday, 13 July 2020

Change Batch status X++ code

static void MKChangeBatchStatus(Args _args)
{
    batchJob    batchJob;
   
    while select forupdate batchJob
       where batchJob.Status  == BatchStatus::Waiting
   {
       ttsBegin;
       batchJob.Status = BatchStatus::Hold;
       batchJob.update();
     
       ttsCommit;
   }
   
}

Wednesday, 8 July 2020

Get default financial dimension values through X++ code

In the below example we will see how we can get the default financial dimension values from Purch line table.
Here we are using AxdDimensionUtil helper class to get the Dimension values.

Note:  we can replace PurchLine table with any table with DefaultDimension field.

static void Job5(Args _args)
{
    PurchLine   purchLine;
    Counter     i;
    container   conDim;
    //
    purchLine = PurchLine::find("WP00097201",1);   
    conDim = AxdDimensionUtil::getDimensionAttributeValueSetValue(purchLine.DefaultDimension);

    for (i=1 ; i<= conLen(conDim) ; i++)
    {
         info(conPeek(conDim,i));
    }
}

There are other ways to find default Dimensions values. The one above is one of them.

The same result can also be achieved using "DimensionAttributeValueSetStorage" class.


public DimensionValue getDimValue(int _index, DimensionDefault _defaultDim)
    {
       // #DocumationGL // List that matches the GL Export
       container       dimAttrList     = ['BPCTrialBalance', 'Customer', 'Department', 'Employee', 'IntercompanyCode', 'Project','ServiceCategory'];
        Name            dimAttribName   = conPeek(dimAttrList, _index);

        DimensionAttributeValueSet          DimensionAttributeValueSet;
        DimensionAttributeValueSetItem      DimensionAttributeValueSetItem;
        DimensionAttributeValue             DimensionAttributeValue;
        DimensionAttribute                  DimensionAttribute;
        ;

        select RecId from DimensionAttributeValueSet
            where DimensionAttributeValueSet.RecId == _defaultDim

        join DisplayValue from DimensionAttributeValueSetItem
            where DimensionAttributeValueSetItem.DimensionAttributeValueSet == DimensionAttributeValueSet.RecId

        join RecId from DimensionAttributeValue
            where DimensionAttributeValue.RecId == DimensionAttributeValueSetItem.DimensionAttributeValue

        join RecId from DimensionAttribute
            where DimensionAttribute.RecId == DimensionAttributeValue.DimensionAttribute
               && DimensionAttribute.Name == dimAttribName;

        return DimensionAttributeValueSetItem.DisplayValue;
    }

    display DimensionValue dimTrialBalance(ProjPostTransViewGL  _projPostTransView)
    {
        ProjJournalTrans    projJournalTrans;
        ProjCostTrans       projCostTrans;
        container   conDim;
        switch (_projPostTransView.ProjTransType)
        {
            case ProjTransType::Item:
            case ProjTransType::Hour:
            case ProjTransType::Revenue:
            case ProjTransType::OnAccount:
                select  DefaultDimension
                from    projJournalTrans
                where   projJournalTrans.TransId == _projPostTransView.TransId;
                
                return  this.getDimValue(1,projJournalTrans.DefaultDimension);

            case ProjTransType::Cost:
                select  DefaultDimension
                from    projCostTrans
                where   projCostTrans.TransId == _projPostTransView.TransId;

                return  this.getDimValue(1,projCostTrans.DefaultDimension);

            default :
                return '';
        }
    }

How to Disable “Advanced Filter or Sort” and Enforce Custom Filters on Any D365FO Form

 In Dynamics 365 Finance and Operations, users can apply filters through the “Advanced filter or sort” feature found under the Options tab...