Passing parameters to another form and open it in Edit mode
Below code can be used to pass one parameter from one form to another and open that form in Edit mode. FormRun formRun; Args args; str defaultFormOnTable; MenuFunction menuFunction; args = new Args(); args.record(CustTable::find(‘test account’)); menuFunction = new MenuFunction(menuitemDisplayStr(CustTable), MenuItemType::Display); if(menuFunction) { menuFunction.openMode(OpenMode::Edit); formRun = menuFunction.create(args); if(formRun) formRun.run(); } In this example I opened customers form in Edit mode by passing customer account ‘test account’as a parameter.
How to validate Email and URL through X++
With default functionality in AX, Email address and URL can be of any format and these do not get validated as per their respective correct formats. Existing functionality Accounts receivable | Common/Customers | All customers | Customer form | Under Contact information fast tab Someone can put Email address in any format as I did “faisal” which is not a correct format of email address. Same applies for URL under contact information for customers. Customized functionality I created a new class with two static methods to validate these attributes as follows by taking concept from this blog post /// <summary> /// Class to validate Logistics Electronic Address using Regex /// </summary> class LogiscticsElectronicAddressValidator { } /// <summary> /// This method accepts a EMail and validates this using REGEX /// </summary> /// <returns> /// true or false based on Regex match /// </returns> Static Server boolean validateEMail(EMail _eMail) { Boolean xppBool; System.Boolean netBool; Str MatchEmailPattern = @”^(([w-]+.)+[w-]+|([a-zA-Z]{1}|[w-]{2,}))@” + @”((([0-1]?[0-9]{1,2}|25[0-5]|2[0-4][0-9]).([0-1]? [0-9]{1,2}|25[0-5]|2[0-4][0-9]).” + @”([0-1]?[0-9]{1,2}|25[0-5]|2[0-4][0-9]).([0-1]? [0-9]{1,2}|25[0-5]|2[0-4][0-9])){1}|” + @”([w-]+.)+[a-zA-Z]{2,4})$”; System.Text.RegularExpressions.Match myMatch; ; new InteropPermission(InteropKind::ClrInterop).assert(); myMatch = System.Text.RegularExpressions.Regex::Match(_eMail,MatchEmailPattern); netBool = myMatch.get_Success(); xppBool = netBool; CodeAccessPermission::revertAssert(); Return xppBool; } /// <summary> /// This method accepts a URL and validates this using REGEX /// </summary> /// <returns> /// true or false based on Regex match /// </returns> Static Server boolean validateURL(URL _url) { Boolean xppBool; System.Boolean netBool; Str matchURLPattern = “^(https?://)” + “?(([0-9a-z_!~*'().&=+$%-]+: )?[0-9a-z_!~*'().&=+$%-]+@)?” //user@ + @”(([0-9]{1,3}.){3}[0-9]{1,3}” // IP- 199.194.52.184 + “|” // allows either IP or domain + @”([0-9a-z_!~*'()-]+.)*” // tertiary domain(s)- www. + @”([0-9a-z][0-9a-z-]{0,61})?[0-9a-z].” // second level domain + “[a-z]{2,6})” // first level domain- .com or .museum + “(:[0-9]{1,4})?” // port number- :80 + “((/?)|” // a slash isn’t required if there is no file name + “(/[0-9a-z_!~*'().;?:@&=+$,%#-]+)+/?)$”; System.Text.RegularExpressions.Match myMatch; new InteropPermission(InteropKind::ClrInterop).assert(); myMatch = System.Text.RegularExpressions.Regex::Match(_url,matchURLPattern); netBool = myMatch.get_Success(); xppBool = netBool; CodeAccessPermission::revertAssert(); Return xppBool; } To call these methods you need to override two methods (ModifiedField and ValidateField) in LogisticsElectronicAddress table. After implementing this solution you will get error on entering invalid email and URL addresses.
Random number generation through X++
AX has buit-in functionality to generate random numbers. You can use Random class (a kernel class, not visible in AOT) for this purpose. There are two more ways to generate random number in X++ One is using RandomGenerate Class which extends Random class and generates values within specified range; Second is using xGlobal::randomPositiveInt32() method. Example using Random class to generate random numbers (All in CAPITAL letters) public str generateRandomCode() { str pass=”; int length, counter, randInt,randAscii; ; length = 4; for(counter = 1; counter <= length; counter++) { randInt = Random.nextInt(); randAscii = randInt mod 91; if((randAscii >= 48 && randAscii <= 57) || (randAscii >= 65 && randAscii <= 90)) { pass += num2char(randAscii); continue; } else { randAscii = randAscii mod 26 ; randAscii += 65 ; pass += num2char(randAscii); continue; } } return pass; }
Exchange rate cannot be found for exchange rate type default between currenies USD & NZD
I came across this error while making some international payments in AX using EFT (method of payment). “Exchange rate cannot be found for exchange rate type default between currenies USD & NZD” Resolution: Go to General Ledger | Setup | Currency | Currency exchange rates Add new exchange rates for required currencies with start date Simple 🙂
Firewall settings for Microsoft Dynamics AX components [AX 2012 R2]
If you use Windows Firewall to help protect your computers, Microsoft Dynamics AX components require the settings in the following table. For more information about Windows Firewall, see the Windows documentation. Document is available to download.
Configure Visual Studio to connect/open with correct AOS
Visual Studio is essential to create/edit SSRS reports with MS Dynamics AX 2012 and later releases. Here are steps to configure visual studio with correct AOS (in my case it is development AOS). Connect with computer where visual studio is installed Go to location where visual studio file is installed and placed. In my case it is placed at C:Program FilesMicrosoft Visual Studio 10.0Common7IDE Right click and sent to create shortcut to desktop (one can create shortcut anywhere) Right click Visual Studio shortcut (will name something “devenv – shortcut”) and take properties Set target = “C:Program FilesMicrosoft Visual Studio 10.0Common7IDEdevenv.exe” /AxConfig C:FaisalFareedWorkfolderDynamicsAXTestSystem.axc Path of the original visual studio installation file as mentioned in step 2 “C:Program FilesMicrosoft Visual Studio 10.0Common7IDEdevenv.exe” Path of the configuration file where MS Dynamics AX 2012 configuration is stored. This can be UNC file path or local path, depends on your requirements. /AxConfig C:FaisalFareedWorkfolderDynamicsAXTestSystem.axc Double click on Make sure visual studio is connected with correct AOS. This “DynamicsAX_Test” is the configuration name in configuration file used as target in visual studio shortcut’s properties.
Running AX 2012: “An invalid directory structure for Microsoft Dynamics AX was detected”
I came across an issue recently when I tried to run Dynamics AX 2012 from client configuration file on 32-bit operating system. Client configuration file was created on 64-bit operating system and was copied to 32-bit operating system computer to use. As error says it tries to bind directory at path C:Program Files (x86)Microsoft Dynamics AX60\ClientBin which was not exist on 32-bit operating system. Resolution 1 Open client configuration (.axc) file in notepad and change the path at the very end of file. This path should match your directory structure in computer. Source computer (64 bit) configuration will look like this. 64-bit operating system generated configuration file Destination computer (32 bit) configuration should look like this. This is the correct path as per 32 bit computer directory structure. Save configuration file after correcting the paths. It will work smoothly. Resolution 2 http://blogs.msdn.com/b/emeadaxsupport/archive/2011/03/31/running-ax-2009-client-raises-error-quot-an-invalid-directory-structure-for-microsoft-dynamics-ax-was-detected-quot.aspx?CommentPosted=true#commentmessage MS Dynamics AX configuration files register in registry when you import/create new configuration files on Dynamics AX configuration. You can also change bindir, datadir and directory paths from registry too, for this you can find registry keys under Computer | HKEY_CURRENT_USER | Software | Microsoft | Dynamics | 6.0 | Configuration | <Configuration name>
The target prinipal name is incorrect – SSRS with AX
Following error made me worried when I was not able to run (existing or customized) SSRS reports from AX. Reports were working fine few days back and there wasn’t a single change done neither at report server nor with AOS service accounts. After digging into issue, came across the issue was services and services groups in AOT. My AOS was connected with TFS and when I got latest from TFS it affects SRSFramework and SSASFramework services and there was some sort of reference lost in service groups. Resolution: Generate FULL CIL Confirm the Business proxy account and AOS service account is same Register SRSFrameworkService and SSASFrwameworkService services from AOT Deploy BIServices and UserSessionServices groups from AOT Restart SQL reporting services
Assigning security roles to new AX user was never that easy !!!
Some time before I got a chance to write on a utility to make user role assignment a much easier….on ONE click. For example; a new accountant joined your organization and CFO asked AX System Admin to add this new user into AX and give him/her same permissions as other accountants have in AX. Without this utility AX system Admin will open existing user roles in a seperate window and add these roles for new joined users….Is it not the time consuming activity when you have to add dozens of roles. This utiltiy not limited to copy user roles but can copy User Groups and user Options too. Some important methods of this utility are; Create User lookup for FROM USER public void fromUserLookup(FormStringControl userLookupControl) { Query qry = new Query(); QueryBuildDataSource qbd; QueryBuildRange qbr; SysTableLookup sysTableLookup; Userinfo userInfo; systablelookup = SysTableLookup::newParameters(tableNum(UserInfo), userLookupControl); SysTableLookup.addLookupfield(fieldNum(UserInfo, ID)); SysTableLookup.addLookupfield(fieldNum(UserInfo, NetworkAlias)); SysTableLookup.addLookupfield(fieldNum(UserInfo, Name)); qbd = qry.addDataSource(tableNum(userInfo)); qbd.addRange(fieldNum(UserInfo,Company)).value(curext()); sysTableLookup.parmQuery(qry); sysTableLookup.performFormLookup(); } Create User lookup for TO USER public void toUserLookup(FormStringControl userLookupControl) { Query qry = new Query(); QueryBuildDataSource qbd; QueryBuildRange qbr; SysTableLookup sysTableLookup; Userinfo userInfo; systablelookup = SysTableLookup::newParameters(tableNum(UserInfo), userLookupControl); SysTableLookup.addLookupfield(fieldNum(UserInfo, ID)); SysTableLookup.addLookupfield(fieldNum(UserInfo, NetworkAlias)); SysTableLookup.addLookupfield(fieldNum(UserInfo, Name)); qbd = qry.addDataSource(tableNum(userInfo)); qbd.addRange(fieldNum(UserInfo,Company)).value(curext()); qbr = qbd.addRange(fieldNum(UserInfo, Enable)); qbr.value(‘1’); sysTableLookup.parmQuery(qry); sysTableLookup.performFormLookup(); } Function to copy USER ROLES /// <summary> /// copy roles assigned to one user to another /// </summary> /// <returns> /// true; roles are copied across. false; roles failed to copied /// </returns> /// <remarks> /// this method is used to copy user roles assigned to one user to another user. /// </remarks> private boolean copyUserRoles() { boolean ret = true; SecurityRole securityRole; SecurityUserRole securityUserRole; SecurityUserRole securityUserRoleExist; SecurityUserRole securityUserRoleInsert; OMUserRoleOrganization userRoleOrganization, userRoleOrganization_Insert; List copiedUserRoles = new List(Types::String); ListEnumerator lEnumerator; setPrefix(strFmt(“Copy user”, fromUser, toUser)); try { select securityRole where securityRole.AotName == ‘SystemUser’; delete_from securityUserRole where securityUserRole.User == toUser && securityUserRole.SecurityRole == securityRole.RecId; while select securityUserRole where securityUserRole.User == fromUser notExists join * from securityUserRoleExist where securityUserRoleExist.SecurityRole == securityUserRole.SecurityRole && securityUserRoleExist.User == toUser { select securityRole where securityRole.RecId == securityUserRole.SecurityRole; copiedUserRoles.addStart(securityRole.Name); securityUserRoleInsert.initValue(); securityUserRoleInsert.SecurityRole = securityUserRole.SecurityRole; securityUserRoleInsert.User = toUser; securityUserRoleInsert.insert(); securityUserRoleInsert.clear(); while select userRoleOrganization where userRoleOrganization.User == fromUser && userRoleOrganization.SecurityRole == securityUserRole.SecurityRole { userRoleOrganization_Insert.initValue(); userRoleOrganization_Insert.OMHierarchyType = userRoleOrganization.OMHierarchyType; userRoleOrganization_Insert.OMInternalOrganization = userRoleOrganization.OMInternalOrganization; userRoleOrganization_Insert.SecurityRole = userRoleOrganization.SecurityRole; userRoleOrganization_Insert.SecurityRoleAssignmentRule = userRoleOrganization.SecurityRoleAssignmentRule; userRoleOrganization_Insert.User = toUser; userRoleOrganization_Insert.insert(); userRoleOrganization_Insert.clear(); } } } catch { ret = false; } if (ret) { lEnumerator = copiedUserRoles.getEnumerator(); if (copiedUserRoles.empty()) info(strFmt(“User %1 and %2 have already the same user role“,fromUser, toUser)); while (lEnumerator.moveNext()) { info(strFmt(‘%1’,lEnumerator.current())); } } else error(strFmt(“User Roles aborted please review list“)); return ret; } Function to copy USER OPTIONS /// <summary> /// copy options assigned to one user to another /// </summary> /// <returns> /// true; options are copied across. false; options failed to copied /// </returns> /// <remarks> /// this method is used to copy user’s options assigned to one user to another user. /// </remarks> private boolean copyUserOptions() { boolean ret = true; UserInfo userInfoSource; UserInfo userInfoTarget; SysUserInfo sysUserInfoSource; SysUserInfo sysUserInfoTarget; setPrefix(strFmt(“Copy user options“, fromUser, toUser)); try { select userInfoSource where userInfoSource.id == fromUser join sysUserInfoSource where sysUserInfoSource.Id == userInfoSource.id; ttsBegin; select forUpdate userInfoTarget where userInfoTarget.id == toUser; userInfoTarget.filterByGridOnByDefault = userInfoSource.filterByGridOnByDefault; userInfoTarget.statuslineInfo = userInfoSource.statuslineInfo; userInfoTarget.update(); select forUpdate sysUserInfoTarget where sysUserInfoTarget.Id == toUser; sysUserInfoTarget.DefaultCountryRegion = sysUserInfoSource.DefaultCountryRegion; sysUserInfoTarget.update(); ttsCommit; } catch { ret = false; } if (ret) { info(strFmt(“User %1 and %2 have already the same user options “, fromUser, toUser)); } else error(strFmt(“User Options aborted please review list “)); return ret; } Function to copy USER GROUPS /// <summary> /// copy groups assigned to one user to another /// </summary> /// <returns> /// true; groups are copied across. false; groups failed to copied /// </returns> /// <remarks> /// this method is used to copy user groups assigned to one user to another user. /// </remarks> private boolean copyUserGroups() { boolean ret = true; UserGroupList userGroupList; UserGroupList userGroupListExist; UserGroupList userGroupListInsert; List copiedGroups = new List(Types::String); ListEnumerator lEnumerator; setPrefix(strFmt(“Copy user groups”, fromUser, toUser)); try { while select userGroupList where userGroupList.userId == fromUser notExists join * from userGroupListExist where userGroupListExist.groupId == userGroupList.groupId && userGroupListExist.userId == toUser { copiedGroups.addStart(userGroupList.groupId); userGroupListInsert.initValue(); userGroupListInsert.groupId = userGroupList.groupId; userGroupListInsert.userId = toUser; userGroupListInsert.insert(); userGroupListInsert.clear(); } } catch { ret = false; } if (ret) { lEnumerator = copiedGroups.getEnumerator(); if (copiedGroups.empty()) info(strFmt(“User %1 and %2 have already the same user Groups “,fromUser, toUser)); while (lEnumerator.moveNext()) { info(strFmt(‘%1’,lEnumerator.current())); } } else error(strFmt(“User Groups aborted please review list “)); return ret; }
Let’s have inside into MS Dynamics AX 2012 R3
Inside MS Dynamics AX 2012 R3 is released with new features (functionally) and performance enhancement (technically). http://blogs.msdn.com/b/microsoft_press/archive/2014/08/12/new-book-inside-microsoft-dynamics-ax-2012-r3.aspx