Quantcast
Channel: Microsoft Dynamics AX - Technical
Viewing all 126 articles
Browse latest View live

“No device” error on AX Client startup

$
0
0
Hi EveryoneJ!!


Today I came across one weird issue while opening AX2009; it was never an issue before.



This error message is caused by the ActiveX component for the phone integration on form smmPhone which returns an error for each phone device which is offline. 

In order to prevent the error message, just edit the code in smmForm, here is the path
\Forms\smmPhone\Designs\Design\ActiveX:axPhone\Methods\onEvent_TapiError

What is TAPI??

TAPI (Telephony Application Programming Interface) is a Microsoft Windows standard interface for integration between telephone systems and windows based software. A typical example is integrating called ID with a database on your computer that contains detailed information about potential callers. When your phone rings, a window pops up
on your computer with information about the caller. This is possible through software applications that are TAPI compliant and support the Screen Pop and Auto-Dialer capabilities.

You can do the following code changes to prevent the error message: 

// AOSRunMode::Client
void onEvent_TapiError(int errorCode)
{;
    if (errorcode != -2147483582)
    error(this.errorMsg(errorCode));
}

Happy Daxing J




Form open through X++ code / Go to main table form

$
0
0

Hello guys J let me cover a very short topic on how to open a form in MS Dynamics Ax by code.

Sometimes it is very essential to open a form (or) intermediate form through code in AX. Or when we implement the functionality of “Go to main table”, we can use X++ code to open the form.

In the direct example, all it takes is one line of code.

new MenuFunction(MenuItemDisplayStr(CustTable),MenuItemType::Display).run();

The above code will open the CustTable form. This is as simple as it looks.

Now if we want to supply some arguments to the opening form, this is also possible with the optional args parameter.

Let us observe this with quick example:

static void FormopenthroughCode()
{ Args args = new Args();
;
args.record(CustTable::find('CUS-0001'));
new MenuFunction(MenuItemDisplayStr(CustTable),MenuItemType::Display).run(Args);
}

This code will open the CustTable form and filter out the customer with accountnumber CUS-0001.

Use the args methods like parm and parmEnum to provide your target form with more data.

Also there are other ways by which we can open form through code:
This next example gives the same result as the previous one.

static void OpenForm ()
{ FormRun formRun;
Args args = new Args();
;
args.name(formstr(CustTable));
args.record(CustTable::find(' CUS-0001'));

formRun = ClassFactory.formRunClass(args);
formRun.init();
formRun.run();
formRun.wait();
}

Also, when you want to assign “Go to main table form” for any string you can directly override the ‘JumpRef’ method and simply write the code in it as:
Args args = new Args();
;
args.record(CustTable::find('CUS-0001'));
new MenuFunction(MenuItemDisplayStr(CustTable),MenuItemType::Display).run(Args);

(or)

SalesTable           sales;
Select firstonly sales
        where sales.SalesId         == SpecTransDetails.JournalNum; // your range from form
    if (sales)
    {

        args.record(salestable::find(SpecTransDetails.JournalNum));
        new MenuFunction(MenuItemDisplayStr(SalesTable),MenuItemType::Display).run(Args);
    }



Thus, by writing the above code in Jumpref method allows you to go to its master form.

Keep reading!! J




To fetch IP address of system through X++

$
0
0

static void FetchIpAddress (Args _args)
{
System.String                       hostName = System.Net.Dns::GetHostName();
System.Net.IPHostEntry              hostEntry = System.Net.Dns::GetHostEntry(hostName);
System.Net.IPAddress[]              addresses = hostEntry.get_AddressList();
System.Net.IPAddress                address;
System.Net.Sockets.AddressFamily    addressFamily;
System.Collections.IEnumerator      enumerator = addresses.GetEnumerator();

while (enumerator.MoveNext())
{
    address = enumerator.get_Current();
    addressFamily = address.get_AddressFamily();
    if (addressFamily == System.Net.Sockets.AddressFamily::InterNetwork)
    {
        info(address.ToString());
    }
}
}

Copying data from notepad to Dynamics AX

$
0
0

Hi friendsJ, Let me discuss a very simple customization wherein you would be required to fetch or bring the data which lies in your copy (ctrl + c).

Let’s say in your notepad there is data “MS Dynamics AX2012”
Now just copying this data through ctrl +c and my requirement is to get this data somewhere in AX form.

static void ToGetDataLiesinCopy(Args _args)
{
    str         ret;
    TextBuffer  tb;
    ;

    tb = new TextBuffer();
    tb.fromClipboard();

    ret = tb.getText();

    info(ret);

}

This is just a dummy example; there can be a situation wherein you need range from notepad to AX forms like “itemid” range or customer range, so this example would suit your requirement. I’ll be back with some more. Thanks for Daxing JJ

Failed to create session in MSD AX 2012.

$
0
0

Hello J I found one more strange issue today, when I tried creating SO (Sales order) an error prompts:

Failed to create a session; confirm that the user has the proper privileges to log on to Microsoft Dynamics.

Also I heard error arrives many times at different times in AX 2012 like when processing journal, Creation of purchase order, catalog etc...

And here is the solution:

The solution is to unchecka checkbox in Options at
Tools > Options > Development > General > Execute business operations in CIL

Purpose of this checkbox (“Execute business operations in CIL” or not?)
In AX 2012, steps are taken by the development team at Microsoft to align AX IDE with .NET. That’s why you regenerate the CIL after X++ code changes.

CIL obviously has its benefits like faster execution in cases of heavy logic processing.
But sometimes while testing or debugging, developers would prefer NOT to avail this option. Debugging can be cumbersome in AX 2012 as you will need to configure Visual Studio for code running in CIL.
Now even the code which was running in CIL will now run in X++ interpreted mode, the old way.
Word of caution, this option should only be used by developers while debugging. It should ideally be checked again after work is finished.

GlobalCache / Alternative to GlobalVariables in X++

$
0
0

Many times because of flawed implementation designs, we often need global variables. We may use a table with a key field and a container field for this purpose, but the simplest way of doing this will be using a Global Cache.

The SysGlobalCache class is used by Axapta to store "global" variables and object references. It is implemented as a standard class in the AOT and uses a simple caching mechanism involving nested Map.
It is possible to create and use SysGlobalCache objects to implement your own local caching, or to use the instances automatically available through infolog.globalCache() (client-side) andappl.globalCache() (server-side).

A global cache is an instance of class - SysGlobalCache, which is nothing but a Map containing Maps with Userid as Key. These Maps contain the actual value and a key.
In Ax, we have three(infact 4) Global Caches - Infolog.globalCache(), Appl.globalCache(), ClassFactory.GlobalCache().

How to use:

To Set a Value:

static void GlobalCacheSet(Args _args)
{
SysGlobalCache globalCache;
;
globalCache = ClassFactory.globalCache();
globalCache.set(curuserid(), 1, "One");

To Get the Value:

static void GlobalCacheGet(Args _args)
{
SysGlobalCache globalCache;
;
globalCache = ClassFactory.globalCache();
print globalCache.get(curuserid(), 1);
globalcache.remove(curuserid(), 1);
pause;
}

In the above the example, we can also use Infolog.globalCache() or Appl.globalCache().


Why do we have three Caches?

Its simple, the term "Caching" comes when there is something to do with Performance.

Infolog Object resides in Client and Application Object resides in Server.
To share your global variable across the network and accept the performance penalty of a client/server call, use the infolog variable (Info class) or appl variable (Application class) instead of ClassFactory.

ClassFactory is a special class, which has instances residing at both server and client. At run time, two instances of the ClassFactory class exist, and they share name classFactory. However confusing the implementation details might sound, this is a powerful concept. When you call a method on classFactory from code running on the client, you are calling the classFactory object on the client tier; when you call a method on classFactory from code running on the server, you are calling the classFactory object on the server tier. Remember this when debugging the ClassFactory class.

Happy Daxing  J

How to use Event Handler in Microsoft Dynamics AX 2012

$
0
0


Microsoft incorporated lot of beautiful new features in Dynamics AX 2012 and one of them is Event Handler. It’s a very nice feature of Dynamics AX 2012 which allows you trigger an event after or before an activity.

So today am going to tell you about these features:

Ø  In Microsoft Dynamics AX 2012 how to handle event on different methods / occurrences.

Ø  In Microsoft Dynamics AX 2012 how to implement or apply pre or post event handler.

Ø  How to develop an event handler step by step in Microsoft Dynamics AX 2012.

Ø  What is new in Microsoft Dynamics AX 2012 from programming point of view?

Ø  X++ Event handling in Dynamics AX 2012.


You should invoke events over using pre or post events on methods.

Pre-Event Handlers and Post-Event Handlers

An event handler can reside underneath a method node can run either before or after the method runs. You can use CalledWhen property on the event handler node. The CalledWhen property has two values:

• Pre – The event handler runs before the method starts.
• Post – The event handler runs after the method ends.

A new class the XppPrePostArgs Parameter is being used by event handlers.

A pre-method event handler that has only an XppPrePostArgs parameter can inspect and change the values of the parameters.

Similarly a post-method event handler that has only an XppPrePostArgs parameter can inspect and change the return value from the method.

When an XppPrePostArgs object is used, the values of the parameters and the return type can be changed by the event handler. The values can be changed even if the parameters and return type are value types, such as an integer or string. If a parameter or a return type is a reference to an object, the event handler can call methods on the object and could change the state of the object.

Event handlers can run only on the same tier as the publisher class of the delegate runs on. For instance, if the publisher class has its RunOn property set to Server, to declare a method with client keyword and call a subscribed event handler method is not allowed.

You can use X++ event as well as .Net made managed code event. You can define it at Event handler proper called EventHandlerType.

How to use event handlers in Microsoft Dynamics AX 2012 step by step:

1) Open your new developer work space and go to AOT then classes node.

2) Right click on Classes node and click on New Class as shown below. 





3) By default system will give a name to it. Here in my case it’s Class1. Right click on newly created class and click on Rename shown below.






4) After clicking Rename, give a name called CustTableEventHandler to it. Here I am going to develop an event to be applied on CustTable so that is the reason why I decided this name (CustTableEventHandler). After renaming this class, it looks as shown below.





5) Right click on class CustTableEventHandler then New then Pre- or post-event handler as shown below.




6) Once you click on this, system gives you a method as shown below. 






7) Customize the method as shown below. 






8) Here args is providing current record to custTable instance and info is displaying the current customer account. The code snippet is below.

public static void custCreateInfo(XppPrePostArgs _args)
{
     CustTable custTable;
     custTable = _args.getThis();

     info(strFmt("Customer account %1 has been created", custTable.AccountNum));
}

This method I support to call from insert method of CustTable with type post event. It means that once insertion is done to CustTable, system will display recently inserted customer account number. It depends on your business requirement what logic you want to apply here. So here you can develop your required business logic or invoke pre built logic.

9) Go to Tables node in AOT then find out CustTable.






10) Go to insert method of CustTable and right click on it then click on New Event Handler Subscription as shown below.





11) After clicking you will get a new Event handler shown below. 





12) Rename the event handler to custCreateInfo and set the property as shown below. 




13) Now save your work.

14) Go to Customer form and create a new customer. Here I created a new customer account called “Test-000001”. 






15) Once this customer is created system will give your infolog as shown below.





Hope this will help you to understand the event handler in Microsoft Dynamics AX 2012.

Happy Daxing J



CrossCompany keyword in X++

$
0
0


Friends,

Going to discuss a simple topic which sometimes play vital role in MSD AX.
CrossCompany is the Keyword to get the data from another company.

In X++, there is a new keyword crossCompany you can use in a select statement. Additionally, you can add a container with all the company ID’s you wants to include in your select statement.
So, your select statement could look like that:
While select CustTable crossCompany : [Test, DAT]
{
... Your Code...
}
(Or)

static void CrossCompany_testing(Args _args)
{
CustTable custTable;
;

while select crossCompany * from custTable
{
print custTable.AccountNum,' : ',custTable.Name,' : ',custTable.dataAreaId;
}
pause;

}
And let me also show you simple example wherein you can copy your data from one company to another company just in a simple click. So here is the code which you can implement wherever necessary.
This code will generate the dialog which asks for user input for ‘fromCompany’ to ‘Tocompany’.
static void CompanyCopyTable(Args _args)
{

    Dialog          dialog;
    DialogField     FromCompany, ToCompany;
    str             FromComp,ToComp;
    CustTable       custTable1, custTable2;
    Args            args = new Args();
    ;

    dialog = new Dialog("CrossCompanyExample");

    dialog.addText("Select the source company:");

    FromCompany = dialog.addField(typeid(dataAreaId));

    dialog.addText("Select the destination company:");

    ToCompany = dialog.addField(typeid(dataAreaId));

    dialog.run();

    if(dialog.closedOk() == true)
    {

        FromComp = FromCompany.value();
        info(FromComp);
        ToComp = ToCompany.value();
        info(ToComp);

        changecompany(FromComp)
        {
            while select CustTable1
            {
                changecompany(ToComp)
                {
                    custTable2 = null;
                    buf2buf(CustTable1, custTable2);
                    if (!custTable2.validateWrite())
                    {
                        throw Exception::Error;
                    }
                    custTable2.insert();

                }
            }
        }
    }

Error: “Request for the permission of type ‘InteropPermission’ failed.”

$
0
0

Many times we would like to call method from dot net assemblies (or) com so we have to assign permission to execute the code .below are ways can be used for CLR and COM

Assign permissions to execute the code (CLR Interop). Like this:

InteropPermission permission = new InteropPermission(InteropKind::ClrInterop);
;
permission.assert();

Assign permissions to execute the code (COM object). Like this::

InteropPermission permission = new InteropPermission(InteropKind::ComInterop);
;
permission.assert();

Write into the System Event Viewer from Dynamics AX

$
0
0

Usually it will be difficult to monitor/debug Batch Jobs running on server . For example  if something goes erroneous than it's not easy to find out from where and how its arising.

So we can make use of Event viewer instead of infolog and monitor the status by checking the event viewer . 

Writing to the event log in Windows using AX is very easy when you use the EventLog class from the System.Diagnostics namespace. The following job demonstrates how to use the EventLog class.

staticvoid writeEventLogEntry(Args _args)
{
    System.Diagnostics.EventLog eventlog;
    #Define.LogSource("Dynamics AX")
    #Define.LogName("Application")
    ;

    // check if the log already exists
    if(!System.Diagnostics.EventLog::SourceExists(#LogSource))
    {
        // create new log
        System.Diagnostics.EventLog::CreateEventSource(#LogSource, #LogName);
    }

    eventlog = newSystem.Diagnostics.EventLog();
    eventlog.set_Source(#LogSource);

    // write info entry
    eventlog.WriteEntry("<Info>: Just writing in the event viewer.");

    // write error entry
    eventlog.WriteEntry("<Error>: Please check the stack trace below. \n\n" +
    con2str(xSession::xppCallStack()), System.Diagnostics.EventLogEntryType::Error);

    // write warning entry
    eventlog.WriteEntry("Job finished." , System.Diagnostics.EventLogEntryType::Warning);

   }

Also there is another way by which you can trace the info or error in to event  viewer.
The following code shows you how to write event log entry with X++:

Create a new class AX_EventLog with a static method WriteEventLog:

static void WriteEventLog(Exception _exception, str _event)
{
   
 str eventSource = "AX event";
   
 str logType = "Application";
    System.Diagnostics.EventLogEntryType eventLogEntryType;
   
 int eventCategory = 9999;
    ;
    switch(_exception)
    {
       
 case Exception::Info:
            eventLogEntryType = System.Diagnostics.EventLogEntryType::Information;
           
 break;
       
 case Exception::Warning:
            eventLogEntryType = System.Diagnostics.EventLogEntryType::Warning;
           
 break;
        default:
            eventLogEntryType = System.Diagnostics.EventLogEntryType::Error;
    }
    if (!System.Diagnostics.EventLog::Exists(eventSource))
    {
        System.Diagnostics.EventLog::CreateEventSource(eventSource, logType);
    }
    System.Diagnostics.EventLog::WriteEntry(eventSource, _event, eventLogEntryType, eventCategory);
}

Microsoft Dynamics Ax 2009 Three-Tier Architecture

$
0
0


The three-tier environment is divided as follows:

   Ø  First Tier - IntelligentClient
   Ø  Second Tier - AOS
   Ø  Third Tier - DatabaseServer

1. The AOS executes Microsoft Dynamics AX 2009 business logic that provides for scalability, flexibility, and better performance for properly designed objects.

2. In this three-tier solution the databaseruns on a server as the third tier; the AOS handles the business logic in the second tier.

3. The thin client is the first tier and it handles the user interface and necessary program logic.

The Figure below shows Tier Interaction Diagram illustrates the interaction between the three tiers.






--The AOS architecture is highly scalable.
--As a business grows and the number of Microsoft Dynamics AX 2009 users increase, expand the capacity of Microsoft Dynamics AX 2009 by adding an additional AOS to the second tier.
--The additional server provides load balancing and introduces fail over safety into the environment.

Dynamics AX 2012 Navigation Overview

Restrict user login for multiple times in ax2009 through X++

$
0
0

Copy Paste the Following Code in startupPost method of info class in AOT.

void startupPost()
{
// To restrict user login form second login
xSession session;
SysClientSessions SysClientSessions;
UserId currentUserId;
int counter;
;
currentUserId = curUserId();
if(currentUserId!=”Admin”)// Allow Admin User to login multiple time
{
while select SysClientSessions
where SysClientSessions.userId == currentUserId &&
SysClientSessions.Status == 1 // 1 : Login 0 : Logout
{
session = new xSession(SysClientSessions.SessionId, true);
if (session && session.userId())
{
counter++;
}
}
if(counter>=2)
{
Box::stop(“Already Logged-in : The same user id can’t log in twice.”);
infolog.shutDown(true);
}
}
}
Please take backup of your application before copying code.
Happy Daxing J

Editor improvement in AX 2012

SysInfoAction class / Go to main table from Infolog message in AX 2009 / AX 2012:

$
0
0

The SysInfoAction class provides the ability to add an action to the standard infolog.

The user can then perform the action by clicking the infolog. The SysInfoAction class can be extended to perform custom actions e.g. opening a file using the default application.

Examples:

The example below uses the SysInfoAction_Formrun class and will open the "Accounts receivable parameters" form if the user clicks the infolog. 

static void AV_TestSysInfoAction_FormRun(Args   _args)
{
    SysInfoAction_FormRun    infoAction = SysInfoAction_FormRun::newFormName(formStr(CustParameters));
    ;

    infoAction.parmDescription("Open parameters");
    info("The parameters have not been entered.", "", infoAction);
}

The Infolog System can be easily customized in Dynamics AX. You can attach actions to the displayed messages in the infolog. Upon double clicking the message you can go to another form, related to the displayed message.

Solution: use SysInfoAction_TableField class
Using the SysInfoAction_TableField class, you can easily add this functionality in your displayed messages. The job beneath shows you the example for this functionality. Double clicking will open the Customer form on the Customer determined in the X++ code. (Instead of double clicking, even you could click the Show button in the Infolog). 

static void MainTableFromInfoMessage(Args _args)
{
    CustTable       ct;
    args            args = new args();
    ;   
    select ct
        where ct.AccountNum     == "1101";
    {
        args.record(ct);
        info(strfmt("Customer account is: %1", ct.Name), "", Sysinfoaction_tablefield::newBuffer(ct));
    }
}

Happy Daxing...!!J 

Open any object from code editor to in AOT node (Editor Script)

$
0
0
Using below code developer will have ease of opening any object from AX code editor to AOT node.


Below image shows how to move in to AOT just by right clicking an object.


And once you click on “OpenInAOT” that particular object pop up. As shown below.



TO GET THIS ADDINS “OPENINAOT” JUST COPY THE CODE AND PASTE IT IN EDITOR SCRIPT CLASS AS A METHOD.


void addIns_OpenInAOT(Editor e)
{
    #AOT
    TreeNode        treeNode;
    FreeText        selectedLine;
    str 1           curSymbol;
    int             iCopyFrom;
    int             iCopyTo;
    Set             selectedNodesSet = new Set(Types::Class);
    SetIterator     setIterator;
    Map             map;

    void add2Set(TreeNodePath _path)
    {
        ;
        treeNode = TreeNode::findNode(_path + #AOTRootPath + selectedLine);
        if (treeNode)
            selectedNodesSet.add(treeNode);
    }
    ;
    if (e.markMode() != MarkMode::NoMark && e.selectionStartCol() != e.selectionEndCol())
    {
        selectedLine = strLRTrim(subStr(e.currentLine(), e.selectionStartCol(), e.selectionEndCol() - e.selectionStartCol()));
    }
    else
    {
        selectedLine = e.currentLine();
        for (iCopyFrom = e.columnNo()+1; iCopyFrom >= 0; iCopyFrom--)
        {
            curSymbol = subStr(selectedLine, iCopyFrom, 1);
            if (!strAlpha(curSymbol) && curSymbol != '_')
                break;
        }
        for (iCopyTo = e.columnNo()+1; iCopyTo <= strLen(selectedLine); iCopyTo++)
        {
            curSymbol = subStr(selectedLine, iCopyTo, 1);
            if (!strAlpha(curSymbol) && curSymbol != '_')
                break;
        }
        selectedLine = (iCopyFrom < iCopyTo) ? subStr(selectedLine, iCopyFrom + 1, iCopyTo - iCopyFrom - 1) : '';
    }

    if (selectedLine)
    {
        add2Set(#ExtendedDataTypesPath);
        add2Set(#BaseEnumsPath);
        add2Set(#TablesPath);
        add2Set(#ClassesPath);
        add2Set(#MacrosPath);

        if (selectedNodesSet.elements() == 0)
        {
            add2Set(#SystemFunctionsPath);
            add2Set(#SystemTablesPath);
            add2Set(#SystemTypesPath);
            add2Set(#SystemEnumsPath);
            add2Set(#SystemClassesPath);
            add2Set(#ClassesPath + #AOTRootPath + classStr(Global));
        }

        if (selectedNodesSet.elements() > 0)
        {
            setIterator = new SetIterator(selectedNodesSet);
            setIterator.begin();
            if (selectedNodesSet.elements() == 1)
            {
                treeNode = setIterator.value();
            }
            else
            {
                map = new Map(Types::String, Types::String);
                while (setIterator.more())
                {
                    treeNode = setIterator.value();
                    map.insert(treeNode.treeNodePath(), treeNode.AOTparent().treeNodeName());
                    setIterator.next();
                }
                treeNode = TreeNode::findNode(pickList(map, "Location", "Select element type"));
            }
            if (treeNode && treeNode.treeNodePath() != TreeNode::rootNode().treeNodePath())
            {
                if (SysTreeNode::hasSource(treeNode))
                {
                    if (treeNode.AOTparent().treeNodePath() == #macrosPath && subStr(e.currentLine(), e.selectionStartCol() - 1, 1) != '#')
                        return;
                    treeNode.AOTedit();
                }
                else
                {
                    treeNode.AOTnewWindow();
                }
            }
        } 

AX 2009: Save the report in different format

$
0
0
In AX 2009, whenever a user runs a report, by default first it prints to screen.
But sometimes, you want a report to go straight to the printer or save it in some format, without a user intervention.  So in such case you need to develop the logic. But again if you want to have a report printed on screen, need to change the logic. So to avoid that you can write your logic in such a way that whenever you open a report, it prompts you whether report should be saved in Excel or not.

If you click on say “Yes” then it asks you for the save path and if you click on “No” then it prints on screen.
Inorder to design this functionality we can add the logic in fetch method of the report. Say,


public boolean fetch()
{
    boolean ret;
    dialog      d  = new dialog("Select file");
    dialogField df;
    ;
    if (box::yesNo("Do you want to save it in Excel?", dialogbutton::Yes, "Save in Excel") == dialogbutton::Yes)
    {
        df = d.addField(typeid(FileNameSave), "Select file to save");
        if (d.run())
        {
        element.printJobSettings().setTarget(Printmedium::File);
        element.printJobSettings().format(Printformat::ASCII);
        element.printJobSettings().fileName(df.value());
        }
    }
    else
    {
        element.printJobSettings().setTarget(Printmedium::Screen);
    }
    ret = super();
    return ret;
}


Keep reading!!!! Happy Daxing J

Record level security (AX 2009)

$
0
0
Hi, Let me walk-through how we can create record level security through code.

When you create record level security from front end, sometimes wizard doesn't show you the exact form or I can say it doesn't capture the actual table query.

So in that case for some tables or form, we can write the job in AOT and executing it results in creating record in record level security form.

static void RecordLevelSecurityEx(Args _args)
{
    Query                   q;
    SysQueryRun             queryRun;
    sysRecordLevelSecurity  sysRecordLevelSecurity,sysRecordLevelSecurity_old;
    ;

    q = new Query();

    q.addDataSource(tablenum(SalesQuotationTable));
    SysQuery::findOrCreateRange(q.dataSourceNo(1),fieldnum(SalesQuotationTable,Createdby)).value('Admin');

    queryRun    =   new SysQueryRun(q);

    // Need to have a RecordSortList and insert will happen for one time only, loop of companies is needed
    sysRecordLevelSecurity.clear();
    sysRecordLevelSecurity.TabId        =   1967;
    sysRecordLevelSecurity.companyId    =   'ARSP';
    sysRecordLevelSecurity.groupId      =   'TEST';
    sysRecordLevelSecurity.restriction  =   queryRun.pack();

    // TODO - Check how to update
    // Need to check for the primark key combination
    // TabId,groupId,companyId
    if(sysRecordLevelSecurity.validateWrite())
    {
        select firstonly sysRecordLevelSecurity_old
        where sysRecordLevelSecurity_old.tabId  ==  sysRecordLevelSecurity.tabId    &&
        sysRecordLevelSecurity_old.companyId    ==  sysRecordLevelSecurity.companyId    &&
        sysRecordLevelSecurity_old.groupId      ==  sysRecordLevelSecurity.groupId;

        if(sysRecordLevelSecurity_old)
        {
            ttsbegin;
            sysRecordLevelSecurity_old.selectForUpdate(true);
            sysRecordLevelSecurity_old.restriction  =   sysRecordLevelSecurity.restriction;
            sysRecordLevelSecurity_old.write();
            ttscommit;
        }
        else
        {
            sysRecordLevelSecurity.selectForUpdate(true);
            sysRecordLevelSecurity.write();
        }
    }

}


Happy Daxing JJ

Sequence of Form's method in MS DAX

$
0
0

This gives the information of method calls in the form level while
1. Opening the Form.
2. Creating/Updating/Deleting the record in the Form.
3. Closing the Form.

Sequence of Methods calls while opening the Form
Form — init()
Form — Datasource — init()
Form — run()
Form — Datasource — execute Query()
Form — Datasource — active()

Sequence of Methods calls while closing the Form
Form — canClose()
Form — close()
Sequence of Methods calls while creating the record in the Form
Form — Datasource — create()
Form — Datasource — initValue()
Table — initValue()
Form — Datasource — active()

Sequence of Method calls while saving the record in the Form
Form — Datasource — ValidateWrite()
Table — ValidateWrite()
Form — Datasource — write()
Table — insert()

Sequence of Method calls while deleting the record in the Form
Form — Datasource — validatedelete()
Table — validatedelete()
Table — delete()
Form — Datasource — active()

Sequence of Methods calls while modifying the fields in the Form
Table — validateField()
Table — modifiedField()


Table collection and Virtual company in Dynamics AX

$
0
0
If we are using more than one company, sometimes, it will be useful to share data from tables with general information, like, tables storing data like zip codes and country codes. The most basic way to share data from a table among all companies is to set the table propertySaveDataPerCompany to No.
This will merge data for the table and make the data accessible from all companies. In practice, the kernel will delete the system field dataAreaId for the table.
Another way to sharing data without having to change any settings in the existing tables is by using Table Collections. A table collection is just a template for tables to be shared by any number of companies and Table collections shared using a virtual company.

The form SysDataAreaVirtual is used to define virtual companies. Virtual company is a term used for sharing table collections among a set of companies and does not exist in reality, and therefore, you cannot switch to a virtual company like any normal company.

When using TC for setup tables (like customer groups), setting up a VC will be easy. But, if you are going to share data from main tables (like the inventory table), you should do more investigation as you cannot only share the table InventTable. You must include all tables which are related to InventTable.

Note: Before creating a VC you should export data for the tables used in the table collection, as existing data will be deleted from these tables when added to a virtual company.

Virtual Company setup:

Step 1: Create Table Collection:
Decide which tables you want to share and create a Table collection for these functionally related tables. For example; if you want to share 'Global Address Book' across companies then you can utilize the existing table collection "DirPartyCollection".

To create a table collection, go to AOT\Data Dictionary\Table Collections and on right click select "New Table Collection", then just drag your required tables in this collection.

Step 2: Create Virtual Company, configure/attach normal companies and table collection:
Create a virtual company that will hold the shared data for normal companies.
Note: Before doing the below steps, make sure you are the Ax administrator and the only user online.
1.       Go to Administration > Setup > Virtual company accounts, and create a virtual company.
2.      Decide which companies needs to share data and attach those normal companies with this virtual company.
3.      Attach the table collection with this virtual company and save form.
Your Ax client will re-start and you are done with setting up the virtual company account.
Now, when you have virtual company in place, all new data will be saved in this virtual company. Only companies attached to the virtual company can use this shared data. All other companies which are not attached will work normally, these companies will continue to read/write data as per company bases.

How to move existing data to virtual company?
When you setup a new virtual company, Ax does not move data automatically from normal company to virtual company. This is done by system administrator manually.

There are many ways to do this data move, let’s see only two such approaches here.

Approach 1: Ax Import / Export
this is standard Ax approach.
Manually export existing normal company data from Ax.
Remove duplicate records from this exported data set.
Delete exported data from normal companies.
Import the exported data back in Ax, while logged into one of the participating companies.
Create records deleted in point 2 again in Ax using your logic. How you want to handle duplicate? For example, if you have customer 'Customer' in more than one normal company, what you want to do with this?

Approach 2: Direct SQL
Use this approach if you have good knowledge about SQL queries and Ax table structures/relationships. Below are steps you can follow.
 All Ax tables store data as per company unless otherwise specified. For this, Ax uses a special field called DataAreaId. In case of virtual company, it does not matter from which normal company you log-in, it is always the virtual company id which is stored in DataAreaId field of shared tables.
 Ax also assigns a unique 64bit number to each record in table. For this, Ax uses a special field called RecId. This RecId is unique in the table and is generated by Ax when you insert a new record in Ax. It is not related to DataAreaId / Company.
For unique records between all participating normal companies, update the DataAreaId to the virtual company id.
For duplicate records, create them again in Ax using some Ax job or Ax import/export technique.



Viewing all 126 articles
Browse latest View live