Quick Tip: Handling scripts that use old() when using triggers to set default values
This Quick Tip comes to you after I faced this issue in a recent customisation project. I had seen this issue before and knew the approach required to solve it, but I don't think it has been documented publicly till now.
The Scenario
The customisation required is fairly common. The customer wanted a batch number to be automatically generated for Purchasing Invoice Entry (Purchasing Invoice Entry) window.
The usual method for setting default values on a window is to trigger after the WIN_PRE event for the window and the set the values for the fields you are interested in and then run the change script for the field.
For example:
'Batch Number' of window POP_Invoice_Entry of form POP_Invoice_Entry = MBS_Generate_Batch();
run script 'Batch Number' of window POP_Invoice_Entry of form POP_Invoice_Entry;
However, for our customisation, we wanted the user to have the opportunity to override the default setting and did not want the "Do want to add this batch?" dialog to pop up when the window was first opened.
So we decided to use the approach of triggering on the PRE event for the 'Batch Number' field which runs when the field gains focus, we could then set the default value (if the field was empty) and issue the force change command to make sure that the change script executes even if the user does not change the value and just tabs off the field.
default form to POP_Invoice_Entry;
default window to POP_Invoice_Entry;
if empty('Batch Number') then
'Batch Number' = MBS_Generate_Batch();
force change field 'Batch Number';
end if;
This code did set the value as desired, but when the user tabbed off the field, the change script appears not to run as the user is not asked to add the batch (if it does not exist).
The Problem
Using Dexterity's built in Script Debugger, we logged the scripts and confirmed that the POP_Invoice_Entry Batch Number_CHG script was being executed. So I checked the source code and found the following lines right at the top of the script.
if ('Batch Number' = old() ) then
{ Chose same batch from the lookup }
abort script;
end if;
What was happening was that the 'Batch Number' and old() values were the same and the script was being aborted as the script decided no action was required.
So now what?
The Cause
The cause of the issue all comes down to the timing of when Dexterity obtains a copy of the field value to use for the old() value. The capture of the previous value happens when the field gains focus and the cursor is moved to the field. The cursor is only moved when the user interface gains control again.... once all the scripts being executed have completed.
So how can we have our scripts complete but continue executing after control has been returned to the user interface?
The Solution
The solution is the run script delayed command. The "delayed" keyword means that the existing scripts will finish before the field's change script will be executed.
For this method to work, you will need a hidden field somewhere that you use with the run script delayed. This field could be added to an alternate window if you are using an alternate window. If you not using an alternate window, you would need to have a hidden form somewhere with a window (AutoOpen=false) to contain the field. If you have a command form for your menus, a hidden window on that form works well.
In our case we were using an alternate window, so we added a local Batch Number field to the window and registered a change trigger against it. We could have added a script directly on the window, but best practice is to use triggers (see my Knowledge Base Article KB 929211 on the topic).
Now the code is in two parts. First is the PRE trigger on the 'Batch Number' field:
default form to POP_Invoice_Entry;
default window to POP_Invoice_Entry;
if empty('Batch Number') then
run script delayed '(L) Batch Number';
end if;
This allows the focus to be set to the 'Batch Number' field and the value for old() to be set to an empty string. Then the second trigger on the '(L) Batch Number' field change event runs to complete the customisation:
default form to POP_Invoice_Entry;
default window to POP_Invoice_Entry;
if empty('Batch Number') then
'Batch Number' = MBS_Generate_Batch();
force change field 'Batch Number';
end if;
This time the POP_Invoice_Entry Batch Number_CHG script continues passed the old() check and works as desired.
More Information
You might notice that the final method used does not have a run script command in it. When a field change script uses the old() function, you might get an error message if you execute it with a run script command. This method using force change, makes Dexterity execute the change script for you without manually using a run script command.
Hope you find this technique useful.
David