Adjust for a Datetime

The Date type has an Adjust intrinsic, which I commonly use for numbers as

#FAKYMD := #FAKYMD.AsDate( CCYYMMDD ).Adjust( -30 ).AsNumber( CCYYMMDD )

The Datetime type has no such intrinsic, but when I end up with a datetime stored in a number, I have used this roundabout way:

#FAKTIME := ((#FAKTIME / 1000000).AsDate( CCYYMMDD ).Adjust( -30 ).AsNumber( CCYYMMDD ) * 1000000) + (#FAKTIME.Mod( 1000000 ))

#FAKYMD is 8 digits, YYYYMMDD, #FAKTIME is 14 digits, YYYYMMDDhhmmss, in my examples.

 

Writing space to a file

This should be easy, but it’s not in RDML. The current solution I have is using HEXTOBIN and binary writing.

I am writing to a file and need to write a space character in certain situations. The codepage of the file is Windows-1252 (mostly equivalent to Latin1 or ISO-8859-1).

I opened the file with

Use Builtin(STM_FILE_OPEN) With_Args(#FILENAME 'APPEND TEXT LINETERMINATOR=NONE CODEPAGE=1252') To_Get(#FILENO #RETNCODE)

but if try to output a space character it gets trimmed and no space is added to the file. Instead I close, open, close and re-open:

Use Builtin(STM_FILE_OPEN) With_Args(#FILENAME 'APPEND BINARY LINETERMINATOR=NONE') To_Get(#FILENO #RETNCODE)
Use Builtin(HEXTOBIN) With_Args('20' Y) To_Get(#WCHAR1)
Use Builtin(STM_FILE_WRITE) With_Args(#FILENO2 #WCHAR1) To_Get(#RETNCODE)
Use Builtin(STM_FILE_CLOSE) With_Args(#FILENO2)
Use Builtin(STM_FILE_OPEN) With_Args(#FILENAME 'APPEND TEXT LINETERMINATOR=NONE CODEPAGE=1252') To_Get(#FILENO #RETNCODE)

This paradoxically only worked because I am running on a different codepage than Windows-1252, so the binary value 0x20 is not considered a space character (it’s Digit Select, whatever that is).

No, it’s not efficient.

Length of string

In RDMLX, easy, just

#LEN := #STR.CurChars

But in RDML it’s not so easy and for some reason there’s not even a buit-in function to return the length of a string.

So I made this:

Define Field(#W_C256) Type(*CHAR) Length(256)
Define Field(#W_C256_B) Type(*CHAR) Length(256)
Define Field(#W_C1) Type(*CHAR) Length(1)
Define Field(#W_D3_0) Type(*DEC) Length(3) Decimals(0)

Subroutine Name(LENGTH) Parms((#W_C256 *RECEIVED) (#W_D3_0 *RETURNED))
  Use Builtin(RIGHT) With_Args(#W_C256) To_Get(#W_C256_B)
  Substring Field(#W_C256_B 256 1) Into_Field(#W_C1)
  Use Builtin(REVERSE) With_Args(#W_C256) To_Get(#W_C256_B)
  Use Builtin(SCANSTRING) With_Args(#W_C256_B #W_C1) To_Get(#W_D3_0)
  Change Field(#W_D3_0) To('(256 + 1) - #W_D3_0')
Endroutine

which I then include when necessary.

Select_Sql does not validate SQL source

I made this error and spent a long time trying to spot it:

Select_Sql Fields((#ANTAL 'SUM(ANTAL')) From_Files(TRANS) Where(...)
...
Endselect

That is how I learned that the optional SQL source for a field is not validated in the editor or during compilation. Adding the missing end-parenthesis fixed my error:

                                    |
                                    V
Select_Sql Fields((#ANTAL 'SUM(ANTAL)')) From_Files(TRANS) Where(...)
...
Endselect

You got me there

Working with CONDCHECK I got confused. The condition check works to mark a field that is filled out incorrectly, but the condition is to be read as “these conditions should be satisfied, if not…”.

Which makes sense; you write the condition that you want satisfied. If it isn’t you get the message.

My problem was that I thought of it in the context of if … then; if this condition is satisfied, I should get the message. Which I now realise was wrong.

An XSLT example

I needed this, so here it is for posterity:

In the list BUTLST, I wanted to select the value from the field WCHAR36, on the entry where the value from the field BUTNR matched the mapped field IZBUTNR.

So, in sequence, I wanted

the list BUTLST

/lxml:data/lxml:lists/lxml:list[@name='BUTLST'] ...

the value from the field WCHAR36

... lxml:column[@name='WCHAR36']

on the entry where the value from the field BUTNR

... lxml:list-entries/lxml:entry/lxml:column[@name='BUTNR'][text() ... ]

matched the mapped field IZBUTNR

... = key('field-value', 'IZBUTNR') ...

Completed that is

/lxml:data/lxml:lists/lxml:list[@name='BUTLST']/lxml:list-entries/lxml:entry/lxml:column[@name='BUTNR'][text() = key('field-value', 'IZBUTNR')]/../lxml:column[@name='WCHAR36']

with

Def_List Name(#BUTLST) Fields(#BUTNR #BUNAVN #WCHAR36) Type(*WORKING) Entrys(*MAX)
Web_Map For(*BOTH) Fields(#IZBUTNR #BUTLST )

 

8 characters is the limit

I keep forgetting this, cause I do development in other languages and frameworks as well, and nothing in the LANSA editor stops me. But WAM command handlers must not have a name of more than 8 characters.

Or rather, it can and it will work, but then when you try to set it up in the VLF, you, again without warning, enter the more-than-8 character name and lo and behold, you end up with errors on your site.

Instead, a command handler named, for example, CH_FAKKOP, should be referenced in the VLF by the first 8 characters, CH_FAKKO, even though the WAM component is 9 characters.


Edit: This also applies to the WAM name in XSLT, like

<xsl:with-param name="on_change_wrname" select="'UHandleEvent'" />
<xsl:with-param name="on_change_wamname" select="'CH_FAKKO'" />

Decimal points and functions

This is one of those issues that arise from me coming from a different background than what I imagine a typical Lansa developer, indeed a typical i series developer, come from. It is a useful reminder what decimals actually mean.

For example, I have a repository field that is defined like this:

Define Field(#JSN) Type(*SIGNED) Length(4) Decimals(2)

That means, a signed decimal value with 2 digits before the decimal point and 2 digits after the decimal point. In a function, I calculate the value and exchange it back to application calling. In that application, I (unfortunately) have an override, changing the number of decimals.

Override Field(#JSN) Decimals(1)

This was probably done to format the display of the value but where I imagined the value would be passed in a lower level format and then parsed in relation to the definition, this is not what happens.

Instead, the value is still the same 4 digits, but one of those that was after the decimal point is now before the decimal point. The value has been multiplied by 10^1; a value of 0.21 is now 2.1.

Because the override only moved the decimal point.

Newlines in Lansa 12 and 14

As someone mentioned a while back on the forum, getting a newline, or indeed any character from a character code, is difficult to find. But it is easy, once you know how:

(10).AsUnicodeString

returns a carriage return in ANSI. I’d probably prefer

(13).AsUnicodeString + (10).AsUnicodeString

as I am primarily targeting the Windows platform.

However, this is a solution for Lansa v. 14, not v. 12, where AsUnicodeString does not exist. In Lansa v. 12 you write

(13).AsByte.AsString + (10).AsByte.AsString