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.
The function uses the opflag variable to determine the state of the operations
for items:
N- no current operationT- temporary row was createdI- row insertion was done in the listM- 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
002thru006define the variables used by this function. - Lines
008thru143define aDIALOGinstruction implementing the controller of the form.- Lines
010thru053implement theINPUT BY NAMEsub-dialog, controlling theorder_recrecord input. All actions triggers declared inside theINPUT BY NAMEsub-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
013thru015implement thefindON ACTIONtrigger, to execute a Query By Example with theorder_query()function. Before calling the query function, we must validate and save current modifications in the order record with theorder_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
017thru021implement thenewON ACTIONtrigger, to create a new order record. Before calling the new function, we must validate and save current modifications in the order record with theorder_update()function. - Lines
023thru024implement thesaveON ACTIONtrigger, to validate and save current modifications in the order record with theorder_update()function. - Lines
026thru027declare theON CHANGEtrigger for thestore_numfield, to check if the number is a valid store identifier with theorder_check_store_num()function. If the function returnsFALSE, we execute aNEXT FIELDto stay in the field. - Lines
029thru035implement thezoom1ON ACTIONtrigger for thef01field, to open a typical "zoom" window with thedisplay_custlist()function. If the user selects a customer from the list, we mark the field as touched with theDIALOG.setFieldTouched()method. This simulates a real user input. - Lines
037thru038implement theAFTER INPUTtrigger, to validate and save current modifications with theorder_update()function when the focus is lost by the order header sub-dialog. - Lines
040thru051implement theON ACTIONtriggers for the four navigation actions to move in the order list with theorder_move()function. Before calling the query function, we must validate and save current modifications with theorder_update()function.
- Lines
- Lines
055thru130implement theINPUT ARRAYsub-dialog, controlling thearr_itemsarray input. All actions triggers declared inside theINPUT ARRAYsub-dialog will only be activated if the focus is in this sub-dialog. The sub-dialog uses theopflagtechnique to implement SQL instructions inside the dialog code and update the database on the fly.- Lines
058thru059implement theBEFORE INPUTtrigger, 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
061thru064implement theBEFORE ROWtrigger, initialize theopflagoperation flag to "N" (no current operation), save the current row index incurr_pavariable and disable thestock_numfield (only editable when creating a new line). - Lines
066thru069implement theBEFORE INSERTtrigger, to set theopflagto "T" (meaning a temporary row was created). A row will be fully validated and ready for SQLINSERTwhen we reach theAFTER INSERTtrigger, there we will setopflagto "I". The code initializes the quantity to 1 and enables thestock_numfield for user input. - Lines
071thru072implement theAFTER INSERTtrigger, to set theopflagto "I" (row insertion done in list). Data is now ready to be inserted in the database. This is done in theAFTER ROWtrigger, according toopflag. - Lines
074thru079implement theBEFORE DELETEtrigger. We execute the SQLDELETEonly ifopflagequals "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
081thru082implement theAFTER DELETEtrigger, to reset theopflagto "N" (no current operation). This is done to clean the flag after deleting a new inserted row, when data validation or SQL insert failed inAFTER ROW. In that case,opflagequals "I" in the nextAFTER DELETE/AFTER ROWsequence and would invoke validation rules again. - Lines
084thru085implement theON ROW CHANGEtrigger, to set theopflagto "M" (row was modified), but only if we are not currently doing a row insertion: Row insertion can have failed inAFTER ROWandAFTER INSERTwould not be executed again, butON ROW CHANGEwould. The real SQLUPDATEwill be done later inAFTER ROW. - Lines
087thru099implement theAFTER ROWtrigger, executingINSERTorUPDATESQL instructions according to theopflagflag. If the SQL statement fails (for example, because a constraint is violated), we set the focus back to the current field withNEXT FIELD CURRENTand keep theopflagvalue as is. If the SQL instruction succeeds,opflagwill be reset to "N" in the nextBEFORE ROW. - Lines
101thru103implement thezoom2ON ACTIONtrigger for thef08field, to open a typical "zoom" window with thedisplay_stocklist()function. If the user selects a stock from the list, we mark the field as touched with theDIALOG.setFieldTouched()method. This simulates a real user input. - Lines
112thru120declare theON CHANGEtrigger for thestock_numfield, to check if the number is a valid stock identifier with theget_stock_info()lookup function. If the function returnsFALSE, we execute aNEXT FIELDto stay in the field, otherwise we recalculate the line total withitems_line_total(). - Lines
122thru128declare theON CHANGEtrigger for thequantityfield, to check if the value is greater than zero. If the value is invalid, we execute aNEXT FIELDto stay in the field, otherwise we recalculate the line total withitems_line_total().
- Lines
- Lines
132thru134implement theBEFORE DIALOGtrigger, to fill the list of orders with an initial result set. - Lines
137thru138implement theaboutON ACTIONtrigger, to display a message box with the version of the program. - Lines
140thru141implement thequitON ACTIONtrigger, to leave the dialog (and quit the program).
- Lines