how to List the top ten customers form the given start date with greater cost/price of orders using progress 4gl - 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.

Related

How to add blank lines to complete an Access report so that it matches a printed form?

I deal with a bunch of government forms, and I constantly find myself trying to get Access 2013 to output a report that matches a pre-printed form.
Most of the forms are now in .pdf form, so Access doesn't handle them well (to my knowledge) without additional software, which I cannot install on user production computers.
So I usually re-create the form as an Access report, but I have real difficulty when I have only enough records for a half a page.
How do I get a report to print the required records, and then fill the page with blank records so the "form" looks correct?
I'd be willing to try any possible solution - I've even gone so far as to create blank records to try to make this work. My goal, however, is to automate this process so any user can generate the report and it prints out correctly without a bunch of fiddling.
On this form, some or all of the lines might be used, and each person (I have ~550 people who each have an individual form) has a different number of lines, depending on the number of jumps they have completed.
I would have a dummy table with just a single numeric field called id. Populate this with a number of records greater than the biggest number of 'extra' records you're ever going to need to fill your form, using numbers from 1 upwards for id. Now say your original record source query for the report is:
select field1, field2, field3, field4
from myTable
order by field1
and you always want 15 rows to fill your form, then change the report's record source to:
select top 15 sort_seq, field1, field2, field3, field4
from (
select 0 as sort_seq, field1, field2, field3, field4
from myTable
union
select id, null, null, null, null
from dummyTable
) as X
order by sort_seq, field1
Ok, so you are recreating the complete PDF form as Access report. And the lower part is the details section of a continuous report.
The easiest way is indeed to create the correct number of empty records, but do this automatically (e.g. using DAO Recordset.AddNew).
I like to use a separate dummy table for this (with the same structure as the original table) to not pollute the original table with dummy records.
The form has 20 lines, so if your original table has 7 records for the current person, insert 13 records into the dummy table, using a large PK so they get sorted to the bottom.
Then use a query as record source for the report, joining the master table with a UNION of Jumps and Dummy table.
Alternatively there is the Report.Line() method: https://msdn.microsoft.com/en-us/library/office/ff198297.aspx
But this will be a lot more complicated to get it right.
I hope you have found the solution to this issue. I am posting this in the hope that if someone else finds this same question they will find this one as the best possible solution, as I have been struggling for about 2 days now.
I suppose you have at least two tables and a query where you mix information from both, and your report is made out from this query. I will call this your Query1, so the trick is that when you open the Report from your Form, you will use the label number of your Form to make the filter of the Query1 from where you will get the information for your report. Like this:
rectotal = DCount("*", "Quote-Report", "[Quote_No]=" & stQtNo)
This instruction will count how many records you have on your “Quote-Report” Query that are of the same value of the [Quote_No] field inside your Quote Form , as you are assigning the stQtNo variable to the Form with the name “Quote”; and put this value on your rectotal variable.
Now with rectotal you need to determine how many lines you want in your report. I used 28 so the following formula will give how many blank rows needed to fill the page.
If rectotal / 28 - Int(rectotal / 28) = 0 Then
x = 28
Else
x = 28 * (rectotal / 28 - Int(rectotal / 28))
End If
blanklines = 28 - x
tLines = rectotal + blanklines
Of course you can use 15, 20 or as many rows you want your report to print per page. The variable blanklines will give you this number.
Then I use the recordset procedure from Access to open the table where I have the information that will print in the detail section of the report and fill with as many records as I need using the blanklines variable and the Do Until Loop.
Set db = CurrentDb
Set rs = db.OpenRecordset("Item_QD", dbOpenDynaset, dbseechanges)
Item_QD is the table
where I want to insert new records to fill the report detail section.
x = 0
rs.MoveLast
Do Until x = blanklines
rs.AddNew
rs![Quote_No] = stQtNo
rs![CODE No] = "Z" & x I use the Z and the number so I can erase the records once I don’t need them anymore.
x = x + 1
rs.Update
Loop
Please note that you need to Dim db As Database and Dim rs As Recordset variables outside your private sub in module header. Also your Access needs to be running these two classes so you can use them.
Finally once you open your report you have to go to your detail section and do a requery command to refresh all the rows prior to print. Need to format your detail cells in order to print the grid line and the rest is history.
The beauty of this procedure is that you can add and handle as many rows as you want or need inside your report and make a professional look of your quotes, packing slips, purchase orders, etc.
Hope this solves many problems. For a fast reference on how to use and manage ACCESS record set visit: https://www.youtube.com/watch?v=WNm17l54z1c
Private Sub Report_Open(Cancel As Integer)
Dim blanklines, tLines As Integer
Dim rectotal, x As Long
Dim stQtNo As String
Set db = CurrentDb
Set rs = db.OpenRecordset("Item_QD", dbOpenDynaset, dbseechanges)
stQtNo = Forms![Quote].[Quote_No]
rectotal = DCount("*", "Quote-Report", "[Quote_No]=" & stQtNo)
If rectotal / 28 - Int(rectotal / 28) = 0 Then
x = 28
Else
x = 28 * (rectotal / 28 - Int(rectotal / 28))
End If
blanklines = 28 - x
tLines = rectotal + blanklines
x = 0
rs.MoveLast
Do Until x = blanklines
rs.AddNew
rs![Quote_No] = stQtNo
rs![CODE No] = "Z" & x
x = x + 1
rs.Update
Loop
End Sub
Another solution. Code behind report. I use it for a simple report without grouping and row height doesn't change.
Option Compare Database
Option Explicit
Public totalCount As Integer
Private Sub Detail_Print(Cancel As Integer, PrintCount As Integer)
printBlankRecords getnumberofBlanks
End Sub
Public Sub printBlankRecords(numberBlanks As Integer)
Dim recordCount As Integer
recordCount = DCount("*", Me.RecordSource)
totalCount = totalCount + 1
If totalCount = recordCount Then
Me.NextRecord = False
'once you get to the last record, stay on last record
ElseIf totalCount > recordCount And totalCount < (recordCount + numberBlanks) Then
Me.NextRecord = False
'make the font and backcolor the same appearing to be empty record for each field
Me.RateID.ForeColor = Me.RateID.BackColor
End If
End Sub
Public Function getnumberofBlanks() As Integer
Dim recordCount As Integer
Dim N As Integer, X As Integer
X = 20
recordCount = DCount("*", Me.RecordSource)
If recordCount <= X Then
getnumberofBlanks = X - recordCount
Else
N = 1
Do While N * 21 + X <= recordCount
N = N + 1
Loop
getnumberofBlanks = (N * 21 + X) - recordCount
End If
End Function

How to split same record in Form Grid?

I need to split the same record two time.
For example, if I have in MyTable five record I need to show in Form's Grid ten record.
If It's possible I can create a View, I just to duplicate one time the same record.
This is my start point :
I want to duplicate the record :
In MyForm can't edit anything, is only View. No Delete, No Edit, No Create
Thanks in advance!
This sounds like an exercise, which we probably shouldn't be giving you the answer to...but try creating MyTableJoin and create a foreign key relation to MyTable.Id then add a field called MyTableJoin.Row and populate the table with 2 matching rows for every 1 row in MyTable. Then on your form, join MyTableJoin.
So:
MyTableJoin.clear();
MyTableJoin.Id = "ID_I";
MyTableJoin.Row = 1;
MyTableJoin.insert();
MyTableJoin.clear();
MyTableJoin.Id = "ID_I";
MyTableJoin.Row = 2;
MyTableJoin.insert();
MyTableJoin.clear();
MyTableJoin.Id = "ID_II";
MyTableJoin.Row = 1;
MyTableJoin.insert();
MyTableJoin.clear();
MyTableJoin.Id = "ID_II";
MyTableJoin.Row = 2;
MyTableJoin.insert();
You can create a union view and add same table twice. Just be shore to select union all option so that you do not get unique results.

How to search and display the fields from a table in an editor widget using 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.

Progress OpenEdge like SQL where IN?

I can do this with a string and it works great.
example:
for each emp no-lock where
lookup(emp.name, "james,john,kerry,david") <> 0:
/* Do something */
end.
Now I have emp.ID that is not a string (it's an integer) how can I do where IN ? I tried similar method as above. but it gives me an error, it says
"incompatible data types"
for each emp no-lock where
lookup(emp.ID, "1,5,89") :
/* Do something */
end.
how do I do this ?
thank yo u
OpenEdge has a SQL-92 engine but you aren't using that. You are using the 4gl engine. There is some limited SQL-89 syntax embedded inside the 4gl engine but it is a bad idea to try to use it. It only leads to pain and suffering
The 4gl has no IN function. To do what you are trying to do with a variable set of integers you would probably want to first create a temp-table and then join the TT with your real table. Something like this:
define temp-table tt_intList
field f1 as integer
.
create tt_intList.
tt_intList.f1 = 1.
create tt_intList.
tt_intList.f1 = 5.
create tt_intList.
tt_intList.f1 = 89.
for each tt_intList, each emp no-lock where emp.Id = tt_intList.f1:
display emp.Id emp.name.
end.
In a large DB table, to make good use of indexes:
FOR EACH emp NO-LOCK WHERE emp.ID = 1 OR emp.ID = 5 OR emp.ID ? 89
A lazy coder in a small table might do:
FOR EACH emp NO-LOCK WHERE LOOKUP (STRING(emp.ID), "1,5,89") > 0

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.