New App – Purchase Tracker

I've been using GnuCash for my money management for a long while, and quite like it.  But certain transactions cause problems.  In particular, when Heather goes to the store and buys apples and laundry detergent, the transaction needs to be split between groceries and household expenses.  You can imagine that when there are 30 different line items, that split gets cumbersome.

I've also oft wondered what the price of different things is over time.  For example, how much has the price of milk fluctuated over the past 12 months.  Being the good programmer I am, I built a very simple purchase tracking app, and make the 0.1 release available for download.  Installation is simple: unzip somewhere, and ensure you've got a MySQL DSN set up that matches the name in act_setappvars.cfm, and that CF has the ability to create/edit tables within.  Note that I've only tested on CFMX 7 and MySQL 4.1.  It should work on any Java-based CFML runtime with CFC support and on MySQL 4.1 or newer, but I haven't tested that.

Basically, it's a place to record the individual line items for a purchase, and the software will track the price of the items as well as split up the total purchase into different product categories.  Everything's dynamic, of course, and it works pretty well.

It's got a couple neat extra features as well:

  • Database auto-upgrade.  Check the database.cfc file for an automated upgrade script.  If/when you upgrade the code, that file will transparently ensure your schema is up to date as well.
  • Externalized strings.  All strings are externalized in standard Java properties files (and loaded through Java's PropertyResourceBundle class).  Note that locale support is NOT enabled, as I didn't want to introduce classpath dependancies, but it would be simple to add it manually.
  • Multiple configurations.  Built on top of the externalized strings is the ability to run the same code in multiple configurations.  In act_setappvars.cfm, you'll see an 'invoices' configuration that is commented out.  Uncomment it and you'll get a second copy of the database (with a different table prefix), and different strings throughout the UI so the same code can be used as a simple invoicing application.  The number of configurations is limitless.
  • Mollio for the UI.  Nothing particularly special here, but saved lots of time and still provides a professional look and feel.
  • Scriptaculous/Prototype for JS remoting.  A lot of the UI is centered around Scriptaculous's Ajax.InPlaceEditor, which saves a lot of page loads, and a lot of UI work.  A good example of how an HTML UI can be made a lot more usable with just a few simple additions.

Notably lacking is ColdSpring and Reactor support.  I greatly dispise their required server-level (ed. or at least webroot-level, per Kurt's comment below) configuration.  CF apps that are distributed as a pile of CFML should be neutral.  If you're distributing a WAR, no such qualms, of course.  If CF allowed for per-application (i.e. request-configured) mappings, that'd fix the issue.

7 responses to “New App – Purchase Tracker”

  1. Kurt Wiersma

    I don't really understand your ColdSpring comment. All you have to do is place the coldspring directory in your web root, no mapping is required.

  2. Barney

    Ah, but what if you're not deploying to the web root? For example, if I have an app at /app1, and another at /app2? They would both have to depend on ColdSpring located /coldspring, which means not only do both apps depend on code that isn't included in them (thereby creating a dependancy that can't be managed except for manually remembering), it also forces both of them to be compatible with the SAME version of ColdSpring.

    So it's still an environment mapping, even though it's not server-wide, just web-root wide.

  3. PaulH

    if you're on java enabled cf, then locale bits should be "there" w/out any classpath issues, though i'm really not sure about BD, is that what you meant?

    your rb keys aren't very "kosher". it would be better not to have dotted keys "addPurchase.title" (if you're doing this to manage these, get an rb tool w/logical groups, etc. instead). the rb CFC's rb finding logic isn't robust, normally you start w/the full rb file & locale (something_en_US.properties) & start chopping back until you hit the base rb (something.properties) or die trying. i guess you might grab my javaRB.cfc it also uses PropertyResourceBundle so it can work w/out the rb files being on the classpath. it also contains a MessageFormat function.

    anyway, great to see you kept i18n in mind when you developed this. and it looks to be a pretty useful app.

    thanks.

  4. Barney

    Paul,

    The locale bits are there, but using ResourceBundle.getBundle() isn't workable without adding your .properties files to the classpath. That's the dependancy I was looking to avoid.

    I know I haven't enabled locale-based selection support in the resource bundle loader. Honestly, the i18n was kind of a free bonus, I was externalizing the strings strictly so I could change the terminology on the site easily. I'll add in the locale selection at some point, but I didn't need it up front, so I didn't code it in. ;)

    There is MessageFormat support in there to, a simple UDF in Application.cfm that takes the string to format and then Java5-style varargs for the formatting arguments. I suppose it could go on the resource bundle class, but that seems like a weird place to put it. It has nothing to do with resource bundles, but everything to do with formatting in your UI.

    Regarding the non-kosher message keys, I don't quite follow. That's just the way I've always done it, because it seemed reasonable enough. What's the "right" way to do it?

  5. PaulH

    actually since you're using PropertyResourceBundle, you're passing in the rb file location & reading it. so there is no need for it to be on the classpath, it can be anywhere that cf can read. rb files have to be on the classpath only for ResourceBundle. that's why i have two java style rb CFCs (javaRB which doesn't need the classpath & rbJava which does).

    well we only use MessageFormat w/rb so that's where we stuck it (and we have enough i18n CFCs running around as it is). you're the 1st person to call it wierd though ;-) btw we've had some issues where numeric data needed to be cast for MessageFormat to swallow it.

    "addPurchaseTitle" would be a better choice. or use an addPurchase rb file w/the addPurchase related keys in that. what are you using to manage these? notepad ;-) you can get extra weirdness w/cf as that comes out like a structure's dotted notation (this just came up last week w/somebody's app falling down on railo). as most folks slap the rb into a shared scope per locale.

  6. Barney

    Notepad. How about Eclipse's Java properties file editor. And written in UTF-8 with Ant to convert them to ASCII with Unicode escapes. Notepad. Sheesh. ;)

    Regarding the MessageFormat numeric stuff, I've haven't run across any issues that using val() in the arrayAppend call didn't solve. Without that, however, plenty of issues. On another app, I'd run into a couple weirdnesses where CF passes a java.lang.Double into a {0,number,integer} format and the formater balks. I think I just used {0,number} in this app, even for integer values, so it wouldn't have arisen.

    I've always gone with the dotted notation to differentiate the camel casing. Otherwise is it the 'purchase title' item of the 'add' screen, or is it the 'title' item on the 'add purchase' screen?

  7. PaulH

    eclipse? might as well be notepad ;-) if you do complex rb get a good rb management tool, save your brains for something else.

    never used that val() trick, it takes care of numeric casts? sounds great. thanks for the info. event after all this time, i learn something new about cf every week i think.