Search code examples
crystal-reports

Crystal Reports - Access All Rows Returned by a Query Instead of One at a Time


I'm using Crystal 2020 and I have two tables, osEntity and osCommunication, that can be joined on osCommunication.fkosEntity=osEntity.PK. osCommunication contains phone numbers for customers in osEntity and each customer can have multiple phone numbers, so naturally this means that some records look like this:

Name            Phone
John Smith      555-555-5555
John Smith      555-555-3333

but I would like it to look like this in my report:

Name            Phone
John Smith      555-555-5555, 555-555-3333

I tried accomplishing this by creating a sub-report and linking osEntity.PK in the outer report to osCommunication.fkosEntity in the sub-report and then creating a formula that loops through osCommunication.information, the field that contains the phone numbers, to concatenate them like so,

stringvar array numbers := {osCommunication.Information};
numbervar cnt := count(numbers);
stringvar out := "";
numbervar i;

for i := 1 to cnt - 1 step 1 do (
    out := out + numbers[i] + ", ";
);
out := out + numbers[cnt];
out

The problem is that Crystal is only giving me access to one row at a time, so this formula produces something more like 5, 5, 5, -, 5, 5, 5, -, 5, 5, 5, 5 rather than 555-555-5555, 555-555-3333. Is there a way that I can get access to all rows returned by the query to osCommunication at once rather than one row at a time? I've done something like this with parameters from user input before but I'm struggling to do the same with the result of a query. Any help would be greatly appreciated, thank you.

UPDATE: I was able to work out the following solution thanks to @MilletSoftware

Admittedly, I don't fully understand why, but I wasn't able to get the list of concatenated phone numbers to print in the group footer of each group until I changed all of my global variables to shared variables. I also ended up using a string array to make filtering out duplicates a little easier. None the less, I'll include my completed solution here for the sake of those who may be looking to do something similar.

Group Header Section:

Shared StringVar Array Phones; //Array containing all phone numbers related to the current group.
Redim Phones[1]; //Reset the Array at the start of each group.
Shared NumberVar reCount; //Current record count for the current group.
Shared StringVar currentEntity; //The entity ID of the current group.

//Check if starting a new group. If so, update the current group's EntityID and resent the record count.
If currentEntity = "" or currentEntity <> {osEntity.EntityID} Then
(
    currentEntity := {osEntity.EntityID};
    reCount := 1
);

Details Section:

Shared StringVar Array Phones;
Shared NumberVar reCount;

//Only add phone number to array if it is unique
If Not({osCommunication.Information} In Phones) Then
(
    ReDim Preserve Phones[reCount];
    Phones[reCount] := {osCommunication.Information};
    reCount := reCount + 1;
);

Group Footer Section:

Shared StringVar Array Phones;

//Join all phone numbers in the array into a comma separated list
Join(Phones,', ');

Solution

  • Group by Customer. In the Group Header, use a formula to reset a global string variable.

    Global Stringvar Phones := "";
    

    In the detail section, concatenate the phone number to the global variable:

    Global StringVar Phones := Phones + {Customer.Phone} + ", " ;
    

    In the Group Footer, use a formula to get rid of the last comma, and display the phone numbers:

    Global StringVar Phones;
    IF Len(Phones) > 2 Then Phones := LEFT(Phones, Len(Phones) - 2);
    Phones;