Files
MBS/TEST/MultipleDialogs/MultiMasterDetail/CustOrders2.4gl
T

433 lines
14 KiB
Plaintext

# 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