Friday, September 4, 2020

Get Menu item path on D365FO Menu in excel

 Hi

At times during designing security architecture for our D365FO implementation we need list of all entry points(Menu item path) on our D365FO menu in an excel file. So that, we could plan the access to them a lot easier. To fulfill this purpose we could use the script written below in D365FO runnable class and get the output in excel for later use.

Script:

using System.IO;

using OfficeOpenXml;

using OfficeOpenXml.Style;

using OfficeOpenXml.Table;

using Microsoft.Dynamics.AX.Metadata.MetaModel;

class GetMenuItems

{

    public static void main(Args _args)

    {

        SysDictMenu      _menu;

        str              formName;

        str             menuItemName;

        int             currentRow;

        MemoryStream    memoryStream = new MemoryStream();

        boolean iterateMenus(SysDictMenu _cmenu, OfficeOpenXml.ExcelRange cells, OfficeOpenXml.ExcelRange cell)

        {

            SysMenuEnumerator menuEnum = _cmenu.getEnumerator();

            SysDictMenu subMenu;

            SysDictMenu parentSubMenu;

            boolean found;

            while (menuEnum.moveNext())

            {

                currentRow ++;

                subMenu = menuEnum.current();

                if (subMenu.isMenu() || subMenu.isMenuReference())

                {

                    if(subMenu.isMenuReference())

                    {

                        cell = cells.get_Item(currentRow, 1);

                    }

                    else

                    {

                        cell = cells.get_Item(currentRow, 2);

                    }

                    cell.set_Value(subMenu.label());

                    cell = null;

                    found = iterateMenus(subMenu,cells,cell);

                    if (found)

                    {

                        return found;

                    }

                }

                else if ((subMenu.isMenuItem()&& subMenu.menuItem().type() == MenuItemType::Display && subMenu.isVisible())

                              || (subMenu.isMenuItem()&& subMenu.menuItem().type() == MenuItemType::Action && subMenu.isVisible())

                    || (subMenu.isMenuItem()&& subMenu.menuItem().type() == MenuItemType::Output && subMenu.isVisible())

                              || (subMenu.isTileReference() && subMenu.isVisible()))

                {

                    cell = cells.get_Item(currentRow, 3);

                    cell.set_Value(subMenu.label());

                    cell = null;

                }

            }

            return false;

        }

        using (var package = new ExcelPackage(memoryStream))

        {

            currentRow = 1;

            var worksheets = package.get_Workbook().get_Worksheets();

            var menuItemWorksheet = worksheets.Add("Menu List");

            var cells = menuItemWorksheet.get_Cells();

            OfficeOpenXml.ExcelRange cell = cells.get_Item(currentRow, 1);

            System.String value = "Main menu";

            cell.set_Value(value);

            cell = null;

            value = "Sub menu";

            cell = cells.get_Item(currentRow, 2);

            cell.set_Value(value);

            cell = null;

            value = "Menu item";

            cell = cells.get_Item(currentRow, 3);

            cell.set_Value(value);

            _menu = SysDictMenu::newMenuName(menuStr(MainMenu));

            if(_menu)

            {

                iterateMenus(_menu,cells,cell);

            }

            package.Save();

            file::SendFileToUser(memoryStream, "Menu Item List.xlsx");

        }

    }

}

Monday, April 13, 2015

Hi DAX folks

Hope all DAX consultants are enjoying implementation of powerful, simple and agile ERP MS Dynamics Ax 2012.

This post subjects to get dimension values from LedgerDimensionAccount. This code is very helpful as often we require to get dimension values while dealing with reports in financial modules.

Let's have look at the code :

private void getDimensionDisplay(DimensionDynamicAccount  _dimensionDynamicAccount,
                                          FuturePerExpTable    _futurePerExpTable)

{
        DimensionStorage                    dimensionStorage;
        DimensionStorageSegment             segment;
        int                                 segmentCount, segmentIndex;
        int                                 hierarchyCount, hierarchyIndex;
        str                                 segmentName, segmentDescription;
        DimensionDisplayValue               segmentValue;
        // Get dimension storage
        dimensionStorage = DimensionStorage::findById(_dimensionDynamicAccount);
        if (dimensionStorage == null)
        {
            throw error("@SYS83964"); //wrong parameters specified.
        }

        // Get hierarchy count
        hierarchyCount = dimensionStorage.hierarchyCount();
        //Loop through hierarchies to get individual segments
        for(hierarchyIndex = 1; hierarchyIndex <= hierarchyCount; hierarchyIndex++)
        {
            setPrefix(strFmt("Hierarchy: %1", DimensionHierarchy::find(dimensionStorage.getHierarchyId(hierarchyIndex)).Name));
            //Get segment count for hierarchy
            segmentCount = dimensionStorage.segmentCountForHierarchy(hierarchyIndex);

            //Loop through segments and display required values
            for (segmentIndex = 1; segmentIndex <= segmentCount; segmentIndex++)
            {
                // Get segment
                segment = dimensionStorage.getSegmentForHierarchy(hierarchyIndex, segmentIndex);

                // Get the segment information
                if (segment.parmDimensionAttributeValueId() != 0)
                {
                    // Get segment name
                    segmentName = DimensionAttribute::find(DimensionAttributeValue::find(segment.parmDimensionAttributeValueId()).DimensionAttribute).Name;
                    //Get segment value (id of the dimension)
                    segmentValue        = segment.parmDisplayValue();
                    //Get segment value name (Description for dimension)
                    segmentDescription  = segment.getName();

                        if(segmentName == "L1_PGU")
                            _futurePerExpTable.PGU =  segmentValue;
                        if(segmentName == "L7_BusinessLocationCode")
                            _futurePerExpTable.BuisnessLocationCode =  segmentValue;
                }
            }

        }
    _futurePerExpTable.update();
}

This code takes ledgerDimensionAccount and the table where dimension are need to be stored for display, as parameters. We then used some of the system classes dedicated for dimension framework and looped through the financial dimension segments.

We could also make use of this code to get values of default dimension by using following method.

DimensionDefaultingService::serviceCreateLedgerDimension(RecId _ledgerDimensionId,
          DimensionDefault _defaultDimension).

This code takes an instance of ledger dimension and default dimension. This method returns the LedgerDimensionAccount to us which we can pass in our getDimensionDisplay() method and get the dimension values.

Hope this post would surely help you. In case of any query you could simply comment over here.

Happy DAXing :) 

Sunday, April 12, 2015

Hi All

Hope All Dynamics Ax consultants are doing good and rocking in implementations of powerful, simple and agile ERP MS Dynamics Ax 2012 :) .

This post by me subjects to populating data from some master in dimension values. You could also use the same logic in creating dimension values in migrations.

The code here was used in a job. Let's have a look


static void createBatchDim(Args _args)
    {
         OMOperatingUnit     operatingUnit;
         DimensionAttributeValueSetStorage   dimstorage;
         InventBatch         inventBatchTable,inventBatchTableDim;
         Name dimName = "L4_CostCenter";
         str 255 dimValue;
         DimensionAttribute   dimattributeContract;
         DimensionAttributeValue       dimAttributeValue;
         recId                           defaultDim;
        ;
        while select inventBatchTable
            where inventBatchTable.inventBatchId!=""
        {
            ttsBegin;
            select  operatingUnit
                where operatingUnit.OMOperatingUnitNumber == inventBatchTable.inventBatchId &&
                      operatingUnit.OMOperatingUnitType   == OMOperatingUnitType::OMCostCenter;
                if(!operatingUnit)
                {
                    operatingUnit.clear();
                    operatingUnit.OMOperatingUnitNumber = inventBatchTable.inventBatchId;
                    operatingUnit.initValue();
                    operatingUnit.OMOperatingUnitType = OMOperatingUnitType::OMCostCenter;
                    operatingUnit.Name = inventBatchTable.inventBatchId;
                    operatingUnit.insert();
 
 
                    dimStorage = DimensionAttributeValueSetStorage::find(InventTable::find(inventBatchTable.itemId).DefaultDimension);
                    dimattributeContract = DimensionAttribute::findByName(dimName);
                    dimValue = inventBatchTable.inventBatchId;
                    if(dimValue)
                    {
                         dimAttributeValue = DimensionAttributeValue::findByDimensionAttributeAndValue(dimattributeContract, dimValue, true, true);
                         dimStorage.addItem(dimAttributeValue);
                    }
                   else
                        dimStorage.removeDimensionAttribute(DimensionAttribute::findByName(dimName).RecId);
                        defaultDim =  dimStorage.save();
 
            }
                 ttsCommit;
        }
    }

Here I had to populate inventory batch as cost center dimension which is linked to General Ledger>Setup>Organization>Cost centers

So i just picked up the inventory batch which didn't exist in cost center master and made the available in dimension values of Cost center dimension.

Hope this post would surely help you. In case of any query you could simply comment over here.

Happy DAXing :) 

Contents

1.      INTEGRATION
2.      Integration with Microsoft Dynamics AX [AX 2012]
2.1         Services and Application Integration Framework (AIF)
2.1.1          Exposing Business Entities as Services
2.1.2          Common Usage Scenarios
2.2         .NET Business Connector
2.3         .NET Interop from X++ [AX 2012]
2.3.1          2.3.1 Proxy Classes for .NET Interop to X++ [AX 2012]
2.3.2          Integration with X++ Objects from Visual Studio [AX 2012]
                  Event Handlers


1.  INTEGRATION


Application integration (sometimes called enterprise application integration or EAI) is the process of bringing data or a function from one application program together with that of another application program. Where these programs already exist, the process is sometimes realized by using middleware, either packaged by a vendor or written on a custom basis. A common challenge for an enterprise is to integrate an existing (or legacy) program with a new program or with a Web service program of another company.

2.  Integration with Microsoft Dynamics AX [AX 2012]


There can be benefits to integrating your Microsoft Dynamics AX application with other technologies. One benefit is you can use other technologies to develop parts of your overall Microsoft Dynamics AX application. For example, you can write some of your functions in C# instead of in X++, and then integrate your Microsoft .NET Framework assembly into Microsoft Dynamics AX.
Another benefit from integration is the ability to coordinate your Microsoft Dynamics AX application with external applications. The external applications can be inside or outside the boundary of your enterprise. The functionalities of the two applications can interact, and data can be exchanged.

The inbound and outbound exchanges can be categorized in the following ways:

  • Send data – Microsoft Dynamics AX sends documents to an external system.
  • Send data in response to requests – Microsoft Dynamics AX receives requests for documents from another authorized system, and retrieves the requested information, such as a document or a list of documents, from the Microsoft Dynamics AX database. Microsoft Dynamics AX then returns the information to the requesting system, and the appropriate filtering and security are applied. The request message contains the entity keys or a query that specifies the data that the external system is requesting.
  • Receive and create data – Microsoft Dynamics AX receives documents from another authorized system and creates new records in the Microsoft Dynamics AX database.



Figure 21 Microsoft Dynamics Ax system architecture
You can integrate your Microsoft Dynamics AX application by using the following technologies:

2.1  Services and Application Integration Framework (AIF)


Exposes business logic to other internal or external systems. Microsoft Dynamics AX supports integration with the Application Integration Framework (AIF) components using the services programming model. Microsoft Dynamics AX can expose its functionality through services that are based on Windows Communication Foundation (WCF). Microsoft Dynamics AX code and external applications can consume Microsoft Dynamics AX services. AIF supports the processing of inbound and outbound messages. This processing includes message transforms and value lookups. Together, services and AIF provide the programming model, tools, and infrastructure support for XML-based integration with external applications and data.

To support a range of options for customization and programmability, Microsoft Dynamics AX provides the following types of services:
·         Custom services are services that you create to expose X++ logic through a service interface. You can use the business logic with inbound or outbound transfers.
·         Document Services represent data and business logic within Microsoft Dynamics AX. You can use or customize the over 70 standard Axd document services that are included with Microsoft Dynamics AX. If none of the standard document services meet your needs, you can create a new document service by using the AIF Document Service Wizard. Each document is represented by a class; the name of a document class is preceded by Axd. For example, AxdSalesOrder is the name of the document and also the name of the document class. The terms "document" and "Axd document" and "document class" are used interchangeably.
·         System services cannot be customized. The Query Service, Metadata Service, and User Session Service are WCF services included with Microsoft Dynamics AX. System services provide the following:
o    Access to data that is returned in AOT queries.
o    Metadata for AOT objects such as tables and extended data types (EDTs).
o    Data about the calling user, such as default language and Default Company.

Microsoft Dynamics AX enables you to consume external web services from X++ code, and to consume web services hosted by Microsoft Dynamics AX from .NET Framework languages such as Microsoft Visual C#.

Security for document services called from an external client is based on the role-based security that is used in Microsoft Dynamics AX.
Microsoft Dynamics AX supports application integration and data exchange in both intranet and Internet-facing scenarios. Services based on WCF classes are hosted on the AOS for applications to integrate within the intranet of a company. To consume or expose services over the Internet, you must install and use Internet Information Services (IIS).


Figure 22 Services and Application Integration Framework architecture

2.1.1          Exposing Business Entities as Services


Microsoft Dynamics AX installs with over 70 document services. The document services framework provides the AIF Document Service Wizard that generates a document service from the query that defines the business entity. To expose business logic through services, you use serialization with the Data Contract class and custom attributes.
The following table contains information that can help you to decide whether to use document services or custom data contract–based services.

Characteristic
Document Services
Custom Services
Entity complexity
The AIF Document Service Wizard handles queries of any complexity and size and generates the service seamlessly.
For example, some queries contain data sources and relationships that include dimensions, polymorphism, date-effective information, and so on. The document services framework handles these types of queries seamlessly.
Suitable when the entity complexity is low. The schema can be written as a data contract class and relevant data member attributes set.
If tables and relationships are used in a data contract, then any feature around dimensions, polymorphism, and so on, must be handled in code by the developer.
Performance requirements
The incoming XML is passed to the document services framework, which parses the XML to validate the AxD schema, and then invokes the relevant operation.
The framework enables customization to handle any complex pattern in the underlying query.
Custom services use the underlying .NET XML Serializer to serialize and de-serialize the XML into a data contract object. No other logic is present.
For simple entity schemas, this approach is faster than document services. For complex schemas, you may have to write a lot of custom code.
Integration requirements
All integration stack elements such as pipelines, transforms, and schema constraints can be applied to document services.
Schema constraints and value substitution are not honored for custom services.
However, transforms that convert between the AifXMLMessage format and other formats and pipelines for preprocessing and postprocessing of XML messages can be used.
Flexibility in service contracts
Because the document service is derived from a query, changes to the query object or the data source schema might require a change the service contract.
The tight coupling between the service contract and the underlying query-table schema could be limiting for certain scenarios.
The data contracts are written by developers and can be controlled to make sure that the underlying schema changes do not have any effect on them.
The control that a developer has in defining the service contracts can be good for service clients.
Microsoft Office Add-ins support
Office Add-ins tools, included with Microsoft Dynamics AX, have built-in support for consuming document services for updating data.
No preinstalled integration with Office add-ins exists.

2.1.2          Common Usage Scenarios


The following table summarizes some of the key scenarios for all the service types in Microsoft Dynamics AX.
Service Type
Common Scenarios
Custom Services
Use to expose very simple business entities as a service, or to expose custom logic from Microsoft Dynamics AX.
Document Services
Use to expose business entitles with varying degrees of complexity, or to support medium-to-complex integration scenario involving business entities.
System Service: Query Service
Use to implement read operations for any Microsoft Dynamics AX, ad-hoc, or existing AOT query.
System Service: Metadata Service
Use to retrieve information about the structures in Microsoft Dynamics AX for a client application. For example, if you want to return information about a table such as what indexes exist on that table, you can use the metadata service.
System Service: User Session Service
Use to retrieve information about the user session.



2.2  .NET Business Connector


NET Business Connector is a component of the Microsoft Dynamics AX development environment. .NET Business Connector enables you to build software applications that integrate with Microsoft Dynamics AX. You can access data or start business logic. The advantage of using .NET Business Connector over other types of integration is that you use the same X++ code and business logic available to clients. You will also be able to use the Microsoft Dynamics AX security model.
Use the .NET Business Connector to interpret and execute code. It provides a run-time environment for interacting with elements in the Application Object Tree (AOT). The .NET Business Connector enables applications to interact with Application Object Server (AOS) instances by providing a set of .NET managed classes. These classes enable access to X++ classes in Microsoft Dynamics AX.
The .NET Business Connector can be installed as a stand-alone component. It can also be used to develop third-party applications that integrate with Microsoft Dynamics AX.
To support integration with Microsoft Windows SharePoint Services and to enhance product security, the .NET Business Connector requires Windows integrated authentication.

2.3  .NET Interop from X++ [AX 2012]


Classes in assemblies that are managed by the common language runtime (CLR) can be accessed in X++ code. This feature of Microsoft Dynamics AX is called .NET interop from X++.
.NET interop from X++ is useful when you want your X++ code to access the functionalities in a CLR managed assembly. This includes assemblies that are installed with the .NET Framework. It also includes any assemblies that you create with a language such as C# or Visual Basic.
The .NET interop from X++ feature works in the direction from X++ calling into CLR managed assemblies. For information about .NET interop in the other direction, see the following topics:

2.3.1    Proxy Classes for .NET Interop to X++ [AX 2012]


A proxy class is a .NET Framework class that provides access to one X++ item type of Microsoft Dynamics AX. You can build proxies for the following X++ item types:
  • Classes – both application and system classes.
  • Tables – both application and system tables.
  • Enums – both base and system enums

Proxies are usually a better alternative to .NET Business Connector. .NET Business Connector requires your C# code to use a late-bound programming model. Proxies require an early-bound programming model. The early-bound model enables the code editor to provide IntelliSense. The early-bound model also enables the compiler to catch misspellings and other errors before the code is run.

2.3.2    Integration with X++ Objects from Visual Studio [AX 2012]


The two features in Microsoft Dynamics AX that support managed code and X++ interop are proxies and event handlers.

2.3.2.1  Proxies

Proxies enable you to add an AOT element to your project in Visual Studio so that element can be accessed by managed code. When you add an AOT element to a project by using Application Explorer, a proxy for that element is created internally by the system.
The AOT elements that you can add to a managed code project are as follows:
·         Classes
·         Tables
·         Enums
After you add the AOT element to your managed code project, all the methods and properties are available through IntelliSense.
A typical scenario for adding an X++ object to managed code is when you identify managed code functionality that you want to call from X++. This functionality may already be located in managed code or the development scenario may require that it run in managed code.
For example, you might want to have your Microsoft Dynamics AX installation updated with customer data from an external system. In this case, you create an X++ job that is scheduled to run periodically and calls a managed code class method. This method could then check for the external data and then call the appropriate CustTablemethods to update the customer data (see the following diagram). Because the managed code that you write runs in the Microsoft Dynamics AX process, that code will typically be in a class library project.
The following diagram provides a high-level view of the integration of X++ and managed code. The initial call from X++ to managed code can originate from either an X++ .NET interop call or from an event handler.

Figure 23 X++ and managed code integration

2.3.2.2    Event Handlers

In the AOT, you can associate a class method with an event handler. An event handler is code that runs before the associated method runs or after the associated method has finished running. The event handler itself is also a class method, and it can be written in either X++ or managed code.
For example, you may have an X++ class method called MyClass.myMethod, and you have managed code that you want to run after the MyClass.myMethod method has finished running. In this case, you would create an event handler class and method in managed code, such as MyEventHandlerClass.PostmyMethod. This event handler contains the code that will execute after the MyClass.myMethod method is called.
When you add an event handler in Visual Studio, the system automatically creates a method that begins with either “Pre” (for event handlers that run before the method runs) or “Post” (for event handlers that run after the method completes). An event handler subscription is also automatically added to the MyClass.myMethod method. In addition, the properties of that subscription are set to point to the managed code class method. After the MyClass.myMethod method has finished running, then theMyEventHandlerClass.PostmyMethod event handler code executes. Event handlers can only be associated with a class in the AOT Classes node.
The following diagram illustrates how a managed code post-event handler is called. You can create the managed code class method and configure the event handler in the AOT to call that class method all from Visual Studio.


Figure 24 Managed code post-event handler flow