Write applications that gracefully accepts new financial dimensions

When you add a new financial dimension to the system either by hand or by using the new wizard, what you basically do is to add new elements to a number of arrays. One of these arrays is the array on the Dimension extended data type. Also you add new outcomes to a number of enums, for example SysDimension.

The idea with the wizard is that after you run the wizard, you do not have more work to do in order to implement the new dimension properly. But nothing comes for free and you have to follow a simple set of guidelines when using the wizard to make the magic happen.

How many dimensions does the system have anyway?

Of course the primary concern before you do anything else will probably be to figure out how many dimensions you have in the application. The function dimOf will return the number of elements in a data type array, so this is how you figure out the number of dimension:

 Dimension dimension;
NumberOfDimension = dimOf(dimension);

Another option is to simply call the numOfDimensions method in the Dimensions table.

Array placement vs. enum value

When you are working with the individual array elements of the Dimension datatype, you will need to convert back and forth between the value of the enum and the actual position of the value in the array of the data type.

With the three standard dimensions conversion between the array position and the Dimension enum is easy:

Dimension Enum Value Property Array Position
Department 0 1
Center 1 2
Purpose 2 3

This looks like the conversion is just a question of adding or subtracting 1, for example:

 ValueOfCenter = Dimension[SysDimension::Center + 1]

But this code breaks if someone adds a new dimension with an enum value not matching the pattern which for example could make the picture look like this:

Dimension Enum Value Property Array Position
Department 0 1
Center 1 2
Purpose 2 3
MyNewDimension 118 4

On the Dimensions table you fortunately find two helpful methods allowing you concentrate on other things than the conversion.

The first method, arrayIdx2Code, converts from the position in the array to the correct enum value:

 public static int arrayIdx2Code(int _idx)
{
   DictEnum dictEnum = new DictEnum(enumnum(SysDimension));
   ;

   return dictEnum.index2Value(_idx - 1);
}

The second method, code2ArrayIdx, converts an enum value from the SysDimension enum to the correct position in the array:

 public static int code2ArrayIdx(SysDimension _sysDimension)
{
   SysDictEnum   sysDictEnum;
   ;
   sysDictEnum = new SysDictEnum(enumnum(SysDimension)); 
   
   return sysDictEnum.value2Index(_sysDimension) + 1;
} 

You should always use these methods when going back and forth between the enum value and the position in the array. It will save you time and pain.

Here’s an example:

 i = Dimensions::code2ArrayIdx(_dimensionCode);

Enable and disable individual array elements in forms

When dimensions are added to forms you should use auto groups to ensure that new dimensions show up automatically in the group. Auto groups will not directly allow you to write code targeting the individual controls.

So what do you do if you need to set properties on the individual fields, for example allow edit? Well, on the Global class a new method called formDataSourceArrayFieldExtObjects will return to you a map with the individual form data objects, given a data source and a field ID as argument.

Here is an example from the method setAllowEditKeepTransaction of the LedgerAllocation table:

 void setAllowEditKeepTransaction()
{
   FormDataObject   formDataObject; 
   MapEnumerator    mapEnumerator;
   Integer          dimIdx;  
   ; 
   
   mapEnumerator = formDataSourceArrayFieldExtObjects(this.dataSource(), 
            fieldnum(LedgerAllocation, KeepTransactionDimension)).getEnumerator();

   while (mapEnumerator.moveNext())
   { 
      formDataObject = mapEnumerator.currentValue();
      dimIdx = fieldExt2Idx(mapEnumerator.currentKey());
   
      if (this.DimensionAllocation[dimIdx] == SelectionCriteria::Optional 
          &&!this.ToDimension[dimIdx])
      {
         formDataObject.allowEdit(true)
      } 
      else 
      {
         formDataObject.allowEdit(false); 
         this.KeepTransactionDimension[dimIdx] = NoYes::No;
      }
   }
}

When you just need to set the allowEdit property, there is a shortcut in the Dimension table method called formDataSourceDimensionsAllowEdit. The method takes a form data source, a field ID and a flag on allowing edit or not.

Here is an example on how to use it:

 // Array fields
if (fieldnum(LedgerJournalTrans, Dimension) == dictTable.fieldCnt2Id(i) || 
    fieldnum(LedgerJournalTrans, InterCoDimension) == dictTable.fieldCnt2Id(i))
{
   Dimensions::formDataSourceDimensionsAllowEdit(journalTrans_ds, 
                                                 dictTable.fieldCnt2Id(i), 
                                                 objectLedgerJournalTrans.allowEdit());
}

Use the array elements in queries

Finally I just want to show you the syntax for adding individual dimension array elements as ranges to a query. The following example is taken from the method initValidationQuery of the DimensionSetValidation class:

 QueryBuildDataSource  qbds; 
QueryBuildRange       qbr; 
... 
for (i = 1; i <= numOfDimensions; i++) 
{
   qbr = qbds.addRange(fieldId2Ext(fieldnum(DimensionSetCombination, Dimension), i)); 
   ... 
} 
...

This posting is provided "AS IS" with no warranties, and confers no rights