Monday, 14 April 2008

Field Captions and the CaptionClass Property

I recently needed to have a field that had a changing field caption based upon some conditions. I knew I had seen this in the standard system for sales orders where the Unit Price field changes between “Unit Price Incl. VAT” and “Unit Price Excl. VAT” depending on how you tick the “Prices Including VAT” field so that is where I started.

If you look on the Unit Price field properties on the Sales Line table you will see that the CaptionClass property is set to GetCaptionClass(FIELDNO("Unit Price")).

That looks like a function call so I take a look in the functions on the table.

GetCaptionClass(FieldNumber : Integer) : Text[80]
IF NOT SalesHeader.GET("Document Type","Document No.") THEN BEGIN
SalesHeader."No." := '';
SalesHeader.INIT;
END;
IF SalesHeader."Prices Including VAT" THEN
SalesPricesIncVar := 1
ELSE
SalesPricesIncVar := 0;
CLEAR(SalesHeader);
EXIT('2,' + FORMAT(SalesPricesIncVar) + ',' + GetFieldCaption(FieldNumber));


Well this seems to be returning either ‘2,1,Unit Price’ or ‘2,0,Unit Price’ depending on whether the Sales Header has the “Prices Including VAT” field set to TRUE or FALSE. How weird is that? Clearly that is not what is displaying on the form.

If you take a look in the C/SIDE Reference Guide (select the option from the Help menu of the application,) there is an interesting line that says “The expression is then interpreted by Trigger 15 in CodeUnit 1.”

Let’s take a look at that codeunit trigger.

CaptionClassTranslate(Language : Integer;CaptionExpr : Text[80]) : Text[80]
CommaPosition := STRPOS(CaptionExpr,',');
IF (CommaPosition > 0) THEN BEGIN
CaptionArea := COPYSTR(CaptionExpr,1,CommaPosition - 1);
CaptionRef := COPYSTR(CaptionExpr,CommaPosition + 1);
CASE CaptionArea OF
'1' : EXIT(DimCaptionClassTranslate(Language,CaptionRef));
'2' : EXIT(VATCaptionClassTranslate(Language,CaptionRef));
'3' : EXIT(CaptionRef);
END;
END;
EXIT('');


This is one of those funny functions that gets called by the system whether you like it or not – you don’t pass the parameters to it but you can guess that what the values contain. I am guessing that in my example the CaptionExpr will contain either ‘2,1,Unit Price’ or ‘2,0,Unit Price’.

On examining the code, I can see that we are pulling out a number from the start of the string into a variable called CaptionArea (which in our case is 2) and using that to either run a new function or return the part of the string that appeared after the number. In our example we are calling VATCaptionClassTranslate(Language,CaptionRef).

So, let’s take a look at what this function does:

VATCaptionClassTranslate(Language : Integer;CaptionExpr : Text[80]) : Text[30]
CommaPosition := STRPOS(CaptionExpr,',');
IF (CommaPosition > 0) THEN BEGIN
VATCaptionType := COPYSTR(CaptionExpr,1,CommaPosition - 1);
VATCaptionRef := COPYSTR(CaptionExpr,CommaPosition + 1);
CASE VATCaptionType OF
'0' : EXIT(COPYSTR(STRSUBSTNO('%1 %2',VATCaptionRef,Text016),1,30));
'1' : EXIT(COPYSTR(STRSUBSTNO('%1 %2',VATCaptionRef,Text017),1,30));
END;
END;
EXIT('');


This function starts by stripping out another parameter into a variable called VATCaptionType and the remainder of the string goes into CaptionRef. Then as you can see the VATCaptionType is evaluated and it returns either ‘Unit Price Excl. VAT’ or ‘Unit Price Incl. VAT’. To know this you have to know that Text016 and Text017 contain ‘Excl VAT’ and ‘Incl. VAT’ respectively.

So that’s it. A good example of how to achieve dynamic field captions using the standard application.

But just to round off, what if I wanted to have my own field with a dynamics caption? Well if you go back to the CaptionClassTranslate function in codeunit 1, you’ll see that option 3 will simply return the value that you passed it back to the caption for the field. This is how you would do it.

Let’s say that we are implementing NAV for a client that wants three addition fields on the Customer Card, the currently have them in their old system called “User Field 1”, “User Field 2” and “User Field 3”. Don’t you just hate that sort of thing? Anyway they say that they want to change the caption to something more meaningful but they can’t decide on what to call them (it’s a lame example I know but it’s late so stick with me.) Being a cunning NAV developer you decide to create three setup fields on the Sales & Receivables Setup table called “Field Caption 1”, “Field Caption 2” and “Field Caption 3”. You can then let the users type the caption they want in these fields and use them for the field captions for the new fields you will add on the customer card.

First of all, you create the fields on the Customer table as “User Field 1”, “User Field 2” and “User Field 3”. Then you create a little function on the Customer table that will take an integer parameter (valued as 1, 2 or 3) and will return a Text value that is the right caption. Let’s call our function “UserFieldCaption”. It might look something like this:

UserFieldCaption(p_FieldNo : Integer) : Text[80]
l_SalesReceivSetup.GET('');
CASE p_FieldNo OF
1: EXIT(l_SalesReceivSetup."Field Caption 1");
2: EXIT(l_SalesReceivSetup."Field Caption 2");
3: EXIT(l_SalesReceivSetup."Field Caption 3");
ELSE
ERROR('UserFieldCaption function on Customer table called with invalid field number of %1',p_FieldNo);
END;


Now on our User Field 1 on the Customer table we would set the CaptionClass property to

'3,'+UserFieldCaption(1)


The other two User Field CaptionClass values will be similar – hopefully you can figure this out yourself.

Once you have compiled everything, set the values of the captions on the Sales & Receivables Setup and Open the Customer table from the object designer. There you should see the caption for your new fields using the value you entered on the setup table.

Wednesday, 9 April 2008

Try Catch in Dynamics NAV

This has to be up there on my list of "all time desirable features" for NAV. As you probably know NAV has an ERROR function that is used to, well, throw errors. It will abort the current transaction (and roll back to the point of the last commit) and display the text message that you selected in an error dialogue box.

The thing is, sometimes you don't want it to do the abort and rollback. For example if we are importing a file or maybe a series of files, we may just want to log the error somewhere and carry on with the next file. You can do this in NAV 5.0 using the new GETLASTERRORTEXT function.

Here's an example of how to do it.

First of all, create a simple codeunit that, when run, will throw an error:

OBJECT Codeunit 50000 ThrowError
{
OBJECT-PROPERTIES
{
Date=09/04/08;
Time=[ 9:34:55 PM];
Modified=Yes;
Version List=;
}
PROPERTIES
{
OnRun=BEGIN
// Call a function that throws an error.
DoSomething();
END;

}
CODE
{

PROCEDURE DoSomething@1000000000();
BEGIN
ERROR('Hey Look I threw an error.');
END;

BEGIN
END.
}
}


When you run this codeunit you get the following error message displayed.



Now create another codeunit that will call the first one and trap the error it generates:

OBJECT Codeunit 50001 Test Throw Error
{
OBJECT-PROPERTIES
{
Date=09/04/08;
Time=[ 9:35:58 PM];
Modified=Yes;
Version List=;
}
PROPERTIES
{
OnRun=VAR
l_ThrowError@1000000000 : Codeunit 50000;
BEGIN
IF NOT l_ThrowError.RUN THEN
MESSAGE(GETLASTERRORTEXT+ ' - or did I?');
END;

}
CODE
{

BEGIN
END.
}
}


When you run this second codeunit, even though it calls the first codeunit, you don't see the error message. Instead you see this:



You'll need to make a COMMIT before trying to trap the return code from running the codeunit but if you have uncommitted transactions you'll get a run time error telling you this. Unfortunately this only works when you Run a codeunit so you either have to use lots of codeunits or write some kind of clever dispatcher that allows you to set the action on the codeunit first and then run it.

So my most desired feature would require some language additions to C/AL. Introduce a TRY CATCH language construct – it would work similar to an IF ELSE statement. Here is an example:


TRY
BEGIN
DoSomething();
DoSomethingElse();
END
CATCH
BEGIN
MESSAGE(GETLASTERRORTEXT);
END;


I have put the BEGIN and END in the CATCH part to illustrate how it work in a similar manner to the IF ELSE construct but it wouldn't be needed. The neat thing about this is you would not need to use a codeunit just to be able to trap the error. The same rules regarding commits, etc. would apply.

I guess I'll just nip over to the Connect site and suggest this little beauty for the product team to ponder over.

Tuesday, 25 March 2008

Get Stuff Done – Go Home Early - Play with your Wii

I've been using ActionThis since the early beta stages and I love it! The ActionThis team are running a promotion whereby you can sign up for a 1-month free trial if you sign up with the promotional code. After that you can continue to use the website for free!


Click this link to go to the site http://www.actionthis.com/product/trial.aspx


Enter the Referral Code INT521


You can use ActionThis to help you and your team work together more effectively, using the power of the web combined with Microsoft Office.

Thousands of people worldwide use http://www.actionthis.com/ to manage the tasks small businesses, teams and their partners need to complete to succeed. Delegate tasks from Microsoft Outlook, connect with your team on the ActionThis task management website, track progress and take action with live reports delivered to your email inbox. ActionThis is free to try, and simple to use. Less time following up, more tasks completed, your business is more productive. ActionThis was designed and developed by Intergen in New Zealand and will help you and your team get things done.

How ActionThis helps you get stuff done:
Use Microsoft Outlook to create and assign tasks to yourself, your team, your partners,
Organize and access these tasks from anywhere using Microsoft Outlook or the http://www.actionthis.com/ website,
Keep track of progress, projects, and workload with reports emailed to your email inbox,
Keep on top of overdue tasks with live alerts designed to help you take action quickly,
Export and analyze your progress with Microsoft Excel,
Telephone and email support is free.

Try it for free. Sign up for a one month free trial at http://www.actionthis.com/product/trial.aspx and use this referral code: INT521.

Dynamics NAV Gets Connected

In my first ever blog post I wrote about a site where ideas for new product features can be posted. Today I came across a news article on PartnerSource that pointed me to a new place for logging feature enhancements, the Connect for Microsoft Dynamics site. You will need to register a Windows Live ID in order to use the site, which site is a big improvement on the old public forum.

Microsoft seems to be committed of late to listening to feedback from partners and customers and this is a very welcome move. Recently they requested feedback on how the online help can be improved through Convergence and some of the public forums. This new listening, caring Microsoft makes me feel warm and fuzzy and I truly believe that we should all be feeding back to Microsoft where we think the product can be improved. Long gone are the days when this was a pointless exercise, so sign up and give it a go!

The site seems to have been active since October 2007 but there are only 7 suggestions for NAV – maybe it hasn't been that well publicised? Well here's a suggestion to get things going. If you want to vote for this suggestion you can register and click the rating. Here is the suggestion:

Every implementation of NAV I have ever been involved with has a test system and a live system. I am assuming this is a universally accepted practice – you don't want to be applying programming modifications to your live system without testing them first. In nearly every implementation of NAV I have been involved with, there have been instances at least one user has been logged in to the live system and thought they were logged in to the test system and they have mistakenly posted entries in their live system. The request I commonly receive is to make it so that it is immediately obvious to the users which system they are in: live or test. This should be immediately visually obvious – to me there is only one way to achieve this and that is to change the colour scheme of the windows. You can change the text in the title bar (via 3rd party utilities or by renaming the company) but this is not immediately visually obvious. Another less common requirement is for users that need to work in more than one company at once and they want to see which company they have open. Again they want something that is instantly obvious and don't want to be reading titles of windows. So my suggestion is: allow the Window Colour and Appearance options to be specified at a Company AND Database level. The Database level is required so that if a user restores their live system over their test system (something they frequently need to do) they do not lose the all important colour settings. At the database level it could be stored in a table similar to the $ndo$srvproperty table in the master database – this would have one record for each NAV database. Restoring a database would not overwrite this value. At the company level, it would be set in a table that is company specific.

Tuesday, 18 March 2008

Quick “Debugging” Tip

Sometimes I want to check something in a bit of code and I don't want to have to step through the debugger. Maybe I want to check the filters that are being applied to a table, maybe it's the value in a particular field. Here's a quick bit of code that you can use to check on a value and, if you're not happy with the value abort the process.

In my example, I wanted to check the table view that had been applied to a record variable. I want the program to stop at the point of my message (something the MESSAGE function doesn't do as NAV saves up messages for a convenient point in time.) I also want to be able to abort the execution in order to give me chance to fix something and try again (something you can't do when debugging with the debugger unless you kill the NAV application in a brutal way.)

Here's the code I put in:

IF NOT CONFIRM(g_JobLedgerEntry.GETVIEW) THEN

ERROR('');

Notice that I'm using the Silent Abort from Vjeko's blog. This will display a confirmation box with the details I am looking for. If I click No, the process aborts and rolls back, allowing me to change some values and run it again.

Friday, 14 March 2008

Look at me! I’m a balloon!

This has got nothing to do with Dynamics NAV so if you're looking for news on ERP systems, leave now. If you are squeamish about medical procedures then you should also leave. Hi Dad – just you and me reading this now.

I am gluten intolerant. That means if I eat anything with Gluten in it I feel crook. Today I went for a Esophagogastroduodenoscopy to see if I have coeliac disease (the disease sounds bad but basically it means you can't eat gluten without it making you crook and you have damage to the bits of your gut that help you absorb nutrients.) There is no cure other than to stop drinking beer, eating pizza, burgers, toast, pasta, etc. So, not too bad, right?

Anyway, back to the procedure. I was given the choice of a local anaesthetic spray (that numbs the throat) or the spray and a sedative. I was told that if I had just the local I would be able to watch the procedure on a video monitor. I was also told that the guy before me just had the local and he was able to keep himself calm, control his breathing and he got through it fine. I was also told that the sedative would make me feel drowsy and unable to do pretty much anything for the rest of the day. I was attracted to the idea of watching the procedure on the video monitor (my wife says this is the geek in me winning out of the sensible part of me.) Now if you ever find yourself in the unfortunate position to be asked if you want a sedative before someone shoves a piece of hose down your throat and pumps your stomach up like a balloon, the correct answer is "Hell yes!"

As for the video: the doctor stood in front of my screen so I didn't see a thing! But the procedure was so unpleasant that there could have been a small family of pixies living in my stomach and I wouldn't have cared. There were several people watching the procedure (I am guessing they were students) and none of them would make eye-contact afterward. This was probably due to the strange retching-gagging-belching noise I was making and the look of fear on my face. I think they were thinking "Dear God! What did we just do to that man?"

So the purpose of this blog posting is that if anyone is searching Google with the question: "Should I take the sedative before having an EGD, OGD, upper GI endoscopy (UGIE), or gastroscopy" then they can read this and know that no matter how appealing seeing their insides on a video screen seems, you should take the drugs!

Wednesday, 12 March 2008

Dynamics NAV SQL Security Roles

I'm studying for the Installation and Configuration exam at the moment and have found some interesting stuff in the training material that I often need to know. I was recently asked about the SQL permissions needed to be able to create new users in Dynamics NAV and, thanks to my studies, was able to go straight to the right page in the document. I was interested to see that this topic also recently surfaced again in the DynamicsUser forum. This table is for Dynamics NAV 5.0.

Invoking the synchronization process or modifying the User table

sysadmin server role. Alternatively both a member of the securityadmin server role and a member of the db_owner database role for this database.

Creating a database

sysadmin or dbcreator server role. Alternatively, the user must have been granted the create database permission. The user must also have public access to the model database.

Altering a database

sysadmin or dbcreator server role. Alternatively a member of the db_owner or db_ddladmin database role for this database.

Creating tables within a database

sysadmin server role or be a member of the db_owner database role for this database.


Now you could argue that you don't want to grant these SQL rights to the user just to let them add users to the ERP database. This raises an interesting question: which is better, giving a user db_owner role membership and securityadmin server role or knowing that any user of the application could potentially add other users to the database? Personally I like the thought that I can pick and chose which user gets these rights from a SQL administrator's role and not leave it up to the users of the ERP to decide.