This is the most important function of the program. It implements the multiple dialog
instruction to control order and items input simultaneously.
The function uses the opflag variable to determine the state of the operations
for items:
- N - no current operation
- T - temporary row was created
- I - row insertion was done in the list
- M - row in the list was modified
Function orditems_dialog (orders.4gl)
001 FUNCTION orditems_dialog()
002 DEFINE query_ok SMALLINT,
003 id INTEGER,
004 name LIKE customer.store_name,
005 opflag CHAR(1),
006 curr_pa INTEGER
007
008 DIALOG ATTRIBUTES(UNBUFFERED)
009
010 INPUT BY NAME order_rec.*, order_total
011 ATTRIBUTES(WITHOUT DEFAULTS, NAME="order")
012
013 ON ACTION find
014 IF NOT order_update(DIALOG) THEN NEXT FIELD CURRENT END IF
015 CALL order_query()
016
017 ON ACTION new
018 IF NOT order_update(DIALOG) THEN NEXT FIELD CURRENT END IF
019 IF NOT order_new() THEN
020 EXIT PROGRAM
021 END IF
022
023 ON ACTION save
024 IF NOT order_update(DIALOG) THEN NEXT FIELD CURRENT END IF
025
026 ON CHANGE store_num
027 IF NOT order_check_store_num() THEN NEXT FIELD CURRENT END IF
028
029 ON ACTION zoom1
030 CALL display_custlist() RETURNING id, name
031 IF id > 0 THEN
032 LET order_rec.store_num = id
033 LET order_rec.store_name = name
034 CALL DIALOG.setFieldTouched("store_num", TRUE)
035 END IF
036
037 AFTER INPUT
038 IF NOT order_update(DIALOG) THEN NEXT FIELD CURRENT END IF
039
040 ON ACTION first
041 IF NOT order_update(DIALOG) THEN NEXT FIELD CURRENT END IF
042 CALL order_move(move_first)
043 ON ACTION previous
044 IF NOT order_update(DIALOG) THEN NEXT FIELD CURRENT END IF
045 CALL order_move(move_prev)
046 ON ACTION next
047 IF NOT order_update(DIALOG) THEN NEXT FIELD CURRENT END IF
048 CALL order_move(move_next)
049 ON ACTION last
050 IF NOT order_update(DIALOG) THEN NEXT FIELD CURRENT END IF
051 CALL order_move(move_last)
052
053 END INPUT
054
055 INPUT ARRAY arr_items FROM sa_items.*
056 ATTRIBUTES (WITHOUT DEFAULTS, INSERT ROW =FALSE)
057
058 BEFORE INPUT
059 MESSAGE msg19
060
061 BEFORE ROW
062 LET opflag = "N"
063 LET curr_pa = DIALOG.getCurrentRow("sa_items")
064 CALL DIALOG.setFieldActive("stock_num", FALSE)
065
066 BEFORE INSERT
067 LET opflag = "T"
068 LET arr_items[curr_pa].quantity = 1
069 CALL DIALOG.setFieldActive("stock_num", TRUE)
070
071 AFTER INSERT
072 LET opflag = "I"
073
074 BEFORE DELETE
075 IF opflag="N" THEN
076 IF NOT item_delete(curr_pa) THEN
077 CANCEL DELETE
078 END IF
079 END IF
080
081 AFTER DELETE
082 LET opflag="N"
083
084 ON ROW CHANGE
085 IF opflag != "I" THEN LET opflag = "M" END IF
086
087 AFTER ROW
088 IF opflag == "I" THEN
089 IF NOT item_insert(curr_pa) THEN
090 NEXT FIELD CURRENT
091 END IF
092 CALL items_line_total(curr_pa)
093 END IF
094 IF opflag == "M" THEN
095 IF NOT item_update(curr_pa) THEN
096 NEXT FIELD CURRENT
097 END IF
098 CALL items_line_total(curr_pa)
099 END IF
100
101 ON ACTION zoom2
102 LET id = display_stocklist()
103 IF id > 0 THEN
104 IF NOT get_stock_info(curr_pa,id) THEN
105 LET arr_items[curr_pa].stock_num = NULL
106 ELSE
107 LET arr_items[curr_pa].stock_num = id
108 END IF
109 CALL DIALOG.setFieldTouched("stock_num", TRUE)
110 END IF
111
112 ON CHANGE stock_num
113 IF NOT get_stock_info(curr_pa,
114 arr_items[curr_pa].stock_num) THEN
115 LET arr_items[curr_pa].stock_num = NULL
116 CALL __mbox_ok(title2,msg07,"stop")
117 NEXT FIELD stock_num
118 ELSE
119 CALL items_line_total(curr_pa)
120 END IF
121
122 ON CHANGE quantity
123 IF arr_items[curr_pa].quantity <= 0 THEN
124 CALL __mbox_ok(title2,msg13,"stop")
125 NEXT FIELD quantity
126 ELSE
127 CALL items_line_total(curr_pa)
128 END IF
129
130 END INPUT
131
132 BEFORE DIALOG
133 IF NOT order_select("1=1") THEN
134 CALL order_query()
135 END IF
136
137 ON ACTION about
138 CALL __mbox_ok(title1,msg18,"information")
139
140 ON ACTION quit
141 EXIT DIALOG
142
143 END DIALOG
144
145 END FUNCTION
Note:
- Lines 002 thru 006 define the variables used by this function.
- Lines 008 thru 143 define a DIALOG instruction implementing the
controller of the form.
- Lines 010 thru 053 implement the INPUT BY
NAME sub-dialog, controlling the order_rec
record input. All actions triggers declared inside the INPUT BY
NAME sub-dialog will only be activated if the focus is in
this sub-dialog. Data validation will occur when focus is lost by this
sub-dialog, or when the user presses the Save button.
- Lines 013 thru 015 implement the
find
ON ACTION trigger, to execute a Query By
Example with the order_query() function. Before
calling the query function, we must validate and save current
modifications in the order record with the
order_update() function. If the
validation/save fails, the cursor remains in the current field
(when the user clicks an action view, such as a Toolbar icon,
the focus does not change.)
- Lines 017 thru 021 implement the
new
ON ACTION trigger, to create a new order
record. Before calling the new function, we must validate and
save current modifications in the order record with the
order_update() function.
- Lines 023 thru 024 implement the
save ON ACTION trigger, to
validate and save current modifications in the order record with
the order_update() function.
- Lines 026 thru 027 declare the ON
CHANGE trigger for the store_num
field, to check if the number is a valid store identifier with
the order_check_store_num() function. If the
function returns FALSE, we execute a
NEXT FIELD to stay in the field.
- Lines 029 thru 035 implement the
zoom1
ON ACTION trigger for the f01
field, to open a typical "zoom" window with the
display_custlist() function. If the user
selects a customer from the list, we mark the field as touched
with the DIALOG.setFieldTouched() method. This
simulates a real user input.
- Lines 037 thru 038 implement the
AFTER INPUT trigger, to validate and save
current modifications with the order_update()
function when the focus is lost by the order header
sub-dialog.
- Lines 040 thru 051 implement the
ON ACTION triggers for the four navigation
actions to move in the order list with the
order_move() function. Before calling the
query function, we must validate and save current modifications
with the order_update() function.
- Lines 055 thru 130 implement the INPUT
ARRAY sub-dialog, controlling the
arr_items array input. All actions triggers
declared inside the INPUT ARRAY sub-dialog will only be
activated if the focus is in this sub-dialog. The sub-dialog uses the
opflag technique to implement SQL instructions
inside the dialog code and update the database on the fly.
- Lines 058 thru 059 implement the
BEFORE INPUT trigger, to display
information message to the user, indicating that item row data
will be validated and saved in the database when the user moves
to another row or when the focus is lost by the item list.
- Lines 061 thru 064 implement the
BEFORE ROW trigger, initialize the
opflag operation flag to "N" (no current
operation), save the current row index in
curr_pa variable and disable the
stock_num field (only editable when
creating a new line).
- Lines 066 thru 069 implement the
BEFORE INSERT trigger, to set the
opflag to "T" (meaning a temporary row was
created). A row will be fully validated and ready for SQL
INSERT when we reach the AFTER
INSERT trigger, there we will set
opflag to "I". The code initializes the
quantity to 1 and enables the stock_num field
for user input.
- Lines 071 thru 072 implement the
AFTER INSERT trigger, to set the
opflag to "I" (row insertion done in list).
Data is now ready to be inserted in the database. This is done
in the AFTER ROW trigger, according to
opflag.
- Lines 074 thru 079 implement the
BEFORE DELETE trigger. We execute the SQL
DELETE only if opflag
equals "N", indicating that we are in a normal browse
mode (and not inserting a new temporary row, which can be
deleted from the list without any associated SQL
instruction).
- Lines 081 thru 082 implement the
AFTER DELETE trigger, to reset the
opflag to "N" (no current operation). This
is done to clean the flag after deleting a new inserted row,
when data validation or SQL insert failed in AFTER
ROW. In that case, opflag equals
"I" in the next AFTER DELETE / AFTER
ROW sequence and would invoke validation rules
again.
- Lines 084 thru 085 implement the
ON ROW CHANGE trigger, to set the
opflag to "M" (row was modified), but only
if we are not currently doing a row insertion: Row insertion can
have failed in AFTER ROW and AFTER
INSERT would not be executed again, but ON
ROW CHANGE would. The real SQL
UPDATE will be done later in AFTER
ROW.
- Lines 087 thru 099 implement the
AFTER ROW trigger, executing
INSERT or UPDATE SQL
instructions according to the opflag flag. If
the SQL statement fails (for example, because a constraint is
violated), we set the focus back to the current field with
NEXT FIELD CURRENT and keep the
opflag value as is. If the SQL instruction
succeeds, opflag will be reset to "N" in the
next BEFORE ROW.
- Lines 101 thru 103 implement the
zoom2
ON ACTION trigger for the f08
field, to open a typical "zoom" window with the
display_stocklist() function. If the user
selects a stock from the list, we mark the field as touched with
the DIALOG.setFieldTouched() method. This
simulates a real user input.
- Lines 112 thru 120 declare the ON
CHANGE trigger for the stock_num
field, to check if the number is a valid stock identifier with
the get_stock_info() lookup function. If the
function returns FALSE, we execute a
NEXT FIELD to stay in the field, otherwise
we recalculate the line total with
items_line_total().
- Lines 122 thru 128 declare the ON
CHANGE trigger for the quantity
field, to check if the value is greater than zero. If the value
is invalid, we execute a NEXT FIELD to stay in
the field, otherwise we recalculate the line total with
items_line_total().
- Lines 132 thru 134 implement the BEFORE
DIALOG trigger, to fill the list of orders with an initial
result set.
- Lines 137 thru 138 implement the
about
ON ACTION trigger, to display a message box with the
version of the program.
- Lines 140 thru 141 implement the
quit
ON ACTION trigger, to leave the dialog (and quit the
program).