Steve Wiseman of IntelliAdmin blogged about this a while ago, but I finally got around to downloading and installing this little add-in for Microsoft Office 2007. It adds "PDF or XPS" to the Save As menu, providing a very easy (and FREE!) way to save your Office documents as PDF files.
Friday, May 30, 2008
A stack is a data structure that's been around the computing world since the beginning, and is part of every CPU. Recently I ran into a problem where a stack was the perfect solution.
The issue was saving and restoring the state of global settings, in this case ON('ESCAPE') and SET('ESCAPE'). At the beginning of a routine where I change them (the RunReport method), I stored the current values into properties of the object (I used properties rather than local variables because another method does the actual restore), changed these settings as needed, and at the end of the routine called a method that restores the settings from the saved values. (I'm sure you've written code like this a hundred times.)
However, here's the problem I ran into:
- RunReport calls a method of another object (PrintReport) that also saves, changes, and restores these settings. This wouldn't normally be a problem because PrintReport cleans up after itself before it exits. However ...
- We added drilldown capabilities in Stonefield Query. While a report is being displayed, execution is still in PrintReport. If you click a link in the report, RunReport is called again to run the linked report.
See the problem? In the second call to RunReport, the settings it saves are those set by PrintReport, which overwrite the original settings it saved. So, by the time you close the reports, the code restoring the settings doesn't set them to the original values.
Since you can have a virtually unlimited number of levels of drilldowns, it made sense to implement a stack to store the settings. At the start of a routine, the code pushes the current settings onto the stack and at the end, pops them off.
As a refresher for stacks, think about a stack of plates in one of those spring-loaded plate holders you see in some restaurants. When a new plate is added to the stack, it's added at the top and the rest of the plates are pushed down. When a plate is removed, it's the one at the top of the stack (the one added most recently) and the rest of the plates move up. That's exactly what we want to happen in this case. Adding an item to a stack is called "pushing" and removing an item is called "popping".
I briefly considered using an array to hold the settings, but decided instead to use one of my favorite classes, Collection. The implementation was unbelievably simple. I subclassed Collection and added two methods: Push and Pop. Here's the code for Push:
Here's the Pop method:
if .Count > 0
luValue = .Item(.Count)
luValue = .NULL.
endif This.Count > 0
Using Count as the index for the item to pop and remove ensures the last pushed item is the one that's popped. Note that I decided to return NULL if the stack is popped more often than pushed (that is, Count is 0). You might decide to throw an error instead.
Here's how this class is used:
loStack = newobject('SFStack', 'SFStack.vcx')
* change the settings and do whatever
lcCurrEscape = loStack.Pop()
lcCurrOnEscape = loStack.Pop()
on escape &lcCurrOnEscape
if lcCurrEscape = 'OFF'
set escape off
endif This.cCurrEscape = 'OFF'
Note that items are removed from the stack in the opposite order they're added, so this code handles that when it retrieves the saved settings.
John Jantsch of Duct Tape Marketing blogged today about something that was essentially the reason my partner and I started Stonefield: we wanted to stop trading hours for dollars. Given that we have a limited inventory of hours, doing consulting work at an hourly rate automatically caps your revenue. Also, hours you don't spend billing (sick, vacation, etc.) are lost from inventory. Instead, we wanted to focus on products.
Of course, when we got started, we didn't have any products, so we had to pay for the development of products by doing fee-for-service consulting and development. We did that for several years while we developed product after product: Pharmacy Partner, a retail pharmacy program; Stonefield Data Dictionary, Stonefield AppMaker, Stonefield Database Toolkit, Stonefield Query, and Stonefield Reports, all tools for Visual FoxPro developers; and currently Stonefield Query, our flagship award-winning query and report writing software, which is actually a whole family of products. We haven't done any custom development projects for about eight years and couldn't be happier.
That's not to say that we do no fee-for-service work. We do a lot of consulting for Stonefield Query end-users (creating very complex reports, for example) and Stonefield Query SDK developers (helping them polish their data dictionary, develop complex scripts, or even do the entire work of creating a customized version for them). However, that really is more of a sideline than our core work.
Of course, when you have actual products for sale, that's the market you're in. One thing that's more difficult to do is what John discusses: turning a service into a product. On our Accpac consulting side (Stonefield Systems Group Inc.), we've struggled to convert services like Accpac installation, report creation, and disaster recovery into fixed-fee projects. Interestingly, the struggle hasn't been from the client side. Most clients prefer you to quote a fixed fee for work, and in fact expect it in one form or another (even if you quote an hourly rate, they still want an estimate of the total bill). No, the struggle has been our own mindset: convincing our staff this is the way to go. We've gone a long way to achieving that, but still are doing lots of work on a fee-for-service basis.
But it's worth the struggle. As I pointed out to our staff a couple of years ago, we have a strange paradox: the more experienced we become, the faster we can do a particular job, and therefore the LESS money we make if we're billing by the hour. We'd be better off giving jobs to junior consultants because although their billing rate is less, it takes them WAY longer to do a job. (Just for the record, we have no junior consultants. They've all been working with Accpac for years and are considered among the best in the business). But if we get out of the mindset of billing by the hour and instead look at the value we deliver to the client, that means our experience becomes of value. Say we charge $500 to do a job. A junior person might take five hours to do it (the equivalent of $100 per hour) but a senior consultant may be able to do it in one hour ($500 per hour). It'd be pretty hard to convince the client to pay us $500 per hour but to charge $500 to do something that will save them (or better, make them) thousands of dollars per year seems like a good deal to most people.
As I said, we're still working on this, but we are getting better and hopefully one day, all but the simplest technical support will be packaged and priced as a product.
Tuesday, May 27, 2008
Sage Accpac held its annual Sage Insights business partner conference in Washington, DC the week of May 11, 2008. Laurie Schultz, General Manager of Sage Accpac, announced the top 10 worldwide Sage Accpac Business Partners for customer service and satisfaction. Stonefield Systems Group Inc. ranked number 6 overall.
The ranking was determined through a comprehensive survey of Accpac customers. Clients were asked, "Are you happy with the service you are receiving from your Accpac Business Partner and would you recommend them to other organizations?"
My business partner Mickey Kupchyk and I were surprised and honored to be ranked so high among the over 600 Accpac dealers worldwide. Accpac is our core business at Stonefield Systems Group (as opposed to Stonefield Software, where Stonefield Query is our flagship product line). We will continue to work hard at providing top level service and training to our customers.
Tuesday, May 06, 2008
Steve Bodnar has a great post on his blog about why you should attend Southwest Fox. Actually, his reasons are applicable to pretty much any conference. If you haven't been to a conference before, or it's been several years, read his blog and find out what you're missing.
Friday, May 02, 2008
On May 1, we announced the speaker and session lineup for Southwest Fox 2008. It was even harder selecting from the outstanding list of proposals this year than it was last year, and Rick, Tamar, and I are very excited about the sessions being presented this year. There are some killer topics such as taking advantage of GDI+ in your VFP applications, creating custom report controls, profiling and refactoring code using the VFPX Code Analyst tool, using WMI, taking advantage of the Sedna Upsizing Wizard, using Ajax and jQuery in Web applications ... the list is long and exciting!
Even better, in my opinion, is the chance to see old friends again and meet new ones. Many people say networking is the real reason to attend conferences, and that's absolutely true.
Registration is now open, so be sure to sign up today for a fun three days in Phoenix in October. Even better, if you register before July 1, you get a free pre-conference session, a $99 value. With sessions on Transact SQL, MySQL, and the VFP 9 report writer available, the hard part will be picking which one to attend.
We're looking forward to seeing you in October!
Thursday, May 01, 2008
I've been using Windows Vista for more than a year now and like it a lot. I really like some of the new fonts that come with Vista, including Calibri, the default font for Microsoft Outlook messages. However, from time to time, I need to take a document I've created on my Vista system and use it on an XP system. The problem is that the new Vista fonts don't exist on XP. Fortunately, there's an easy way to install those fonts, as described at http://labnol.blogspot.com/2007/03/download-windows-vista-fonts-legally.html.