Archive for June, 2020
I have written before about Crystal Dispatch, a client based viewer put out by APB Reports. It is a simple no frills viewer that covers all the basics and lets you run reports with one click. You can read more about how it works in my previous article, and even watch a video of how it works.
But what is new this year is the price. APB Reports is now offering an unlimited site license to a company for $5 (yes, five US dollars). Adam Butt, the owner of APB Reports, said, “I want as many companies as possible to start using Crystal Reports, so hopefully this is a move in the right direction”.
So if you are thinking about deploying a locally installed viewer program in your company, this is one very low cost option to consider. For other options you can check out my annual comparison of client based viewers.
And now for some breaking news. It appears that SAP is starting to release Crystal Reports 2020 and Crystal Reports Server 2020. Based on things I have heard the scheduled release of CR 2020 as packaged product is Early August. I haven’t seen the software, but I read an article about Crystal Reports 2020 written by Pursuit Technology with some actual details. The big news is that it is the first 64 bit version of Crystal Reports.
One other significant change is that come January, Crystal Reports will no longer be available from resellers. The only place to purchase Crystal next year will be the SAP web site. This is part of a reconfiguration of the SAP partner program and will probably result in the elimination of many smaller resellers.
If anyone has more info to share on this topic, please let me know.
I had a fun challenge today. A customer had a table with an odd structure. One column was a payment amount. It was followed by 40 different columns for various fees. He wanted the payment amount to be applied to the fees from smallest amount to largest amount until the payment was used up. The report was to show the balance for each fee after the payment was applied.
Normally this type of data would be vertical. There would be a separate row for each fee and only 2 columns (fee name and the fee amount). With that structure you could sort by fee amount and use a variables to apply the payment to the records in ascending order. But with 40 fixed columns I had to load the values into an array, and then put the values in the right order.
I loaded the fees into the array as strings, combining the amounts and fee names into one element looked this:
24.50=xyzFee
This allowed the fee name to follow the amount through the sorting process.
Once I loaded all these strings into the array I used my bubble sort formula to re-sort the array from lowest amount to highest amount. In the bubble sort comparison I used the Val() function to convert the strings to numbers. This put them in order by their true numeric value.
Then I wrote a second loop to apply the payment. It steps through the array, which is now in the right order, and applies the payment to the fees, one at a time. Each fee amount is reduced to zero while the payment value is reduced by the amount of each fee. If the payment’s remaining value drops below the amount of the next fee the remainder is subtracted from that fee and the payment is reduced to zero. Any fee amounts in the array beyond that point stay the same.
Last, to display the results, I wrote 40 separate formulas, one for each fee. Each of these formulas loops through the array looking for its specific fee description. When it finds the element with a matching description it uses the Val() function to convert that string to a numeric value and displays that value. This is the relatively simple loop formula:
EvaluateAfter({@Build Fees Array});
stringVar Array Fees;
Local numberVar i;
Local numberVar fee;
FOR i:=1 to ubound(Fees)
DO (
if 'xyzFee' in Fees[i]
then Fee := val(Fees[i])
);
Fee
Not many people would think this process was fun, but I did enjoy mapping out a creative approach to a unique requirement. And it was less than 2 hours from the requirements to a validated report. The next time your report requirements are a bit unorthodox, keep me in mind.
Today a user asked me how to automatically select the prior month’s transactions from the database. The transactions didn’t have dates, but instead had two numeric fields for Period (month) and Year. They were using a formula to convert these two numbers into a date and then using that formula as part of the selection formula. That approach will work but it is usually slow. This is because the database (or the SQL Query) doesn’t know anything about the date formula in the report, so it can’t apply any date criteria before sending back the data. Crystal gets all years and periods and then has to apply the criteria in the report.
A more efficient approach is to start at the other end. Instead of converting the DB fields to match the criteria values, we convert the criteria values to match the the database fields and let the database do the heavy lifting. In this case created three formulas:
// Prior Pd End Date
Maximum (LastFullMonth)
This gives us the last date of the prior month. From there I created two formulas that extract the year and the month from that date:
// Prior Pd Month
Month ({@Prior Pd End Date})
// Prior Pd Year
Year ({@Prior Pd End Date})
Then the selection formula can use these values to filter the DB fields, something like this:
….
and {Trans.Year} = {@Prior Pd Year}
and {Trans.Period} = {@Prior Pd Month}
This should allow the criteria to be passed to the WHERE clause of the SQL, and maybe even hit an index to make short work of finding the correct records.
The principle works the same if you are using a Date Range parameter. You could use formulas to convert the parameter values so they match the database fields. I wrote about this a few years ago in a similar case where the dates were stored as strings.