How to search and display the fields from a table in an editor widget using progress 4gl - progress-4gl

Accept a customer number and then output the details of each order and items to an editor widget.
Here customer , order ,order-line and items are table names.
where as customer and order tables have cust-num as common field , order and order-line(table-name) and order have order-num as common field , order-line and item have item-num as common field.
now i have to use a fill-in (f1 is object name) to get the cust-num and use a search button (search1 as object name) to find the corresponding fields and display them in the editor widget ( editor-1 as object name).
define temp-table ttcustomer
field custnum like customer.cust-num
field cname like customer.name
field orders like order.order-num
field items like item.item-num
field itemname like item.item-name .
find first customer WHERE customer.cust-num = input f1 NO-LOCK .
create ttcustomer .
assign
ttcustomer.custnum = customer.cust-num
ttcustomer.cname = customer.name.
for each order WHERE Order.cust-num = input f1 NO-LOCK .
assign
ttcustomer.orders = order.order-num.
for each order-line where order-line.order-num = order.order-num no-lock.
for each item where item.item-num = order-line.item-num no-lock.
assign
ttcustomer.items = item.item-num
ttcustomer.itemname = item.item-name.
end.
end.
end.
i have written this code in search button .
how can i display the temp-table ttcustomer in editor widget please help me :)

You'd probably be better off using a browser instead of an editor. But if you are going to use an editor, this should give you what you need:
DEFINE VARIABLE edData AS CHARACTER NO-UNDO.
FOR EACH ttcustomer:
edData = edData + ttcustomer.items + ", " + ttcustomer.itemname + CHR(10).
END.
editor-1:SCREEN-VALUE = edData.
The editor is just text so you won't be able to do any record selection/manipulation like you can with a browser. And if you have many ttcustomer records, you run the risk of overflowing the 32k character string size limit.

Related

libreoffice base create a list filtered by another list's value

I have a table of provinces and a table of cities with ProvienceID. In a form, I want to create a list of cities filtered by selected value of provience list.
How can I do that?
I can create both lists but Cities list shows all cities from all provinces but i want to show only cities from the province that I have selected in Provinces list.
I have another table "Users" with "CityID" and "ProvinceID" that my form edits it and I need to save selected values of Province and City Lists in it, not only show it in the form.
Create two example tables named "Provinces" and "Cities".
ProvinceID Name
~~~~~~~~~~ ~~~~
0 South
1 North
2 Large Midwest
3 Southeast
4 West
CityID Name ProvinceID
~~~~~~ ~~~~ ~~~~~~~~~~
0 Big City 2
1 Very Big City 2
2 Rural Village 1
3 Mountain Heights 0
4 Coastal Plains 4
5 Metropolis 2
Create a query called "ProvinceNames":
SELECT "Name" AS "Province"
FROM "Provinces"
ORDER BY "Province" ASC
Create a query called "Province of City":
SELECT "Provinces"."Name" AS "Province", "Cities"."Name" AS "City"
FROM "Cities", "Provinces" WHERE "Cities"."ProvinceID" = "Provinces"."ProvinceID"
ORDER BY "Province" ASC, "City" ASC
In the form, create a table control based on the query "ProvinceNames".
Using the Form Navigator (or the Form Wizard), create a subform for query "Province of City".
Right-click on subform and choose Properties. Under Data tab:
Link master fields "Province"
Link slave fields "Province"
Create a table control for the subform as well. Now, the cities shown in the subform control depend on the province selected in the main form control.
EDIT:
Here is an example using a filter table to store the current value of the list box. Create two more tables named "Users" and "FilterCriteria".
UserID Name ProvinceID CityID
~~~~~~ ~~~~~~~ ~~~~~~~~~~ ~~~~~~
0 Person1 1 2
1 Person2 2 0
RecordID ProvinceID CityID
~~~~~~~~ ~~~~~~~~~~ ~~~~~~
the only 0 0
We'll also need two Basic macros which can be stored in the document or in My Macros. Go to Tools -> Macros -> Organize Macros -> LibreOffice Basic.
Sub ReadProvince (oEvent as Object)
forms = ThisComponent.getDrawPage().getForms()
mainForm = forms.getByName("MainForm")
cityForm = forms.getByName("CityForm")
listboxProvince = mainForm.getByName("listboxProvince")
listboxCity = cityForm.getByName("listboxCity")
selectedItemID = listboxProvince.SelectedValue
If IsEmpty(selectedItemID) Then
selectedItemID = 0
End If
conn = mainForm.ActiveConnection
stmt = conn.createStatement()
strSQL = "UPDATE ""FilterCriteria"" SET ""ProvinceID"" = " & selectedItemID & _
"WHERE ""RecordID"" = 'the only'"
stmt.executeUpdate(strSQL)
listboxCity.refresh()
lCityCol = mainForm.findColumn("CityID")
currentCityID = mainForm.getInt(lCityCol)
cityForm.updateInt(cityForm.findColumn("CityID"), currentCityID)
listboxCity.refresh()
End Sub
Sub CityChanged (oEvent as Object)
listboxCity = oEvent.Source.Model
cityForm = listboxCity.getParent()
mainForm = cityForm.getParent().getByName("MainForm")
lCityCol = mainForm.findColumn("CityID")
selectedItemID = listboxCity.SelectedValue
If IsEmpty(selectedItemID) Then
selectedItemID = 0
End If
mainForm.updateInt(lCityCol, selectedItemID)
End Sub
Now we need to set up the form like this. In this example, I used two top-level forms instead of a subform. ProvinceID and CityID text boxes are not required but may be helpful in case something goes wrong.
To start creating this form, use the form wizard to create a new form and add all fields from the Users table.
Now, in the Form Navigator, create a form called "CityForm". Content type is SQL command, and Content is:
SELECT "RecordID", "ProvinceID", "CityID" FROM "FilterCriteria"
WHERE "RecordID" = 'the only'
Next, create the "listboxProvince" list box under MainForm. Data Field is "ProvinceID", and List content is the following Sql.
SELECT "Name", "ProvinceID" FROM "Provinces" ORDER BY "Name" ASC
Finally, create the "listboxCity" list box under CityForm. Data Field is "CityID", and List content is the following Sql.
SELECT "Name", "CityID" FROM "Cities" WHERE "ProvinceID" = (
SELECT "ProvinceID" FROM "FilterCriteria"
WHERE "RecordID" = 'the only')
Macros are linked under the Events tab of each control.
Assign "After record change" of the MainForm to ReadProvince().
Assign "Changed" of listboxProvince to ReadProvince().
Assign "Changed" of listboxCity control to CityChanged().
The result allows us to select the Province to filter the list of Cities. Provinces and Cities that are selected are saved in the Users table.
There is another approach which may be better that I have not had time to explore. Instead of the "FilterCriteria" table, apply a filter to the Cities list. The relevant code in ReadProvince() would look something like this.
cityForm.Filter = "ProvinceID=" & selectedItemID
cityForm.ApplyFilter = True
cityForm.reload()
cityForm.absolute(0)
Whatever approach is taken, a complete solution requires complex macro programming. To make it easier, you may decide to use a simpler solution that is not as powerful. For more information, there is a tutorial at https://forum.openoffice.org/en/forum/viewtopic.php?t=46470.
EDIT 2
A solution that requires fewer queries is at https://ask.libreoffice.org/en/question/143186/how-to-use-user-selected-value-from-combobox1-in-combobox2-select-statement/?answer=143231#post-id-143231. The second list box is based on a list of values instead of an SQL query.

how to List the top ten customers form the given start date with greater cost/price of orders using progress 4gl

a single customer could have multiple orders and corresponding cost.
now the cost/price has to be added and according to the max price the customer's should be displayed.
i have just written the skeleton of the prog , please help me.
define temp-table ttorder
field ocust-num like order.cust-num
field oorder-num like order.order-num.
define variable i as int.
define variable a as int.
define temp-table ttorderln
field oprice like order-line.price.
for each order where order-date > 01/08/93 /*by cust-num*/ .
create ttorder .
assign
ttorder.ocust-num =cust-num
ttorder.oorder-num = order-num.
for each order-line where order-line.order-num = ttorder.oorder-num break by ocust-num.
i = order-line.price.
a = i + a.
/* display cust-num order-num a. */
if last-of (ttorder.ocust-num) then
do:
create ttorderln.
ttorderln.oprice=a.
end.
display ttorderln.
end.
end.
If you're looking for the top customers, you should use a temp table based on customers, not orders. Go through the orders and order lines, accumulating the amounts by customer.
define temp-table ttcust
field cust-num like order.cust-num
field order-tot as decimal
index idx1 cust-num
index idx2 order-tot.
define variable i as integer.
for each order where order-date > 01/08/93:
/* Find or create a customer temp record. */
find first ttcust where ttcust.cust-num = order.cust-num no-error.
if not available(ttcust) then
do:
create ttcust.
ttcust.cust-num = order.cust-num.
end.
/* Add up the order lines. */
for each order-line where order-line.order-num = order.order-num no-lock:
ttcust.order-tot = ttcust.order-tot + order-line.extended-price.
end.
end.
/* Display the top 10. */
for each ttcust by order-tot descending:
i = i + 1.
message "Cust: " ttcust.cust-num skip
"Total: " ttcust.order-tot view-as alert-box.
if i >= 10 then leave.
end.

Search and filter in MS Access forms

Background: I have a table named 'SNR_log', out of which I have queried to obtain below list.
Next,I designed a split form, with a text box.In this text box SNR is enterted to find and filter from the list. To perform this operation 'Apply filter' macro is used with a where condition,
[SNR] Like "*" & [Forms]![SNR_History]![Text17] & "*"
Problem: Is it possible to filter by inserting SNR in the textbox and obtaining the below result.
In other words, with SNR as input, display UID and all the SNR under that UID.
You're not going to be able to do this in an "Apply Filter" macro. You're going to need some VBA behind the scenes.
The first thing you want to do is query your data for the UID. You would do that like this:
Dim db as Database
Dim Rec as Recordset
Dim UIDx as Integer
Dim strSQL as String
Dim strSQL2 as String
Set db = CurrentDB
strSQL = "SELECT UID FROM SNR_Log " & _
"WHERE [SNR] Like ""*" & [Forms]![SNR_History]![Text17] & "*""
Set Rec = db.OpenRecordSet(strSQL)
'Now grab the UID that's returned and put it inside a variable
UIDx = Rec(0)
Now you have your UID that you want to query on. So set up your RecordSource based on that:
strSQL2 = "SELECT * FROM SNR_Log WHERE [UID] = " & UIDx & ""
Forms!Me!frmSubFormName.RecordSource = strSQL2
Forms!Me!frmSubFormName.ReQuery
That will change the record source of the form, and refresh the form so it's looking at the current record source you just set it to.
You may have to putz with strSQL a bit, I can't remember exactly how to work with the Like operator inside a SQL string. I'm pretty sure it's correct, though.
I was able to work around it in a simple way. I created a main form and subform. The record source for subform is the text box where SNR is entered. Once a command button is clicked with following event procedure in it I obtained the desired result;
Private Sub Command4_Click()
Text0.SetFocus
Me.SNR_log_Subform.Form.RecordSource = "Select * from SNR_Log where UID in(Select UID from SNR_Log where SNR like '*" & Text0.Text & "*')"
End Sub

Returning to a text field block again from the query

I have a form that prompt for customer name and pass that value to a query,
FORM compname
customer.cusname
WITH FRAME f1.
UPDATE compname WITH FRAME f1.
This form wil pass the compname value to the following query,
FOR EACH customer WHERE customer.name = compname NO-LOCK :
if available(company) then
do:
CREATE temptt.
assign temptt.num = customer.kco
temptt.no = string(customer.kco)
temptt.name = customer.name
temptt.status = false.
END.
else
message "not matched " view-as alert-box.
end.
What i want to do is, if search does not receive any rows, it should again prompt for customer name. what should i do for this ??
how do i call that form again in the "else block" and also, currently I am giving the complete name in the field, but i want to give part of the name, for eg., customer name is "John Smith Doe" and if i input "Smith" it should retrieve the related rows. How should i alter the "Where" clause for this ?? Please help me.
Repeating the search
This can be done in several ways. Here's one example:
DEFINE TEMP-TABLE customer NO-UNDO
FIELD cusname AS CHARACTER
FIELD num AS INTEGER.
DEFINE VARIABLE compnum AS INTEGER NO-UNDO.
DEFINE VARIABLE compname AS CHARACTER NO-UNDO.
DEFINE QUERY qSearch FOR customer.
FORM compname compnum WITH FRAME f1.
/* Create some bogus data */
CREATE customer.
ASSIGN customer.cusname = "john doe"
customer.num = 1.
CREATE customer.
ASSIGN customer.cusname = "jane doe"
customer.num = 2.
CREATE customer.
ASSIGN customer.cusname = "name name"
customer.num = 3.
loop:
REPEAT:
CLEAR FRAME f2 ALL.
UPDATE compname compnum WITH FRAME f1.
/* Quit if neither name or number is entered */
IF compname = "" AND compnum = 0 THEN
LEAVE loop.
/* If num is entered - search by it, otherwise by name */
IF compnum <> 0 THEN DO:
OPEN QUERY qSearch FOR EACH customer NO-LOCK WHERE customer.num = compnum.
END.
ELSE DO:
OPEN QUERY qSearch FOR EACH customer NO-LOCK WHERE customer.cusname MATCHES "*" + compname + "*".
END.
GET NEXT qSearch.
DO WHILE AVAILABLE customer:
IF AVAILABLE customer THEN DO:
DISPLAY customer WITH FRAME f2 10 DOWN.
DOWN WITH FRAME f2.
END.
GET NEXT qSearch.
END.
/* If we have results - leave the loop otherwise try again */
IF QUERY qSearch:NUM-RESULTS = 0 THEN
LEAVE loop.
END.
MESSAGE "Quitting" VIEW-AS ALERT-BOX.
Searching for part of the name
There are a couple of operators for matching strings:
BEGINS
Tests a character expression to see if that expression begins with a second character expression.
Syntax:
expression1 BEGINS expression2
Example:
FOR EACH customer WHERE NO-LOCK customer.cusname BEGINS "john":
MATCHES
Compares a character expression to a pattern and evaluates to a TRUE value if the expression satisfies the pattern criteria.
The pattern can contain wildcard characters: a period (.) in a particular position indicates that any single character is acceptable in that position; an asterisk (*) indicates that any group of characters is acceptable, including a null group of characters.
Syntax:
expression1 MATCHES expression2
Example:
FOR EACH customer NO-LOCK WHERE customer.cusname MATCHES "*doe*":
MATCHES sounds like what you're after but be adviced: MATCHES will not utilize indices in the database so whole tables will be scanned. This can/will effect performance and possibly make your queries take long time.
The WHERE clause above replaced with MATCHES would look something like this:
FOR EACH customer NO-LOCK WHERE customer.cusname MATCHES "*" + compname + "*":
CONTAINS
There's also a third operator called CONTAINS that uses something called a WORD index. That will require you or your DBA to create these kind of indices in the database first. Read more about word indices and CONTAINS in the online help or in the PDF found here: Progress ABL Reference (page 1004).
CONTAINS is probably a better idea than MATCHES but will require you to make changes to your database as well.

Using cascading combo boxes in a datasheet subform

In Access 2010, I have tables Task and Action that have a many-to-many relationship through table ActionTask. In the form for Task, I want to put a subform for all the Actions related to the current task through the ActionTask junction table.
This, in itself, I can do.
The trick is that Action is actually the bottom rank of a four-tier hierarchy of tables.
Each Action belongs to a Goal, each Goal belongs to a Theme, each Theme belongs to a Strategy.
Rather than just have a combo box listing all the available Actions, I'd like to use cascading combo boxes for the Strategy/Theme/Goal/Action.
This I can also do.
The problem is when I use this technique in a datasheet, if I select a given row, the selected row shows the proper Strategy/Theme/Goal/Action, but in all the other rows
the Action is blank
the Strategy/Theme/Goal is set to the current row's values, rather than that row's values
I've tried using the "Continuous Forms" view rather than the "Datasheet" view, but the result is pretty much the same.
I think I know why (the Me.StrategyCombo = ... stuff in my Form_Current callback), but I don't know another way to achieve that.
How can I make the subform display all the rows properly?
Here's the Access file, but all the relevant details should be below.
Tables:
Strategy : (ID, Desc)
Theme : (ID, StrategyID, Desc)
Goal : (ID, ThemeID, Desc)
Action : (ID, GoalID, Desc)
Task : (ID, Desc, ...)
ActionTask : (ActionID, TaskID)
Form Settings:
[Forms]![Task]![ActionTaskSub]:
Link Master Fields: ID
Link Child Fields : TaskID
[Forms]![Task]![ActionTaskSub].[Form]:
On Current:
Private Sub Form_Current()
' when the form loads a record, should reverse propegate
' action > goal > theme > strategy
Dim goalID, themeID, strategyID
' figure out the goal, theme, and strategy that go with this action
If (Me.ActionID) Then
goalID = DLookup("[GoalID]", "Action", "[ID] = " & CStr(Me.ActionID))
themeID = DLookup("[ThemeID]", "Goal", "[ID] = " & CStr(goalID))
strategyID = DLookup("[StrategyID]", "Theme", "[ID] = " & CStr(themeID))
End if
' populate the combo boxes and make the appropriate selections
Me.StrategyCombo = strategyID
Me.ThemeCombo.Requery
Me.ThemeCombo = themeID
Me.GoalCombo.Requery
Me.GoalCombo = goalID
Me.ActionCombo.Requery
Me.ActionCombo = Me.ActionID
End Sub
[Forms]![Task]![ActionTaskSub].[Form]![StrategyCombo]:
Row Source : SELECT [Strategy].[ID], [Strategy].[Desc] FROM [Strategy];
After Update:
Private Sub StrategyCombo_AfterUpdate()
Me.ThemeCombo = Null
Me.ThemeCombo.Requery
Call ThemeCombo_AfterUpdate
End Sub
[Forms]![Task]![ActionTaskSub].[Form]![ThemeCombo]:
Row Source : SELECT [Theme].[ID], [Theme].[Desc] FROM [Theme] WHERE
[Theme].[StrategyID] = [Forms]![Task]![ActionTaskSub].[Form]![StrategyCombo];
After Update:
Private Sub ThemeCombo_AfterUpdate()
Me.GoalCombo = Null
Me.GoalCombo.Requery
Call GoalCombo_AfterUpdate
End Sub
[Forms]![Task]![ActionTaskSub].[Form]![GoalCombo]:
Row Source : SELECT [Goal].[ID], [Goal].[Desc] FROM [Goal] WHERE
[Goal].[ThemeID] = [Forms]![Task]![ActionTaskSub].[Form]![ThemeCombo];
After Update:
Private Sub GoalCombo_AfterUpdate()
Me.ActionCombo = Null
Me.ActionCombo.Requery
End Sub
[Forms]![Task]![ActionTaskSub].[Form]![ActionCombo]:
Row Source : SELECT [Action].[ID], [Action].[Desc] FROM [Action] WHERE
[Action].[GoalID] = [Forms]![Task]![ActionTaskSub].[Form]![GoalCombo];
I solved the issue of dependent (Cascading) comboboxes in a datasheet.
By nature, if combobox 1 is dependent on a value from combobox 2, it will apply the "rowsource" from the current row to all rows in the datatable
Solution:
1 In the load or designer, define the rowsource to include all rows so the datavalue behind it will always match with an option in the rowsource (otherwise you display a blank value)
ie:
Private Sub Form_Load()
Me.cmbProjects.RowSource = "SELECT [Projects].[ProjectPK], [Projects].[ProjectName], [Projects].[PI_ID] FROM [Projects] ORDER BY [ProjectName] "
End Sub
2 In the dependent combobox get focus event, redefine the rowsource to be based on a value from the other combo box that the former depends on. Requery the control
Private Sub cmbProjects_GotFocus()
Me.cmbProjects.RowSource = "SELECT [Projects].[ProjectPK], [Projects].[ProjectName], [Projects].[PI_ID], [Projects].[Status] FROM [Projects] WHERE [PI_ID] = " + CStr(cmbPIs.Value) + " ORDER BY [PI_ID], [ProjectName] "
cmbProjects.Requery
End Sub
3 In the dependent combobox lost focus event, redefine the rowsource to NOT be based on a value from the other combo box that the former depends on.
Private Sub cmbProjects_LostFocus()
Me.cmbProjects.RowSource = "SELECT [Projects].[ProjectPK], [Projects].[ProjectName], [Projects].[PI_ID], [Projects].[Status] FROM [Projects] ORDER BY [PI_ID], [ProjectName] "
cmbProjects.Requery
End Sub
You can't. Any action applies to the subform current record but appears to affect all controls. There are various work-arounds.
As far as I know you can't, but you can work around it by creating a continuous form, putting your fields in tabular layout, then adding a textbox for each combobox (if field is prodID, then I name the textbox txtprodID). Move the textbox right on top of the combobox, but a little narrower, just shy of the combo list arrow, then write code to update the textbox.