Monday, April 22, 2019

Difference between changeCompany and CrossCompany in X++?

CrossCompany:- By using this keyword we can retrieve data from multiple companies or from all companies.
         
                 Example:-
                    CustTable cust;
                    Container con;
                    con=["ceu","Dat","cee"];
                    Select crosscompany:con cust;
                    print cust.accountnum;
                    pause;


                 Example:-
                    CustTable cust;
                    Select crosscompany cust;
                    print cust.accountnum;
                    pause;


ChangeCompany:- By using this keyword we can change company any time and retrieve data from specific companies.

                Example:-
                    CustTable cust;
                    Select *from cust;
                    print cust.accountnum;
                    changecompany("Cee")
                    {
                     cust=null
                    Select *from cust;
                    print cust.accountnum;
                     }
                    changecompany("Ceeu")
                    {
                     cust=null
                    Select *from cust;
                    print cust.accountnum;
                     }
                    pause;

Reference :- https://community.dynamics.com/ax/b/poojakarkiblogforax/archive/2014/12/22/crosscompany-and-changecompany-in-ax

Tuesday, April 9, 2019

Explaination on Dimensions

Dimensions are ways to tag each financial transaction so that you later can track and group them in a meaningful way. In this article we will dig deeper into what it is and how we can use them.

1. Business perspective

In the end every financial activity becomes a voucher transaction: every sales, production, invoice and salary.Voucher
From a legal perspective we keep track of all voucher transactions with main accounts (listed in the chart of accounts). But this is not enough, dimensions are needed to tag every transaction, so that we later can say that Department X have spent Y and earned Z.
In AX 2012, Main Account is one dimension and we can have unlimited dimensions. Common ones are Business unit and Department, but you can choose Customer or your own implemented dimension.
The dimensions used for each legal entity can be different and can be configured in Ledger setup (General ledger > Setup > Ledger) and Account Structures (General ledger > Setup > Chart of accounts > Configure account structures).

2. Technical terms

  • Dimension Attribute is the type of information that is traced. In the image above the attributes are MainAccount, BusinessUnit, Department and ServiceLine.
  • Dimension Attribute Value is the value of the attribute. In the image above the values are 140200, 004, 023 and Application Development.

Ledger dimension vs. default dimension

There are many ways to store dimension attribute values. The most common ways are ledger dimensions and default dimensions. This is used for instance in General journal (General ledger > Journals > General journal). The table below will explain it more detailed. Because the label, field names and EDT are not consistant (for its reasons), it is important that you get this right from the beginning, before we continue to elaborate all related concepts.
TermsLedger dimensionDefault dimension
Example
Label in formsAccount or Ledger AccountFinancial Dimension
Field nameLedgerDimensionDefaultDimension
EDTDimensionDynamicAccount, LedgerDimensionAccountDimensionDefault
Referenced tableDimensionAttributeValueCombinationDimensionAttributeValueSet
CommentsLedger dimension is an ordered list of dimension attribute values.Default dimensions unordered set of dimension attribute values. In the scenario of general journal lines, default dimensions are inherited from customers, vendors, journals and so on, all the way to the journal lines, but only if the account type is not Ledger. If the account type is ledger then only ledger dimension is enough.
Both ledger account and financial dimensions are stored as RecIds referencing to the tables listed above (Referenced table). The string in ledger dimension is a form control that gets the value from DimensionAttributeValueCombination.DisplayValue.
It is important to understand how the dimensions are used and what the differences are. In the final posted voucher only ledger dimension is used. But in the journal lines, default dimensions might be used. If the account type is Ledger then only ledger dimension will be used. But in all other cases Customer, Vendor, Fixed assets, Project and Bank - the default dimension will be used. The default dimensions will be combined with the main account into a ledger dimension. The strength with this is that the default dimensions can be set in the customer level and journal level and inherited to the journal lines.
Important patterns used in ledger dimension:
  • Default Account - Ledger Dimension, but with only one dimension attribute value, main account.
  • Dynamic Account - Similar to Ledger Dimension but it handles the dynamic usage as in journals. When a journal type is Ledger then it stores Ledger Dimension. But when the ledger type is for instance customer, then the dynamic account will have a lookup for customer accounts instead.
Lesser used terms:
  • Dimension Attribute Set - An unordered set for Dimension Attributes.
  • Dimension Set - Similar to Ledger Dimension but without requirement of Main Account. Mainly used for reports.

3. Data Model

There are a lot of tables that involves dimension. In this section we will focus on a few important ones and how we can use these.

Get display value from LedgerJournalTrans

In the table LedgerJournalTrans, the ledger dimension is stored as a RecId reference. To get the display value you need to get it from DimensionAttributeValueCombination.DimensionAttributeValue Data model
static DimensionDisplayValue getDisplayValue(LedgerJournalTrans _transaction)
{
    DimensionAttributeValueCombination combination;
    ;
    select DisplayValue from combination 
        where combination.recId == _transaction.LedgerDimension;
    return combination.DisplayValue;
}

Get specific attribute from a combination

This can be useful for instance in a reporting situation where you need to extract the BusinessUnit. In the example below we assume that you have used the previous code to get the DimensionAttributeValueCombination already.DimensionAttributeValue Data model
static DimensionDisplayValue getAttributeValueFromCombination(
    DimensionAttributeValueCombination _combination, 
    Name _attributeName = 'BusinessUnit')
{
    DimensionAttributeLevelValueView valueView;
    DimensionAttribute attribute = DimensionAttribute::findByName(_attributeName);
    ;
    select DisplayValue from valueView 
        where valueView.ValueCombinationRecId == _combination.recId 
        && valueView.DimensionAttribute == attribute.RecId;
    return valueView.DisplayValue;
}

Get main account from a combination

DimensionAttributeValue Data model
static AccountName getMainAccountFromCombination(
    DimensionAttributeValueCombination _combination)
{
    MainAccount mainAccount;
    ;
    select mainAccount where mainAccount.RecId == _combination.MainAccount;
    return mainAccount.MainAccountId;
}

Get specific attribute from customer

DimensionAttributeValue Data model
static DimensionDisplayValue getAttributeValueFromCustomer(
    CustTable _custTable, 
    Name _attribute = 'BusinessUnit')
{
    DimensionAttribute  attribute = DimensionAttribute::findByName(_attribute);
    DimensionAttributeValueSetItemView valueSetItemView;
    ;
    select DisplayValue from valueSetItemView 
        where valueSetItemView.DimensionAttributeValueSet == _custTable.DefaultDimension 
        && valueSetItemView.DimensionAttribute == attribute.RecId;
    return valueSetItemView.DisplayValue;
}

4. Service class for integration

The are many service classes that can be used to handle the dimensions. These classes can be used from both X++, C# using proxies and through AIF. Very useful in other words. The first time to use them might take some time but once you recognize the pattern it is not difficult to guess how to utilize them. The code below is one example for creating a dimension attribute value.
DimensionValueService service = new DimensionValueService();
DimensionValueContract contract = new DimensionValueContract();
;
contract.parmDimensionAttribute('Department');
contract.parmValue('HR')
contract.parmDescription('Human Resources');
service.createDimensionValue(contract);
Because the service classes can be used in both AIF and in code so easily. I recommend that you at least read through the following list at least once, so that you remember to use them when the opportunity arises. Notice that this is not a complete list of all service classes and methods, but a chosen few that believe are useful.
ChartOfAccountsService
  • createMainAcccount(MainAccountContract _contract)
  • getLedgerChartOfAccounts()
  • getLedgers()
  • getMainAccounts(LedgerChartOfAccountContract _ contract)
DimensionService
  • getListDimensionsAll()
  • getDimensions(AccountStructureContract _contract)
DimensionValueService
  • createDimensionValue(DimensionValueContract _contract)
  • getDimensionValues(DimensionContract _contract)
FinancialDimensionValidationService
  • validateLedgerAccount(LedgerValidationContract _contract)
FinancialDimensionBalanceService
  • getBalance(DimensionSetContract _contract)

5. Useful classes

If you can't find your solution in the service classes the following classes will probably do that.
  • DimensionStorage
  • DimensionDefaultingEngine
  • DimensionDefaultingService
  • DimensionAttributeValueSetStorage
For instance we can use this to create a LedgerDimension in this way:
//Example usage
//createLedgerDimension('110110', 'Service Industries B/S', '003', '022');
public static LedgerDimensionAccount createLedgerDimension(
    MainAccountNum _mainAccountId, 
    Name _accountStructureName, 
    Name _businessUnit, 
    Name _department)
{
    MainAccount mainAccount;
    DimensionHierarchy hierarchy;
    DimensionAttributeValueSetStorage storage = new DimensionAttributeValueSetStorage();
    ;
    mainAccount = MainAccount::findByMainAccountId(_mainAccountId);
    hierarchy = DimensionHierarchy::findByTypeAndName(
        DimensionHierarchyType::AccountStructure, _accountStructureName);
    storage.addItem(DimensionAttributeValue::findByDimensionAttributeAndValue(
        DimensionAttribute::findByName('BusinessUnit'), _businessUnit));
    storage.addItem(DimensionAttributeValue::findByDimensionAttributeAndValue(
        DimensionAttribute::findByName('Department'), _department));
    return DimensionDefaultingEngine::getLedgerDimensionFromAccountAndDim(
        mainAccount.RecId, hierarchy.RecId, storage.save());  
}

6. Creating a new dimension attribute

Creating a new dimension attribute is very simple. First create new view called DimAttribute<name>, in this example we create DimAttributePartyTable. Add the table, and three fields: Key (PartyTable.recId), Value (PartyTable.PartyId) and Name (PartyTable.Name)
Notice that for Value only 30 chars will get stored. So in the case for PartyTable the PartyId will get truncated if we don't shorten the PartyId EDT. Also the Name field is limited to 60 chars. But truncated names are lesser sensitive than the Value.
DimAttributePartyTable
After refreshing the cache (Tools > Cache > Refresh Elements). We can add the new attribute to a structure. In the form Configure account structures click the Add segment you will see your new dimension attribute.Create financial dimension


Source : http://axmasterclass.com/blog/financial-dimensions-deep-dive/

How to create a Dialog form by extending Runbase class

class CustCreateDialog extends RunBase
{
    DialogField fieldAccount;
    DialogFIeld fieldGroup;

    CustTable   custTable;
    CustTrans   custTrans;
    CustAccount custAccount;
    CustGroupId custGroup;
}

--------------------------------------------------------------------------------------------------------------------------

protected Object Dialog()
{
    Dialog dialog;
    ;

    dialog = super();

    // Set a title for dialog
    dialog.caption( 'Simple Dialog');

    // Add a new field to Dialog
    fieldGroup   =  dialog.addField(extendedTypeStr(CustGroupId), 'Customer Group');
    fieldAccount = dialog.addField(extendedTypeStr(CustAccount),'Customer account');

    return dialog;
}

-------------------------------------------------------------------------------------------------------------------------

public boolean getFromDialog()
{
    // Retrieve values from Dialog
    custAccount = fieldAccount.value();
    custGroup   = fieldGroup.value();
    return super();
}


-------------------------------------------------------------------------------------------------------------------------

public void run()
{
    while select AccountNum, CustGroup from custTable
        where custTable.AccountNum == custAccount
            && custTable.CustGroup == custGroup
        join Voucher, AmountCur, TransDate from custTrans
            where custTrans.AccountNum == custTable.AccountNum
       
    if (custTable)
    {
        // Shows retrieved information
        info( strFmt('%1 -- %2 -- %3 --- %4 ---%5' , custTable.AccountNum, custTable.name(), custTrans.Voucher, custTrans.AmountCur, custTrans.TransDate));
    }
    else
    {
        error( 'Customer Account not found!');
    }
}

--------------------------------------------------------------------------------------------------------------------------

public static void main(Args _args)
{
    CustCreateDialog custCreate = new CustCreateDialog();

    // Prompt the dialog, if user clicks in OK it returns true
    if (custCreate.prompt())
    {
        custCreate.run();
    }
}

Tuesday, April 2, 2019

How to filter the grid based on the control value in X++

Under data source Execute Query we can pass the Query range with the control value as mentioned below

public void executeQuery()
{
    this.query().dataSourceTable(tableNum(CICVendorCustomerCreation)).addRange(fieldNum(CICVendorCustomerCreation, CustomerCreated)).value(SysQuery::value(CustomerCreated.value()));
    super();
}

On control modified we can call the data source execute query as mentioned below

public boolean modified()
{
    boolean ret;
    CICVendorCustomerCreation_DS.query().dataSourceTable(tableNum(CICVendorCustomerCreation)).clearRanges();
    CICVendorCustomerCreation_DS.executeQuery();
    ret = super();
    return ret;
}

How to enable the dimension fields based on the Item selected on the form.

[Form] public class KMTShipFromWarehouses extends FormRun {     InventDimCtrl_Frm_EditDimensions        inventDimFormSetup;     /// ...