Column headings scrambled in Excel export

Monday 16 September 2019 @ 9:20 pm

I had a customer recently complain about a report not exporting correctly to Excel (Data Only). The column headings worked correctly in preview but were re-arranged in the spreadsheet in a seemingly random fashion.

My first thought was to make sure that they were all the same width as the field below them and precisely aligned with the fields, but this didn’t solve the problem. Then I aligned their bottom edges but again this didn’t solve the problem.

Then I noticed that the headings that moved to the right were all text objects that were either two rows or three rows deep, making them taller than the other headings. The tallest headings moved further to the right, which meant that it had something to do with the position of the top edge. So I made all the heading objects the same height and then aligned their top edges. At that poing they all exported to Excel in the correct order.

Note that this only affects Excel exports that are “Data Only”. Of course this is the option that I use 99% of the time.





Real rounding vs fake rounding

Sunday 8 September 2019 @ 8:00 pm

Crystal makes it easy to reduce the number of decimals that you display for numeric values. There are two buttons on the toolbar (near the percent sign) and these allow you to either reduce or increase the decimals displayed. When you reduce decimals the remaining digits will either round up or down based on the digits no longer displayed. In other words, if you are showing 75.28 and you click the button to reduce the decimals displayed by one, the value will display as 75.3. This is because the hidden 8 causes the value to round up to the next 1/10th.

However, this is deceptive. The underlying value in the report is still 75.28. And if you were to total that column or use that value in a calculation the value used would still be 75.28. This can give unexpected results, because the visible total at the end of the report might not reflect the sum of the visible values that go into that total. For that reason, I call this ‘fake’ rounding. You will find the same type of issue in Excel spreadsheets.

Most of the time, this isn’t an issue. And many people who read financial statements understand ‘rounding errors‘. But there are cases where you need ‘real’ rounding. Real Rounding requires writing a formula that uses the Round() function. For instance, if you have to apply a tax rate to a purchase and the calculated tax amount.  You could use a formula like this:

{Sales.Charge} * {Sales.TaxRate}

But if the tax value has more than two decimals, those extra decimals aren’t real.  Most transactions have to be rounded to the nearest penny. In these cases you would need a formula like this:

Round ( {Sales.Charge} * {Sales.TaxRate} , 2 )

This rounds the tax to the nearest penny.  If you used the first formula and created a total of the tax values, the total would include all the fictional fractions of a penny.  If everything was displayed with two decimals the total tax would not match the individual tac values, because those would be using fake rounding to display only two decimals.  But if you use the second formula and then create a total of that formula the total should reflect the visible values.  The total calculation is using values that display the same number of digits as the underlying value.





How to turn records/rows into columns

Tuesday 27 August 2019 @ 7:32 pm

A customer recently requested an unusual report layout. They wanted the field labels in the first column on the page and each record to be a new column on that page. I remembered struggling with a similar layout request years ago and pulled up an old report from 2009. What I came up with then worked fine if you only needed one set of labels on each page, meaning that the columns were so tall that there wasn’t room to fit a second set below the first one each page. In 2009 I had half-height columns which meant two sets of labels along the left side of the page. That got very complicated.

Fortunately my current project had full page columns, which meant my 2009 solution was a perfect fit. Here are the steps:
1) Create a static value formula that can be used as a group. I usually use something like this:

WhileReadingRecords; 1

2) Group on this formula, and in the group options check “repeat Group Header on each page”
3) Make the group header about 6 inches deep and type all the field labels along the left side.
4) Make your details section about 6 inches deep and put the corresponding fields in.
5) Go into the section expert for the details section and check “Format with multiple columns”.
6) When the layout tab appears set the width of the column to an appropriate number.
7) Check the layout options: “Across then down” and “Format Groups with multiple columns”.

Note – you can do something similar with a cross-tab, but the additional pages for cross-tabs are all to the right on page 1 when in preview.  And the layout isn’t as flexible with a cross-tab.





Math tweet splits internet mathematicians

Tuesday 20 August 2019 @ 12:10 pm

One of my colleagues sent me a New York Times article about a math tweet.  The tweet was of a deceptively simple math equation and asked people to calculate the result.  All of the internet mathematicians immediately divided into two camps based on their interpretation of the rules of precedence, which were intentionally ambiguous in the equation. The point of the article (and of my colleagues sharing) was the importance of using parentheses to clarify the order in which things should occur. I often add extra parens, even when the order of precedence wouldn’t require them, just to give myself a visual clue of what is supposed to happen when.

The funny thing about the original article, and an interesting follow-up article, is that experts can still be found on both sides of the divide. That is somewhat surprising given the apparent simplicity of the problem.

And thanks to Zvi Flanders of Huron Consulting for sending me the link.





Calculating the begin and end of Daylight Saving Time

Friday 16 August 2019 @ 11:32 pm

I am not a big fan of Daylight Saving Time (DST). I even hear some states and countries are talking about dropping it (yeah!). But in the meantime there are plenty of reports that need to adjust the time twice a year. This usually happens when the datetime values are stored in Greenwich Mean Time (GMT) and have to be converted to a local time. Then you need to know when DST begins and ends.

The first two formulas below calculate the beginning and ending dates of DST, based on the year of your transaction date. The third formula uses the first two formulas to make the one-hour adjustment. Substitute your GMT date fields into the first and third formula.

//DST Start
DateVar Start:= Date (Year ({@YourDateTimeGMT}) , 3, 15);
DateVar BOM:= Start - Day(Start)+7;
DateVar BOW:= BOM - DayOfWeek(BOM) + 8;
DateAdd('h', 2, BOW);

//DST End
{@DST Start} + 238 ;

//Adjusted DateTime
if {@YourDateTimeGMT} in {@DST Start} to {@DST End}
then DateAdd('h', 5, {@YourDateTimeGMT})
else DateAdd('h', 4, {@YourDateTimeGMT})





When that zero isn’t really a zero

Thursday 8 August 2019 @ 10:24 pm

Last week a customer was really befuddled. He had a formula that said:

if {@field} = 0 then ...

He could see lots of zero values but the formula didn’t work as expected.  He couldn’t figure it out so he sent it to me. The first thing I did was add a few more decimals to see if it was a rounding issue. That didn’t show anything, but the formula still insisted that the value was somehow NOT equal to zero.

So I went into the formula and multiplied the current value by one trillion. Then instead of zeros I started to see some small numbers.  I am not an expert on floating point values or database precision but I have seen this before in reports.  The solution is to round the value in the formula before comparing it to zero. In this case we rounded the value to two decimals like this:

if Round ({field}, 2) = 0 then ....

That made the formula behave as expected.  The odd part is that I have seen the same problem with two other customers in the past week. It could be just a coincidence, but I figured I would mention this and see if this is happening to more people.





RPT management utilities for 2019

Sunday 28 July 2019 @ 10:37 pm

I have just updated my comparison of RPT management utilities for 2019. These are tools that allow you to scan, document, compare and in some cases batch update RPT files. The list includes 9 tools:

Report Runner Documentor by Jeff-Net
R-Tag Documentation and Search by R-Tag
CR Data Source Updater by R-Tag
Visual CUT and DataLink Viewer by Millet Software
Report Miner by the Retsel Group
Code Search Professional by Find it EZ Software Corp.
Dev Surge 365 by Find it EZ Software Corp.
.rpt Inspector 3 Professional Suite by Software Forces, LLC
.rpt Inspector Online by Software Forces, LLC





Preventing the “division by zero” error

Friday 26 July 2019 @ 9:52 am

Crystal formulas can use 3 different divide operators:

  • Regular divide [ / ]
  • Integer divide [ \ ]
  • Percentage [ % ]

But all of these will fail if you follow the operators with a zero. The report will stop and Crystal will throw the error message “Division by Zero”.  The standard solution is to check and make sure the number you are dividing by is not zero before you do the calculation, something like this:

if {fieldA} = 0
then 0
else {fieldB} / {fieldA}

This way, whenever the bottom of the fraction (denominator) is a zero, the formula will print a zero and NOT try to do the calculation.

But even when customers use this formula pattern I still see the divide by zero error. Some users mistakenly check the top of the fraction (numerator) instead of the bottom (denominator).  Some do it correctly at first, but then change the denominator and forget to change the first line to match.  So I have developed the habit of using Local variables to make things easier. My normal pattern now looks like this:

Local Numbervar n:= {FieldA};//numerator
Local Numbervar d:= {FieldB}; //denominator
if d = 0 then 0 else n/d

This ensures that the value being checked is always the value on the bottom. Some other advantages of this method are:

1) If d is a subtotal or a long expression you only have to enter it in one place.
2) If you have to create a series of similar expressions, like for 12 different months, you can duplicate the first example and you only have to change the values at the top of the formula.





How to make sure you are on the last day of the month

Friday 19 July 2019 @ 9:18 am

Lots of reports require that you compare two different date periods and I often calculate prior date ranges based on the current range. But you have to be careful when the end of your prior period falls in a month with more days the the current period month?

Here is an example. Lets say your current period is in June and your prior period ends 6 months earlier in December. Calculating the Start Date is simple. If your Current Start is 6/1/2019 you can use a simple DateAdd like this:

DateAdd ('m', -6, {@CurrentStartDate})

but if you try a similar formula for the End Date you won’t get the right date. The following formula will return 12/30/2018:

DateAdd ('m', -6, {@CurrentEndDate})

The same issue occurs when your prior period is a year before and your current period ends in February. If he prior year is a Leap Year your prior period end date will be off by one day, ending on the 28th rather than the 29th.

My solution involves adding one day before you do the DateAdd and then subtracting that same day back out again, like this:

DateAdd ('m', -6, {@CurrentEndDate} +1) -1

This works because adding the one day puts on you on the first of the month. This is always a clean calculation when moving forward or backward by months or years. Then once you get to the first of the month in the prior period you subtract one day which always puts you on the last day of the month prior.





Finding groups where the last record meets a criteria

Monday 15 July 2019 @ 9:52 pm

One of my students presented me with the following challenge. Their address records were stored in a table that keeps an address history. That means that new addresses don’t replace the old addresses. Each new address is a new record with a time stamp. The current address is the record for that customer that has the latest timestamp.

To display the current address for each customer is fairly easy and can be done in one of two ways:

1) Group by Customer and sort by time stamp. Hide the details and place the address fields in the Group Footer. This would display the last address for each Customer.

2) Group by Customer and put in a group selection formula that says:

{Address.TimeStamp} = maximum ( {Address.TimeStamp} , {Address.CustomerID} )

Either of these will work to show the current address for each customer. But if you want to select only current addresses in a particular state, like NY, you have to be careful. If you put the State criteria in the record select expert or record selection formula the criteria will be applied before the grouping happens. Crystal will start by selecting only New York records regardless of how old the timestamp is. Then it will do the grouping and show the last NY record in each group. You would end up with the last New York address for each customer, rather than getting the accounts that have New York in their last record.  Anyone who had moved of NY to somewhere else would still show up.

My original solution involved a formula that combined the Date and the State into a single string field. Then I used a complicated group selection formula to find the right records. You can read about it here and it works fine.

But today I realized there is a simpler approach. The key is putting the State rule into the group selection formula, so it is applied after the grouping is done. So your Group Selection would look like this:

{Address.TimeStamp} = maximum ( {Address.TimeStamp} , {Address.CustomerID} )
and {Address.State} = "NY"

As long as the last line stays in the group selection formula this will return the desired records.





Next Posts »» «« Previous Posts
Jeff-Net
Recrystallize Pro

Crystal Reports Server