Commit inicial: fuentes MBS ERP (Genero 6) + .gitignore + docs/SETUP_PC_MBS.md
This commit is contained in:
@@ -0,0 +1,169 @@
|
||||
# Property of Four Js*
|
||||
# (c) Copyright Four Js 1995, 2019. All Rights Reserved.
|
||||
# * Trademark of Four Js Development Tools Europe Ltd
|
||||
# in the United States and elsewhere
|
||||
#
|
||||
# Four Js and its suppliers do not warrant or guarantee that these
|
||||
# samples are accurate and suitable for your purposes. Their inclusion is
|
||||
# purely for information purposes only.
|
||||
|
||||
# Included in other demos to simplify compilation / link
|
||||
IMPORT FGL MsgBox
|
||||
|
||||
FUNCTION select_city(prefix)
|
||||
DEFINE prefix STRING
|
||||
DEFINE filter STRING
|
||||
DEFINE r SMALLINT
|
||||
DEFINE name, state STRING
|
||||
DEFINE cityarr DYNAMIC ARRAY OF RECORD
|
||||
city_id INTEGER,
|
||||
city_name VARCHAR(50),
|
||||
city_state CHAR(2)
|
||||
END RECORD
|
||||
|
||||
OPEN WINDOW w_city WITH FORM "CityList"
|
||||
|
||||
DIALOG ATTRIBUTES(UNBUFFERED, FIELD ORDER FORM)
|
||||
|
||||
INPUT BY NAME filter
|
||||
END INPUT
|
||||
|
||||
DISPLAY ARRAY cityarr TO sr.*
|
||||
END DISPLAY
|
||||
|
||||
BEFORE DIALOG
|
||||
IF LENGTH(prefix) < 3 THEN
|
||||
LET filter = prefix || "*"
|
||||
ELSE
|
||||
LET filter = "*"
|
||||
END IF
|
||||
CALL select_city_fill(DIALOG,filter,cityarr)
|
||||
CALL DIALOG.setActionActive("accept", DIALOG.getCurrentRow("sr")>0)
|
||||
|
||||
ON ACTION filter
|
||||
CALL select_city_fill(DIALOG,filter,cityarr)
|
||||
CALL DIALOG.setActionActive("accept", DIALOG.getCurrentRow("sr")>0)
|
||||
|
||||
ON ACTION accept
|
||||
LET name = cityarr[DIALOG.getCurrentRow("sr")].city_name
|
||||
LET state = cityarr[DIALOG.getCurrentRow("sr")].city_state
|
||||
LET r = 0
|
||||
EXIT DIALOG
|
||||
|
||||
ON ACTION cancel
|
||||
LET r = -1
|
||||
EXIT DIALOG
|
||||
|
||||
END DIALOG
|
||||
|
||||
CLOSE WINDOW w_city
|
||||
|
||||
RETURN r, name, state
|
||||
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION select_city_fill(d,filter, cityarr)
|
||||
DEFINE d ui.Dialog
|
||||
DEFINE filter STRING
|
||||
DEFINE cityarr DYNAMIC ARRAY OF RECORD
|
||||
city_id INTEGER,
|
||||
city_name VARCHAR(50),
|
||||
city_state CHAR(2)
|
||||
END RECORD
|
||||
DEFINE i INTEGER
|
||||
|
||||
LET i = 0
|
||||
CALL cityarr.clear()
|
||||
|
||||
&define ADD_CITY(name, state) \
|
||||
LET cityarr[i:=i+1].city_id = i \
|
||||
LET cityarr[i].city_name = name \
|
||||
LET cityarr[i].city_state = state
|
||||
|
||||
ADD_CITY("Austin", "TX")
|
||||
ADD_CITY("Boston", "MA")
|
||||
ADD_CITY("Chicago", "IL")
|
||||
ADD_CITY("Dallas", "TX")
|
||||
ADD_CITY("Denver", "CO")
|
||||
ADD_CITY("Detroit", "MI")
|
||||
ADD_CITY("Houston", "TX")
|
||||
ADD_CITY("Las Vegas", "NV")
|
||||
ADD_CITY("Los Angeles", "CA")
|
||||
ADD_CITY("Miami", "FL")
|
||||
ADD_CITY("New York", "NY")
|
||||
ADD_CITY("Philadelphia", "PA")
|
||||
ADD_CITY("Phoenix", "AZ")
|
||||
ADD_CITY("Portland", "ME")
|
||||
ADD_CITY("San Antonio", "TX")
|
||||
ADD_CITY("San Diego", "CA")
|
||||
ADD_CITY("San Francisco", "CA")
|
||||
ADD_CITY("Washington", "DC")
|
||||
|
||||
FOR i=1 TO cityarr.getLength()
|
||||
IF cityarr[i].city_name NOT MATCHES filter THEN
|
||||
CALL cityarr.deleteElement(i)
|
||||
LET i = i - 1
|
||||
END IF
|
||||
END FOR
|
||||
|
||||
CALL d.setCurrentRow("sr",1)
|
||||
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION combo_fill_states(cb)
|
||||
DEFINE cb ui.ComboBox
|
||||
|
||||
CALL cb.addItem("AK","Alaska")
|
||||
CALL cb.addItem("AL","Alabama")
|
||||
CALL cb.addItem("AR","Arkansas")
|
||||
CALL cb.addItem("AZ","Arizona")
|
||||
CALL cb.addItem("CA","California")
|
||||
CALL cb.addItem("CO","Colorado")
|
||||
CALL cb.addItem("CT","Connecticut")
|
||||
CALL cb.addItem("DC","D.C.")
|
||||
CALL cb.addItem("DE","Delaware")
|
||||
CALL cb.addItem("FL","Florida")
|
||||
CALL cb.addItem("GA","Georgia")
|
||||
CALL cb.addItem("HI","Hawaii")
|
||||
CALL cb.addItem("IA","Iowa")
|
||||
CALL cb.addItem("ID","Idaho")
|
||||
CALL cb.addItem("IL","Illinois")
|
||||
CALL cb.addItem("IN","Indiana")
|
||||
CALL cb.addItem("KS","Kansas")
|
||||
CALL cb.addItem("KY","Kentucky")
|
||||
CALL cb.addItem("LA","Louisiana")
|
||||
CALL cb.addItem("MA","Massachusetts")
|
||||
CALL cb.addItem("MD","Maryland")
|
||||
CALL cb.addItem("ME","Maine")
|
||||
CALL cb.addItem("MI","Michigan")
|
||||
CALL cb.addItem("MN","Minnesota")
|
||||
CALL cb.addItem("MO","Missouri")
|
||||
CALL cb.addItem("MS","Mississippi")
|
||||
CALL cb.addItem("MT","Montana")
|
||||
CALL cb.addItem("NC","North Carolina")
|
||||
CALL cb.addItem("ND","North Dakota")
|
||||
CALL cb.addItem("NE","Nebraska")
|
||||
CALL cb.addItem("NH","New Hampshire")
|
||||
CALL cb.addItem("NJ","New Jersey")
|
||||
CALL cb.addItem("NM","New Mexico")
|
||||
CALL cb.addItem("NV","Nevada")
|
||||
CALL cb.addItem("NY","New York")
|
||||
CALL cb.addItem("OH","Ohio")
|
||||
CALL cb.addItem("OK","Oklahoma")
|
||||
CALL cb.addItem("OR","Oregon")
|
||||
CALL cb.addItem("PA","Pennsylvania")
|
||||
CALL cb.addItem("PR","Puerto Rico")
|
||||
CALL cb.addItem("RI","Rhode Island")
|
||||
CALL cb.addItem("SC","South Carolina")
|
||||
CALL cb.addItem("SD","South Dakota")
|
||||
CALL cb.addItem("TN","Tennessee")
|
||||
CALL cb.addItem("TX","Texas")
|
||||
CALL cb.addItem("UT","Utah")
|
||||
CALL cb.addItem("VA","Virginia")
|
||||
CALL cb.addItem("VT","Vermont")
|
||||
CALL cb.addItem("WA","Washington")
|
||||
CALL cb.addItem("WI","Wisconsin")
|
||||
CALL cb.addItem("WV","West Virginia")
|
||||
CALL cb.addItem("WY","Wyoming")
|
||||
|
||||
END FUNCTION
|
||||
@@ -0,0 +1,43 @@
|
||||
# Property of Four Js*
|
||||
# (c) Copyright Four Js 1995, 2019. All Rights Reserved.
|
||||
# * Trademark of Four Js Development Tools Europe Ltd
|
||||
# in the United States and elsewhere
|
||||
#
|
||||
# Four Js and its suppliers do not warrant or guarantee that these
|
||||
# samples are accurate and suitable for your purposes. Their inclusion is
|
||||
# purely for information purposes only.
|
||||
|
||||
ACTION DEFAULTS
|
||||
ACTION filter (ACCELERATOR = CONTROL-ENTER, ACCELERATOR2 = CONTROL-RETURN)
|
||||
END
|
||||
|
||||
LAYOUT ( STYLE = "dialog4" )
|
||||
GRID
|
||||
{
|
||||
Filter: [f ]
|
||||
<t t1 >
|
||||
Id City State
|
||||
[id |name |st ]
|
||||
[id |name |st ]
|
||||
[id |name |st ]
|
||||
[id |name |st ]
|
||||
[id |name |st ]
|
||||
[id |name |st ]
|
||||
< >
|
||||
}
|
||||
END
|
||||
END
|
||||
|
||||
ATTRIBUTES
|
||||
BUTTONEDIT f = FORMONLY.filter TYPE CHAR, ACTION=filter, IMAGE="filter",
|
||||
COMMENT = "Presse Control-Enter to apply filter";
|
||||
TABLE t1 : COMMENT = "Double-click to select an item from the list";
|
||||
EDIT id = FORMONLY.city_id TYPE INTEGER;
|
||||
EDIT name = FORMONLY.city_name TYPE CHAR;
|
||||
EDIT st = FORMONLY.city_state TYPE CHAR;
|
||||
END
|
||||
|
||||
INSTRUCTIONS
|
||||
SCREEN RECORD sr (FORMONLY.city_id, FORMONLY.city_name, FORMONLY.city_state);
|
||||
END
|
||||
|
||||
@@ -0,0 +1,490 @@
|
||||
# Property of Four Js*
|
||||
# (c) Copyright Four Js 1995, 2019. All Rights Reserved.
|
||||
# * Trademark of Four Js Development Tools Europe Ltd
|
||||
# in the United States and elsewhere
|
||||
#
|
||||
# Four Js and its suppliers do not warrant or guarantee that these
|
||||
# samples are accurate and suitable for your purposes. Their inclusion is
|
||||
# purely for information purposes only.
|
||||
|
||||
IMPORT FGL CityList
|
||||
IMPORT FGL OrderInput
|
||||
IMPORT FGL MsgBox
|
||||
|
||||
GLOBALS "Globals.4gl"
|
||||
|
||||
DEFINE cust_curr INTEGER
|
||||
DEFINE cust, old_cust custrec
|
||||
DEFINE cust_state SMALLINT
|
||||
|
||||
CONSTANT STATE_NAVIGATE = 0
|
||||
CONSTANT STATE_MODIFY = 1
|
||||
CONSTANT STATE_APPEND = 2
|
||||
|
||||
CONSTANT otitle = "Customer"
|
||||
|
||||
MAIN
|
||||
DEFINE ordlist_lastrow INTEGER
|
||||
|
||||
OPTIONS INPUT WRAP
|
||||
|
||||
OPEN FORM f_cust FROM "CustOrders"
|
||||
DISPLAY FORM f_cust
|
||||
|
||||
DIALOG ATTRIBUTES(FIELD ORDER FORM, UNBUFFERED)
|
||||
|
||||
INPUT cust.* FROM custrec.* ATTRIBUTES(WITHOUT DEFAULTS)
|
||||
ON CHANGE custrec.cust_name
|
||||
IF NOT cust_check_name(DIALOG) THEN NEXT FIELD CURRENT END IF
|
||||
ON CHANGE custrec.cust_address
|
||||
IF NOT cust_check_address(DIALOG) THEN NEXT FIELD CURRENT END IF
|
||||
ON CHANGE custrec.cust_state
|
||||
INITIALIZE cust.cust_city, cust.cust_zipcode, cust.cust_address TO NULL
|
||||
END INPUT
|
||||
|
||||
DISPLAY ARRAY orders TO ordlist.*
|
||||
BEFORE ROW
|
||||
MESSAGE SFMT("Order record %1 / %2", DIALOG.getCurrentRow("ordlist"), DIALOG.getArrayLength("ordlist"))
|
||||
AFTER ROW
|
||||
LET ordlist_lastrow = arr_curr()
|
||||
END DISPLAY
|
||||
|
||||
BEFORE DIALOG
|
||||
CALL cust_loadlist(DIALOG,100)
|
||||
CALL cust_move_to(DIALOG, 1)
|
||||
|
||||
ON ACTION zoom_city
|
||||
CALL cust_zoom_city(DIALOG)
|
||||
|
||||
ON ACTION cust_query
|
||||
CALL cust_query(DIALOG)
|
||||
|
||||
ON ACTION cust_first
|
||||
CALL cust_move_to(DIALOG, 1)
|
||||
ON ACTION cust_previous
|
||||
CALL cust_move_to(DIALOG, cust_curr - 1)
|
||||
ON ACTION cust_next
|
||||
CALL cust_move_to(DIALOG, cust_curr + 1)
|
||||
ON ACTION cust_last
|
||||
CALL cust_move_to(DIALOG, customers.getLength())
|
||||
|
||||
ON ACTION dialogtouched
|
||||
CALL cust_touched(DIALOG)
|
||||
|
||||
ON ACTION cust_append
|
||||
IF NOT cust_append(DIALOG) THEN CONTINUE DIALOG END IF
|
||||
NEXT FIELD cust_name
|
||||
|
||||
ON ACTION cust_delete -- VALIDATE=NO
|
||||
CALL cust_delete(DIALOG)
|
||||
DISPLAY BY NAME cust.*
|
||||
|
||||
ON ACTION cust_undo -- VALIDATE=NO
|
||||
CALL cust_undo(DIALOG)
|
||||
DISPLAY BY NAME cust.*
|
||||
|
||||
ON ACTION cust_save
|
||||
IF NOT cust_save(DIALOG) THEN CONTINUE DIALOG END IF
|
||||
|
||||
ON ACTION ord_append
|
||||
CALL ord_append(DIALOG)
|
||||
|
||||
ON ACTION ord_modify
|
||||
CALL ord_modify(DIALOG)
|
||||
|
||||
ON ACTION ord_delete
|
||||
CALL ord_delete(DIALOG)
|
||||
|
||||
ON ACTION close
|
||||
IF cust_check_changed(DIALOG) THEN EXIT DIALOG END IF
|
||||
|
||||
END DIALOG
|
||||
END MAIN
|
||||
|
||||
FUNCTION cust_zoom_city(d)
|
||||
DEFINE d ui.Dialog
|
||||
DEFINE s INTEGER
|
||||
DEFINE name, state STRING
|
||||
CALL select_city(d.getFieldBuffer("cust_city")) RETURNING s, name, state
|
||||
IF s==0 THEN
|
||||
LET cust.cust_city = name
|
||||
LET cust.cust_state = state
|
||||
CALL d.setFieldTouched("cust_city",TRUE)
|
||||
CALL d.setFieldTouched("cust_state",TRUE)
|
||||
CALL cust_touched(d)
|
||||
END IF
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION cust_touched(d)
|
||||
DEFINE d ui.Dialog
|
||||
LET cust_state = STATE_MODIFY
|
||||
CALL cust_setup(d)
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION cust_check_changed(d)
|
||||
DEFINE d ui.Dialog
|
||||
DEFINE r INTEGER
|
||||
IF cust_modifying() THEN
|
||||
CALL cust_setup(d)
|
||||
LET r = mbox_ync(otitle,"Do you want to save your changes?","question")
|
||||
IF r == 1 THEN
|
||||
IF NOT cust_save(d) THEN LET r = 3 END IF
|
||||
END IF
|
||||
IF r == 2 THEN
|
||||
CALL cust_clean(d)
|
||||
END IF
|
||||
RETURN (r == 1 OR r == 2)
|
||||
END IF
|
||||
RETURN TRUE
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION cust_modifying()
|
||||
RETURN cust_state != STATE_NAVIGATE
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION cust_undo(d)
|
||||
DEFINE d ui.Dialog
|
||||
CASE cust_state
|
||||
WHEN STATE_MODIFY
|
||||
IF NOT mbox_yn(otitle,"Undo customer modification?","question") THEN RETURN END IF
|
||||
LET cust.* = old_cust.*
|
||||
CALL cust_clean(d)
|
||||
WHEN STATE_APPEND
|
||||
IF NOT mbox_yn(otitle,"Cancel customer creation?","question") THEN RETURN END IF
|
||||
CALL customers.deleteElement(customers.getLength())
|
||||
CALL cust_clean(d)
|
||||
CALL cust_move_to(d, customers.getLength())
|
||||
END CASE
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION cust_verify_address_format(value)
|
||||
DEFINE value STRING
|
||||
DEFINE num STRING, p,x INTEGER
|
||||
IF value IS NULL THEN RETURN TRUE END IF
|
||||
LET p = value.getIndexOf(",", 1)
|
||||
IF p == 0 THEN RETURN FALSE END IF
|
||||
LET num = value.subString(1, p)
|
||||
LET x = num
|
||||
IF x <= 0 THEN RETURN FALSE END IF
|
||||
IF value.getLength() <= p THEN RETURN FALSE END IF
|
||||
RETURN TRUE
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION cust_check_name(d)
|
||||
DEFINE d ui.Dialog
|
||||
IF LENGTH(cust.cust_name) = 0 THEN
|
||||
CALL mbox_ok(otitle,"You must specify a customer name.","stop")
|
||||
RETURN FALSE
|
||||
END IF
|
||||
IF cust.cust_name MATCHES "*[@#$%^&]*" THEN
|
||||
CALL mbox_ok(otitle,"The customer name containts invalid characters.","stop")
|
||||
RETURN FALSE
|
||||
END IF
|
||||
RETURN TRUE
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION cust_check_address(d)
|
||||
DEFINE d ui.Dialog
|
||||
IF NOT cust_verify_address_format(cust.cust_address) THEN
|
||||
CALL mbox_ok(otitle,"Address must hold a number followed by a comma and the street name.","stop")
|
||||
RETURN FALSE
|
||||
END IF
|
||||
RETURN TRUE
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION cust_check_city(d)
|
||||
DEFINE d ui.Dialog
|
||||
-- Inter-dependent field validation
|
||||
IF cust.cust_city IS NULL AND cust.cust_zipcode IS NOT NULL THEN
|
||||
CALL mbox_ok(otitle,"You must specify a city because zipcode is not null.","stop")
|
||||
RETURN FALSE
|
||||
END IF
|
||||
RETURN TRUE
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION cust_check_data(d)
|
||||
DEFINE d ui.Dialog
|
||||
IF d.validate("custrec.*") < 0 THEN
|
||||
RETURN "*"
|
||||
END IF
|
||||
IF NOT cust_check_name(d) THEN RETURN "custrec.cust_name" END IF
|
||||
IF NOT cust_check_city(d) THEN RETURN "custrec.cust_city" END IF
|
||||
IF NOT cust_check_address(d) THEN RETURN "custrec.cust_address" END IF
|
||||
RETURN NULL -- data is valid
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION cust_check_fields(d)
|
||||
DEFINE d ui.Dialog
|
||||
DEFINE fn STRING
|
||||
LET fn = cust_check_data(d)
|
||||
IF fn IS NULL THEN RETURN TRUE END IF
|
||||
IF fn != "*" THEN CALL d.nextField(fn) END IF
|
||||
RETURN FALSE
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION cust_save(d)
|
||||
DEFINE d ui.Dialog
|
||||
IF NOT cust_check_fields(d) THEN RETURN FALSE END IF
|
||||
IF cust_state == STATE_APPEND THEN
|
||||
-- SQL INSERT ...
|
||||
END IF
|
||||
IF cust_state == STATE_MODIFY THEN
|
||||
-- SQL UPDATE ...
|
||||
END IF
|
||||
LET customers[cust_curr].* = cust.*
|
||||
CALL cust_clean(d)
|
||||
CALL mbox_ok("Customers", "Changes saved to database", "information")
|
||||
CALL d.setFieldTouched("custrec.*",FALSE)
|
||||
CALL cust_setup(d)
|
||||
RETURN TRUE
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION cust_append(d)
|
||||
DEFINE d ui.Dialog
|
||||
IF NOT cust_autosave(d) THEN RETURN FALSE END IF
|
||||
INITIALIZE cust.* TO NULL
|
||||
CALL ord_loadlist(d,0,0)
|
||||
LET cust.cust_id = cust_curr + 101044
|
||||
LET cust_state = STATE_APPEND
|
||||
CALL customers.appendElement()
|
||||
LET cust_curr = customers.getLength()
|
||||
CALL cust_setup(d)
|
||||
RETURN TRUE
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION cust_delete(d)
|
||||
DEFINE d ui.Dialog
|
||||
IF cust_curr<= 0 THEN RETURN END IF
|
||||
IF cust_modifying() THEN
|
||||
CALL cust_setup(d)
|
||||
IF NOT mbox_yn(otitle,"Cancel current changes?","question") THEN RETURN END IF
|
||||
CALL cust_clean(d)
|
||||
ELSE
|
||||
IF NOT mbox_yn(otitle,"Delete current customer?","question") THEN RETURN END IF
|
||||
END IF
|
||||
-- SQL DELETE ...
|
||||
CALL customers.deleteElement(cust_curr)
|
||||
IF customers.getLength()==0 THEN
|
||||
CALL mbox_ok(otitle,"No more customers in list, switching to query mode","stop")
|
||||
CALL cust_query(d)
|
||||
RETURN
|
||||
END IF
|
||||
IF cust_curr>customers.getLength() THEN
|
||||
LET cust_curr=customers.getLength()
|
||||
END IF
|
||||
CALL cust_move_to(d,cust_curr)
|
||||
CALL cust_setup(d)
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION cust_query(d)
|
||||
DEFINE d ui.Dialog
|
||||
DEFINE sql STRING
|
||||
IF NOT cust_check_changed(d) THEN RETURN END IF
|
||||
DISPLAY "Enter customer search criterias" TO cust_info
|
||||
WHILE TRUE
|
||||
LET int_flag = FALSE
|
||||
CONSTRUCT BY NAME sql ON cust_id, cust_name, cust_state,
|
||||
cust_city, cust_zipcode, cust_address, cust_checked
|
||||
ATTRIBUTES( CANCEL=FALSE, ACCEPT=FALSE )
|
||||
ON ACTION close
|
||||
IF mbox_yn(otitle,"Quit application?","question") THEN EXIT PROGRAM END IF
|
||||
ON ACTION cust_fetch
|
||||
ACCEPT CONSTRUCT
|
||||
END CONSTRUCT
|
||||
IF NOT int_flag THEN
|
||||
-- Dummy query, should use database query instead.
|
||||
CALL cust_loadlist(d,LENGTH(sql))
|
||||
CALL cust_setup(d)
|
||||
CALL mbox_ok(otitle,SFMT("Found %1 customer records in database",customers.getLength()),"information")
|
||||
EXIT WHILE
|
||||
END IF
|
||||
END WHILE
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION cust_setup(d)
|
||||
DEFINE d ui.Dialog
|
||||
DEFINE has_rows, on_first, on_last, cust_ro INTEGER
|
||||
|
||||
LET has_rows = (cust_curr > 0 AND customers.getLength()>0)
|
||||
LET on_first = (cust_curr == 1)
|
||||
LET on_last = (cust_curr == customers.getLength())
|
||||
LET cust_ro = (has_rows AND cust_state == STATE_NAVIGATE)
|
||||
|
||||
CALL d.setActionActive("dialogtouched", cust_ro)
|
||||
CALL d.setActionActive("cust_undo", NOT cust_ro)
|
||||
CALL d.setActionActive("cust_save", NOT cust_ro)
|
||||
CALL d.setActionActive("cust_append", TRUE) -- always active
|
||||
CALL d.setActionActive("cust_delete", has_rows) -- can delete creation
|
||||
CALL d.setActionActive("cust_query", TRUE) -- always active
|
||||
CALL d.setActionActive("cust_first", NOT on_first)
|
||||
CALL d.setActionActive("cust_previous", NOT on_first)
|
||||
CALL d.setActionActive("cust_next", NOT on_last)
|
||||
CALL d.setActionActive("cust_last", NOT on_last)
|
||||
|
||||
CALL d.setActionActive("ord_append", cust_ro)
|
||||
CALL d.setActionActive("ord_modify", cust_ro)
|
||||
CALL d.setActionActive("ord_delete", cust_ro)
|
||||
|
||||
CASE cust_state
|
||||
WHEN STATE_APPEND
|
||||
DISPLAY "Creating a new customer record (save with ctrl-s)" TO cust_info
|
||||
WHEN STATE_MODIFY
|
||||
DISPLAY "Modifying current customer record (save with ctrl-s)" TO cust_info
|
||||
OTHERWISE
|
||||
DISPLAY SFMT("Customer record %1 / %2",cust_curr,customers.getLength()) TO cust_info
|
||||
END CASE
|
||||
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION cust_clean(d)
|
||||
DEFINE d ui.Dialog
|
||||
LET old_cust.* = cust.*
|
||||
LET cust_state = STATE_NAVIGATE
|
||||
CALL cust_setup(d)
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION cust_autosave(d)
|
||||
DEFINE d ui.Dialog
|
||||
IF cust_modifying() THEN
|
||||
IF NOT cust_save(d) THEN RETURN FALSE END IF
|
||||
END IF
|
||||
RETURN TRUE
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION cust_move_to(d,index)
|
||||
DEFINE d ui.Dialog
|
||||
DEFINE index INTEGER
|
||||
IF NOT cust_autosave(d) THEN RETURN END IF
|
||||
IF index > customers.getLength() THEN
|
||||
LET index = customers.getLength()
|
||||
END IF
|
||||
IF index < 1 THEN
|
||||
LET index = 1
|
||||
END IF
|
||||
LET cust.* = customers[index].*
|
||||
LET old_cust.* = cust.*
|
||||
LET cust_curr = index
|
||||
MESSAGE SFMT("Customer: %1/%2", cust_curr, customers.getLength())
|
||||
CALL ord_loadlist(d, customers[index].cust_id, (index mod 5) * 3)
|
||||
CALL d.setCurrentRow("ordlist",1)
|
||||
CALL cust_setup(d)
|
||||
END FUNCTION
|
||||
|
||||
-- Dummy function to fill the list of customers (normally, SELECT from database)
|
||||
FUNCTION cust_loadlist(d,m)
|
||||
DEFINE d ui.Dialog
|
||||
DEFINE m,i INTEGER
|
||||
CALL customers.clear()
|
||||
FOR i=1 TO m
|
||||
LET customers[i].cust_id = i
|
||||
LET customers[i].cust_name = "Name " || i
|
||||
LET customers[i].cust_state = "CA"
|
||||
LET customers[i].cust_city = "San Fransisco"
|
||||
LET customers[i].cust_zipcode = 94102
|
||||
LET customers[i].cust_address = "52, Marlon Street"
|
||||
LET customers[i].cust_checked = i mod 2
|
||||
END FOR
|
||||
IF customers.getLength()>0 THEN
|
||||
LET cust_curr = 1
|
||||
LET cust.* = customers[cust_curr].*
|
||||
CALL ord_loadlist(d, customers[cust_curr].cust_id, (cust_curr mod 5) * 3)
|
||||
ELSE
|
||||
LET cust_curr = 0
|
||||
INITIALIZE cust.* TO NULL
|
||||
CALL ord_loadlist(d, 0, 0)
|
||||
END IF
|
||||
CALL cust_clean(d)
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION ord_loadlist(d,mid,m)
|
||||
DEFINE d ui.Dialog
|
||||
DEFINE mid,m,i INTEGER
|
||||
CALL orders.clear()
|
||||
FOR i=1 TO m
|
||||
CALL orders.appendElement()
|
||||
LET orders[i].ord_id = mid * 100 + i
|
||||
LET orders[i].ord_crea = CURRENT YEAR TO SECOND
|
||||
LET orders[i].ord_amount = ( i mod 5 ) * 133.56
|
||||
END FOR
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION ord_append(d)
|
||||
DEFINE d ui.Dialog
|
||||
IF NOT cust_check_changed(d) THEN RETURN END IF
|
||||
CALL ord_input('A', cust.cust_id, cust.cust_name, 0)
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION ord_modify(d)
|
||||
DEFINE d ui.Dialog
|
||||
IF NOT cust_check_changed(d) THEN RETURN END IF
|
||||
IF d.getCurrentRow("ordlist")==0 THEN RETURN END IF
|
||||
CALL ord_input('M', cust.cust_id, cust.cust_name, orders[d.getCurrentRow("ordlist")].ord_id)
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION ord_delete(d)
|
||||
DEFINE d ui.Dialog
|
||||
IF NOT cust_check_changed(d) THEN RETURN END IF
|
||||
IF NOT mbox_yn(otitle,"Delete select order from database?","question") THEN
|
||||
RETURN
|
||||
END IF
|
||||
CALL d.deleteRow("ordlist",d.getCurrentRow("ordlist"))
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION combo_fill_states(cb)
|
||||
DEFINE cb ui.ComboBox
|
||||
|
||||
CALL cb.addItem("AK","Alaska")
|
||||
CALL cb.addItem("AL","Alabama")
|
||||
CALL cb.addItem("AR","Arkansas")
|
||||
CALL cb.addItem("AZ","Arizona")
|
||||
CALL cb.addItem("CA","California")
|
||||
CALL cb.addItem("CO","Colorado")
|
||||
CALL cb.addItem("CT","Connecticut")
|
||||
CALL cb.addItem("DC","D.C.")
|
||||
CALL cb.addItem("DE","Delaware")
|
||||
CALL cb.addItem("FL","Florida")
|
||||
CALL cb.addItem("GA","Georgia")
|
||||
CALL cb.addItem("HI","Hawaii")
|
||||
CALL cb.addItem("IA","Iowa")
|
||||
CALL cb.addItem("ID","Idaho")
|
||||
CALL cb.addItem("IL","Illinois")
|
||||
CALL cb.addItem("IN","Indiana")
|
||||
CALL cb.addItem("KS","Kansas")
|
||||
CALL cb.addItem("KY","Kentucky")
|
||||
CALL cb.addItem("LA","Louisiana")
|
||||
CALL cb.addItem("MA","Massachusetts")
|
||||
CALL cb.addItem("MD","Maryland")
|
||||
CALL cb.addItem("ME","Maine")
|
||||
CALL cb.addItem("MI","Michigan")
|
||||
CALL cb.addItem("MN","Minnesota")
|
||||
CALL cb.addItem("MO","Missouri")
|
||||
CALL cb.addItem("MS","Mississippi")
|
||||
CALL cb.addItem("MT","Montana")
|
||||
CALL cb.addItem("NC","North Carolina")
|
||||
CALL cb.addItem("ND","North Dakota")
|
||||
CALL cb.addItem("NE","Nebraska")
|
||||
CALL cb.addItem("NH","New Hampshire")
|
||||
CALL cb.addItem("NJ","New Jersey")
|
||||
CALL cb.addItem("NM","New Mexico")
|
||||
CALL cb.addItem("NV","Nevada")
|
||||
CALL cb.addItem("NY","New York")
|
||||
CALL cb.addItem("OH","Ohio")
|
||||
CALL cb.addItem("OK","Oklahoma")
|
||||
CALL cb.addItem("OR","Oregon")
|
||||
CALL cb.addItem("PA","Pennsylvania")
|
||||
CALL cb.addItem("PR","Puerto Rico")
|
||||
CALL cb.addItem("RI","Rhode Island")
|
||||
CALL cb.addItem("SC","South Carolina")
|
||||
CALL cb.addItem("SD","South Dakota")
|
||||
CALL cb.addItem("TN","Tennessee")
|
||||
CALL cb.addItem("TX","Texas")
|
||||
CALL cb.addItem("UT","Utah")
|
||||
CALL cb.addItem("VA","Virginia")
|
||||
CALL cb.addItem("VT","Vermont")
|
||||
CALL cb.addItem("WA","Washington")
|
||||
CALL cb.addItem("WI","Wisconsin")
|
||||
CALL cb.addItem("WV","West Virginia")
|
||||
CALL cb.addItem("WY","Wyoming")
|
||||
|
||||
END FUNCTION
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
# Property of Four Js*
|
||||
# (c) Copyright Four Js 1995, 2019. All Rights Reserved.
|
||||
# * Trademark of Four Js Development Tools Europe Ltd
|
||||
# in the United States and elsewhere
|
||||
#
|
||||
# Four Js and its suppliers do not warrant or guarantee that these
|
||||
# samples are accurate and suitable for your purposes. Their inclusion is
|
||||
# purely for information purposes only.
|
||||
|
||||
ACTION DEFAULTS
|
||||
ACTION cust_query ( IMAGE="find", COMMENT = "Query customers", ACCELERATOR = control-q )
|
||||
ACTION cust_append ( IMAGE="new", COMMENT = "Create new customer", ACCELERATOR = control-n )
|
||||
ACTION cust_undo ( IMAGE="refresh", COMMENT = "Undo last changes", ACCELERATOR = control-u, VALIDATE = NO )
|
||||
ACTION cust_save ( IMAGE="disk", COMMENT = "Save changes in database", ACCELERATOR = control-s )
|
||||
ACTION cust_delete ( IMAGE="eraser", COMMENT = "Delete current customer", ACCELERATOR = control-d, VALIDATE = NO )
|
||||
ACTION cust_fetch ( IMAGE="services", COMMENT = "Search records in database", ACCELERATOR = enter, ACCELERATOR2 = return )
|
||||
ACTION cust_first ( IMAGE = "gobegin", COMMENT = "Move to first customer in list")
|
||||
ACTION cust_previous ( IMAGE = "gorev", COMMENT = "Move to previous customer in list")
|
||||
ACTION cust_next ( IMAGE = "goforw", COMMENT = "Move to next customer in list")
|
||||
ACTION cust_last ( IMAGE = "goend", COMMENT = "Move to last customer in list")
|
||||
ACTION cust_print ( IMAGE="printer", COMMENT = "Print customer")
|
||||
ACTION ord_append ( IMAGE = "new", COMMENT="Add a new order")
|
||||
ACTION ord_modify ( IMAGE = "pen", COMMENT="Modify selected order")
|
||||
ACTION ord_delete ( IMAGE = "eraser", COMMENT="Delete selected order")
|
||||
END
|
||||
|
||||
TOPMENU
|
||||
GROUP cust ( TEXT="Customers" )
|
||||
COMMAND cust_query ( TEXT="Query" )
|
||||
COMMAND cust_append ( TEXT="New" )
|
||||
COMMAND cust_undo ( TEXT="Undo" )
|
||||
COMMAND cust_save ( TEXT="Save" )
|
||||
COMMAND cust_print ( TEXT="Print" )
|
||||
SEPARATOR
|
||||
COMMAND cust_fetch ( TEXT="Fetch" )
|
||||
COMMAND cancel
|
||||
SEPARATOR
|
||||
COMMAND cust_first ( TEXT="First" )
|
||||
COMMAND cust_previous ( TEXT="Previous" )
|
||||
COMMAND cust_next ( TEXT="Next" )
|
||||
COMMAND cust_last ( TEXT="Last" )
|
||||
SEPARATOR
|
||||
COMMAND close ( TEXT="Close" )
|
||||
END
|
||||
GROUP ord ( TEXT="Orders" )
|
||||
COMMAND ord_append ( TEXT="Append" )
|
||||
COMMAND ord_modify ( TEXT="Modify" )
|
||||
COMMAND ord_delete ( TEXT="Delete" )
|
||||
END
|
||||
GROUP help ( TEXT="Help" )
|
||||
COMMAND help
|
||||
SEPARATOR
|
||||
COMMAND about ( TEXT="About" )
|
||||
END
|
||||
END
|
||||
|
||||
LAYOUT ( STYLE="main2", TEXT = "Customers and Orders" )
|
||||
GRID
|
||||
{
|
||||
<g g1 >
|
||||
[f1 ][f2 ]
|
||||
State: [f3 ] [f4 ]
|
||||
City: [f5 ]
|
||||
Zipcode: [f6 ]
|
||||
Address: [f7 ]
|
||||
[ ]
|
||||
-------------------------------------------------------------------
|
||||
[tr |cq:cx:cf:cp:cn:cl:ca:cd:cu:cs]
|
||||
< >
|
||||
<g g2 >
|
||||
<t t1 >[oad]
|
||||
[c1 |c2 |c3 ][omo]
|
||||
[c1 |c2 |c3 ][ode]
|
||||
[c1 |c2 |c3 ]
|
||||
[c1 |c2 |c3 ]
|
||||
[c1 |c2 |c3 ]
|
||||
[c1 |c2 |c3 ]
|
||||
[c1 |c2 |c3 ]
|
||||
[c1 |c2 |c3 ]
|
||||
[c1 |c2 |c3 ]
|
||||
[c1 |c2 |c3 ]
|
||||
< >
|
||||
< >
|
||||
}
|
||||
END
|
||||
END
|
||||
|
||||
ATTRIBUTES
|
||||
|
||||
GROUP g1: TEXT = "Customer information";
|
||||
EDIT f1 = FORMONLY.cust_id TYPE INTEGER, NOT NULL, NOENTRY;
|
||||
EDIT f2 = FORMONLY.cust_name TYPE VARCHAR, NOT NULL, REQUIRED;
|
||||
COMBOBOX f3 = FORMONLY.cust_state TYPE VARCHAR, NOT NULL, REQUIRED,
|
||||
QUERYEDITABLE, DEFAULT="CA", INITIALIZER=combo_fill_states;
|
||||
CHECKBOX f4 = FORMONLY.cust_checked TYPE CHAR, TEXT="Checked";
|
||||
BUTTONEDIT f5 = FORMONLY.cust_city TYPE CHAR, NOT NULL, REQUIRED,
|
||||
ACTION=zoom_city, IMAGE="find";
|
||||
EDIT f6 = FORMONLY.cust_zipcode TYPE VARCHAR, NOT NULL, REQUIRED,
|
||||
COMMENT="Possible values: 10000 to 99999",
|
||||
INCLUDE=(10000 to 99999);
|
||||
TEXTEDIT f7 = FORMONLY.cust_address TYPE CHAR,
|
||||
COMMENT="Must hold '<number>, <street name>'";
|
||||
|
||||
|
||||
LABEL tr = FORMONLY.cust_info, STYLE="information";
|
||||
|
||||
BUTTON cq: cust_query, TABINDEX=0;
|
||||
BUTTON cx: cust_fetch, TABINDEX=0;
|
||||
BUTTON cf: cust_first, TABINDEX=0;
|
||||
BUTTON cp: cust_previous, TABINDEX=0;
|
||||
BUTTON cn: cust_next, TABINDEX=0;
|
||||
BUTTON cl: cust_last, TABINDEX=0;
|
||||
BUTTON ca: cust_append, TABINDEX=0;
|
||||
BUTTON cd: cust_delete, TABINDEX=0;
|
||||
BUTTON cu: cust_undo, TABINDEX=0;
|
||||
BUTTON cs: cust_save, TABINDEX=0;
|
||||
|
||||
GROUP g2: TEXT = "Orders";
|
||||
TABLE t1: DOUBLECLICK=ord_modify;
|
||||
EDIT c1 = FORMONLY.ord_id TYPE INTEGER, TITLE = "Id";
|
||||
EDIT c2 = FORMONLY.ord_crea TYPE DATE, TITLE = "Creation";
|
||||
EDIT c3 = FORMONLY.ord_amount TYPE MONEY(15,2), TITLE = "Amount";
|
||||
|
||||
BUTTON oad: ord_append, TABINDEX=0;
|
||||
BUTTON omo: ord_modify, TABINDEX=0;
|
||||
BUTTON ode: ord_delete, TABINDEX=0;
|
||||
|
||||
END
|
||||
|
||||
INSTRUCTIONS
|
||||
SCREEN RECORD custrec (FORMONLY.cust_id THROUGH FORMONLY.cust_address);
|
||||
SCREEN RECORD ordlist (FORMONLY.ord_id THROUGH FORMONLY.ord_amount);
|
||||
END
|
||||
@@ -0,0 +1,432 @@
|
||||
# Property of Four Js*
|
||||
# (c) Copyright Four Js 1995, 2019. All Rights Reserved.
|
||||
# * Trademark of Four Js Development Tools Europe Ltd
|
||||
# in the United States and elsewhere
|
||||
#
|
||||
# Four Js and its suppliers do not warrant or guarantee that these
|
||||
# samples are accurate and suitable for your purposes. Their inclusion is
|
||||
# purely for information purposes only.
|
||||
|
||||
IMPORT FGL CityList
|
||||
IMPORT FGL OrderInput
|
||||
IMPORT FGL MsgBox
|
||||
|
||||
GLOBALS "Globals.4gl"
|
||||
|
||||
DEFINE cust, old_cust custrec
|
||||
|
||||
DEFINE cust_curr INT -- current position in the array (scroll cursor)
|
||||
DEFINE cust_touched INT -- set if something in the customer record was changed
|
||||
DEFINE cust_new INT -- > 0 if new customer is currently edited
|
||||
|
||||
CONSTANT mytitle = "Customer"
|
||||
|
||||
CONSTANT S_MOVE = "move"
|
||||
CONSTANT S_NEW = "new"
|
||||
CONSTANT S_CLOSE = "close"
|
||||
CONSTANT S_QUERY = "query"
|
||||
CONSTANT S_SAVE = "save"
|
||||
|
||||
MAIN
|
||||
DEFINE state STRING -- requested state in the dialog
|
||||
DEFINE req_pos INT -- required position in the dialog
|
||||
DEFINE del INT -- delete flag
|
||||
|
||||
OPTIONS INPUT WRAP
|
||||
|
||||
OPEN FORM f_cust FROM "CustOrders2"
|
||||
DISPLAY FORM f_cust
|
||||
|
||||
CALL cust_loadlist(9)
|
||||
LET state=S_MOVE
|
||||
LET cust_curr=1
|
||||
WHILE state<>S_CLOSE
|
||||
DISPLAY "WHILE,state=",state,",cust_curr():",cust_curr,",req_pos:",req_pos
|
||||
IF cust_len() == 0 OR state == S_NEW THEN
|
||||
LET cust_curr=cust_len()+1
|
||||
LET cust_new=cust_curr
|
||||
ELSE
|
||||
LET cust_new=0
|
||||
CALL cust_fetch(req_pos)
|
||||
END IF
|
||||
LET state=S_MOVE
|
||||
LET req_pos=cust_curr
|
||||
LET cust_touched=0
|
||||
DIALOG ATTRIBUTES(FIELD ORDER FORM, UNBUFFERED)
|
||||
|
||||
INPUT cust.* FROM custrec.* ATTRIBUTES(WITHOUT DEFAULTS=NOT cust_new)
|
||||
|
||||
AFTER FIELD cust_name
|
||||
IF cust.cust_name MATCHES "*[@#$%^&]*" THEN
|
||||
ERROR "The customer name contains invalid characters."
|
||||
NEXT FIELD cust_name
|
||||
END IF
|
||||
|
||||
AFTER FIELD cust_address
|
||||
IF NOT cust_check_address(cust.cust_address) THEN
|
||||
NEXT FIELD cust_address
|
||||
END IF
|
||||
|
||||
AFTER INPUT
|
||||
IF cust.cust_city IS NULL AND cust.cust_zipcode IS NOT NULL THEN
|
||||
CALL mbox_ok(mytitle,"You must specify a city because zipcode is not null.","stop")
|
||||
NEXT FIELD cust_city
|
||||
END IF
|
||||
|
||||
END INPUT
|
||||
|
||||
DISPLAY ARRAY orders TO ordlist.*
|
||||
BEFORE DISPLAY
|
||||
IF cust_new THEN
|
||||
CALL mbox_ok(mytitle,"Please save the new customer before adding orders!","stop")
|
||||
NEXT FIELD cust_name
|
||||
END IF
|
||||
|
||||
BEFORE ROW
|
||||
MESSAGE SFMT("Order record %1 / %2", DIALOG.getCurrentRow("ordlist"), DIALOG.getArrayLength("ordlist"))
|
||||
|
||||
ON ACTION ord_append
|
||||
CALL ord_append(DIALOG)
|
||||
|
||||
ON ACTION ord_modify
|
||||
CALL ord_modify(DIALOG)
|
||||
|
||||
ON ACTION ord_delete
|
||||
CALL ord_delete(DIALOG)
|
||||
END DISPLAY
|
||||
|
||||
BEFORE DIALOG
|
||||
CALL cust_setup(DIALOG)
|
||||
CALL order_of_cust(cust_curr)
|
||||
IF cust_new THEN
|
||||
LET old_cust.* = cust.*
|
||||
END IF
|
||||
|
||||
ON ACTION zoom_city
|
||||
CALL cust_zoom_city(DIALOG)
|
||||
|
||||
ON ACTION cust_first
|
||||
LET req_pos=1
|
||||
LET state=S_MOVE ; ACCEPT DIALOG
|
||||
|
||||
ON ACTION cust_previous
|
||||
LET req_pos=cust_curr-1
|
||||
LET state=S_MOVE ; ACCEPT DIALOG
|
||||
|
||||
ON ACTION cust_next
|
||||
LET req_pos=cust_curr+1
|
||||
LET state=S_MOVE ; ACCEPT DIALOG
|
||||
|
||||
ON ACTION cust_last
|
||||
LET req_pos=cust_len()
|
||||
LET state=S_MOVE ; ACCEPT DIALOG
|
||||
|
||||
ON ACTION cust_append
|
||||
LET state=S_NEW ; ACCEPT DIALOG
|
||||
|
||||
ON ACTION cust_query --we can perform a safe query also only after
|
||||
LET state=S_QUERY ; ACCEPT DIALOG --the validations have been done
|
||||
|
||||
ON ACTION cust_delete
|
||||
IF cust_len() > 0 THEN
|
||||
LET del=0
|
||||
IF cust_new AND NOT cust_touched THEN
|
||||
LET del=1
|
||||
ELSE
|
||||
IF mbox_yn(mytitle,"Delete current customer?","question") THEN
|
||||
LET del=1
|
||||
END IF
|
||||
END IF
|
||||
IF del THEN
|
||||
CALL customers.deleteElement(cust_curr)
|
||||
-- normally a
|
||||
-- IF cust_id>0 THEN
|
||||
-- DELETE from customers WHERE cust_id=cust.cust_id
|
||||
-- END IF
|
||||
LET state=S_MOVE
|
||||
EXIT DIALOG
|
||||
END IF
|
||||
END IF
|
||||
|
||||
ON ACTION dialogtouched
|
||||
LET cust_touched=1
|
||||
CALL cust_setup(DIALOG)
|
||||
|
||||
ON ACTION cust_undo
|
||||
LET cust.* = old_cust.*
|
||||
DISPLAY BY NAME cust.*
|
||||
CALL DIALOG.setFieldTouched("custrec.*",FALSE)
|
||||
LET cust_touched=0
|
||||
CALL cust_setup(DIALOG)
|
||||
|
||||
ON ACTION cust_save
|
||||
LET state=S_SAVE ; ACCEPT DIALOG
|
||||
|
||||
ON ACTION cancel
|
||||
LET state=S_CLOSE
|
||||
--close is a bit difficult: if we did not touch something,
|
||||
--we can go out safely, otherwise we need to duplicate things
|
||||
--we do normally in AFTER DIALOG
|
||||
IF cust_touched THEN
|
||||
--we touched something
|
||||
CASE mbox_ync(mytitle,"Save your changes?","question")
|
||||
WHEN 1 --yes
|
||||
ACCEPT DIALOG --go thru the validation and fall finally out
|
||||
--in AFTER DIALOG
|
||||
WHEN 2 --no
|
||||
EXIT DIALOG
|
||||
WHEN 3 --cancel
|
||||
CONTINUE DIALOG
|
||||
END CASE
|
||||
ELSE
|
||||
--nothing changed, just go out
|
||||
EXIT DIALOG
|
||||
END IF
|
||||
|
||||
AFTER DIALOG
|
||||
IF state=S_CLOSE OR state=S_SAVE THEN
|
||||
--we already confirmed the save, jump to the save code
|
||||
GOTO savedb
|
||||
ELSE
|
||||
IF old_cust.* <> cust.* THEN
|
||||
CASE mbox_ync(mytitle,"Save your changes?","question")
|
||||
WHEN 1 --yes
|
||||
GOTO savedb
|
||||
WHEN 3 --cancel and stay in the dialog
|
||||
LET state=S_MOVE ; CONTINUE DIALOG
|
||||
END CASE
|
||||
END IF
|
||||
END IF
|
||||
EXIT DIALOG
|
||||
LABEL savedb: --use the label here to save a local var and some IF's
|
||||
IF cust_new THEN
|
||||
DISPLAY "INSERT new customer into db"
|
||||
LET customers[cust_new].* = cust.*
|
||||
ELSE
|
||||
DISPLAY "UPDATE customer in db"
|
||||
LET customers[cust_curr].* = cust.*
|
||||
END IF
|
||||
END DIALOG
|
||||
--we can perform the query action only if everything was validated before
|
||||
IF state == S_QUERY THEN
|
||||
CALL cust_query()
|
||||
END IF
|
||||
END WHILE
|
||||
END MAIN
|
||||
|
||||
FUNCTION cust_len()
|
||||
--we use the 'customers' array as a db replacement here
|
||||
--normally you could return the real COUNT of the DB statement cursor
|
||||
RETURN customers.getLength()
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION cust_fetch(index)
|
||||
DEFINE index INTEGER
|
||||
IF index > cust_len() THEN
|
||||
LET index = cust_len()
|
||||
END IF
|
||||
IF index < 1 THEN
|
||||
LET index = 1
|
||||
END IF
|
||||
--fetch customer from the DB, index could be used to FETCH ABSOLUTE
|
||||
LET cust.* = customers[index].*
|
||||
LET old_cust.* = cust.*
|
||||
LET cust_curr = index
|
||||
MESSAGE SFMT("Fetched Customer: %1/%2", cust_curr, cust_len())
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION cust_setup(d)
|
||||
DEFINE d ui.Dialog
|
||||
DEFINE has_rows, on_first, on_last, cust_ro INTEGER
|
||||
|
||||
LET has_rows = (cust_curr > 0 AND cust_len() > 0 )
|
||||
LET on_first = (cust_curr == 1)
|
||||
LET on_last = (cust_curr == cust_len())
|
||||
LET cust_ro = (has_rows AND NOT cust_touched)
|
||||
|
||||
CALL d.setActionActive("dialogtouched", cust_ro)
|
||||
CALL d.setActionActive("cust_undo", NOT cust_ro)
|
||||
CALL d.setActionActive("cust_save", NOT cust_ro)
|
||||
CALL d.setActionActive("cust_append", TRUE) -- always active
|
||||
CALL d.setActionActive("cust_delete", has_rows) -- can delete creation
|
||||
CALL d.setActionActive("cust_query", TRUE) -- always active
|
||||
CALL d.setActionActive("cust_first", NOT on_first)
|
||||
CALL d.setActionActive("cust_previous", NOT on_first)
|
||||
CALL d.setActionActive("cust_next", NOT on_last)
|
||||
CALL d.setActionActive("cust_last", NOT on_last)
|
||||
|
||||
IF cust_new THEN
|
||||
DISPLAY "New customer (save with ctrl-s)" TO cust_info
|
||||
ELSE
|
||||
IF cust_touched THEN
|
||||
DISPLAY "Modifying customer (save with ctrl-s)" TO cust_info
|
||||
ELSE
|
||||
DISPLAY SFMT("Customer record %1 / %2",cust_curr,cust_len()) TO cust_info
|
||||
END IF
|
||||
END IF
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION cust_query()
|
||||
DEFINE sql STRING
|
||||
DISPLAY "Enter customer search criterias" TO cust_info
|
||||
LET int_flag = FALSE
|
||||
CONSTRUCT BY NAME sql ON cust_id, cust_name, cust_state,
|
||||
cust_city, cust_zipcode, cust_address, cust_checked
|
||||
IF NOT int_flag THEN
|
||||
-- Dummy query, should use database query instead.
|
||||
CALL cust_loadlist(LENGTH(sql))
|
||||
CALL mbox_ok(mytitle,SFMT("Found %1 customer records in database",cust_len()),"information")
|
||||
END IF
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION cust_zoom_city(d)
|
||||
DEFINE d ui.Dialog
|
||||
DEFINE s INTEGER
|
||||
DEFINE name, state STRING
|
||||
CALL select_city(d.getFieldBuffer("cust_city")) RETURNING s, name, state
|
||||
IF s==0 THEN
|
||||
LET cust.cust_city = name
|
||||
LET cust.cust_state = state
|
||||
CALL d.setFieldTouched("cust_city",TRUE)
|
||||
CALL d.setFieldTouched("cust_state",TRUE)
|
||||
LET cust_touched = TRUE
|
||||
CALL cust_setup(d)
|
||||
END IF
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION cust_verify_address_format(value)
|
||||
DEFINE value STRING
|
||||
DEFINE num STRING, p,x INTEGER
|
||||
IF value IS NULL THEN RETURN TRUE END IF
|
||||
LET p = value.getIndexOf(",", 1)
|
||||
IF p == 0 THEN RETURN FALSE END IF
|
||||
LET num = value.subString(1, p)
|
||||
LET x = num
|
||||
IF x <= 0 THEN RETURN FALSE END IF
|
||||
IF value.getLength() <= p THEN RETURN FALSE END IF
|
||||
RETURN TRUE
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION cust_check_address(value)
|
||||
DEFINE value STRING
|
||||
IF NOT cust_verify_address_format(value) THEN
|
||||
CALL mbox_ok(mytitle,"Address must hold a number followed by a comma and the street name.","stop")
|
||||
RETURN FALSE
|
||||
END IF
|
||||
RETURN TRUE
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION order_of_cust(index)
|
||||
DEFINE index INTEGER --index should be normally the cust_id
|
||||
--MESSAGE SFMT("Customer: %1/%2", cust_curr, cust_len())
|
||||
IF index >= 1 AND index <= cust_len() THEN
|
||||
CALL ord_loadlist(customers[index].cust_id, (index mod 5) * 3)
|
||||
ELSE
|
||||
CALL orders.clear()
|
||||
END IF
|
||||
END FUNCTION
|
||||
|
||||
-- Dummy function to fill the list of customers (normally, SELECT from database)
|
||||
FUNCTION cust_loadlist(m)
|
||||
DEFINE m,i INTEGER
|
||||
CALL customers.clear()
|
||||
FOR i=1 TO m
|
||||
LET customers[i].cust_id = i
|
||||
LET customers[i].cust_name = "Name " || i
|
||||
LET customers[i].cust_state = "CA"
|
||||
LET customers[i].cust_city = "San Fransisco"
|
||||
LET customers[i].cust_zipcode = 94102
|
||||
LET customers[i].cust_address = "52, Marlon Street"
|
||||
LET customers[i].cust_checked = i mod 2
|
||||
END FOR
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION ord_loadlist(mid,m)
|
||||
DEFINE mid,m,i INTEGER
|
||||
CALL orders.clear()
|
||||
FOR i=1 TO m
|
||||
CALL orders.appendElement()
|
||||
LET orders[i].ord_id = mid * 100 + i
|
||||
LET orders[i].ord_crea = CURRENT YEAR TO SECOND
|
||||
LET orders[i].ord_amount = ( i mod 5 ) * 133.56
|
||||
END FOR
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION ord_append(d)
|
||||
DEFINE d ui.Dialog
|
||||
CALL ord_input('A', cust.cust_id, cust.cust_name, 0)
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION ord_modify(d)
|
||||
DEFINE d ui.Dialog
|
||||
IF d.getCurrentRow("ordlist")==0 THEN RETURN END IF
|
||||
CALL ord_input('M', cust.cust_id, cust.cust_name, orders[d.getCurrentRow("ordlist")].ord_id)
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION ord_delete(d)
|
||||
DEFINE d ui.Dialog
|
||||
IF NOT mbox_yn(mytitle,"Delete select order from database?","question") THEN
|
||||
RETURN
|
||||
END IF
|
||||
CALL d.deleteRow("ordlist",d.getCurrentRow("ordlist"))
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION combo_fill_states(cb)
|
||||
DEFINE cb ui.ComboBox
|
||||
|
||||
CALL cb.addItem("AK","Alaska")
|
||||
CALL cb.addItem("AL","Alabama")
|
||||
CALL cb.addItem("AR","Arkansas")
|
||||
CALL cb.addItem("AZ","Arizona")
|
||||
CALL cb.addItem("CA","California")
|
||||
CALL cb.addItem("CO","Colorado")
|
||||
CALL cb.addItem("CT","Connecticut")
|
||||
CALL cb.addItem("DC","D.C.")
|
||||
CALL cb.addItem("DE","Delaware")
|
||||
CALL cb.addItem("FL","Florida")
|
||||
CALL cb.addItem("GA","Georgia")
|
||||
CALL cb.addItem("HI","Hawaii")
|
||||
CALL cb.addItem("IA","Iowa")
|
||||
CALL cb.addItem("ID","Idaho")
|
||||
CALL cb.addItem("IL","Illinois")
|
||||
CALL cb.addItem("IN","Indiana")
|
||||
CALL cb.addItem("KS","Kansas")
|
||||
CALL cb.addItem("KY","Kentucky")
|
||||
CALL cb.addItem("LA","Louisiana")
|
||||
CALL cb.addItem("MA","Massachusetts")
|
||||
CALL cb.addItem("MD","Maryland")
|
||||
CALL cb.addItem("ME","Maine")
|
||||
CALL cb.addItem("MI","Michigan")
|
||||
CALL cb.addItem("MN","Minnesota")
|
||||
CALL cb.addItem("MO","Missouri")
|
||||
CALL cb.addItem("MS","Mississippi")
|
||||
CALL cb.addItem("MT","Montana")
|
||||
CALL cb.addItem("NC","North Carolina")
|
||||
CALL cb.addItem("ND","North Dakota")
|
||||
CALL cb.addItem("NE","Nebraska")
|
||||
CALL cb.addItem("NH","New Hampshire")
|
||||
CALL cb.addItem("NJ","New Jersey")
|
||||
CALL cb.addItem("NM","New Mexico")
|
||||
CALL cb.addItem("NV","Nevada")
|
||||
CALL cb.addItem("NY","New York")
|
||||
CALL cb.addItem("OH","Ohio")
|
||||
CALL cb.addItem("OK","Oklahoma")
|
||||
CALL cb.addItem("OR","Oregon")
|
||||
CALL cb.addItem("PA","Pennsylvania")
|
||||
CALL cb.addItem("PR","Puerto Rico")
|
||||
CALL cb.addItem("RI","Rhode Island")
|
||||
CALL cb.addItem("SC","South Carolina")
|
||||
CALL cb.addItem("SD","South Dakota")
|
||||
CALL cb.addItem("TN","Tennessee")
|
||||
CALL cb.addItem("TX","Texas")
|
||||
CALL cb.addItem("UT","Utah")
|
||||
CALL cb.addItem("VA","Virginia")
|
||||
CALL cb.addItem("VT","Vermont")
|
||||
CALL cb.addItem("WA","Washington")
|
||||
CALL cb.addItem("WI","Wisconsin")
|
||||
CALL cb.addItem("WV","West Virginia")
|
||||
CALL cb.addItem("WY","Wyoming")
|
||||
|
||||
END FUNCTION
|
||||
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
# Property of Four Js*
|
||||
# (c) Copyright Four Js 1995, 2019. All Rights Reserved.
|
||||
# * Trademark of Four Js Development Tools Europe Ltd
|
||||
# in the United States and elsewhere
|
||||
#
|
||||
# Four Js and its suppliers do not warrant or guarantee that these
|
||||
# samples are accurate and suitable for your purposes. Their inclusion is
|
||||
# purely for information purposes only.
|
||||
|
||||
ACTION DEFAULTS
|
||||
ACTION close(VALIDATE=NO)
|
||||
ACTION cancel(VALIDATE=NO)
|
||||
ACTION cust_query ( IMAGE="find", COMMENT = "Query customers", ACCELERATOR = control-q )
|
||||
ACTION cust_append ( IMAGE="new", COMMENT = "Create new customer", ACCELERATOR = control-n )
|
||||
ACTION cust_undo ( IMAGE="refresh", COMMENT = "Undo last changes", ACCELERATOR = control-u, VALIDATE = NO )
|
||||
ACTION cust_save ( IMAGE="disk", COMMENT = "Save changes in database", ACCELERATOR = control-s )
|
||||
ACTION cust_delete ( IMAGE="eraser", COMMENT = "Delete current customer", ACCELERATOR = control-d, VALIDATE = NO )
|
||||
ACTION cust_first ( IMAGE = "gobegin", COMMENT = "Move to first customer in list")
|
||||
ACTION cust_previous ( IMAGE = "gorev", COMMENT = "Move to previous customer in list")
|
||||
ACTION cust_next ( IMAGE = "goforw", COMMENT = "Move to next customer in list")
|
||||
ACTION cust_last ( IMAGE = "goend", COMMENT = "Move to last customer in list")
|
||||
ACTION cust_print ( IMAGE="printer", COMMENT = "Print customer")
|
||||
ACTION ord_append ( IMAGE = "new", COMMENT="Add a new order")
|
||||
ACTION ord_modify ( IMAGE = "pen", COMMENT="Modify selected order")
|
||||
ACTION ord_delete ( IMAGE = "eraser", COMMENT="Delete selected order")
|
||||
END
|
||||
|
||||
|
||||
|
||||
LAYOUT (TEXT = "Customers and Orders" )
|
||||
GRID
|
||||
{
|
||||
<g g1 >
|
||||
[f1 ][f2 ]
|
||||
State: [f3 ] [f4 ]
|
||||
City: [f5 ]
|
||||
Zipcode: [f6 ]
|
||||
Address: [f7 ]
|
||||
[ ]
|
||||
-------------------------------------------------------------------
|
||||
[tr : :cq:cf:cp:cn:cl:ca:cd:cu:cs]
|
||||
< >
|
||||
<g g2 >
|
||||
<t t1 >[oad]
|
||||
[c1 |c2 |c3 ][omo]
|
||||
[c1 |c2 |c3 ][ode]
|
||||
[c1 |c2 |c3 ]
|
||||
[c1 |c2 |c3 ]
|
||||
[c1 |c2 |c3 ]
|
||||
[c1 |c2 |c3 ]
|
||||
[c1 |c2 |c3 ]
|
||||
[c1 |c2 |c3 ]
|
||||
[c1 |c2 |c3 ]
|
||||
[c1 |c2 |c3 ]
|
||||
< >
|
||||
< >
|
||||
}
|
||||
END
|
||||
END
|
||||
|
||||
ATTRIBUTES
|
||||
|
||||
GROUP g1: TEXT = "Customer information";
|
||||
EDIT f1 = FORMONLY.cust_id TYPE INTEGER, NOT NULL, NOENTRY;
|
||||
EDIT f2 = FORMONLY.cust_name TYPE VARCHAR, NOT NULL, REQUIRED;
|
||||
COMBOBOX f3 = FORMONLY.cust_state TYPE VARCHAR, NOT NULL, REQUIRED,
|
||||
QUERYEDITABLE, DEFAULT="CA", INITIALIZER=combo_fill_states;
|
||||
CHECKBOX f4 = FORMONLY.cust_checked TYPE CHAR, TEXT="Checked";
|
||||
BUTTONEDIT f5 = FORMONLY.cust_city TYPE CHAR, NOT NULL, REQUIRED,
|
||||
ACTION=zoom_city, IMAGE="find";
|
||||
EDIT f6 = FORMONLY.cust_zipcode TYPE VARCHAR, NOT NULL, REQUIRED,
|
||||
COMMENT="Possible values: 10000 to 99999",
|
||||
INCLUDE=(10000 to 99999);
|
||||
TEXTEDIT f7 = FORMONLY.cust_address TYPE CHAR,
|
||||
COMMENT="Must hold '<number>, <street name>'";
|
||||
|
||||
|
||||
LABEL tr = FORMONLY.cust_info, STYLE="information",SIZEPOLICY=FIXED;
|
||||
|
||||
BUTTON cq: cust_query, TABINDEX=0;
|
||||
BUTTON cf: cust_first, TABINDEX=0;
|
||||
BUTTON cp: cust_previous, TABINDEX=0;
|
||||
BUTTON cn: cust_next, TABINDEX=0;
|
||||
BUTTON cl: cust_last, TABINDEX=0;
|
||||
BUTTON ca: cust_append, TABINDEX=0;
|
||||
BUTTON cd: cust_delete, TABINDEX=0;
|
||||
BUTTON cu: cust_undo, TABINDEX=0;
|
||||
BUTTON cs: cust_save, TABINDEX=0;
|
||||
|
||||
GROUP g2: TEXT = "Orders";
|
||||
TABLE t1: DOUBLECLICK=ord_modify;
|
||||
EDIT c1 = FORMONLY.ord_id TYPE INTEGER, TITLE = "Id";
|
||||
EDIT c2 = FORMONLY.ord_crea TYPE DATE, TITLE = "Creation";
|
||||
EDIT c3 = FORMONLY.ord_amount TYPE MONEY(15,2), TITLE = "Amount";
|
||||
|
||||
BUTTON oad: ord_append, TABINDEX=0;
|
||||
BUTTON omo: ord_modify, TABINDEX=0;
|
||||
BUTTON ode: ord_delete, TABINDEX=0;
|
||||
|
||||
END
|
||||
|
||||
INSTRUCTIONS
|
||||
SCREEN RECORD custrec (FORMONLY.cust_id THROUGH FORMONLY.cust_address);
|
||||
SCREEN RECORD ordlist (FORMONLY.ord_id THROUGH FORMONLY.ord_amount);
|
||||
END
|
||||
@@ -0,0 +1,309 @@
|
||||
# Property of Four Js*
|
||||
# (c) Copyright Four Js 1995, 2019. All Rights Reserved.
|
||||
# * Trademark of Four Js Development Tools Europe Ltd
|
||||
# in the United States and elsewhere
|
||||
#
|
||||
# Four Js and its suppliers do not warrant or guarantee that these
|
||||
# samples are accurate and suitable for your purposes. Their inclusion is
|
||||
# purely for information purposes only.
|
||||
|
||||
IMPORT FGL CityList
|
||||
IMPORT FGL OrderInput
|
||||
IMPORT FGL MsgBox
|
||||
|
||||
GLOBALS "Globals.4gl"
|
||||
|
||||
CONSTANT mytitle = "Customer"
|
||||
DEFINE myd ui.Dialog
|
||||
DEFINE cust_new INT
|
||||
|
||||
MAIN
|
||||
DEFINE deleted INT
|
||||
DEFINE oldcust custrec
|
||||
|
||||
OPTIONS INPUT WRAP
|
||||
|
||||
OPEN FORM f_cust FROM "CustOrders3"
|
||||
DISPLAY FORM f_cust
|
||||
|
||||
CALL cust_loadlist(9)
|
||||
|
||||
DIALOG ATTRIBUTES(FIELD ORDER FORM, UNBUFFERED)
|
||||
|
||||
INPUT ARRAY customers FROM custrec.* ATTRIBUTES(INSERT ROW=0)
|
||||
BEFORE ROW
|
||||
CALL order_of_cust(arr_curr())
|
||||
CALL cust_setup(0)
|
||||
LET oldcust.*=customers[arr_curr()].*
|
||||
|
||||
AFTER FIELD cust_name
|
||||
IF customers[arr_curr()].cust_name MATCHES "*[@#$%^&]*" THEN
|
||||
ERROR "The customer name contains invalid characters."
|
||||
NEXT FIELD cust_name
|
||||
END IF
|
||||
|
||||
AFTER FIELD cust_address
|
||||
IF NOT cust_check_address(customers[arr_curr()].cust_address) THEN
|
||||
NEXT FIELD cust_address
|
||||
END IF
|
||||
|
||||
BEFORE INSERT
|
||||
LET cust_new=1
|
||||
CALL cust_setup(0)
|
||||
LET oldcust.*=customers[arr_curr()].*
|
||||
CALL order_of_cust(arr_curr())
|
||||
|
||||
AFTER ROW
|
||||
--be aware of the temp row...
|
||||
IF arr_curr()<= arr_count() AND NOT deleted THEN
|
||||
IF customers[arr_curr()].cust_city IS NULL AND customers[arr_curr()].cust_zipcode IS NOT NULL THEN
|
||||
ERROR "You must specify a city because zipcode is not null."
|
||||
NEXT FIELD cust_city
|
||||
END IF
|
||||
IF FIELD_TOUCHED(custrec.*) THEN
|
||||
IF cust_new THEN
|
||||
--INSERT INTO ....
|
||||
DISPLAY sfmt("INSERT into DB for name:%1",customers[arr_curr()].cust_name)
|
||||
MESSAGE sfmt("INSERT into DB for name:%1",customers[arr_curr()].cust_name)
|
||||
ELSE
|
||||
--UPDATE
|
||||
DISPLAY sfmt("UPDATE DB for name:%1",customers[arr_curr()].cust_name)
|
||||
MESSAGE sfmt("UPDATE DB for name:%1",customers[arr_curr()].cust_name)
|
||||
END IF
|
||||
IF customers[arr_curr()].cust_name="err" THEN --simulate error
|
||||
CALL mbox_ok("Error","Can't write record to DB","stop")
|
||||
NEXT FIELD cust_name
|
||||
END IF
|
||||
END IF
|
||||
END IF
|
||||
LET deleted=0
|
||||
LET cust_new=0
|
||||
|
||||
BEFORE DELETE
|
||||
IF NOT cust_new THEN
|
||||
IF NOT mbox_yn(mytitle,"Delete current customer?","question") THEN
|
||||
CANCEL DELETE
|
||||
ELSE
|
||||
DISPLAY "DELETE from DB"
|
||||
MESSAGE "DELETED from DB:",customers[arr_curr()].cust_name
|
||||
END IF
|
||||
END IF
|
||||
|
||||
AFTER DELETE
|
||||
LET deleted=1
|
||||
CALL cust_setup(0)
|
||||
|
||||
ON ACTION zoom_city
|
||||
CALL cust_zoom_city()
|
||||
|
||||
ON ACTION undo
|
||||
DISPLAY BY NAME oldcust.*
|
||||
LET customers[arr_curr()].* = oldcust.*
|
||||
CALL DIALOG.setFieldTouched("custrec.*",FALSE)
|
||||
CALL cust_setup(0)
|
||||
|
||||
ON ACTION dialogtouched
|
||||
CALL cust_setup(1)
|
||||
END INPUT
|
||||
|
||||
DISPLAY ARRAY orders TO ordlist.*
|
||||
BEFORE ROW
|
||||
MESSAGE SFMT("Order record %1 / %2", ARR_CURR(), ARR_COUNT())
|
||||
|
||||
ON ACTION append
|
||||
CALL ord_append()
|
||||
|
||||
ON ACTION ord_modify
|
||||
CALL ord_modify()
|
||||
|
||||
ON ACTION delete
|
||||
CALL ord_delete()
|
||||
|
||||
END DISPLAY
|
||||
|
||||
BEFORE DIALOG
|
||||
LET myd=DIALOG
|
||||
|
||||
ON ACTION cancel
|
||||
IF FIELD_TOUCHED(custrec.*) THEN
|
||||
CASE mbox_ync(mytitle,"Save your changes?","question")
|
||||
WHEN 1 --yes
|
||||
ACCEPT DIALOG --go thru the validation
|
||||
WHEN 2 --no
|
||||
EXIT DIALOG
|
||||
WHEN 3 --cancel
|
||||
CONTINUE DIALOG
|
||||
END CASE
|
||||
END IF
|
||||
EXIT DIALOG
|
||||
|
||||
END DIALOG
|
||||
LET myd=NULL
|
||||
END MAIN
|
||||
|
||||
FUNCTION cust_setup(touched)
|
||||
DEFINE touched INT
|
||||
--because the dialogtouched/undo action is only defined in the INPUT ARRAY
|
||||
--may run into an error if we try them to switch off in another context
|
||||
CALL myd.setActionActive("dialogtouched",NOT touched)
|
||||
CALL myd.setActionActive("undo", touched)
|
||||
IF cust_new THEN
|
||||
DISPLAY "Input new customer" TO cust_info
|
||||
ELSE
|
||||
DISPLAY SFMT("Customer record %1 / %2",c_arr_curr(),customers.getLength()) TO cust_info
|
||||
END IF
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION c_arr_curr() RETURN myd.getCurrentRow("custrec") END FUNCTION
|
||||
|
||||
FUNCTION o_arr_curr() RETURN myd.getCurrentRow("ordlist") END FUNCTION
|
||||
|
||||
FUNCTION cust_zoom_city()
|
||||
DEFINE s INTEGER
|
||||
DEFINE name, state STRING
|
||||
CALL select_city(myd.getFieldBuffer("cust_city")) RETURNING s, name, state
|
||||
IF s==0 THEN
|
||||
LET customers[c_arr_curr()].cust_city = name
|
||||
LET customers[c_arr_curr()].cust_state = state
|
||||
CALL myd.setFieldTouched("cust_city",TRUE)
|
||||
CALL myd.setFieldTouched("cust_state",TRUE)
|
||||
CALL cust_setup(1)
|
||||
END IF
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION cust_verify_address_format(value)
|
||||
DEFINE value STRING
|
||||
DEFINE num STRING, p,x INTEGER
|
||||
IF value IS NULL THEN RETURN TRUE END IF
|
||||
LET p = value.getIndexOf(",", 1)
|
||||
IF p == 0 THEN RETURN FALSE END IF
|
||||
LET num = value.subString(1, p)
|
||||
LET x = num
|
||||
IF x <= 0 THEN RETURN FALSE END IF
|
||||
IF value.getLength() <= p THEN RETURN FALSE END IF
|
||||
RETURN TRUE
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION cust_check_address(value)
|
||||
DEFINE value STRING
|
||||
IF NOT cust_verify_address_format(value) THEN
|
||||
CALL mbox_ok(mytitle,"Address must hold a number followed by a comma and the street name.","stop")
|
||||
RETURN FALSE
|
||||
END IF
|
||||
RETURN TRUE
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION order_of_cust(index)
|
||||
DEFINE index INTEGER
|
||||
--MESSAGE SFMT("Customer: %1/%2", cust_curr, customers.getLength())
|
||||
IF cust_new THEN
|
||||
CALL orders.clear()
|
||||
ELSE
|
||||
CALL ord_loadlist(customers[index].cust_id, (index mod 5) * 3)
|
||||
END IF
|
||||
END FUNCTION
|
||||
|
||||
-- Dummy function to fill the list of customers (normally, SELECT from database)
|
||||
FUNCTION cust_loadlist(m)
|
||||
DEFINE m,i INTEGER
|
||||
CALL customers.clear()
|
||||
FOR i=1 TO m
|
||||
LET customers[i].cust_id = i
|
||||
LET customers[i].cust_name = "Name " || i
|
||||
LET customers[i].cust_state = "CA"
|
||||
LET customers[i].cust_city = "San Fransisco"
|
||||
LET customers[i].cust_zipcode = 94102
|
||||
LET customers[i].cust_address = "52, Marlon Street"
|
||||
LET customers[i].cust_checked = i mod 2
|
||||
END FOR
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION ord_loadlist(mid,m)
|
||||
DEFINE mid,m,i INTEGER
|
||||
CALL orders.clear()
|
||||
FOR i=1 TO m
|
||||
CALL orders.appendElement()
|
||||
LET orders[i].ord_id = mid * 100 + i
|
||||
LET orders[i].ord_crea = CURRENT YEAR TO SECOND
|
||||
LET orders[i].ord_amount = ( i mod 5 ) * 133.56
|
||||
END FOR
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION ord_append()
|
||||
DEFINE r INT
|
||||
LET r=c_arr_curr()
|
||||
CALL ord_input('A', customers[r].cust_id, customers[r].cust_name, 0)
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION ord_modify()
|
||||
DEFINE r INT
|
||||
LET r=c_arr_curr()
|
||||
IF o_arr_curr()==0 THEN RETURN END IF
|
||||
CALL ord_input('M', customers[r].cust_id, customers[r].cust_name, orders[o_arr_curr()].ord_id)
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION ord_delete()
|
||||
IF NOT mbox_yn(mytitle,"Delete select order from database?","question") THEN
|
||||
RETURN
|
||||
END IF
|
||||
CALL myd.deleteRow("ordlist",o_arr_curr())
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION combo_fill_states(cb)
|
||||
DEFINE cb ui.ComboBox
|
||||
|
||||
CALL cb.addItem("AK","Alaska")
|
||||
CALL cb.addItem("AL","Alabama")
|
||||
CALL cb.addItem("AR","Arkansas")
|
||||
CALL cb.addItem("AZ","Arizona")
|
||||
CALL cb.addItem("CA","California")
|
||||
CALL cb.addItem("CO","Colorado")
|
||||
CALL cb.addItem("CT","Connecticut")
|
||||
CALL cb.addItem("DC","D.C.")
|
||||
CALL cb.addItem("DE","Delaware")
|
||||
CALL cb.addItem("FL","Florida")
|
||||
CALL cb.addItem("GA","Georgia")
|
||||
CALL cb.addItem("HI","Hawaii")
|
||||
CALL cb.addItem("IA","Iowa")
|
||||
CALL cb.addItem("ID","Idaho")
|
||||
CALL cb.addItem("IL","Illinois")
|
||||
CALL cb.addItem("IN","Indiana")
|
||||
CALL cb.addItem("KS","Kansas")
|
||||
CALL cb.addItem("KY","Kentucky")
|
||||
CALL cb.addItem("LA","Louisiana")
|
||||
CALL cb.addItem("MA","Massachusetts")
|
||||
CALL cb.addItem("MD","Maryland")
|
||||
CALL cb.addItem("ME","Maine")
|
||||
CALL cb.addItem("MI","Michigan")
|
||||
CALL cb.addItem("MN","Minnesota")
|
||||
CALL cb.addItem("MO","Missouri")
|
||||
CALL cb.addItem("MS","Mississippi")
|
||||
CALL cb.addItem("MT","Montana")
|
||||
CALL cb.addItem("NC","North Carolina")
|
||||
CALL cb.addItem("ND","North Dakota")
|
||||
CALL cb.addItem("NE","Nebraska")
|
||||
CALL cb.addItem("NH","New Hampshire")
|
||||
CALL cb.addItem("NJ","New Jersey")
|
||||
CALL cb.addItem("NM","New Mexico")
|
||||
CALL cb.addItem("NV","Nevada")
|
||||
CALL cb.addItem("NY","New York")
|
||||
CALL cb.addItem("OH","Ohio")
|
||||
CALL cb.addItem("OK","Oklahoma")
|
||||
CALL cb.addItem("OR","Oregon")
|
||||
CALL cb.addItem("PA","Pennsylvania")
|
||||
CALL cb.addItem("PR","Puerto Rico")
|
||||
CALL cb.addItem("RI","Rhode Island")
|
||||
CALL cb.addItem("SC","South Carolina")
|
||||
CALL cb.addItem("SD","South Dakota")
|
||||
CALL cb.addItem("TN","Tennessee")
|
||||
CALL cb.addItem("TX","Texas")
|
||||
CALL cb.addItem("UT","Utah")
|
||||
CALL cb.addItem("VA","Virginia")
|
||||
CALL cb.addItem("VT","Vermont")
|
||||
CALL cb.addItem("WA","Washington")
|
||||
CALL cb.addItem("WI","Wisconsin")
|
||||
CALL cb.addItem("WV","West Virginia")
|
||||
CALL cb.addItem("WY","Wyoming")
|
||||
|
||||
END FUNCTION
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
# Property of Four Js*
|
||||
# (c) Copyright Four Js 1995, 2019. All Rights Reserved.
|
||||
# * Trademark of Four Js Development Tools Europe Ltd
|
||||
# in the United States and elsewhere
|
||||
#
|
||||
# Four Js and its suppliers do not warrant or guarantee that these
|
||||
# samples are accurate and suitable for your purposes. Their inclusion is
|
||||
# purely for information purposes only.
|
||||
|
||||
ACTION DEFAULTS
|
||||
ACTION close(VALIDATE=NO)
|
||||
ACTION cancel(VALIDATE=NO)
|
||||
ACTION undo(TEXT="Undo",IMAGE="refresh",VALIDATE=NO)
|
||||
ACTION ord_modify ( TEXT="Edit Order", IMAGE="pen", COMMENT="Modifies selected order")
|
||||
END
|
||||
TOOLBAR
|
||||
ITEM firstrow (COMMENT="Move to the first record")
|
||||
ITEM prevrow (TEXT="Prev",COMMENT="Move to the previous record")
|
||||
ITEM nextrow (COMMENT="Move to the next record")
|
||||
ITEM lastrow (COMMENT="Move to the last record")
|
||||
SEPARATOR
|
||||
ITEM append (TEXT="New",IMAGE="new",COMMENT="Input a new record")
|
||||
ITEM delete (TEXT="Del",IMAGE="delete",COMMENT="Delete the current record")
|
||||
SEPARATOR
|
||||
ITEM ord_modify
|
||||
END
|
||||
|
||||
LAYOUT (TEXT = "Customers and Orders" )
|
||||
GRID
|
||||
{
|
||||
<g g1 >
|
||||
[f1 ][f2 ]
|
||||
State: [f3 ] [f4 ]
|
||||
City: [f5 ]
|
||||
Zipcode: [f6 ]
|
||||
Address: [f7 ]
|
||||
[ ]
|
||||
-------------------------------------------------------------------
|
||||
[tr : :undo ]
|
||||
|
||||
<g g2 >
|
||||
<t t1 >
|
||||
[c1 |c2 |c3 ]
|
||||
[c1 |c2 |c3 ]
|
||||
[c1 |c2 |c3 ]
|
||||
[c1 |c2 |c3 ]
|
||||
[c1 |c2 |c3 ]
|
||||
[c1 |c2 |c3 ]
|
||||
[c1 |c2 |c3 ]
|
||||
[c1 |c2 |c3 ]
|
||||
[c1 |c2 |c3 ]
|
||||
[c1 |c2 |c3 ]
|
||||
< >
|
||||
|
||||
}
|
||||
END
|
||||
END
|
||||
|
||||
ATTRIBUTES
|
||||
|
||||
GROUP g1: TEXT = "Customer information";
|
||||
f1 = FORMONLY.cust_id TYPE INTEGER, NOT NULL, NOENTRY,DEFAULT=0;
|
||||
EDIT f2 = FORMONLY.cust_name TYPE VARCHAR, NOT NULL, REQUIRED;
|
||||
COMBOBOX f3 = FORMONLY.cust_state TYPE VARCHAR, NOT NULL, REQUIRED,
|
||||
QUERYEDITABLE, DEFAULT="CA", INITIALIZER=combo_fill_states;
|
||||
CHECKBOX f4 = FORMONLY.cust_checked TYPE CHAR, TEXT="Checked",HIDDEN;
|
||||
BUTTONEDIT f5 = FORMONLY.cust_city TYPE CHAR, NOT NULL, REQUIRED,
|
||||
ACTION=zoom_city, IMAGE="find";
|
||||
f6 = FORMONLY.cust_zipcode TYPE VARCHAR, NOT NULL, REQUIRED,DEFAULT=11111, COMMENT="Possible values: 10000 to 99999",
|
||||
INCLUDE=(10000 to 99999);
|
||||
TEXTEDIT f7 = FORMONLY.cust_address TYPE CHAR,
|
||||
COMMENT="Must hold '<number>, <street name>'";
|
||||
|
||||
|
||||
LABEL tr = FORMONLY.cust_info, STYLE="information",SIZEPOLICY=FIXED;
|
||||
BUTTON undo:undo,TEXT="Undo";
|
||||
|
||||
GROUP g2: TEXT = "Orders";
|
||||
TABLE t1: DOUBLECLICK=ord_modify;
|
||||
EDIT c1 = FORMONLY.ord_id TYPE INTEGER, TITLE = "Id";
|
||||
EDIT c2 = FORMONLY.ord_crea TYPE DATE, TITLE = "Creation";
|
||||
EDIT c3 = FORMONLY.ord_amount TYPE MONEY(15,2), TITLE = "Amount";
|
||||
|
||||
END
|
||||
|
||||
INSTRUCTIONS
|
||||
SCREEN RECORD custrec (FORMONLY.cust_id THROUGH FORMONLY.cust_address);
|
||||
SCREEN RECORD ordlist (FORMONLY.ord_id THROUGH FORMONLY.ord_amount);
|
||||
END
|
||||
@@ -0,0 +1,38 @@
|
||||
# Property of Four Js*
|
||||
# (c) Copyright Four Js 1995, 2019. All Rights Reserved.
|
||||
# * Trademark of Four Js Development Tools Europe Ltd
|
||||
# in the United States and elsewhere
|
||||
#
|
||||
# Four Js and its suppliers do not warrant or guarantee that these
|
||||
# samples are accurate and suitable for your purposes. Their inclusion is
|
||||
# purely for information purposes only.
|
||||
|
||||
GLOBALS
|
||||
TYPE custrec RECORD
|
||||
cust_id INTEGER,
|
||||
cust_name VARCHAR(50),
|
||||
cust_state CHAR(3),
|
||||
cust_checked SMALLINT,
|
||||
cust_city VARCHAR(50),
|
||||
cust_zipcode INT,
|
||||
cust_address VARCHAR(200)
|
||||
END RECORD
|
||||
|
||||
DEFINE customers DYNAMIC ARRAY OF custrec
|
||||
|
||||
DEFINE orders DYNAMIC ARRAY OF RECORD
|
||||
ord_id INTEGER,
|
||||
ord_crea DATE,
|
||||
ord_amount MONEY(15,2)
|
||||
END RECORD
|
||||
|
||||
DEFINE ordrec RECORD
|
||||
ord_id INTEGER,
|
||||
ord_daddr VARCHAR(50),
|
||||
ord_crea DATE,
|
||||
ord_deliv DATE,
|
||||
ord_valid CHAR(1),
|
||||
ord_amount MONEY(15,2)
|
||||
END RECORD
|
||||
|
||||
END GLOBALS
|
||||
@@ -0,0 +1,33 @@
|
||||
# Property of Four Js*
|
||||
# (c) Copyright Four Js 1995, 2019. All Rights Reserved.
|
||||
# * Trademark of Four Js Development Tools Europe Ltd
|
||||
# in the United States and elsewhere
|
||||
#
|
||||
# Four Js and its suppliers do not warrant or guarantee that these
|
||||
# samples are accurate and suitable for your purposes. Their inclusion is
|
||||
# purely for information purposes only.
|
||||
|
||||
LAYOUT
|
||||
TABLE
|
||||
{
|
||||
Id Item Price
|
||||
[id |name |price ]
|
||||
[id |name |price ]
|
||||
[id |name |price ]
|
||||
[id |name |price ]
|
||||
[id |name |price ]
|
||||
[id |name |price ]
|
||||
}
|
||||
END
|
||||
END
|
||||
|
||||
ATTRIBUTES
|
||||
EDIT id = FORMONLY.item_id TYPE INTEGER;
|
||||
EDIT name = FORMONLY.item_name TYPE VARCHAR;
|
||||
EDIT price = FORMONLY.item_price TYPE DECIMAL(10,2);
|
||||
END
|
||||
|
||||
INSTRUCTIONS
|
||||
SCREEN RECORD sr (FORMONLY.*);
|
||||
END
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
# Property of Four Js*
|
||||
# (c) Copyright Four Js 1995, 2019. All Rights Reserved.
|
||||
# * Trademark of Four Js Development Tools Europe Ltd
|
||||
# in the United States and elsewhere
|
||||
#
|
||||
# Four Js and its suppliers do not warrant or guarantee that these
|
||||
# samples are accurate and suitable for your purposes. Their inclusion is
|
||||
# purely for information purposes only.
|
||||
|
||||
MODULES =\
|
||||
MsgBox.42m\
|
||||
CityList.42m\
|
||||
CityList.42f\
|
||||
OrderInput.42m\
|
||||
CustOrders.42m\
|
||||
CustOrders2.42m\
|
||||
CustOrders3.42m
|
||||
|
||||
FORMS = \
|
||||
CustOrders.42f\
|
||||
CustOrders2.42f\
|
||||
CustOrders3.42f\
|
||||
Orders.42f\
|
||||
ItemList.42f
|
||||
|
||||
include ../../Makeincl
|
||||
@@ -0,0 +1,36 @@
|
||||
# Property of Four Js*
|
||||
# (c) Copyright Four Js 1995, 2019. All Rights Reserved.
|
||||
# * Trademark of Four Js Development Tools Europe Ltd
|
||||
# in the United States and elsewhere
|
||||
#
|
||||
# Four Js and its suppliers do not warrant or guarantee that these
|
||||
# samples are accurate and suitable for your purposes. Their inclusion is
|
||||
# purely for information purposes only.
|
||||
|
||||
FUNCTION mbox_ok(t, m, i)
|
||||
DEFINE t,m,i STRING
|
||||
MENU t ATTRIBUTES(STYLE="dialog",COMMENT=m,IMAGE=i)
|
||||
COMMAND "Ok" EXIT MENU
|
||||
END MENU
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION mbox_yn(t, m, i)
|
||||
DEFINE t,m,i STRING
|
||||
DEFINE r INTEGER
|
||||
MENU t ATTRIBUTES(STYLE="dialog",COMMENT=m,IMAGE=i)
|
||||
COMMAND "Yes" LET r = TRUE EXIT MENU
|
||||
COMMAND "No" LET r = FALSE EXIT MENU
|
||||
END MENU
|
||||
RETURN r
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION mbox_ync(t, m, i)
|
||||
DEFINE t,m,i STRING
|
||||
DEFINE r INTEGER
|
||||
MENU t ATTRIBUTES(STYLE="dialog",COMMENT=m,IMAGE=i)
|
||||
COMMAND "Yes" LET r = 1 EXIT MENU
|
||||
COMMAND "No" LET r = 2 EXIT MENU
|
||||
COMMAND "Cancel" LET r = 3 EXIT MENU
|
||||
END MENU
|
||||
RETURN r
|
||||
END FUNCTION
|
||||
@@ -0,0 +1,186 @@
|
||||
# Property of Four Js*
|
||||
# (c) Copyright Four Js 1995, 2019. All Rights Reserved.
|
||||
# * Trademark of Four Js Development Tools Europe Ltd
|
||||
# in the United States and elsewhere
|
||||
#
|
||||
# Four Js and its suppliers do not warrant or guarantee that these
|
||||
# samples are accurate and suitable for your purposes. Their inclusion is
|
||||
# purely for information purposes only.
|
||||
|
||||
IMPORT FGL MsgBox
|
||||
GLOBALS "Globals.4gl"
|
||||
|
||||
DEFINE items DYNAMIC ARRAY OF RECORD
|
||||
item_line INTEGER,
|
||||
item_name VARCHAR(50),
|
||||
item_quant DECIMAL(8,2),
|
||||
item_price MONEY(10,2),
|
||||
item_total MONEY(15,2),
|
||||
item_id INTEGER
|
||||
END RECORD
|
||||
|
||||
CONSTANT mytitle = "Orders"
|
||||
|
||||
FUNCTION ord_input(mode, cust_id, cust_name, ord_id)
|
||||
DEFINE mode CHAR(1)
|
||||
DEFINE cust_id INTEGER
|
||||
DEFINE cust_name VARCHAR(50)
|
||||
DEFINE ord_id INTEGER
|
||||
DEFINE i, id, s,r INTEGER
|
||||
DEFINE name STRING
|
||||
DEFINE price DECIMAL(10,2)
|
||||
DEFINE touched,reorder INT
|
||||
|
||||
OPEN WINDOW w_ord WITH FORM "Orders"
|
||||
|
||||
CALL items.clear()
|
||||
IF mode == 'M' THEN
|
||||
-- Simulate database fetch
|
||||
LET ordrec.ord_id = ord_id
|
||||
LET ordrec.ord_daddr = "5, Market Street"
|
||||
LET ordrec.ord_crea = TODAY - ord_id
|
||||
LET ordrec.ord_deliv = ordrec.ord_crea + 3
|
||||
LET ordrec.ord_valid = "n"
|
||||
-- Some item lines...
|
||||
LET i=0
|
||||
LET items[i:=i+1].item_line = i
|
||||
LET items[1].item_id = 2
|
||||
LET items[i].item_name = "Oranges (1Kg)"
|
||||
LET items[i].item_quant = 2
|
||||
LET items[i].item_price = 4.65
|
||||
LET items[i].item_total = items[i].item_quant * items[i].item_price
|
||||
LET items[i:=i+1].item_line = i
|
||||
LET items[1].item_id = 1
|
||||
LET items[i].item_name = "Apples (1Kg)"
|
||||
LET items[i].item_quant = 3
|
||||
LET items[i].item_price = 3.45
|
||||
LET items[i].item_total = items[i].item_quant * items[i].item_price
|
||||
LET ordrec.ord_amount = 0
|
||||
FOR i=1 TO items.getLength()
|
||||
LET ordrec.ord_amount = ordrec.ord_amount + items[i].item_total
|
||||
END FOR
|
||||
END IF
|
||||
|
||||
DIALOG ATTRIBUTES(FIELD ORDER FORM, UNBUFFERED)
|
||||
|
||||
INPUT BY NAME ordrec.* ATTRIBUTE(WITHOUT DEFAULTS=(mode=='M'))
|
||||
END INPUT
|
||||
|
||||
INPUT ARRAY items FROM itemlist.* ATTRIBUTE(WITHOUT DEFAULTS)
|
||||
|
||||
BEFORE INSERT
|
||||
CALL orditem_reorder(DIALOG)
|
||||
AFTER INSERT
|
||||
LET reorder=1
|
||||
|
||||
AFTER DELETE
|
||||
LET reorder=1
|
||||
|
||||
AFTER ROW
|
||||
IF FIELD_TOUCHED(itemlist.*) THEN
|
||||
LET touched=TRUE
|
||||
END IF
|
||||
IF reorder THEN
|
||||
CALL orditem_reorder(DIALOG)
|
||||
LET reorder=0
|
||||
END IF
|
||||
|
||||
ON CHANGE item_quant
|
||||
CALL orditem_total(DIALOG, arr_curr())
|
||||
ON ACTION zoom_item
|
||||
LET r=DIALOG.getCurrentRow("itemlist")
|
||||
CALL select_item(DIALOG.getFieldBuffer("item_name")) RETURNING s, id, name, price
|
||||
IF s==0 THEN
|
||||
LET items[r].item_id = id
|
||||
LET items[r].item_name = name
|
||||
LET items[r].item_price = price
|
||||
CALL orditem_total(DIALOG, r)
|
||||
CALL DIALOG.setFieldTouched("item_name",TRUE)
|
||||
END IF
|
||||
END INPUT
|
||||
|
||||
BEFORE DIALOG
|
||||
DISPLAY BY NAME cust_id, cust_name
|
||||
--init some items in the new record after the defaults have been filled in
|
||||
IF mode='A' THEN
|
||||
LET ordrec.ord_deliv = TODAY+1
|
||||
END IF
|
||||
|
||||
ON ACTION accept
|
||||
ACCEPT DIALOG
|
||||
|
||||
ON ACTION cancel
|
||||
IF touched OR FIELD_TOUCHED(ordrec.*) OR
|
||||
FIELD_TOUCHED(itemlist.*) THEN
|
||||
IF NOT mbox_yn(mytitle,"Close window without saving input?","question") THEN
|
||||
CONTINUE DIALOG
|
||||
END IF
|
||||
END IF
|
||||
EXIT DIALOG
|
||||
|
||||
AFTER DIALOG
|
||||
-- Save data in database.
|
||||
DISPLAY "save data"
|
||||
|
||||
END DIALOG
|
||||
|
||||
CLOSE WINDOW w_ord
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION orditem_reorder(d)
|
||||
DEFINE d ui.Dialog
|
||||
DEFINE i INTEGER
|
||||
FOR i=1 TO d.getArrayLength("itemlist")
|
||||
LET items[i].item_line = i
|
||||
END FOR
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION orditem_total(d, r)
|
||||
DEFINE d ui.Dialog
|
||||
DEFINE r, i INTEGER
|
||||
|
||||
LET items[r].item_total = items[r].item_quant * items[r].item_price
|
||||
LET ordrec.ord_amount = 0
|
||||
FOR i=1 TO d.getArrayLength("itemlist")
|
||||
LET ordrec.ord_amount = ordrec.ord_amount + items[i].item_total
|
||||
END FOR
|
||||
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION select_item(filter)
|
||||
DEFINE filter STRING
|
||||
DEFINE i INTEGER
|
||||
DEFINE itemarr DYNAMIC ARRAY OF RECORD
|
||||
item_id INTEGER,
|
||||
item_name VARCHAR(50),
|
||||
item_price MONEY(10,2)
|
||||
END RECORD
|
||||
|
||||
OPEN WINDOW w_item WITH FORM "ItemList"
|
||||
|
||||
LET i = 0
|
||||
LET itemarr[i:=i+1].item_id = i LET itemarr[i].item_name = "Apples (1Kg)" LET itemarr[i].item_price = 4.25
|
||||
LET itemarr[i:=i+1].item_id = i LET itemarr[i].item_name = "Oranges (1Kg)" LET itemarr[i].item_price = 5.35
|
||||
LET itemarr[i:=i+1].item_id = i LET itemarr[i].item_name = "Lemons (1Kg)" LET itemarr[i].item_price = 4.95
|
||||
LET itemarr[i:=i+1].item_id = i LET itemarr[i].item_name = "Melons (unit)" LET itemarr[i].item_price = 1.55
|
||||
LET itemarr[i:=i+1].item_id = i LET itemarr[i].item_name = "Peanuts (1Kg)" LET itemarr[i].item_price = 2.25
|
||||
|
||||
DISPLAY ARRAY itemarr TO sr.*
|
||||
AFTER DISPLAY
|
||||
IF NOT int_flag THEN
|
||||
LET i = arr_curr()
|
||||
ELSE
|
||||
LET int_flag = FALSE
|
||||
LET i = -1
|
||||
END IF
|
||||
END DISPLAY
|
||||
|
||||
CLOSE WINDOW w_item
|
||||
|
||||
IF i > 0 THEN
|
||||
RETURN 0, itemarr[i].item_id, itemarr[i].item_name, itemarr[i].item_price
|
||||
ELSE
|
||||
RETURN -1, NULL, NULL, NULL
|
||||
END IF
|
||||
|
||||
END FUNCTION
|
||||
@@ -0,0 +1,86 @@
|
||||
# Property of Four Js*
|
||||
# (c) Copyright Four Js 1995, 2019. All Rights Reserved.
|
||||
# * Trademark of Four Js Development Tools Europe Ltd
|
||||
# in the United States and elsewhere
|
||||
#
|
||||
# Four Js and its suppliers do not warrant or guarantee that these
|
||||
# samples are accurate and suitable for your purposes. Their inclusion is
|
||||
# purely for information purposes only.
|
||||
|
||||
ACTION DEFAULTS
|
||||
ACTION insert ( IMAGE = "listins", COMMENT="Insert a new item line")
|
||||
ACTION append ( IMAGE = "listadd", COMMENT="Append a new item line")
|
||||
ACTION delete ( IMAGE = "listdel", COMMENT="Delete current item line", VALIDATE=NO)
|
||||
ACTION validate ( TEXT = "Validate", COMMENT="Validate the order", ACCELERATOR=Control-V)
|
||||
END
|
||||
|
||||
LAYOUT ( STYLE="dialog4", TEXT = "Order" )
|
||||
GRID
|
||||
{
|
||||
<g g1 >
|
||||
Id: [f1 ] Name: [f2 ]
|
||||
< >
|
||||
<g g2 >
|
||||
Id: [o1 ] Crea: [o3 ]
|
||||
Delivery address: [o2 ]
|
||||
Delivery date: [o4 ][o5 ]
|
||||
< >
|
||||
<g g3 >
|
||||
<t t1 >[iad]
|
||||
[c1 |c2 |c3 |c4 |c5 |c6][imo]
|
||||
[c1 |c2 |c3 |c4 |c5 |c6][ide]
|
||||
[c1 |c2 |c3 |c4 |c5 |c6]
|
||||
[c1 |c2 |c3 |c4 |c5 |c6]
|
||||
[c1 |c2 |c3 |c4 |c5 |c6]
|
||||
[c1 |c2 |c3 |c4 |c5 |c6]
|
||||
[c1 |c2 |c3 |c4 |c5 |c6]
|
||||
< >
|
||||
< >
|
||||
<g g4 >
|
||||
[ :o6 ]
|
||||
< >
|
||||
}
|
||||
END
|
||||
END
|
||||
|
||||
ATTRIBUTES
|
||||
|
||||
GROUP g1: TEXT = "Customer information";
|
||||
EDIT f1 = FORMONLY.cust_id TYPE INTEGER, NOENTRY;
|
||||
EDIT f2 = FORMONLY.cust_name TYPE VARCHAR, NOENTRY;
|
||||
|
||||
GROUP g2: TEXT = "Order header";
|
||||
EDIT o1 = FORMONLY.ord_id, NOENTRY;
|
||||
EDIT o2 = FORMONLY.ord_daddr TYPE VARCHAR, NOT NULL, REQUIRED;
|
||||
EDIT o3 = FORMONLY.ord_crea TYPE DATE, NOENTRY,DEFAULT=TODAY;
|
||||
DATEEDIT o4 = FORMONLY.ord_deliv TYPE DATE;
|
||||
|
||||
GROUP g3: TEXT = "Order items";
|
||||
TABLE t1: DOUBLECLICK=none;
|
||||
LABEL c1 = FORMONLY.item_line TYPE INTEGER, TITLE = "Pos";
|
||||
BUTTONEDIT c2 = FORMONLY.item_name TYPE VARCHAR, TITLE = "Item name",
|
||||
SCROLL, NOT NULL, REQUIRED, ACTION=zoom_item, IMAGE="find";
|
||||
EDIT c3 = FORMONLY.item_quant TYPE DECIMAL(5,2), TITLE = "Quantity",
|
||||
SCROLL, NOT NULL, REQUIRED, INCLUDE=(0.0001 TO 10000),DEFAULT=1;
|
||||
LABEL c4 = FORMONLY.item_price TYPE MONEY(10,2), TITLE = "Unit price";
|
||||
LABEL c5 = FORMONLY.item_total TYPE MONEY(15,2), TITLE = "Total";
|
||||
LABEL c6 = FORMONLY.item_id TYPE INTEGER, TITLE = "Item id", HIDDEN=USER;
|
||||
|
||||
BUTTON iad: itemlist.insert;
|
||||
BUTTON imo: itemlist.append;
|
||||
BUTTON ide: itemlist.delete;
|
||||
|
||||
GROUP g4: TEXT = "Order amount", GRIDCHILDRENINPARENT;
|
||||
CHECKBOX o5 = FORMONLY.ord_valid TYPE CHAR, NOT NULL, REQUIRED,
|
||||
TEXT="Validated", VALUECHECKED="y", VALUEUNCHECKED="n",DEFAULT='n';
|
||||
EDIT o6 = FORMONLY.ord_amount TYPE MONEY(15,2), STYLE="amount", NOENTRY;
|
||||
|
||||
END
|
||||
|
||||
INSTRUCTIONS
|
||||
SCREEN RECORD custrec (FORMONLY.cust_id, FORMONLY.cust_name);
|
||||
SCREEN RECORD ordrec (FORMONLY.ord_id, FORMONLY.ord_daddr,
|
||||
FORMONLY.ord_crea, FORMONLY.ord_deliv,
|
||||
FORMONLY.ord_valid, FORMONLY.ord_amount);
|
||||
SCREEN RECORD itemlist (FORMONLY.item_line THROUGH FORMONLY.item_id);
|
||||
END
|
||||
@@ -0,0 +1,104 @@
|
||||
Input multiple master records + details
|
||||
---------------------------------------
|
||||
|
||||
This directory contains some examples to perform the following task:
|
||||
a master and a detail table should be edited simultaneously.
|
||||
(a customer record acts as the master record and an orders table contains the details)
|
||||
It should be possible to have forward/backward actions to advance to the next physical record for the master in the database and update the details display/input as well.
|
||||
|
||||
This is something pretty new in 4GL, this was not possible before.
|
||||
Particularly, the INPUT of the master by staying visually 'in the dialog' was only possible before by using an INPUT ARRAY on a single record (which is still a good idea as shown later on).
|
||||
|
||||
3 DIALOG samples try to achieve this: CustOrders.4gl, CustOrders2.4gl, and CustOrders3.4gl
|
||||
CustOrders.4gl and CustOrders2.4gl have the same user interface but show
|
||||
different methods for vaidation of the record data.
|
||||
Both use an INPUT for the master data and a DISPLAY ARRAY for the details.
|
||||
|
||||
CustOrders3.4gl has a more simple user interface (no confirm for the row and no save button) and shows how an INPUT ARRAY on a single row could be used to achieve basically the same result as in CustOrders.4gl and CustOrders2.4gl (with other limitations however).
|
||||
|
||||
OrderInput.4gl shows the INPUT of a single master record + details in an INPUT ARRAY (FUNCTION ord_input) and is common to all CustOrders*.4gl samples.
|
||||
In the AFTER DIALOG of ord_input there is only a small DISPLAY "save data", which is of course a bigger piece of code to write a complete order to the DB.
|
||||
A real database demo with a similar DIALOG is shown in $FGLDIR/demo/MultipleDialogs/StoresMD/d4_orders.4gl (FUNCTION order_modify)
|
||||
|
||||
A central problem in this demo is validation:
|
||||
When the current customer record is modified, clicking on the previous/next/append buttons should cause the validation of the current record. That means the built-in constraints for the fields must be run thru, as well as the AFTER FIELD of the current field, and the code that is normally in AFTER INPUT to perform checks that cannot be done in AFTER FIELD (due to multiple field dependencies).
|
||||
In the first 2 samples code is written to achieve that, in the 3rd the INPUT ARRAY with it's built-in triggers does a lot of the work.
|
||||
|
||||
|
||||
CustOrders.4gl
|
||||
--------------
|
||||
Implements all customer record validation by 'staying in the dialog' and performs explicit field validation for the user programmed actions.
|
||||
Each action that require a validation, like going to the next customer or invoking a search, must validate the current INPUT.
|
||||
Further, those actions must perform the same checking as the built-in 'accept' action of a traditional INFORMIX INPUT instruction:
|
||||
|
||||
v1:verify if the field constraints are met (achieved by calling the new DIALOG.validate() method).
|
||||
|
||||
v2:call the code that is called in the AFTER FIELD trigger of the field you are currently in.
|
||||
|
||||
v3:call the code that is called in the AFTER INPUT trigger.
|
||||
|
||||
This is achieved by calling the cust_check_changed() function, which checks the validity in cust_save() by calling cust_check_fields()(calling cust_check_data(), then performing DIALOG.validate() + checking all user programmed field constraints)
|
||||
The user programmed field constraints are the same in each sample:
|
||||
1. The customer's address is checked when entered
|
||||
2. A city cannot be entered without entering a zip code
|
||||
|
||||
Advantages: 1. you never have to leave the DIALOG instruction (in comparison to sample2)
|
||||
|
||||
Disadvantages:1. need to perform self made (explicite) validation for each of the standard actions
|
||||
2. a lot of boilerplate code needed to achieve the standard
|
||||
first/next/previous/last/append/delete actions (in comparison to sample3)
|
||||
3. the WITHOUT DEFAULTS flag for the INPUT is not usable (see in ../ListAndEditableDetail/README what this means)
|
||||
|
||||
CustOrders2.4gl
|
||||
---------------
|
||||
- implents all customer record validations by using implicit validation: calling ACCEPT DIALOG
|
||||
That means that each action which requires validation calls ACCEPT DIALOG.
|
||||
This lets the DIALOG run thru all 3 validation stages and causes a falling out of the current DIALOG instruction if the validation was succesful.
|
||||
That means also that one has to maintain a state variable to reenter the DIALOG in a WHILE loop around the DIALOG (in addition to maintain a variable to distinguish between new record or record modification).
|
||||
The main advantage using that technology is that all you AFTER FIELD constraints can be left in 1 place, it's not needed to use a function for a simple constraint and not needed to duplicate the call in your private validating function compared to sample 1 (see for example the AFTER FIELD cust_name checking) .
|
||||
|
||||
Advantages: 1. you only need to check the field constraints at the usual places:
|
||||
AFTER FIELD and AFTER INPUT and not in each action
|
||||
2. the WITHOUT DEFAULTS flag for the INPUT is still working like intended (see in ../ListAndEditableDetail/README what this means)
|
||||
|
||||
Disadvantages: 1. need to write a WHILE loop with a state variable
|
||||
2. a lot of boilerplate code needed to achieve the standard
|
||||
first/next/previous/last/append/delete actions (in comparison to sample3)
|
||||
|
||||
CustOrders3.4gl
|
||||
---------------
|
||||
-uses an INPUT ARRAY in contradiction to the previous samples where an INPUT was used for the master record
|
||||
-shows that the standard next/previous/append/delete actions are already builtin into INPUT ARRAY and usable nearly like intended
|
||||
-implements an MS Access behavior: going to the next/prevoius/first/last row saves the current record to the database without confirmation. If one wants to throw away the changes in the current record, he must press 'Undo'
|
||||
-shows how the firstrow/prevrow/nextrow/lastrow/append/delete actions can be put into the toolbar and work depending on the context .
|
||||
That means: if the focus is in INPUT ARRAY-> toolbar buttons act on the INPUT ARRAY
|
||||
if the focus is in the DISPLAY ARRAY-> toolbar buttons act on the DISPLAY ARRAY
|
||||
This is similar to how the toolbar is working in MS Access.
|
||||
(Please note that in earlier versions the single row INPUT ARRAY had only prevrow/nextrow actions)
|
||||
A further goodie is that INPUT ARRAY/DISPLAY ARRAY now manage the activation of firstrow/prevrow/nextrow/lastrow automatically: no need to call setActionActive by yourself.
|
||||
|
||||
Advantages: 1. *a lot* shorter code than in the 2 previous samples because the
|
||||
next/previous/append/delete mechanisms are already foreseen in INPUT ARRAY
|
||||
|
||||
2. you only need to check the field constraints at the usual places:
|
||||
AFTER FIELD and AFTER ROW (which comparable to the AFTER INPUT trigger of the single record INPUT) and not in each action
|
||||
|
||||
Disadvantages: 1. Pressing 'Tab' at the last customer record field moves to the next customer record instead of jumoing to the DISPLAY ARRAY
|
||||
|
||||
2. you need to operate on an array which may be inadequate for a large number of database items
|
||||
|
||||
CustOrders3.4gl sounds promising but at the moment it's impossible to implement confirmation for the new record (which is not absolutely necessary like MS Access shows).
|
||||
It's still the usual INPUT ARRAY to input an array and not much foreseen to be synchronized with the data base. However it's intended to be improved in one of the next releases. Then it very likely includes built-in undo support as well as built-in support for synchronizing a row with the database.
|
||||
The navigation is probably also not what the end user expects, pressing the 'Down' arrow moves to the next record.
|
||||
Pressing 'Tab' at the end of the record advances also to the next record (which is fine if the INPUT ARRAY runs in a table but not expected for the single record case).
|
||||
This could be enhanced by giving the INPUT ARRAY a hint that it should make the navigation different in the single record case.
|
||||
|
||||
Conclusion:
|
||||
----------
|
||||
The first 2 samples show that it's at least possible to achieve the wanted behavior however with a pretty large overhead for implementing the standard actions.
|
||||
In both cases you need to be careful about the validations for the standard actions and need to add extra code to achieve this.
|
||||
Both samples lack easy readability because of all the necessary validation stuff which would be an easy game using an INPUT ARRAY.
|
||||
|
||||
Sample 3 shows that you can have much less code using INPUT ARRAY, but you still need to know well the pitfalls of how to synchronize the INPUT ARRAY with the database and you will have to live with the limitations (navigation a bit different from expectation)
|
||||
|
||||
If this kind of dialog is not a top priority for you, you better wait for an enhanced INPUT ARRAY which has built-in support to sync with the database and enhanced navigation for the single record case to make programming this a joy.
|
||||
Reference in New Issue
Block a user