Showing posts with label Data Group. Show all posts
Showing posts with label Data Group. Show all posts

Thursday, April 9, 2009

SET TALK appears to be on when running reports with SP2

I've been using VFP 9 SP2 ever since it first came out which was a long time ago. Yeah, I know there was that nasty Data Group bug, but to me, the enhancements in SP2 far outweighed the negative. Now that Microsoft has released a hotfix for the Data Group bug, I'm hoping more developers jump on the bandwagon and start adopting SP2. That said, a colleague reminded me last night about a bug where text is echoed to the screen as if a mysterious SET TALK ON had been issued.

This is a bug, but it’s one that you can easily fix yourself. You see, some of the new enhancements in SP2 were not implemented in the core VFP exe. They were implemented with a creative use of several Report Listener subclasses that reside in the FFC directory. When you run reports with SP2, the default behavior is to utilize these Report Listeners instead of the base VFP Report Listener class. The bug exists in one of those classes so you can change it yourself.

Modify the fxListener class of the _ReportListener class library and change the code in the LoadReport method. Simply move This.setFRXDataSessionEnvironment() so it comes before This.createHelperObjects() and the problem is solved.  

If you’re running on Vista, however, you have to be aware of the Virtual Store and work around it. When you edit the fxListener class, Vista makes a copy of the _ReportListener class library in the Virtual Store and your modifications are actually saved there and not in the C:\Program Files\Microsoft Visual FoxPro 9\FFC directory as you expected. Because a Virtual Store is specific to each user, it means the change you just made only works for the login you’re currently using. As soon as you switch to another login, the changes don’t exist for that user. To permanently fix the bug, move the _ReportListener class library from your Virtual Store back to the C:\Program Files\Microsoft Visual FoxPro 9\FFC directory, overwriting the original. Also, be sure to delete the _ReportListener class library from the Virtual Store for all users. If you forget this step, those users could be accessing an old copy of the class library.

Wednesday, April 8, 2009

VFP 9 SP2 Hotfix Updated

The VFP 9 SP2 Hotfix which addresses the Report Data Group bug (download from http://code.msdn.microsoft.com/KB968409) has been updated. This latest file includes the missing VFP9T.DLL (Multi-threaded runtime) file. In addition, the other hotfixes are no longer password protected.

Saturday, April 4, 2009

Installing VFP 9 SP2

Now that Microsoft has related the HotFix for the Report Data Group bug (read about it here), developers who have been holding off are now upgrading to VFP 9 SP2. This is great and I'm glad to see it. However, I'd like to take the time to remind everyone about some "gotchas" that might happen if you don't follow the right procedure when upgrading. 

If you have VFP 9 SP1 installed on your machine, there's a problem with the installation when upgrading from SP1 to SP2. It doesn't tell you there's a problem, but it doesn't fully install everything. The recommended procedure is to completely uninstall VFP 9 from your machine. Then re-install VFP 9, without any service packs. Once that step is completed, install Service Pack 2. Do NOT install SP1 .. skip that entirely. Just go straight to SP2. 

If you are running Vista, you also have to remember about the Virtual Store so as not to get bitten. Whenever you change a file in the Program Files directory, Vista creates a copy in your Virtual Store and redirects all future access to the new file. When Visual FoxPro opens a class, it does something that makes the O/S think it's been changed. Therefore, if you have ever opened any classes in VFP's FFC directory, those classes are now copied and sitting in your Virtual Store. That means when you upgrade to SP2, it installs new FFC files in the Program Files directory, but Vista keeps referring to the old ones in the Virtual Store whenever you access them. It completely ignores the new ones loaded with SP2. To get around this, delete the files from your Virtual Store ... HOWEVER ... if you have actually changed any of the FFC classes, you'll need to make copies of the files in the Virtual Store before deleting them so you can go back and compare your changes to the new SP2 versions and merge as needed.  

For those of you who want to install SP2 and still keep plain-Jane VFP 9 or VFP 9 SP1, that's not a problem. Rick Schummer wrote a great whitepaper that explains how to do this (Rick's whitepaper). 

VFP 9 SP2 Hotfix Minor Glitch

A few days ago I blogged about Microsoft releasing a hotfix for the VFP 9 SP2 Report Data Group bug (read it here). Last night, Rick Schummer blogged about a slight glitch with the hotfix and let us all know that Microsoft will have a resolution quickly. Instead of repeating what he said, I'll just point you to his blog post: (read it here). 
 

Thursday, April 2, 2009

Microsoft fixes Data Group Bug in Reports!

Woo Hoo!!   Yippee!!  Hooray!!  I'm jumping for joy!!

I'm extremely excited to let everyone know that Microsoft has fixed the serious Data Group bug in VFP 9 SP2 reports which I blogged about here. The bug is officially described in the following Microsoft KB article, where you'll also find detailed instructions on how to get the fix. 

FIX: The group header of a data grouping is not printed at the top of each page as expected after you intall Microsoft Visual FoxPro 9.0 Service Pack 2 (968409)

I've heard lots of developers say they've held off upgrading to SP2 because of this one bug. So those of you that fall in this category .. it's time to upgrade!! There are no more excuses!! 

Several people in the FoxPro Community, myself included, worked hard at getting this bug fixed and spent a lot of time testing it. Please ... I don't want to hear any bitching from anyone about how long it took to get this. Instead, let's all focus on the positive side which is the fact that Microsoft listened and provided a fix to a serious bug.

P.S.  Read Rick Schummer's blog for more thoughts about this bug fix.  

Sunday, March 22, 2009

Workaround: Gap with Multiple Data Groups

In November, 2007, I reported a bug in the VFP 9 Report Writer that related to data groups (Bug 312572). This isn't the major data grouping bug that everyone's been talking about, but rather a smaller one I stumbled on while doing all my testing. It has to do with a strange gap of whitespace appearing on the report under certain circumstances as shown in the following:



After getting some insight from a developer at Microsoft, I was able to understand what was going on behind the scenes. That knowledge helped me figure out a workaround for this issue.

The report has three data groups. The first data group breaks on an expression of: .T. The second data group breaks on a field in the data: GrpField. The third group is used to print some whitespace every 5 detail records. Its expression is: INT(rnLine / 5). The report also has a variable defined to help count the number of detail lines: Name = rnLine, Value to store = 0, Initial Value = 0, Reset value = GrpField, Calculation = Count.

The problem is that the third data group uses the rnLine report variable which is reset on the second data group. As the second data resets, it alters the value of the report variable, which messes with the data grouping and completely confuses the Report Writer.

The solution is to alter the report variable in a slightly different way so it doesn't confuse the data grouping. Only two slight changes are required, but the changes are enough to make the Report Writer happy. First, change the definition of the report variable to reset at the end of the report instead of the second data group.

The second change is to add the following to the On Entry Expression in the Data Group Header of the second Data Group: _VFP.SetVar('rnLine', rnLine - MOD(rnLine,5)). This change manipulates the report variable by reducing the value back to the last number that triggered a break. It's a sneaky workaround that's pretty effective as shown in the following example that doesn't have the gap:



As with most of the Visual FoxPro Report Writer, a little creative thinking can go a long way!

Tuesday, January 1, 2008

How Do I ... Create some complicated data groups and build a recap?

I recently had to create a very complicated report for a client that covers several different techniques. I thought it would be good to share this modified sample with everyone.

At first, the report sounded simple (don't they all!) Create a list of orders, sorted and subtotaled by customer and each customer should start on a new page. That's easy, I can handle that. Put the orders in the detail band. Create a data group on the Customer and mark it to start on a new page. Put subtotals for the customer in the Data Group Footer band.

Oh .. the client says .. the list of orders per customer can get quite long. Can we have ledger-style shading or a line every so many lines? Sure, I think I can handle this. Oh ... the client says again ... we'd also like subtotals at the bottom of each page showing the totals of just that page. That should be easy .. I can use the page footer band. Oh wait .. as I think that through some more, it's not going to work because on the last page for the customer, the data group footer band would print and then the page footer would show up at the bottom. That's not cool. Hmm ... these two requirements combined make things a little sticky .. but I'm sure I can come up with the answer.

Then comes the killer requirement .. Remember those page subtotals we want at the bottom of each page .. well .. at the end of the customer we'd also like a recap that shows each one of those subtotals like the following:

Page 1, $999.99
Page 2, $999.99
Page 3, $999.99

Yuk .. I really don't like that one especially combined with the ledger-style and page subtotals. Now I'm really going to have to put some thought into this report. I think my head is spinning!

For this example, I'm using VFP 9 SP2 (although it should work with previous versions because I'm not using a ReportListener or any of the new features.) I'll use the TasTrade data stored in C:\Program Files\Microsoft Visual FoxPro 9\Samples\Tastrade\Data. I wanted to be flexible with the number of lines per ledger section and the number of lines per page so I decided to control them with a variable. That way, if the customer changes their mind down the road I can easily change it.

I also didn't want to mess around with trying to build the data for the recap ahead of time. Again, I was thinking down the road about using this technique for other situations and if I were to use it on a report with stretchable fields, I didn't want to use hard-coding to "count" each record and figure out the appropriate page it falls on. I just wanted to let it flow and let the report build the recap data for me no matter how many records appeared on a page. I also didn't want to have to hard-code all the variables to be totaled. I wanted some generic code that totals all the numeric or currency fields.

Let's start by looking at the final report as shown below in Figure 1. The first page shows the records for the first customer. The second page shows the "recap" for that customer, which only contains one page. The third and fourth pages do the same thing for the second customer. The fifth through seventh pages show the details for the third customer and the last pages shows the "recap" of each of the three previous pages which belong to the third customer.









Figure 1: The final printed report.

Next, let's look at the simple program that I use to create the data and print the report. Notice that I use two variables to control the groupings. I could have hard-coded this in the report itself, but this is more flexible.

*-- Build the data
SELECT '1' AS RecType,
Orders.Customer_ID, ;
Customer.Company_Name, ;
Orders.Order_Number, ;
Orders.Order_Date, ;
CAST(Orders.Discount AS N(14,2)) AS Discount, ;
CAST(Orders.Freight AS N(14,2)) AS Freight, ;
CAST(NVL(SumLines.nPrice, 0) AS N(14,2)) nPrice ;
FROM 'C:\Program Files\Microsoft Visual FoxPro 9\Samples\Tastrade\Data\Orders' ;
LEFT OUTER JOIN 'C:\Program Files\Microsoft Visual FoxPro 9\Samples\Tastrade\Data\Customer' ;
ON Customer.Customer_ID = Orders.Customer_ID ;
LEFT OUTER JOIN ;
(SELECT OrdItems.Order_ID, ;
SUM(OrdItems.Unit_Price * OrdItems.Quantity) AS nPrice ;
FROM 'C:\Program Files\Microsoft Visual FoxPro 9\Samples\Tastrade\Data\OrdItems' ;
GROUP BY OrdItems.Order_ID ;
) SumLines ;
ON SumLines.Order_ID = Orders.Order_ID ;
INTO CURSOR tmpReportData READWRITE

*-- Index the data
SELECT tmpReportData
INDEX ON Customer_ID + RecType + Order_Number TAG SortKey
GOTO TOP

*-- Set some variables to control # of lines
*-- for each section and # of lines per page.
nMaxLinesPerLedger = 2
nMaxLinesPerPage = 6

*-- Run the report
REPORT FORM ComplexGroups_Recap TO PRINTER PROMPT PREVIEW

Besides the above program, I also need a function that I'm going to call from the report itself. This function will update the cursor that is driving the report by either adding a new record for a particular page, or adding to the numeric fields for the given page. Note that I have to take great care to move the record pointer back to where it was when this function was called. Otherwise, you'll make a mess of the running report. Add the following function to the bottom of the program that runs the report so it's available to the report.

************************
FUNCTION AddToPageTotals
************************
LPARAMETERS tcField, txPage, tcSeek, tcIndex

LOCAL loRecord, lnRecNo, lcAlias, lcSeek, lx

*-- Don't do this on the total records themselves!
*-- (Can you say "infinite loop")
IF RecType = '2'
RETURN
ENDIF

*-- Remember where we are
lnRecNo = RECNO()
lcAlias = ALIAS()

SCATTER NAME m.loRecord
m.loRecord.RecType = '2'
m.loRecord.&tcField = txPage

lcSeek = EVALUATE(STRTRAN(m.tcSeek, 'XTMPX', 'm.loRecord'))
IF SEEK(m.lcSeek, m.lcAlias, m.tcIndex)
*-- add the amount fields to the existing record
lnFields = AFIELDS(laFields)
FOR ln = 1 TO lnFields
lcField = laFields[m.ln, 1]
lx = EVALUATE(m.lcField)
IF VARTYPE(lx) $ 'NY'
REPLACE (m.lcField) WITH NVL(EVALUATE(m.lcField),0) + NVL(m.loRecord.&lcField,0)
ENDIF
ENDFOR
ELSE
*-- Add the record for this page
INSERT INTO (m.lcAlias) FROM NAME m.loRecord
ENDIF

*-- Go back to the "detail" record that we were processing
GOTO m.lnRecNo

Now let's get to the definition of the report, which is the meat of this solution. Figure 2 shows how the report definition will look when we're done.


Figure 2: The report definition.

Start by creating 3 report variables:
  • rnLinesOnPage

    • Value to Store = 0
    • Reset = Report
    • Calculation Type = Count

  • rnLinesForCustomer

    • Value to Store = 0
    • Reset = Report
    • Calculation Type = Count

  • Dummy

    • Value to Store = AddToPageTotals('Order_Number', TRANSFORM(_PageNo, '999999'), 'XTMPX.Customer_ID + XTMPX.RecType + XTMPX.Order_Number', 'SortKey')
    • Reset = Report
    • Calculation Type = None

At first thought, it might look like I made a typo above because none of the variables reset at the Customer group or Page group. But trust me .. it's not a mistake. Just bear with me and it will make sense soon.

The last variable is used to call the function we created earlier. It looks a little complex but that's just because I made the function generic so I can reuse it on other reports. You could simplify it by taking away all the parameters and hard-coding the function.

Next, create 4 different Data Groups:
  • 1: Customer_ID, New Page Number 1, Reprint group header on each page

  • 2: RecType, New Page

  • 3: INT(rnLinesForCustomer / nMaxLinesPerPage), New Page

  • 4: INT(rnLinesOnPage / nMaxLinesPerLedger)


Expand the #1 Data Group Header band and put the Customer field.

Put the detail fields in the Detail band and lay them out as you desire.

Expand the #4 Data Group Header band and add a horizontal line to separate the chunks of detail records. I made my light gray so it's not so distracting.

Expand the Data Group Footer bands for #3 and #4 so you can add some totals. Copy your numeric fields from the Detail band into the two different group footer bands. Next, change the Calculate option on each to SUM and reset on the applicable group.

Now go ahead and add whatever column headings you want. You can use Print When logic of RecType = '1' if you want to print something for just the regular data. Use RecType = '2' if you want to print something on just the page recaps.

At this point you might think we're done, but we're not. Remember when we defined the report variables I mentioned that we are not resetting them at the appropriate data groups. The reason is that whenever you try to alter the variable that is being used to control the groups, the VFP Report Writer gets completely confused. You end up with more breaks than you want and often times you get pages with no detail records and just some totals.

So here's the trick behind this whole report. We need to reset the report variables ourselves. Well, actually, we aren't resetting them, we're fudging them. The problem is that if we reset them to zero, we get the extra breaks I just mentioned. So instead of resetting them to zero, we fudge them to a value that makes the next page start at a clean point.

We are going to alter these variables in the On Entry and On Exit Expressions of the #2 Group Footer band as follows:

On Entry: _VFP.SetVar('rnLinesForCustomer', rnLinesForCustomer- MOD(rnLinesForCustomer, nMaxLinesPerPage))

The above "expression" resets the rnLinesForCustomer variable to a value that is equal to what the count would be if the page was completely full of lines, without resetting it for each page. So if we allowed 10 lines per page, at the end of the first page we reset it to 10, at the end of the 2nd page we reset it to 20, and so on. Regardless of how many lines actually printed, we just make it think that full pages have been printed. By doing this, we avoid the funky extra breaks that happen when you try to zero out the variable.

On Exit: _VFP.SetVar('rnLinesOnPage', rnLinesOnPage- MOD(rnLinesOnPage, nMaxLinesPerLedger))

The above "expression" does the same thing for the ledger breaking as the previous one did for the page breaking. It really doesn't matter which one is in the On Exit and which one is in the On Entry. I just needed to reset two variables and this gave me a place to do each one.

That's it. We're done. Simple, right? Well .. of course it's simple when you already know the answer. Trust me .. it wasn't that simple for me to come to this conclusion, but now you can benefit from my troubles. If you want the sample program and report I used to create this blog, just send me an email and I'll gladly send it to you.

Tuesday, December 4, 2007

Gotcha: Workaround for Data Group bug

I've had a few people ask for suggestions on how to work around the ugly data group bug that I previously blogged about. So far, the only work around I have is not generic and has to be applied to EVERY report that uses data groups. I HATE this solution, but it's the only one I have so far.

1. Create a report variable for any data you need to show in the Data Group Header. Set the expression to the data field. (rcCustomer)

2. Create a report variable to count the number of detail lines in the data group. Set the value to 0, Reset it on the data group, and set the Calculation to Count. (rnCustCount)

3. In the Data Group Footer, force the counter to zero after the data group by entering the following in the On Exit Expression: _VFP.SetVar('rnCustCount', 0)

4. In the field object in the Data Group header, enter the following in the Print When expression: rnCustCount = 0

5. Now copy the field object, but change the reference to the data field to refer to your report variable (rcCustomer). Then change the Print When to rnCustCount > 0. Just stack this on top of the original field object.

You now have two objects in the data group header. The original one prints only the first time the data group prints. The second one prints on subsequent pages and uses the report variable instead of the data reference so it will be the right record. An added bonus to this solution is that you can add the word "Continued" in the expression that prints on subsequent pages.

Tuesday, November 27, 2007

Gotcha: VFP Reporting Bugs

I'm really frustrated that I have not received ANY response at all from Microsoft about the recent Data Group bugs I reported. I was already frustrated that they hadn't addressed any of my other report-related bugs .. but these ones are serious and were introduced by SP2.

In hopes of putting more pressure on them .. here's a list of all the bugs I have reported through the official channels. You can use the following link to go to the Microsoft Connect page. Near the bottom of the page is a place to enter the ID of the issue. Then you can rate the bug, validate it, and provide any additional comments. Maybe if enough of us complain, they will address it.

Microsoft Connect

249264 VFP Print When processing wrong parent record
291486 VFP Detail Footer Band -- Wrong record
292987 VFP Preview - Zoom level not always programmatically changeable
293615 Print When logic is evaluated at the wrong time in VFP Reports
295113 Shapes stretched across Detail Header and Detail Footer bands whacko
288896 Squished Printing
286313 VFP SYS(1037) no longer has a PRINTER button in Vista
286314 Report Properties dialog locks up
286552 Code References - error printing when not in Admin on Vista
286958 Drag control from Toolbox to Pageframe and Labels disappear
287927 Intellisense on SET gets confused
312568 SP2 broke data group headers repeated on subsequent pages
312572 Gap with multiple data groups on reports

Tuesday, November 20, 2007

Gotcha: More on the Data Group Bug

I've been trying to see if I can come up with a ReportListener that will somehow generically workaround the serious Data Group bug I previously blogged about (Data Group Bug) In the process, I tried to come up with a really good test report to make sure I'm doing it right. I created a report with 3 data groups so I can be sure my code handles multiple data groups.

Well .. let's just say I haven't even gotten to the part of writing code yet. While developing a test report, I came across ANOTHER bug related to data groups. What I'm seeing is a gap on the report after the 3rd data group header prints, but not every time. It only happens for the third data group, when it immediately follows the second data group header. Oh .. and it doesn't happen the first time for the set.

Figure 1 shows how the report looks on page 1, which is correct. Figure 2 shows what happens on subsequent pages, which is incorrect.


Figure 1 - The first page - without any gap.


Figure 2 - Subsequent pages - with a big gap!

Obviously, this whole data group issue is getting worse and worse. I'm afraid to keep working on the "workaround" code. Who knows what other bugs I'll uncover! I sure hope Microsoft fixes this! Maybe we should ALL flood them with emails about these report bugs!!

Wednesday, November 14, 2007

Gotcha: Serious report bug with Data Groups introduced in VFP 9 SP2

Here's the situation: You have a report with a data group such as Customer. You checked the "Reprint group header on each page" option to make sure the group header reprints whenever the details for the group overflow to a new page. You put some total information in the group footer band. The report looks somewhat like what is in Figure 1.

Figure 1: The "Good" report

Now let's say that the data just so happens to work out so that the last detail record for customer 1 prints at the bottom of the page, thus, the data group footer needs to go to the top of the next page to print.

In VFP 7, VFP 8, and VFP 9 SP1 ... you get what's shown in Figure 2. The data group footer for customer 1 prints at the top of the next page, without bothering to reprint the data group header information for customer 1. This is a bug .. but not exactly the end of the world. I could probably live with this anomaly .. if I really had to.

Figure 2: The "Bad" report in VFP 7, VFP 8, and VFP 9 SP1

However, in VFP 9 SP2, Microsoft attempted to fix the bug and inadvertently introduced a more severe bug as shown in Figure 3. The data group header prints at the top of the next page BUT it's printing the data from Customer 2, the NEXT record. Then it backs up and the data group footer prints for Customer 1. Then the detail records for Customer 2 begin printing WITHOUT printing the data group header for Customer 2.

Figure 3: The "Bad" report in VFP 9 SP2

As you can see, this is a real mess. Printing the group header with the WRONG customer information is far worse than omitting the group header. This is completely unacceptable in my opinion!! It's completely misleading to print Customer 2 and then show the totals for Customer 1. In fact, as far as I'm concerned, it could cause some serious legal issues for my clients using my reports in some situations.

As it stands now, you cannot ever safely print data in the Data Group Header band if it's marked to repeat on subsequent pages. This is a serious bug --- and Microsoft needs to fix this ASAP!!!