Structure
There are three major parts of a report: Data structure, Data binding and Report layout.
Data Structure
All ported reports always have two tables: InputItems and ReportVariables. The tables are linked by a unique progressive ID column named _ReportVariablesRowId. All the input items from the original report are generated as fields in the InputItems table, while all the report variables are fields in the ReportVariables table.
Data binding
At runtime, the PPJ Framework takes care of building a DataSet by generating and handling all the SAM_Report* events in the correct order. The dataset is then passed to the CR.NET module to print the report. While generating the data set, the value _ReportVariablesRowId is increased every time a new report variable is assigned. We have a one-to-many relationship between the ReportVariable table and the InputItems table because report variables usually change only a handful of times in a typical report and would be a big waste of memory to repeat the same row for each InputItem row.
Report layout
Report Builder reports are separated in the usual sections. However, each section is separated in old fashioned lines. While porting, we try to consolidate lines into the same report section in Crystal Report. Only when a line needs to grow or has different properties compared to the next line, we generate a new section in CR.
Linked fields in Report Builder are ported as subfields in a larger text field in CR.
All report formulas are ported with minimal transformations. We try to use CR built-in functions when possible. We have implemented the following unsupported functions as new methods at the report level:
- DateAddDay
- DateIFF
- DateMonthBegin
- DateMonthEnd
- DateToStr
- NumberIFF
- Power
- StrCase
- StrMid
- StrPad
- StrReplace
- StrTranslate
Data and Layout Checks
A report-variable row represents the values in effect for a set of input rows. Think of it as a snapshot, not as one mutable “current variables” row reused by every detail:
ReportVariables snapshot 1: region = North
├─ InputItems row 1 → snapshot 1
└─ InputItems row 2 → snapshot 1
ReportVariables snapshot 2: region = South
└─ InputItems row 3 → snapshot 2
If snapshot 1 is overwritten with South while preparing the third row, the first two rows can later render with the wrong region. Preserve the adapter's snapshot/link behavior instead of collapsing the two-table structure during a refactor.
Preserve the relationship between InputItems rows and ReportVariables snapshots; repeating or changing a variable at the wrong time can produce correct-looking details with incorrect totals or headers. Compare reports where variables change between groups.
Check growing text, linked fields, section suppression, and page breaks with short and long values. The complete DataSet can be significant for large reports, so measure memory before rendering as well as during export.