Showing posts with label query. Show all posts
Showing posts with label query. Show all posts

Friday, July 28, 2023

XDS in action: How to restraint access to data based on the current user employee

Extensible Data Security (XDS) policies allow restraint access to D365FO data in a very flexible way.

Business case: a user can see those purchase orders and their related confirmations only if he or she is Requester. The whole project can basically contain three objects: role, query, and policy.



After creating a specific security role, we need to create a query with HCMWorker table so that it is filtered for the current user.





All constrained tables should be added to the policy by referencing a particular relation (PurchTable, for example, has two relations with HCMWorker table; thus, we need to pick up the required one related to Requester field)







Note: we can create a join expression if a required relation does not exist for a given table.

Once the project built and synchronized, we can assign this new role along with some standard ones to a user. For example sake, I added Ada to my user.





Wednesday, June 29, 2022

Union query for Project transactions

Union query may be a very efficient and useful option when you need to fetch similar data fields from different tables. A good example of this may be the case when you to gather information about project related transactions as they can be of different types, like, expenses, fees, items, etc. 

Say, we need to render some report collecting all these different types of transaction that can be posted against a given project and group them by a given financial dimension value in Project group. Let's see how Union query can help us.

In order to better understand the goal, you can take a look to the standard form ProjInvoiceJournal which perfectly explains how all these table relate to each other. 

Most of them contain the same set of fields, and we simply need to get some of them into one view to populate by the report data provider.

So, at the first step, let's create all necessary queries for the source Project transaction related tables. Pick up the first and create a simple query as depicted. 

Then create a view based on this query. 

Then pick up the next and do the same.

Complete these two steps for all necessary transaction types.

Now you can create a Union query and add all your views together as they have the same set of fields.

If you need to distinguish them later who is who, say, in computed column methods, you can use  unionAllBranchId field, but this is out of the current focus.

Finally you can elaborate it by adding a new view based on the latter with a simple SUM aggregation for Amount field.

Basically, you achieve your goal with no coding.

I am not going into details about the whole project, which you can get by this URL https://github.com/wojzeh/tmxProjectSalesPerSegment. Ping me, if you have any questions.


Related topics: 

Computed column for union values from multiple outer joined data sources in view

Wednesday, July 29, 2020

How to get a list of the Tables maintained by Change Tracking in SQL

Thanks to Brent Ozar and Dave Phillips who showed us how to get a list of the Tables maintained by Change Tracking directly in MS SQL Server Management Studio. It works for both AX2012 and D365 versions.


SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
GO
SELECT
   sct1.name AS CT_schema,
   sot1.name AS CT_table,
   ps1.row_count AS CT_rows,
   ps1.reserved_page_count*8./1024. AS CT_reserved_MB,
   sct2.name AS tracked_schema,
   sot2.name AS tracked_name,
   ps2.row_count AS tracked_rows,
   ps2.reserved_page_count*8./1024. AS tracked_base_table_MB,
   change_tracking_min_valid_version(sot2.object_id) AS min_valid_version
FROM sys.internal_tables it
JOIN sys.objects sot1 ON it.object_id=sot1.object_id
JOIN sys.schemas AS sct1 ON sot1.schema_id=sct1.schema_id
JOIN sys.dm_db_partition_stats ps1 ON it.object_id = ps1. object_id AND ps1.index_id in (0,1)
LEFT JOIN sys.objects sot2 ON it.parent_object_id=sot2.object_id
LEFT JOIN sys.schemas AS sct2 ON sot2.schema_id=sct2.schema_id
LEFT JOIN sys.dm_db_partition_stats ps2 ON sot2.object_id = ps2. object_id AND ps2.index_id in (0,1)
WHERE it.internal_type IN (209, 210)
order by tracked_name
;

GO


Friday, April 5, 2019

AX2012-D365 Bug: TrvWorkflowExpLines query misses fields

The issue appears once you post project or inter-company expenses.

Once you approved and never opened Accounting distribution form for an expense, Project activity number field will be empty, or, for the Inter-company case, even the project information will be lost.






This is how TrvWorkflowExpLines query should look like to overcome the issue.


Thursday, March 31, 2016

Date valid fields in View and AOT Query

Unfortunately, I did not manage to select addresses effective as of today from the DirPartyPostalAddressView view.



Nevertherless, its ValidTimeStateEnabled property set to Yes.



My workaround is to use SysQueryRangeUtil class providing greaterThanUtcNow and lessThanUtcNow methods to create an extended range. So my Query looks like the following.



Wednesday, June 18, 2014

How to count query loops number

Amongst our other mundane chores, counting of a query loops is not the trickiest one but, unfortunately, still clumsy developed even in AX 2012.

 Not only is it strange to have two different methods for one only and multiple data sources, but it takes long time and returns incorrect results when it comes to groupings.

 SysQuery::countTotal and SysQuery::countLoops use the private method SysQuery::countPrim, which can be redone in a way suggested here (in Russian).

private server static container сountPrim(container _queryPack)
{
    Query                   countQuery;
    QueryRun                countQueryRun;
    QueryBuildDataSource    qbds;
    QueryBuildFieldList     qbfl;
    Common                  common;
    Integer                 counter;
    Integer                 loops;
    
    Integer                 tmxGroupNumber;
    Integer                 tmxDataSourceNumber;
    ;
    countQueryRun   = new QueryRun(_queryPack);
    countQuery      = countQueryRun.query();
    tmxGroupNumber  = countQuery.groupByFieldCount(); //<-- this guarantees number of groupings

    for (tmxDataSourceNumber = 1; tmxDataSourceNumber <= countQuery.dataSourceCount(); tmxDataSourceNumber++)
    {
        qbds = countQuery.dataSourceNo(tmxDataSourceNumber);
         qbds.update(false);
        //qbds.sortClear();

        //tmxGroupNumber +=(qbds.orderMode()==orderMode::GroupBy); 
        qbfl = qbds.fields();
        qbfl.dynamic(false);
        qbfl.clearFieldList();
         qbds.addSelectionField(fieldNum(Common,RecId),SelectionField::Count);
    }
  
    countQueryRun   = new QueryRun(countQuery);

    while (countQueryRun.next())
    {
         common  = countQueryRun.getNo(1);
        counter += common.RecId;
        loops++;
    }
    //return [counter,loops];
    return [counter,(tmxGroupNumber ? loops : counter)];
 }

Tuesday, December 13, 2011

Dates In An Extended Query Range

Let's say we need to range all the BOM versions that are active as of today. For this purpose we can use an extended range in the query.

{
    QueryBuildRange qbr;
    ;
    if(!_args.record())
        return;
    // there is an active caller!
    switch (_args.record().TableId)
    {
        case tablenum(InventTable):
            inventTable = element.args().record();
            this.query().dataSourceTable(tablenum(InventTable)).addRange(fieldnum(InventTable, ItemId)).value(inventTable.ItemId);
            qbr = this.query().dataSourceTable(tablenum(BOMVersion)).addRange(fieldnum(BOMVersion, RecId));
            qbr.value('(fromDate <= '+date2StrXpp(today())+') && (toDate >= '+date2StrXpp(today())+')');
            break;

The final SQL query will look like this (a fragment):


= BOMVersion.ItemId AND ((Active = 1)) AND (((fromDate <=13\12\2011) && (toDate >= 13\12\2011))) JOIN * FROM BOM(BOM_1) ORDER BY BOM.LineNum ASC ON BOMVersion.BOMId = BOM.BOMId

The idea was taken from the AX forum (in Russian).

Thursday, August 12, 2010

Value in Query range

How to use Enum values in Query ranges:



public void init()
{
    QueryBuildRange   criteriaOpen;
    ;
    super();
    criteriaOpen = this.query().dataSourceTable(tableNum(ProdTable)).addRange(fieldnum(ProdTable, ProdStatus));
    criteriaOpen.value("Started"); // it does not work in non-English interface!!!

    criteriaOpen.value(enum2str(ProdStatus::StartedUp)); // not enough good...

        criteriaOpen.value(QueryValue(ProdStatus::StartedUp); // now it is correct!    
}