DOC #748
=====================================================================
Once-Per-Break Operations in Crystal Reports
=====================================================================
Product: Crystal Reports for R:BASE
=====================================================================
Area: Report Techniques CATALOG: Forms, Reports & Labels
=====================================================================
A common task in R:BASE reports is how to calculate or print a piece
of data only once for a particular break or group. The R:BASE report
writer is a single pass or row-by-row report writer -- it processes a
row and then prints it. It does not have any built in operations or
functions to do a once-per-break operation; to accomplish this, you
need to define variables. Previous R:BASE Exchange articles describe
the variables to define for the most commonly requested once-per-break
operations. The most recent article was published in the May/June 1993
Exchange.
This article shows how to accomplish these same operations in a
Crystal Reports report. Some of the operations require defining
formulas in Crystal, while others take advantage of Crystal's built in
formatting features. Because Crystal is a two-pass report writer -- it
reads and sorts all the records, then prints -- the logic involving
some of the operations is different from that used in R:BASE. The once-
per-break operations described here are:
- Print a descriptive heading on the first detail row without
repeating the heading on every row of detail
- Count the number of breaks (different groups of like data values)
- Add a blank line after every five lines of detail
- Add a lookup value once per break
- Do calculations, such as a sum, of break footer totals.
The techniques cover basic reporting techniques that you will use
throughout your Crystal Reports.
Heading on the first detail line
--------------------------------
Often you need to print heading information on the first detail line
of a break group without repeating the information on every line of
detail. For example, a report based on a transaction table prints the
company name on the first line of the detail section for each company.
A separate group header section is not used. The report might look
like this:
Company A Transaction Detail Information
Transaction Detail Information
Transaction Detail Information
Company B Transaction Detail Information
Company C Transaction Detail Information
Transaction Detail Information
In R:BASE you define variables. In Crystal you format the fields. All
you need to do is check the "Suppress if duplicated" box in the field
format box.
Count the Number of Breaks
--------------------------
When grouping data, you sometimes need to know how many different
groups are in the report. This number is printed in a footer at the
end of the report. You count the groups using the built-in Group
Number Field or by defining formulas.
The Group Number Field only numbers and counts one group, the
innermost. Use this option when you only have one break group in your
report or if you have multiple breaks but want to count the innermost
one. Define formulas to count for other situations.
The Group Number Field is numbering the breaks, it isn't really
counting them per se. It numbers a group and then increments itself.
So after the last group, the value in the GroupNumber function is
actually one more than the number of groups on the report. To print
the correct number of groups, define and locate a formula with the
expression, GroupNumber - 1. The GroupNumber function can be accessed
at the bottom of the functions list in the Formula editor under Other.
Locate the formula in the Page Footer section, or create a Grand Total
section. Format the field to remove the ".00" so it prints just the
integer value.
When the GroupNumber doesn't fit your situation, define formulas using
Crystal's two-pass capability to count the groups. The formulas are
defined using the "WhilePrinting Records" function -- wait to
calculate until all records have been read.
The first formula counts the groups. It is located in either the Group
Header or Group Footer section for the group to count. Hide this
field, you don't want it to print for each group. Define the formula
as follows:
WhilePrintingRecords;
NumberVar VCount;
VCount := VCount + 1
The Crystal formula sets the evaluation time, declares the variable
and increments it. By locating the field in the header or footer
section for the group, it only evaluates once per group and thus
increments by one for each different value in this group.
This formula is placed in a header or footer section to evaluate
correctly, but the value is printed at the end of the report. A second
formula is defined to print the result.
WhilePrintingRecords;
NumberVar VCount;
VCount
Again we identify the evaluation time and declare the variable. This
time we aren't defining a calculation, but simply the name Vcount.
This prints the result of our group calculations. Locate this formula
in a Page Footer or Grand Total section. Format the field to display
integer values only.
You'll notice some differences between defining formulas in Crystal
and variables in R:BASE. Crystal formulas are like programs with the
different commands separated by ";". You don't need parentheses around
the expression and Crystal variables are not "dotted". Also, you don't
equate items in an expression with an "=" sign but you use ":=". Don't
forget to use ":=", if you just use an "=", your formula results
become Boolean -- true/false.
For more information about the GroupNumber function and defining and
using formulas, refer to your Crystal Reports for R:BASE User's
Manual.
Add a Blank Line Every Five Lines:
----------------------------------
To improve report readability by adding a blank line after every five
lines you use Crystal's conditional printing and formatting
capabilities. To find out when five lines have printed, use Crystal's
built in RecordNumber formula with the Remainder function. The
RecordNumber formula, like the GroupNumber formula, assigns a number
to each record when the records are printed.
Create a formula as follows:
if Remainder(RecordNumber,5) = 0 then " _" else " "
If five lines have printed, then the result of the formula is an
underscore, if not, the result is a blank. Locate the formula field on
a detail line by itself and shorten it so the underscore character
doesn't fit in the field. Then format the Detail section to suppress
blank lines. The line is suppressed except for every fifth one.
Because you shortened the field, the character doesn't print, it looks
just like a blank line; however, Crystal knows that the field does
contain a character and the line is not completely blank.
Subtotals
---------
In R:BASE, multiple levels of totals are usually done by repeating the
same totaling expression for a different variable name and not
resetting the variable. For example, three variables are defined to
print totals sales by region, salesperson, and then for the whole
company:
vregion_tot = SUM OF sales_amount
vsalesprsn_tot = SUM OF sales_amount
vcompany_tot = SUM OF sales_amount
Each variable has the same definition but accumulates the totals for
different groups of data based on when the variable is reset.
Crystal operates similarly when you insert subtotals for the various
groups and for grand totals. Crystal automatically resets the totals
for each group.
Sometimes, however, a sum function returns the wrong total. This can
be because multiple tables are used in the report and a value gets
added more than once, or because the value being totaled is the result
of other calculations. In R:BASE these are called "Add a lookup value
once-per-break" and "Summing break footer totals". In Crystal, the
same basic procedure provides the solution to both problems. The
technique can be used anywhere a sum function does not return a
correct result.
Add a Lookup Once Per Break
---------------------------
Whether you add tables to a report in Crystal or print from an R:BASE
view, you may need to total values from the "one" table. The values in
the "one" table are duplicated for each matching row in the "many"
table. Just doing a sum to get the total leads to the wrong amount,
the values are added in more than once.
The procedure to sum a value once for each particular group is similar
to creating a running total. Running totals are described on page 8-28
of the Crystal Reports for R:BASE User's Manual.
Formulas are used to do the totaling. You need two or three formulas
depending on whether or not you need to reset the total at a higher
level break group. Like the formula for counting the groups, one
formula at the group header accumulates the total, a second one in the
grand total section prints the result. To reset, you need a second
formula in a higher level group header section.
For a specific example using the Concomp sample database, include the
transmaster and transdetail tables together on a report. The tables
are linked by the transid column. The desired result is a grand total
of all the amounts in the invoicetotal column. The invoicetotal column
is in the transmaster table, the one side of a one-to-many
relationship. If we just sum the invoicetotal column, our grand total
is too big. The sum includes a value for invoicetotal for each
matching row in the transdetail table. The column needs to be summed
only once for each transid value.
First, insert a group section for transid. Then locate a formula field
in the transid group header section. The formula declares a variable
and accumulates the amounts from the invoicetotal column. Define the
formula, Report total, as follows:
WhilePrintingRecords;
CurrencyVar GrandInvTotal;
GrandInvTotal := GrandInvTotal + {transmaster.invoicetotal}
Because the formula is located in the group header, it is only
evaluated when that section executes, i.e. once for each transid
value. The WhilePrintingRecords ensures that the formula is not
evaluated and data accumulated until all the records have been read
and sorted.
The formula field is formatted to hide when printing. You don't want
the running total to print in the header. It is printed in a Grand
Total section using a second formula, Print report total.
WhilePrintingRecords;
CurrencyVar GrandInvTotal;
GrandInvTotal
This formula again specifies WhilePrintingRecords for correct results,
then declares the variable and simply reads the value accumulated
through the header formula. Even though the variable is the same, it
needs to be declared in each formula that uses it.
These formulas work when you have a single group, or are totaling the
value for the outermost group. If you wanted to have a total for each
customer, for example, and a report total you would need additional
formulas and group sections.
First change the groups. Group 1 needs to be custid, transid moves to
group 2. Add custid as a group, then select Edit|Group section to
change the group columns -- change Group section #1 to custid, Group
section #2 to transid. Move the formula @Report total to group header
2, now the group section for transid. Leave the formula @Print report
total in the Grand total section.
Now define formulas to accumulate the total for each customer. Two
additional formulas, Cust total and Print cust, are defined.
WhilePrintingRecords;
CurrencyVar InvTotal;
InvTotal := InvTotal + {transmaster.invoicetotal}
WhilePrintingRecords;
CurrencyVar InvTotal;
InvTotal
Notice that the formulas are the same as the others, just a different
variable name is used to accumulate the total. The first formula,
@Cust total, is placed in the Group Header section for transid and is
hidden. This variable will be reset for each customer. The second
formula, @Print cust, is placed in the the Group Footer 1 section, the
footer for custid.
Define a third formula, Reset cust, to reset the customer invoice
total variable, InvTotal. It needs to start at zero for each customer.
Place the formula field, Reset cust, in the group header for custid.
It is defined as follows:
WhilePrintingRecords;
CurrencyVar InvTotal;
InvTotal := 0
This resets InvTotal to zero for each new customer. The formula,
@Reset cust, is also formatted to hide when printing.
Sum Break Footer Totals:
------------------------
When a subtotal or break total is not a simple sum of a column, but is
derived from other calculations, you get the correct total by summing
the group footer calculation amount, not the column. In Crystal you do
this with a series of expressions similar to the above formulas for
summing a value once per group. The only difference is that you place
the accumulator formula in the group footer section.
For example, place the formula, Cust total, defined above:
WhilePrintingRecords;
CurrencyVar InvTotal;
InvTotal := InvTotal + {transmaster.invoicetotal}
in the Group Footer section for transid. It evaluates only when the
Group Footer section is processed. It does not evaluate for every row,
so you can have other calculations that are evaluated in the Detail
section (every row) or at lower level groups where the final result of
the calculation, the value at the Group Footer, is accumulated.