Lisa of
RMCis faxed me Kevin's original post. I typed it up. Couple of observations. This must have been before Kevin discovered that HTML worked on baanfans. That is why he mentions about code not formatting properly. His later posts were always formatted. I have used VB tags for the code when typing this.
This solution works iff you have source code. And like he mentions in his post, you better have a very good reason to print x of y. This is a lot of coding for that functionality. He did mention in a
recent post that he found a simpler method that did not require any changes to the session or the report. I believe he posted yet another solution after this one. But I am not sure. If someone has used it or has an electronic copy somewhere, please post it.
~Vamsi
<hr>
NOTE! Though this is long, I've posted it as I thought many folks may be interested in this solution.
Sorry about the formatting for the code samples, this message board trashes the indents.
I've not tried to do this myself ... that's what I was going to say a couple of days ago, but I was challenged by this question and I had fun solving it. I've run into this before but been able to get out fo doing page-of processing but decided to actually figure it out. Nove I have a general solution (perhaps someone else has something easier, but I don't understand how the second footer/lattr.enddata suggestion is supposed to do this either).
In most environments, page of processing can be quite difficult. The usual requirement is to count the number of pages and then print as, most times, the data output is too complex to compute the number of pages. If you can compute the number of pages up front it's probably a very simple and consistent report.
The other solution is to actually run the full report two times. The first time simply does not physically output anything but is used to get the actual final result. This can be done using the original data as long as the data is not changing during the report. I have implemented this solution more or less generically. You must also be careful with this solution for reports that update as you must not do so during the first pass (ofcourse the results could then be different as your report may process the resultant records differently after the update).
Here's what you can do. First, let's look at the report itself as this is the easiest to change. If you do not have a report script you will have to define one. Declare 2 new variables and add a before.program and after.program section (if there isn't already one):
Code:
domain tcmcs.long total.pages
domain tcbool page.of
before.program:
import("page.of", page.of)
if page.of then
total.pages = 0
else
import("total.pages", total.pages)
endif
after.program:
|Send the current and final page count back to the parent session
if page.of then
export("total.pages", total.pages)
endif
Then, change the header (or any other layout) of the report to include the variable "total.pages" where you need to show the total page count. That's it for the report. Comile it.
Now, for the tougher stuff; the main session (you must have source to make these changes). Add the following variable declarations:
Code:
long spl.date
long spl.fontnumber
long spl.left.mrg
long spl.pr.copies
long spl.pg.length
long spl.pg.width
long spl.time
long spl.view.rtl
string spl.device(14)
string spl.fileout(100)
string spl.paper.type(6)
domain tcbool report.has.data
long sp1
long sp2
long rptid
#define SEND(x) brp.ready(x)
^ report.has.data = true
|*** Report variable additions
extern domain tcmcs.long total.pages
extern domain tcool page.of
I've included a define to make life a little easier later on. Most of these variables (the spl.* ones) are used to save off the spooler variables for later use. What we will be doing is opening up 2 separate spool devices. The first one the user has control over and will be where the report will be printed. The second is for internal processing to get the total page count. This will work best on Unix systems as the output device can be directed to /dev/null, though there may be a good solution for NT as well (nul device may work, I've not tried it).
I'll just include my code from my test along with the comments. Hopefully this will explain the rest.
Some of the exit code (at the choice.again() call) is not the greatest as a lot of statements get repeated, but does work. I've also taken the quick route of using message(), instead of mess() and message codes, when showing error/warning texts.
Code:
choice.print.data:
on.choice:
|Initial open to get the user's selections, report defaults to choice
|from session
sp1 = spool.open("", "", 1)
if (sp1 <= 0) then
choice.again()
endif
save.spooler.choices()
|Now open the temporary device to NULL to run the report
|for page of processing.
|Note: pg.length must be reset in case the two device definitions
|are different; we want page of calculated against the real device
spool.fileout = "/dev/null" |Platform specific! (i.e. Unix)
sp2 = spool.open("", "ASCIF", 0)
spool.pg.length = spl.pg.length
if (sp2 <= 0) then
message("Unable to open spooler for page of processing")
spool.id = sp1
spool.close()
reset.spooler.choices()
choice.again()
endif
|Run the report to the temporary device and expect a total page
|count back. Note! Report program MUST export to the local
|total.pages variable
report.has.data = false
page.of = true
spool.id = sp2
rptid = brp.open(spool.report, "", 0)
if (rptid > 0) then
read.main.table() |Process the report
brp.close(rptid)
spool.id = sp2
spool.close()
|Determine if hte report will have any data the FIRST time
|to save some processing time.
if not report.has.data then
message("Nothing found to print")
spool.id = sp1
spool.close()
reset.spooler.choices()
choice.again()
endif
else
message("Unable to open report!")
spool.id = sp2
spool.close()
spool.id = sp1
spool.close()
choice.again()
endif
reset.spooler.choices()
|Run the real report (spooler#2 - to null device should already
|be closed by this time)
page.of = false
spool.id = sp1
rptid = brp.open(spool.report, "", 0)
if (rptid > 0) then
read.main.table() |Process the report
brp.close(rptid)
spool.id = sp1
spool.close()
else
message("Unable to open report!")
spool.id = sp1
spool.close()
endif
|*** functions
**************************************************************
functions:
function save.spooler.choices()
{
|Save contents of spool.* variales changed by the temporary
|output report so the user's last selections are not lost.
spl.date = spool.date
spl.fontnumber = spool.fontnumber
spl.left.mrg = spool.left.mrg
spl.pr.copies = spool.pr.copies
spl.pg.length = spool.pg.length
spl.pg.width = spool.pg.width
spl.time = spool.time
spl.view.rtl = spool.view.rtl
spl.fileout = spool.fileout
spl.device = spool.device
spl.paper.type = spool.paper.type
}
function reset.spooler.choices()
{
|Reset all the spooler variables for later use by the report or
|this session.
spool.date = spl.date
spool.fontnumber = spl.fontnumber
spool.left.mrg = spl.left.mrg
spool.pr.copies = spl.pr.copies
spool.pg.length = spl.pg.length
spool.pg.width = spl.pg.width
spool.time = spl.time
spool.view.rtl = spl.view.rtl
spool.fileout = spl.fileout
spool.device = spl.device
spool.paper.type = spl.paper.type
}
function read.main.table()
{
|These routines are not included. However, whenever a record is
|ready to send to the report, I used my macro, e.g. SEND(rptid). This
|way the report.has.data variable is set whenever something has
|actually been sent.
... rest of program ...
}
Caution! Baan will actually build two temporary files. It appears that if the output is directed to /dev/null or perhaps ASCIF this is immediately removed from $BSE/tmp. Be careful you don't fill up the spool directory with the output from these.
It would be best if a full spooler output device would not need to be opened and the physical writes could be skipped. However, setting lattr.print = false in the report during page of processing won't work as the line counter will not be incremented. I've used similar logic in some COBOL report routines but was able to keep the final output from occurring as I had full control of the code. This could be done in Baan as long as you wrote your report from scratch and skipped using the report writer (not a good solution). Any one have any ideas on this?
I hope this wasn't too long and the cost of running the report 2 times doesn't outweigh the result.
Have fun with this! Let me know if you've been able to make use of this solution or if there is a better way/improvement you can think of.
Kevin Brock
Senior Technical Consultant
Quality Consultants, Inc.
...a member of the Process Technology Group of companies
Atlanta, Georgia
Ask me about QKEY: a new solution for developing additions to Baan standard code when no source is available.