Wednesday, March 31, 2010

How to set the language used on forms and menus

Ax is available in many languages (approx 45 I believe). It comes in Spanish, English, Polish, German, Arabic, ... thereby underlining it's global presence.

In reports, you can use any supported language, weather you bought a license key for it or not.

This policy makes senses, as your company, your customers or vendors may not speak the same language. When you exchange documents (for example an order), you need to understand its contents.

I had a post about how to change the language in a report a while back.

Another thing is the user interface, the client you do your every day work in. You can set the language used in forms and menus with the user options. You can find them under Administration - Users - User Options.

In this setting, you are limited to the languages you bought a license for.

Now you can set the language used in the user interface by code as well. And strangely enough, this way you are not limited to the set of licensed languages (this applies to MS Dynamics Ax 2009 SP1).

You can set the language like this

infolog.language('en-us');

Play around with it and see what's available!



Tuesday, March 30, 2010

Fun and games with Ax

Every Windows OS version has its games included.

They're fun for the guys who programmed them, they are useful for the users who play them. And vice versa. (Solitaire in Windows for example helps you exercise your mouse control).

Ax comes with some games as well, showcasing some of it's programming techniques.
You'll find them hidden in the AOT under the forms node, just lookup under 'Tutorial'.


An overview:


Horserace (form tutorial_HorseRace)

A simple betting game, showcasing a progress bar and using random numbers.




Memory (form tutorial_Memory)

A very basic memory game, with buttons on a form.


Tetris (form tutorial_Tetris)

An implementation of the arcade hit Tetris. With graphics. And a timeOut implementation. And catching keystrokes with the task method.


During some extra time a couple of years ago, I extended the Tetris game in Ax. I've included some start levels in them. Feel free to download the modified version from my SkyDrive here .


The above games are included in Ax 2009. The older Ax versions had some more built-in fun, like BattleShips,


or Tic Tac Toe.

The people over at Trucos Ax, an Axapta community site in the Spanish language, even have a link up for a Space Invaders game in Ax. Cool! Maybe the gameplay isn't as smooth as you can find in the arcade hall, but this is definitely a solid effort to bring back that old spirit.

Know some more ways to have fun with Ax? Share it in the comments!

How to filter records in a form by code

The standard filter functionality in Ax forms is a neat and powerful feature.
Using this filter functionality in your code is something you'll definitely use at some point in time as a programmer.

Although it's possible to do it in a single line of code, I prefer a 3 step solution. That way it's more flexible.
Let me show you by example. We'll filter the customers records in form CustTable, only showing customers with currency USD.

Step 1: Declare a class variable
In the ClassDeclaration method of the form, define a range.

QueryBuildRange CurrencyQBR;

Step 2: Instantiate the new range.
In the init method on the datasource of the form, you assign the range to a specific field (after the super call).

public void init()
{
super();

CurrencyQBR = this.query().dataSourceName('CustTable').addRange(fieldnum(CustTable,Currency));
}

Step 3: In the last step, you assign a value to the range.
This is done in the executeQuery method on the same datasource of the form. Before the super call. Like this:

public void executeQuery()
{ ;

CurrencyQBR.value(queryvalue('USD'));

super();
}

You're done! When you open the form, your customer records are filtered, you only get the customers with currencycode USD set up.




Like I said in the intro of this post, this can be done in one line of code as well.
In the init method of the form datasource, after the super call, place this code:

this.query().dataSourceName('CustTable').addRange(fieldnum(CustTable,Currency)).value(queryvalue('USD'));

But this way, it's fixed. If you choose the 3 step method, you could for example use a variable in the range value. The way to go would be to place an input field on your form, get the value from it and supply it in the executeQuery method.

For example like this:

public void executeQuery()
{ ;

CurrencyQBR.value(queryvalue(MyInputField.text()));

super();
}

Just make sure the executeQuery method is executed, thus applying the desired filter (maybe be using a button on your form to activate it).
Of course it's possible to combine multiple querybuildranges.

Thursday, March 25, 2010

How to create a GUID in Ax

In my previous post I talked about the Ax cache file and how it's name is created by using a GUID. Actually GUIDs are used in several places in Ax, like AIF for example.

You can create your own GUID with Ax as well, if you have a need for one. The WinAPI class has a method for that.

Example:

static void CreateGUID(Args _args)
{ str myGUID;
;
myGUID=Winapi::createGUID();

info(myGUID);
}

This method depends on the kernel function newguid() to create a globally unique identifier.

Note that you can have the GUID with or without the { }.

Wednesday, March 24, 2010

More information about the AUC file

What does AUC stand for?

AUC is short for Application Unicode Cache.


What is the AUC file used for?

The file contains a cache. Objects of the application object tree from Ax are stored locally, so that they don't have to be read from the AOS server over and over again.
So it should speed things up a bit.


Where can you find the AUC file?

This cache file is stored together with the user profile information. And as so it's location varies according to the operating system version used.

You can find it in following folder:
Windows XP and Windows 2003
C:\Documents and Settings\%username%\Local Settings\Application Data

Windows Vista, Windows 7 and Windows 2008
C:\Users\%username%\AppData\Local


Can you delete the AUC file?

Barack Obama and I agree: Yes you can!
Close your Ax client session, delete the AUC file. The AUC file gets recreated automatically next time you logon to Ax.


How is the naming of the AUC file defined?

In older Ax versions (3 and 4), the filename for the cache file is based on the AOS name, server name etc. In Ax 2009, the name is based on a GUID. The GUID used is the one you can find in the Ax table SysSqmSettings (field GlobalGuid).
As such, watch out with duplicating databases for test purposes. You don't want 2 Ax server instances using the same GUID and as such the same cache file, as this will lead to unexpected results. (Should this happen: Replace the GUID in table SysSqmSettings with an empty one. A new GUID will be created by the AOS at restart.)

Tuesday, March 23, 2010

The Cloud

You've heard about it. You've read about it.
Maybe you've worked with it. Or only had a short test drive with it.

What am I talking about? The cloud.

Or to be more precise: Microsoft's offering for the cloud. What's in?

Over at ZDNet, they do not believe in Microsoft as a serious cloud contender. A very critical voice is heard on Microsoft's cloud offering in this article, titled 'Why Microsoft really, really, hates the cloud'.
Summing it up: In 5 years Microsoft's operating income will drop by 75 %. Because it cannot adapt to the changing IT environment.

My opinion, for what it is worth: A bit over simplified reasoning in this article.

Will we see a move to cloud based applications: Absolutely. Your communications will be without doubt cloud based. Email only being one of them.
Your basic word processing, spreadsheet functions will be cloud based. I can dig that. Image, audio and video management, sure.
But your business critical applications like an ERP system? I don't think so. Partially maybe.
Small companies (-25 employees) will be interested. But anything above that?

I also do not see a reason on why Microsoft cannot adapt and change it's income pattern (that's without doubt needed). But are Cisco or IBM better positioned then Microsoft in this area then?

Microsoft is not postponing the arrival of Cloud Computing as stated in the article at ZDNet. It's just gearing up things and when and where there is money to be made with the Cloud, Microsoft will be around.
Remember the spread sheet. Lotus 1-2-3 was once number one. And the word processor, with WordPerfect. Or the internet browser.
Maybe this time around Microsoft's rival isn't another software company.

Wednesday, March 17, 2010

How to get rid of the report scaling message the lazy way

When a report doesn't fit on a page, depending on it's properties Ax will resize the report. This is a powerful and very useful feature.
Now Ax will inform you that the report has been rescaled (Report is scaled xx percent to fit to page) and this message is generally not well received by users.




Users are annoyed by the message, they get it every time they run the report, they cannot do anything about it, they have to click to close the infolog, ...

Ax has a builtin feature to suppress this scaling message. You can modify the init method of your report, and add something like this:


this.printJobSettings().suppressScalingMessage(true);


This is very effective and will do the job.
Only, this requires you to modify every report with these kind of messages.


A nicer way would be if we could switch it off in one place for all reports. Fortunately, this is possible as well.

Go to class SysReportRun, in the Run method, place following code before the call to super:

if(this.printJobSettings())
this.printJobSettings().suppressScalingMessage(true);



Now we don't have to modify each and every report and our users are happy.

Note that you can still override the settings in your report. In some reports this is done by default, like SalesInvoice and SalesConfirm.

Monday, March 15, 2010

An unknown error occurred while accessing an unnamed file

When working with the Microsoft Dynamics AX Debugger the other day, we ran into following error:










An unknown error occurred while accessing an unnamed file. So I guess Ax doesn't have a clue on what's going on, right?




The Ax client crashed, the debugger came up with the above error text. It was not possible to close the debugger the proper way, we had to kill the process.


Once we got this error, we always got it with every breakpoint that was set in the code.

We googled the web for a solution or a cause, but came up short of an answer. Badly written X++ code probably caused the crash of the client, which in term caused the debugger to respond with the error message (after all, when there is no client to connect, there's nothing to debug)


The only solution left for us was to restart the AOS service (and rewrite our custom code). After that, everything runned fine again.

How to set security on a temporary table

The security for temporary tables works exactly the same way as for "normal" tables.
Meaning you can set the security in the AOT with the table properties. Like this


Now the appropriate rights are effective for the security groups a user is member of.

But... (There had to be a but, otherways there wasn't a blog post about this subject, right?)
These temporary tables don't show up in the list when assigning rights to security groups.


Strangely enough, there are multiple temporary tables in the AOT with a security key attached to them. For example tables TmpDimTransExtract, TmpInventBalance, TmpInventAge, TmpPackMaterialFeeSum, ...

When a non administrator user has use of them, he or she will get an error like this:
(for example when you run a report based on a temporary table)

Unable to run report xyz due to access restriction in table ABC.
Object 'ReportRun' could not be created


I see 2 ways of solving this issue:

1) Remove the security key in the table properties
(Wether it makes sense to setup a security key to a temporary table in the first place is an interesting discussion.)

2) Change some code, either in the SysDictTable class or the SysSecurity class.
With this modification, you can assign rights for temporary tables to the user groups, just like you can with "normal" tables.


For the SysDictTable class, change the allowSecuritySetup method. Comment out the call to isTmp().



Alternative: For the SysSecurity class, change the expandSecurityKey method.

Make sure the AllowSecuritySetup method for SysDictTable isn't called for temporary tables.


This is a bit of a contradiction in Ax. According to the code, security is not expected to be setup for temporary tables. You cannot assign rights to user groups for them. But in the SYS layer of the AOT, there are a few temporary tables setup with security keys.


(If you've already openend the user group permissions form, you may have to restart the client after the above modifications, as the information in this form is cached.)

Tuesday, March 9, 2010

How to create a new workspace from code

One of the nicer things in Ax is the possibility to start a whole new workspace from scratch, just next to the one you are working on right now.
So when you're in the middle of something in one module in Ax and get a phone call for something completely different, this gives you the option to simply start with a clean sheet, handle the phone call and then go back to where you were with your first business.

For a user, this option can be manually activated by the keyboard combination CTRL +W.

From code, you can access this by using the xInfo class. Like this:

new xInfo().createWorkspaceWindow();

Saturday, March 6, 2010

How to set up a tax exempt text on your sales documents

I don't know about you, but in our company the accountants are more nervous then ever with all the new tax (VAT) regulations that got into effect January 2010.

We needed to update some of our legal stuff regarding VAT calculations that goes onto the sales documents, so I decided to create a (very) short checklist on how to set this up.

Step 1: Create the tax exempt code.

Go to General Ledger - Setup - Tax - Tax exempt code.

Create the necessay exempt codes. Also create any necessary language texts.


Step 2: Assign the exempt codes to tax codes in the tax groups.

Go to General Ledger - Setup - Tax - Tax groups.

Select the desired group and go to the tab page Setup.
Now for the appropriate tax codes, mark the field Tax Exempt and fill in the Tax Exempt Codes.


And that's it.
When the right tax setup is applied on for example a sales invoice, the tax exempt text will be shown in the appropriate language.

One small remark:


Maybe you have a pretty long exempt text set up ("... chapter xyz from law 123 ..."). And you notice that only a part of the text ends up on your sales documents.


Reason: A language text in Ax is bound to EDT LanguageTxtText, a Memo field. But in the code, EDT Description is used for the actual text. This EDT has only 60 characters.


Solution: Modify class TaxSpec\ClassDeclaration.

Replace

Description taxExemptDescription;

with

LanguageTxtText taxExemptDescription;


That will do the trick.

How to get the different parts of a real number

A real basically has 2 parts: the part before and the part behind the decimal separator. The integer part and the decimal part.
It's easy to get both parts separated, by using these functions: trunc and frac.



trunc

Truncates a real number by removing any decimal places.


Example


trunc (123.45) equals 123



frac

This function retrieves the decimal part of a real number.


Example


frac (123.45) equals 0.45


Note: The help section for function trunc says that 'numbers are always rounded down to a complete integer'.
Now if you've read my previous post about rounding in Ax, you may find this remark not to be completely true. Rounding to zero is closer to the truth.

Example

trunc ( -9.1 ) equals -9

while

rounddown (- 9.1 , 1) equals -10
roundzero ( -9.1 , 1) equals -9

On the other hand: rounddown ( -9.1 , -1) equals -9



Now I don't use these functions that often. But when I need them, I always have to go to the back of my head to remember their names.
So I thought I post them up here, as my online help.

How to round numbers

There is a versatile function in Ax to perform rounding operations: round.
This function rounds the first real argument to the nearest multiple of the second real argument. So plenty of possibilities, for example

round ( 1.2 , 1) equals 1
round ( 1.2 , 5) equals 0
round ( 6.4 , 5) equals 5
round ( 7.5 , 5) equals 10
round ( 1.2 , 0.5) equals 1
round ( 1.12 , 0.1) equals 1.1

If you don't want to work with the multiples of the second argument and instead just want to specify a number of decimals places to round, you can use decround.
This functions rounds the first real argument to the number of decimals specified (second argument). So for example

decround (1.2 , 0) equals 1
decround (1.23 , 0) equals 1
decround (1.23 , 1) equals 1.2
decround (1.25 , 1) equals 1.3

But the second argument can be negative as well. Like this:

decround (123, -2) equals 100


Now for rounding with a little twist: roundup.

If you want to round a number up to the next real value, you can use roundup. This function is the same as the ceiling function from other environments.
Same format as the previous round functions, needing 2 arguments.
So for example

roundup ( 1.23 , 0.1) give as result 1.3
roundup ( 123 , 5) equals 125
roundup ( 123.23 , 5) equals 125
roundup ( 123.23 , 1) equals 124

If that ain't enough, more rounding functions: rounddown and roundzero.
Rounddown rounds your real value always down to the multiple of your second argument.
While roundzero, as the function name says, rounds towards zero.
The difference you can see in the next example:

rounddown (-9 , 5) equals -10
roundzero (-9 , 5) equals -5