Here is some cool info on Financial Dimensions in D365
Category: Dynamics AX
Default Dimension Sorting
Often ERP users request that the default dimensions visible on the master records or transaction sources are sorted in a particular sequence. In D365, default dimensions are visible in the sorted order of the names. Taking this into account, following simple steps can be followed to create a sorted sequence with proper names displayed.
First create the financial dimensions with names that the sorted order is the sequence you need. Example, for this demo we need to create Region and then Business segment so this is what we do.
Create a new financial dimension with name A1Region
Go ahead and create 2nd Financial dimension with name A2BusinessSegment
Now the end user may not like these names displayed this way. Go ahead and setup the translations as shown below:
On translation form, enter as shown below:
Now go ahead, configure account structure, assign the structure as required. Once the setups are in, check any master record or transaction source and notice the dimension sequence.
So easy to setup the sorted sequences isn’t it?
Report Parameters for Query Based Reports – D365 SSRS
The blog mentioned below beautifully explains how one can add report parameters for query-based AX SSRS reports. A good read:
Report Parameters for Query Based Reports
#D365, #SSRSReporting, #X++
Customer Changes Approval – D365
A new feature for customer changes approval has been introduced in D365 FO. The following link beautifully explains this new feature.
Do go through this article: Customer Approval Changes
Implement GST (Goods & Services Tax) functionality in Microsoft Dynamics AX
We have successfully implemented GST patches for India localization for Dynamics AX. You can go through the link below:
Please contact us at sales@axpeditesoftware.com in case you need help in implementing GST patches for DAX.
#OneIndiaOneTax
Why should SAP and Oracle consultants consider a career move into Dynamics AX
Friends,
Here is a good article by Cognitive Group on reasons as to why other ERP consultants should rush to try their expertise in Dynamics AX. It’s a good read.
#DynamicsAX, #AXJobs
AOSAuthorization property on tables
This property is part of Tables Permission Framework (TPF). The Table Permissions Framework (TPF) enables administrators to add an additional level of security to tables that store sensitive data. TPF adds table-level security that verifies access rights no matter the origin of the request.
To enable TPF, an administrator specifies a value for the AOSAuthorizationProperty on a specific table in the AOT. The AOSAuthorizationProperty authorizes Create, Read, Update, and Delete operations. For some tables, it is important to authorize all operations because the data is sensitive. For other tables, you might find it suitable to specify a subset of operations, such as Create, Update, and Delete. In the case when you have specified a subset, the AOS authorizes the Create, Update, and Delete operations, but allows users to perform View operations if they have access to Microsoft Dynamics AX.
TPF can be enabled on any table in the Microsoft Dynamics AX database. For the sake of time and efficiency, however, administrators assign TPF to tables that are considered to be sensitive or to be of critical business value.
For example, consider the following scenario:
- Microsoft Dynamics AX and allows users to access data by using the Microsoft Dynamics AX client, Enterprise Portal, the Application Integration Framework, and a third-party application that connects to Microsoft Dynamics AX by using the .NET Business Connector.
- The administrator configured a Microsoft Dynamics AX user group called Senior Accountants, and members of this group have access to sensitive data about financial information and trade secrets. One of the database tables that stores this sensitive information is called FinancialResults. This table was added as part of a customization done by a partner after Microsoft Dynamics AX was installed.
- In the Application Object Tree (AOT), the administrator configures the FinancialResults table so that the Application Object Server (AOS) must authorize all operations for that table. The administrator specifies the value CreateReadUpdateDelete for the AOSAuthorizationProperty.
- Soon thereafter, a malicious user discovers a vulnerability in Contoso’s third-party application that connects to Microsoft Dynamics AX by using the .NET Business Connector. The malicious user connects to the database as a member of the CRM_users group and attempts to read the data in the FinancialResults table.
- Before allowing the read operation, the AOS checks to see if the user is a member of the Senior Leadership user group and if members of the group have permission to read the data. The malicious user is not a member of the Senior Leadership group, so the AOS denies the read operation.
You can change or add TPF for a table, but its is recommended that you perform TPF changes in a test environment so that you can study the impact of TPF changes on user groups that access that table.
To enable TPF on database table:
- In the AOT, expand Data Dictionary > Tables.
- Right-click a table, and then click Properties.
- Click AOSAuthorizationProperty and select a new value by using the drop-down list.
- Click Save All.
If you added TPF to a table, you might need to specify or expand permissions for user groups that access that table. You can view which objects access a table by using the Used-by command in the AOT:
- In the AOT, expand Data Dictionary > Tables.
- Right-click a table, and then click Add-ins > Cross-reference > Update.
- Right-click a table, and then click Add-ins > Cross-reference > Used by. The Used by form is displayed. This form shows all objects that access the selected table and what permissions (the Reference column) are required when accessing the table. You might need to adjust user group permissions if you set tighter restrictions on a table.
Iterators Vs. Enumerators
We can traverse our collections by using either an enumerator or an iterator.But there is no clarity why sometimes iterator fails while on other occasions it is flawless.Simply what we do is that just replace iterator with the enumerator.
First theoretically what happens behind the scenes is as follows:
When collection classes were first introduced in DAX, the iterator was the only option.But because of a few unwarranted drawbacks that appear as hard-to-find errors, enumerators were added, and iterators were kept for the backward compatibility. Just see the below listed code snippet
List list = new List(Types::Integer);
ListIterator iterator;
ListEnumerator enumerator;
;
//Populate List
…..
…..
//Traverse using an iterator.
iterator = new ListIterator(list);
while(Iterator.more())
{
print iterator.value());
iterator.next();
}
//Traverse using an enumerator
enumerator = list.getEnumerator();
while(enumerator.moveNext())
{
print enumerator.current();
}
The 1st difference is the way iterator and enumerator instances are created.For the iterator,you call new,and for the enumerator,you get an instance from the collection class by calling the getEnumerator method.
In most cases, both approaches will work equally well. However, when the collection class resides on the opposite tier from the tier on which it is traversed,the situation is quite different.
For example, if the collection resides on the client tier and is traversed on the server tier, the iterator approach fails because the iterator does not support cross-tier referencing.
The enumerator does not support cross-referencing either, but it doesn’t have to because it is instantiated on the same tier as the collection class. Traversing on the server tier using the client tier enumerator is quite network intensive, but the result is logically correct because some code is marked as “Called From”, meaning that it can run on either tier,depending on where it is called from. You could have broken logic if you use iterators, even if you test one execution path.In many cases, hard-to-track bugs such as this surface only when an operation is executed in batch mode.
The 2nd difference is the way traversing happens which is another potential threat as the onus lies on the developer to ensure that he moves the pointer by using .next() method else the code can land into endless loop.So again enumerator is clear winner here
But there is still one scenario while iterator holds edge over enumerator, if you want to delete/insert items from list.See the code snippet below:
List list = new List(Types::Integer);
ListIterator iterator;
;
list.addEnd(100);
list.addEnd(200);
list.addEnd(300);
iterator = new ListIterator(list);
while(iterator.more())
{
if(iterator.value() == 200)
iterator.delete();
iterator.next();
}
print list.toString(); //{100,300}
pause;
}
Restoring delete sales order or purchase order
static void restoreDeletedSO(Args _args)
{
SalesTableDelete salesTableDelete;
SalesLineDelete salesLineDelete;
SalesTable salesTable;
SalesLine salesLine;
;SalesTableDelete = SalesTableDelete::find(‘00450_036’, true);ttsbegin;switch (salesTableDelete.Cancelled)
{
case Voided::Voided :
salesTable = conpeek(salesTableDelete.SalesTable, 1);
salesTable.insert();while select forupdate salesLineDelete where salesLineDelete.SalesId == salesTableDelete.SalesId
{
salesLine = conpeek(salesLineDelete.SalesLine, 1);
salesLine.insert();
}
salesTableDelete.delete();
break;case Voided::linesVoided :
while select forupdate salesLineDelete where salesLineDelete.SalesId == salesTableDelete.SalesId
{
salesLine = conpeek(salesLineDelete.SalesLine, 1);
salesLine.insert();
salesLineDelete.delete();
}
salesTableDelete.delete();
break;
}ttscommit;
}
static void restoreDeletedPO(Args _args)
{
PurchTableDelete purchTableDelete;
PurchLineDelete purchLineDelete;
PurchTable purchTable;
PurchLine purchLine;
;purchTableDelete = PurchTableDelete::find(‘00242_049’, true);ttsbegin;switch (purchTableDelete.Cancelled)
{
case Voided::Voided :
purchTable = conpeek(purchTableDelete.PurchTable, 1);
purchTable.insert();while select forupdate purchLineDelete where purchLineDelete.PurchId == purchTableDelete.PurchId
{
purchLine = conpeek(purchLineDelete.PurchLine, 1);
purchLine.insert();
}
purchTableDelete.delete();
break;case Voided::linesVoided :
while select forupdate purchLineDelete where purchLineDelete.PurchId == purchTableDelete.PurchId
{
purchLine = conpeek(purchLineDelete.PurchLine, 1);
purchLine.insert();
purchLineDelete.delete();
}
purchTableDelete.delete();
break;
}ttscommit;
}
Insight Into Record IDs
static void getNextRecIdAX40(Args _args)
{
//Table that stores record ids details for tables
SystemSequences systemSequences;//Class that handles Record id generation
SystemSequence systemSequence = new SystemSequence();
;select firstonly systemSequences where systemSequences.tabId == tableNum(CustTable);systemSequence.suspendRecIds(systemSequences.tabId);
info(strFmt(‘Next record id: %1’, systemSequence.reserveValues(systemSequences.minVal, systemSequences.tabId)));
systemSequence.removeRecIdSuspension(systemSequences.tabId);
}
static void getNaxtRecIdAX30(Args _args)
{
SystemSequence systemSequence;
;systemSequence = new SystemSequence();systemSequence.flushCache();
systemSequence.setCacheSize(30)
info(strFmt(‘Buffer size: %1’, systemSequence.getCacheSize()));
info(strFmt(‘Next record id: %1’, systemSequence.nextVal();));
}
CREATE PROCEDURE initFromSMMQuotationTable @DATAAREAID NVARCHAR(3
AS DECLARE @NEXTVAL BIGINT, @ROWCOUNT BIGINT
SELECT ……,
RECID = IDENTITY(BIGINT,0,1) AS QUOTATIONID —Assign an IDENTITY column with a starting value of 0 incremented by 1
INTO #TEMP
FROM DEL_SMMQUOTATIONTABLE WHERE QUOTATIONSTATUS = 0 —SMMQuotationStatus::InProcess
SELECT @NEXTVAL=NEXTVAL —Retrieve the next value for RECID for this table (by TABID)
FROM SYSTEMSEQUENCES
WITH(UPDLOCK, HOLDLOCK) WHERE ID = -1 AND TABID = 1967
INSERT INTO SALESQUOTATIONTABLE
(column-list)
SELECT ……,
RECID = QUOTATIONID+@NEXTVAL —When we insert into the permanent table, we add the temporary table‟s IDENTITY column to the next value retrieved from SYSTEMSEQUENCES
FROM #TEMP
SELECT @ROWCOUNT = COUNT(*) FROM #TEMP
UPDATE SYSTEMSEQUENCES —We update SYSTEMSEQUENCES to reflect the number of rows that we have added to this table
SET NEXTVAL=NEXTVAL + @ROWCOUNT
WHERE ID = -1 AND TABID = 1967
GO