Tuesday, August 14, 2018

Microsoft Dynamics 365 - How to Create default dimension with a set of dimension values through x++

According to the client requirements, sometime we need to create ledger dimension account (financial dimension value) through code in D365. In Microsoft Dynamics 365, create financial dimension value is totally different form AX 2012.





Here we have to understand some terminology before implementing the solution in your project.

1. Before creating a default dimension with a set of dimension values in D365, you need to know the your accounting structure. For this go to
https://usnconeboxax1aos.cloud.onebox.dynamics.com/?cmp=igil&mi=DimensionConfigureAccountStructure



Before creating financial dimension you should know your dimension value combination sequence from accounting structure you have define for the journal.





2. create a container, which is used as a parameter for function contains dimension values in sequence of accounting structure.


First index of container contains main account number and subsequent index contains financial dimension values.

3.     AccountId        = conPeek(_conData,1);
        mainAccount
   = MainAccount::findByMainAccountId(AccountId);

The above lines are responsible for getting the main account buffer from accountid set on the first index of _conData which holds account id and dimension values.

4.     recordvalue   = DimensionHierarchy::getAccountStructure(mainAccount.RecId,Ledger::current());

The recordvalue holds the accounting structure buffer which has been define on main account you mention in _conData, which is helpful while creating default dimensions in ledger journal.

5. hierarchyCount   = DimensionHierarchy::getLevelCount(recordvalue);
    DimensionSet      = DimensionHierarchyLevel::getDimensionHierarchyLevelNames(recordvalue);


DimensionHierarchy::getLevelCount return the total count of financial dimension used in accounting structure which has been define on main account including main account value.

DimensionHierarchyLevel::getDimensionHierarchyLevelNames returns the sequence names of financial dimension used in accounting structure  which has been define on main account including main account value.

Example: HierarchyCount = 3
                HierarchyName  = ['Main Account', 'Business Unit', 'Department']
                DimensionSet    = ["1135205","Centre","ATK"]

6. DimensionAttribute::findByLocalizedName(DimensionSet[hierarchyIdx],
                                                                            false,
                                                                            "en-us");

 The dimension value set in container used to get dimension attribute buffer, which is helpful in finding dimension attribute value buffer.

Note: In some case we are responsible to also create financial dimension values through code in D365, for which dimension attribute value is not initializes/recorded. If dimension attribute value is not set for some dimension we have to set create if necessary parameter as true to create record in dimensionAttributeValue table.

Like this:
dimensionAttributeValue = DimensionAttributeValue::findByDimensionAttributeAndValue(dimensionAttribute,
                                                                                                        conPeek(_conData,hierarchyIdx),
                                                                                                        false,
                                                                                                        true);


Run the below code for creating financial dimension values:  
dimensionAttribute      = DimensionAttribute::findByName(DimensionName);
 
if(dimensionAttribute.RecId)
{
       financialTag.initValue();

       financialTag.Description                       = "Description";
       financialTag.Value                                 = "42051";
       financialTag.FinancialTagCategory       = dimensionAttribute.financialTagCategory();


       if(financialTag.validateWrite())

       {
              financialTag.insert();
       }
 }

Now see below code for reference.

Run the below job with your specific set of dimension values and get the default dimension(Recid) for that combination:

/// <summary>
/// The method used to create a dimension dynamic account according to the financial dimesnion accounting structure
/// </summary>
/// <param name = "_conData">container constains all group record financial dimension in accouting structure sequences</param>
/// <returns></returns>

private DimensionDynamicAccount   generateLedgerDimension(container _conData)
{
    //Finanical Dimension Attribute.
    DimensionAttribute                                   dimensionAttribute;
    DimensionAttributeValue                          dimensionAttributeValue;
    DimensionSetSegmentName                     DimensionSet;
    DimensionStorage                                     dimStorage;
    DimensionAttributeValueContract            ValueContract;
    dimensionAttributeValueCombination      dimensionAttributeValueCombination;
    //Main Account details
    MainAccount                                             mainAccount;
    //table ref recid
    LedgerRecId                                              ledgerRecId;
    RefRecId                                                    recordvalue;
    //index count
    int                                                               hierarchyCount;
    int                                                               hierarchyIdx;
    //Main Account
    Str                                                              AccountId;

    //Account Contract.
    LedgerAccountContract LedgerAccountContract = new LedgerAccountContract();
    List                                 valueContracts                = new List(Types::Class);
       
    AccountId                = conPeek(_conData,1);
    mainAccount           = MainAccount::findByMainAccountId(AccountId);
    recordvalue              = DimensionHierarchy::getAccountStructure(mainAccount.RecId,Ledger::current());
    hierarchyCount        = DimensionHierarchy::getLevelCount(recordvalue);
    DimensionSet          = DimensionHierarchyLevel::getDimensionHierarchyLevelNames(recordvalue);

    for(hierarchyIdx = 1;hierarchyIdx<=hierarchyCount;hierarchyIdx++)
    {
        dimensionAttribute = DimensionAttribute::findByLocalizedName(DimensionSet[hierarchyIdx],
                                                                            false,
                                                                            "en-us");

        if(dimensionAttribute)
        {
            if(hierarchyIdx <= conLen(_conData))
            {
                dimensionAttributeValue = DimensionAttributeValue::findByDimensionAttributeAndValue(dimensionAttribute,
                                                                                                        conPeek(_conData,hierarchyIdx),
                                                                                                        false,
                                                                                                        true);
            }
            else
            {
                dimensionAttributeValue = DimensionAttributeValue::findByDimensionAttributeAndValue(dimensionAttribute,
                                                                                                        conPeek(_conData,hierarchyIdx));
            }
            if(dimensionAttributeValue)
            {
                ValueContract = new DimensionAttributeValueContract();
                ValueContract.parmName(dimensionAttribute.Name) ;
                ValueContract.parmValue(dimensionAttributeValue.CachedDisplayValue);
                valueContracts.addEnd(ValueContract);
            }
        }
    }

    LedgerAccountContract.parmMainAccount(AccountId);
    LedgerAccountContract.parmValues(valueContracts);
    dimStorage                          = DimensionServiceProvider::buildDimensionStorageForLedgerAccount(LedgerAccountContract);
    dimensionAttributeValueCombination  = DimensionAttributeValueCombination::find(dimStorage.save());
    ledgerRecId                         = dimensionAttributeValueCombination.RecId;

    return ledgerRecId;
}
 

Monday, August 6, 2018

Microsoft Dynamics 365 - Iterator over non-system fields of a table in AX



In development phase, many case leads developer to iterator overall/some non-system field of a table to perform some computational on it. Iteration over field also include some systems fields like RecId, Createddatetime etc.

Before look into complete code. We must have to understand some terminology/classes before development.

What are the objects Dict*?

Dict objects are generally used for creating an object of related type (for DictClass e.g. CustBalanceList class), getting information about that object (for DictClass e.g. is class defined Abstract, Final or is Runnable {has main method}) etc.

DictClass

Object can be constructed by  “new” word (Params: ClassId). Mostly and effectively used for creating a child class (if we know classId of it).
SysDictTable dictTable = new SysDictTable(currentTable.TableId);

DictField

Object can be constructed by  “new” word (Params: TableId, FieldId). Mostly used for finding basetype or label etc of any table field.
SysDictField dictField;

FieldId

FieldId is the unique identifier for field present in a particular table, used to store field value.
FieldId fieldId = dictTable.fieldNext(0);

Code - Iterator over non-system fields of a table in AX
SysDictTable dictTable = new SysDictTable(currentTable.TableId);
SysDictField dictField;
FieldId fieldId = dictTable.fieldNext(0);
while (fieldId)
{
    dictField = dictTable.fieldObject(fieldId);
    if(dictField.isSql() && !dictField.isSystem())
   {
        //write your code here
        info(strFmt("%1: %2", dictField.name(),currentTable.(fieldId)));
    }
    fieldId = dictTable.fieldNext(fieldId);
}

Friday, August 3, 2018

Microsoft Dynamics 365 – Data entity for a table without a natural key



What is Data Entity?
Data Entity provides an abstraction from physical implementation of database table and denormalized the database table’s schemas to represent key data concepts and functionalities known as encapsulation. Data entity overcome a huge gap between business needs and Dynamics 365 to makes development and integration even easier than before.

Data Entities Capabilities

Data Entities Sample Examples
If a consumer wants to access data related to a customer object, currently data is scattered across multiple normalized tables. This makes reading and writing customer data very tedious. Instead, the customer entity can be designed to encapsulate all the underlying physical schema into a single de-normalized view.





Supported Scenarios
Data entities support the following scenarios:
A.            Integration Scenarios
1.            Synchronous service (OData)
Entity will allow exposing public API on entities to enable synchronous services. Synchronous services will be utilized for:
             Office integration
             Third party mobile apps
2.            Asynchronous integration
Entity will also support asynchronous integration through data management pipeline. This will enable asynchronous as well as high performing data insertion and extraction scenarios. Examples include:
             Interactive file based import/export.
             Recurring integrations (such as file, queue)
3.            Business Intelligence
             Aggregate data.
             Standardized key performance indicators (KPIs).
Categories of entity
Entities are categorized into various buckets based on their functions and type of data that they serve. The following are five categories for a data entity.
Parameter
             Functional or behavioral parameters.
             Required to set up a deployment or a module for a specific build or customer.
             May include data that is specific to an industry or business. It could apply to broader set of customers.
             Tables which contain only one record, where the columns are values for settings.
Examples of these tables exist for Account payable (AP), General ledger (GL), client performance options, workflow, and more.
Reference
             Simple reference data, of small quantity, required to operate a business process.
             Data that is specific to an industry or to a business process.
Examples include units, dimensions, and tax codes.
Master
             Data assets of the business. Generally these are the nouns of the business which typically fall into categories of people, places, concepts, etc.
             Complex reference data, of large quantity.
Examples include customer, vendor, project, etc.
Documents
             Worksheet data which is converted later into transactions.
             Documents with complex structures, such a several line items for each header record.
Examples include sales order, purchase order, open balances, journals, etc.
             The operational data of the business.
Transactional
             The operational transaction data of the business.
             Posted transaction. These are non-idempotent items such as posted invoiced and balances.
Typically these are excluded during a full dataset copy.
Examples include pending invoices

Problem Definition
The Data entities are responsible for doing many things in d365. It’s very easy to create a data entity in Microsoft dynamics 365 by right clicking on a table and selecting create data entity Add-ins option. However it’s really important to have a unique index in your table (Table RecId index is not considerable for unique index in data entity). If you don’t have a unique index you will get an error named as “The Natural Key not found”.
Following case has been shared with you to clearly understand the error getting in data entity, if unique index is not found.







If your table doesn’t contains a unique index you won’t be able to create a data entity because of systems requirement that every data entity must have a primary key defined. The primary key is used to keep track of existing data, if record is not found in the table by the help of key defined in that table so the record is inserted into staging table and to existing table and if record found on the table, record is selected for update and changes will reflected on existing tables.
To create a unique index for a non-unique index table is not a solution because it will cost you in production and will not be a flexible solution for you. This post contains information to overcome this problem with different techniques.

Solution for “The natural key not found” error in Microsoft dynamics 365 finance and operation data entity:
First Approach:
The first approach to resolve the error is to add an additional field to a table known as “Line Num” its help you to create a unique key without interrupting existing table schema. It’s an incremental value type of real, linenum variable get increment every time when a row is inserted into the table.


The problem is how to increment values every time when a record is inserted. For this we have to override insert methods and write a code to initialize linenum every time a record is inserted.


Note: If your table already contains data, you will get DB synchronization errors. To overcome this issue you need to run a manual SQL update script to update linenum value for all record with unique values.



Second Approach:
The Second approach is based on composite primary key, very often we have seen that in our data we have identify some columns in combination may create a unique key value pair for whole data in our table. The unique column in combination help us to identity each row of the input data consumed by a data entity.
Note: Before implementing this approach you have to take care of some important things which are as following:
1.       Combination of values in columns that correspond to the data entity primary key fields must be unique for each row of the incoming data
2.       Incoming data must not violate data integrity enforced by the updated data sources that a data entity is built on.










Third Approach:
The third approach is based on number sequence, as we all are familiar with number sequence. Number sequence are unique and continuous. We have to call next sequence value every time new row come into account. First of all, we have to create a number sequence as per your requirement.



Go to organization administrator -> Number Sequence -> Generate the new number sequence create from X++ Code.






After Generating Number Sequence, you have to write some chunk of code to get next number sequence for the new inserted data.


Override insert method, and write above code before creating data entity. Add field of type number sequence and add to index property in table. At last, create data entity for that table.



Approach Four:
This approach is very simple but not a feasible solution indeed. This solution is for sake of knowledge, do not try these solution for production. This solution is not recommend and guaranteed to give 100 percent result.
For this method you have to create a normal real number field data type in your table and add this field to the index. After than create data entity based on this field value.
Question: How this field we become unique and incremental?
The answer to this question is very simple. User/client is play a key role in this approach as they are sole responsible for keep track and add values in source file. For example. In excel we have to add a new column and manual assign a unique value to each row. For every new records/ update rows user have to maintain index by her own, that’s why this solution is not a feasible one.


Approach Five:
Third approach is based on the fact that a data entity primary key can be built on the primary data source RecId field. Standard data entity GeneralJournalAccountEntryEntity is a vivid example of applying such a technique.



Although that conceptually contradicts to the main data entity paradigm, the workaround may be useful when there is a need to prepare a data entity for data import/export without troubling to much about changing the primary data source table or manipulating with a set of fields for the data entity primary key.

Workflow - Bulk Action processing on workflow in Microsoft Dynamics 365

Problem Description: Some organization required to have ( approve/reject/request change ) documents/Vouchers through workflow in a bul...