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 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).