Wednesday, March 31, 2010
How to set the language used on forms and menus
Tuesday, March 30, 2010
Fun and games with Ax
An overview:
How to filter records in a form by code
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
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
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
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
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
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
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
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
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
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
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