Programming Standards - March14 PDF
Programming Standards - March14 PDF
Contents
1
2
Introduction ................................................................................................................................................ 5
Database design........................................................................................................................................... 6
2.1
Table (database file) ...................................................................................................................... 6
2.2
Fields ............................................................................................................................................. 6
2.3
Indexes .......................................................................................................................................... 7
2.4
2.5
Sequences...................................................................................................................................... 9
2.6
Naming conventions.................................................................................................................................. 11
3.1
General ........................................................................................................................................ 11
3.2
3.3
Internal procedures...................................................................................................................... 12
3.4
Functions ..................................................................................................................................... 12
3.5
Variables ..................................................................................................................................... 12
3.6
3.7
Frames ......................................................................................................................................... 14
3.8
Buffers......................................................................................................................................... 14
3.9
3.10
3.11
Fields ........................................................................................................................................... 15
3.12
Streams ........................................................................................................................................ 15
3.13
Block labels................................................................................................................................. 16
3.14
Indexes ........................................................................................................................................ 16
3.15
Prodataset .................................................................................................................................... 16
3.16
3.17
3.18
Browses ....................................................................................................................................... 17
3.19
Variables ..................................................................................................................................... 26
5.3
Page |2
5.4
5.5
5.6
6.3
Capitalisation .............................................................................................................................. 31
6.4
6.5
Punctuation ................................................................................................................................. 32
6.6
Comments ................................................................................................................................... 32
6.7
6.8
6.9
6.10
6.11
Abbreviations .............................................................................................................................. 35
6.12
Frames ......................................................................................................................................... 35
6.13
Transaction.................................................................................................................................. 36
6.14
6.15
6.16
6.17
6.18
6.19
Debugging ................................................................................................................................... 40
6.20
Miscellaneous ............................................................................................................................. 40
Portability .................................................................................................................................................. 42
7.1
Terminals .................................................................................................................................... 42
7.2
Workfiles..................................................................................................................................... 42
7.3
8.3
8.4
8.5
Substrings.................................................................................................................................... 46
Page |3
11
12
Page |4
1 Introduction
The QAD GRC development endeavors, with regard to Progress/OE, to concentrate itself mainly
with QAD EA. Customization consists of new developments and adjustments of the existing
packages. All adaptations developed by QAD GRC will be based on the QAD GRC standard.
These programming standards don't always follow the programming standards of QAD inc. and
related products; If an existing standard program is customized, the existing code will not be
adapted. So more than one programming standard may occur in an adjusted program.
These programming standards are for various Progress/OpenEdge (and QAD EA ) releases. If a
certain standard is not valid for a certain release, its explicitly mentioned. Also in this manual
mandatory standards and recommendations are described; Mandatory standards must be used.
Recommendations should preferably be used.
The document itself explains the general programming standards. We are aware that the standard
will remain open to improvement. This document should reflect changes and developments. All
comments and ideas (in writing) are welcome.
Page |5
2 Database design
2.1 Table (database file)
Mandatory
File names should be close to 8 characters for portability. Another reason to keep file names
short is that Progress/OE stores these names in the compiled R-code. Therefore, long names
have a direct impact on R-code size. This also applies to field, variable, temp & work table
names.
Example
pt_mstr
xxpt_mstr
xxerr_det
2.2 Fields
Mandatory
Page |6
The format for logical data types should always have the positive format on the left and
the negative on the right. A format of "No/Yes" could cause severe logic problems.
Recommendation
Note that when you add a decimal field into database it has only 2 decimals by default.
Always consider to extend this number, e.g. to 10 decimals.
Use the format option with the data type char.
Be aware of the limitations of using the RECID (or ROWID ) data type. During a data
dump using EXPORT, the RECID is conveted to the unknown value (?). Using stored
RECIDS normally requires the developer to write custom load procedures.
Furthermore the bulkloader description file should be adjusted if fields with the RECID
datatype are used in the database. If the bulkloader file is created, the fields with RECID
datatype are not part of the file, this should be changed manually.
For character field formats, avoid using the "!" and "9" formatting characters unless a
character MUST be entered because Progress/OE does not allow a blank or nonalphabetic character when using this format.
COLUMN-LABELS should be the same width or less than the format of the field to act as
a "short label" for tight reports and screens. If screen space is limited do not stack
column labels more than 2 levels deep.
Information
Progress/OE does not check the syntax of a validation expression until a procedure is
compiled that utilises that expression. Whenever a VALEXP is entered or modified, the
expression can be checked by:
INSERT filename.
This will check the syntax of all the validation expressions in the file. Also remember
that the dictionary validation only applies to data entry statements (UPDATE, SET and
PROMPT-FOR, but not for event statements (ENABLE and DISABLE)).
Example
Date
:
Percentage :
Codes
:
Amount
Qty
:
Description
99/99/99
>>9.99%
"x(8)"
: ->>>,>>>,>>9.99
->>,>>>,>>9.9<<<<<<<<<
: "x(24)"
2.3 Indexes
Mandatory
The domain field (when it exists) should be the first field in all indexes.
The primary index should be the index that is used most often for traversing a file with a
FOR EACH, not the index mainly used for random access. This will cause the data to be
Page |7
dumped in that order. When the data is subsequently reloaded into a new database, the
RECID (ROWID) are assigned sequentially to the records during the load. When the
primary index is built, it will be build with references to these sequential RECIDs. All
other indexes will be built with the RECIDs in non-sequential order.
Do not create indexes whose components are exactly the same as the leading
components of another index.
Index A
Index B
Field A
Field A
Field B
Field B
Field C
Index B would usually be considered redundant in this example.
Information
Forgetting to define an important index to support a frequent access mode can cause poor
performance.
Never use the over-rule option of a database trigger; If you define a database trigger with
the overrule option and a session (in a program) trigger, the database trigger will not fire
!! Make sure that always both triggers (if defined) are fired !! Currently the PROCON
triggers are the only exception on this.
Use the delete trigger for the deletion of related data, for example comments and detail
(line) information.
Don't give messages or allow user input in a trigger; Use return error instead and take
action in the calling procedure or event (use ERROR-STATUS:ERROR).
Don't use the CRC option with triggers because this requires that the source and the
object must be located in the same directory. Normally Progress/OE will search the
trigger program via the propath , but when using the startup -trig parameter, the relative
(trigger) location will be added to each propath entry to find the trigger program.
Information
Use of the WRITE or ASSIGN trigger is preferably because this trigger has access to the
old and new (changed) data (old + new buffer).
Page |8
Trigger references are dumped with the df file. The trigger code itself is a program and
can't be dumped or loaded.
All trigger definitions (object) can be up to 64K (not advisable).
More detailed information on triggers can be found in the Progress/OE Programming
Handbook (Chapter 10).
2.5 Sequences
Recommendation
A (cycle) sequence always gives a value; No checks are done if the current (in case of
the cycle) or previous value is used. Be aware that using sequences doesnt give a
correct number range because undo doesn't undo the sequence (don't use, for example,
sequences for invoice numbers).
Sequence (current) values are separately dumped using a QAD EA 36 menu option or
via the database administration functions (preferred).
A custom database should be created to hold custom tables , indexes and sequences
If a new database table is created, also add some user & underscore fields for future use.
Also (re)create load and dump procedures when new tables are added (or changed) and
deliver them to the customer.
To create shadow tables use the template generated by ICT as starting point.
Information
In a standard QAD EA database two files (usrw_wkfl and mfc_ctrl) can be used for adding
new field and/or files.
All the fields except usrw_key3 can be used; usrw_key3 should contain the program name
(without .p) of the program where the records are created. QAD Inc uses the qad_wkfl,
Page |9
which has the same structure as usrw_wkfl, for temporary bug fixes. They use qad_wkfl to
store new field values or files, without changing the standard database structure. In the next
release the database structure will be changed and new files will be created.
If a new field or new parameter should be added to a control file, the file mfc_ctrl file can be
used. For example this file is used to store an additional printer name. The following code is
added in the sources:
create mfc_ctrl.
assign mfc_domain = global_domain
mfc_field
= "gl_stat_prt"
mfc_type = "C"
mfc_label = "Statem. of acct. printer"
mfc_module
= "FIN"
mfc_seq = 99.
Please note, the field is not added to the module control program; It must be added
manually!!
P a g e | 10
3 Naming conventions
3.1 General
All names should be in English language to serve the largest community of developers.
Mandatory
Use the "_" (underscore) character to connect words, prefixes and suffixes in the names
described below (except for internal procedures).
Use short but explicit names for identification.
The following file name and extensions should be used for QAD EA procedures:
xxmmyyzz.x external procedure (program)
xx = fixed value
mm = module name
yy = program indication (free to choose)
zz = mt (maintenance), iq (inquiry), rp (report), up
(update) or
pm (control file maintenance.)
.x =
xxyz.t
xxyyyyyy.i
Clone of a standard program, for customization, should be named with xx prefix to the
standard program name.
Eg: xxsosomt.p
P a g e | 11
The name of the procedure is normally a short name of the procedure purpose.
Always use input & output parameters to call internal procedures and try to avoid the
use of local & shared variables.
Example
run checkItem (input pt_part, buffer xxpt_mstr, output lvl_ok, buffer tt_log).
PROCEDURE
define
define
define
define
checkItem:
input parameter
ipc_in_part
parameter buffer ipb_xpt_mstr
output parameter
ipl_out_ok
output parameter table
like pt_part
for xpt_mstr.
like mfc_log
for tt_log.
no-undo.
no-undo.
....
END PROCEDURE.
3.4 Functions
Mandatory
The name of the function is normally a short name of the function performed.
Example
if testWO(wo_nbr) = false
then do:
{pxmsg.i ....} /* .... */
next mainloop.
end.
3.5 Variables
P a g e | 12
Mandatory
Always use LIKE database field if the variable is related to a database field. Will raise
readability.
If logicals (Yes/No fields) are defined for UI, then use LIKE mfc_logical. This field has
the format of the database language. (STD-0044)
Give logicals a positive variable name; Use OK instead of NOTOK.
To distinguish local variables from database fields, a prefix should be used to set the
actual names off.
The prefix lv for local variable and sv for shared variable should be followed by
the data-type indicator character and an underscore.
c - Character
d - Decimal
t - Date
i - Integer
r - Recid
Parameters passed to internal or external procedure should have ip, op and iop prefix
Followed by the data-type character described above
Examples:
lvt_start_date
lvc_login_name
lvi_qty
svc_usr_name
ipc_curr_code
opd_exch_rate
Recommendation
Try to standardize the names of the variables. Use the following codes:
date
perc
qty
code
amt
desc
nbr
st
for
for
for
for
for
for
for
for
'date' fields
'percentage' fields
'quantity' fields
'code' fields
'amount' fields
'description' fields
'number' fields
'status' fields
P a g e | 13
Recommendation
The NetUI interface supports only the Fill-In data type OBCM widgets are not
supported.
3.7 Frames
Recommendation
As with variables, frame names should also have a prefix to set them off. In addition
the frame should be identified by type (local or shared). In multi-screen conversations,
the frame may also need to be numbered. Examples:
f_cust
(local) customer frame
fs_cust
shared customer frame
Selection frames of inquiry or reports should have a frame called "a" (see Chapter
concerning QAD EA includes).
3.8 Buffers
Recommendation
Always use temporary tables because you can define indexes on the temporary tables,
use like database table name option and temporary tables will put there data on disk if
memory is full. Workfiles dont have these features.
Always use the option NO-UNDO, in case UNDO processing is required use a
comment to identify the reason.
Recommendation
To distinguish temporary and work tables from database tables, prefix tt_ should be
used for temp-tables while workfiles can have wf_.
The fields of a temp-table should have a prefix of tt and work file fields should have
wf prefix.
Also if the temporary or work table represents a dictionary table, the temporary or work
table name should contain the dictionary table name. This will make it easier to locate
when doing text searches.
tt_so_mstr
ttso_nbr
P a g e | 14
Recommendation
3.11 Fields
Mandatory
Fields should preferably adopt the name assigned to corresponding value in QAD
standard table.
Eg: Table Name: xxpt_mstr
3.12 Streams
Recommendation
Streams should also have a prefix lvs AND a suffix to identify input and output
streams:
lvs_rep_in
lvs_det_out
P a g e | 15
Duplicate block labels are allowed in the same procedure, however, this practice is
discouraged because of the potential for confusion.
The first block label in a procedure should be called "mainloop" (the outside loop).
Recommendation
Block labels should have a prefix and should indicate the block contents:
trans_blk:
transaction block
level1_blk:
level1 block
3.14 Indexes
Mandatory
In domain(ed) systems an index must contain the domain field as the first field
When an index consists of only one field (apart from domain), the name of the index
should be the same as the field.
When an index has more than one component, the index should attempt to paraphrase
the fields used in the index
cp_part_cust
3.15 Prodataset
Mandatory
All custom messages should be created from the specified range of 21000 - 21999.
It is recommended that the messages be available in English.
Message Numbers in ranges 9000 to 9999 and 90000 to 99999 will be reserved
exclusively for our customers and distributors.
Labels should be preceded with the prefix QAD_CUST_.
Also, the label term and the text should be in English.
P a g e | 16
When program output is directed to an ASCII file, the following naming conventions
should apply
For portability, the file name should not exceed 8 characters. Only 3 character
suffix is allowed.
To prevent collisions with other users who may also be writing ASCII files to
disk, one of the following conventions can be used:
- Write to the user's home directory, or
- Make the name of the file unique. Usually the TIME function with a
tiebreaker such as the QAD EA variable global_userid (or mfguser) is
sufficient to prevent naming conflicts.
3.18 Browses
Browses always have a name consisting of 2 letters and 3 numbers.
Custom browses should always start with xx (unless defined differently within the project) and
require a sequential number between 001 and 999.
When working in Browse maintenance, press the right mouse button on the Query or Header tab
and select: Show All tabs. The History tab now appears. In the history tab the following lines are
automatically created:
* $Revision: $ BY: Marcel Boertien DATE: 11/09/16 ECO: *XXXX*
In the XXXX area, enter the line next version of the browse. Use 1.0 for the first version and 1.1
for the next small change of a browse etc. Major changes will get a 2.0 number etc.
Proxy
CUST_XXX.p
P a g e | 17
4 Include files
4.1 General
Mandatory
Shared frames, buffers, variables etc. should be declared using an include file.
Include files should not terminate a block started outside of the include file. Conversely,
an include file should not start a block that ends outside of the include file.
Recommendation
Note that according to STD-0114 you should avoid using of shared variable. You
should not introduce a shared variable in a new program/procedure. If another
program or a procedure needs a variable, pass it as an input or input-output parameter.
Do not define variables within a procedure include file. The only exception is when the
include is especially developed for some purpose (i.e. Non-Intrusive development,
include: icdef.i).
Use standards include files whenever possible.
Named parameters rather than positional parameters should be used for new include
files, whenever parameters need to be passed to an include file.
It is recommended that include files not be nested more than 3 levels deep as debugging
can be difficult. Also the "explosion" of the include files seen when using the
COMPILE/LISTING option makes the code difficult to read
Example
/* Simple include */
{include.i}
/* Multiple named parameters */
{include.i &file-name
= pt_mstr
&frame-attr
= "no-box 2 columns"
&key
= pt_part }
Include
mfdtitle.i
mfdeclre.i
pxmsg.i
mfphead2.i or
mfphead.i
mftrl080.i or
Needed for
main programs (variables definition)
sub-programs (variables definition)
standard messages (msg_mstr)
header for report with 80 or 132 columns
trailer for report with 80 or 132 columns (also contains
P a g e | 18
"
"
"
General
"
Call
"
General
inquiry/report
eB call
eB general
"
mfrtrail.i
mfreset.i
mfrpexit.i
mfquoter.i or
mfquoter.p
window1.i &
window2.i
mfnp*.i
gprun.i
gprunp.i
mfdel.i
gpselout.i
pxrun.i
gplabel.i
cxcustom.i
mfreset.i to
reset output
abort report
batch option
scrolling window
browse through table with cursor keys
call external program
call internal procedure in persistent external program
deletion of (temp) table records
output selection for inquiry/report
call internal procedure
translation of frames
EPM code (contains pre-processors with code)
P a g e | 19
columns reports or {mfphead2.i} for 80 columns reports. The header is repeated on each
page.
{mfphead.i}
or
{mfphead.i "stream str_log"}
{mfreset.i} - Close output
The first include will close the output and the message include will give a message that its
the end of the report. If an output is opened via a stream, the stream name is optional in the
"reset" include.
{mfreset.i}
{mftrl080.i or mfrtrail.i} - Report selection frame, close output
With use of these includes (for respectively 80 or 132 columns report), the selection values
of FRAME A will be printed and who requested the report. Than the output is closed. If the
output destination was scrolled output, the output is quotered into an ASCII file and this file
is viewed (character only). The same if the output is (hard coded) window for a GUI
environment. Note that when the output is opened via a stream, close it also with the same
stream name.
end. /* for each so_mstr */
/* REPORT TRAILER */
{mfrtrail.i "stream str_log"}
end. /* mainloop */
{mfrpexit.i} - Abort Report output
This includes checks whether the F4 or Esc key has been pressed during the printing of the
report. If so, a "Report aborted" message is printed and a LEAVE is performed. Note that is
mandatory that this include is only used within a FOR EACH block. The optional parameter
suppresses the "Report aborted" message;
{mfrpexit.i false}
end. /* each sod_det */
....
{mfrpexit.i}
end. /* end so_mstr */
/* REPORT TRAILER */
{mfrtrail.i}
end. /*mainloop */
P a g e | 20
{mfquoter.i or mfquoter.p} - Report output with a batch option, prepare for the batch
When a report must be executed in the QAD EA batch, the selection values must be saved
into the (global shared) variable BCDPARM. To save a selection value, use the
{mfquoter.i} includes. To save 20 selection values directly, use the mfquoter.p
program. Note that its mandatory that the sequence of saving the values must be identical
with the UPDATE or SET phrase.
bcdparm = "".
{gprun.i ""gpquote.p"" "(input-output bcdparm, 20,
nbr, nbr1, cust, cust1, string(ord),
string(ord1), spsn, spsn1,
po, po1, stat, stat1, quote, quote1, base_rpt,
string(mixed_rpt),
string(include_allocated),
string(include_picked),
string(include_shipped),
string(include_unprocessed))"}
{mfquoter.i print_options}
{mfquoter.i show_comments}
Note that the mfquoter.p program is used more often because each include takes a lot of
coding space (64K limit). Another option is an internal procedure with the mfquoter.i
includes.
{window1.i }
These include files can be used to create new scrolling window procedures. {window1.i}
is used to scroll through a single indexed field. {window2.i} permits the user to tab and
scroll through either of the two indexed fields.
{window1.i loc_mstr loc_loc loc_loc "use-index loc_loc"
yes
"Location Master"}
Note: The window1.i and windo2.i includes are applicable to ChUI only and do not work
with .NETUI .
The parameters for {window1.i} are:
1
File name
2
Field names surrounded by quotes to be displayed, including the indexed
field
3
Field name of indexed field (must be highest order field)
4
Index name displayed as "use-index pt_part"
P a g e | 21
5
Either "yes" or an expression surrounded by quotes to be used as selection
criteria
6
Frame title
Note that these window includes only work for 1 table. When using multiple tables, the
window includes cant be used. A workaround could be viewers & browsers.
Viewers & browsers Valid as of QAD EA
Browse Maintenance should be used to create browses and lookups. The NetUI version of
the program should be used to create the browses.
{mfnp.i ...}
The include file mfnp.i is one of several mfnp*.i include files which can be used to add the
capability to browse through a file using F9 (PREV) / cursor-up and F10 (NEXT) / cursordown. This include can only be used in an EDITING phrase. The editing phrase is added to
the end of the update statement. The FRAME-FIELD function is used to test an input field
to determine whether or not special action should take place. The include searches through
the table retrieves records using find next and/or find prev. At the beginning or end of the
table, (global shared) variable RECNO will be equal to ?.
update lvc_from_so
lvc_to_so
llv_detail
editing:
if frame-field = "lvc_from_so" then do:
{mfnp.i so_mstr lvc_from_so so_nbr lvc_from_so
so_nbr so_nbr}
if recno <> ? then
display so_nbr @ lvc_from_so.
end.
else if frame-field = "lvc_to_so" then do:
{mfnp.i so_mstr lvc_to_so so_nbr lvc_to_so
so_nbr so_nbr}
if recno <> ? then
display so_nbr @ lvc_to_so.
end.
else do:
readkey.
apply lastkey.
end.
end.
/* EDITING */
P a g e | 22
Note that here also the CASE statement could be used for checking the value of the FRAMEFIELD. The include file has six parameters:
1
File name
2
Input variable #1
3
Field corresponding to input variable #1
4
Input variable #2
5
Field corresponding to input variable #2
6
Index name
The values for the parameters 4 and 5 may be the same or different than those for parameter
2 and 3.
{gprun.i ....}
This include file can be used to call external procedure (or programs). Besides the program
itself, you give it input/output parameters and if the program must be called persistent (or
not).
{gprun.i 'sosomt.p'} or {gprun.i ""sosomt.p""'}
{gprun.i 'sosoa2.p' "(input so_nbr, output l_stat,
buffer tt_list"}}
{gprun.i "'nitrig' + string(l_i, '99') + '.p'"
" "
"persistent set
l_trig_handle[l_i]"}
{gprunp.i ....}
This routine is used to run an internal procedure contained in a persistent external procedure.
See an example below from sorp1001.p.
/*L024*
MASTER */
/*L024*/
{mfdel.i ....}
With this include the deletion process of any (temp) table can be done faster because the
include makes a new transaction per 100 deleted records. Note that no transaction must
be started
{mfdel.i so_mstr "where so_due_date <= (today 100)"}
{mfdel.i tt_list}
P a g e | 23
P a g e | 24
output mc-error-number)"}
P a g e | 25
5 Procedure structure
5.1 Header
Mandatory
The following item must be found in the header comments of any program
Program name
Author
Purpose and general description of program
Copyright
Modification history:
- Date
- Who
- Change request number (i.e. SUI number, Requirement ID or Internal customer
reference)
Example New program
/*
/*
/*
/*
*/
*/
*/
*/
5.2 Variables
Mandatory
Variables should not have the same name as a database field or a database for clarity.
All NEW SHARED/SHARED variables should be stored in include files passing NEW as
a parameter in the program where initially defined. This will make maintenance
easier and also prevent problems with conflicts in data type, NO-UNDO, EXTENT, etc.
{sosqvars.i new}
P a g e | 26
When defining variables, definition options should be aligned for readability. Example:
Note that according to STD-0114 you should avoid using of shared variable. You should not
introduce a shared variable in a new program/procedure. If another program or a procedure needs
a variable, pass it as an input or input-output parameter.
Values used more than once in a procedure should be coded using a defined variable.
block_1:
for lvi_i = 1 to lvi_num:
.... transaction-1 .....
end. /* block_1 */
block_2:
for lvi_i = 1 to lvi_num:
.... transaction-2 .....
end. /* block_2 */
P a g e | 27
The same, but then with pre-processors (which at determined at compilation time and
NOT at executing time)
&SCOPED-DEFINE many_times 5
block_1:
for lvi_i = 1 to {&MANY_TIMES}:
.... transaction-1 .....
end. /* block_1 */
block_2:
for lvi_i = 1 to {&MANY_TIMES}:
.... transaction-2 .....
end. /* block_2 */
Strings must not be hardcoded, messages come from msg_mstr, strings that require
mnemonics come from lngd_det, labels come from lbl_mstr.
Do not build sentences from separate text strings. Create new messages in msg_mstr for
displaying informational messages as well as errors and warning messages.
P a g e | 28
NetUI interface uses standard ChUI code. GUI converted code is not to be used in
NetUI environments.
In GUI environments, never use the define frame statement because the GUI
converter doesnt convert the frame definition (it checks only the form
statements)
Always define a form for better performance and less coding.
All frame phrase options associated with a frame should be listed only on the form
statement for ease of maintenance.
All forms described in a program should be given explicit frame names. This is
particularly important because forms should be placed at the beginning of a program,
scooping them to the program block.
All changes to a program should have a change request-number (i.e. SUI number,
Requirement ID or Internal customer reference). It is not allowed to delete the old
source code.
lvi_a = 1.
lvi_a = lvi_b.
*MFG0409600*/
/*MFG0496000*/
P a g e | 29
**/
**/
**/
Note: If a lot of lines are put between comments (i.e. deleted), its recommended that each
deleted line should start with a | or *. This will give a better view of what has been commented
and what is real coding.
P a g e | 30
6 Procedure style
6.1 Indentation
Mandatory
6.3 Capitalisation
Recommendation
All code will be in lower case with the exception of text inside a comment, preprocessor definitions and the CASE, FUNCTION and PROCEDURE statements.
See STD-0230.
Functions and Procedure names that use the verbNoun naming convention can use the
camel case e.g. getNextWorkOrder
All UNDO, RETRY, LEAVE & NEXT should explicitly indicate the target block.
If UNDO, RETRY, LEAVE, NEXT is used in the corresponding REPEAT, DO or FOR
EACH block, it should be preceded with a block label. The block label should describe
the function of the block.
P a g e | 31
Recommended
The label should be on a line by itself above the block header (see preceding example).
6.5 Punctuation
Mandatory
All statements should be terminated with periods except block labels and block headers
(FOR EACH/DO/REPEAT) which are terminated with a colon.
6.6 Comments
Recommendation
*/
*/
For variables specify label and format in the define or FORM statement rather than on
data handling statements such as DISPLAY and UPDATE.
Recommendation
Use LABEL when defining side-labels and COLUMN-LABEL when defining column
labels.
If possible LABELS, FORMATS, VALIDATION and HELP from the data dictionary
should be used rather than overriding at the procedure level.
Only use not for testing positive named logical and standard functions like CANFIND, AVAILABLE, etc.
Always use DO and END directly around include files. Also with 1 line include files.
Else statements should appear on separate lines, aligned with the if statements to which
they apply.
For compound tests, align the ORs and ANDs.
P a g e | 32
If more that 2 nested IF/THEN/ELSES are used, the CASE statement is a good
alternative because the CASE statement doesnt has the limited of 15 nested
IF/THEN/ELSE
The use of "null" THENs and ELSEs is not allowed. The only exception for this is
when the program is running against an Oracle database.
IF RECID(MyRecord) <> ? then .
Recommendation
Mixed AND and OR conditions should be avoided, but if used the order of evaluation
should be explicitly noted through the use of parentheses.
Information
For a group of conditions connected by AND, the most unlikely condition(s) should be
tested first.
For a group of conditions connected by OR, the most likely condition(s) should be tested
first.
When testing logical fields or variables, explicitly test against TRUE, FALSE, YES and
NO rather than the implicit TRUE or FALSE value of the field.
NOT
BUT
if lvl_logical then
if lvl_logical = true then
NOT
BUT
Example
if lvi_vara
lvi_varb
lvi_varc
do:
stmt1.
stmt2.
end. /* IF
= 1 and
> 2 and
< 30 then
lvi_vara*/
case lvd_a:
when 1 then do:
stmt1.
stmt2.
end. /* lvd_a = 1 */
when 2 then do:
stmt3.
stmt4.
end. /* lvd_a = 2 */
otherwise do:
P a g e | 33
stmt5.
stmt6.
end. /* REMAINING */
end case.
If common table names are going to be used between multiple databases, the convention
dbname.tablename.fieldname should be used. Avoid the use of the same table & field
names.
Lists of field names in a DISPLAY, UPDATE, FORM etc. should be shown as
follows.
One field per line indented 3 spaces. Also if the frame phrase is used, it will be
aligned + 1 space with the starting statement.
update
so_nbr
so_cust
so_due_date
so_remarks
with frame ....
One field per line, but starting on the position of the first field.
update so_nbr
so_cust
so_due_date
so_remarks
with frame ....
Lists of field names and options in a form should be shown as follows; One field per line
and identical line options (types) starting at the same column.
form
so_nbr
at 1
label "SO Nbr"
so_cust
at 25
no-label
so_due_date to 60
no-label skip(1)
so_remarks colon 10
label "Rem"
with frame ....
Recommendation
When displaying a subset of an array, use the FOR option for arrays. This method is
easier to read and maintain than listing out each individual array element.
display array[1 for 5].
P a g e | 34
MESSAGE-LINES statement
Only returns the constant '2'.
VALIDATE statement, PAUSE statement and MESSAGE .....
VIEW-AS ALERT-BOX. statement in NetUI environments.
STOP statement
Useful for testing only.
ENTERED function
The entered flag is easily cleared by an UNDO, RETRY and could lead to incorrect
results.
USE-INDEX option
Essentially hard codes an index name into a procedure. Don't use unless knowledge of
data distribution shows that automatic index selection will be incorrect. Also because
multiple
indexes can be accessed by the database engine when use-index is not used.
Use
RELEASE statement
To be effective, release should be at the end of the transaction block (or the record
scope).
PAUSE 0 BEFORE-HIDE (be careful see STD-0039) and STATUS statements (see
STD-0091)
Realise that the changes these statements make to the default environment is inherited
by called procedures.
6.11 Abbreviations
Mandatory
6.12 Frames
Mandatory
Frames should be standardized using the default box. Frames used for printed output
should use NO-BOX to recover the blank line that is allocated by default for the
"invisible" graphics box.
Frames should be 80 characters width.
P a g e | 35
6.13 Transaction
Mandatory
Block headers that define a transaction should explicitly use the transaction keyword for
self-documentation and to allow the compiler to catch possible transaction errors. When
there is no transaction, don't use the transaction keyword (because it always forces a
transaction to begin, although no database update will take place).
A hard error (e.g. entering a duplicate key into a unique index) should not send the user
back to the beginning of the block which is the default (UNDO BLOCK-LABEL, RETRY
BLOCK-LABEL). Instead more localised DO ON ERROR blocks along with NEXTPROMPT should be used to keep the cursor on the field where the error occurred.
The UNDO statement should never be used without a RETRY, NEXT, LEAVE OR
RETURN . Always follow the UNDO with the appropriate action (NEXT, LEAVE,
RETRY, RETURN). Always use a block-label.
For good style the left side of the comparison in a where clause will always be the field
used to retrieve the record.
NOT
BUT
error.
When there are multiple conditions in the where clause, use the following forms:
for each cm_mstr where cm_domain = global_domain
and cm_addr >= "10"
and cm_addr <= "A" no-lock:
P a g e | 36
Do not use a FOR EACH block when retrieving a single record based upon a one-to-one
relationship. The use of FOR EACH when retrieving the item misleads one into thinking
that the sales order line <-> item relationship is one-to-many rather than one-to-one.
for each sod_det no-lock:
display sod_det.
for each pt_mstr where pt_domain = global_domain and
pt_part = sod_part no-lock:
display pt_mstr.
end.
end.
USING may be used instead of WHERE because USING specifies the key being used (in
combination with PROMPT-FOR).
If the transaction level and framing support it, all related records should be found in a
single for each statement.
for each cm_mstr no-lock,
each so_mstr where so_domain = global_domain and so_cust = cm_addr no-lock,
each sod_det where sod_domain = global_domain and sod_nbr = so_nbr no-lock:
display ....
Information
Be careful with the OR statement in selections with a FIND as well as FOR EACH loop.
Although Progress /OE has multiple indexing, an OR statement can causes a (partial)
sequential search. In the following example it won't lead to a sequential search through
the entire pt_mstr table.
for each pt_mstr where pt_domain = global_domain
and (pt_part = "12345"
<--- index search
or pt_part = "23456")
<--- index search
for each pt_mstr where pt_domain = global_domain
and pt_group = "AUTO" <--- index search
and (
pt_part = "12345" <--- index search
P a g e | 37
or pt_part
global_domain
>= lvc_part
<= lvc_part
>= lvt_mdate
<= lvt_mdate1
The MATCH statement always causes a (partial) sequential search. Only use matches:
With non-key fields (is automatically sequential search)
With key fields that are not passed through the index because the file is
approached through another file
If the match search only consists of a few records
Use, if possible, the begins instead of the matches statement
for each pt_mstr where
and
and
search
for each pt_mstr where
and
search
for each pt_mstr where
and
pt_domain = global_domain
pt_group =
"AUTO"
pt_part matches "123*"
pt_domain = global_domain
pt_part matches "123*" ...
<--- sequential
pt_domain = global_domain
pt_part begins "123*" ...
P a g e | 38
Selecting the right index is not always easy. Progress/OE does have a so called "index
optimizer", but this uses the available "technical" search possibilities/index, but can not
functionally pass judgment.
Select an index from which as many fields as possible are used beginning with the first
field of the index. In the given example is a sequential search. Although 4 of the index
fields are used, LD_PART is the first field in the index and is not included in the
WHERE-clause! It is recommended to use LD_LOC_P_LOT in this case. Then (the first 2
fields) of the index will be used.
find first ld_det where ld_domain = global_domain
and ld_site
= "1000"
and ld_loc = "O/REP"
and ld_lot = "S12345"
and ld_ref = "0"
use-index ld_part_loc ...
Information
Using the FIELDS option can result in a better performance because when using this
option, only the related fields are sent to the (character/GUI) client and not all the fields
of that record. This can be especially handy for "big" records such as pt_mstr and
so_mstr.
for each pt_mstr fields (pt_part pt_desc1 pt_desc2) no-lock:
display pt_part pt_desc pt_desc2.
end.
If a field is missing in the fields declaration and it is used in the code a runtime error will occur.
P a g e | 39
Combine assignments as much as possible for better performance and saving R-code
space.
Example
NOT
BUT
OR
6.19 Debugging
Recommendation
Use temporary debug statements to verify the flow of code and to locate the area where
a problem occurs, but dont forget to remove the debug message after testing !!
In versions of QAD EA where appservers are used the messages would be seen in the
appserver logs. The view-as alert-box clause is to be avoided as it would cause the
NetUI interface to block for input.
message "Im here".
Information
6.20 Miscellaneous
Mandatory
Use algebraic style logical operators instead of mnemonics (Fortran style), see STD0214.
Always leave at least one space surrounding comparison and assignment operators.
STD-0234.
NOT
BUT
a NE b
a <> b
NOT
BUT
IF a>c THEN
IF a > c THEN
Recommendation
Hard coding of values into any program is discouraged with the following exceptions;
Parameters passed to an include file and unchanging constants (e.g. 12 months in a year)
.
When using DO WHILE/REPEAT WHILE loops, always test for a "less than" or "greater
than" condition rather than "equal to". This prevents infinite looping if the "equal to"
condition is never met.
P a g e | 40
Using REPEAT can result in creating an unnecessary and unwanted transaction. Use DO
instead of REPEAT in this case.
Use DEBLANK for data entry on any character field sensitive to leading blanks (i.e. sort
fields). TRIM function can be used too if available. Needed for Oracle db (STD-0012).
Information
Progress/OE cross-reference output is used to capture all run programs and uses of
"mfmsg...i" files so we can trace program and message usage. However Progress
cannot resolve what the value of variables may be during runtime so any gprun<>.i
or mfmsg<>.i that uses a variable for program or message cannot be resolved.
Please add extra comments (using if false then), with some examples of possible
variable values.
Example
P a g e | 41
7 Portability
7.1 Terminals
Mandatory
Because the possibility exists that Programs may be run using terminals with a varying
number of available lines (25 for DOS, 24 for most ASCII terminals), use the following
functions to calculate the available lines and make maximum use of the available screen
space:
SCREEN-LINES
FRAME-DOWN
FRAME-ROW
FRAME-LINE
In the FRAME Phrase
expression DOWN
ROW expression
Do not use the COLOR statement.
WINDOW-HEIGHT
WINDOW-WIDTH
WINDOW-ROW
WINDOW-COLUMN
Recommendation
7.2 Workfiles
Mandatory
The Progress OPSYS function must be used to enable programming to run under
different operating systems. Example:
P a g e | 42
CASE opsys:
when "UNIX" then do:
...
end.
when "WIN32" then do:
...
end.
END CASE.
Information
OS-COPY
OS-RENAME
OS-DELETE
P a g e | 43
8 Oracle issues
8.1 Record creation and availability
Record creation behaviour and record availability can occur differently between a
PROGRESS database and an ORACLE database accessed through the data server.
Record creation behaviour and record availability can occur differently between a
PROGRESS database and an ORACLE database accessed through the
PROGRESS/ORACLE DataServer.
In native PROGRESS, the index blocks are populated as soon as the key information is
available. In ORACLE, a record is not available until it is "inserted" or "updated". The
PROGRESS/ORACLE DataServer does not insert records to the ORACLE database until
the end of the record scope, therefore making the record available at a later time than
native PROGRESS.
This difference causes a problem when coding with the PROGRESS/ORACLE
DataServer and assuming normal PROGRESS behaviour. A record created in a parent
procedure is not available to a called procedure, even if the primary key is used as the
search condition, because the PROGRESS/ORACLE DataServer has not written the
record to the database. A consequence is the called procedure tries to create the record the
parent had created. ORACLE will flag the duplicate record error once the two records are
actually being written to the database.
To solve this problem in the safest way, you can use the RECID-function. First usage of
the RECID function causes PROGRESS to create an index entry, thus assuring record
availability in sub-procedures. See also STD-0075.
We will show you two possible solutions to the problem, using the RECID function:
DEFINE VARIABLE MyVar AS RECID.
...
update-loop:
REPEAT WITH FRAME frmUpdate:
...
CREATE MyRecord.
ASSIGN MyRecord.KeyField1 = FirstValue
MyRecord.KeyField2 = SecondValue
MyVar = RECID(MyRecord).
/* This is the first method of forcing */
RUN NextProc(RECID(MyRecord)).
IF RECID(MyRecord) <> ? then .
END.
*/
In the above example, the solutions provided can be used together without causing any
problems.
Another way to solve the problem of record availability within an ORACLE environment
is to use one of the statements RELEASE or VALIDATE. A complication however is
that both statements do record validation and this might not be appropriate at that stage in
the program.
P a g e | 44
QAD EA on ORACLE includes a utility named utdbsize.p, which helps locate fields
that need the MAX-LENGTH setting increased.
Care should be taken with fields of data-type DECIMAL within an ORACE
environment. The protoora utility checks the format of a field to determine the scale
(= total length) and precision of its ORACLE counterpart NUMBER. The decimal
positions before the decimal point are regarded to be the scale and the decimal
positions after the decimal point are regarded to be the precission of the ORACLE
number field. The decimals-option for a PROGRESS decimal field is not taken in
consideration at all. Therefor a PROGRESS decimal field with a format of ">>>>,>>>,>>9.99" (16 pos.) and 3 decimals are converted by protoora to an
ORACLE number field with a scale of 14 and a precision of 2. There are 2 possible
solutions to this problem. Firstly: make sure the format reflects exactly what you want
the scale and precision to be in ORACLE and format the fields within your
application in the way you want them to see. Secondly: manually change the scale
and precision of the number fields in the ORACLE database to the desired scale and
precision.
Refer to the QAD EA on ORACLE Installation and Conversion Guide for more details.
P a g e | 45
8.5 Substrings
Substrings are the cause of many "record not found" errors. Using the TRIM function
can resolve this issue. Substrings are the cause of many "record not found" errors. If a
value does not meet the length passed to the SUBSTRING function, ORACLE fills it
with blanks. If this blank-padded value is compared to another value that is not blankpadded, the comparison fails.
To resolve this issue, you can use the TRIM function. See STD-0012.
find first pt_mstr where pt_domain = global_domain and
pt_prt = TRIM(SUBSTRING(part,10,8)) no-lock no-error.
P a g e | 46
Example:
P a g e | 47
P a g e | 48
Customer may already have triggers and/or other functionality which could conflict with
ICT.
All the detail documentation to QAD ICT Toolkit is published on qad web pages
http://www.qad.com/documentlibrary/
QAD ICT training and certification is required.
Restrictions:
This section describes restrictionsactions that cannot be set up in QAD ICT. Please note that
these restrictions result from Progress and .NetUI limitationsnot QAD ICT itself.
Supported in
.NET UI
Supported in
CIM
On Entry on Field
No
No
On Leave on Field
No
No
On Go on Field
No
No
On Entry on Frame
Yes
No
On Go
Yes
No
GetFieldValue()
Yes
No
SetFieldValue()
Yes
No
GlobalValue()
Yes
No
Validation
Yes
No
Default Value
Yes
No
Yes
No
Trigger
P a g e | 49
CIM loading
o black art, linked to old technology only, bug sensitive
Invasive code changes
o Break ability to implement support patches and lock customers into releases
QAD EE customizations training is required for QAD and Non-QAD developer as the first
step in EF customizations.
P a g e | 50
Performance tools are designed to identify common performance issues encountered by the
performance team on customer production systems and take less than one hour to run.
For more information access http://qdn.qad.com/display/RF/Performance+Developer+SelfService.
P a g e | 51