Last update: July 11, 2010
umm web site!What's umm? It's Uwe's Money Mangler — errr,
Manager! umm is a tiny, minimal, command-line
accounting program. I was motivated to write it by a couple of things:
I had been using Quicken from Intuit for some years, and that had an
annoying failure mode every so often: an upgrade of computer hardware
or OS would cause Quicken to not run anymore, and some years of my
data would thus be trapped in a binary blob and lost. I'd chuck the
whole thing in frustration and stop keeping track of this (really
rather important) stuff for a year or two, and then I'd start over
again. Not ideal!
So... eventually I got tired of this, and I decided to try
something different. I had looked at gnucash, but the last time I looked
at it I was frankly intimidated... huge, with lots of
dependencies — although it's open-source, it was not going to be
as simple as I wanted. Recently I came across
ledger
and its haskell remix hledger, and I looked at
those. But there were a couple of minor things I didn't like so much,
and there were a couple of features I wanted that they were missing,
and anyway I needed a hacking project, so I decided to just take the
idea and write my own: umm.
The design criteria for this little program were fairly simple:
umm →
plain-text file formatumm is concernedledger and
hledger; it seems eminently sensible to me)utf8-string, to better handle the aforementioned currency
symbols;A few specific things I wanted the program to do:
umm doesn't care.umm can figure
it outumm can keep track of them and only bug me and thee when
it's time to do soledger, I didn't want umm to automatically
infer the names of new accounts or categories as I enter them:If you prefer a more GUI-oriented program, you should probably look elsewhere... but if you think this might work for you, then read on.
umm.exe
or something similarummumm from SourceYou'll need a working haskell compiler for this. I've used ghc 6.8.3 on an old iMac G3 running OSX 10.3.9 (my main development platform, actually — if it works here & performs ok, it'll be fine everywhere else...), ghc 6.10.{1,3,4} on x86 running a slightly out-of-date version of Ubuntu Linux, and ghc 6.10.3 on Windows XP. I've also tried ghc 6.12.1, but I haven't fully upgraded all the libraries, so that didn't work. But I don't anticipate any significant problems once libraries and cabal are updated.
Grab the source, above, unpack it, chdir into the source directory,
then run
cabal configure && cabal build
or
ghc -O2 --make -o umm UMM*.hs
ummWhether you built as per above, or you downloaded one of the pre-built
versions,
just move the umm executable to wherever you want,
and you're good to go!
Here's a complete small ledger file. See also sample1.dat in the source tarball, slightly tweaked from this to test some currency symbols.
ccs US$ "The Almighty Buck"
income cash
income interest
expense cash
account acc1 2009-9-1 "test account 1"
account acc2 2009-9-1 "test account 2"
group all acc1 acc2
ccs FOO "The Foo Companies"
ccs BAR "The Bar Companies"
ccs CLAM "Groupe Mollusque"
xfer 2009-9-1 cash acc1 100
buy 2009-9-1 acc1 10 FOO 15
xfer 2009-9-2 interest acc1 0.25
buy 2009-9-2 acc1 20 FOO 27
xfer 2009-9-3 acc1 cash 5.25
xfer 2009-9-1 cash acc2 100
buy 2009-9-1 acc2 10 BAR 25
xfer 2009-9-2 interest acc2 0.25
buy 2009-9-2 acc2 20 FOO 27
xfer 2009-9-3 acc2 cash 5.25
xfer 2009-9-4 acc1 acc2 25
xfer 2009-9-5 cash acc1 200
buy 2009-9-5 acc1 30 FOO 40
buy 2009-9-5 acc1 30 BAR 40
price 2009-9-5 FOO 1.5
xfer 2009-9-6 cash acc1 20 CLAM
price 2009-10-21 FOO 2.1
price 2009-10-24 FOO 2.4
price 2009-10-25 FOO 2.5
price 2009-10-23 FOO 2.3
price 2009-10-22 FOO 2.2
price 2009-10-25 BAR 3.25
price 2009-10-27 3 BAR 10
split 2009-11-5 CLAM 2 1
price 2009-11-11 CLAM 0.001
You run the program like so:
umm ledger-file command options
where command options is one of the following
balance [account-or-group] [date]register account-or-group [date-range]reconcile [account-or-group] [date]change account-or-pseudo-account
[date-range]price ccs [date-range]todo [date]list [all | accounts |
ccs | expenses | income |
groups]basis ccs [date]exportledger format —
only partially implementedledger
plot ccs-or-account [date-range]
[output-file-template]gnuplot to generate a plot of the specified
ccs-or-account in the specified date range.The commands may be shortened to the unique prefixes or anything
longer: 'bal', 'bala' etc, 'reg', 'regi' etc, but not 'r'.
If no command or options are specified, the default action is to show the
balances of all accounts as of the current date.
account is the name of an account, account-or-group
is the name of an account or an account group,
and
account-or-pseudo-account is the name of an account or an
income or expense pseudo-account.
ccs is the name of a currency, commodity, or security.
date defaults to the current date if not specified.
date-range specifies the range in which the operation should show results. There are several ways to specify a date range:
2010-1-1 2010-3-31" describes the first
quarter of 20102010-6-31", when given in a range context,
specifies the range of dates from the beginning of time until the end of
the second quarter of 20102010-6" corresponds to June 1 through 13 of 2010,
whereas "2009-6" corresponds to June 1 through 30 of 2009last N", where N is some number of days:
"last 30" corresponds to roughly the past month, etc.
still under construction — see the actual program, which does have a description of each record type in its help listing.
umm is line-oriented, with each line of the ledger file
forming a separate record. There are about a dozen types of records
in a ledger, (mostly) indicated with keywords:
ccs
name [description] [amount] [name]income
name [description]expense
name [description]account
[rec-mark]
name
[date]
[description] [amount [name]]group
name [name...]price
date
[amount] name
amount [name]xfer
[rec-mark]
date name name
amount [name]
[description] [id]xfer
[rec-mark]
date name
{
name1 amount1 [name1],
name2 amount2 [name2],
...
} [description] [id]buy
[rec-mark]
date name
amount name
amount [name]
[description]sell
[rec-mark]
date name
amount name
amount [name]
[description]exch
[rec-mark]
date name
amount name
amount [name]
[description]split
date name
amount amounttodo
[rec-mark]
date description
birthday
[rec-mark]
date description
anniversary
[rec-mark]
date description
# comment
; commentBlank lines are also treated as comments, and leading and trailing
whitespace in any line is ignored. Also, all combinations (or mixtures)
of <CR> and <LF> characters are
accepted as line-end markers, so there should be no portability issues
between *nix and windows systems — the intent is that it should
Just Work... if it doesn't, shout at me!
Internally, not all of these are distinct: buy and
sell are just syntactic sugar for exch, and
birthday and anniversary are more or less
syntactic sugar for todo, but you probably don't need to
worry about that unless you want to hack on umm. Also,
internally there is another type of record, for indicating parsing
errors. These get printed out as
#err ccs 123bux
New in version 0.1.5 is the second form of the xfer
record: this allows specifications of multiple transfers from one
source account to multiple target accounts as one transaction. This is
to do things like specifying that a part of my salary goes to my savings
account, a part goes to my 401(k), etc. I don't have as compelling a use
case for the corresponding merge, ie multiple source accounts
to one target account, so I haven't implemented that.
Since split-transfer transactions can get somewhat long, I've also added, as of version 0.1.5, the ability to split a record across multiple lines, like this:
xfer 2038-1-19 salary {savings 100 GalCred,\
\ retirement 20 GalCred, taxes 80 GalCred}
The effect is to merge the two lines and annihilate the two backslashes and all the whitespace between them. Because there are two backslashes, you have exact control over other whitespace outside the backslashes; thus, this allows you to break a line in the middle of a string or indeed in the middle of an account or ccs name. This is not necessarily a good idea... don't abuse it.
Also, because of the order in which various operations get done inside the program, in order to comment out the above, you would need to add just one '#' at the beginning of the first line only:
# xfer 2038-1-19 salary {savings 100 GalCred,\
\ retirement 20 GalCred, taxes 80 GalCred}
However, in accordance with DWIM, the program will also accept
# xfer 2038-1-19 salary {savings 100 GalCred,\
# \ retirement 20 GalCred, taxes 80 GalCred}
New in version 0.2.0 is the optional periodic prefix for
xfer, buy, sell, and exch
records. This is a way to indicate that the transaction is repeated multiple
times at regular intervals. This is useful for tracking regular payments, or
automatic transfers of money into an investment account, etc. I'm not quite
sure that it's as useful for buying and selling stuff, since presumably
usually the price will vary depending on market conditions, but I've added it
anyway. There may possibly be a use for it with a todo record,
but I can't think of any reason why any of the other record types would need
this modifier... see the next paragraph for essentially periodic
todo records, albeit only with a period of 1 year.
New in version 0.2.1 are the birthday and
anniversary records. These aren't quite financial,
but it seemed to me that they are related enough, certainly in the
periodic-reminder aspect, that it made sense to add them. They act pretty
much like todo records, except that (i) they only
trigger in a period of one week on either side of the specified date, and
(ii) the print format is ever so slightly different: the year is
not included in the printout.
The structure of the periodic prefix is as follows:
recurring period until end-date [reconciled rec-date]
where recurring, until, and
reconciled are literal keywords, end-date and
rec-date are dates in the usual format, and period is one
of the following:
daily |
every day | |
weekly |
every week, ie, every 7 days | |
monthly |
every month | |
quarterly |
every 3 months | |
annually |
every year | |
biweekly |
every 2 weeks, ie, every 14 days | |
bimonthly |
every 2 months | |
biannually |
every 2 years | |
semiweekly |
twice a week, at D and D+3 | |
semimonthly |
twice a month, at D and D+15 | |
semiannually
|
every 6 months | |
N days |
||
N weeks |
||
N months |
||
N years |
The start date of the series is taken as the date in the base record. To generate the series, this start date is stepped by the period and records are generated as long as the stepped date is not later than the specified end date. The generated records are marked as reconciled if they are before the rec-date, and unreconciled if after. If that's not specified, then internally the rec-date is set to "the beginning of time" which means that none of the generated records are marked reconciled.
For example, here is a complete xfer record describing a
transfer of $100 from a checking account to an investment account every
other Friday for all of 2010 (and please ignore the fact that January 1,
the start date, was a holiday):
recurring biweekly until 2010-12-31 reconciled 2010-5-1 \
\ xfer 2010-1-1 checking invest 100
Note that since the periodic modifier is longer than the record
itself, I've used split-line notation here, and in fact umm
will do this automatically: when printing periodic records like the above,
it will always show them in the above split-line form.
There are six modules:
Main |
UMM.hs |
this is the main routine, plus most of the IO stuff |
UMMData |
UMMData.hs |
definitions of the various data types, plus routines to show them, plus a couple of utility routines |
UMMParser |
UMMParser.hs |
parsers for records, commands, dates — all built on Parsec, very easy to understand (if I do say so myself) |
UMMPlot |
UMMPlot.hs |
code to very minimally exercise gnuplot — there are modules on hackage that do very nice graphing, but this is part of keeping this self-contained |
UMMEval |
UMMEval.hs |
the main routines for processing records |
UMMHelp |
UMMHelp.hs |
one long string which is the help message |
Overall, it's about 2300 lines of code. The parser is pretty much all standard Parsec, and the help file is basically just a large string constant; so, leaving those out, there's not a huge amount of code to dig into.
Version 0.1.0 unleashed upon an unsuspecting world on 2009-11-18... muahahaha! Look out, Intuit, here I come! :-)
Version 0.1.1 released on 2009-11-22: added support for UTF-8 encoded Unicode currency symbols, plus made the date optional in an account record.
Version 0.1.2 released on 2009-11-27: turned check number into a more-general ID (first user-contributed patch — thanks, Nicolas!), plus added an optional "initial price" to ccs records. There's also a contributed vim syntax-highlighting mode from Nicolas!
Version 0.1.3 released on 2009-11-29:
no changes in functionality, just made it -Wall-clean.
Version 0.1.4 released on 2009-12-05:
wrote basis command, partially-working export
command to write out data in ledger format, cleaned up
a bunch of hlint suggestions.
Version 0.1.5 released on 2009-12-19: added code to allow records to continue across multiple lines if desired, and allow split-transfer records: in one transaction, transfer multiple chunks of loot from one source account to multiple destinations.
Version 0.1.6 released on 2010-01-18: added the ability to specify an optional initial amount in an account, plus work on price translation: specify ccs as either basic or derived, and allow specification of the base ccs into which any given derived ccs is to be translated.
Version 0.1.7 released on 2010-03-20: fixed a small bug in the reporting of split xfer transactions: although the amounts were all always correct, the transaction wasn't always printed properly.
Version 0.2.0 released on 2010-05-02: added recurring transactions.
Version 0.2.1 released on 2010-05-09:
added significantly better date range specification, plus birthday
and anniversary records, plus a few small cleanups here &
there — for one, the reporting of split xfer transactions still
wasn't quite right.
Version 0.2.2 released on 2010-06-13:
register and reconcile
commands: now the program can show a combined register for a group of
accounts specified in the same way a group is specified for the
balance commandplot command,
so far only for price dataVersion 0.3.0 released on 2010-07-11:
plot command now works for accounts as well as price data
— see sample 1 or
sample 2 — and X axis is properly formatted
as time data.
If you have questions or comments about the stuff here, please contact me at korg@korgwal.com.
Enjoy! -- Uwe Hollerbach
Page last modified at 2010/7/11 17:30 US/Pacific