Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations gkittelson on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Report printing records multiple times

Status
Not open for further replies.

Olaf Doschke

Programmer
Oct 13, 2004
14,847
DE
I had an idea of a report printing records multiple times depending on a field iCount in the report driving cursor.

Main ingredient is, the detail band has the on exit expression:
Code:
execscript("Skip iif(nRepeat<nCount,-1,0)")

nCount here is a report variable initialised with iCount when the report group changes, which is set to every record, ie the report groups by RECNO().
nRepeat is another report variable set to 0 at each group start and counting.

The main drawback is, this does not work out for RECNO()=1, because SKIP -1 does not skip before the first record, so next processed record always is RECNO()=2, no matter what iCount is in RECNO()=1.

A simple solution would be keeping an empty first record, but I wonder, if anyone has another idea.
I would otherwise revert to a much simpler concept of processing a report cursor into another one with repeated records, so each record is printed once as usual, but users of a certain library can insert rows with a count anyway and that count simply determines the number of record copies made into the real report driving cursor.

Bye, Olaf.
 
One of my rules of thumb, is to always run a report from a cursor, and not from I table. This is a very good example of why. You can simply have the same number of repeated identical records as you want reports. If necessary, add an extra field with unique random data, since the report writer need something to know when to start a new page.
 
You misundertand, the original data already is a cursor, too, not a table.

Bye, Olaf.
 
Yes, I was sure that it was a cursor in your case, I only wanted to point out to others the imporance of using a cursor, and not a table, to drive reports. If you re-read my answer, you will hopefully understand the solution I suggested. One page = one record. The same report several time = several 99% identical records, the difference being the unique field. The unique field is necessary to tell the report writer to start a new page.
 
Actually it's not about pages but labels, more general detail bands. I also don't see the need for an added field with a different value, unless you have print repeated values unchecked, but that's individual for each report control field anyway and not for a whole record.

A solution also working for me is doing this in BeforeOpenTables of the report DE:

Code:
Local lcOriginalcursor, lcReportCursor, lnI

lcOriginalcursor = Alias()
lcReportCursor = "crsdestroyme"+SYS(2015)

Select * From (lcOriginalcursor) Into Cursor (lcReportCursor) WHERE .F. READWRITE 

Select (lcOriginalcursor)
Scan
   Scatter Name oRecord Memo
   For lnI = 1 To oRecord.iCount
      Insert Into (lcReportCursor) From Name oRecord
   Endfor
EndScan

Select (lcReportCursor)

Finally in the report DE.Destroy() event closing this report cursor copy.

That way no need for groups and variables and no on exit expression.

Anyway, I'd still be glad to hear a trickier solution not needing to copy all the data multiple times for multiple printing.

Bye, Olaf.
 
Right, for labels you don't need the unique field. I use the same trick myself. Why not do it simple, unless you used to be in the military? :)
 
Two issues with this are: This does not apply the FOR conditions of REPORT/LABEL FORM to the copied data and it needs time to copy the data.

The users see the original data and can influence the iCount in a spinner control, therefore it's not an option to create the data as needed initially, that would break the user interface idea of that counter specification.

Bye, Olaf.



 
The users see the original data and can influence the iCount in a spinner control, therefore it's not an option to create the data as needed initially, that would break the user interface idea of that counter specification.

I don't get that. Why can't you give them the user interface with the spinner, let them update the underlying data, but at the point at which they say "OK, I now want to print the report", you generate a cursor with repeated records and use that to drive the report? The user needn't be aware of the cursor. As far as they are concerned, they are simply adjusting a spinner and asking for a report to be printed. How is that breaking the UI?

Mike


__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Of course. But there is no way to put the record pointer before recno()=1 and to change the report engine behaviour to SKIP 1 before the next iteration of printing/rendering occurs. I also tried to put this stuff into the "on entry" expression, but that also doesn't work out. I also tried to always skip -1 and then finally skip 0 or 1, but that just gives weird results.

Thanks for your thought, though, Nasib.

I'm thinking along the lines of an additional cursor with just repeated recnos and a relation into the original cursor data. If the report scans through that cursor the relation will point to the same record iCount times, too, that way. The report controls just would need to reference the fully qualified field names, as the report data then is not in the selected cursor.

Bye, Olaf.
 
OK, this is a compromise for me and will do fine enough.

DE.BeforeOpenTables:

Code:
Local lcOriginalcursor, lcReportCursor, lnI

This.AddProperty("ordertag",Set("Order"))
lcOriginalcursor = Alias()
This.AddProperty("reselect",lcOriginalcursor)
lcReportCursor = "crsdestroyme"+SYS(2015)
This.AddProperty("closealias",lcReportCursor)

Create Cursor (lcReportCursor) (iRecno I)

Select (lcOriginalcursor)
Scan
   For lnI = 1 To iCount
      Insert Into (lcReportCursor) Values (Recno(lcOriginalcursor))
   Endfor
EndScan

Select (lcOriginalcursor)
Set Fields Global
For lnI = 1 To Fcount()
   lcField = Field(lnI)
   Select (lcReportCursor)
   Set Fields To &lcField=&lcOriginalcursor..&lcField
   Select (lcOriginalcursor)
EndFor

Set Order To

Select (lcReportCursor)
Set Relation To iRecno into (lcOriginalcursor)

The SET FIELD statements enable to refer to report cursor fields without fully qualified names, records are not duplicated, only their recno is duplicated into a new report driving cursor relating into the data cursor.

Besides that, there are some finishing moves code in the destroy event of the DE:

Code:
Local lcOrder

Set Fields To
Set Fields Off

Use in Select(This.closealias)
If Used(This.reselect)
   Select (This.reselect)
   If !Empty(This.ordertag)
      lcOrder = This.ordertag
      Set Order To &lcOrder
   Endif
   Go Bottom
   Skip 1
EndIf

Bye, Olaf.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top