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 thru 5 define the variables used by this function.
  • Line 7 declares the DIALOG block with UNBUFFERED attribute.
  • Lines 9 thru 54 declare the INPUT sub-dialog, to control the fields containing order header information such as customer, order date, etc.
  • Lines 56 thru 144 declare the INPUT ARRAY sub-dialog to control the current order item list.
  • Lines 146 thru 151 define the BEFORE DIALOG interaction block executing code before giving the control to the user. Here we fetch order records and we disable the stock_num field of the items array: This field is only enabled when creating a new order item row.
  • Lines 153 thru 154 implement an action handler for the "about" action, to show an application information dialog box.
  • Lines 156 thru 157 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 the DIALOG.getFieldTouched function.
  • Lines 159 thru 157 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 the INPUT sub-dialog which bind program variables to form fields.
  • Line 2 defines INPUT attributes WITHOUT DEFAULTS, to use the order_rec program variable values as defaults and defines the name of the sub-dialog with the NAME attribute.
  • Lines 4 thru 6 declare the action handler for the "find" action. We save the modified data if needed, by calling the order_update function. If the function fails to update the SQL row, we force the focus to stay in the current field with NEXT 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 the order_update function, to update the database row if needed.
  • Line 6 calls the query function order_query.
  • Line 10 calls the order_insert function, to create a new row in the database table.
  • Lines 15 thru 16 implement the "save" action to write changes done in the order header, into the database row.
  • Lines 18 thru 19 check the value entered in the order date field with the ON CHANGE trigger. If the value is not correct, we force the focus to stay in the current field. We use the order_check function to verify the field values.
  • Lines 21 thru 28 implement the zoom1 action, that can be fired with a click of the BUTTONEDIT button of the cust_name form field. We call the select_customer function from the custlist 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 the DIALOG.setFieldTouched method, as if the user had entered the values by hand.
  • Lines 30 thru 31: 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 thru 44 implement the action handlers to navidate in the orders list. We call the order_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 the INPUT ARRAY sub-dialog, binding the orditems dynamic array to the sa_items screen array of the current form.
  • Lines 2 defines the dialog attribute WITHOUT DEFAULTS to use the current values in the dynamic array, INSERT ROW = FALSE to deny the insert action (we keep the append action), and AUTO APPEND = FALSE to avoid new row creation when the user clicks after the last row in the items list: The user must explicitly fire the append 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 as AFTER ROW, with the op flag usage are described in this chapter of the tutorial.
  • Line 15 enables the stock_num field with DIALOG.setFieldActive, when a new row is created. Otherwise, when browsing the list, the stock_num field is always disabled. This is done in the BEFORE ROW control block in line 9.
  • Lines 34 thru 45 implement the "save" action, to write item line modifications into the database.
  • Line 23: The item_delete function implements the removal of the current item row from the database.
  • Line 50: The item_insert function implements the creation of a new item row in the database.
  • Line 54: The item_update function implements the modification of an item row in the database.
  • Lines 58 thru 68 implement the zoom2 action, that can be fired with a click of the BUTTONEDIT button of the stock_num form field. We call the select_stock_item function from the stocklist module to let the user choose a stock item. If a positive item number is returned, we fetch stock item information with the get_stock_info function, and we set the number to the variables corresponding to the form field and we set the touched flag by using the DIALOG.setFieldTouched method, as if the user had entered the value by hand.
  • Lines 70 thru 78: If the item number is changed, we fetch stock item information for this item, and re-compute the totals by calling the items_line_total function.
  • Lines 80 thru 87: When changing the quantity field, the code verifies that the quantity is a positive number, and we automatically re-compute the total of the line.