Crystal Reports: Create Array or get values from a column - crystal-reports

Say I'm working on a report, on a column called someTable.someColumn. What I need is to have all of the values in the ReportHeader. I know I cound create a subreport, pass them this column and iterate it in the details, but I would like to know if it's possible to do it without a subreport.
If I use the following, I get the right result, which in my case is 9:
COUNT(someTable.someColumn)
I also tried the following, but it doesn't work - it only takes one of the 9 values randomly.
Local NumberVar i := 0;
Local StringVar Array items := {someTable.someColumn};
Local StringVar output := "";
while (i < count(items)) do (
i := i+1;
output := output + items[i];
);
output;
Is it possible to iterate over a column in order to get the values?

You can't do this in the report header. You have to use the detail section to read each value.
The only solution which springs to mind is using a subreport. Although you wouldn't pass them this column and iterate it in the details. Because it would only pass the first value from the column. You would pass it the report parameters which it could use to obtain the column values from the database the same way your main report does.

I haven't found a way to do this in the report header, but you could put it in your report footer with the following formula:
global StringVar output;
output := output + {someTable.someColumn} + ",";
This will execute each time someTable.someColumn changes over the course of report generation, and append it to the global variable. Unfortunately, it doesn't keep all instances of it up to date - for example, if you include it in the report details somewhere, you'll be able to see the evolution of output over the course of the report. In the header, it'll only display the first value.
Note: You won't want to set output := "" in this formula, as that'll reset the value each time. It seems, though, that Crystal automatically initializes the variable for you when it's created, and I didn't run into any errors with only the above formula (i.e, I didn't need to create a second formula to initialize output). I've also included a delimeter (+ ","), to help separate each of the values, but you can easily just remove that.

Related

Crystal Reports - Pushing Down Query with Variables to Server

I've been having trouble pushing down queries to a SQL server when using Crystal Reports (I'm currently working with Crystal Reports 2013).
If I enter the following as a record selection formula, Crystal uses a WHERE clause to select the records:
{LINKAGE.ENTITY_ID_1} = 1 and
{LINKAGE.ENTITY_TABLE_1} = "ENT" and
{LINKAGE.ENTITY_TABLE_2} in ["DOC","PHO"]
-Gives-
SELECT "LINKAGE"."ENTITY_ID_1", "LINKAGE"."ENTITY_TABLE_1", "LINKAGE"."ENTITY_TABLE_2"
FROM "TESTING_DB"."dbo"."LINKAGE" "LINKAGE"
WHERE "LINKAGE"."ENTITY_ID_1"=1 AND "LINKAGE"."ENTITY_TABLE_1"='ENT' AND ("LINKAGE"."ENTITY_TABLE_2"='DOC' OR "LINKAGE"."ENTITY_TABLE_2"='PHO')
If I change the 'hard coded' values to instead use variables, Crystal no longer includes a WHERE clause when selecting records:
numbervar valueNumber := 1;
stringvar valueString := "ENT";
stringvar array valueArray := ["DOC","PHO"];
{LINKAGE.ENTITY_ID_1} = valueNumber and
{LINKAGE.ENTITY_TABLE_1} = valueString and
{LINKAGE.ENTITY_TABLE_2} in valueArray
-Gives-
SELECT "LINKAGE"."ENTITY_ID_1", "LINKAGE"."ENTITY_TABLE_1", "LINKAGE"."ENTITY_TABLE_2"
FROM "TESTING_DB"."dbo"."LINKAGE" "LINKAGE"
From reading the documentation it sounds as though there shouldn't be an issue pushing these down, so I'm not too sure what I might be missing. Any insight would be greatly appreciated!
I seem to have found the answer in the Crystal Reports User Guide. There it's mentioned that only a constant or parameter value can be pushed down but not a variable:
https://help.sap.com/doc/dfc124becfa845ffa91b1e717b20e3ec/2016%20SP3/en-US/4770d0cd6e041014910aba7db0e91070.html#loio4770d0cd6e041014910aba7db0e91070

Crystal reports select critera with comma delimited

I have a crystal report I need to change the select criteria on. Currently criteria compare's a database field to the parameter I created in the report.
{MaterialCR.MaterialId} = {?MaterialId}
I am now have a field that has comma delimited data in it I need to make sure the parameter includes any of the other ids in the new field.
Materialused has this data in it. "MA0161 ,MA0167" (No double quotes) . This doesn't work
{MaterialCR. MaterialUsed} = {?MaterialId}
I have tried to create a function to compare the two but it does not seem to work. It does not see the parameter as a string array.
My material match function that does not work
Function MaterialMatch (MaterialUsed as string,v1 () As String)
dim MyArray() as string
MyArray = Split (MaterialUsed,"," )
dim Match as boolean
Match = false
dim x as number
For x = 1 To count(v1) Step 1
IF "ALL" in v1 then
Match = true
x = count(MyArray)
end if
if MyArray(x) in v1 then
Match = true
x = count(MyArray)
end if
Next x
MaterialMatch = Match
End Function
This is what the data I am looking at looks like. We have many materials with a Material ID in it. We also have associated time that we need to select. It does not have a material id as it is a many to one situation. I need to retrieve all the records associated with the material including the time. Getting the material with ids is not the issue. I need to get the Time records also. I modified the view this report uses to include the material that overlaps the time. This is where I am stuck.
This is what my select expert formula looks like now. I do know the material used part is wrong.
(
{JobTimeMaterialCR.MaterialId} = {?MaterialId}
or
(
{JobTimeMaterialCR.Type} = "Time"
and
{JobTimeMaterialCR.MaterialUsed} = {?MaterialId}
))
I was able to write a formula that worked for me using the logic I described in my comment. Use this formula as your Record Selection Formula. In Formula Workshop these are found in Selection Formulas > Record Selection.
Local StringVar array values := Split({?Search Values},",");
Local NumberVar indexCount := Count(values);
Local BooleanVar found := false;
Local NumberVar counter;
For counter := 1 to indexCount Step 1 Do
(
If InStr({ARINVT.DESCRIP}, values[counter]) > 0 Then
found := true
);
found;
It's rough, but a good start. The search is case sensitive, so you may need to tweak it with some Lower() functions if you want case insensitive searches. Also, if there is a space between the delim character and the search string in your CSV string, then the space is included in the search. A Replace() function can help you get around this, but that would prevent you from using spaces in the search strings. If you need to use spaces in searches, then just take care when building your CSV String that there are no spaces before or after the comma that is your delim character.
If you need any help understanding the syntax of my formula feel free to comment and I will answer any questions.
I used a parameter field called {?Search Values} to simulate the CSV string data. {ARINVT.DESCRIP} is a field name from my test database I used to search thousands of records for key words I typed into my parameter field. You will want to replace these field names in the formula with your field names and you should be able to get this working without much trouble.

How do you retrieve a date value from a buffer-field in Progress Openedge?

I've got a buffer which contains a mix of data, number and character fields. I am getting the displaying the values of the fields, but for some reason date fields return "?" when I try to add them to a string.
I still get ? even if I do
ASSIGN lvString = lvString + STRING( hField:BUFFER-VALUE ).
I've also tried assigning the BUFFER-VALUE to a local DATE variable, and converting that to a string, but that doesn't work either - still ?.
However if I use the STRING-VALUE attribute, it works fine.
How do I get the value out as a date field, rather than just a string?
There are two ways that you can use to achieve your needs. One is to use directly the table buffer and the other is to use an QUERY handle.
First example using a buffer directly from a table (or a TEMP-TABLE, doesn't matter):
DEF VAR dateVar AS DATE NO-UNDO.
FIND FIRST job NO-LOCK.
dateVar = DATE(BUFFER job:BUFFER-FIELD('dt-job'):BUFFER-VALUE).
MESSAGE dateVar
VIEW-AS ALERT-BOX INFO BUTTONS OK.
Second example using a query handle:
DEF VAR dateVar AS DATE NO-UNDO.
DEF QUERY qrJob FOR job.
OPEN QUERY qrJob FOR EACH job.
QUERY qrJob:GET-FIRST().
dateVar = DATE(QUERY qrJob:GET-BUFFER-HANDLE(1):BUFFER-FIELD('dt-job'):BUFFER-VALUE).
MESSAGE dateVar
VIEW-AS ALERT-BOX INFO BUTTONS OK.
As Tim Kuehn said you can substitute 'dt-job' by # of field in the query if you know its position inside the query. I could used BUFFER-FIELD(2) in substitution of BUFFER-FIELD('dt-job') because dt-job is the #2 field in my query. Keep in mind that use the FIELDS clause in a FOR EACH or in an OPEN QUERY statement changes the order of fields in query. Generally, for browsers only available the columns fields specified in FIELDS section, in order.
These might work for you. It's important to say that BUFFER-VALUE always returns a CHARACTER data type and because of this you need to use DATE statement for data conversion.
Hope it helps.
The standard form for getting a data of the field's data type is
buffer table-name:buffer-handle:buffer-field("field-name"):buffer-value.
for arrays it's:
buffer table-name:buffer-handle:buffer-field("field-name"):buffer-value[array-element].
You can also substitute a field # for "field-name" to get the buffer field handle.

Displaying a parameter's description value

In Crystal Reports v 11, is it possible to display the 'Parameter Description' value on the report, as opposed to only the parameter value? Whenever I drag the parameter onto the report to display it, only the value is displayed, and I want to print the description.
Note: I'm working in Crystal 11 (XI), not the .NET Crystal Report plug-in.
I agree with Ryan--there isn't a native mechanism to grab these values. A user-function library (UFL) might be an approach worth investigating.
In lieu of that, I handle this a number of ways:
a formula with a case statement to convert the value to a description.
a custom function that does the same a #1; custom functions can be shared w/ other reports via the BOE repository
use a subreport to query a table that can covert the value to a description. store the values in an array in the Detail section (suppressed), then Join() the array in the report footer. if you embed the subreport in its own section, it will expand as needed to accommodate an expansive list
you might be able to adapt #3 to use a list of values exposed by BO's query as a web service (QAAWS), but I haven't experimented with this. If it doesn't have an associated schema document, CR won't be able to use the XML webservice as a data source.
First, Concatenate the parameter value and the description together and delimit the two values using a pipe '|' or other lesser used character
Example:
CustomerID, CustomerName -> 12345|ABC Company
Second Use a Crystal function with an array to display the description side on your report i.e everything after your delimiting character
Numbervar i;
Numbervar j;
StringVar Array z := "";
Local StringVar Array x := split(Join({?My Parameter},";"),";");
j := count(x);
redim preserve z[j];
for i := 1 to j do(
z[i] := right(x[i],Length(x[i])-instr(x[i],"|"))
);
join(z,",")
Third. Create a second function like the one above to return the Parameter value to the left of the delimiter to use as part of the report's Selection Formula**
I don't believe there is a way to directly get at the description (several other posts scattered around the interwebs seem to agree). If you have a small(-ish) number of parameter options, you can just create a formula or function that contains a case-statement that will translate the value to its description.
I got over the shock that Crystal Reports somehow is able to retrieve the description of a parameter but it doesn't expose it to be used in formulas. Being forced to duplicate the definition of parameters and description seems to me bad form, simply because one is not supposed to duplicate things when programming. An alternative hack (besides all the ones listed) would be to make your Values and Descriptions as values of a SQL data source (hardcode them in SQL). Then you can populate your parameter from said data source and you will have the Descriptions available as a field. That way you only have "one version of the truth" that maps your values to your descriptions, although more moving pieces.
See: 1678487 - How to display parameter descriptions instead of parameter values in the Crystal Reports at: https://apps.support.sap.com/sap/support/knowledge/public/en/1678487
There is a new function that can actually be used for displaying the descriptions of a parameter on a report.
Source : https://launchpad.support.sap.com/#/notes/2037542
I will paste the entire document for those users who don't have to access to this page.
A new function GetValueDescriptions() is added in Crystal Reports 2016, to allow user create formula that could operate / display descriptions of selected value for a parameter.
GetValueDescriptions() supports all kinds of parameters (single and multiple, discrete and range). Below is an formula example where {?customerMultiDiscreteAndRange} is a multiple range parameter:
local stringVar range array im := GetValueDescriptions ({?customerMultiDiscreteAndRange});
local numberVar i := 1;
local stringVar out := "";
while i <= count(im) do
(
if (IncludesLowerBound ({?customerMultiDiscreteAndRange}[i])) then
( out := out + "["; )
else
( out := out + "("; );
if (HasLowerBound ({?customerMultiDiscreteAndRange}[i])) then
( out := out + GetLowerBound(im[i]) );
out := out + "..";
if (HasUpperBound ({?customerMultiDiscreteAndRange}[i])) then
( out := out + GetUpperBound(im[i]) );
if (IncludesUpperBound ({?customerMultiDiscreteAndRange}[i])) then
( out := out + "]"; )
else
( out := out + ")"; );
if (i < count(im)) then
out := out + ", ";
i:= i + 1;
);
out;
Also note two new functions - GetLowerBound() and GetUpperBound(), they can be used to get correct lower / upper bound descriptions. These two are especially useful if Maximum() and Minimum() returns swapped descriptions.
Above formula example can be adjusted easily, e.g. to show both the value and descrition, or to satisfy more reporting needs.
To use these functions, upgrade your Crystal Reports to a release where the enhancement has been made.
See the SP Patch Level section for details and available patches.
For SAP Crystal Reports, version for Eclipse:
These functions are supported for runtime only since SP22, with following limitations:
Static multi-range parameters are not supported in SAP Crystal Reports, version for Eclipse runtime, which is the same as before. Dynamic multi-range parameters are supported.
"Value Options" to show description is not supported in the embedded designer in SAP Crystal Reports, version for Eclipse. If you have an existing report designed in SAP Crystal Reports 2016 with parameters' "Value Options" to show description, any modification resaved in the embedded designer will overwrite the "Value Options" to show value. Instead, reports created in SAP Crystal Reports 2016 with the "Value Options" to show description is supported in SAP Crystal Reports, version for Eclipse runtime.
To download the latest Support Packages of SAP Crystal Reports, version for Eclipse:
https://wiki.scn.sap.com/wiki/display/BOBJ/SAP+Crystal+Reports+version+for+Eclipse+-+Downloads

Crystal reports 11 : blank field bombs the report

I'm creating a invoice crystal report for sage mas 500 AR module. In it, I'm attempting to add the tarinvoice.balance field with the following formula:
if {tarPrintInvcHdrWrk.Posted} = 1 then
ToText({tarInvoice.Balance})
I'm assuming that when the {tarPrintInvcHdrWrk.Posted} = 1 conditional statement holds FALSE, it doesn't attempt to pull the invoice field because when I remove the formula from the report, the form displays correctly without it.
When the conditional statement renders true in the report, the balance fields behaves correctly. However, with the formula renders FALSE in the CR form, the entire crystal report bombs and displays blank. Any ideas why or what I'm doing wrong?
Just tried setting everything to zero and the report still bombs. I'm starting to think its more of a query error in the report. I wish there was a way to exclude the field in the query when posted = 0.
With tarinvoice.balance removed when the posted = 0, the report works fine.
With tarinvoice.balance included and posted = 1, report works fine.
With tarinvoice.balance included and posted =0, report bombs.
I believe the conditional statement fails immediately if you encounter a NULL, so your formula needs to test IsNull({tarPrintInvcHdrWrk.Posted}) before it tests equality with "1".
You can change the way Crystal handles a null value for a value in a formula. At the top of the Formula Workshop there is a drop down box that usually says "Exceptions For Nulls".
Change this to the other option "Default Values For Nulls" and your formula should no longer bomb out. You used to be able to specify the what the default values applied were, but more recent versions of Crystal have these hard coded.
Search the help for "Null Treatment" for a table showing them.
I modified the formula to this:
if isnull({tarPrintInvcHdrWrk.Posted}) = FALSE then
if {tarPrintInvcHdrWrk.Posted} = 1 then
if isnull({tarInvoice.Balance}) = FALSE then
ToText({tarInvoice.Balance})
else
"0.00"
else
"0.0"
else
"0"
The crystal report still bombs.. Nevertheless, it does show "0" in the appropriate space.
I saw a suggestion on Exp.Exch to try putting the field into a variable before converting it to text.
e.g.
NumberVar InvoiceBalance;
If isnull({tarInvoice.Balance}) then
InvoiceBalance := 0
Else
InvoiceBalance := {tarInvoice.Balance};
If {tarPrintInvcHdrWrk.Posted} = 1 then
ToText(InvoiceBalance);
I also tried to recreate your problem, since I have see similar things before.
No luck though trying with CR 8.5 & XI R2. Perhpas it has to do with linked tables as well, since I only tried on a simple single table.
I have also seen similar behaviour when using a formula within a Running Total - they do not like nulls at all!
If you put {tarInvoice.Balance} directly on report (into details "debug" section - often needed, don't forget supress it in production :)), what values it displays or does report become empty?
Maybe you have Suppress If Blank section on your report. Try to put: Else " "
if isnull({tarPrintInvcHdrWrk.Posted}) or {tarPrintInvcHdrWrk.Posted}=0 then
" "
else
if {tarPrintInvcHdrWrk.Posted} = 1 then
ToText({tarInvoice.Balance})
else
" "
I have trouble with this kind of field when making reports to export to excel. A field with no data in will pull all columns to the right of it over to "fill the gap".