Tuesday, June 6, 2017

Display files in CL

how to code dspf in clp or clle

I could give this a subtitle: "How to put a bad date in a date field", but this is not the reason of this post, just an accidental discovery. Having previously written about how to use database files in a CL program I have to mention using display files in CL.

You cannot use CL for anything as complex as a subfile. But you can for what I call "report submission screens", you know the type: a screen is presented to the user, they enter the selection criteria they desire and press Enter, and a program is submitted to batch to generate either a paper report or an email attachment. My example display file has two record formats, and is very simple:

01  A                                      DSPSIZ(24 80 *DS3)
02  A                                      CA03(03)
      *---------------------------------------------------------------------
03  A          R SCREEN1
04  A                                 3  3'Company. . .'
05  A            COMPANY        3A  B  3 16
06  A  50                                  ERRMSG('Invalid company number')
07  A                                  4  3'From date. .'
08  A            FROMDATE        L  B  4 16DATFMT(*MDY)
09  A  51                                  ERRMSG('From date cannot be +
10  A                                      greater than To date')
11  A                                  5  3'To date. . .'
12  A            TODATE          L  B  5 16DATFMT(*MDY)
13  A                                 22  3'F3=Exit'
     *----------------------------------------------------------------------
14  A          R SCREEN2
15  A                                      CA12(12)
16  A                                  3  3'Company. . :'
17  A            COMPANY   R        O  3 16
18  A                                  4  3'From date. :'
19  A            FROMDATE  R        O  4 16DATFMT(*MDY)
20  A            FROMDATE2      8   O  4 26
21  A                                  5  3'To date. . :'
22  A            TODATE    R        O  5 16DATFMT(*MDY)
23  A            TODATE2        8   O  5 26
24  A                                 22  3'F3=Exit   F12=Cancel'

When you compare this to other examples I have given of display files notice that there isn't an Indicator area keyword, INDARA, this is because CL does not support indicator data structures.

I also have to give my function keys, lines 2 and 15, indicators as CL does not allow the "old fashioned" *INKx indicators.

Lines 3 – 13: My first record format, SCREEN1, has three fields where I can input data. FROMDATE and TODATE are date fields, this means that the fields themselves will ensure that a valid date has to be entered. I want them in *MDY format as I am in the USA, if you are in other parts of the world that uses a different date format then you'll want to use that instead. I also have two error messages, ERRMSG, on lines 6 and 9-10.

Lines 14 – 24: The second record format, SCREEN2, is much simpler as it just has five output fields based. Notice that I have date fields on lines 19 and 22 and alphanumeric equivalents on lines 20 and 23. This screen also has F12 enabled.

The CL program is also very simple:

01  PGM

02  DCL VAR(&LOOP) TYPE(*LGL) VALUE('1')
03  DCLF FILE(TESTDSPF)

04  DOWHILE COND(&LOOP)
05    SNDRCVF RCDFMT(SCREEN1)

06    IF COND(&IN03) THEN(RETURN)

07    IF COND(&COMPANY = ' ') THEN(DO)
08      CHGVAR VAR(&IN50) VALUE('1')
09      ITERATE
10    ENDDO
11    ELSE CMD(CHGVAR VAR(&IN50) VALUE('0'))

12    CVTDAT DATE(&FROMDATE) TOVAR(&FROMDATE) +
               FROMFMT(*MDY) TOFMT(*YYMD) TOSEP(*NONE)

13    CVTDAT DATE(&TODATE) TOVAR(&TODATE) +
               FROMFMT(*MDY) TOFMT(*YYMD) TOSEP(*NONE)

14    IF COND(&FROMDATE > &TODATE) THEN(DO)
15      CHGVAR VAR(&IN51) VALUE('1')
16      ITERATE
17    ENDDO
18    ELSE CMD(CHGVAR VAR(&IN51) VALUE('0'))

19    CHGVAR VAR(&FROMDATE2) VALUE(&FROMDATE)
20    CHGVAR VAR(&TODATE2) VALUE(&TODATE)

21    DOWHILE COND(&LOOP)
22      SNDRCVF RCDFMT(SCREEN2)

23      IF COND(&IN03) THEN(RETURN)
24      IF COND(&IN12) THEN(LEAVE)
25    ENDDO
26  ENDDO

27  ENDPGM

Line 3: As a display file is a file it needs to be declared with a Declare file command, DCLF.

Line 4: As I am using modern CL there are no GOTO commands. The Do loop will execute while the variable &LOOP is on, in other words forever. This Do loop ends on line 26.

Line 5: When I was dealing with database file I would RCVF to get the data from them. With a display file I need to do a write followed by a read. The Send Receive command, SNDRCVF, does that just like the EXFMT operation code does in RPG.

Line 6: If F3 was pressed indicator 3, &IN03, will be on and want to quit the program.

Lines 7 – 10: Here I am validating that the company code, &COMPANY, entered is not blank (well, I did say this was a very simple program). If it is indicator 50, &IN50, is set on, and this will cause the error message for the company field on the screen to display an error message. The ITERATE command is just like RPG's ITER operation code, which will cause the processing to go to the top of the Do loop.

Line 11: If the company code is not blank then indicator 50 is set off.

I need to compare the dates to ensure that the from date is not greater than the to date. As the date fields are defined as dates you would think it would be easy. But CL does not support date fields, it converts them to character. The section below is copied from the compile listing showing that &FROMDATE and &TODATE date fields from the display file are handled as character in the CL program.

Declared Variables
Name         Type    Length
&COMPANY     *CHAR       3
&FROMDATE    *CHAR       8
&FROMDATE2   *CHAR       8
&IN03        *LGL        1
&IN12        *LGL        1
&IN50        *LGL        1
&IN51        *LGL        1
&LOOP        *LGL        1
&TODATE      *CHAR       8
&TODATE2     *CHAR       8

I cannot safely compare two character dates if they are in *MDY format, I need to convert them to be in YYYYMMDD. The first thought is: "That's *ISO format!" Alas, in CL an *ISO date must contain its date separators, YYYY-MM-DD. Fortunately there is a similar date format *YYMD which will give me what I desire without date separators.

Lines 12 and 13: I am converting the character values in &FROMDATE and &TODATE from *MDY to *YYMD using the Convert Date command, CVTDAT.

Lines 14 – 17: If the from date is greater than the to date indicator 51 is set on, line 15, and an iterate is performed, line 16, to take processing back to the top of the Do loop.

Line 18: If there is date range is OK then indicator 51 is set off.

Lines 19 and 20: I am moving the reformatted dates from the character date fields to character fields. I am doing this to show you something on the second screen.

Line 21: I have a second Do loop here to "contain" the display of the second screen. This loop ends on line 25.

Line 22: The second record format is displayed.

Line 23: If F3, &IN03, is press the program quits.

Line 24: In the display I assigned indicator 12 for the F12 key. When F12 is pressed, &IN12 is on and the logic quits this do loop, but remains within the first one.

So what does this look like when I run this program. I enter the following into the fields on the first screen.

 Company. . . 001
 From date. . 010101   
 To date. . . 123117   


 F3=Exit

When I press Enter the second screen is displayed:

 Company. . : 001
 From date. : 20/10/01  20010101
 To date. . : 20/71/31  20171231


 F3=Exit   F12=Cancel

Look at those dates, there are not valid! Remember that these date fields are in *MDY format. There is no month "20", and in the To date there cannot be "71" days in any month. It would appear that the date fields in the display file have been overridden with bad dates as the program considers them as character fields. So be careful in your own programs to prevent this from happening in a non-test program.

In the above example the display contains two record format. If the display file contained only one then I can just use the SNDRCVF with no record format name and the program assumes the first, and only, record format name from the display file:

  DCLF FILE(TESTDSPF)


  SNDRCVF

 

You can learn more about this from the IBM website:

 

This article was written for IBM i 7.3, and should work for earlier releases too.

6 comments:

  1. Hello Mr. Hutchinson.

    I was reading your post, that's so good, however, I detected a mistake after this part "When I press Enter the second screen is displayed:"

    In the screen or picture you have this information deploy:

    "To date. . : 20/71/31 20171231"

    And I think that your first date is wrong.

    A part of that, I do not have any other comment.

    Thanks for sharing this form to display a screen in a CL program.

    Regards.

    ReplyDelete
    Replies
    1. It may look like a typo, but it is really what happens.

      I have to admit I was shocked when it happened.

      Delete
  2. CL is great for the simple display files. One suggestion to simplify the error handling is to add the ERRSFL keyword to the DDS at the file level and change the error test logic to:

    IF COND(&IN03) THEN(RETURN)

    CHGVAR VAR(&IN50) VALUE(&COMPANY = ' ')

    CVTDAT ...
    CVTDAT ...

    CHGVAR VAR(&IN51) VALUE(&FROMDATE > &TODATE)

    IF COND(&IN50 *OR &IN51) THEN(ITERATE)

    CHGVAR VAR(&FROMDATE2) VALUE(&FROMDATE)
    ...

    That allows all of the errors to show on the screen at once instead of one at a time and the reset key does not have to be pressed to clear the error(s).

    ReplyDelete
    Replies
    1. You are right. It would be better in a real program, rather than an example, to do it the way you suggest. And yes I do use the ERRSFL, and written about it too here.

      Delete
  3. Well, you can have your slashes or you can have your century but not both. You needed 2 more statements to convert the dates back to the proper format as defined by the DDS.

    CVTDAT DATE(&FROMDATE2) TOVAR(&FROMDATE) +
    FROMFMT(*YYMD) TOFMT(*MDY) TOSEP(/)

    CVTDAT DATE(&TODATE2) TOVAR(&TODATE) FROMFMT(*YYMD) +
    TOFMT(*MDY) TOSEP(/)

    You corrupted your date fields as defined on your screen as *MDY when your CL program converted them to *YYMD.

    Myself, I would not have modified the screen fields for comparison. I would have used the work fields &FROMDATE2 and &TODATE2.
    CVTDAT DATE(&FROMDATE) TOVAR(&FROMDATE2) +
    FROMFMT(*MDY) TOFMT(*YYMD) TOSEP(*NONE)

    CVTDAT DATE(&TODATE) TOVAR(&TODATE2) FROMFMT(*MDY) +
    TOFMT(*YYMD) TOSEP(*NONE)

    IF COND(&FROMDATE2 > &TODATE2) THEN(DO)

    Interesting problem though.

    ReplyDelete

To prevent "comment spam" all comments are moderated.
Learn about this website's comments policy here.

Some people have reported that they cannot post a comment using certain computers and browsers. If this is you feel free to use the Contact Form to send me the comment and I will post it for you, please include the title of the post so I know which one to post the comment to.