Example: orders.4gl (Function orditems_dialog)
This is the most important function of the program. It implements the multiple dialog instruction to control order and items input simultaneously.
In the INPUT ARRAY
, the function uses the op
flag pattern to
determine the state of the operations for items
rows. Go to Tutorial Chapter 9: Record list with direct update for more details.
Function orditems_dialog
(orders.4gl)
1 PRIVATE FUNCTION orditems_dialog() RETURNS ()
2 DEFINE num INTEGER,
3 name LIKE customer.cust_name,
4 op CHAR(1),
5 x INTEGER
6
7 DIALOG ATTRIBUTES(UNBUFFERED)
8
9 INPUT BY NAME order_rec.*, order_total
10 ATTRIBUTES(WITHOUT DEFAULTS, NAME="order")
11
.. ...
53
54 END INPUT
55
56 INPUT ARRAY orditems FROM sa_items.*
57 ATTRIBUTES (WITHOUT DEFAULTS, INSERT ROW = FALSE, AUTO APPEND = FALSE)
58
.. ...
143
144 END INPUT
145
146 BEFORE DIALOG
147 IF NOT orders_fetch_nums("1=1") THEN
148 MESSAGE msg01
149 CALL order_query()
150 END IF
151 CALL DIALOG.setFieldActive("stock_num", FALSE)
152
153 ON ACTION about
154 CALL comutils.mbox_ok(title1,msg18)
155
156 ON ACTION quit
157 EXIT DIALOG
158
159 ON ACTION close
160 EXIT DIALOG
161
162 END DIALOG
163
164 END FUNCTION
Note:
- Lines
2
thru5
define the variables used by this function. - Line
7
declares theDIALOG
block withUNBUFFERED
attribute. - Lines
9
thru54
declare theINPUT
sub-dialog, to control the fields containing order header information such as customer, order date, etc. - Lines
56
thru144
declare theINPUT ARRAY
sub-dialog to control the current order item list. - Lines
146
thru151
define theBEFORE DIALOG
interaction block executing code before giving the control to the user. Here we fetch order records and we disable thestock_num
field of the items array: This field is only enabled when creating a new order item row. - Lines
153
thru154
implement an action handler for the"about"
action, to show an application information dialog box. - Lines
156
thru157
implement the action to leave the dialog and stop the program. This code could show a warning, if the form data has been edited, but not saved, by checking theDIALOG.getFieldTouched
function. - Lines
159
thru157
implement the "close" action handler, which is triggered when the user clocks on the X button of the window.
The
INPUT
sub-dialog: 1 INPUT BY NAME order_rec.*, order_total
2 ATTRIBUTES(WITHOUT DEFAULTS, NAME="order")
3
4 ON ACTION find
5 IF NOT order_update(DIALOG) THEN NEXT FIELD CURRENT END IF
6 CALL order_query()
7
8 ON ACTION new
9 IF NOT order_update(DIALOG) THEN NEXT FIELD CURRENT END IF
10 IF NOT order_insert() THEN
11 CALL mbox_ok(title1,msg20)
12 EXIT PROGRAM
13 END IF
14
15 ON ACTION save
16 IF NOT order_update(DIALOG) THEN NEXT FIELD CURRENT END IF
17
18 ON CHANGE order_date
19 IF NOT order_check(DIALOG) THEN NEXT FIELD CURRENT END IF
20
21 ON ACTION zoom1
22 CALL custlist.select_customer() RETURNING num, name
23 IF num > 0 THEN
24 LET order_rec.cust_num = num
25 CALL DIALOG.setFieldTouched("cust_num", TRUE)
26 LET order_rec.cust_name = name
27 CALL DIALOG.setFieldTouched("cust_name", TRUE)
28 END IF
29
30 AFTER INPUT
31 IF NOT order_update(DIALOG) THEN NEXT FIELD CURRENT END IF
32
33 ON ACTION first
34 IF NOT order_update(DIALOG) THEN NEXT FIELD CURRENT END IF
35 CALL order_move(move_first)
36 ON ACTION previous
37 IF NOT order_update(DIALOG) THEN NEXT FIELD CURRENT END IF
38 CALL order_move(move_prev)
39 ON ACTION next
40 IF NOT order_update(DIALOG) THEN NEXT FIELD CURRENT END IF
41 CALL order_move(move_next)
42 ON ACTION last
43 IF NOT order_update(DIALOG) THEN NEXT FIELD CURRENT END IF
44 CALL order_move(move_last)
45
46 END INPUT
Note:
- Line
1
declares theINPUT
sub-dialog which bind program variables to form fields. - Line
2
definesINPUT
attributesWITHOUT DEFAULTS
, to use theorder_rec
program variable values as defaults and defines the name of the sub-dialog with theNAME
attribute. - Lines
4
thru6
declare the action handler for the"find"
action. We save the modified data if needed, by calling theorder_update
function. If the function fails to update the SQL row, we force the focus to stay in the current field withNEXT FIELD CURRENT
, and any subsequent intructions in this block are skipped. Note that this automatic update is done for most of the other actions. - Line
5
calls theorder_update
function, to update the database row if needed. - Line
6
calls the query functionorder_query
. - Line
10
calls theorder_insert
function, to create a new row in the database table. - Lines
15
thru16
implement the "save" action to write changes done in the order header, into the database row. - Lines
18
thru19
check the value entered in the order date field with theON CHANGE
trigger. If the value is not correct, we force the focus to stay in the current field. We use theorder_check
function to verify the field values. - Lines
21
thru28
implement thezoom1
action, that can be fired with a click of theBUTTONEDIT
button of thecust_name
form field. We call theselect_customer
function from thecustlist
module to let the user choose a customer. If a positive customer number is returned, we set the number and name to the variables corresponding to the form fields and we set the touched flag for these fields by using theDIALOG.setFieldTouched
method, as if the user had entered the values by hand. - Lines
30
thru31
: When we leave the order header fields, we automatically save the changes to the database table if needed. In case of error, the focus is forced to stay in the current field. - Lines
33
thru44
implement the action handlers to navidate in the orders list. We call theorder_move
function to reach the new order row.
The
INPUT ARRAY
sub-dialog: 1 INPUT ARRAY orditems FROM sa_items.*
2 ATTRIBUTES (WITHOUT DEFAULTS, INSERT ROW = FALSE, AUTO APPEND = FALSE)
3
4 BEFORE INPUT
5 MESSAGE msg19
6
7 BEFORE ROW
8 LET op = "N"
9 CALL DIALOG.setFieldActive("stock_num", FALSE)
10
11 BEFORE INSERT
12 LET op = "T"
13 LET x = DIALOG.getCurrentRow("sa_items")
14 LET orditems[x].quantity = 1
15 CALL DIALOG.setFieldActive("stock_num", TRUE)
16
17 AFTER INSERT
18 LET op = "I"
19
20 BEFORE DELETE
21 IF op="N" THEN
22 LET x = DIALOG.getCurrentRow("sa_items")
23 IF NOT item_delete(x) THEN
24 CANCEL DELETE
25 END IF
26 END IF
27
28 AFTER DELETE
29 LET op="N"
30
31 ON ROW CHANGE
32 IF op != "I" THEN LET op = "M" END IF
33
34 ON ACTION save
35 IF DIALOG.getFieldTouched("items.*") THEN
36 IF DIALOG.validate("sa_items.*") < 0 THEN CONTINUE DIALOG END IF
37 LET x = DIALOG.getCurrentRow("sa_items")
38 IF op == "I" THEN
39 IF NOT item_insert(x) THEN NEXT FIELD CURRENT END IF
40 ELSE
41 IF NOT item_update(x) THEN NEXT FIELD CURRENT END IF
42 END IF
43 CALL items_line_total(x)
44 END IF
45 LET op="N"
46
47 AFTER ROW
48 LET x = DIALOG.getCurrentRow("sa_items")
49 IF op == "I" THEN
50 IF NOT item_insert(x) THEN NEXT FIELD CURRENT END IF
51 CALL items_line_total(x)
52 END IF
53 IF op == "M" THEN
54 IF NOT item_update(x) THEN NEXT FIELD CURRENT END IF
55 CALL items_line_total(x)
56 END IF
57
58 ON ACTION zoom2
59 LET num = stocklist.select_stock_item()
60 LET x = DIALOG.getCurrentRow("sa_items")
61 IF num > 0 THEN
62 IF NOT get_stock_info(x,num) THEN
63 LET orditems[x].stock_num = NULL
64 ELSE
65 LET orditems[x].stock_num = num
66 END IF
67 CALL DIALOG.setFieldTouched("stock_num", TRUE)
68 END IF
69
70 ON CHANGE stock_num
71 LET x = DIALOG.getCurrentRow("sa_items")
72 IF NOT get_stock_info(x, orditems[x].stock_num) THEN
73 LET orditems[x].stock_num = NULL
74 CALL comutils.mbox_ok(title2,msg07)
75 NEXT FIELD stock_num
76 ELSE
77 CALL items_line_total(x)
78 END IF
79
80 ON CHANGE quantity
81 LET x = DIALOG.getCurrentRow("sa_items")
82 IF orditems[x].quantity <= 0 THEN
83 CALL comutils.mbox_ok(title2,msg13)
84 NEXT FIELD quantity
85 ELSE
86 CALL items_line_total(x)
87 END IF
88
89 END INPUT
Note:
- Line
1
defines theINPUT ARRAY
sub-dialog, binding theorditems
dynamic array to the sa_items screen array of the current form. - Lines
2
defines the dialog attributeWITHOUT DEFAULTS
to use the current values in the dynamic array,INSERT ROW = FALSE
to deny theinsert
action (we keep theappend
action), andAUTO APPEND = FALSE
to avoid new row creation when the user clicks after the last row in the items list: The user must explicitly fire theappend
action to create a new order item row. - This
INPUT ARRAY
uses the programming pattern discussed in Tutorial Chapter 9: Record list with direct update. Control blocks such asAFTER ROW
, with the op flag usage are described in this chapter of the tutorial. - Line
15
enables thestock_num
field withDIALOG.setFieldActive
, when a new row is created. Otherwise, when browsing the list, thestock_num
field is always disabled. This is done in theBEFORE ROW
control block in line9
. - Lines
34
thru45
implement the "save" action, to write item line modifications into the database. - Line
23
: Theitem_delete
function implements the removal of the current item row from the database. - Line
50
: Theitem_insert
function implements the creation of a new item row in the database. - Line
54
: Theitem_update
function implements the modification of an item row in the database. - Lines
58
thru68
implement thezoom2
action, that can be fired with a click of theBUTTONEDIT
button of thestock_num
form field. We call theselect_stock_item
function from thestocklist
module to let the user choose a stock item. If a positive item number is returned, we fetch stock item information with theget_stock_info
function, and we set the number to the variables corresponding to the form field and we set the touched flag by using theDIALOG.setFieldTouched
method, as if the user had entered the value by hand. - Lines
70
thru78
: If the item number is changed, we fetch stock item information for this item, and re-compute the totals by calling theitems_line_total
function. - Lines
80
thru87
: When changing thequantity
field, the code verifies that the quantity is a positive number, and we automatically re-compute the total of the line.