Working with Moneydance API
This page has notes on using Moneydance Ruby extension and introduces most important Moneydance methods. The documentation is pretty thin, but it tries to present things in the order that most people will use with special focus on the methods used to produce reports. While Moneydance API documentation contains minimal definitions of all classes and methods, this page concentrates on most common and useful methods and explains how to best use them in Ruby scripts.
Many of the Moneydance classes and methods can be accessed with just a few commands on the Ruby console (GUI version of IRB with few add-ons), and you can cut and paste many of the examples below to check out the methods in your environment. The methods below were chosen for their utility in writing reports. They will be useful in most other programming too, such as those that modify Moneydance data. Once you understand the methods here, many others will be simple to learn.
Running Ruby scripts
Once you have the Moneydance Ruby extension installed, you can run Ruby programs through two mechanisms, the Ruby console or from the command line. Console is great for testing your ideas and running one-liners, as well as generally learning your way around Moneydance classes and methods. You can load/execute Ruby source files from console too, using “Load file” button at the bottom. Any output or errors generated by loaded code is fed into the console, and you can continue your IRB session normally once the code is loaded.
You can also run your scripts inside Moneydance environment via special command line mode that this program supports.
Under Linux/Unix can run your Ruby scripts via command like this:
$ moneydance moneydance:fmodule:ruby:file?/path/to/script.rb
On OSX (assuming you installed Moneydance into /Applications), use:
$ /Applications/Moneydance.app/Contents/MacOS/JavaApplicationStub \
-invoke_and_quit moneydance:fmodule:ruby:file?/path/to/script.rb
That’s a mouthful, so you may want to create an alias in your profile.
Accessing Moneydance data from Ruby
All your Ruby code (either run in a console or as standalone scripts) can utilize following global constants giving you access to Moneydance API:
-
MD - Moneydance application context, you can access internal application aspects such as views, features, urls, account editors and so on.
-
ROOT - Moneydance Root Account, see explanation below.
-
TRANS - Moneydance Transaction Set, contains all transactions. Use it to get transactions for a specific account. One of the Moneydance quirks, transactions cannot be accessed directly from the account, you need to use TransactionSet’s methods for this.
You can call any API methods on Moneydance objects from Ruby. In addition to calling camel case methodLikeThis(), Moneydance Ruby allows you to call snake case method_like_this, with the same effect. You can also omit unnecessary parentheses. Java getters/setters can also be substituted for their Ruby equivalents, so instead of:
> acct.getAccountName()
> acct.setAccountName('Name')
you can just as easily use:
> acct.account_name
> acct.account_name = 'Name'
This helps you to keep your code more readable and Ruby-like. However, througout this tutorial I will only use full camelCaseMethodNames() for easier lookup in Moneydance API documentation.
Class RootAccount
RootAccount serves as a root of Moneydance account hierarchy, all other accounts are considered its subaccounts (either direct or through their parents, similar to directory structure). You can get access to RootAccount in your Ruby scripts using ROOT constant, or by calling accessor method of the app context object: MD.getRootAccount()
RootAccount-specific methods are not very useful for writing reports, the methods of the generic Account class are the useful ones. However, there are a couple:
- getHighestAccountNum()
- getAccountById(int id)
- getAccountByName(String name)
Accounts have ID ranging from 0 to getHighestAccountNum(). getAccountById() is mainly used in loops that scan all the accounts. getAccountByName() is defined in the generic Account class, it’s listed here because it is frequently accessed through the root account.
> ROOT.getHighestAccountNum()
112
> acct = ROOT.getAccountById(112)
'Zecco:Radium Arc Lamps'
> acct = ROOT.getAccountByName('Zecco')
-
getAddressBook() - Get Moneydance address book.
-
getBudgetList() - Get the set of budgets for this data file.
-
getBudgetName(int budgetNum, String defaultName) - Get the name of the budget that is identified by the given number.
-
getCurrencyTable() - Get Moneydance currency table.
-
getTransactionSet() - This provides access to the class TransactionSet from root account.
Class Account
Reports generally involve accounts or transactions, and transactions involve accounts, so the Account class is important. You can get access to any account in the Ruby console:
> acct = ROOT.getAccountByName('Paypal')
#<Java::ComMoneydanceAppsMdModel::BankAccount:0x13fe2493>
> acct.methods
[...] long list of methods that you can use on this account ;)
- getAccountName()
There are ways of getting accounts where you don’t know the name or you may not be certain about which characters are upper or lower case, this lets you get the exact name.
> acct.getAccountName()
'Paypal'
- getFullAccountName()
- getAllAccountNames()
- getDepth()
These are related and most useful when dealing with subaccounts.
> pg = ROOT.getAccountByName('personal:groceries')
> pg.getFullAccountName()
'Personal:Groceries'
> pg.getAllAccountNames().to_a
['Personal', 'Groceries']
> pg.getAllAccountNames()[0]
'Personal'
> pg.getAllAccountNames()[1]
'Groceries'
> pg.getDepth()
2
> acct.getDepth()
1
> ROOT.getDepth()
0
- getAccountNum()
Accounts can be accessed by a numeric ID. You won’t see this in the Moneydance GUI, but it is useful at times (say, for iteration).
> acct.getAccountNum()
1
- getBalance()
- getStartBalance()
Balances are kept as integers, scaled to the account’s currency or significant places of a share. Integer math avoids roundoff errors typical of floating point numbers with decimal fractions. For US bank accounts, a balance of 3541 means $35.41.
> acct.getBalance()
3541
> acct.getStartBalance()
0
Transaction and SplitTransaction
Transactions involve at least two accounts, the account you entered the transaction into and the account it referenced. If you used a split, then it will reference several accounts.
To get all the transactions associated with an account, you need to have the TransactionSet search for them. Consider this account named ‘Checking’ with a short register:
Date Chk Desc Account Payment Deposit Balance
1/01/2005 Dep Myco Bonus 1,000.00 1,000.00
1/03/2005 Bank Savings 100.00 900.00
1/07/2005 1 CG Groceries 20.00 880.00
1/15/2005 2 Sitgo -2 splits- 30.00 850.00
Split 1: Lunch Dining 10.00
Split 2: Gas Auto:Fuel 20.00
1/31/2005 ATM Cash Cash 100.00 750.00
You can work with it like this:
> acct = ROOT.getAccountByName('Checking')
> txns = TRANS.getTransactionsForAccount(acct).getAllTxns.to_a
> txns.each {|t| puts t}
[ParentTxn(12) desc=Cash; val=-10000; stat=; #splits=1; chk=ATM; ...
[ParentTxn(10) desc=Bank; val=-10000; stat=; #splits=1; chk=; ...
[ParentTxn(8) desc=CG; val=-2000; stat=; #splits=1; chk=1; ...
[ParentTxn(6) desc=Sitgo; val=-3000; stat=; #splits=2; chk=2; ...
[ParentTxn(3) desc=Myco; val=100000; stat=; #splits=1; chk=Dep; ...
The list of attributes displayed above isn’t complete and Moneydance doesn’t let you access them directly, e.g. txns[0].val won’t work. The attributes can be accessed only through accessor methods. Here are all the fields displayed above for the transactions with 2 splits with the method your script can use to get the attribute value or its close relative:
-
ParentTxn(6): txn.getTxnId()
-
desc=Sitgo; txn.getDescription()
-
val=-3000; txn.getValue()
-
stat= ; txn.getStatusChar(), txn.getStatus()
-
#splits=2; txn.getOtherTxnCount()
-
chk=2; txn.getCheckNumber(), txn.getCheckNumAsInt()
-
acct=Checking; txn.getAccount()
-
date=20050115; txn.getDateInt()
-
splits=[ SplitTxn(4), SplitTxn(5) ]
-
SplitTxn(4): sxn = txn.getOtherTxn(0), sxn.getTxnId()
- desc=Lunch; sxn.getDescription()
- val=1000; sxn.getValue()
- stat= ; sxn.getStatusChar(), sxn.getStatus()
- acct=Dining; sxn.getAccount()
- rate=1.0; sxn.getRate()
- amt=-1000; sxn.getAmount()
- val=1000; sxn.getValue()
-
SplitTxn(5) sxn = txn.getOtherTxn(1), sxn.getTxnId()
- desc=Gas;
- val=2000;
- stat= ;
- acct=Auto:Fuel;
- rate=1.0;
- amt=-2000;
- val=2000;
-
-
Please note, proper function to retrieve transaction date is getDateInt, not getDate as expected (see explanation of MD date conversion specifics below). Other useful methods include the following. Methods called on txn apply to the parent transaction (instance of ParentTxn) while sxn represents the first split transaction (instance of SplitTxn).
-
getMemo()
> txn.getMemo() 'Eat here and get gas.'
-
getOtherTxnCount() - From the parent transaction, this returns the number of splits. From the split SplitTxn, I think this always returns 1.
> txn.getOtherTxnCount() 2 > sxn.getOtherTxnCount() 1
-
getParentTxn() - From either the parent or split transaction, this return the parent transaction.
> txn.getParentTxn() [ParentTxn(6) desc=Sitgo; val=-3000; ... > sxn.getParentTxn() [ParentTxn(6) desc=Sitgo; val=-3000; ...
-
getTransferType() - This returns a string describing the type of the transfer between parent and split accounts. Transaction class (to be specific, it’s parent Class AbstractTxn) contains constants that describe such types.
> txn.getTransferType() 'xfrtp_bank' > sxn.getTransferType() 'xfrtp_bank' > txn.class::TRANSFER_TYPE_BANK 'xfrtp_bank'
Class InvestmentAccount
There isn’t much difference between banking accounts and investment accounts within Moneydance. For that matter, there isn’t much difference between foreign currency holdings and security holdings in that both are held in units with a value that fluctuates over time.
Suppose we have an investment account called Zecco, which has two stocks from these transactions:
Security: Itanium Mining
Date Dir Shares $/sh Value Balance
2005/04/01 Buy 10.0000 40.00 $400.00 10.0000
Security: Radium Arc Lamps
Date Dir Shares $/sh Value Balance
2005/03/14 Buy 100.0000 3.14 $314.00 100.0000
Commission: $0.15
If we look at these via Moneydance:
> acct = ROOT.getAccountByName('Zecco')
> acct.getBalance()
0
> txns = TRANS.getTransactionsForAccount(acct).getAllTxns.to_a
> txns.each {|t| puts t}
[ParentTxn(21) desc=Buy Shares; val=-40000; #splits=1; chk=; date=20050401; ...
[SplitTxn(18) desc=Zecco; val=40000; rate=1.0; amt=-40000; val=40000; ]
[SplitTxn(16) desc=Zecco; val=31415; rate=1.0; amt=-31415; val=31415; ]
[ParentTxn(15) desc=Buy Shares; val=-31415; #splits=2; chk=; date=20050314; ...
The last transaction has two splits, let’s take a closer look at it as we look at some important methods.
> txn = TRANS.getTxnByID(15)
> txn.getSplitCount()
2
> sxn0 = txn.getSplit(0)
> sxn1 = txn.getSplit(1)
-
getAccount() - Nothing new here, but note how the split transactions link to the various accounts:
> txn.getAccount().to_s 'Zecco' > sxn0.getAccount().to_s 'Zecco:Radium Arc Lamps' > sxn1.getAccount().to_s 'Investment:Trading Commission'
-
getValue() - While this is defined in the parent transaction, here it is the change in the amount of this account’s shares (or currency) in some fraction of a share (or currency unit). This is explained in the CurrencyType section below.
> txn.getValue() -31415 > sxn0.getValue() 1000000 > sxn1.getValue() 15
-
getAmount() - This is the amount of a split in the units of the parent transaction, in this case 1/100ths of a dollar (i.e. cents). Obviously, this method only works on split transactions:
> txn.getAmount() NoMethodError: undefined method `getAmount' for #<Java::ComMoneydanceAppsMdModel::ParentTxn:0x45a2f279> > sxn0.getAmount() -31400 > sxn1.getAmount() -15
-
getRate() - This is the value divided by the amount, and is used to convert between the two units.
> sxn0.getRate() 31.84713375796178 > sxn0.getValue() / sxn0.getAmount() -31.84713375796178 > sxn1.getRate() 1.0
Class CurrencyType
Moneydance tracks currencies and share values in CurrencyType classes. As implied above, these holding sizes are tracked as multiples of some fraction of a share. The fraction size, currency name, and many other attributes are tracked within this class.
You can get to an account’s currency type from the Ruby console:
> stock = ROOT.getAccountByName('Zecco:itanium mining')
> stock_currency = stock.getCurrencyType()
> stock_currency.to_s
'Zecco:Itanium Mining'
> checking = ROOT.getAccountByName('checking')
> checking_currency = checking.getCurrencyType()
> checking_currency.to_s
'US Dollar'
-
getName()
-
getIDString()
These return name/identifier of the security or currency.
> stock_currency.getName() 'Itanium Mining' > stock_currency.getIDString() '^itm' > checking_currency.getName() 'US Dollar' > checking_currency.getIDString() 'USD'
-
getDecimalPlaces() - This returns the number of decimal places in share amounts. By representing shares/currency as integers, moneydance avoids most roundoff errors.
> stock_currency.getDecimalPlaces() 4 > checking_currency.getDecimalPlaces() 2
Essentially this means that ‘Itanium Mining’ is tracked in 1/10000ths units of a share, while your checking account is tracked 1/100ths of a dollar (i.e. cents).
-
format(amount, decimalChar)
-
formatSemiFancy(amount, decimalChar)
-
formatFancy(amount, decimalChar)
These are convenience methods to print amounts in user-oriented formats. Amount is in fractions of a share or the smallest unit of the currency. DecimalChar is a decimal character ASCII CODE - use ?. to print American-style numbers and ?, to print European.
> stock_currency.format(123456789, ?.) '12345.6789' > stock_currency.formatSemiFancy(123456789, ?.) '12,345.6789' > stock_currency.formatFancy(123456789, ?.) '12.345,6789 itm Shares' > checking_currency.formatFancy(123456789L, ?.) '$ 1,234,567.89'
-
getRawRate()
This returns the current conversion rate to convert some amount in the base currency to the number of shares. The number of shares divided by the rate is the value of the shares. For example, the itm conversion rate is 4.0, so one share is worth 10,000/4.0 = 2,500 cents, $25.00.
> stock_currency.getRawRate() 4.0 > checking_currency.getRawRate() 1.0
-
getRawRateByDateInt()
The CurrencyType class includes any value history entered for a security or foreign currency. For ITM, the history has these date, value pairs:
04/01/2010 $40.00 09/01/2010 $30.00 10/01/2010 $25.00
Moneydance date values are tracked as integers in a form of YYYYMMDD (see explanation of MD date conversions below).
> stock_currency.getRawRateByDateInt(20101001) 3.333333333333333 > stock_currency.getRawRateByDateInt(20101002) 4.0
Moneydance Date/Time conversion
Moneydance date values may be accessed in two ways. Getters such as Txn#getDate, CurrencyType.getRawRateByDate return a number of milliseconds since start of Unix era. You can convert it into Ruby Time like this:
> md_date = txns.last.getDate
1308124800000
> ruby_date = Time.at md_date/1000 # Time.at needs seconds, not millis
Wed Jun 15 12:00:00 +0400 2011
In fact, this way of getting/setting Moneydance time is considered deprecated. Recommended way of getting/setting time is through use of accessors such as Txn#getDateInt, CurrencyType.getRawRateByDateInt. These return time as integers in a form of YYYYMMDD. So, integer 20101001 is equivalent to 10/01/2010.
> txns.last.getDateInt
20110615
This integer date representation works great when all you need is to see the date. It is definitely much easier to interpret when obscure number of Unix-time millis. However, when you’re manipulating currencies programmatically, you often need to convert between Ruby Date/Time and Monedance date representation. Here is an example of how to convert Ruby time into Moneydance format:
> ruby_date = Time.gm 2010, 12, 25
Sat Dec 25 00:00:00 UTC 2010
> md_date_int = ruby_date.strftime("%Y%m%d").to_i
20101225
> stock_currency.getRawRateByDateInt(md_date_int)
4.0
Here is an example of reverse conversion, from Moneydance date format to Ruby Time:
> md_date_int = 20101225
20101225
> ruby_date = Time.gm(*md_date_int.to_s.match(/(....)(..)(..)/).captures)
Sat Dec 25 00:00:00 UTC 2010
Credits
This document is an extension of an excellent Jython Primer written by Ric Werme, as relevant to Moneydance Ruby.