O4W Sample Stored Procedures
O4W_EXAMPLE0
- O4W_EXAMPLE0
Subroutine O4W_EXAMPLE0(CtlEntId, Event, Request) * Example #0: A simple entry/collection form * * Always include our equates $Insert O4WEquates * * Determine how we were called, and what to do * Begin Case Case event _eqc "CREATE" * Creation event - called the first time through for each application * Specify that this will generate a full web page O4WForm() * Put up the 'header' describing the page O4WHeader("Revelation Software POS (Pizza Ordering System)", 4) * "Space down" a few blank lines O4WBreak() O4WBreak() * Use a table to align everything neatly O4WTableStart("mainTable") * Display a prompt in the first 'cell' of the table O4WSetCell(1,1) O4WText("Delivery Address: ") * And put up a 'textbox' for the response in the next 'cell' O4WSetCell(1,2) O4WTextbox('','' ,'' , 'ADD1') * Repeat for the other desired information * Notice the shortcut: only specifying the 'row number' to O4WSetCell automatically puts you in the first column of that row O4WSetCell(2) O4WText("Address 2:") * Notice the shortcut: if neither the row nor column is specified in O4WSetCell, it automatically puts you in the next column of the current row O4WSetCell() O4WTextbox('','','','ADD2') O4WSetCell(3) O4WText("City:") O4WSetCell() O4WTextBox('','','','CITY') O4WSetCell(4) O4WText("State:") O4WSetCell() O4WTextBox('','','','STATE') O4WSetCell(5) O4WText("Zip:") O4WSetCell() O4WTextBox('','','','ZIP') O4WSetCell(6) O4WText("Pizza Size:") O4WSetCell() * Display a list of choices as radio buttons (allowing only a single choice) O4WRadioButton("Small", "S", "SIZE") O4WRadioButton("Medium", "M", "SIZE") O4WRadioButton("Large", "L", "SIZE") O4WSetCell(7) O4WText("Toppings:") O4WSetCell() * Display a list of toppings as checkboxes (allowing multiple choices) O4WCheckBox("Pepperoni", "P", "TOPPINGS") O4WBreak() O4WCheckBox("Sausage", "S", "TOPPINGS") O4WBreak() O4WCheckBox("Extra Cheese", "C", "TOPPINGS") O4WBreak() O4WCheckBox("Mushrooms", "M", "TOPPINGS") * End the table O4WTableEnd("mainTable") O4WBreak() * Display a button for them to submit the order O4WButton("Order!", "BTNORDER") * Tell O4W you wish to be notified when the user clicks on this button O4WQualifyEvent("BTNORDER", "CLICK") Case event _eqc "CLICK" * Click event - called when the button is pressed * Read in all the values ADD1 = O4WGetValue("ADD1") ADD2 = O4WGetValue("ADD2") CITY = O4WGetValue("CITY") STATE = O4WGetValue("STATE") ZIP = O4WGetValue("ZIP") * 'SIZE' will return the S/M/L code from the SIZE radio buttons SIZE = O4WGetValue("SIZE") * 'TOPPINGS' will return an @VM-delimited list of the selected toppings TOPPING = O4WGetValue("TOPPINGS") * Calculate the price from the toppings and size selected cost = 0 Begin Case Case SIZE = "S" cost = 700 Case SIZE = "M" cost = 1000 Case SIZE = "L" cost = 1300 Case 1 * Not a valid size - return an error O4WError("We're sorry, but the size '":size:"' doesn't appear to be valid!") Return End Case * Now find each of the toppings num.toppings = dcount(topping, @VM) For each.topping = 1 To num.toppings this.topping = topping<1,each.topping> Begin Case Case THIS.TOPPING = "P" cost += 100 Case THIS.TOPPING = "S" cost += 100 Case THIS.TOPPING = "C" cost += 50 Case THIS.TOPPING = "M" cost += 100 Case 1 * Not a valid topping? Report an error O4WError("We're sorry, but the topping '":this.topping:"' doesn't appear to be valid!") Return End Case Next each.topping * In a 'real' application, you'd now write all the information we collected into a database somewhere orderNum = DATE():"*":TIME() * <WRITE INFORMATION HERE> * Notify the customer that his order has been received, and give him/her the total O4WError('Thank you for your order! Your order number is ':orderNum:'. Your total is ':OCONV(cost,'MD2,$'):'.', 'Thank You') End Case * Return 0
O4W_EXAMPLE1
- O4W_EXAMPLE1
Subroutine O4W_EXAMPLE1(CtlEntId, Event, Request) * Example #1: A simple entry/collection form * * example 1: establish some constants we'll use in the example Equ SizeList$ To "Small,Medium,Large" Equ SizeCode$ To "S,M,L" Equ SizeCost$ To "700,1000,1300" * Equ ToppingList$ To "Pepperoni,Sausage,Extra Cheese,Mushrooms" Equ ToppingCode$ To "P,S,C,M" Equ ToppingCost$ To "100,100,50,100" * * Always include our equates $Insert O4WEquates * * Determine how we were called, and what to do * Begin Case Case event _eqc "CREATE" * Creation event - called the first time through for each application * Specify that this will generate a full web page O4WForm() * example 1: Define the section that the form will go in O4WSectionStart('mainSection') * Put up the 'header' describing the page O4WHeader("Revelation Software POS (Pizza Ordering System)", 4) * "Space down" a few blank lines O4WBreak() O4WBreak() * Use a table to align everything neatly O4WTableStart("mainTable") * Display a prompt in the first 'cell' of the table O4WSetCell(1,1) O4WText("Delivery Address: ") * And put up a 'textbox' for the response in the next 'cell' O4WSetCell(1,2) O4WTextbox('','' ,'' , 'ADD1') * Repeat for the other desired information * Notice the shortcut: only specifying the 'row number' to O4WSetCell automatically puts you in the first column of that row O4WSetCell(2) O4WText("Address 2:") * Notice the shortcut: if neither the row nor column is specified in O4WSetCell, it automatically puts you in the next column of the current row O4WSetCell() O4WTextbox('','','','ADD2') O4WSetCell(3) O4WText("City:") O4WSetCell() O4WTextBox('','','','CITY') O4WSetCell(4) O4WText("State:") O4WSetCell() O4WTextBox('','','','STATE') O4WSetCell(5) O4WText("Zip:") O4WSetCell() O4WTextBox('','','','ZIP') O4WSetCell(6) O4WText("Pizza Size:") O4WSetCell() * Display a list of choices as radio buttons (allowing only a single choice) * example 1: Instead of hard-coding the choices... /* O4WRadioButton("Small", "S", "SIZE") O4WRadioButton("Medium", "M", "SIZE") O4WRadioButton("Large", "L", "SIZE") */ *...it's better to drive this from the constants we declared at the top (so we can change them without changing the program logic) NUM.SIZES = DCount(SizeList$, ",") For each.size = 1 To num.sizes this.size = Field(SizeList$, ",", each.size) this.code = Field(SizeCode$, ",", each.size) O4WRadioButton(this.size, this.code, "SIZE") Next each.size O4WSetCell(7) O4WText("Toppings:") O4WSetCell() * Display a list of toppings as checkboxes (allowing multiple choices) * example 1: instead of hard-coding the choices... /* O4WCheckBox("Pepperoni", "P", "TOPPINGS") O4WBreak() O4WCheckBox("Sausage", "S", "TOPPINGS") O4WBreak() O4WCheckBox("Extra Cheese", "C", "TOPPINGS") O4WBreak() O4WCheckBox("Mushrooms", "M", "TOPPINGS") */ *...it's better to drive this from the constants we declared at the top (so we can change them without changing the program logic) NUM.TOPPINGS = DCount(ToppingList$, ",") For each.topping = 1 To num.toppings this.topping = Field(ToppingList$, ",", each.topping) this.code = Field(ToppingCode$, ",", each.topping) O4WCheckBox(this.topping, this.code, "TOPPINGS") O4WBreak() Next each.topping * End the table O4WTableEnd("mainTable") O4WBreak() * Display a button for them to submit the order O4WButton("Order!", "BTNORDER") * Tell O4W you wish to be notified when the user clicks on this button O4WQualifyEvent("BTNORDER", "CLICK") * example 1: finish up the main section we defined O4WSectionEnd('mainSection') * example 1: create a special area for us to use when displaying some response to the user * nothing goes in it now, but we may fill it in later O4WSectionStart('responseArea') O4WSectionEnd('responseArea') Case event _eqc "CLICK" * Click event - called when the button is pressed * example 1: Tell O4W we'll be generating a response updating the current form (as opposed to making a whole new form) O4WResponse() * Read in all the values ADD1 = O4WGetValue("ADD1") ADD2 = O4WGetValue("ADD2") CITY = O4WGetValue("CITY") STATE = O4WGetValue("STATE") ZIP = O4WGetValue("ZIP") * 'SIZE' will return the S/M/L code from the SIZE radio buttons SIZE = O4WGetValue("SIZE") * 'TOPPINGS' will return an @VM-delimited list of the selected toppings TOPPING = O4WGetValue("TOPPINGS") * example 1: calculate the price from the toppings and size selected, using the equated list locate size In SizeCode$ Using "," Setting pos Then cost = Field(SizeCost$,",",pos) End Else * Couldn't find the size in the list? Report an error to the customer O4WError("We're sorry, but the size '":size:"' doesn't appear to be valid!") Return End * example 1: Now find each of the toppings in the equated list num.toppings = dcount(topping, @VM) For each.topping = 1 To num.toppings this.topping = topping<1,each.topping> Locate this.topping In ToppingCode$ Using "," Setting pos Then cost += Field(ToppingCost$, ",", pos) End Else * Couldn't find the topping in the list? Report an error O4WError("We're sorry, but the topping '":this.topping:"' doesn't appear to be valid!") End Next each.topping * In a 'real' application, you'd now write all the information we collected into a database somewhere orderNum = DATE():"*":TIME() * <WRITE INFORMATION HERE> * Notify the customer that his order has been received, and give him/her the total * example 1: We'll put this information into our 'responseArea' * Note that we use a special style so that O4W knows we're updating the previously-defined area (and not asking to create a new one) O4WSectionStart('responseArea', O4WResponseStyle()) O4WHeader('Your order number is ':orderNum, 4) O4WBreak() O4WText('Thank you for your order! Your order total is ':OCONV(cost,'MD2,$'):'.') O4WBreak() O4WText('Please press ') * Build a 'link' to return to the home page O4WLink('here', O4W_LINKTYPE_NORMAL$, 'www.revelation.com') O4WText(' to return to the home page') O4WSectionEnd('responseArea') * Display this area as a 'dialog box' to the user O4WDialog('responseArea', 'Thank You') End Case * Return 0
O4W_EXAMPLE2
- O4W_EXAMPLE2
Subroutine O4W_EXAMPLE2(CtlEntId, Event, Request) * Example #2: A simple entry/collection form with some dynamic updates * * Establish some constants we'll use in the example Equ SizeList$ To "Small,Medium,Large" Equ SizeCode$ To "S,M,L" Equ SizeCost$ To "700,1000,1300" * Equ ToppingList$ To "Pepperoni,Sausage,Extra Cheese,Mushrooms" Equ ToppingCode$ To "P,S,C,M" Equ ToppingCost$ To "100,100,50,100" * * Always include our equates $Insert O4WEquates * * Determine how we were called, and what to do * Begin Case Case event _eqc "CREATE" * Creation event - called the first time through for each application * Specify that this will generate a full web page O4WForm() * Define the section that the form will go in O4WSectionStart('mainSection') * Put up the 'header' describing the page O4WHeader("Revelation Software POS (Pizza Ordering System)", 4) * "Space down" a few blank lines O4WBreak() O4WBreak() * Use a table to align everything neatly O4WTableStart("mainTable") * Display a prompt in the first 'cell' of the table O4WSetCell(1,1) O4WText("Delivery Address: ") * And put up a 'textbox' for the response in the next 'cell' O4WSetCell(1,2) O4WTextbox('','' ,'' , 'ADD1') * Repeat for the other desired information * Notice the shortcut: only specifying the 'row number' to O4WSetCell automatically puts you in the first column of that row O4WSetCell(2) O4WText("Address 2:") * Notice the shortcut: if neither the row nor column is specified in O4WSetCell, it automatically puts you in the next column of the current row O4WSetCell() O4WTextbox('','','','ADD2') O4WSetCell(3) O4WText("City:") O4WSetCell() O4WTextBox('','','','CITY') O4WSetCell(4) O4WText("State:") O4WSetCell() * O4WTextBox('','','','STATE') * example 2: Use a combo box for the state * O4W provides a standard 'states' code record in the standard "configuration" file stateInfo = Xlate("O4WCODES", "CODES_STATES", "", "X") num.states = dcount(stateInfo<1>, @VM) * Define a 'default' value to prompt the user O4WListBox("Select...", "", "STATE") For each.state = 1 To num.states state.name = stateInfo<1,each.state> state.code = stateInfo<2,each.state> O4WListBox(state.name, state.code, "STATE") Next each.state O4WSetCell(5) O4WText("Zip:") O4WSetCell() O4WTextBox('','','','ZIP') O4WSetCell(6) O4WText("Pizza Size:") O4WSetCell() * Display a list of choices as radio buttons (allowing only a single choice) * If we wanted to 'hard code' the choices, we could do this: /* O4WRadioButton("Small", "S", "SIZE") O4WRadioButton("Medium", "M", "SIZE") O4WRadioButton("Large", "L", "SIZE") */ * But it's better to drive this from the constants we declared at the top (so we can change them without changing the program logic) NUM.SIZES = DCount(SizeList$, ",") For each.size = 1 To num.sizes this.size = Field(SizeList$, ",", each.size) this.code = Field(SizeCode$, ",", each.size) *O4WRadioButton(this.size, this.code, "SIZE") * example 2: identify each radio button with a unique name O4WRadioButton(this.size, this.code, "SIZE", "SIZE_":this.code) Next each.size O4WSetCell(7) O4WText("Toppings:") O4WSetCell() * Display a list of toppings as checkboxes (allowing multiple choices) * If we wanted to 'hard code' the choices, we could do this: /* O4WCheckBox("Pepperoni", "Pepperoni", "TOPPINGS") O4WBreak() O4WCheckBox("Sausage", "Sausage", "TOPPINGS") O4WBreak() O4WCheckBox("Extra Cheese", "ExtraCheese", "TOPPINGS") O4WBreak() O4WCheckBox("Mushrooms", "Mushrooms", "TOPPINGS") */ * But it's better to drive this from the constants we declared at the top (so we can change them without changing the program logic) NUM.TOPPINGS = DCount(ToppingList$, ",") For each.topping = 1 To num.toppings this.topping = Field(ToppingList$, ",", each.topping) this.code = Field(ToppingCode$, ",", each.topping) *O4WCheckBox(this.topping, this.code, "TOPPINGS") * example 2: identify each checkbox with a unique name O4WCheckBox(this.topping, this.code, "TOPPINGS", "TOPPINGS_":this.code) O4WBreak() Next each.topping * End the table O4WTableEnd("mainTable") O4WBreak() * example 2: define an area where the 'running total' can go O4WSectionStart('runningTotal') O4WSectionEnd('runningTotal') * Display a button for them to submit the order O4WButton("Order!", "BTNORDER") * Tell O4W you wish to be notified when the user clicks on this button O4WQualifyEvent("BTNORDER", "CLICK") * example 2: Tell O4W we want to be notified when the size or toppings change O4WQualifyEvent("TOPPINGS", "CHANGE", "CHECKBOX") O4WQualifyEvent("SIZE", "CHANGE", "RADIOBUTTON") * finish up the main section we defined O4WSectionEnd('mainSection') * create a special area for us to use when displaying some response to the user * nothing goes in it now, but we may fill it in later O4WSectionStart('responseArea') O4WSectionEnd('responseArea') Case event _eqc "CLICK" * Click event - called when the button is pressed * Tell O4W we'll be generating a response updating the current form (as opposed to making a whole new form) O4WResponse() * Read in all the values ADD1 = O4WGetValue("ADD1") ADD2 = O4WGetValue("ADD2") CITY = O4WGetValue("CITY") STATE = O4WGetValue("STATE") ZIP = O4WGetValue("ZIP") * example 2: Make sure these values have something in them If TRIM(ADD1) = "" Or TRIM(CITY) = "" Or TRIM(STATE) = "" Or TRIM(ZIP)="" Then O4WError("Please be sure to enter complete address information before proceeding") Return End * example 2: move the extraction of the size and topping values, and the calculation of the cost, to a subroutine so we can call it from the 'change' event too /* * 'SIZE' will return the S/M/L code from the SIZE radio buttons SIZE = O4WGetValue("SIZE") * 'TOPPINGS' will return an @VM-delimited list of the selected toppings TOPPING = O4WGetValue("TOPPINGS") * Calculate the price from the toppings and size selected locate size In SizeCode$ Using "," Setting pos Then cost = Field(SizeCost$,",",pos) End Else * Couldn't find the size in the list? Report an error to the customer O4WError("We're sorry, but the size '":size:"' doesn't appear to be valid!") Return End * Now find each of the toppings num.toppings = dcount(topping, @VM) For each.topping = 1 To num.toppings this.topping = topping<1,each.topping> Locate this.topping In ToppingCode$ Using "," Setting pos Then cost += Field(ToppingCost$, ",", pos) End Else * Couldn't find the topping in the list? Report an error O4WError("We're sorry, but the topping '":this.topping:"' doesn't appear to be valid!") End Next each.topping */ Gosub calcTotal If error <> "" Then O4WError(error) Return end * In a 'real' application, you'd now write all the information we collected into a database somewhere orderNum = DATE():"*":TIME() * <WRITE INFORMATION HERE> * Notify the customer that his order has been received, and give him/her the total * We'll put this information into our 'responseArea' * Note that we use a special style so that O4W knows we're updating the previously-defined area (and not asking to create a new one) O4WSectionStart('responseArea', O4WResponseStyle()) O4WHeader('Your order number is ':orderNum, 4) O4WBreak() O4WText('Thank you for your order! Your order total is ':OCONV(cost,'MD2,$'):'.') O4WBreak() O4WText('Please press ') * Build a 'link' to return to the home page O4WLink('here', 0, 'www.revelation.com') O4WText(' to return to the home page') O4WSectionEnd('responseArea') * Display this area as a 'dialog box' to the user O4WDialog('responseArea', 'Thank You') Case event _eqc "CHANGE" * example 2: the 'CHANGE' event is triggered when our toppings or size is changed * Update our total cost cost = 0 * Because of how we organized our form, all the values are passed to us when we get the CHANGE event Gosub calcTotal If error <> "" Then * display any error message instead of our total O4WError(error) Return end * No error - display the total cost * Tell O4W we're generating an update to the current form and not an entire new form O4WResponse() * We'll put this information into our 'runningTotal' * Note that we use a special style so that O4W knows we're updating the previously-defined area (and not asking to create a new one) O4WSectionStart('runningTotal', O4WResponseStyle()) O4WText("Current Order Total: ":Oconv(cost, "MD2,$")) O4WSectionEnd('runningTotal') End Case * Return 0 * * * example 2: calcTotal subroutine will pull in the size and toppings info and calculate the cost * if any errors occur, the variable "error" will be returned with the error text calcTotal: error = "" * 'SIZE' will return the S/M/L code from the SIZE radio buttons SIZE = O4WGetValue("SIZE") * 'TOPPINGS' will return an @VM-delimited list of the selected toppings TOPPING = O4WGetValue("TOPPINGS") * Calculate the price from the toppings and size selected locate size In SizeCode$ Using "," Setting pos Then cost = Field(SizeCost$,",",pos) End Else * Couldn't find the size in the list? Report an error to the customer error = "We're sorry, but the size '":size:"' doesn't appear to be valid!" Return End * Now find each of the toppings num.toppings = dcount(topping, @VM) For each.topping = 1 To num.toppings this.topping = topping<1,each.topping> Locate this.topping In ToppingCode$ Using "," Setting pos Then cost += Field(ToppingCost$, ",", pos) End Else * Couldn't find the topping in the list? Report an error error = "We're sorry, but the topping '":this.topping:"' doesn't appear to be valid!" return End Next each.topping Return
O4W_EXAMPLE3
- O4W_EXAMPLE3
Subroutine O4W_EXAMPLE3(CtlEntId, Event, Request) * Example #3: A simple entry/collection form with some dynamic updates, validation, and images * * Establish some constants we'll use in the example Equ SizeList$ To "Small,Medium,Large" Equ SizeCode$ To "S,M,L" Equ SizeCost$ To "700,1000,1300" * Equ ToppingList$ To "Pepperoni,Sausage,Extra Cheese,Mushrooms" Equ ToppingCode$ To "P,S,C,M" Equ ToppingCost$ To "100,100,50,100" * * example 3: include some images Equ imagePath To "../examples/images/pizza" * Always include our equates $Insert O4WEquates * example 3: define a 'validation' style that we can use requiredStyle = O4WValidateStyle('','required') * * Determine how we were called, and what to do * Begin Case Case event _eqc "CREATE" * Creation event - called the first time through for each application * Specify that this will generate a full web page O4WForm() * Define the section that the form will go in O4WSectionStart('mainSection') * Put up the 'header' describing the page O4WHeader("Revelation Software POS (Pizza Ordering System)", 4) * "Space down" a few blank lines O4WBreak() O4WBreak() * Use a table to align everything neatly O4WTableStart("mainTable") * Display a prompt in the first 'cell' of the table O4WSetCell(1,1) O4WText("Delivery Address: ") * And put up a 'textbox' for the response in the next 'cell' O4WSetCell(1,2) * example 3: add in some validation (note that we now provide an "ID" for the textbox, in addition to the "Name") O4WTextbox('','' ,'' , 'ADD1', "ADD1", requiredStyle) * Repeat for the other desired information * Notice the shortcut: only specifying the 'row number' to O4WSetCell automatically puts you in the first column of that row O4WSetCell(2) O4WText("Address 2:") * Notice the shortcut: if neither the row nor column is specified in O4WSetCell, it automatically puts you in the next column of the current row O4WSetCell() O4WTextbox('','','','ADD2') O4WSetCell(3) O4WText("City:") O4WSetCell() * example 3: add in some validation O4WTextBox('','','','CITY', 'CITY', requiredStyle) O4WSetCell(4) O4WText("State:") O4WSetCell() * O4WTextBox('','','','STATE') * Use a combo box for the state * O4W provides a standard 'states' code record in the standard "configuration" file stateInfo = Xlate("O4WCODES", "CODES_STATES", "", "X") num.states = dcount(stateInfo<1>, @VM) * Define a 'default' value to prompt the user O4WListBox("Select...", "", "STATE") For each.state = 1 To num.states state.name = stateInfo<1,each.state> state.code = stateInfo<2,each.state> O4WListBox(state.name, state.code, "STATE") Next each.state O4WSetCell(5) O4WText("Zip:") O4WSetCell() * example 3: add in another specific validation for zip codes O4WTextBox('','','','ZIP', 'ZIP', O4WValidateStyle('','zip','1')) O4WSetCell(6) O4WText("Pizza Size:") O4WSetCell() * Display a list of choices as radio buttons (allowing only a single choice) * If we wanted to 'hard code' the choices, we could do this: /* O4WRadioButton("Small", "S", "SIZE") O4WRadioButton("Medium", "M", "SIZE") O4WRadioButton("Large", "L", "SIZE") */ * But it's better to drive this from the constants we declared at the top (so we can change them without changing the program logic) NUM.SIZES = DCount(SizeList$, ",") For each.size = 1 To num.sizes this.size = Field(SizeList$, ",", each.size) this.code = Field(SizeCode$, ",", each.size) *O4WRadioButton(this.size, this.code, "SIZE") * identify each radio button with a unique name O4WRadioButton(this.size, this.code, "SIZE", "SIZE_":this.code) Next each.size * example 3: add another cell to contain the image of the pizza O4WSetCell() * example 3: for now, put up the generic cheese image O4WImage("Mmmm...pizza!", imagePath:"L_.jpg",'','','','imagePizza') O4WSetCell(7) O4WText("Toppings:") O4WSetCell() * Display a list of toppings as checkboxes (allowing multiple choices) * If we wanted to 'hard code' the choices, we could do this: /* O4WCheckBox("Pepperoni", "Pepperoni", "TOPPINGS") O4WBreak() O4WCheckBox("Sausage", "Sausage", "TOPPINGS") O4WBreak() O4WCheckBox("Extra Cheese", "ExtraCheese", "TOPPINGS") O4WBreak() O4WCheckBox("Mushrooms", "Mushrooms", "TOPPINGS") */ * But it's better to drive this from the constants we declared at the top (so we can change them without changing the program logic) NUM.TOPPINGS = DCount(ToppingList$, ",") For each.topping = 1 To num.toppings this.topping = Field(ToppingList$, ",", each.topping) this.code = Field(ToppingCode$, ",", each.topping) *O4WCheckBox(this.topping, this.code, "TOPPINGS") * identify each checkbox with a unique name O4WCheckBox(this.topping, this.code, "TOPPINGS", "TOPPINGS_":this.code) O4WBreak() Next each.topping * End the table O4WTableEnd("mainTable") O4WBreak() * define an area where the 'running total' can go O4WSectionStart('runningTotal') O4WSectionEnd('runningTotal') * Display a button for them to submit the order O4WButton("Order!", "BTNORDER") * Tell O4W you wish to be notified when the user clicks on this button * example 3: we don't just want to click the button - we want to validate our validations too *O4WQualifyEvent("BTNORDER", "CLICK") O4WQualifyEvent("BTNORDER", "VALIDATE") * Tell O4W we want to be notified when the size or toppings change O4WQualifyEvent("TOPPINGS", "CHANGE", "CHECKBOX") O4WQualifyEvent("SIZE", "CHANGE", "RADIOBUTTON") * finish up the main section we defined O4WSectionEnd('mainSection') * create a special area for us to use when displaying some response to the user * nothing goes in it now, but we may fill it in later O4WSectionStart('responseArea') O4WSectionEnd('responseArea') Case event _eqc "CLICK" * Click event - called when the button is pressed * Tell O4W we'll be generating a response updating the current form (as opposed to making a whole new form) O4WResponse() * Read in all the values ADD1 = O4WGetValue("ADD1") ADD2 = O4WGetValue("ADD2") CITY = O4WGetValue("CITY") STATE = O4WGetValue("STATE") ZIP = O4WGetValue("ZIP") * Make sure these values have something in them If TRIM(ADD1) = "" Or TRIM(CITY) = "" Or TRIM(STATE) = "" Or TRIM(ZIP)="" Then O4WError("Please be sure to enter complete address information before proceeding") Return End * move the extraction of the size and topping values, and the calculation of the cost, to a subroutine so we can call it from the 'change' event too /* * 'SIZE' will return the S/M/L code from the SIZE radio buttons SIZE = O4WGetValue("SIZE") * 'TOPPINGS' will return an @VM-delimited list of the selected toppings TOPPING = O4WGetValue("TOPPINGS") * Calculate the price from the toppings and size selected locate size In SizeCode$ Using "," Setting pos Then cost = Field(SizeCost$,",",pos) End Else * Couldn't find the size in the list? Report an error to the customer O4WError("We're sorry, but the size '":size:"' doesn't appear to be valid!") Return End * Now find each of the toppings num.toppings = dcount(topping, @VM) For each.topping = 1 To num.toppings this.topping = topping<1,each.topping> Locate this.topping In ToppingCode$ Using "," Setting pos Then cost += Field(ToppingCost$, ",", pos) End Else * Couldn't find the topping in the list? Report an error O4WError("We're sorry, but the topping '":this.topping:"' doesn't appear to be valid!") End Next each.topping */ Gosub calcTotal If error <> "" Then O4WError(error) Return end * In a 'real' application, you'd now write all the information we collected into a database somewhere orderNum = DATE():"*":TIME() * <WRITE INFORMATION HERE> * Notify the customer that his order has been received, and give him/her the total * We'll put this information into our 'responseArea' * Note that we use a special style so that O4W knows we're updating the previously-defined area (and not asking to create a new one) O4WSectionStart('responseArea', O4WResponseStyle()) O4WHeader('Your order number is ':orderNum, 4) O4WBreak() O4WText('Thank you for your order! Your order total is ':OCONV(cost,'MD2,$'):'.') O4WBreak() O4WText('Please press ') * Build a 'link' to return to the home page O4WLink('here', 0, 'www.revelation.com') O4WText(' to return to the home page') O4WSectionEnd('responseArea') * Display this area as a 'dialog box' to the user O4WDialog('responseArea', 'Thank You') Case event _eqc "CHANGE" * the 'CHANGE' event is triggered when our toppings or size is changed * Update our total cost cost = 0 * Because of how we organized our form, all the values are passed to us when we get the CHANGE event Gosub calcTotal If error <> "" Then * display any error message instead of our total O4WError(error) Return end * No error - display the total cost * Tell O4W we're generating an update to the current form and not an entire new form O4WResponse() * example 3: update the image with our current selections O4WImage("Mmmm...pizza!", imagePath:imageType:".jpg",'','','','imagePizza', O4WResponseStyle('','','1')) * We'll put this information into our 'runningTotal' * Note that we use a special style so that O4W knows we're updating the previously-defined area (and not asking to create a new one) O4WSectionStart('runningTotal', O4WResponseStyle()) O4WText("Current Order Total: ":Oconv(cost, "MD2,$")) O4WSectionEnd('runningTotal') End Case * Return 0 * * * calcTotal subroutine will pull in the size and toppings info and calculate the cost * if any errors occur, the variable "error" will be returned with the error text * example 3: also create imageType (with details about which picture to display) calcTotal: error = "" * 'SIZE' will return the S/M/L code from the SIZE radio buttons SIZE = O4WGetValue("SIZE") * example 3: make sure a size is selected If size = "" Then error = "We're sorry, but you must select a size to continue" Return End * 'TOPPINGS' will return an @VM-delimited list of the selected toppings TOPPING = O4WGetValue("TOPPINGS") * Calculate the price from the toppings and size selected locate size In SizeCode$ Using "," Setting pos Then cost = Field(SizeCost$,",",pos) End Else * Couldn't find the size in the list? Report an error to the customer error = "We're sorry, but the size '":size:"' doesn't appear to be valid!" Return End * Now find each of the toppings num.toppings = dcount(topping, @VM) For each.topping = 1 To num.toppings this.topping = topping<1,each.topping> Locate this.topping In ToppingCode$ Using "," Setting pos Then cost += Field(ToppingCost$, ",", pos) End Else * Couldn't find the topping in the list? Report an error error = "We're sorry, but the topping '":this.topping:"' doesn't appear to be valid!" return End Next each.topping * example 3 * always show 'large' pizza * imageType = size:"_" imageType = "L_" * don't show anything special for 'extra cheese' * If Index(topping, "C", 1) Then imageType := "C" If Index(topping, "M", 1) Then imageType := "M" If Index(topping, "P", 1) Then imageType := "P" If Index(topping, "S", 1) Then imageType := "S" Return
O4W_REVSHOW
- O4W_REVSHOW
Subroutine O4W_REVSHOW(CTLENTID, EVENT, REQUEST) * * Build an O4W Stored Procedure to make Penn & Teller reservations * * Demonstrated at Revelation Software User Conference 04/2010 * NOTE: for this to work you will need (in addition to the standard O4W files): * 1. The "template" named tickets.html, and * 2. The "youtubin" plugin * * * Insert our required equates * $Insert O4WCOMMON $Insert O4WEQUATES * * Respond to the various events that our O4W form generates * Begin Case * Every form is called with the "create" event when it starts up Case EVENT _EQC "CREATE" * Specify the location of the "template" we want to use for this form O4WForm("C:\TEMP\tickets.html") * Add in the "js" (javascript) and "css" (style sheets) for any of our plugins O4WScript("../plugins/Zebra_DatePicker/zebra_datepicker.js") O4WStyleSheet("../plugins/Zebra_DatePicker/metallic.css") O4WScript("../plugins/youtube/jquery.youtubin-1.js") * What do we want to show at the top of our browser page? O4WTitle("Penn & Teller Reservations") * Convert our sections into tabs O4WTabs("orderTabs", "showing":@VM:"reserve", "Select A Show":@VM:"Reservation Details") O4WSectionStart("showing", O4WMarkedStyle('','0')) * Put up a button to hide or show our on-line help O4WButton("Show/Hide Instructions...", "BTN_HELP") * Build the on-line help into a special section O4WSectionStart("helpSection") O4WText("You should put some help text here. It would make everyone's life much easier!") O4WSectionEnd("helpSection") * When the form starts up, make sure the help section is hidden O4WQualifyEvent("", "hide", "helpSection") * Enable the pure client-side "toggling" of the help section when the help button is clicked O4WQualifyEvent("BTN_HELP", "toggle", "helpSection", "1") * Output a newline so things look nice O4WBreak() * Lay out our show information as a table so it looks nice as well O4WTableStart("showInfo") O4WSetCell(1,1) O4WText("Show date:") O4WSetCell(1,2) * Build a textbox with the current date as the default value * The textbox will be 10 characters wide, it will be named "DATE", and * it will have a unique ID of "txtDATE" O4WTextbox(Oconv(DATE(), "D4/"), 10, "", "DATE", "txtDATE") O4WSetCell(2,1) O4WText("Number of tickets:") O4WSetCell(2,2) * Build a textbox with "0" as the default value * The textbox will be 5 characters wide, and will accept no more than 5 characters * It will be named "NUMTICKETS" and have the unique ID of "txtNUMBER" * When the form is submitted, or validated, the textbox will be checked to make sure only numeric data * is present - and that something is, indeed, present O4WTextbox("0", "5", "5", "NUMTICKETS", "txtNUMBER", O4WValidateStyle('', O4W_VALIDATE_NUM$, "1")) O4WSetCell(3,1) O4WText("Total Cost":@VM:"($49.99/seat):") O4WSetCell(3,2) O4WText("$0.00", "lblCost") O4WSetCell(3,3) * We'll put up a button labeled "Update"... O4WButton("Update", "BTN_UPDATE") * ...that will generate a "click" event when pressed O4WQualifyEvent("BTN_UPDATE", "CLICK") O4WTableEnd("showInfo") O4WSectionEnd("showing") * In the next section let's collect the "demographic" data for the buyer O4WSectionStart("reserve", O4WMarkedStyle('','0')) O4WTableStart("demoInfo") O4WSetCell(1,1) O4WText("Name:") O4WSetCell(1,2) O4wTextbox("", "", "", "NAME") O4WSetCell(2,1) O4WText("Address 1:") O4WSetCell(2,2) O4WTextbox("", "", "", "ADD1") O4WSetCell(3,1) O4WText("Address 2:") O4WSetCell(3,2) O4WTextbox("", "", "", "ADD2") O4WSetCell(4,1) O4WText("City:") O4WSetCell(4,2) O4WTextbox("", "", "", "CITY") O4WSetCell(4,3) O4WText("State:") O4WSetCell(4,4) * Build a listbox with the state information: first, read the state names and codes... states = Xlate("O4WCODES", "CODES_STATES", "", "X") * ...and then pass them into a listbox named "ST" O4WListbox(states<1>, states<2>, "ST") O4WSetCell(5,3) O4WText("ZIP:") O4WSetCell(5,4) O4WTextbox("", "", "", "ZIP") O4WTableEnd("demoInfo") O4WSectionEnd("reserve") O4WSectionEnd("orderTabs") O4WBreak() * Outside of the tabs, display a "Make Your Reservation" button and a link to YouTube o4wTableStart("displayTable", O4WMarkedStyle('','0')) O4WButton("Make Your Reservation!", "BTN_RESERVE") O4WSetCell(1,2) url = "http://www.youtube.com/watch?v=_qQX-jayixQ&NR=1" O4WLink("Watch a clip!", O4W_LINKTYPE_NORMAL$, url, "", "lnkPT") O4WTableEnd("displayTable") * When the "BTN_RESERVE" is clicked, it will generate a "SUBMIT" event * (which is like a CLICK event, but it also performs any validation) O4WQualifyEvent("BTN_RESERVE", "SUBMIT") * Turn our textbox (with the ID "txtDATE") into a "date picker" field by using * the "Zebra DatePicker" plugin O4WPlugin("testDATE","Zebra_DatePicker", "{format: 'M d, Y'}") * Turn our Penn & Teller link (with the ID "lnkPT") into an embedded video by * using the "youtubin" plugin O4WPlugin("lnkPT", "youtubin") * The CLICK event was generated by the BTN_UPDATE Case EVENT _EQC "CLICK" * Let O4W know we're building a response (and not a full page) O4WResponse() * Pull in the value of the NUMTICKETS field numTickets = O4WGetValue("NUMTICKETS") If Num(numTickets) And numTickets <> "" then * Had a valid number; calculate the cost and update the lblCost control * Note the use of the O4WResponseStyle to tell the control we're only * updating its text portion O4WUpdate("lblCost", Oconv(numTickets * 4999, "MD2,$"), O4WResponseStyle('','1')) End Else * Didn't have a valid number - show an error message O4WError("You must enter a valid number to continue") End * The SUBMIT event was generated by the BTN_RESERVE Case EVENT _EQC "SUBMIT" * After a SUBMIT, we do have to build a new page - so we'll call O4WForm with * our template again O4WForm("C:\TEMP\tickets.html") * Load in the data from the form numTickets = O4WGetValue("NUMTICKETS") NAME = O4WGetValue("NAME") amt = Oconv(numTickets * 4999, "MD2,$") * Imagine that we get all the rest of the info from the form too... * ...and write it out somewhere in the database * Now generate a thank you message O4WText("Thanks, ":NAME:"! Please be sure to complete your purchase using PayPal and your tickets will be waiting for you at the 'All Call' box") * And call the PayPal site (with the O4WPayPal api) to do a "buy it now" transaction O4WPaypal("BUY", "", "xxx@revelation.com", "Penn and Teller tickets", "pttickets", NUMTICKETS, "$49.99", "$0.00", "$0.00") End Case * Return 0
Login Screen
Below is code for a sample login screen for use with an O4W application. This code will prompt for username and password, validate the user and launch an O4W menu.
- O4W_CUSTLOGIN
Subroutine O4W_CUSTLOGIN(CtlEntId, Event, Request) * * O4W Example code: login screen * $Insert O4WCommon $Insert O4WEquates * * Begin Case Case EVENT _EQC "CREATE" O4WForm() * Have we already logged in? loginCookie = O4WCookie("tempCookie") If loginCookie <> "" Then * we've already had our cookie set; we must have logged in already * get the information stored in our temp file (referenced by the cookie) Read userInfo From O4WTempFile%, loginCookie Then * found the information from the successful login; go ahead and display the menu userName = userInfo<1> userPrivileges = userInfo<2> * display the menu O4WMenu("SAMPLE_MENU") * and some welcome text O4WHeader("Welcome, ":userName, 4) O4WBreak() O4WBreak() O4WText("Please select an option from the menu to continue") Return 0 End * Couldn't read our user information record from the temp file? * Must have been a false alarm; fall through to the login page end O4WHeader("O4W Customer Login":@VM:"Login Please", 4) O4WTableStart("loginTable", O4WTableStyle("", "", "", "1")) O4WText("User name: ") O4WSetCell(1,2) O4WTextBox("",'' ,'' ,'' , "uname") O4WSetCell(2,1) O4WText("Password: ") O4WSetCell(2,2) O4WPwdBox("",'' ,'' ,'' , "pwd") O4WSetCell(3,2) O4WButton("Login", "BTNLOGIN", O4WInputStyle('','','','',1)) O4WTableEnd("loginTable") O4WQualifyEvent("BTNLOGIN", "CLICK") * Define an empty section for any messages we might have to issue O4WSectionStart("msgArea", O4WMarkedStyle('','0')) O4WSectionEnd("msgArea") Case EVENT _EQC "CLICK" Begin Case Case CtlEntId _eqc "BTNLOGIN" O4WResponse() * evaluate what the user entered userName = O4WGetValue("uname") passwd = O4WGetValue("pwd") bIsBad = 1 userPrivileges = "" * given the username and password, validate this user * normally done by looking in a table and verifying that the passwords match onfileInfo = Xlate("MYUSERTABLE", userName, "", "X") If onFileInfo <> "" Then If passwd = onFileInfo<1> And passwd <> "" Then * this user is ok to proceed bIsBad = 0 * record what "permissions level" they have as well (in case we need to allow different "levels" of users) userPrivileges = onFileInfo<2> End end If bIsBad then * Bad - display error in the "msgArea" O4WSectionStart("msgArea", O4WResponseStyle():O4WMarkedStyle('','0')) O4WText("Invalid logon - please retry",'' , O4WColors("","","red")) O4WSectionEnd("msgArea") RETURN End * Success! Record login status in our temp file so we can find this information later * first, build a unique ID that we can use as our key to the temp table tempKey = O4WGenerateID("LOGIN") * and save it into our cookie O4WCookie("tempCookie", tempKey) * now save the user information into the temp table with this key cfgInfo = "" CFGINFO<1> = username CFGINFO<2> = userPrivileges Write CFGINFO On O4WTempFile%, tempKey * force redisplay of this page to properly display the menu O4WRedirect("O4W_CUSTLOGIN") End case End CASE * Return 0
This sample login routine must be registered and its required permissions must be set to “none”.
The routine also assumes that you have created an O4W menu (in the sample code above it is called SAMPLE_MENU). The permissions for this O4W menu must be set to “none”.
This routine also assumes you have a table that contains user names, passwords and “permission levels” which can be customized as needed.
For anything that needs to determine what user you are logged in as, and what permissions you have, you can use the following code snippet:
tempKey = O4WCookie(“tempCookie”) userName = “” userPerms = “” if tempKey <> “” then Read userInfo from O4WTempFile%, tempkey then userName – userInfo<1> userPerms = userInfo<2> End End
For this login screen to be truly “integrated”, you must also change your O4W configuration so that you have a custom “O4W Permissions Authorization procedure”; this custom procedure will check the required permissions for any O4W Form, Report, Dashboard, or Procedure against the “permissions level” assigned to the logged-in user, and determine whether the user should be allowed to execute the Form, Report, Dashboard, or Procedure. A sample Permissions Authorization procedure might look like the following:
- O4WCUSTOMPERMISSIONS
Function O4WCUSTOMPERMISSIONS(ReqdPerms, SessionInfo) $Insert O4WCommon $Insert O4WEquates tempKey = O4WCookie(“tempCookie”) userName = “” userPerms = “” if tempKey <> “” then Read userInfo from O4WTempFile%, tempkey then userName = userInfo<1> userPerms = userInfo<2> End End ** For this simple example, assume being logged in is all that is required to proceed (so we do not compare “userPerms” with “ReqdPerms”) If userName <> “” then Rslt = 1 End Else Rslt = -1; * not logged in – do not let them run this End Return Rslt
Invoice Form
- O4W_INVOICE_REPORT
Subroutine O4W_INVOICE_REPORT(CTLENTID, EVENT, REQUEST) * * Demonstration routine * $Insert O4WCOMMON $Insert O4WEQUATES Declare Function ucase * Open our tables (otherwise we can't generate any report) Open 'INVOICES' To INV.FL Else O4WError("Unable to open INVOICES table - cannot proceed!") Return 0 End Open 'DICT','INVOICES' To @DICT Else O4WError("Unable to open DICT.INVOICES table - cannot proceed!") Return 0 End * Define some style variables that we'll use repeatedly * This creates some "temporary" unnamed styles that we can reuse rightAlignStyle = O4WTextOptions("", "","1"):O4WAlignStyle("", O4W_ALIGN_RIGHT$) greenbarStyle = o4wcolorstyle("", "lightgreen") * Alternatively, this creates a named style ("overallSection") that we can reuse O4WTableStyle("overallSection", "", "", O4W_ALIGN_CENTER$) O4WSizeStyle("overallSection", "90%") * This creates some named styles ("invoiceSection", "headingOverride") with some explicit CSS commands * These could also go directly into our stylesheet O4WRawStyle("invoiceSection", "margin-left":@fm:"margin-right":@fm:"text-align", "0px":@FM:"0px":@FM:"left") O4WRawStyle("headingOverride", "padding", "8px 0px") Begin Case Case EVENT _EQC "CREATE" * Create event should always use O4WForm call (optionally specifying the template to use) O4WFORM() * Load in the stylesheet that will "dress up" our output * CSS derived from editable invoice example at http://www.css-tricks.com (lots of good information there!) O4WStyleSheet("../stylesheet/invoicereport.css") O4WHeader("Invoice Report", 3) O4WBreak() O4WBreak() * * In the CREATE event, we will define a section for our prompt * In the later pages, this section will be replaced with the invoice report output * * Only take up 90% of the page, and center this section O4WSectionStart("invOutput", O4WMarkedOptions('1'):"overallSection") * Allow for override in URL for the number of invoices per page nppg = O4WGetValue("PPG") If nppg = "" Or Not(Num(nppg)) Then nppg = 10 * Store this away so our later pages know how many should be shown O4WStore(nppg, "inv_per_page", "inv_per_page") O4WStore("1", "pageno", "pageno") * Build a table to display the prompts O4WTableStart("paramTable") O4WText("Enter starting date: ") O4WSetCell() * As a further enhancement, this could be a 'datepicker' control O4WTextbox(Oconv(DATE(), "D4/"), "", "", "STDATE") O4WSetCell(2) O4WText("Enter ending date: ") O4WSetCell() O4WTEXTBOX(Oconv(DATE(), "D4/"), "", "", "EDDATE") O4WSetCell(3, 2) * Display the button... O4WButton("Go...", "BTN_GO") * ... and associate it with the "CLICK" action O4WQualifyEvent("BTN_GO", "CLICK") O4WTableend("paramTable") O4WSectionEnd("invOutput") * * CLICK event handler * This may be invoked either when navigating back and forth, or when first submitting the report parameters * Case event _eqc "CLICK" Begin Case Case ctlentid _eqc "BTN_GO" * Called after submitting the report parameters * When responding to an event, we will almost always need the O4WResponse() call (comparable to the O4WForm() call on the CREATE event) o4wresponse() * determine which invoices fall in our date (if any) STDATE = O4WGETVALUE("STDATE") EDDATE = O4WGETVALUE("EDDATE") * As an enhancement, we chould check that these are valid, and if not, return an error... * "normal" OpenInsight programming is used here stmt = 'SELECT INVOICES BY INVOICE_DATE BY CUST_NAME' DELIM = "" If stdate <> "" Then stmt := ' WITH INVOICE_DATE >= "':STDATE:'"' DELIM = ' AND' End If eddate <> "" Then stmt := DELIM:' WITH INVOICE_DATE <= "':EDDATE:'"' End * Get the unique ID that O4W has already generated for this page * Use this as the key to store the selected IDs UniqueID = ucase(o4wgetvalue("O4WUniqueID")) LISTID = UniqueID:"*LIST" Call Rlist(STMT, 4, LISTID,'' ,'' ) * did we select anything? * note: this only works if we don't span multiple records; in newer versions of OpenInsight, use RTI_LIST instead to read in the entire list listinfo = Xlate("SYSLISTS", LISTID, "", "X") If listinfo = "" Then * Return an error o4wError("No invoices selected; please retry") End Else * Preserve the list of IDs in the O4WTEMP table listinfo = Delete(listinfo, 1, 0, 0) ;* First, remove the "list header" from field 1 Write listinfo On O4WTempFile%, LISTID * Some browsers may have available the parameters from the URL even on our AJAX events * O4W will make multiple occurences of the same parameter available as @VM delimited values * Make sure, then, that if we have more than one hanging around, we operate on only one invperpage = o4wgetvalue("inv_per_page")<1,1> If invperpage = "" Or Not(Num(invperpage)) Then invperpage = 10 End pageno = o4wgetvalue("pageno")<1,1> If pageno = "" Or Not(Num(pageno)) Then pageno = 1 End * The "buildInvoices" subroutine will generate the invoices for the current page Gosub buildInvoices End Case ctlentid _eqc "BTN_NEXT" * Called by the NEXT button o4wresponse() invperpage = o4wgetvalue("inv_per_page")<1,1> ;* make sure, if we have more than one hanging around, we only get one If invperpage = "" Or Not(Num(invperpage)) Then invperpage = 10 End pageno = o4wgetvalue("pageno")<1,1> If pageno = "" Or Not(Num(pageno)) Then pageno = 1 End * Increment the page number that we were on pageno += 1 * Build the invoices for this page Gosub buildInvoices Case ctlentid _eqc "BTN_PREV" * Called by the PREV button o4wresponse() invperpage = o4wgetvalue("inv_per_page")<1,1> ;* make sure, if we have more than one hanging around, we only get one If invperpage = "" Or Not(Num(invperpage)) Then invperpage = 10 End pageno = o4wgetvalue("pageno")<1,1> If pageno = "" Or Not(Num(pageno)) Then pageno = 1 End * Decrement the page number that we were on pageno -= 1 * Sanity check of the results If pageno < 1 Then pageno = 1 * Build the invoices for this page Gosub buildInvoices End case End Case Return 0 buildInvoices: * Tell O4W that we want to replace the "invOutput" section of the displayed browser page O4WSectionStart("invOutput", O4WResponseOptions():O4WMarkedOptions('1'):"overallSection") * Preserve the values we want to pass in on the "Next" or "Prev" button presses O4WStore(invperpage, "inv_per_page", "inv_per_page") O4WStore(pageno, "pageno", "pageno") * Retrieve the unique ID that O4W has generated for this form UniqueID = ucase(o4wgetvalue("O4WUniqueID")) LISTID = UniqueID:"*LIST" * Try to retrieve the list of selected invoices we generated previously Read listInfo From O4WTempFile%, ListID Else listInfo = "" * Is that list missing or empty? => Error If listInfo = "" Then O4WError("Error: No items selected, or list item lost") Return End * Calculate how many invoices are in the list, and which ones in the list we should process for this page num.items = dcount(listInfo, @FM) stno = (pageno-1)*invperpage + 1 edno = stno + invperpage - 1 * Sanity check - are we beyond the end of the list? If stno > num.items Then O4WError("Error: Invalid page number") Return End * If the last page has fewer than our maximum to display, adjust ourselves accordingly If edno > num.items Then edno = num.items cntr = 0 * * For each ID in our list, we will read the invoice record, pull out its information (and associated information from the CUSTOMERS table), and display it in appropriate sections * For each.invoice = stno To edno @ID = listInfo<each.invoice> Read @record From INV.FL, @ID Else @RECORD = "Record ":@ID:" not found" End cntr += 1 * get all the details INV_DATE = {INVOICE_DATE} CUST_NO = {CUST_NO} CUST_NAME = {CUST_NAME} ORD_DATE = {ORDER_DATE} ORD_NUM = {ORDER_NUMBER} DUE_DATE = {DUE_DATE} PAID_DATE = {PAID_DATE} QTYS = {QTYS} DESCS = {DESCS} ITEMS = {ITEMS} PRICES = {PRICES} EXTENDED = {EXT_COSTS} AMOUNT = {AMOUNT} * get address information CUSTINFO = Xlate("CUSTOMERS", CUST_NO, "", "X") ADD1 = CUSTINFO<3> ADD2 = CUSTINFO<4> CITY = CUSTINFO<5> ST = CUSTINFO<6> ZIP = CUSTINFO<7> COUNTRY = CUSTINFO<8> * Display this invoice in its own section * "Green bar" the output for easier identification invcolor = "" If cntr/2 = int(cntr/2) Then * change the background color of this section invcolor = greenbarStyle End * Insert a few blank lines O4WBreak() O4WBreak() O4WBreak() * * Each invoice will go into its own section * Note the use of the CNTR variable - this makes sure that each element has a unique ID (as required) * o4wsectionstart("invoice_":cntr, "invoiceSection":@SVM:invcolor) * Display the word "INVOICE", and assign it to the "hdr" style * We also assign it to the "headerOverride" style (dynamically created at the top of this report) to override the padding setting in our template's css o4wtext("INVOICE", "", "headingOverride":@SVM:O4WTextOptions("","","1"):"hdr") O4WBreak() O4WBreak() * Create a section to hold who we are, assigning it to the "identity" class O4WSectionStart("identity_":cntr, "identity") * Create a section for the address itself, assigning it to the "address" class O4WsectionStart("address_":cntr, "address") * ...and output our address information O4WText("Revelation Software") o4wbreak() O4WText("99 Kinderkamack Rd.") O4WBreak() O4WText("First Floor") O4WBreak() O4WText("Westwood, NJ 07675") O4WSectionEnd("address_":cntr) * Display a logo with the "logo" class O4WSectionStart("logo_":cntr, "logo") o4wimage("logo", "../images/logo.gif") O4WSectionEnd("logo_":cntr) O4WSectionEnd("identity_":cntr) * Some css to make things align properly o4wsectionstart("clearing_":cntr, O4WPositionStyle("","","","","","both"));o4wsectionend("clearing_":cntr) * Create the customer section using the "customer" class O4WSectionStart("customer_":cntr, "customer") * Output the customer information, assigned to the "customer-title" class * O4W will automatically make each value its own line in the display * Let's put this all together custOutput = "" custOutput<1,-1> = CUST_NAME custOutput<1,-1> = TRIM(ADD1) If trim(ADD2) <> "" Then custOutput<1,-1> = TRIM(ADD2) custOutput<1,-1> = CITY:", ":ST:" ":ZIP If COUNTRY _eqc "US" or COUNTRY _eqc "USA" Then country = "" End If trim(COUNTRY) <> "" Then custOutput<1,-1> = COUNTRY O4WText(custOutput, "", "customer-title") * Display a table of overall information about this invoice O4WTableStart("meta_":cntr, "meta") * Move to cell (1,1), and assign that cell to the "meta-head" class O4WSetCell(1,1,"","meta-head") O4WText("Invoice #") * Shorthand to move to the next cell - (1,2) in this case O4WSetCell() O4WText(@ID) O4WSetCell(2, 1, "", "meta-head") O4WText("Date") O4WSetCell() O4WText(Oconv(INV_DATE, "D")) O4WSetCell(3, 1, "", "meta-head") O4WText("Amount Due") O4WSetCell() O4WText(Oconv(AMOUNT, "MD2,$")) O4WTableEnd("meta_":cntr) O4WSectionEnd("customer_":cntr) * Create a table to display the line items, and the subtotals/totals O4WTableStart("items_":cntr, "items") * Display the column headings O4WTableHeader("Item") O4WTableHeader("Description") O4WTableHeader("Unit Price") O4WTableHeader("Quantity") O4WTableHeader("Ext") num.lines = dcount(items, @VM) total = 0 For each.item = 1 To num.lines O4WSetCell(each.item, 1, "", "item-name") o4wtext(items<1,each.item>) O4WSetCell(each.item, 2, "", "description") O4WText(descs<1,each.item>) O4WSetCell(each.item, 3, "", "cost") O4WText(Oconv(PRICEs<1,EACH.ITEM>, "MD2,$"), "", rightAlignStyle) O4WSetCell(each.item, 4, "", "qty") O4WText(qtys<1,each.item>, "", rightAlignStyle) O4WSetCell(each.item, 5, "", "price") O4WText(Oconv(extended<1,each.item>, "MD2,$"), "", rightAlignStyle) total += extended<1,each.item> Next each.item * Move to the next row, first column, and use "column spanning" to consume 2 columns; also, mark this as the "blank" style O4WSetCell(num.items+1, 1, "", O4WTableCellOptions(2):"blank") * Move to the 3rd column, and use column spanning to consume 2 more columnes; mark this as the "total-line" style O4WSetCell("", 3, "", O4WTableCellOptions(2):"total-line") O4WText("Subtotal") O4WSetCell("", 5, "", "total-value") O4WText(Oconv(total, "MD2,$"), "", rightAlignStyle) O4WSetCell(num.items+2, 1, "", O4WTableCellOptions(2):"blank") O4WSetCell("", 3, "", O4WTableCellOptions(2):"total-line") O4WText("Total") O4WSetCell("", 5, "", "total-value") O4WText(Oconv(total, "MD2,$"), "", rightAlignStyle) O4WTableEnd("items_":cntr) * Done with the line items and subtotals/totals * Start the terms section, assigning it to the "terms" style O4WSectionStart("terms_":cntr, "terms") O4WHeader("Terms", 5) O4WText("NET 30 days. Finance charges of 1.5% will be made on unpaid balances after 30 days") O4WSectionEnd("terms_":cntr) o4wsectionend("invoice_":cntr) O4WBreak() O4WBreak() * Finally, display a line using the "dashes" style O4WDivider("", "dashes") Next each.invoice * * Display our forward/back buttons * Only enable them, though, if appropriate If stno > 1 Then * Create the button, and make sure it's enabled O4WButton("< Back", "BTN_PREV", O4WInputStyle("","1", "0")) O4WQualifyEvent("BTN_PREV", "CLICK") End Else * Create the button, but make it disabled and read-only O4WButton("< Back", "BTN_PREV", O4WInputStyle("", "0","1")) End o4wspace(5) If edno < num.items Then O4WButton("Next >", "BTN_NEXT", O4WInputStyle("","1","0")) O4WQualifyEvent("BTN_NEXT", "CLICK") End Else O4WButton("Next >", "BTN_NEXT", O4WInputStyle("", "0","1")) End * O4WSectionEnd("invOutput") Return
Order Entry
- O4W_ORDER_ENTRY
Subroutine O4W_ORDER_ENTRY(ctlentid, event, request) * * O4W coding example * * Create, edit, or delete records in the ORDERS table * URL: http://.../oecgi4.exe/O4W_ORDER_ENTRY * * Prompt the user for the order number: allow for lookup, manual entry, or left blank => new order * * Main Program flow: CREATE event (to draw form initially) * * RETURNVALUE event/ONUM if order search button pressed or * LOSTFOCUS event/ONUM if order number entered explicitly * handleOrderNumber {may optionally display dialog if existing record has been changed, and then CLICK\BTN_READ or CLICK\BTN_READ_CANCEL} => loadRecord => retrieveRecord => displayHeader => displayDetails * * RETURNVALUE event/OCUST if customer search button pressed * CHANGE event/OCUST to update the customer name * markAsChanged * * CLICK event/BTN_SEARCH_PROD_<line#> if product search button pressed to ask which type of search * RETURNVALUE event/ITEM after product search completed * CHANGE event/ITEM, QTY, or PRICE to update the order details * displayDetails => markAsChanged * * CLICK event/BTN_SAVE to save the record * lockrecord => saverecord => resetpages * CLICK event/BTN_DELETE to confirm deletion of record => CLICK event/BTN_DELETE2 to delete the record * lockrecord => deleterecord => resetpages * CLICK event/BTN_CANCEL to reload the original order record * retrieveRecord => displayHeader => displayDetails => markAsFresh * Insert required equates and commons $Insert O4WCOMMON $Insert O4WEQUATES * Since we're going to check the configuration record, include this equate as well $Insert O4WConfigEquates Declare Function Repository * Open our data and dictionary tables, otherwise we have an error Open "ORDERS" To ORDER.TBL Else O4WError("Unable to access ORDERS table") Return 0 End Open "DICT","ORDERS" To @DICT Else O4WError("Unable to access DICT.ORDERS table") Return 0 End * define some O4W "styles" selectedStyle = O4WMarkedOptions("1") deselectedStyle = O4WMarkedOptions("0") textReplace = O4WResponseOptions("1") textReplaceTrigger = O4WResponseOptions("1", "", "1") borderStyle = O4WTableStyle("", "1", "1") roStyle = O4WInputOptions("0","1"):O4WColorStyle("", "lightblue") size10pct = O4WSizeStyle("", "10%") size50pct = O4WSizeStyle("", "50%") rtJustn = O4WAlignStyle("", "2") * retrieve (if set) our unique id uniqueID = O4WGetValue("O4WUniqueID") * read the configuration record (using the appropriate repository method) configRec = Repository("ACCESS", @APPID<1>:"*CONFIG*O4W*CFG_O4W") Call Set_Status(0) * determine the "lock procedure" that has been specified in the configuration record LOCK_PROC = configRec<O4WCONFIG_LOCK_PROC$> If LOCK_PROC = "" Then LOCK_PROC = "O4WI_LOCKHANDLER" Begin Case Case EVENT _EQC "CREATE" * Generate the base form * Since no template has been explicitly specified, use the default template O4WForm() * Save this variable in the web page O4WStore("", "activeRecord", "activeRecord") * After the O4WForm call, the unique ID has been created - load it in uniqueID = O4WGetValue("O4WUniqueID") * Display a header O4WHeader("Order Entry/Inquiry", 3) * Establish the tabs O4WTabs("orderForm", "orderHeader":@VM:"orderDetails", "Header":@VM:"Details") * Create the header section O4WSectionStart("orderHeader", deselectedStyle) * and a table to contain the order header information O4WTableStart("tableHeader") O4WSetCell(1,1) O4WText("Order Number: ") O4WSetCell() O4WButton("?", "BTN_SEARCH_ORDERS") * Associate a popup with this button O4WPopup("Order Search", O4W_LINKTYPE_POPUP$, "ORDER_POPUPS", "", "orderpopup", "BTN_SEARCH_ORDERS") O4WTextbox("", "", "", "ONUM","ONUM") * Tell O4W we want to be notified when the ONUM textbox has lost focus o4wqualifyevent("ONUM", "LOSTFOCUS") O4WSetCell(4, 1) O4WText("Customer Number: ") O4WSetCell() O4WButton("?", "BTN_SEARCH_CUST") * Associate a popup with this button too O4WPopup("Customer Search", O4W_LINKTYPE_POPUP$, "CUSTOMER_ID_POPUP", "", "custpopup", "BTN_SEARCH_CUST") O4WTextbox("", "", "", "OCUST", "OCUST") * Tell O4W we want to be notified when the OCUST textbox has changed o4wqualifyevent("OCUST", "CHANGE", "textbox") O4WSetCell() O4WSetCell(5, 2, "", O4WTableCellOptions(2)) * Display a textbox, but mark it as "read only" and style it so that it looks different O4WTextbox("", "40", "", "OCUSTNAME", "OCUSTNAME", roStyle) O4WSetCell(6,1) O4WText("Order Date: ") O4WSetCell() O4WDatePicker("","12","1","","ODATE", "ODATE") O4WTableEnd("tableHeader") O4WSectionEnd("orderHeader") O4WSectionStart("orderDetails", deselectedStyle) * Can't fill in the order details yet (without an order being selected), but set up this section as a placeholder O4WText("Please select an existing or new order...") O4WSectionEnd("orderDetails") O4WSectionEnd("orderForm") * Define this section for our special dialogs O4WSectionStart("specialDialog", deselectedStyle) O4WSectionEnd("specialDialog") * Define the buttoms O4WButton("Save", "BTN_SAVE") O4WButton("Delete", "BTN_DELETE") O4WButton("Cancel", "BTN_CANCEL") * And set them so they generate the 'click' event O4WQualifyEvent("BTN_SAVE", "CLICK") O4WQualifyEvent("BTN_DELETE", "CLICK") O4WQualifyEvent("BTN_CANCEL", "CLICK") Case event _eqc "LOSTFOCUS" * Generated when a control loses focus (if the appropriate O4WQualifyEvent on that control has been set) Begin Case Case ctlentid _eqc "ONUM" O4WResponse() @ID = O4WGetValue("ONUM") * Load in the value of the ONUM textbox, and call handleOrderNumber to retrieve that record (if appropriate) Gosub handleOrderNumber End case Case event _eqc "CHANGE" * Generated when a control's value changes (if the appropriate O4WQualifyEvnet on that control has been set) * Note that O4W provides 2 additional values: O4WChangeID (the ID of the control that made the change), and O4WChangeValue (the value it's been changed to) Begin Case Case ctlentid _eqc "OCUST" * Customer # has changed - update our customer name display O4WResponse() @RECORD = "" @ID = O4WGetValue("ONUM") {CUST_NO} = O4WGetValue("O4WChangeValue") O4WUpdate("OCUSTNAME", {CUSTOMER_NAME}, textReplace) * make sure we know that something's changed, so we can't just navigate away from this without being prompted to save our changes Gosub markAsChanged Case ctlentid _eqc "ITEM" Or ctlentid _eqc "QTY" Or ctlentid _eqc "PRICE" * One of our detail lines has been added or changed O4WResponse() * who changed? which line, which control? This way, we can determine who to set the focus to... whichOne = O4WGetValue("O4WChangeID") * Our controls are defined as "ITEM_1", "ITEM_2", etc. However, if a new line has been added, it will have a more complex (auto-generated) ID, such as ITEM_3_o4wid_2 If Count(whichOne, "_") > 1 Then * More than one "_" in the ID? This must be a newly-added line changeLine = -1 End Else changeLine = Field(whichOne, "_", 2) End * Load in the current contents of all our fields Gosub retrieveCurrentRecord * fill in any defaults, and determine where to set the focus to items = {ITEM} qtys = {QTY} cost = {PRICE} num.items = dcount(items, @VM) For each.item = num.items To 1 step -1 * default the quantity and price if they're not set If qtys<1,each.item> = "" Then qtys<1,each.item> = 1 End If cost<1,each.item> = "" then cost<1,each.item> = Xlate('PRODUCTS', items<1,each.item>, "PRICE", "") end Next each.item {ITEM} = items {QTY} = qtys {PRICE} = cost * Update the "order details" section Gosub displayDetails * now set the focus as appropriate If changeLine = -1 Then * this was a newly-added line, so it's the last value changeLine = dcount(items,@VM) end Begin Case Case ctlentid _eqc "ITEM" O4WFocus("PRICE_":changeLine) Case ctlentid _eqc "PRICE" O4WFocus("QTY_":changeLine) Case ctlentid _eqc "QTY" O4WFocus("ITEM_":(changeLine+1)) End Case * Remember to mark us as having been changed, so we can't navigate away and lose our changes without a verification prompt Gosub markAsChanged End Case Case event _eqc "CLICK" * Generated when a button is pressed Begin Case Case ctlentid _eqc "BTN_CANCEL" * Throw away any changes, and reload the original contents of the record? O4WResponse() * Get the ID @ID = O4WGetValue("ONUM") * see if there are any changes, and if there are prompt before reloading Gosub handleOrderNumber Case CTLENTID _EQC "BTN_SAVE" * called when the 'save' button is pressed * make sure we can lock the record, and it hasn't been changed by anyone else since we originally read it in Gosub lockRecord If error = "" Then * No problems - write it out Gosub saveRecord * Display our "update successful" response O4WError("Order '":@ID:"' updated", "Save Order") * blank the fields, mark us as unchanged, and return to first tab Gosub resetPages End Case ctlentid _eqc "BTN_DELETE" * called to delete the record * put up a dialog box to verify the deletion first O4WResponse() * retrieve the item id @ID = O4WGetValue("ONUM") * build our dialog to verify the deletion O4WSectionStart('specialDialog', selectedStyle:O4WResponseStyle()) * save the order ID in the dialog O4WStore(@ID, "ONUM", "ONUM") O4WText("Please verify deletion of order '":@ID:"'") O4WBreak() O4WButton("Delete", "BTN_DELETE2", selectedStyle) O4WButton("Cancel", "BTN_DIALOG_CANCEL") * Notify us when these buttons are pressed O4WQualifyEvent("BTN_DELETE2", "CLICK") O4WQualifyEvent("BTN_DIALOG_CANCEL", "CLICK") O4WSectionEnd('specialDialog') * and then show the dialog O4WDialog('specialDialog', 'Confirm Deletion','' ,'' , 'width:350') Case ctlentid _eqc "BTN_DELETE2" * called after confirming the delete request * first, make sure we can lock the record and no one else has changed it since we read it Gosub lockRecord If error = "" Then * no problems - proceed with the deletion Gosub deleteRecord * display our 'deletion successful' message O4WError("Order '":@ID:"' deleted", "Delete Order") * blank the fields, mark us as unchanged, and return to first tab Gosub resetPages End * the following 2 controls are displayed on the "record has changed - do you want to discard these changes" dialog box Case ctlentid _eqc "BTN_READ" * yes, the user wants to discard any changes and load in the new record O4WResponse() * dismiss the dialog box O4WDialog("specialDialog") * retrieve their new ID @ID = O4WGetValue("ONUM") * load in the record Gosub loadRecord Case ctlentid _eqc "BTN_CANCEL_READ" * no, the user does _not_ want to throw away their changes to the current record O4WResponse() * retrieve the original ID origID = O4WGetValue("ID") * dismiss the dialog box O4WDialog("specialDialog") * and just reset the value in the order number textbox O4WUpdate("ONUM", origID, textReplace) * this is generated when the 'search' button is pressed in one of the order detail lines * the user is given a choice of search methods Case ctlentid[1,16] _eqc "BTN_SEARCH_PROD_" O4WResponse() * determine which line the user was on (each button is identified with its line number) whichOne = ctlentid[17, Len(ctlentid)] * build a dialog box to ask what type of search to perform O4WSectionStart("specialDialog", O4WResponseOptions()) * Note the button contains the line number (so we'll know what to update after the user makes their choice) O4WButton("Search for product by text", "BTN_SEARCH3_PROD_":whichOne) * associate an index lookup popup with this button O4WPopup("Product Search", O4W_LINKTYPE_INDEXLOOKUP$, "", "PRODUCTS":@FM:"DESCRIPTION_XREF":@fm:"ID":@VM:"DESCRIPTION", "prodpopup2", "BTN_SEARCH3_PROD_":whichOne) O4WText(" or ") O4WButton("List all products", "BTN_SEARCH2_PROD_":whichOne) * associate a regular popup with this button O4WPopup("Product Search", O4W_LINKTYPE_POPUP$, "PRODUCT_POPUP", "", "prodpopup1", "BTN_SEARCH2_PROD_":whichOne) * Provide a 'cancel' button to dismiss the dialog O4WBreak() O4WBreak() O4WButton("Cancel", "BTN_DIALOG_CANCEL") * Generate the 'click' event when it's pressed O4WQualifyEvent("BTN_DIALOG_CANCEL", "CLICK") O4WSectionEnd("specialDialog") O4WDialog("specialDialog", "Product Search",'' ,'' , 'width:550') Case ctlentid _eqc "BTN_DIALOG_CANCEL" O4WResponse() * dismiss the dialog box O4WDialog("specialDialog") End Case Case event _eqc "RETURNVALUE" * The user has selected a value from a popup O4WResponse() * determine which control had the popup, and what the user selected callingCtl = O4WGetValue('o4wPopupCtl') popupValue = O4WGetValue('o4wValue') Begin Case Case callingCtl _eqc "BTN_SEARCH_ORDERS" * this was the order search popup * update the order number with the selected value O4WUpdate("ONUM", popupValue, textReplace) @ID = popupValue * Fill in the rest of the form based on this record ID * If "automatic lose focus" is not desired, disable the following line: Gosub handleOrderNumber Case callingCtl _eqc "BTN_SEARCH_CUST" * this was the customer search popup * update the customer number textbox with the selected value * note the use of the "textReplaceTrigger" style - this not only replaces the text, but also triggers the "change" event * the change event (previously defined) will derive the customer name from this value O4WUpdate("OCUST", popupValue, textReplaceTrigger) Case callingCtl[1,17] _eqc "BTN_SEARCH2_PROD_" Or callingCtl[1,17] _eqc "BTN_SEARCH3_PROD_" * this was either of the 2 different product search popups (regular or index lookup) * first, dismiss the dialog box that asked which type of search to use O4WDialog("specialDialog") * determine which line item in the order details called the popup whichLine = callingCtl[18, Len(callingCtl)] * update the product on that line of the order details, and trigger the "change" event for that item O4WUpdate("ITEM_":whichLine, popupValue, textReplaceTrigger) End case End Case Return 0 retrieveRecord: * call the lockhandler routine to read the original record Call @LOCK_PROC(O4W_LOCKHANDLER_ACTION_READ$, order.tbl, @ID, @RECORD, UniqueID:"*ORDERS*":@id:"*ORIG") Return lockRecord: * call the lockhandler routine to lock (if possible) the original record * this will verify that the underlying record is unchanged, And we can lock it error = "" ;* assume success * load in the current ID and record contents from the browser form Gosub retrieveCurrentRecord * call the lockhandler to see if the record is able to be locked, and is unchanged from its original state stat = Function(@lock_proc(O4W_LOCKHANDLER_ACTION_CHECK$, order.tbl, @id, curr.record, UniqueID:"*ORDERS*":@ID:"*ORIG", O4W_LOCKHANDLER_OPTION_LOCK$)) * check for results from the 'check' call If stat = O4W_LOCKHANDLER_NO_LOCK$ Then * couldn't get the lock on this item - let the user choose another ID error = "Unable to lock order '":@ID:"'" O4WError(error) Return End If stat = O4W_LOCKHANDLER_ITEM_CHANGED$ Then * someone else changed the record while we worked on it - let the user choose another ID error = "Order '":@ID:"' has been changed by another user" * remember to unlock our current record, though Call @LOCK_PROC(O4W_LOCKHANDLER_ACTION_UNLOCK$, order.tbl, @ID) O4WError(ERROR) Return End Return saveRecord: * call the lockhandler routine to save the modified record Call @LOCK_PROC(O4W_LOCKHANDLER_ACTION_WRITE$, ORDER.TBL, @ID, @RECORD, UniqueID:"*ORDERS*":@ID:"*ORIG") Return deleteRecord: * delete the record from the orders table, and release any lock Delete order.tbl, @ID * release the record lock Call @LOCK_PROC(O4W_LOCKHANDLER_ACTION_UNLOCK$, ORDER.TBL, @ID) Delete O4WTempFile%, UniqueID:"*ORDERS*":@ID:"*ORIG" return retrieveCurrentRecord: @ID = O4WGetValue("ONUM") @RECORD = "" {CUST_NO} = O4WGetValue("OCUST") odate = O4WGetValue("ODATE") * if no date filled in, use todays date If odate = "" Then odate = date() End Else odate = Iconv(odate,"D") end {ORDER_DATE} = odate items = O4WGetValue("ITEM") qtys = O4WGetValue("QTY") prices = O4WGetValue("PRICE") * trim off any blank lines num.items = dcount(items, @VM) For each.item = num.items To 1 step -1 If items<1,each.item> = "" Then items = Delete(items, 1, each.item, 0) qtys = Delete(qtys, 1, each.item, 0) prices = Delete(prices, 1, each.item, 0) End Next each.item {ITEM} = items {QTY} = qtys {PRICE} = Iconv(prices, "MD2") Return resetPages: * blank out the order number O4WUpdate("ONUM", "", textReplace) @RECORD = "" Gosub displayHeader * mark as unchanged (as of now) Gosub markAsFresh * go to the first tab O4WGoToTab("orderForm", 1) return handleOrderNumber: * do we currently have an 'active' (changed) record in process? isActive = O4WGetValue("activeRecord") If isActive <> "" Then * yes; build our dialog to verify the change of record O4WSectionStart('specialDialog', selectedStyle:O4WResponseStyle()) * save the newly-entered order ID in the dialog O4WStore(@ID, "ONUM", "ONUM") * save the original item ID in the dialog, too O4WStore(isActive, "ID", "ID") O4WText("Discard any changes to existing record?") O4WBreak() O4WButton("Yes, Discard and Load New Record", "BTN_READ", selectedStyle) O4WButton("No, Cancel and Keep Current Record", "BTN_CANCEL_READ") * Notify us when these buttons are pressed with the 'click' event O4WQualifyEvent("BTN_READ", "CLICK") O4WQualifyEvent("BTN_CANCEL_READ", "CLICK") O4WSectionEnd('specialDialog') * and then show the dialog O4WDialog('specialDialog', 'Confirm Discard of Any Changes','' ,'' , 'width:650') return End * no, no currently active record - just load in the record for this @ID Gosub loadRecord Return loadRecord: If @ID = "" Then * new order - get next sequential number Lock @DICT, "%SK%" Else O4WError("Unable to lock sequential counter record") Return END Read NEXT.NUM From @DICT, "%SK%" Else NEXT.NUM = 1000 @ID = NEXT.NUM Write NEXT.NUM +1 On @DICT, "%SK%" Unlock @DICT, "%SK%" * Update the text box with this order number O4WUpdate("ONUM", @ID, textReplace) End * read in the record for this @ID Gosub RetrieveRecord * display the header and details Gosub displayHeader * Mark this record as unchanged (for now) Gosub markAsFresh * make sure we're on the first tab O4WGoToTab("orderForm", 1) Return markAsFresh: * turn off the browser page warning O4WPlugin("$", "o4wsetConfirmUnload", "false") * and clear out our own flag that indicates the record has changed o4wUpdate("activeRecord", "", textReplace) Return markAsChanged: * tell the browser that we need the "don't navigate away!" warning message O4WPlugin("$", "o4wsetConfirmUnload", "true") * remember what our current record ID is in case someone tries to change it o4wUpdate("activeRecord", @ID, textReplace) Return displayHeader: * load up the header fields O4WUpdate("OCUST", {CUST_NO}, textReplace) O4WUpdate("OCUSTNAME", {CUSTOMER_NAME}, textReplace) O4WUpdate("ODATE", Oconv({ORDER_DATE}, "D"), textReplace) * and fall through to update the details... displayDetails: * rebuild the section containing our order details o4wSectionStart("orderDetails", O4WResponseStyle():deselectedStyle) * build a table for our order lines O4WTableStart("tableDetails", borderStyle:O4WSizeStyle("","100%")) O4WTableHeader("Item", "2", "", size10pct) O4WTableHeader("Description", "", "", size50pct) O4WTableHeader("Cost", "", "", size10pct) O4WTableHeader("Qty", "", "", size10pct) O4WTableHeader("Ext Cost") num.items = dcount({ITEM}, @vm) +1 ;* always have one blank row at the end For each.item = 1 To num.items O4WSetCell(each.item, 1,'' , borderStyle) O4WButton("?", "BTN_SEARCH_PROD_":each.item) * display some choices as to what type of popup should be run when this button is pressed O4WQualifyEvent("BTN_SEARCH_PROD_":each.item, "CLICK") O4WSetCell('','' ,'' , borderStyle) * mark these with the 'o4wClearDuplicateVal' and 'o4wClearDuplicateText' styles so that the "Add" button clears out these fields when a new row is added O4WTextBox({ITEM}<1,EACH.ITEM>, "", "", "ITEM", "ITEM_":each.item, "o4wClearDuplicateVal") O4WSetCell('','','',borderStyle) O4WText({DESCRIPTION}<1,EACH.ITEM>, "DESC_":each.item, "o4wClearDuplicateText") O4WSetCell('','','',borderStyle) O4WTextBox(Oconv({PRICE}<1,EACH.ITEM>, "MD2,$"), "", "", "PRICE", "PRICE_":each.item, rtJustn:"o4wClearDuplicateVal") O4WSetCell('','','',borderStyle) O4WTextBox({QTY}<1,EACH.ITEM>, "", "", "QTY", "QTY_":each.item, rtJustn:"o4wClearDuplicateVal") O4WSetCell("", "", "", rtJustn:borderStyle) O4WText(Oconv({EXT_COST}<1,EACH.ITEM>, "MD2,$"), "EXT_":each.item, "o4wClearDuplicateText") Next each.item O4WTableEnd("tableDetails") * build another table to display the sub total O4WTableStart("tableSummary", O4WSizeStyle("","100%")) O4WSetCell(1,1,'', O4WSizeStyle("","90%")) O4WSpace(5) O4WSetCell(1,2,'' , rtJustn:borderStyle) O4WText(Oconv({SUB_TOTAL}, "MD2,$")) O4WTableEnd("tableSummary") O4WButton("Add Row", "BTN_ADD") O4WButton("Delete Row", "BTN_DEL") * Automatically add or remove the last row when these buttons are pressed O4WQualifyEvent("BTN_ADD", "ADDTOTABLE", "tableDetails", "-1") O4WQualifyEvent("BTN_DEL", "DELETEFROMTABLE", "tableDetails", "-1") * Notify us when any of the ITEM, QTY, or PRICE field values are changed O4WQualifyEvent("ITEM", "CHANGE", "textbox") O4WQualifyEvent("PRICE","CHANGE", "textbox") O4WQualifyEvent("QTY", "CHANGE", "textbox") o4wSectionEnd("orderDetails") Return
Generic Edit Record Routine
- O4W_EDIT_RECORD
Subroutine O4W_EDIT_RECORD(CtlEntId, Event, Request) * * O4W coding example * * Create, edit, or delete records in any table * URL: http://.../oecgi4.exe/O4W_EDIT_RECORD * or : http://.../oecgi4.exe/O4W_EDIT_RECORD?TABLE=<tablename> * or : http://.../oecgi4.exe/O4W_EDIT_RECORD?TABLE=<tablename>&ITEM=<itemid> * * Prompt the user for the table name, and (after it's selected) the item ID, or allow for entry of a new item ID, or * Prompt the user for the item ID, or allow for entry of a new item ID, when a table name is passed in * After the table and item name have been specified, determine which fields in the dictionary we can use, and build * editable fields for each allowed dictionary item * * Main Program flow: CREATE event (which may call getID) * CHANGE event (if table not specified in URL), which then calls getID * CLICK event/BTNEDIT which then calls DisplaySection * * CLICK event/BTNCANCEL which then calls DisplaySection, or * CLICK event/BTNSAVE which calls LockIT (may then call ChooseAnother), or * CLICK event/BTNDELETE which calls LockIT (may then call ChooseAnother) * * * Insert required equates and commons $Insert O4WCommon $Insert O4WEquates * Since we're going to check the configuration record, include this equate as well $Insert O4WConfigEquates Declare Function Repository * define some O4W "styles" textReplace = O4WResponseStyle('','1','') styleReplace = O4WResponseStyle('','','1') selectMe = O4WMarkedStyle('','1') deSelectMe = O4WMarkedStyle('','0') enableMe = O4WInputStyle('','1') disableMe = O4WInputStyle('','0') promptColor = O4WColorStyle("", "lightblue") * retrieve (if set) our unique id uniqueID = O4WGetValue("O4WUniqueID") * read the configuration record (using the appropriate repository method) configRec = Repository("ACCESS", @APPID<1>:"*CONFIG*O4W*CFG_O4W") Call Set_Status(0) * determine the "filter procedure" that has been specified in the configuration record FILTER_PROC = configRec<O4WCONFIG_FILTER_PROC$> If FILTER_PROC = "" Then FILTER_PROC = "O4WI_FILTER" Begin Case Case Event _eqc "CREATE" * Initial form creation O4WForm() * get our unique ID (set by o4wform) uniqueID = O4WGetValue("O4WUniqueID") O4WHeader("OpenInsight for Web: Create/Edit Records", 3) O4WBreak() O4WBreak() * define the area for our 'breadcrumbs' O4WSectionStart("breadcrumb", deSelectMe) O4WSectionEnd("breadcrumb") *...and define the breadcrumbs themselves O4WBreadCrumbs("breadcrumb", "SELECTION":@VM:"EDITSECTION", O4WI_FORMATMSG(O4W_MESSAGE_BREADCRUMB_SELECT$):@VM:"Edit") O4WBreak() O4WBreak() * this is the 'selection' page - allow the user to specify the table and item to edit O4WSectionStart("SELECTION", selectMe) * Might we be called with a table and item already specified? TABLE = O4WGetValue("TABLE") ID = O4WGetValue("ITEM") bHaveBoth = 1 O4WTableStart("promptTable") If table = "" Then bHaveBoth = 0 * build the prompt for the table name O4WSetCell(1,1) O4WText("Please select the table: ") * use the shorthand to move to the next cell in the current row O4WSetCell() * call the filter procedure to retrieve what table(s) we are allowed to work on TLIST = Function(@FILTER_PROC("O4W_EDIT_RECORD", O4WSessionInfo%)) * build the list of tables into a listbox - and select 'none' to start O4WListBox("Please select...", "<none>", "TABLE", "", "tblNONE", selectMe) O4WListbox(tlist, tlist, "TABLE",'' , tlist) * mark the table name input so we know when one has been selected O4WQualifyEvent("TABLE", "CHANGE", "LISTBOX") * define a section for getting the ID once the table is selected * of course, we can't actually fill this in yet - these are "placeholders" until the table has been chosen O4WSetCell(2,1) O4WText("Please select the record ID: ") O4WSetCell(2,2) O4WSectionStart("idSection") O4WText("<Please select a table...>") O4WSectionEnd("idSection") O4WSetCell(3,1) O4WText("Or enter the name of a record to create: ") O4WSetCell(3,2) O4WSectionStart("newidSection") O4WText("<Please select a table...>") O4WSectionEnd("newidSection") End Else * have table specified; what about item? If ID = "" Then * No, the item ID has not been passed in - build the list of available item ids bHaveBoth = 0 O4WSetCell(1,1) O4WText("Please select the ":TABLE:" record ID: ") O4WSetCell() Gosub getID O4WSetCell(2,1) O4WText("Or enter the name of a record to create: ") O4WSetCell(2,2) O4WTextbox("", "", "", "NEWITEM") End End If bHaveBoth = 0 Then * since we don't have both, put up the button to proceed when they _do_ have both specified * use "+1" to increment the row (whatever it currently is) O4WSetCell("+1", 2) O4WButton("Edit", "BTNEDIT", selectMe) * Qualify our "click" event O4WQualifyEvent("BTNEDIT", "CLICK") End * finish up our table for the prompts O4WTableEnd("promptTable") * finish up the section (page) for the prompts O4WSectionEnd("SELECTION") * define the section (page) for the actual editing O4WSectionStart("EDITSECTION", selectMe) If id = "" Or table = "" Then * don't have all the required pieces - just leave us on the selection page O4WSetBreadcrumb("SELECTION") End Else * have both table and item id - move directly to the editing O4WSetBreadcrumb("EDITSECTION") Open TABLE To CODE.FL Else O4WError("Unable to open table '":TABLE:"'") Return End * call the lockhandler routine to read the original record Call o4wi_lockhandler(O4W_LOCKHANDLER_ACTION_READ$, code.fl, id, info, UniqueID:"*":table:"*":id:"*ORIG") Gosub displaySection ;* re-entry point to redisplay end o4wsectionEnd("EDITSECTION") * preserve a section for our special messages O4WSectionStart("specialDialog", selectMe) O4WSectionEnd("specialDialog") Case event _eqc "CHANGE" * table to operate on has changed O4WResponse() * what table have they selected? TABLE = O4WGetValue("O4WChangeID") * rebuild the "placeholders" with the prompts for the id O4WSectionStart("idSection", o4wresponsestyle()) Gosub getID O4WSectionEnd("idSection") *...and the prompt for a "new item" O4WSectionStart("newidSection", o4wresponsestyle()) O4WTextbox("", "", "", "NEWITEM") O4WSectionEnd("newidSection") Case event _eqc "CLICK" Begin Case Case CTLENTID _EQC "BTNCANCEL" * Throw away any changes, and refresh the page with a current copy of the record O4WResponse() TABLE = O4WGetValue("TABLE")<1,1> ID = O4WGetValue("ID")<1,1> If ID = "" Or TABLE = "" Then * don't know what we were working on? That's a problem O4WError("Unable to retrieve record") Return End Open TABLE To CODE.FL Else * Can't open the table we think we're working on? That's a problem O4WError("Unable to open table '":table:"'") Return End * re-read the record Call o4wi_lockhandler(O4W_LOCKHANDLER_ACTION_READ$, code.fl, ID, info, UniqueID:"*":TABLE:"*":ID:"*ORIG") * recreate the section containing the editable fields O4WSectionStart("EDITSECTION", O4WResponseOptions():selectMe) Gosub displaySection ;* redisplay this record O4WSectionEnd("EDITSECTION") Case CTLENTID _EQC "BTNSAVE" Or ctlentid _eqc "BTNSAVE2" * called either on the original 'save' button press, or after selecting a new item ID * make sure we can lock the record, and it hasn't been changed by anyone else since we originally read it in Action = "Save" Actionbtn = "BTNSAVE" Gosub lockIT If error = "" Then * No problems - write it out Call O4WI_Lockhandler(O4W_LOCKHANDLER_ACTION_WRITE$, code.fl, ID, INFO, UniqueID:"*":TABLE:"*":ID:"*ORIG") * Display our "update successful" response O4WError("Record '":ID:"' updated", "Save Record") End Case ctlentid _eqc "BTNCANCEL2" * Called when cancelling the save/delete dialog * Just dismiss the dialog O4WDialog("specialDialog") Case ctlentid _eqc "BTNDELETE" * called to delete the record * verify the deletion first O4WResponse() * retrieve the table and item id ID = O4WGetValue("ID")<1,1> TABLE = O4WGetValue("TABLE")<1,1> * build our dialog to verify the deletion O4WSectionStart('specialDialog', SelectMe:O4WResponseStyle()) * save the table and Item ID in the dialog O4WStore(ID, "ID", "ID") O4WStore(TABLE, "TABLE", "TABLE") O4WText("Please verify deletion of record '":ID:"' in table '":table:"'") O4WBreak() O4WButton("Delete", "BTNDELETE2", selectMe) O4WButton("Cancel", "BTNCANCEL2") O4WQualifyEvent("BTNDELETE2", "CLICK") O4WQualifyEvent("BTNCANCEL2", "CLICK") O4WSectionEnd('specialDialog') * and then show the dialog O4WDialog('specialDialog', 'Confirm Deletion') Case ctlentid _eqc "BTNDELETE2" * called after confirmation of deletion action * first, make sure we can lock the record and no one else has changed it since we read it Action = "Delete" Actionbtn = "BTNDELETE" Gosub lockIT If error = "" Then * no problems - proceed with the deletion * Check to see if this record already existed (ie, it wasn't a new record request) Open table To code.fl Else O4WError("Unable to open table '":TABLE:"'") Return End Readv tstExist From code.fl, ID, 1 Else tstExist = "" * do the actual deletion Delete code.fl, ID * release the record lock Call O4WI_Lockhandler(O4W_LOCKHANDLER_ACTION_UNLOCK$, code.fl, ID) Delete O4WTempFile%, UniqueID:"*":TABLE:"*":ID:"*ORIG" * display our 'deletion successful' message O4WError("Record '":ID:"' deleted", "Delete Record") * If the record existed, remove it from our list of records on the selection page If tstExist <> "" And bIsNew = 0 then * refresh our list of available records O4WScript('','o4wMoveValue("ITEM","","1");') End *...and move back to that selection page O4WSetBreadcrumb("SELECTION") End Case CTLENTID _EQC "BTNEDIT" * Called when the "Edit" button is pressed O4WResponse() * determine which table the user entered table = O4WGetValue("TABLE")<1,1> If table = "<none>" Then table = "" * determine whether the user selected a record name from the list, or entered a new one existingItem = O4WGetValue("ITEM") If existingItem = "<none>" Then existingItem = "" newItem = O4WGetValue("NEWITEM") * make sure we have all the information we need, and it's valid If existingItem = "" And newItem = "" Then O4WError("You must specify a record to edit or create.") Return End If table = "" Then O4WError("You must specify which table to use") Return End Open table To code.fl Else o4werror("Unable to open table '":table:"'") Return end If existingItem <> "" Then * try to read the record Call o4wi_lockhandler(O4W_LOCKHANDLER_ACTION_READ$, code.fl, existingItem, info, UniqueID:"*":table:"*":existingItem:"*ORIG") * didn't read anything? That's not right... If info = "" then O4WError("Record '":existingItem:"' not on file in table '":TABLE:"'") Return End ID = existingItem End Else * even if they keyed in the item id explicitly, still try to read it (maybe it _does_ exist) Call o4wi_lockhandler(O4W_LOCKHANDLER_ACTION_READ$, code.fl, newItem, info, UniqueID:"*":table:"*":newItem:"*ORIG") ID = newItem End * rebuild the section where the editable fields are displayed O4WSectionStart("EDITSECTION", selectMe:O4WResponseStyle()) Gosub displaySection O4WSectionEnd("EDITSECTION") * set our breadcrumb so we display the editable section O4WSetBreadcrumb("EDITSECTION") End case End Case * * Return 0 * * * lockIT: * verify underlying record is unchanged, And we can Lock it error = "" ;* assume success O4WDialog('specialDialog') ;* Dismiss dialog (if it's up) TABLE = O4WGetValue("TABLE")<1,1> Open TABLE To code.fl Else error = "Table '":TABLE:"' could not be opened" O4WError(error) Return End * This may be a new record (potentially), or an existing record bIsNew = 0 ID = O4WGetValue("NEWID") If ID <> "" Then bIsNew = 1 End else ID = O4WGetValue("ID")<1,1> End If ID = "" Then * couldn't retrieve the item ID error = "Unable to access record ID" O4WError(error) Return End * call the lockhandler to see if the record is able to be locked, and is unchanged from its original state stat = O4WI_LOCKHANDLER(O4W_LOCKHANDLER_ACTION_CHECK$, code.fl, id, curr.info, UniqueID:"*":table:"*":ID:"*ORIG", O4W_LOCKHANDLER_OPTION_LOCK$) * the "check" method of the lockhandler will also retrieve the current contents of the record * load in any changes made by the user into this current record info = curr.info ;* we start with the original record, because in this routine we may not modify all fields MAXFIELDS = O4WGetValue("MAXFIELDS") For each.field = 1 To MAXFIELDS * get the value (if any) for this field * note that, since we may not have an entry for each field, we have to see if there was a field present for this field number this.value = o4wgetvalue("FIELD_":EACH.FIELD, bStatus) If bStatus Then * yes, this field was present - continue processing * if this value contained @VM or @SVM originally, and it wasn't marked as a multivalued field, we recorded that information maybeConvert = o4wgetvalue("FIELD_":EACH.FIELD:"_TYPE") If maybeConvert <> "" Then * we had VM or SVM in the original - but not "M" in the dict * we converted (for display) to a text area - that means each line is @TM delimited * convert back to VM or SVM as appropriate If maybeConvert = "0" Then * we had @SVM in the original Convert @TM To @SVM In this.value End Else * we had @VM in the original Convert @TM To @VM In this.value End End * record this information in the record info<each.field> = this.value end Next each.field * finally, check for results from the 'check' call If stat = O4W_LOCKHANDLER_NO_LOCK$ Then * couldn't get the lock on this item - let the user choose another ID error = "Unable to lock record '":ID:"' in table '":table:"'" Gosub chooseAnother Return End If stat = O4W_LOCKHANDLER_ITEM_CHANGED$ Then * someone else changed the record while we worked on it - let the user choose another ID error = "Record '":ID:"' in table '":TABLE:"' has been changed by another user" * remember to unlock our current record, though Call o4wi_lockhandler(O4W_LOCKHANDLER_ACTION_UNLOCK$, table, ID) Gosub chooseAnother Return End If bIsNew Then * this is supposed to be a new record - is it? Read tst From code.fl, id Else tst = "" If tst <> "" Then * some contents for this record already exists - we can't overwrite it - let the user choose another ID error = "Record '":ID:"' already on file" Gosub chooseAnother return end O4WUpdate("ID", ID, textReplace) end Return * * * chooseAnother: * Give users option To rename record * Use our special dialog for the entry prompts O4WSectionStart('specialDialog', selectMe:O4WResponseStyle()) * save the loaded-in contents from the browser O4WStore(INFO, "INFO", "INFO") O4WText(Error) O4WBreak() O4WText("Enter a new name for this record (if desired): ") O4WTextBox("", "", "",'' ,"NEWID") O4WBreak() O4WBreak() O4WButton(ACTION, ACTIONBTN:"2", selectMe) O4WQualifyEvent(ACTIONBTN:"2", "CLICK") O4WSectionEnd('specialDialog') * show the dialog O4WDialog('specialDialog', 'Error During ':ACTION) Return * * displaySection: * show the editable record O4WHeader("Table: ":TABLE:"; Record: ":ID, 3) * Determine which fields to display, and how to display them... * call the filter procedure to retrieve the list of valid dictionary items for us DICT.IDS = Function(@FILTER_PROC("O4W_EDIT_RECORD", O4WSessionInfo%, TABLE)) * for each of the dictionaries, determine which one is the 'master' for each field num.ids = dcount(DICT.IDS, @VM) attinfo = "" For each.id = 1 To num.ids thisID = dict.ids<1,each.id> dictInfo = Xlate("DICT.":table, thisID, "", "X") If dictInfo <> "" Then If dictInfo<1> = "F" And dictInfo<28> = "1" Then fno = dictInfo<2> If fno <> "0" Then * determine the various pieces of information about this dictionary that we care about isMV = dictInfo<4> justn = dictInfo<9> Locate justn In "L,R,C,T" Using "," Setting justPosn Then * convert into O4W alignment code justn = Field("0,2,1,-1", ",", justPosn) end colwidth = dictInfo<10> Locate fno In attInfo<1> by "AR" Using @VM Setting posn Else * store this relevant information away in the attInfo array for each field we'll work on attInfo = Insert(attInfo, 1, posn, 0, fno) attInfo = Insert(attInfo, 2, posn, 0, thisID) attInfo = Insert(attInfo, 3, posn, 0, isMV) attInfo = Insert(attInfo, 4, posn, 0, justn) attInfo = Insert(attInfo, 5, posn, 0, colwidth) End end end end Next each.id * Start the table that will contain all the fields to display/edit O4WTableStart('bigEdit') num.atts = dcount(attInfo<1>, @VM) For each.att = 1 To num.atts * retrieve the relevant formatting,etc. information from the attInfo array we built fno = attInfo<1,each.att> fname = attInfo<2,each.att> isMV = attInfo<3,each.att> justn = attInfo<4, each.att> colwidth = attInfo<5,each.att> * set our cell to column one on each row, and set our color for this cell O4WSetCell(each.att, 1,'' , promptColor) * display the field number and the field name O4WText(fmt(fno, "R#3"):". ":fname) * move to column 2 O4WSetCell() * get the current value thisField = info<fno> If isMV = "M" Then * display the multivalues in a 'growing/shrinking' table with a fixed maximum size O4WSectionStart("mvfield_":fno, O4WSizeStyle("", "", "300")) num.val = dcount(thisField, @VM) If num.val = 0 Then num.val = 1 ;* always allow at least one value so we draw at least one textbox O4WTableStart("subtable_":fno) * display some table headers to look nice o4wtableheader("Action") O4WTableHeader("Value") For each.val = 1 To num.val * get the current value thisVal = thisField<1,each.val> * move to the next row, column 1 O4WSetCell("+1", 1) * add in our insert and delete buttons * note the use of a "named style" for each button - either "insbutton" or "delbutton" O4WButton("Ins", "BTN_INS_":fno:"_":EACH.VAL, "insbutton") O4WButton("Del", "BTN_DEL_":fno:"_":EACH.VAL, "delbutton") * move to column 2 O4WSetCell() * display the contents, using the width and alignment from the dictionary * we mark this with the "o4wClearDuplicateVal" named style, so that O4W knows to clear this out when it's duplicated with the "insert" button O4WTextbox(thisVal, colwidth,'' , "FIELD_":fno, "", O4WTextStyle("", "", "", "", justn):"o4wClearDuplicateVal") Next each.val O4WTableEnd("subtable_":fno) * add an additional "add" and "delete" set of buttons to add and delete the last row O4WButton("Add Value", "BTN_ADD_":fno) O4WButton("Delete Value", "BTN_DEL_":fno) * qualify the add and delete buttons O4WQualifyEvent("BTN_ADD_":fno, "ADDTOTABLE", "subtable_":fno, "-1") O4WQualifyEvent("BTN_DEL_":fno, "DELETEFROMTABLE", "subtable_":fno, "-1") O4WSectionEnd("mvfield_":fno) * use a plugin so that, if the number of multivalues exceeds our fixed size, we'll have a scroll bar O4WPlugin("$", "o4wScrollTable", "'subtable_":fno:"','mvfield_":fno:"','0'") End Else * _should_ be single valued display here HAVE.VM = Count(thisField, @VM) HAVE.SVM = Count(thisField, @SVM) If HAVE.VM+HAVE.SVM Then * what do we do about multivalue data here? Treat this as a text area... Convert @VM:@SVM To @tm:@tm In thisField O4WTextArea(thisField, colwidth, 5, "0", "FIELD_":fno) * remember whether we had any @VM - if we did, that's what we'll use when we read the data back in O4WStore(HAVE.VM, "", "FIELD_":fno:"_TYPE") End Else * actual single valued data, as expected * display in a text box, justified and sized as specified in the dictionary O4WTextbox(thisField, colwidth,'' , "FIELD_":fno, "", O4WTextStyle("", "", "", "", justn)) end end Next each.att O4WTableEnd('bigEdit') * use our "named style" for the insert and delete buttons to send a single qualify event for _all_ those buttons * note the use of "." before the style name - this tells the qualify event that this applies to an entire named style, and not a specific O4W control O4WQualifyEvent(".delbutton", "deleterow") O4WQualifyEvent(".insbutton", "insertrow") * save the ID and table name O4WStore(ID, "", "ID") O4WStore(TABLE, "", "TABLE") * save the number of fields we operated on (so we know how many to retrieve) O4WStore(NUM.ATTS, "", "MAXFIELDS") O4WBreak() * display our buttons O4WButton("Save", "BTNSAVE") O4WSpace(5) O4WButton("Cancel", "BTNCANCEL") O4WSpace(5) O4WButton("Delete", "BTNDELETE") * and qualify our buttons O4WQualifyEvent("BTNSAVE", "CLICK") O4WQualifyEvent("BTNCANCEL", "CLICK") O4WQualifyEvent("BTNDELETE", "CLICK") Return * * getID: * build section For entry of ID Call Set_Status(0) Call Rlist("SELECT ":TABLE:" BY @ID", '5') DONE = 0 ids = "" Loop Readnext thisid Else DONE = 1 Until DONE Do ids<1,-1> = thisid Repeat If ids <> "" Then O4WListBox('Select...', '<none>', 'ITEM','' , 'itmNONE') O4WListBox(ids, ids, 'ITEM') End Else O4WText("<No records found>") end return