Quantcast
Channel: Microsoft Dynamics Ax
Viewing all 181 articles
Browse latest View live

AX 2012 – “Lights out” System Compile

$
0
0
As most of you are aware by now, AX 2012 makes extensive use of .NET CIL to perform various functions within the system e.g. the AIF framework, AX Batch Server, AX SSRS Reports, etc – all of these components now run as .NET CIL instead of X++ code.
This has made it increasingly important to regularly compile your X++ code into CIL using either of the following methods:
  • Full CIL Generation
  • Incremental CIL generation.
To ensure a healthy system during development, I would recommend that you regularly perform a full system compile. The problem is that this can be a time consuming task, as it requires a two-stepped process that needs to be manually kicked off one after the other i.e. A System Administrator triggers the Full System Compile Step, and then once that successfully completed, (s)he can then trigger the Full CIL compilation.
This can end up being an extremely painful process, as it normally requires this task to be performed outside of working hours, and for somebody to stay up late to wait for the Full System Compile to finish so that the CIL compile can be started.
To get around this, we can make use of a batch script that will perform both of these tasks outside of working hours, which can then be scheduled to run nightly.
This blog will provide a basic example of the AX32.exe commands that can be used to perform the required system compilations (FYI – There are several blogs on the internet that talks about creating automated build scripts. These blogs go into a lot more detail about deploying models, loading XPO files, etc. So if you need to perform these tasks, please don’t hesitate to go and check them out).
For my “Lights out” compilation script, I’m going to perform the following very basic tasks:
The following screenshot shows a batch script that performs all of the tasks as summarized above (Each section within the script will be described in more detail later in this blog):

Script Variables

SET serviceName=AOS60$01

SET aosHost=AX2012-A

SET aosPort=2712

SET appPath=”C:\Program Files (X86)\Microsoft Dynamics AX\60\Client\Bin\AX32.EXE”

The first section of the script should be pretty self-explanatory i.e. 4 variables that are used to identify the name of the AOS Service (as shown in the Services Management snap-in), the server hosting the AOS Server, the Port number assigned to the AOS Server, and then finally the Path to the AX Client executable.
(I’ve included the Host Name and Port number parameters to allow for environments where multiple AOS servers are installed on a single host e.g. a DEV and TEST environments).
Restart AOS Server

net stop %serviceName%

net start %serviceName%

The next section in the script restarts the AOS server. I include this restart step to ensure that the latest label file changes are loaded into the AX Model Store.
Perform Full System Compile

%appPath% -aos2=%aosHost%:%aosPort% -startupcmd=CompileAll_- -lazytableloading -lazyclassloading

Once the AOS service has been restarted, I initiate the Full X++ compile command.
The AX32 executable supports multiple command line parameters (A full listing can be found here). The two significant parameters are: -aos2 AND –startupcmd.
  • The –aos2 command line parameter is used to tell the AX32 executable which AOS Server to connect to.
  • The –startupCmd command line parameter tells the AX32 executable which command to execute during start-up.
    You will notice that I’ve included a _- at the end of the CompileAll command. This tells AX to perform a full system compile, but NOT update the Cross Reference information. If you want to perform a Cross Reference update, change the value to _+.
The other two parameters used in this script are: -lazytableloading AND –lazyclassloading.
When the AX client starts up, it performs various initialization tasks to improve the overall performance of the client e.g. the system pre-compiles and loads various system tables and classes into memory for quick reference.
This process slows down the initial start-up time of the AX client.
For the purpose of doing a full system compile, these performance optimization tasks performed during start-up doesn’t provide any benefit to us, so by using the –lazytableloading and –lazyclassloading parameters we tell the application to not perform these tasks during start-up.
Perform Full CIL Compile

%appPath% -aos2=%aosHost%:%aosPort% -startupcmd=CompileIl -lazytableloading -lazyclassloading
After the full compile is completed we use the same AX32 command, but this time we use the CompileIL start-up command.
Restart AOS Server

net stop %serviceName%

net start %serviceName%

And finally we restart the AOS service to release all of the system resources that used during the compilation process.
Conclusion

Now there are several enhancements that can be made to this script e.g. checking to see if any errors occurred during the Full X++ compile, and then only initiating the CIL compile if no errors occurred. But this is something for another day.
I hope this proves useful, and let me know if you have any questions.

Schedule full CIL compile

$
0
0
A full CIL compile can be run from the AX client, but you cannot schedule it. This post describes how to achieve scheduling a full CIL compile recurring using Task Scheduler.

Solution: step-by-step scheduling with Task Scheduler

Open Task Scheduler, create a new folder for your Dynamics AX tasks and choose Create new Task.

In the Triggers tab define the recurrence pattern for your task.

In the Actions tab define the task to run, being the full CIL compile.
In the Program/script field point to your AX client, normally it should be located in "C:\Program Files (x86)\Microsoft Dynamics AX\60\Client\Bin\Ax32.exe".
As arguments you specify the following:
-aos2=<AOSServer>:<portnumber> -startupcmd=CompileIl -lazytableloading –lazyclassloading where you replace <AOSServer> and <portnumber> with your actual AOS and port.

You are done: CIL compile is scheduled to run recurrent, just make sure you have a remote desktop session open to the server where you have scheduled it, so it can kick off at the scheduled time.

Dynamics AX Command Line Parameters

$
0
0
Axapta Command Line


Following are Axapta Command line parameters and its description

-allowunauth
When using Windows Authentication, this option enables users that do not pass the authentication process to be allowed logging in using user name and password (traditional Axapta logon sequence). If not enabled, users will be rejected if not authenticated.

-aol="s"
aol is an acronym for Application Object Layer. Valid layers are:
sys, syp, gls, glp, dis, dip, los, lop, bus, bup, var, vap, cus, cup, usr and usp.

-aolcode="s" Access code for aol.

-aos=host:port
Connect to the AOS running at given port number on specified host. Host is either a DNS hostname (for example server1.damgaard.com) or an IP address (192.88.253.41). The port number is the number specified for the AOS instance that should be connected to. No instance name needs to be specified because only one instance can be running at a given port on a given machine. Using this option will connect the client directly to the AOS using TCP traffic only and bypass the initial search for the AOS and thereby eliminates the need for networking that supports UDP traffic. This eases firewall configuration and NAT appliances

-aos=instance@host
Connect to the specified AOS instance running at the specified host machine. Instance is the name (for example 'Axapta'), and host is DNS name or IP address of the machine running the AOS. Specifying -aos=MyAOS@MyHost equals setting -servermask=MyAOS and -internet=MyHost (or specifying these in corresponding fields on the server tab in the Configuration Utility).

-aos=ad("adsn")
Use Active Directory integration. "adsn" is the "Active Directory Server Name to search the Active Directory for. Same as "By name - find a specific AOS" in the Axapta Configuration Utility

-aos=adbrowse
Use Active Directory integration. Search the Active Directory for "Active Directory Server Names" in User objects and Organizations Units. Same as "By organization - browse for per-user or per-organizational specific AOS" in the Axapta Configuration Utility.

-aos=ad
Use Active Directory integration - search the Active Directory for any AOS. Same as "Simple - find any AOS" in the Axapta Configuration Utility

-applexclusive
The application files are opened in exclusive mode. Note applexclusive" and applshare cannot both be given. If you do give both, the one given last on the command line will take effect. You will not get any error messages.

-application=s
Specify the name of the Axapta application. Default: Standard

-applshare
The application files are opened in shared mode. This is default. Note applexclusive" and applshare cannot both be given. If you do give both, the one given last on the command line will take effect. You will not get any error messages.

-broadcast= xx.xx.xx.xx
Specify a broadcast address to be used in CLIENT mode. A request is sent to all the broadcast addresses to obtain identification of the available application servers. The address consists of four decimal values between zero and 255 separated by dots.

-bwsim=speed:latency
where speed states the bandwidth simulated (in bytes per second). The latency is specified as a number designating number of ms spent for communication round trip (the fixed overhead (time used) the network applies to sending a package to the server and receive one back). To verify....

-client
Connect to an AOS and run as a three-tier thin client. Use -aos= to specify which AOS.
client = thin : Connect to an AOS and run as a three-tier thin client.
Use -aos= to specify which AOS.
client = fat : Connect to an AOS and run as a three-tier fat client.
Use aos= to specify which AOS.

-company=s Select initial company s. Default: dat

-connectionidletimeout=seconds
Set the time in seconds to leave an idle database connection open before closing it. Shorter idle time will decrease database server and Axapta memory usage, but will potentially cause time-consuming re-logins on the fly. Default: 60 seconds is the default for Microsoft SQL Server, 30 minutes the default for Oracle.

-createdsn=microsoftsqlserver or-createdsn=oracle
Have the data source created automatically in the ODBC manager.

-createdsn_tcpipport=integer
TCP/IP port number required for Oracle. This parameter is relevant only when
-createdsn=oracle is given. The parameter is ignored if given with
-createdsn=microsoftsqlserver.

-database=s
Use database s when connecting to the database server. Default: The default option is to use the database set in the ODBC driver.

-dbcli= [ODBC][OCI]
Runs Axapta in either ODBC or OCI mode. -DBCLI=ODBC is the default.

-dbserver=s
Use server s during login. Default: The default option is to use the server set in the ODBC driver.

-dbunicodeenabled=0 1 Initialize database for Unicode

-directory=s Specify the Axapta root-directory.

-doclanguage=s
Use this option if you would like to have the documentation in a different language than the one used in menus and dialogs.
Example:
-doclanguage=da : will give you the documentation in Danish.
Default: The default is that the documentation language is identical to the language used in the system. This is set by the -language option.

-dsn=s
Use ODBC driver data source s. Default: BMSDSN

-featurekeysystem
The 3.0 security system is ON by default.Use this parameter to enable the old featurekey system.
-fetchahead=n A maximum of records retrieved from the database at a time. Default: 100

-hint=n
Apply database dependent SQL hint(s). Default: Empty for default settings.

-internal=relaxedsyntax The 3.0 kernel defaults to strict X++ syntax checking.
For relaxed syntax checking, use this parameter to ease restrictions.

-internet=s
Specify an Internet address to be used in �client mode. A request is sent to all the Internet addresses to obtain identification of the available Object servers.

-job=s
Run external job s prior to any other database-related action during startup.
Default: The default is not to run a job.

-language=s
Select language s for the user interface. Default: Language must be selected during setup.

-log=s
Name the SQL error log file (may include a full drive and path specification).
Default: trcAxaptaError.log in the standard Axapta log-directory.

-logdir=s
Use an alternative directory for the log files generated when you compile, import or export in Axapta. Default: The default is that the log files are generated in the Log folder.

-noauto
Use this parameter to bypass system related application calls made by the Axapta kernel. This includes the ability to bypass startup code, and some timer based calls. This parameter will allow you to startup Axapta in order to fix problems that were introduced in the application code. Normally these problems would prevent you from starting Axapta. For example, if code is introduced in the startup method that causes Axapta to go into infinite loop, and therefore, never finishes the startup procedure, . To change this, start with the �NOAUTO switch, correct the code and restart without the �NOAUTO to have the startup code included again.

-opencursors=n
A maximum of n database cursors are kept open per connection for cursor reuse.
Default: 90 cursors

-port=integer : TCP port for the AOS

-preloadthresholdmsec=milliseconds Time used for preloading.
For example, -preloadthresholdmsec=3000 results in the issue of a warning whenever preloading exceeds 3000 milliseconds. This value can not be specified per user in Tools, Options, SQL, Warnings. This threshold is only activated when warnings are enabled.
-preloadthresholdrecords=records Number of records preloaded.
For example, -preloadthresholdrecords=300 results in the issue of a warning whenever preloading exceeds 300 records. This value can not be specified per user in Tools, Options, SQL, Warnings. This threshold is only activated when warnings are enabled.

-querytimelimit =[table:][milliseconds]
Save queries running longer than a given number of milliseconds to file. If the value of QuerytimeLimit is zero (0), which is the default, no queries are logged. This parameter supports directing output to a table, i.e. SysTraceTable (default is disk-file). Use -QuerytimeLimit=ms for tracing all SQL statements exceeding the ms milliseconds threshold and -QuerytimeLimit=table:ms to do the same to table.

-regconfig=name Use a Registry configuration called name.
A configuration can be created using the Axapta Configuration Utility.

-regimport=file name Import a configuration to the Registry.
The import is performed prior to the evaluation of any other options. This means that you can import a configuration using �regimport and then select it using �regconfig.

-repair
Any non-zero value will force a re-synchronization of SQL system tables during startup. The use of this command-line parameter is logged in the Event log. Use this option to handle situations when problems in SQL system tables prevent Axapta from starting, for example missing indexes.

-retry=n
Delay in seconds before re-executing after a deadlock. Default: 5 seconds

-securityprovider=s
Selects the security provider to use with Windows Authentication and is only relevant for Object Server configuration. For AOS running on Windows NT the only valid option is "NTLM" which provides authentication based on the NTLM security provider. For Windows 2000 systems 'Kerberos' is also a valid security provider. For Windows 2000 networks with solely Windows 2000 servers and clients 'Negotiate' is also an option. This will elect the best suitable security provider automatically.

-serveridletimeout=seconds
Specifies how long (in seconds) the AOS instance should be allowed to be running without servicing clients. When this timeout expires without having clients connected, the instance will be shut down automatically. This option is well suited to be combined with setting instance startup mode to OnDemand making the server auto-start upon request from client and shutdown when no clients need service for at given amount of time.

-servermask=s
Specify the mask s for selecting a subset of object servers when running in CLIENT mode. if this option is not specified, and multiple object servers are found, all available object servers will be presented in a selection box.

-share
Share label and identifier files between several applications. if not specified, the files will not be shared.

-singleuser
Run the program in single user mode.

-sqlbuffer=n Set the upper limit in Kbytes of the fixed internal data retrieval buffer. Default: 24 Kbytes

-sqlcomplexliterals=n
About literals and placeholders. Setting sqlcomplexliterals to the value 1 enables this feature, the value 0 disables this feature.

-sqlformliterals=n About literals and placeholders.
Setting sqlformliterals to the value 1 enables this feature, the value 0 disables this feature.

-sqloraclefirstrowsfix=n
Oracle Versions 8.05, 8.06 and 8.15 occasionally selects a poor query plan for queries using the Axapta keyword firstFast row. The symptom is that an index matching the order by specification is preferred, even though another index much better serves the where part and the number of rows returned is small. Axapta includes a workaround for this problem, which you should only enable if you have verified that the above problem is the cause for poor performance. The Axapta Query Analyzer can be used for detecting this. A value of 1 enables this work around, a value of 0 disables this feature.

-sqlparm=s
Add additional parameters s upon database login. The format follows the ODBC standard: key1=value1;key2=value2. An example: DIR=c:\db;ID=9. Default: The default is no additional parameters.

-sqlpwd=s
Use password s upon login to the SQL database. Default: bmssa_pwd

-sqltrace[=Table]
Invoke SQL statement tracing to log file or table. Use -sqltrace for tracing all generated SQL statements to file and -sqltrace=table to do the same to table. Default: No tracing.

-sqluser=s
Use user name s during login to the SQL database. Default: bmssa

-startupmsg=s
Text to be displayed during Axapta startup.

-startupcmd=MyCommand
A string that is passed to Axapta and can be used to have your own startup commands executed. The string is passed in the calls appl.startup(MyCommand) info.startup(MyCommand) appl and info are instantiated objects of the application classes Application and Info respectively. The application classes are inherited from the system classes xApplication and xInfo. Learn more in the Developer's Guide. You can access the guide from Axapta's Help menu.

-useis Use integrated security during SQL database login
and thus disabling values set by using parameters sqluser and sqlpwd.

-user=s Log on as user s.

-useserverprinters
Have the client direct all printing to the printer connected to the server.

-warnings[=table]
Enable various run-time warnings which are logged to a file, or table. Use -warnings to trace all developer warnings to file and -warnings=table to trace all warnings to a table. Default: No warnings.

-windowsauth={01}
This option disables/enables Windows Authentication which, when enabled, is providing Single Sign-On and Authentication of client machine account and the user logging in.

12 comments:

Anonymous said...
Great goods from you, man. I've understand your stuff previous to and you are just too magnificent. I really like what you've acquired here, really like what you are saying and the way in which you say it.
You make it enjoyable and you still take care of to keep it sensible.
I can not wait to read far more from you. This is really
a great web site.

Look into my page ... All Natural Cleanse
Anonymous said...
Have you ever thought about adding a little bit more than
just your articles? I mean, what you say is fundamental and
all. Nevertheless think of if you added some great pictures or videos to give
your posts more, "pop"! Your content is excellent but with images and clips, this blog could
undeniably be one of the greatest in its field. Wonderful
blog!

Here is my site; Xength X1 Reviews
Anonymous said...
a week and also transform your physique very quickly.
While they are struggling to pack on any muscle at all, you.

Having a six-pack or eight-pack abdomen is a dream for thousands of
people, which is further fortified by the screen shots of
their favorite actors who have the perfect physique with muscular arms and
chest.

Also visit my page :: Power Pump XL
Anonymous said...
Thanks on your marvelous posting! I seriously enjoyed
reading it, you may be a great author. I will ensure
that I bookmark your blog and will come back down the road.
I want to encourage that you continue your great work, have a nice
afternoon!

Here is my webpage; Test-o-boost reviews
Anonymous said...
Wow, this piece of writing is nice, my sister
is analyzing these kinds of things, so I am going to inform her.


Also visit my website; raspberry ketone extracct
Anonymous said...
Hi there to all, for the reason that I am
in fact eager of reading this webpage's post to be updated daily. It includes good stuff.

Visit my web-site male enhancement products
Anonymous said...
Hello just wanted to give you a quick heads up and let you know a few of the pictures aren't loading correctly. I'm not
sure why but I think its a linking issue. I've tried it in two different browsers and both show the same outcome.

Feel free to visit my web-site; Green Coffee Cleanse Diet
Anonymous said...
You could certainly see your enthusiasm within the work you write.

The arena hopes for more passionate writers like you who are not afraid to say how they believe.
At all times follow your heart.

My weblog Green coffee reviews
Anonymous said...
hello!,I really like your writing so much!
share we keep up a correspondence more approximately
your post on AOL? I need an expert in this area to resolve my
problem. Maybe that's you! Taking a look ahead to see you.

Look at my web site - muscle building facts
Anonymous said...
It's an awesome piece of writing in favor of all the internet visitors; they will get benefit from it I am sure.

My web blog wrinkle cream
Anonymous said...
I need to to thank you for this wonderful read!
! I absolutely enjoyed every little bit of it. I've got you bookmarked to look at new stuff you post…

Also visit my page :: wrinkle cream
Anonymous said...
I rarely leave a response, however i did some searching
and wound up here "Dynamics AX Command Line Parameters".
And I actually do have a couple of questions for you if it's allright. Could it be only me or does it appear like some of these responses look like they are left by brain dead people? :-P And, if you are posting at other online social sites, I'd like to keep up with everything fresh you have to post.
Would you list of all of all your communal sites like your twitter feed,
Facebook page or linkedin profile?

Tips to help prevent long-running reports from timing out [AX 2012]

$
0
0
Updated: June 20, 2013
Applies To: Microsoft Dynamics AX 2012 R3, Microsoft Dynamics AX 2012 R2, Microsoft Dynamics AX 2012 Feature Pack, Microsoft Dynamics AX 2012
Microsoft Dynamics AX uses Microsoft SQL Server Reporting Services to render reports. Reporting Services retrieves report data from the Microsoft Dynamics AX Application Object Server (AOS) by using a custom extension that uses Windows Communication Foundation (WCF) to communicate with the AOS.
The size of the data set and the complexity of the report that is rendered can affect the time that is required to display the report. Additionally, if various time-outs and other thresholds are reached, the report rendering may fail. This topic provides tips that can help you prevent reports that run for a long time from timing out.

Use pre-processed RDP classes

If the report uses the Report Data Provider (RDP) to retrieve data, the report should be modified to use a pre-processed RDP class as the data source, so that processing logic is invoked before a call is made to Reporting Services. For more information about RDP classes, see Using Report Data Provider Classes to Access Report Data and Report Programming Guide.

Specify the report execution time-out period

Reporting Services has a report execution time-out period, which defines the number of seconds after which the reporting processing times out. The default value for this period is 30 minutes. If report execution requires more time, report execution fails.
You can specify the report execution time-out period for all reports by configuring the time-out settings at the level of the Reporting Services site. Alternatively, you can specify the report execution time-out period for a specific report.

Specify the report execution time-out period for all reports

The report execution timeout period at the level of the Reporting Services site should be set to a value that is more than the time that is required to render your largest report. Alternatively, report execution can be set so that it never times out. You can specify the report execution time-out period in Report Manager or SQL Server Management Studio.
If you want to specify the report execution time-out period by using Report Manager, follow these steps.
  1. Open the Report Manager website for the Reporting Services instance. By default, the URL is http://[SSRSServerName]:80/Reports.
  2. Click Site Settings. The Properties page is displayed.
  3. In the Report Timeout section, specify a time-out period by entering the number of seconds. Alternatively, you can choose not to have a time-out period.
  4. Click Apply to save your changes.
If you want to specify the report execution time-out period by using SQL Server Management Studio, follow these steps.
  1. Open SQL Server Management Studio, and connect to your Reporting Services instance.
  2. In the Object Explorer pane, right-click the name of your report server, and then select Properties. The Server Properties window is displayed.
  3. In the Select a page area, click Execution.
  4. Specify a time-out period by entering the number of seconds. Alternatively, you can choose not to have a time-out period.
  5. Click OK to save your changes.

Specify the report execution time-out period for a specific report

To specify the report execution time-out period for a specific report, follow these steps.
  1. Open the Report Manager website for the Reporting Services instance. By default, the URL is http://[SSRSServerName]:80/Reports.
  2. Click the DynamicsAX folder. The Microsoft Dynamics AX reports are listed.
  3. Hover over a specific report, click the drop-down arrow, and then click Manage. The properties page is displayed.
  4. Click the Processing Options tab.
  5. In the Report Timeout section, specify a time-out period by entering the number of seconds. Alternatively, you can choose not to have a time-out period.
  6. Click Apply to save your changes.

Specify the user session time-out period

Reporting Services maintains a user session, which may time out if report execution requires a long time. If the user session times out, the report rendering fails. You can resolve this issue by configuring two properties, SessionTimeout and SessionAccessTimeout, by using the rs.exe tool. These properties should be set to a value that is more than the time that is required to render your largest report.
On the Reporting Services server, follow these steps to configure the SessionTimeout and SessionAccessTimeout properties.
Caution noteCaution
Keeping a user session around longer than necessary can cause your ReportServerTempDB database to grow larger because temporary session snapshots will not be aged out as often.
  1. Create a SessionTimeout.rss file by following these steps:
    1. Open Notepad.
    2. Copy the following code into Notepad.
      Public Sub Main()
      Dim props() as [Property]
      props = new [Property] () { new [Property](), new [Property]() }

      props(0).Name = "SessionTimeout"
      props(0).Value = timeout

      props(1).Name = "SessionAccessTimeout"
      props(1).Value = timeout

      rs.SetSystemProperties(props)
      End Sub
    3. Save the file to the local hard drive as SessionTimout.rss.
  2. Open a Command Prompt window.
  3. Run the script that you created in step 1 by entering the following command. Keep the following points in mind:
    • By default, the URL of the report server is http://[SSRSServerName]:80/ReportServer.
    • The timeout value is expressed in seconds. In the example below, the SessionTimeout and SessionAccessTimeout properties are set to 20 hours.
    $>rs.exe -i <Path to SessionTimeout.rss file> -s <Report Server URL> -v timeout="72000"–l 0
    By default, the rs.exe tool is located at \Program Files\Microsoft SQL Server\110\Tools\Binn. For more information about how to use this tool, see the rs Utility (rs.exe) topic in the SQL Server documentation and the Session Timeout during execution blog post.

Specify WCF time-outs and thresholds

Reporting Services uses the Microsoft Dynamics AX query service to retrieve data. The Microsoft Dynamics AX query service is a WCF service that is exposed by the AOS. For reports that have large data sets, the default WCF configuration may cause WCF to reach some time-outs and thresholds at run time. To help prevent this issue, you can modify the WCF configuration in the following ways.

Configure the server-side settings

Follow these steps to configure server-side WCF settings. Complete these steps on the AOS server.
  1. Open the Ax32Serv.exe.config file. This file is typically located at \Program Files\Microsoft Dynamics AX\<version>\Server\MicrosoftDynamicsAX\Bin.
  2. Find the QueryServiceBinding element.
    </system.diagnostics>
    <system.serviceModel>
    <bindings>
    <netTcpBinding>
    <binding name="QueryServiceBinding" sendTimeout="00:10:00" transferMode="StreamedResponse" maxBufferSize="65536" maxReceivedMessageSize="104857600" listenBacklog="200" maxConnections="200">
    <readerQuotas maxStringContentLength="104857600" />
    </binding>

  3. The default value for the sendTimeout property on this element is 10 minutes. Increase the value for the sendTimeout property. For example, set the value to 30 minutes.
  4. Save your changes.

Configure the client-side settings

Reporting Services communicates with the AOS to retrieve data. In this communication process, Reporting Services acts as the client, and the AOS acts as the server. Therefore, to configure client-side WCF settings, you must follow these steps on the server where Reporting Services is installed.
  1. Create a new, local client configuration as explained in Manage a client configuration.
  2. While the configuration utility is still open, click the Connection tab.
  3. Click the Configure Services button.
  4. A message is displayed that indicates that the configuration will no longer be updated automatically after a change is made to the server. Click OK.
  5. A message may be displayed that indicates that you have to install the Windows SDK for Windows Server and the Microsoft .NET Framework version 4.0. In this case, download and install the Windows SDK for Windows Server and the .NET Framework version 4.0.
    After you install the Windows SDK, connect to the client configuration that you created in step 1. Then repeat steps 2 and 3 to open the Microsoft Service Configuration Editor.
  6. In the Configuration area, click Bindings > QueryServiceEndpoint (netTcpBinding).
  7. On the Bindings tab, modify the following properties:
    • SendTimeout – By default, this property is set to 10 minutes. Increase the value. For example, set the value to 30 minutes.
    • MaxReceivedMessageSize – By default, this property is set to 2147483647. Increase the value. For example, if you want to double the value, set it to 4294967294. The maximum value that is allowed is Int64.MaxValue.
  8. Click File > Save.

Use batch processing

To improve performance when you print statements or reports that include large amounts of data, use batch processing. When you use batch processing, you can run specific tasks as batch jobs, and then schedule those batch jobs to be run on a different computer (a batch server). When you move the processing of these tasks to a batch server, the report performance on the client computer can improve. You can also apply range restrictions to limit the size of each batch. You can improve performance by submitting multiple, smaller batches to be processed at the same time on different servers, instead of submitting one large batch.
Many tasks in Microsoft Dynamics AX can be run as part of batch jobs. For more information, see Batch processing overview.

How To: Addressing SSRS Session Timeouts

$
0
0

Microsoft Dynamics AX 2012

Dealing with Dynamics AX 2012 Reporting Timeouts and Thresholds

Dynamics AX 2012 uses SQL Server Reporting Services for rendering reports. SSRS gets the data from AOS by using a custom SSRS Extension that uses WCF to communicate with AOS.
Depending on the size of the data and the complexity of the report, it might take a long time for the report to execute, resulting in various timeout and other thresholds being hit, which might cause the report rendering to fail. This article attempts to identify all the places where rendering large reports may cause thresholds could be hit and suggest tweaks or workarounds to address them.

A. Getting data ready

If the report uses Report Data Provider (RDP) to get the data, then it should be modified to use a pre-processed RDP class as the data source to invoke processing logic before a call is made to Reporting Services. For more information about RDP classes, see Using Report Data Provider Classes to Access Report Data and Report Programming Guide.

B. Report Execution Timeout

SSRS defines a Report Execution Timeout, which specifies the number of seconds after which the reporting processing times out. The default value for this is 30mins. If the report execution takes longer than that, then the report execution will fail.
This setting can be updated at the Report Server level or at an individual report level
Site level Settings
The report execution timeout at the report server level should be set to a value greater than the time required for the largest report to render. Alternately it can be set to never time out. This can be done in one of two ways -
1. Using the Report Manager - From the Site settings, the Report Timeout property can be changed.
clip_image002
2. This can also be changed using SQL Server Management Studio – In SSMS, Right-click the name of a report server, then click Properties. On the Server Properties window, click the Execution page and change the value for “Limit report execution to the following number of seconds”.
Report level Settings
The report execution timeout can also be set on each report, using Report Manager. Go to the report properties (see how) and in Processing Options, select either “Do not timeout report” or change the “Limit report processing to the following number of seconds” option.
clip_image004

C. SSRS Session Timeout

SSRS maintains a User Session which may time out if the report takes a long time to execute, causing the report execution to fail. This can be fixed for the report server by setting the 2 properties SessionTimeout and SessionAccessTimeout using the rs.exe tool. Again, these should be set to be greater than the time taken to render the largest report.
You should configure these values to be no less than the time it takes to render your largest report. Here is a sample script for rs.exe which will set these values for you:


Public Sub Main()
Dim props() as [Property]
props = new [Property] () { new [Property](), new [Property]() }

props(0).Name = "SessionTimeout"
props(0).Value = timeout

props(1).Name = "SessionAccessTimeout"
props(1).Value = timeout

rs.SetSystemProperties(props)
End Sub


You can run this script with the following command:
rs.exe -i <Path to SessionTimeout.rss> -s <Report Server URL> -v timeout="6000"
The tool rs.exe is usually located at “c:\Program Files(x86)\Microsoft SQL Server\110\Tools\Bin”.
The timeout is expressed in seconds, so this example sets the SessionTimeout and SessionAccessTimeouts to about an hour and a half. 
Example: c:\Program Files(x86)\Microsoft SQL Server\110\Tools\Bin\rs.exe -i c:\Temp\sessiontimeout.rss -s http://localhost/reportserver -v timeout="6000"
Important: Do this with caution, keeping a session around longer than necessary can cause your ReportServerTempDB database to grow larger since temporary session snapshots will not be aged out as often.  Also, this utility must be run as Administrator.
You can also check out this msdn blog post for more information.

D. WCF Timeouts and Thresholds

SSRS uses the Query Service (which is a WCF service exposed by the AOS) to get data. For reports with large datasets, the default WCF configuration may cause WCF to hit some thresholds at runtime. So the WCF configuration can be tweaked as follows……

Updating Server side settings

1) Open the Ax32Serv.exe.config file (it is typically under c:\Program Files\Microsoft Dynamics AX\<version>\Server\MicrosoftDynamicsAX\Bin).
2) Locate the QueryServiceBinding element. The default value for the sendTimeout on this element is 10mins.
clip_image007
3) Increase the sendTimeout to a larger value, say 30mins, like so – sendTimeout=”00:30:00”

Updating Client configuration settings on the SSRS Server

1) Access the Microsoft SQL Reporting Services host using an account with Administrative privileges
2) Open the Microsoft Dynamics AX Configuration Utility (typically here: Start –> Administrative Tools –> Microsoft Dynamics AX 2012 Configuration)
3) Click the Configuration Target: drop-down and select Business Connector (non-interactive use only)
AX Client Configuration Utility dialog
4) Create a new local client configuration using the Microsoft Dynamics AX 2012 Configuration as explained here.
5) On the Connection tab, click on “Configure Services” to open the SVC configuration utility.
AX Client Configuration Utility dialog (Configure Services)
6) Expand the Bindings folder and select QueryServiceEndpoint (netTcpBinding).
7) Update the values for the following properties -
SendTimeout – This is set to 10 minutes by default. Increase it to a larger value, like 20 minutes.
ReceiveTimeout – This is set to 10 minutes by default. Increase it to a larger value, like 20 minutes.
Note:  If you are encountering connection failures due to the size of reports update the following….
MaxReceiveMessageSize – This is set to 2147483647 by default. Increase it to double that value or 4294967295. The maximum allowed value is Int64.MaxValue.
Configuration Services dialog

Debugging in Dynamics AX 2012 as a non-admin Role

$
0
0
To access breakpoints as a limited user:
1. open dynamics as admin
2. add yourself to your favorite role (in addition to admin)
3. shift control w to open a workspace.
4. insert a break point into a well know x++ class that the role accesses.
5. create a new job
6. insert this:
static void NonAdmin(Args _args)
{

securityutil::sysadminmode(false); 
}
7. run job
8. control w to open regular workspace (or open it from the menus, sometimes its sticky)
9. Open the form/class via navigation

observe that you can now stop in debugger as "limited" user

Creating Custom Number Sequences in Microsoft Dynamics AX 2012

$
0
0

Overview

Number sequences are unique identifiers that can be associated with a master record so that they can be individually distinguished. They can be either formatted as alpha-numeric strings or simply as numbers.
Microsoft Dynamics AX 2012 provides an easy to implement framework to generate custom number sequences.

Scenario

As part of this tutorial, a custom number sequence will be generated for the Customer Groups setup form (Accounts receivable àSetup à Customers à Customer groups)

Steps

  1. First create a new Extended Data Type (EDT). Open AOT àData Dictionary à Extended Data Types
  2. Right Click on Extended Data Types and create a new EDT NumSeqDemoCustGroupNum of type String
  3. Set the properties as shown below

  4. Now go to AOT à Classes and open the NumberSeqModuleCustomer class by right clicking it and selecting View Code

  5. In the loadModule method, add the following code after the last line of code
  6. //customer group number
    //define the EDT
    datatype.parmDatatypeId(extendedTypeNum(NumSeqDemoCustGroupNum));
    //define its default propertiesdatatype.parmReferenceHelp(literalStr(“Unique number for customer group”));datatype.parmWizardIsContinuous(true);datatype.parmWizardIsManual(NoYes::No);datatype.parmWizardIsChangeDownAllowed(NoYes::No);datatype.parmWizardIsChangeUpAllowed(NoYes::No);datatype.parmWizardHighest(999999);datatype.parmSortField(27);
    //define its scope
    datatype.addParameterType(NumberSeqParameterType::DataArea, truefalse);this.create(datatype);

  7. Now, go to AOT à Jobs and create a new job loadNumSeqCustDemo
  8. Write the following code in the job and then run it
    static void loadNumSeqCustDemo(Args _args){
    //define the class variableNumberSeqModuleCustomer seqMod = new NumberSeqModuleCustomer();
    //load the number sequences that were not generatedseqMod.load();}

  9. Now, go to Organization administration à Common à Number sequences à Number sequences
  10. Click on Generate button in the New button group
  11. In the Setup number sequences wizard, Press Next
  12. In the Setup set different values for the number sequence like the format, highest value and lowest value
  13. Click Next
  14. In the last step, Click Finish to generate the number sequences
  15. The number sequence is generated and can be used on the Customer Groups form
  16. Open AOT à Data Dictionary à Tables à CustGroup
  17. Add a new String field and set the properties as follows
  18. Add the newly added field in the Overview field group
  19. Now go to Forms àCustGroup and restore the form. It will add the newly added field in the grid
  20. Write the following code on the Class declaration node
     NumberSeqFormHandler numberSeqFormHandler;

  21. Create a new method on the form and write the following code
    NumberSeqFormHandler numberSeqFormHandler(){
    if (!numberSeqFormHandler){
    //create a reference of number sequence form handler class specifying the         EDT, Data source name and the field of the table
    numberSeqFormHandler =NumberSeqFormHandler::newForm(NumberSeqReference::findReference(extendedtypenum(NumSeqDemoCustGroupNum)).NumberSequenceId, element,CustGroup_DS,fieldnum(CustGroup,CustGroupNumber));}return numberSeqFormHandler;
    }

  22. Override the close method of the form and write the following code
    public void close(){
    if (numberSeqFormHandler)
    {numberSeqFormHandler.formMethodClose();}
    super();}

  23. Override the create method on the CustGroup data source and add the following code
    public void create(boolean _append = false){
    element.numberSeqFormHandler().formMethodDataSourceCreatePre();
    super(_append);
    element.numberSeqFormHandler().formMethodDataSourceCreate(true);}

  24. Override the write method on the CustGroup data source and add the following code
    public void write(){
    super();
    element.numberSeqFormHandler().formMethodDataSourceWrite();}

  25. Override the validateWrite method on the CustGroup data source and add the following code
    public boolean validateWrite(){
    boolean ret;
    ret = super();
    ret = element.numberSeqFormHandler().formMethodDataSourceValidateWrite(ret) && ret;
    return ret;}

  26. Override the delete method on the CustGroup data source and add the following code
    public
    void delete()
    {
    element.numberSeqFormHandler().formMethodDataSourceDelete();
    super();}

  27. Override the linkActive method on the CustGroup data source and add the following code
    public
    void linkActive()
    {
    element.numberSeqFormHandler().formMethodDataSourceLinkActive();
    super();}

  28. Now go to Accounts receivable à Setup à Customers à Customer groups
  29. Create a new record. The number sequence is generated according to the format defined as shown below

Tutorial: Debugging AX while still using restricted roles (non-admin)

$
0
0
Saw this on our internal forum today, and thought it's a nice thing to share with all of you.


1.       Close All AX instances

2.       Open AX Development Environment (ax32.exe -development)

3.       Open AX Application (ax32.exe)

4.       Add Role that you would like to test to your UserId

a.       System Administration > Common > Users

b.      Find yourself and double click to get to the detail window

c.       Assign role that you want to test

5.       Close AX Application

6.       Set break points in AX Development Environment

7.       Create a job and add this line

             SecurityUtil::sysAdminMode(false);
 
8.       From the AX Development Environment “Ctrl W” to open the application

9.       You are now in a reduced permission user and have the ability to debug. J
 

To get your environment back to full admin, re-execute the job in step 7 with a true.


Note: This will not work for EP, Services, direct BC.NET and cases using runas(), as the SecurityUtil::sysAdminMode is limited to the current session.


Ed Budrys from our Security team deserves the credit for the above.

Let me know if you found this to be useful.
Thanks

Tip: Use the debugger with limited permissions

$
0
0
First I would like to give also credits to a great person I have worked with on a project where we discovered the “hidden” feature within the Security Development Tool. His name is Peter Collewijn. Because there was an issue during the implementation which was only reproducible with limited access rights, we were looking for a way to find the culprit of it. He asked whether it was possible to use the Security test workspace in combination with the debugger. Where other people told him it was not possible and insane, I gave it a chance… So we tried out and read below the outcome…
Well in fact the procedure to achieve this is very easy. For this blog post I did not create a complex scenario for debugging, but simply added a breakpoint within an AX method to show you this feature. Before you start, make sure you do have the System administrator role within AX and you are assigned to the AD group Microsoft Dynamics AX Debugging Users.
  1. Put a breakpoint where you want to have the debugger enabled. In this example I have put the breakpoint on the add method of the class Info. In this way on every infolog message the debugger will be triggered.
    SDT6-01
  2. Open the security development tool form and select the role which raises the errors.
    SDT6-03
  3. Open the Security test workspace. You will get the next notification first. It will provide some basic information what will happen with your security settings in AX during the time you have the workspace opened.
    SDT6-04
  4. In this scenario I will just do something stupid to have AX raising an infolog by not filling all mandatory fields on the Number sequence form.
    SDT6-05
  5. Try to close the form or save the records and the debugger will be triggered.
    SDT6-06
Now you are able to debug using the permissions of another user. Note that business logic that runs in CIL will not be triggered when you put a breakpoint. You can then either disable the user option Execute business operations in CIL or attach the Microsoft Visual Studio debugger to the Application Object Server (AOS). Have also a look at the Technet page Debug in Interpreted Mode Your X++ Code that Runs as .NET CIL [AX 2012].

Tip: Turn on portal security

Normally when you open the Security test workspace, a new workspace will be opened and the session will be restricted to non Admin mode.  The current roles of the user will be disabled, except for the System administrator and System user role. The role which should be tested will be granted to the current user.
SDT6-07
Because you still have System administrator rights in AX, you cannot test the role on the enterprise portal and also SSRS reports will behave differently compared to normal user rights. As SSRS will use another thread connection towards the AOS, the SSRS started from the Security test workspace will have all rights on every table. So when you have tested the role, normal users can get an error on e.g. display methods because the user within the normal role has no access to certain tables.
To be able to test the Portal security and SSRS reports correctly you can enable the Portal security. If you then open the Security test workspace, you will get another message which you need to read very carefully! In this case also the System administrator role will be disabled. When something goes wrong, e.g. a client crash, you can’t login anymore because the System administrator role is disabled. The information message will tell you how to recover from this issue.
SDT6-08
Now you are able to fully test the role also with restricted rights for the Enterprise portal and SSRS reports. Note that when you use this option, the trick with the AX debugger is not working as you are not a System administrator during the time the Security test workspace is active.
That’s all for now. Till next time!

In Microsoft Dynamics AX, how to get a look up of all the tables available in AOT

$
0
0
This article explains you how to get a look up to a control for All the AOT tables and can answer to below relevant questions.
·  Create a look up to show all the tables from AOT.
·  How to develop a drop down to show all the AOT tables where user can select one among?
·  How to create an edit method in Microsoft Dynamics AX?


Applied on: Dynamics AX 2012 R2 
Prerequisite: X++; MorphX; Basic programming knowledge 
Target audience: Any

 
 

 












Output: Look up of all the AOT tables
  

Create a table with a single field. Here my table name is TableLookUp with field name TableObjID.
The field should be extended to RefTableId EDT as shown below.


Write an edit method to your table. I wrote a method in the name of tableObject2Name. The method looks like:


// This method returns table name
// BP deviation documented
edit TableName tableObject2Name(boolean _set, TableName _name) // System Documentation>Type>TableName
{
    Dictionary dictionary;
    TableId tableID;

    if (_set)
    {
        dictionary = new Dictionary(); // System Documentation>Classes>Dictionary
        tableID = dictionary.tableName2Id(_name);

        if (tableID)
        {
            this.TableObjID = tableID;
        }
    }
    return tableid2name(this.TableObjID);
}

 
Create a new form and use the table you created as data sources and method to get a control.
Here my form looks like:
 

<> 

AX 2012 Export Data with Outbound ports

$
0
0
I've recently made some tests with AIF (Application Integration Framework), inbound ports and outbound ports.
I had never used AIF before AX 2012 and this is a great tool once you understand how it works.
MSDN explains the subject quite correctly : Services and Application Integration Framweork 2012

At the beginning, I did not understand the concept of outbound port. SoI decided to take an interest in it. I've known web services for a long time now so I could correctly understand the "inbound port" concept; a service is "listening" at a specific "port", ready to execute your method with input parameters.
But what for outbound ports ? There is no socket "port" to pull out data....

Outbound ports are not related with web services or network at all (talking about WCF, bindings and endpoints). You can use it in order to retreive data from Dynamics AX and send the results to either a file or a message queue (MSMQ).


The following post will show a summary of the steps I followed in order to export customers in several XML files into a directory on the AOS server.



  1. I create an outbound port specifying "File system adapter" as adapter.
    You can limit it to a specific company if you want
    In the operations I choose : CustCustomerService.read

    This service operation will be used to read a customer from AX by specifying an account number.
    The output result of the operation will be the customer itself.
  2. I create a new folder on the AOS side (in the example C:\AIFOutputTemp).
    Don't forget to give the correct write access rights for the user executing your AOS Service.
    The AOS will need write access to create the file.
  3. I specify the newly created folder in the URI of the outbound port.
  4. I save the outbound port and I activate it.
  5. I create a new job to post some customer in the AIF Queue :

    static void AIF_SendCustomer(Args _args)
    {

        AxdSendContext axdSendContext = AxdSendContext::construct();
        AifEntityKey aifEntityKey = AifEntityKey::construct();
        Map keyData;
        AifConstraintList aifConstraintList = new AifConstraintList();
        AifConstraint aifConstraint = new AifConstraint();

        CustTable       custTable;
        ;

        custTable = CustTable::find("<CUSTACCOUNT>");
       
        keyData = SysDictTable::getKeyData(custTable);

        aifEntityKey.parmTableId(custTable.TableId);
        aifEntityKey.parmRecId(custTable.RecId);
        aifEntityKey.parmKeyDataMap(keyData);

        axdSendContext.parmXMLDocPurpose(XMLDocPurpose::Original);
        axdSendContext.parmSecurity(false);

        aifConstraint.parmType(AifConstraintType::NoConstraint) ;
        aifConstraintList.addConstraint(aifConstraint) ;

        AifSendService::submitDefault(
            classnum(CustCustomerService),
            aifEntityKey,
            aifConstraintList,
            AifSendMode::Async,
            axdSendContext.pack());

    }
  6. Once this code is executed, a new record will be created in the AIF Queue manager.

    You can reach the queue manager with the following path :
    System Administration > Periodic > Services and Application Integration Framework > Queue manager

    Status will be ready and you will see the document service operation involved.

  7. Now the instructions are created with correct parameters. You can either setup a batch system tha will automatically execute the following method, or you can simply execute it manually.
    There is an explanation of this at msdn : Configuring batch jobs for AIF
    AifOutboundProcessingService = new AifOutboundProcessingService();
    AifOutboundProcessingService.run();
  8. Once this method has been executed, you will be able to see the resulting newly created AIF message (XML result). For this, use the following menu : Document Log and View message




  9. Once the message has been created, we can execute the following method :

    AifGatewaySendService = new AifGatewaySendService();
    AifGatewaySendService.run();
  10. This operation will export the message to a physical XML file on the folder we specified in the outbound port.

 That's it for outbound ports.

    13 comments:

    1. Hi

      I have created some outbound services to export data from Ax to a interface.
      I tested them by passing a specific record in a job.
      But i want to run this outbound service as a RunBase batch class.( I want to create a batch job class and run the outbound serivce)
      So here i want to run the service for all the data in the service (query).
      How can i achieve this ?

      Thanks in advance
      Reply
      Replies
      1. Hi,

        In order to automate the export process you could create as you said a new class that inherit from RunBaseBatch and then running this class within a batch job.
        This will send the records to the queue.
        In order to automatically process the queue you filled with the class, take a look at this link : http://technet.microsoft.com/en-us/library/hh352328.aspx

        It explains how to configure the batch jobs for AIF.
      2. This comment has been removed by the author.
    2. Hello, here is the code you can use in order to export a list of customers with a Query :

      Query queryCustTable;
      AifActionId actionId;
      AifEndpointList endPointList;
      AifConstraintList aifConstraintList = new AifConstraintList();
      AifConstraint aifConstraint = new AifConstraint();
      AifOutboundProcessingService AifOutboundProcessingService;


      queryCustTable = new Query();
      queryCustTable.addDataSource(tableNum(custTable),"CustTable");
      queryCustTable.dataSourceTable(tableNum(custTable)).addRange(fieldNum(custTable,AccountNum)).value("13*");

      aifConstraint.parmType(AifConstraintType::NoConstraint);
      aifConstraintList.addConstraint(aifConstraint);

      actionId = AifSendService::getDefaultSendAction(classnum(CustCustomerService), AifSendActionType::SendByQuery);

      if (actionId)
      {
      endPointList = AifSendService::getEligibleEndpoints(actionId, aifConstraintList);
      if(endpointList.getEndpointCount()>0)
      AifSendService::submitFromQuery(actionId,endPointList,queryCustTable,AifSendMode::Async);
      }


      AifOutboundProcessingService = new AifOutboundProcessingService();
      AifOutboundProcessingService.run();

      Don't forget to change the operation on the outbound service from CustCustomerService.read to CustCustomerService.find
      Reply
      Replies
      1. How can i do the similar operation for a specific Inbound message
        Like through x++ i want to run a specific Inbound message
      2. Hello,

        Take a look a the AifInboundProcessingService class. It should do the job.
    3. where to write this code
      AifOutboundProcessingService = new AifOutboundProcessingService();
      AifOutboundProcessingService.run();
      and
      AifGatewaySendService = new AifGatewaySendService();
      AifGatewaySendService.run();
      Reply
    4. Hello,

      I would say it depends.

      The AifOutboundProcessingService will process all the requests that were placed in the AifOutboundProcessingQueue by the submitDefault method. It will send them in the AifGatewayQueue table.

      The AifGatewaySendService on the other hand will retrieve records that are
      in the AifGatewayQueue and will process the messages. In this case, as the outbound port is configured to export a file, it will export the message (XML file) in the specified directory.

      So you can use this code in different ways depending on your intentions :)
      Reply
    5. Hi,

      I am trying to export Items data using standard service "InventItemService"
      But when the file is exported i cannot see all the items in the file. Also i cannot see the newly created items in the file.

      If i try to export the specific item (which is not coming in the xml) through job the xml got generated for that item.

      But when the service is running for all the items some items and new items are not coming in the xml. Also the query returns the item data (which is not coming

      Am i missing anything ? what can be the reason for this .
      Reply
    6. Hello, your problem is weird... All the items should be exported.
      Honestly, I don't know what could be the cause. Do you use the same code to export only one item and all items ? I mean do you send "byKey" or "byQuery" ?
      Reply
    7. Hi Renauld,
      I have a question about preparation of entity key lists. The normal way is, to define this like you said: keyData = SysDictTable::getKeyData(custTable);
      In my case, I get the RecId but I need the naturalKey for my outbound update message instead. Do you know some samples to prepare this in the axd class? I've seen, there is a method called useNaturalKey and a possibility to manipulate the KeyMap in getEntityKeyReplaycmentElementMap. Do you have some experiences to do this? I can't find anything in the web.

      Best regards,
      Frank
      Reply
    8. Hi,
      I've created a query with vendtable and dirpartyview, and created a document service through AIFDocumentservicewizard and configured that in outbound port. Can anyone tell me how to access the service and send the details to an XML file
      Reply

    Creating DIEF Entities for DocuRef in Dynamics AX

    $
    0
    0
    Since it is nearly impossible to generalize the DocuRef table entity in Dynamics AX, we need to create the code that will allow the data to be inserted into the table. The biggest issue is that we need to get a RefRecId and RefTableId.

    Using Customer Notes for DocuRef in AX

    For this example, we will be using Customer Notes, but you can see how this would work with any other entity that can connect to DocuRef.
    The first thing we need to do is edit the existing customer entity staging table (DMFCustomerEntityTable). We need to add the field to the table for the Note value we want to put into DocuRef. Make sure the EDT type is set to Note so it will match up to the Note field on the DocuRef table. For each note you need to add, create another column into DMFCustomerEntityTable.
    The second step is to create a method in the DMFCustomerEntityClass class. Here is where you are going to handle the insert/update from the staging table to the DocuRef table. You can either create one of these for every note you want to create/update per customer or you can handle it all in the one method.
    public void generateNote(boolean _isUpdate)
    {
    DocuRef docuRef;
    str note;

    note = strLRTrim(entity.Note);

    if (note != ”)
    {
    select forUpdate docuRef
    where docuRef.RefRecId == target.RecId
    && docuRef.RefTableId == target.TableId
    && docuRef.Name == ‘Note Name’;

    if (docuRef)
    {
    docuRef.Notes = note;
    docuRef.update();
    }
    else
    {
    docuRef.clear();
    docuRef.RefRecId = target.RecId;
    docuRef.RefTableId = target.TableId;
    docuRef.Name = ‘Note Name’;
    docuRef.Notes = note;
    docuRef.TypeId = ‘Note’;
    docuRef.RefCompanyId = curext();
    docuRef.insert();
    }
    }
    }

    The next method that we need to look at is on the same class. In the insertUpdate() method we need to add the call to our new method.
    public Common insertUpdate(Common _target, boolean _callInsertLogic = falseboolean _callValidateLogic = false)
    {
    Common                             ret;
    CustTable                           custTable;
    boolean                             isUpdate;

    if(target.RecId)
    {
    isUpdate = true;
    }

    ret = super(_target, _callInsertLogic, _callValidateLogic);

    if (ret)
    {
    this.generateNote();
    }

    if (!this.parmIsCompare())
    {
    custTable = CustTable::find(entity.AccountNum);
    if (custTable && (entity.AccountBalance != 0) && !isUpdate) //check for customer/Accountbalance existance
    {
    this.generateBalance();
    }
    }
    return ret;
    }

    You can see that the call is after the super call, so the insert should already be run. This means we have a recID in target that will allow us to insert into DocuRef.
    You can see that using this method, we can easily add comments for a number of other entities. Simply go to the Relations on the DocRef table to see where it is hooked up to.

    AOT node compare

    $
    0
    0
    One of the most often used tool is the AOT node Compare utility, which has some improvements from the past but can be changed further.
    Now in AX 2012 the compare tool can be executed as CIL code rather than in the X++ runtime tier, which should give you considerable performance boost for larger code comparison. Here is a great post (The compare tool–and running X++ code as IL) from mfp about improvements that Microsoft did.
    On the other hand you can do some improvements yourself by adding the following line to the SysCompare class, selectionChanged method:
    This allows you that for example when you compare objects that is being imported from an XPO file, you will be able to add new objects which would not be allowed otherwise, like new fields.
    The other improvement you might want to do is to get back the good old AX 4.0 behaviour, by always having the lower layer on the top of the compare form, thus highlighting the new changes in blue colour. The simplest solution is adding a button next to the buttongroup on the SysCompareForm form to switch the layers around, with the following lines in the clicked method:

    AX 2012 - Understanding the changes to Table Relations

    $
    0
    0
    Well I hope everyone is having a fine week so far. Oh Wednesdays, the furthermost point between two weekends. It's a day of great productivity, as we push towards that glimpse, and hope for our Saturday and Sunday.

    To help you out during the middle of this week, I thought it was time we spent a little focus around the changes in AX 2012 around extended data types, and specifically around understanding the way relations are made.

    First off the bat, lets look to the Extended Data Types in the Database [AX 2012] In this article on MSDN, Microsoft points out some highlights about EDT's in AX 2012, specifically, highlighted is:


    "Starting in Microsoft Dynamics AX 2012, you can no longer define relations under an EDT element in the AOT. If your system has any EDT relations, you should migrate those relations to the appropriate table elements. For more information, see EDT Relation Migration Tool."

    On this same page, before we move forward in looking at what this means, there is a link onHow to: add Dimensions. Make sure to NOT follow that link. It has not been updated yet for AX 2012.

    Now with that aside, lets look at what this means for AX 2012. Back in AX 2009, relations would exist, on the EDT, for example, linking a specific EDT, to a reference table, in which the EDT was the primary key.

    Now in AX 2012, this has moved to the table relations level. So lets take the following for example. If I have a Table, and as part of it, I would like to have an EDT, that is controlled and is referenced to an underlying table itself. This EDT then, and the field that it represents would be a foreign key for this new table we are building.

    To help set this straight, lets look at the following.:


    With this, we have the relationship, that now lives at the Primary Table, that has a Foreign Key, represented by the EDT. The EDT, in turn is the Primary Key, for the underlying reference table.

    Moving ahead, lets see this in action, with a simple example. To see this in action, we will need a project, an Extended Datatype, or EDT, as well as our reference and Primary table.



    You will notice that I have already added the EDT, as the PrimaryField to the idbReference Table node. Also, I've created a Primary Index, and placed that field in the Primary Index, as set the index to not allow duplicates.

    Now that we have this base, we must move to the EDT, and add the idbReference, as a table reference to the EDT. This does not set the relation, instead it is the primary reference for the EDT.





    After this, we add the EDT, to the Primary table, which I've called ForeignKeyField, in order to help understand the point.



    Now that we have this, we next have to create our relation, from the Primary table to the reference table. We do this, and on the properties of the relation, we set that it's an EDTRelation = Yes.



    In doing this, we can now set the relation, via a Normal Relation Option, from our Primary Table, to our Reference Table field. In doing this, and because of the EDTRelation = Yes is true, we can now select what EDT should be used in this relation.



    After this we end up, with our goal, of having correctly related reference data, from one table, as part of the record for another table.



    Moving the relations to the table level makes correct sense. It's a change in how you've worked with AX in the past for this area, but it's a very good change. For example because of this table relations now support Dynamic Links, as well as unbound control lookups. Implementing the above, means also that when it comes time for form design, your reference field, from your Primary datasource, will enable correct lookup of data, from the reference table.

    This is important to understand, for those people who are doing new custom development work, and also for those who are focused on upgrades. There is Whitepaper, for those looking to how best and tackle this for upgrades. You can find that white paper here: Migrating EDT Relations

    Well I hope that on this fine Wednesday, you have a productive one, and hope that the above might come in handy, or better help your understanding of this topic, as it relates to new customization in AX 2012 as well as how this can impact and affect task when upgrading.

    That's all for now, till next time!

    Update: Fellow Blogging Peer Joris, over @ DAXMusings pointed a two other facts out, via twitter today for this post. First, "When you use an EDT with a reference table, it asks if you want to copy the relation onto the table where you're referencing" and Second, "[A]lso, the reference is important on EDT still for "view details" (goto main table) on dialogs." I would like to thank Joris for taking the time to add to this post, in a very positive way. These are good points that need to be understood, with the context of this post.

    "Visit the Dynamics AX Community Page today!"


    1 Comments:

    Blogger TonyZeigler said...
    I have a self-related table that I'm trying to setup. Let's say the tablename is WorkUnit, and it has a field called WorkUnitParent which refers to the recid from Workunit. I've setup the EDT with ReferenceTable WorkUnit, and given it a table relation of WorkUnitParent == WorkUnit.WorkUnitParent. On the table I have a relationship WorkUnit.WorkUnitParent == WorkUnit.recid, set to use EDTRelation, and specifying the EDT I created. On the form where I display the WorkUnitParent field, I get a view details option, but selecting it just re-opens the same form with the same workunit. (Instead of opening the form with the workunit's parent). Any hints on what I might be doing wrong?

    Thanks,
    Tony

    Developing a SSRS report using the Report Data Provider in Microsoft Dynamics AX 2012

    $
    0
    0

    Overview

    There are multiple methods to develop SSRS reports in Microsoft Dynamics AX 2012. This tutorial will guide you in developing Report Data Provider (RDP) based SSRS reports.
    RDP based SSRS Reports are used when complex business logic cannot be achieved using AOT query.

    Pre-requisites

    1. Microsoft Dynamics AX 2012
    2. Visual studio 2012
    3. SQL Server Reporting Services (SSRS) must be configured
    4. Reporting services extensions must be installed in Dynamics AX

    Important Concepts

    1. Report Data Provider (RDP) Class

    2. Report Data Provider Class is an X++ class that is used to access and process data for a SSRS report. The RDP class processes the business logic based on a specified parameter and/or query and returns a dataset to the reporting services. In order to create a RDP class in AX, you have to extend that class with SRSReportDataProviderBase. This tells AX that this class will be used by reporting services to process the data.
      Two important attributes are used in RDP classes:
      1. SRSReportQueryAttribute: specifies which AOT query will be used in this report. If the RDP class uses an AOT query to process data, define this attribute at the beginning of the class.
      2. SRSReportParameterAttribute: defines the data contract class that will be used by this report to prompt for parameter values. If the RDP class contains any parameters this define this attribute at the beginning of the class.
      Both the attributes are optional. If the report does not use any query or does not want any parameter to filter report data, these attributes do not need to be used.
    3. Data Contract Class

    4. A data contract class is an X++ class which contains parm methods with the DataMemberAttribute defined at the beginning of the method. This class is used to define one or more parameters that will be used in a SSRS report.
    5. Table

    An AX table is used as the dataset to store data for the report. The RDP class processes the data and stores it in the table which is then used by a SSRS report to render data.
    A table can be a temporary table (InMemory or TempDB) or a regular table, but it is Microsoft best practice to use a temporary table.
    The type of temporary table is based upon the performance considerations. InMemory temporary table is used when the data set is small, while TempDB is normally used for larger datasets to improve performance.

    Scenario

    As part of this tutorial, the report will print a list of customers and their invoiced sales order counts.

    Steps

    1. First of all, create a temporary table. Open AOT à Date Dictionary à Tables.
    2. Right Click on Tables and create a new Table called CustReportRDPDemoTmp.
    3. Set the TableType property to InMemory. This will define the table as a temporary table.


    4. Expand the CustReportRDPDemoTmp table node and add the following fields in the table:

    5. S. No.Field nameExtended Data TypeLabel
      1CustAccountCustAccount
      2NameName
      3SalesOrderInvoiceCountIntegerSales order invoiced

    6. The final table should look like the following:


    7. Now create a RDP class. Go to Classes and create a new class called CustReportRDPDemoDP by right clicking onClasses and selecting New Class. It is a best practice to suffix the RDP class name with DP .


    8. Open the Class declaration by right clicking on it and selecting View code.


    9. Now write the following code:
    10. Add a new method and name it getCustReportRDPDemoTmp. This method is mandatory because reporting services uses this method to get the table buffer containing the processed data. The SRSReportDataSetAttribute attribute is used to indicate the temporary table name and also tells the reporting services to use this method to retrieve the processed data.
    11. Write the following code in the method:
    12. Add a new method and name it processReport. This method contains the business logic and is called by reporting services to generate data.
    13. This method will query customer details and fill the temporary table buffer. Write the following code in the method:
    14. Now create a new report. Since the development of a SSRS report is done in Visual studio, we first need to create a new project in Visual studio.
    15. Open Visual studio. Go to File à New à Project
    16. In the Installed templates section select Microsoft Dynamics AX and then select Report Model in the right pane. Name the project RDPBasedDemo and press Ok.


    17. A new project will be created as shown below.


    18. Now add a new report in the project by right clicking on the project RDPBasedDemo à Add à Report.

    19. A report will be added to the project with the name Report1. Rename the report RDPBasedDemo.
    20. Now double click the report to open it.

    21. The description of the individual node is given below:
      1. Datasets: Datasets retrieve data from RDP class. It acts as a bridge between AX and the SSRS report. Only the fields added in the datasets can be used in a report.
      2. Designs: It defines the layout of the report.
      3. Images: It contains the images that you want to display in the SSRS report.
      4. Data Methods: It contains the business logic which can then be used in the report.
      5. Parameters: It is used to apply filtering to the data in a report. All the parameters defined in the data contract class are automatically added here when the RDP class is defined in the datasets.
    22. Now you will want to create a new Dataset by right clicking Datasets àAdd Dataset. Name it CustDetail.


    23. Select the CustDetail dataset and open the properties window. Set the Data Source Type to Report Data Provider. Then select the Query field. An ellipse button appears. Click it to open a dialog box.


    24. This dialog box lists all the RDP classes present in the AOT. Select CustReportRDPDemoDP and press Next.


    25. Select the fields to be displayed in the report and press OK. Only the fields selected in this dialog box can be shown in the report.


    26. There are two types of designs that can be created in a SSRS report:
      1. Auto design: Visual studio automatically creates a design based on the dataset provided. Auto design is the preferred method because it is easy and usually fulfills the majority scenarios.
      2. Precision Design: This is used when you need custom placement of fields or the layout of the report is too complex.
    27. In this demo we will use Auto Design. Now right click the Designs nodeàAdd àAuto Design. A new design is added. Rename it Design. It is recommended that you set the name of the Design to either ‘Design‘ or ‘Report‘.


    28. Now drag the CustDetail form to the Datasets node and drop it on the Design node. A table will be created which contain all the fields present in the data set. These fields will appear in the same order as in the report. So if you want to arrange the fields, right click the field and select either ‘move up’ or ‘move down’.
    29. The final design should look like the following:


    30. Now we have to define the layout of the report. Visual studio provides built in templates. Select the Design and open the properties window. Select ReportLayoutStyleTemplate in the LayoutTemplate field. Give a suitable title to the report.


    31. Select CustDetailTable under the Design node and open the properties window. SelectTableStyleAlternatingRowsTemplate in the Style Template field.


    32. The report is now completed and can be viewed. To preview the report, select the Design node, right click it and select preview.


    33. Select the Report tab. The report will appear as shown below:

    34. To view this report from AX, Add the report to AOT and create an Output menu item and set the appropriate Properties. For further details on creating SSRS reporting, refer to our previous article ‘Developing SSRS report using Query‘.


    Using Controller Class in Developing SSRS Reports in Microsoft Dynamics AX 2012

    $
    0
    0

    Overview

    Controller class is used to control the report execution as well as preprocessing of the report data. The SSRS reporting framework uses this class to modify the report dialogs, calling the SQL Server reporting services, as well preprocessing parameters for the report.
    Following are the scenarios where Controller class can be used:
    1. Modifying a report query based on the input data
    2. Modifying report contract data based on the input data
    3. Control a report parameters dialog
    4. Open different reports/designs from the same menu item based on the input data
    5. Reports that are opened from a form
    To create a controller class, extend it with SrsReportRunController.

    Prerequisites

    1. Microsoft Dynamics AX 2012
    2. Reporting services extensions must be installed in Dynamics AX

    Sample Controller Class

    1. Create a new class. Open AOT → Classes
    2. Right Click on Classes and select New Class. Name it as SSRSDemoController.
    3. example of creating a new class in Dynamics AX
       
    4. Open the Class declaration by right clicking on it and selecting View code.
    5. example of viewing code in AOT class AX
       
    6. Now write the following code:
    7. Create a new method and write the following code:
    8. Examples of Controller Class Usage

      Based on different scenarios, different methods are overridden as shown in the following examples:
      1. Modifying report query based on the input data

    • Used in those scenarios where a report query needs to be modified based on the caller args parameters or recorded before the report parameter dialog is rendered.
    • Override prePromptModifyContract method to modify the report query as shown below:
    Note: prePromptModifyContract is called by report controller before the parameter dialog is shown to the User.
  1. Modifying report contract data based on the input data

    • Used in those scenarios where report contract parameters need to be modified based on the caller args prior to the execution of the report.
    • Override preRunModifyContract method to modify the report contract as shown below:
    Note: preRunModifyContract is called by report controller before the report is run.
  2. Control report parameters dialog

    • In some scenarios, a report parameter dialog should not be visible to the end user. Controller class is also used to control the visibility of the report parameter UI.
    • Add the following code in the main method of the controller class before startOperation method call to hide/show the report parameter UI:
  3. Open different reports from the same menu item based on the input data

    • It is used in those scenarios where different reports or different designs of a same report need to be opened from a same menu item depending upon the caller args.
    • Write the following code in main method to achieve this scenario:
  4. Reports that are opened from a form

    • Controller class is also used when reports are opened from a form and are needed to show selected record details.
    • Use either prePromptModifyContract method or preRunModifyContract method to achieve this scenario.

    RDP, Contract, UI Builder and Controller classes for SSRS report Development

    $
    0
    0
    Without going into more details over individual classes (RDP, Contract, UI Builder and Controller) I am writing this post to develop an SSRS report using all these classes (some of them are optional). Let’s find out how?

    RDP Class
    Details of this class are here;

    Create a new class in AOT, I have named it FF_ReportDP and extends it from SRSReportDataProviderBase


    SRSReportQueryAttribute: specifies which AOT query will be used in this report. If the RDP class uses an AOT query to process data, define this attribute at the beginning of the class.
    SRSReportParameterAttribute: defines the data contract class that will be used by this report to prompt for parameter values. If the RDP class contains any parameters this define this attribute at the beginning of the class.
    NOTE: Both the attributes are optional. If the report does not use any query or does not want any parameter to filter report data, these attributes do not need to be used.

    Add a new method and name it getFF_ReportReportTmp. This method is mandatory because reporting services uses this method to get the table buffer containing the processed data. The SRSReportDataSetAttributeattribute is used to indicate the temporary table name and also tells the reporting services to use this method to retrieve the processed data.

     








    Add a new method and name it processReport. This method contains the business logic and is called by reporting services to generate data.


    Initialize and retrieve contract class parameters in processReport method to get filtered records from tables based on parameter inputs. e.g. Customer account is added as a range to retrieve data only for selected customer.
    Tip: For better performance RecordInsertList is used in processReport method to write data into database in one call.

    Contract class

    Create a new class and name it FF_ReportContract
    In this class I have added two parameters Company and CustomerAccount which can be used to filter data on report either passing values from a menuitem or from a class or while calling report from a form. I will address this later in this or next post.

    It will tell the contract class to build the parameter dialog. In other words, it will link the UI Builder Class with the contract class.



    Parameter methods: 











    UI Builder Class

    Details of this class are here;

    Create a new class and extends it from SysOperationAutomaticUIBuilder







    Override build method to create own dialog box







    Override postBuild method

    New method to create custom lookup of all companies in AX

    Controller class


    Create a new class and extends it from SrsReportRunController








    Create a new Main method to call this class from a menu item which will internally call SSRS report

     
    Add a new output menu item in AOT with name FF_ReportController with propertied Object type = Class and Object = FF_RerportController.
    Run menu item and this will be the output dialog screen
     





    Accounts receivable group parameters are three ranges added in CustBalanceList query which is used in this report example.

    To hide these group parameters override showQueryValues method and return false.



     






    After hiding parameters












    AOT node compare

    $
    0
    0
    One of the most often used tool is the AOT node Compare utility, which has some improvements from the past but can be changed further.
    Now in AX 2012 the compare tool can be executed as CIL code rather than in the X++ runtime tier, which should give you considerable performance boost for larger code comparison. Here is a great post (The compare tool–and running X++ code as IL) from mfp about improvements that Microsoft did.
    On the other hand you can do some improvements yourself by adding the following line to the SysCompare class, selectionChanged method:
    This allows you that for example when you compare objects that is being imported from an XPO file, you will be able to add new objects which would not be allowed otherwise, like new fields.
    The other improvement you might want to do is to get back the good old AX 4.0 behaviour, by always having the lower layer on the top of the compare form, thus highlighting the new changes in blue colour. The simplest solution is adding a button next to the buttongroup on the SysCompareForm form to switch the layers around, with the following lines in the clicked method:

    Dynamics Ax Query Extended Range Value Expression

    $
    0
    0

    Dynamics Ax Query Extended Range Value Expression

    In this article we will see how to define extended query ranges using expressions. These expressions can be used in any query where you need to express a range that is more complex than is possible with the usual range value notations.

    The rules for creating query range value expressions are:
    • Enclose the whole expression in parentheses.
    • Enclose all subexpressions in parentheses.
    • Use the relational and logical operators available in X++.
    • Only use field names from the range's data source.
    • Use the dataSource.field notation for fields from other data sources in the query.
    • Values must be constants in the expression, so any function or outside variable must be calculated before the expression is evaluated by the query. This is typically done by using the strFmt function.
    Let's do it with some examples:

    OR clause on same field:
        Query q;
        QueryBuildDataSource qbd;
        QueryBuildRange qbr;
        q = new Query();
        qbd = q.addDataSource(TableNum(CustTable));
        qbr = qbd.addRange(FieldNum(CustTable, AccountNum));
        qbr.value('4005, 4006');

    Result: SELECT * FROM CustTable WHERE ((AccountNum = N'4005' OR AccountNum = N'4006'))

    or use this expression to achieve the same...

        qbr.value(strFmt('((AccountNum == "%1") || (AccountNum == "%2"))',
                QueryValue('4005'),
                QueryValue('4006')));

    Result: SELECT * FROM CustTable WHERE ((((AccountNum == "4005") || (AccountNum == "4006"))))

    OR clause on different fields: In this example we are using DataAreaId field to obtain range object but actual range is on AccountNum and Name. This means when you use range value expressions you can use any field to obtain range object and use it to insert your range in the query. The field you use to get range object is not included in the query.  I usually use DataAreaId to denote that this is a special range.

        qbr = qbd.addRange(FieldNum(CustTable, DataAreaId));
        qbr.value(strFmt('((%1 == "4000") || (%2 == "The Bulb"))',
                fieldStr(CustTable, AccountNum),
                fieldStr(CustTable, Name)));

    Result: SELECT * FROM CustTable WHERE ((((AccountNum == "4000") || (Name == "The Bulb"))))

    The above result can also be obtained with this below example. The only difference is we are using DataSource name as well. This will also give you some idea on how to use expressions when more than one DataSources are involved.

        qbr = qbd.addRange(FieldNum(CustTable, DataAreaId));
        qbr.value(strFmt('((%1.%2 == "4000") || (%1.%3 == "The Bulb"))',
                qbd.name(),
                fieldStr(CustTable, AccountNum),
                fieldStr(CustTable, Name)));

    Result: SELECT * FROM CustTable WHERE ((((CustTable_1.AccountNum == "4000") || (CustTable_1.Name == "The Bulb"))))

    Query range value expressions are evaluated only at run time, so there is no compile-time checking. If the expression cannot be understood, a modal box will appear at run time that states "Unable to parse the value."


    Feel free to post any comment / feedback here.

    Reduce SSRS deployment time for static reports in AX2012

    $
    0
    0
    Are you wasting minutes deploying and redeploying static SSRS reports in all the languages provided with AX2012? If you only need a handful of them, you might just as well consider disabling the licenses for the unwanted languages. You can enable them back if you need them later.

    Now disabling one language at a time manually might not be your cup of tea, so I would like to share a small job that disables all languages except the ones you want to keep enabled. Just create a new job and paste in the code below. Use at own risk of course, take backups and backups of the backups etc (you know the drill).

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    // Remove licence codes for unwanted languages
    staticvoidAdLanguageRemover(Args _args)
    {
        SysConfig                   sysConfig;
        SysRemoveLicense            remLic;
         
        Query                       query;
        QueryBuildDataSource        qbd;
        QueryBuildRange             qbr;
        QueryRun                    queryRun;
         
        FormRun                     confirmForm;
        Set                         languagesToKeep = newSet(Types::String);
        Set                         licenseCodeSet  = newSet(Types::Integer);
        SetEnumerator               it;
        int                         confCount       = 0;
        boolean                     licenseChanged  = false;
        Args                        args            = newArgs(formStr(SysLicenseCompareForm));   
        boolean                     proceed         = false;
        SysLicenseCodeDescription   codeDescription;
        str                         currentLanguageId;
        int                         pos, sysConfigId;   
         
        // List of languages to keep. Add, remove, change to fit your preference
        languagesToKeep.add('nb-no');
        languagesToKeep.add('en-us');
        languagesToKeep.add('sv');
        languagesToKeep.add('nl');
        languagesToKeep.add('fr');
        languagesToKeep.add('da');
        languagesToKeep.add('de');   
         
        query = newQuery();
        qbd = query.addDataSource(tableNum(sysConfig));
         
        qbr = qbd.addRange(fieldNum(SysConfig,ConfigType));
        qbr.value(enum2Value(ConfigType::AccessCodes));
         
        qbr = qbd.addRange(fieldNum(SysConfig,Id));
        qbr.value(SysLicenseCodeReadFile::rangeLanguage());
         
        queryRun = newQueryRun(query);
         
        delete_from remLic;
             
        while(queryRun.next())
        {
            if(queryRun.changed(tableNum(sysConfig)))
            {
                sysConfig = queryRun.get(tableNum(sysConfig));
            }
             
            codeDescription     = SysLicenseCodeReadFile::codeDescription(sysConfig.Id);
            pos                 = strFind(codeDescription,'(',strLen(codeDescription),-strLen(codeDescription));
            currentLanguageId   = subStr(codeDescription,pos+1,strLen(codeDescription)-pos-1);
             
            if(!languagesToKeep.in(currentLanguageId))
            {
                warning(strFmt('Removing language %1',SysLicenseCodeReadFile::codeDescription(sysConfig.Id)));
                licenseCodeSet.add(sysConfig.Id);
                remLic.clear();
                remLic.LicenseCode = sysConfig.Id;
                remLic.Description = SysLicenseCodeReadFile::codeDescription(sysConfig.Id);
                remLic.insert();
            }
            else
            {
                info(strFmt('Keeping language %1',SysLicenseCodeReadFile::codeDescription(sysConfig.Id)));
            }
         
        }
         
        if(licenseCodeSet.elements())
        {
            // if not valid code, then we should display the warning           
            confCount   = SysLicenseCodeReadFile::findConfigKeysFromLicenseCodeSet(licenseCodeSet);
     
            confirmForm = classfactory.formRunClass(args);
            confirmForm.init();
            confirmForm.run();       
            confirmForm.wait(); 
             
            if(confirmForm.closedOk())
            {
                it = licenseCodeSet.getEnumerator();
                while(it.moveNext())
                {
                    sysConfigId = it.current();
                    
                    update_recordSet sysConfig
                        setting value = ''
                        where sysConfig.id == sysConfigId;
                     
                }
                 
                SysLicenseCodeReadFile::codesModified();
            }
        }
    }

    Allow for a synchronization to run through after the licenses are modified. Remember that this may impact the database schema, but if you really do not want the (ie.) Norwegian language to be enabled, it should be safe to disable. Thanks for reading!
    Viewing all 181 articles
    Browse latest View live


    <script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>