Post sales packing slip through code

Those, who want to have more control over the data to be posted, should take a look at the parameters of salesFormLetter.update().

static void postSalesPackingslip(Args _args)
SalesTable salesTable = SalesTable::find(“000747”);
SalesFormLetter salesFormLetter;

salesFormLetter = SalesFormLetter::construct(DocumentStatus::PackingSlip);
salesFormLetter.update(salesTable, systemDateGet(), SalesUpdate::All);

Post purchase order through code

Those, who want to have more control over the data to be posted, should take a look at the parameters of purchFormLetter.update().

static void postPurchaseOrder(Args _args)
PurchTable purchTable = PurchTable::find(“000025”);
PurchFormLetter purchFormLetter;

purchFormLetter = PurchFormLetter::construct(DocumentStatus::PurchaseOrder);
purchFormLetter.update(purchTable, “”, systemDateGet(), PurchUpdate::All);

Lookup on temp table on form control

Let’s assume there is a temporary table of type “InMemory” as a datasource on a form and you want to create a lookup on a control on the same form.

public void lookup()
SysTableLookup sysTableLookup = SysTableLookup::newParameters(tablenum(TmpTableFieldLookup), this);
Query query = new Query();
QueryBuildDataSource queryBuildDataSource;
queryBuildDataSource = query.addDataSource(tablenum(TmpTableFieldLookup));

sysTableLookup.addLookupfield(fieldnum(TmpTableFieldLookup, TableName), true);

// DataSource on form with populated data via .setTmpData():

FastTabExpanded property on form TabPage

On a TabPage control on forms there is a property called FastTabExpanded.
The property controls weather the FastTab appears expanded or collapsed and how this can be changed by the user.

The default value is Auto.




Default value. Decision is taken by the system. User settings are considered.


When a user opens the form for the first time, the FastTab appears expanded. As soon as the user collapses the FastTab, this is getting stored in the users usage data and the next time opening the form the FastTab appears collapsed (or whatever state the user defined for the FastTab before leaving the form).


Behaves like the option Yes but on the first opening the FastTab appears collapsed.


The FastTab always appears expanded and the user cannot collapse the FastTab!
In this example below, the first FastTab has the property Alwaysand the second has the property Yes.

AllowUserSetup property form controls

Today I recognized the property AllowUserSetup on a Tab control for the first time.

My case was the form WMSShipmentWizard.

But first lets take a closer look on the property itself and its values:
form property user setup

Explaining the values
This means the user cannot add fields to the form over right-click => customize. Also the user cannot resize grid columns and the layout. Strictly, no customizations.

The user can make customizations of controls. They are restricted to the following properties for a control:

Width (useful for grid columns)
With the AllowUserSetup property set to Yes we need to differentiate here between two constellations with the AllowAdd property on form DataSourceField:

AllowAdd = No
The user is allowed to make customizations on the layout only.
Properties can be adjusted like they are needed and controls can be moved to other containers. Also own tabbed pages can be created.
Remark on moving: both containers (origin and target) must have AllowUserSetup set to Yes in order to enable moving controls!

AllowAdd = Yes
Full customization control for the user. With this property the user can mess up the design of the form completely for himself 😉

My case
In my case I wanted to enable the user to resize the grid (\Forms\WMSShipmentWizard\Designs\Design\[Tab:ctrlTab]\[TabPage:Orders]\[Grid:Grid]) in the shipment wizard. The default value of the Tab control here is No so I changed it to Restricted.

Nice way to catch CLRError with less lines

When using .NET objects we should think of error handling also.

Previous way to catch CLRErrors looked like this:

catch (Exception::CLRError)
ex = ClrInterop::getLastException();
if (ex != null)
ex = ex.get_InnerException();
if (ex != null)
error(ex.get_Message()); // Message

Today I found a method that lets you handle errors with less lines. By simply replacing all the code in the catch-block with a call to AifUtil::getClrErrorMessage().

Making it look a bit cleaner:

catch (Exception::CLRError)

Hope to save you some lines of code with this post 😉

Find record with Common

When we create a new table we add some useful methods like find and exist so the next developer can use these methods to find the a needed record.

When writing generic code and using Commons instead of specific declared tables, there are multiple ways to find a specific record.

Checking if a find method is present on the table and finding out which parameters it takes is the worst way.

For me the best way is to use KeyData.
KeyData is a container that contains the values of the unique fields to search for.

Let me show you an example:

static void findKeyDataExample1(Args _args)
Common record;
Map mapKeyData;
KeyData kd;

mapKeyData = new Map(Types::Integer, Types::String);
mapKeyData.insert(fieldNum(SalesTable, SalesId), ‘SO-00001’);

kd = mapKeyData.pack();

record = SysDictTable::findFromKeyData(tableNum(SalesTable), kd);
So this job initializes a new instance of SysDictTable for the table SalesTable and makes the common to be the same type. Then it selects the first record and retrieves the KeyData. This will return a map with the FieldCount and the value. The value is then written to a container and passed to the method SysDictTable::findFromKeyData which returns the same record as we selected first.

This is also working with tables that have more than one field as unique index.
For example PurchAgreementHeader.

To get KeyData from a record:

static void findKeyDataExample2(Args _args)
SalesTable salesTable;
Common recordCheck;
SysDictTable dictTable;
Map mapKeyData;
KeyData kd;
MapEnumerator mapEnum;

select firstOnly salesTable;

mapKeyData = SysDictTable::getKeyData(salesTable);

mapEnum = mapKeyData.getEnumerator();
while (mapEnum.moveNext())
info(strFmt(‘Field ID: %1 Fieldname: %2 Value: %3’, mapEnum.currentKey(), fieldId2name(tableNum(salesTable), mapEnum.currentKey()), con2Str(mapEnum.currentValue())));

JumpRef lookupRecord

When there is the need to override the jumpRef method manually on a form, sometimes it seems to have a problem with the args.record parameter of the args and doesn’t jump to the right record.
Looks like the kernel doesn’t fill the args.lookupRecord automatically in some cases.

The fix is to fill the args.lookupRecord manually before calling the form or display menu item from code.

public void jumpRef()
DictTable recordDictTable = new DictTable(DataSource.RefTableId);
Common common;
ExecutePermission perm;
MenuFunction menuFunction;
Args args = new args();


if (!recordDictTable || !recordDictTable.formRef())

perm = new ExecutePermission();

common = recordDictTable.makeRecord();
select firstOnly common
where common.RecId == DataSource.RefRecId;

menuFunction = new MenuFunction(recordDictTable.formRef(), MenuItemType::Display);


Short code: name of current user

The old fashioned way to get the name of the current user is to select the record from UserInfo:

UserInfo userInfo;

select firstOnly name from userInfo

where == curUserId();

Instead of selecting the table UserInfo<, the application has a neat class for such information:


The find method returns a record of type UserInfo and then you can access its fields easily.