Proc Guidelines
Home Up Photo Album Favorites

 

Home
Up
The overall goal of style guidelines is to lower the maintenance costs of software

The overall goal of style guidelines is to lower the maintenance costs of software. Please do not succumb to the desire to put your mark on a program by using a different style. Any code style differences only drive UP the maintenance costs of the software. If you have a style conventions

PROC Guidelines:

Labels - all Labels must have a "C"omment on them. There should be no proc commands on the same line as the label.

Buffer Values - be sure to document the contents of all of the buffer values at the top of the proc. With the following convention:

C <<A1,CONO>>
C <<A2,CONAME>>
C <<A3,X>>
C <<A4,X>>
C <<A5,X>>
C <<A6,X>>
C <<A7,X>>
C <<A8,X>>
C <<A9,X>>
C <<A10,X>>

Access sentences - When composing an access sentence in a PROC place one statement per line instead of long statements on a single line.

Instead of:
H WITH STATUS = "A" AND WITH CONO # "001" AND WITH TOTAL.SALES # "0"
Do this:
H WITH STATUS = "A"
H AND WITH CONO # "001"
H AND WITH TOTAL.SALES # "0"

Select List and Sort Statements: It is better to perform two different commands to do SSELECT and LIST. After the SSELECT statement a check should ALWAYS be done to make sure that the access command returned a select list. The 9997, 9998 and 9999 proc labels are reserved for this standard behavior of the procs. If a proc is allowed to produce a report to the terminal, then be sure to use the 9997 routine to stop after to last page and require the operator to hit return. Please follow the template below closely.

USER.PREBILL.NOPOST

PQN
C$ tpackert - 10:24:05 Oct 07 1997
C$ THE REPORT WIDTH IS 79
C
C Copyright 1997, Avanti-Case Hoyt - all rights reserved
C Portions Copyright Computer Business Associates/Vercom Software
C
C this proc is a template to be used when setting up new procs, this proc
C prompts for the Terminal or printer option (question # 899)
C it also waits for a carriage return if it is run to the terminal
C it also executed the Select and List separately so that the proc
C can check for the successful generation of a list with IF #S and jump
C to label 9998 if no select list results
C
C
C <<A1,CONO>>
C <<A2,CONAME>>
C <<A3,REPORT.DATE>>
C <<A4,TORP>>
C
HGET.PROC.CONO
P
IF %1 = "END" GO 9999
MV %3 "PROGRESS BILLINGS REPORT"
MV %4 "127,899"
HREPORT.SCRN
P
IF %1 = "END" GO 9999
C
C
C
IF %4 = "T" HTERM132
IF %4 = "P" HSET.132WIDE
P
HSSELECT INVOICE
H WITH CONO =
A"1
H BY CUST
H BY JOB
H BY INV.NUM
H AND WITH TRANS.FLG = "PB"
H AND WITH PB.PERIOD = ""
STON
H<
P
C
IF #S GO 9997
C
H LIST INVOICE
H JOB
H CUST
H CUST.NAME
H INV.NUM
H INVOICE.DATE
H TOTAL USER.TOTAL
H ID-SUPP
H HEADING "'C'
A2
H'L'
H'C'PROGRESS BILLINGS
H 'C'AS OF
A3
H 'L'"
H FOOTING "PAGE 'P' - DATE 'D'"
IF %4 = "P" H LPTR
P
9996 C
T (0,22),"Report is complete, press <enter> to continue"
IP:
GO 9998
9997 C
T (0,22),"No items were selected, press <enter> to continue"
IP:
9998 C
IF %4 = "T" HTERM80
IF %4 = "P" HSET.132WIDE
P
9999 C
MV %1 "RETURN"
HEASY.MENU
P

Hard Cording Values - Don't do it! If you need to modify a proc that has hardcoded values clean up the code to read like the following examples. This first sample code contains a hidden bug. When the company number is 004 or 005 then the program for 003 is run and the correct program is the same one as company 002. This was a real occurrence! The benefit of the latter sample (if you don't have time to really fix the program) is that the appearance of the code reflects the function of the proc. And if someone creates company 006 then nothing happens which is better than the wrong thing happening. Hardcoding values also makes program testing much more troublesome because each program must be checked for each company.

Instead of this:

090: A"
091: 80 C
092: STON
093: C We may want a separate menu option for these ? ...
094: C
095: IF %1 # 002 GO 85
096: C H DAILY.HOURS.LIST
097: H PRS.DAILY.HOURS.LIST
098: P
099: GO 88
100: 85 C
101: H PRS.DAILY.EARN.LIST
102: P
103: 88 C
104: GO 999
105: 100 C

Please clean it up to look like this:

044: C
045: IF %1 = "001" H DAILY.HOURS.LIST
046: IF %1 = "002" H DAILY.HOURS.LIST
047: IF %1 = "003" H DAILY.EARN.LIST
048: IF %1 = "004" H DAILY.HOURS.LIST
049: IF %1 = "005" H DAILY.HOURS.LIST
050: P

Demorgan's Law - When coding an access sentence for selection and the selection criteria require multiple things not equal to something then code it as follows:

SELECT SQV WITH NO STATUS = "A""B""C"

Instead of this:

SELECT SQV
WITH STATUS # "A"
AND WITH STATUS # "B"
AND WITH STATUS # "C"

Select List and Sort Statements: It is better to perform two different commands to do SSELECT and LIST. After the SSELECT statement a check should ALWAYS be done to make sure that the access command returned a select list. The 9997, 9998 and 9999 proc labels are reserved for this standard behavior of the procs. If a proc is allowed to produce a report to the terminal, then be sure to use the 9997 routine to stop after to last page and require the operator to hit return. Please follow the template below closely.

USER.PREBILL.NOPOST

PQN

C$ tpackert - 10:24:05 Oct 07 1997

C$ THE REPORT WIDTH IS 79

C

C Copyright 1997, Avanti-Case Hoyt - all rights reserved

C Portions Copyright Computer Business Associates/Vercom Software

C

C this proc is a template to be used when setting up new procs, this proc

C prompts for the Terminal or printer option (question # 899)

C it also waits for a carriage return if it is run to the terminal

C it also executed the Select and List separately so that the proc

C can check for the successful generation of a list with IF #S and jump

C to label 9998 if no select list results

C

C

C <<A1,CONO>>

C <<A2,CONAME>>

C <<A3,REPORT.DATE>>

C <<A4,TORP>>

C

HGET.PROC.CONO

P

IF %1 = "END" GO 9999

MV %3 "PROGRESS BILLINGS REPORT"

MV %4 "127,899"

HREPORT.SCRN

P

IF %1 = "END" GO 9999

C

C

C

IF %4 = "T" HTERM132

IF %4 = "P" HSET.132WIDE

P

HSSELECT INVOICE

H WITH CONO =

A"1

H BY CUST

H BY JOB

H BY INV.NUM

H AND WITH TRANS.FLG = "PB"

H AND WITH PB.PERIOD = ""

STON

H<

P

C

IF #S GO 9997

C

H LIST INVOICE

H JOB

H CUST

H CUST.NAME

H INV.NUM

H INVOICE.DATE

H TOTAL USER.TOTAL

H ID-SUPP

H HEADING "'C'

A2

H'L'

H'C'PROGRESS BILLINGS

H 'C'AS OF

A3

H 'L'"

H FOOTING "PAGE 'P' - DATE 'D'"

IF %4 = "P" H LPTR

P

9996 C

T (0,22),"Report is complete, press <enter> to continue"

IP:

GO 9998

9997 C

T (0,22),"No items were selected, press <enter> to continue"

IP:

9998 C

IF %4 = "T" HTERM80

IF %4 = "P" HSET.132WIDE

P

9999 C

MV %1 "RETURN"

HEASY.MENU

P

 

Hard Cording Values - Don't do it! If you need to modify a proc that has hardcoded values clean up the code to read like the following examples. This first sample code contains a hidden bug. When the company number is 004 or 005 then the program for 003 is run and the correct program is the same one as company 002. This was a real occurrence! The benefit of the latter sample (if you don't have time to really fix the program) is that the appearance of the code reflects the function of the proc. And if someone creates company 006 then nothing happens which is better than the wrong thing happening. Hardcoding values also makes program testing much more troublesome because each program must be checked for each company.

Instead of this:

090: A"

091: 80 C

092: STON

093: C We may want a separate menu option for these ? ...

094: C

095: IF %1 # 002 GO 85

096: C H DAILY.HOURS.LIST

097: H PRS.DAILY.HOURS.LIST

098: P

099: GO 88

100: 85 C

101: H PRS.DAILY.EARN.LIST

102: P

103: 88 C

104: GO 999

105: 100 C

Please clean it up to look like this:

044: C

045: IF %1 = "001" H DAILY.HOURS.LIST

046: IF %1 = "002" H DAILY.HOURS.LIST

047: IF %1 = "003" H DAILY.EARN.LIST

048: IF %1 = "004" H DAILY.HOURS.LIST

049: IF %1 = "005" H DAILY.HOURS.LIST

050: P

Demorgan's Law - When coding an access sentence for selection and the selection criteria require multiple things not equal to something then code it as follows:

SELECT SQV WITH NO STATUS = "A""B""C"

Instead of this:

SELECT SQV

WITH STATUS # "A"

AND WITH STATUS # "B"

AND WITH STATUS # "C"

 

Calling Basic from Dict Items - You can easily call basic programs from Dict items using Unidata. Generally you should avoid designing files that need to do things like this btu sometimes it just can not be helped because of the need to interface to a file in an unexpected way. When this happens a simple basic program called from the dict item, can save many hours of programming an entire report in Basic and will allow you to reuse the dict item in multiple reports.

In the sales reports, there is the need to determine the SALES date of a job. Unfortunately, there is not a single sales date available due to the fact that there are multiple invoices and multiple invoice dates. For sales forcasting purposes, it is required to have a single date for all sales of a job.

There is no single attribute where this data can be retrieved. It needs to be determined programmatically. In order to solve this, I created the subroutine in the DICT.BP file called COMM.SALES.DATE. This program determines the Sales date by finding the invoice date of the largest, non-pre-bill invoice. You can not use the most recent invoice date because jobs often have small invoices issued after the main "FINAL" invoice. The best guess for Sales period is the perion in which the largest invoice amount that is a non-prebill (ie invoice number does not end in "PB").

The rules for calling basic programs from dict items:

 
basic program must be globally catalogued

Style rule - Place all basic programs call from dict items in a separate file (ie DICT.BP)

The first parameter of the subroutine is the value passed back to the dict item

The DICT item in the COMMISSION file:

:ED DICT COMMISSION COMM.SALES.DATE

Top of "COMM.SALES.DATE" in "DICT COMMISSION", 6 lines, 66 characters.

*--: P

001: I

002: SUBR('COMM.SALES.DATE',@RECORD,@ID)

003: D2/

004: SALESēDATEē--------

005: 8R

006: S

Bottom.

*--:

The basic program that is Globally cataloged:

The SALES.PERIOD parameter is the one passed back to the dictionary for processing. The second and third parameters are the first and second parameters from the DICT item (ie @RECORD and @ID). Note that the dict item @RECORD is a dynamic array. You can save parameter passing overhead, by passing specific attributes when the items get larger and only one or two attributes are needed by the subroutine. In this case, I only need attributes 15 and 18, but I got lazy.

COMM.SALES.DATE

SUBROUTINE COMM.SALES.DATE(SALES.PERIOD, COMM.ITEM, COMM.ID)

*

*

* t packert - 3/10/98

*

* this program is called from the COMMISSION file dict items, the purpose

* is to determine a single date of sales. It does this by sorting the

* invoice dates and totaling the amounts. The invoice date with the

* largest amount invoiced is the sales date. This is used for the

* sales forecasting information which needs a single invoice date for

* considering the amounts as SALES.

*

SALES.PERIOD=''

*

*

* first build an array of the invoice amounts totaled by date

*

AMT.ARR=''

DATE.ARR=''

MAX=DCOUNT(COMM.ITEM<14>,@VM)

FOR INDX=1 TO MAX

INVOICE.NUMBER=COMM.ITEM<14,INDX>

IF INVOICE.NUMBER[LEN(INVOICE.NUMBER)-1,2] # "PB" THEN

SALES.DATE=COMM.ITEM<15,INDX>

LOCATE(SALES.DATE, DATE.ARR;POS;'AR') THEN

AMT.ARR<POS> += COMM.ITEM<18,INDX>

END ELSE

DATE.ARR=INSERT(DATE.ARR,POS;SALES.DATE)

AMT.ARR=INSERT(AMT.ARR,POS;COMM.ITEM<18,INDX>)

END

END

NEXT INDX

*

* then find the date with the largest amount and use this date

*

NUM.PERIODS=DCOUNT(DATE.ARR,@AM)

IF NUM.PERIODS=1 THEN

SALES.PERIOD=DATE.ARR<1>

END ELSE

SALES.PERIOD=''; MAX.AMT=0

FOR INDX=1 TO NUM.PERIODS

IF AMT.ARR<INDX> > MAX.AMT THEN

SALES.PERIOD=DATE.ARR<INDX>

MAX.AMT=AMT.ARR<INDX>

END

NEXT INDX

END

*

RETURN

*

 

The Sales reports also need to break out the components of costs and these are stores as specific values in a single attibute in the COMMISSION file.

Attribute 23 of Commission file: (poor design - should be multiple attributes)

Value 1 = Paper Cost

Value 2 = Separations

Value 3 = Authors Alterations

:ED DICT COMMISSION PAPER.COST.AMT AA.COST.AMT SEPARATION.COST.AMT D E . T R R P

< 1 > Top of "PAPER.COST.AMT" in "DICT COMMISSION", 6 lines, 77 characters.

*--: P

001: I

002: SUBR('COMM.PAPER.COST',@RECORD,@ID)

003: MR02,

004: PAPERēCOST AMTē------------

005: 12R

006: S

Bottom.

*--:

COMM.PAPER.COST

SUBROUTINE COMM.PAPER.COST(PAPER.COST,COMM.ITEM, COMM.ID)

*

PAPER.COST=0

*

MAX=DCOUNT(COMM.ITEM(23),@VM)

FOR INDX=1 TO MAX

PAPER.COST += COMM.ITEM<23,INDX,1>

NEXT INDX

*

RETURN

*

< 2 > Top of "AA.COST.AMT" in "DICT COMMISSION", 6 lines, 71 characters.

*--: P

001: I

002: SUBR('COMM.AA.COST',@RECORD,@ID)

003: MR02,

004: AAēCOST AMTē------------

005: 12R

006: S

Bottom.

*--:

 

COMM.AA.COST

SUBROUTINE COMM.AA.COST(AA.COST,COMM.ITEM, COMM.ID)

*

AA.COST=0

*

MAX=DCOUNT(COMM.ITEM(23),@VM)

FOR INDX=1 TO MAX

AA.COST += COMM.ITEM<23,INDX,3>

NEXT INDX

*

RETURN

*

< 3 > Top of "SEPARATION.COST.AMT" in "DICT COMMISSION", 6 lines, 79 characters.

*--: P

001: I

002: SUBR('COMM.SEPRTN.COST',@RECORD,@ID)

003: MR02,

004: SEPRTNēCOST AMTē------------

005: 12R

006: S

Bottom.

*--:

COMM.SEPRTN.COST

SUBROUTINE COMM.SEPRTN.COST(SEPRTN.COST,COMM.ITEM, COMM.ID)

*

SEPRTN.COST=0

*

MAX=DCOUNT(COMM.ITEM(23),@VM)

FOR INDX=1 TO MAX

SEPRTN.COST += COMM.ITEM<23,INDX,2>

NEXT INDX

*

RETURN

*