2. Adding Promotional Materials to an Invoice Report
Before you customize the SalesInvoice
report for this example, you must decide where in the design of the
report to place the printing of the promotion. The printed information
should be printed for each invoiced item, so you must place it under the
CustInvoiceTrans section group because the CustInvoiceTrans table contains the invoiced items. The CustInvoiceTrans
section group contains a reference body section that can print other
pieces of reference information, such as from inventory dimensions or
the packing slip lines posted when the invoiced item is shipped. The
promotion resembles this kind of information in terms of when and how it
is printed.
This example, therefore, creates a new section
group within the reference body section below the existing three groups.
The new section group must reference a table type so that it can be
invoked when a record buffer of the same type is sent to the report by
using the element.send method. The DocuRef table stores the promotion text, and the DocuValue table stores the promotion image with an association created in the DocuRef table.
Although the storage of the text and image results in the creation of DocuRef records, the choice of DocuRef
as the reference table type for the new section group isn’t an optimal
solution. First, the information is stored as two records in the DocuRef table, but the text and image should be printed side by side for this example. The element.send method should be called only once, parsing in only a single record buffer. Also, two other section groups already use DocuRef
as the table type, so using this type might result in the other section
groups getting invoked as well when the promotion prints. You could
prevent this by introducing a variable to control which section group to
invoke, but then you would have to customize even more of the report,
making it harder to upgrade the report when a new version or service
pack is installed.
Both of the DocuRef records are, however, related to the same InventTable record, so you can use this table as the type for the section group, and an InventTable record buffer is sent to the report to print the promotion text and image. Figure 4 shows the new section group, named InventTable, and its positioning within the report.
Implementing Promotional Methods
When the promotion text and image print, an InventTable record buffer is sent to the report. For this reason, this example implements two methods to return the text and image by using an InventTable
record buffer. The methods can be implemented directly in the report,
but because the methods are not report specific—and therefore can be
reused in other reports, or even forms—they are implemented as instance
methods on InventTable. The following code shows the new methods. The PromotionImage method is implemented like the BikeImage method in the forms example discussed earlier. However, the PromotionImage method must look in only the DocuRef table to find the text.
display server public DocuValueFile PromotionImage()
{
DocuRef docuref;
DocuValue docuValue;
;
select firstonly tableid from docuRef
where docuRef.RefCompanyId == this.DataAreaId &&
docuRef.RefTableId == this.TableId &&
docuRef.RefRecId == this.RecId &&
docuRef.TypeId == 'PromoImage'
join file from docuValue
where docuValue.RecId == docuRef.ValueRecId;
return docuValue.File;
}
display server public Notes PromotionText()
{
DocuRef docuref;
;
select firstonly notes from docuRef
where docuRef.RefCompanyId == this.DataAreaId &&
docuRef.RefTableId == this.TableId &&
docuRef.RefRecId == this.RecId &&
docuRef.TypeId == 'PromoText';
return docuRef.Notes;
}
|
Both methods are implemented as display methods to allow them to bind directly to report controls and to print the information.
Binding Display Methods to Report Controls
The next step is to bind the methods to report controls. A new body section named BodyInventTable is created in the InventTable section group, and several of its properties are altered, as shown in Table 1.
Table 1. BodyInventTable Property Settings
Property | Settings |
---|
NoOfHeadingLines | 0 |
LineAbove | Solid |
LineBelow | Solid |
LineLeft | Solid |
LineRight | Solid |
The NoOfHeadingLines property must be set to 0 because the text and image must not include any headings when printed. The Line property settings create a border around the promotion.
In the body section, a string control, named PromotionText, and a bitmap control, named PromotionImage, are added and bound to the two new InventTable methods. The properties shown in Table 2 are changed on the two controls.
Table 2. PromotionText and PromotionImage Property Settings
Property | PromotionText | PromotionImage |
---|
Left | | Auto (right) |
Width | 70.00 char | 2.0 inch |
Height | | 2.0 inch |
DynamicHeight | Yes | |
ShowLabel | No | No |
Table | InventTable | InventTable |
DataMethod | PromotionText | PromotionImage |
The ShowLabel properties are set to No because no headings should be printed. The PromotionText control is set to a fixed width of 70 characters with a dynamic height so that the text won’t be truncated. The PromotionImage has a fixed size of 2 inches by 2 inches and is right-justified on the page.
The last step is to look up an InventTable
record buffer based on the invoiced item and then send the buffer to
the report. You do this with the following new method on the BodyReference body section.
void printInventTable()
{
InventTable inventTable = custInvoiceTrans.inventTable();
if (inventTable.RecId)
{
element.send(inventTable);
}
}
|
The method uses the InventTable lookup method on the CustInvoiceTrans table, which returns a record buffer for the invoiced item, which the method subsequently sends to the report.
The preceding method should be called from the executionSection method on the same body section. The following method is therefore customized by including the call to the printInventTable method.
void executeSection()
{;
this.printCustPackingSlipTrans();
this.printDimHistory();
this.printInventTable();
}
|
The positioning of the body section, report control, and report methods is shown in Figure 5.
After the completion of all the customizations to the SalesInvoice report and the addition of new methods to InventTable, the report prints the promotion below each invoiced item on the report, as shown in Figure 5-14.
Preventing Printing of an Empty Body Section
The solution thus far has one flaw: it prints an empty BodyInventTable body section if there is no document reference for the PromoText and PromoImage document types, which causes an empty box to appear below each item on the invoice. You could easily fix this by altering the printInventTable method to include a check for text or images, as shown in the following change to the printInventTable method.
void printInventTable()
{
InventTable inventTable = custInvoiceTrans.inventTable();
if (inventTable.RecId &&
(inventTable.PromotionText() || inventTable.PromotionImage()))
{
element.send(inventTable);
}
}
|
This code ensures that the InventTable record buffer is sent to the report only if the PromotionText method or the PromotionImage method returns a value.
In terms of performance, this change isn’t optimal because methods could be executed twice if a promotion were added to the InventTable record. This could result in as many as five round-trips to the database for each printed invoiced item: two from the printInventTable method, two when printing the values, and one when the report runtime determines the height of the PromotionText control.
A better solution is to cache the returned values from the PromotionText and PromotionImage methods when they are called in the printInventTable method and then use the cached values instead of retrieving them from the database when printing the PromotionText and PromotionImage controls.
The cache variables must be added to the classDeclaration of the report, so the following lines are inserted there.
DocuValueFile promotionImage;
Notes promotionText;
|
The printInventTable method is modified to store the returned values from the PromotionText and PromotionImage methods on the InventTable record buffer in the newly created variables, as shown in the following copy of the method.
void printInventTable()
{
InventTable inventTable = custInvoiceTrans.inventTable();
;
promotionImage = inventTable.PromotionImage();
promotionText = inventTable.PromotionText();
if (inventTable.RecId &&
(promotionText || promotionImage))
{
element.send(inventTable);
}
}
|
In addition to these two new display methods, PromotionText and PromotionImage are created to return the values of the variables. The following code samples show these methods, implemented in the BodyInventTable body section.
display Notes PromotionText()
{
return promotionText;
}
|
display DocuValueFile PromotionImage()
{
return promotionImage;
}
|
With these two methods named similarly to the InventTable methods, you must remove only the value in the Table property on the PromotionImage and PromotionText report controls to enable the report to retrieve the value from the local report methods instead of the InventTable methods. You can even remove the display method modifiers from the two InventTable methods because they are no longer used as display methods.
When you print the report again, no empty BodyInventTable
body sections appear, and the printing of this specific section is
optimized. The report will never result in more than two round-trips to
the database for each invoiced item. The only disadvantages are that
return types of the methods on the InventTable
and the equivalent methods on the report should be kept synchronized,
and these return types should again be kept synchronized with the types
of the cache variables. This synchronization wasn’t necessary earlier in
the example, before the values in the report were cached.