MoneyJar
 

Example usage

This document shows example usage of the MoneyJar library.

Examples

The following examples show some of the concepts of using the library. There are more ways to find out more about the usage of the library. One is to investigate the actual sources. An initial better way is to look at the unit tests and see what is being done there for taxes, rounding, etc.

FinanceFactory

The FinanceFactory is the class where money and percentages are created. The reason for not allowing developers to instantiate their own have to do with default settings in the library and being able to configure the library for specific needs.

The MoneyJar library is initialized by the "moneyjar.xml" file, which should be in the current directory where the JVM is executed from. The default "moneyjar.xml" configuration file path can be overriden by specifying a system property on the command line.

-Dmoneyjar.config

The configuration file in XML for the MoneyJar library looks internally as follows:

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE moneyjar SYSTEM "moneyjar.dtd">
<moneyjar>
    <factory roundingrule="HALF_UP" precision="18" currency="EUR"/>
    <taxmodel id="1" description="Regular VAT"
    	<taxrule id="1" description="VAT Applied" decimals="2" roundingrule="HALF_UP">
    	    <taxcode id="1" order="1" start="20060101" end="20061212" 
    	    	roundingrule="HALF_UP" decimals="2" taxontax="false" percent="18"/>
    	</taxrule>
    </taxmodel>
</moneyjar>

Many countries use "HALF_UP" as the rounding policy for monetary calculations. However, some tax regulations in the U.S. and probably other countries have been known to use "Banker's rounding", which is "HALF_EVEN". "HALF_UP" rounding always rounds 0.5 and higher upwards. "HALF_EVEN" rounding rounds 0.5 towards the nearest even number, which can effectively result in either "HALF_DOWN" or "HALF_UP".

So, to use different configuration files for this library on the same system, you could store a different 'moneyjar.xml' in each subdirectory where the application is started from, or you can set up a generic configuration directory with different filenames for each application. You would start the finance library differently in this case using:

java -Dmoneyjar.config=/path/to/config/pool/myfinanceapp.xml my.finance.app.Main

The Money class

The Money class is the class that stores the actual money. Besides the "amount", money is also described by the currency it is in, the number of decimals required for its display and used when rounding, the formatting string when displaying the money, the total number of digits used when calculating (precision) and the rounding method to use when rounding.

To use the library, you'll need to use the FinanceFactory class to create money. The reason for a factory is that the factory maintains data that makes your money calculations using the Money class consistent throughout the application (configuration of rounding type, precision, default currency, etc. )

Assuming that the FinanceFactory has been configured, you'll typically create money using one of the "createMoney" calls inside that class ( see JavaDocs ). This works as follows:

Money money = FinanceFactory.createMoney();

The above call would have created money for the default amount of 0.00, assigned the default configured currency, the default configured precision with the default configured rounding policy. After the Money class is returned, it is ready for use elsewhere.

To create money of a different amount, one would issue (notice the use of dot, not comma):

Money money = FinanceFactory.createMoney( "123.53" );

And to create money of a different amount in a different currency:

Money money = FinanceFactory.createMoney( "152.05", "EUR" );

To add one money amount to another (only money of the same currency can be added):

Money money3 = money1.add( money2 );

To subtract money2 from money1 (again same currency):

Money money3 = money1.subtract( money2 );

To get a percentual amount of money from another:

Money money2 = money1.applyPercent( percent );

These are just a couple of methods that show a little bit of the usage of the Money class. Have a look at the JavaDocs for a more complete overview.

The Percent class

The Percent class is used to represent a percentual amount in the library. It is also tied internally to the BigDecimal representation. The Percent should be represented as you would write it on paper, i.e. 5% would equal "5" or "5.00" if you like.

Assuming that the FinanceFactory has been configured, you'll typically create percentages using one of the "createPercent" calls inside that class ( see JavaDocs ). This works as follows:

Percent percent = FinanceFactory.createPercent( "5.00" );

The above call would have created a percentual amount of 5.00, assigned the default configured precision with the default configured rounding policy. After the Percent class is returned, it is ready for use elsewhere. Either in tax calculations or as a means to calculate a percentage of a certain money amount.

The Tax class and model

The usage of the tax model and classes is a bit more elaborate. The library supports a simple, single tax on top of a price, but one can also add more "taxcodes" to a tax line, which will calculate more taxes on top of the price, or on top of the tax calculated so far.

The "TaxModel" class is a Tax class holder. This is given an ID for later lookup. Having different instances of the TaxModel is useful for segmenting your different customers. For example, certain institutions are tax-exempt, they'd be assigned a model "exempt", while private companies may be assigned the general "VAT" tax model.

The "TaxModel" class is also assigned a "TaxStore" class. This is a class that you provide. It is sort of a 'lookup' class that the library uses in order to be supplied with the taxes applicable to a certain tax model, a certain billing ID and a certain date (the date of purchase).

The "DefaultTaxStore" class in the unit tests may serve as an example of such a class. In this case, the taxes are stored directly inside the class file, hardcoded. It would not be too complicated to query an external file or a database instead with the given date, model id and billing id.

Every "Tax" object is not the 'percentual' tax that is calculated for the particular invoice ID. The "Tax" is another holder for a number of tax codes. The tax codes are essentially the percentages to be applied to either the price or the previously calculated tax so far. The tax amounts calculated in each step are added to a final tax amount.

Every "Tax" object needs to be assigned a model ID, a tax id (which is matched with the billing ID in the invoice line), a number for rounding after all taxes are calculated and summed and a name.

Also, every Tax object will need to be assigned a tax code. This is a percentual amount calculated over the price or the tax amount so far. It is possible to configure for each tax code what the rounding policy is after that particular tax is calculated, how many decimals to use in this rounding or to specify that no rounding is used at all ( -1 for the rounding decimals ).

The tax code also contains two dates, which are the limits of its validity.

Example scenario:

      	TaxModel model = new TaxModel( "Default Tax Model" );
	model.setId( 1 );
	model.setTaxStore( new DefaultTaxStore() );
	Tax taxRule = model.getTax( 4, System.currentTimeMillis() );
	Money tax = taxRule.calculate( money, false );
      

When the taxRule calculates the tax, the boolean specifies whether the tax is to be considered included in the price or should be calculated on top of the price.

A tax store object (that supplies the Tax and TaxCode objects ) might look like this:

      	Tax tax = new Tax( 1, 1, (short)2, "HALF_UP", "Regular tax 1" );
	tax.addTaxCode( new TaxCode( 
		1L,
		(short)1,
		new Date( 0L ),
		null,
		"HALF_UP",
		2,
		false,
		FinanceFactory.createPercent( "18.5" ) ) );
	taxes.put( new TaxPK( tax.getModelId(), tax.getInternalBillingId()), tax );
	
	public Tax getTax( long modelId, long internalBillingID, long taxTimestamp ) {
		TaxPK pk = new TaxPK( modelId, internalBillingID );
		
		if (! taxes.containsKey( pk )) {
			return null;
		}
		return (Tax)taxes.get( pk );
	}
	

When every tax code is added to the tax, it is also given a specific 'order' in which that tax code needs to be applied. That order is very important in the case of tax-on-tax calculations.

Invoice calculations

The invoices can be calculated without delving too much into rounding, tax models or Money class usage at all. If you extend the "AbstractInvoice" and "AbstractInvoiceLine" classes a bit, you'll probably be able to wrap up a very short implementation of your invoices that is going to look very easy for other people to understand.

Typical usage of the SimpleInvoice class is to instantiate it, assign it an invoice number, assign it a tax model (probably depends on type of customer) and then add the invoice lines to it. Each invoice line has an internal billing id, that will be matched against a tax id (which needs to exist). This will automatically calculate applicable tax for each line item, which will be summed up in the end in separate so-called "Tax buckets". Tax buckets aggregate prices together that have the same tax rules applied to them. The subtotals of the buckets at the end of the invoice are then used to calculate the tax on.

The final sum on the invoice is determined by summing up all the prices for the invoice line items, then summing up the totals in the tax buckets and then sum those totals together. (Summing up tax / price per line item is incorrect from the perspective of rounding and legally, from the perspective of tax calculation itself. Tax is normally calculated on the sum of the net of the invoice. If different tax rules apply to a particular line item, then that needs to be separately bucketed in another subtotal).

Typical usage of the library for an invoice is as follows. Note that the implementation of "DefaultTaxStore" is not shown here. That implementation needs to gather the tax data from an external storage like a file or database. The "SimpleInvoice" is not directly usable by any application because it lacks too much information (customer address, logo, etc.). But if you extend on the "AbstractInvoice", you should be able to come up with a very easy-to-understand class in your own application.

	TaxModel model = new TaxModel( "Default Tax Model" );
	model.setId( 1 );
	model.setTaxStore( new DefaultTaxStore() );

	SimpleInvoice invoice = new SimpleInvoice( "00001", model );
	invoice.addInvoiceLine( 
		new SimpleInvoiceLine(
			"High Tariff Product", 
			DEFAULT_HIGH_TARIFF_PRICE,
			1,
			System.currentTimeMillis(),
			false ) );
	invoice.addInvoiceLine( 
		new SimpleInvoiceLine(
			"A Low Tariff Product", 
			DEFAULT_LOW_TARIFF_PRICE,
			2,
			System.currentTimeMillis(),
			false ) );		

	invoice.toDebugString();
	

In this case, the 'invoice.toDebugString' call already shows a dump of the invoice line items added to the invoice, the tax buckets and the invoice total that was calculated.

Financial Calendar

The financial calendar helps in determining chargeable periods, weekdays, businessdays, notification days, etc.

For example, if you sell 'subscriptions' by the year, then there can be a lot of complexity involved with determining the rate, especially if you allow pro-rata charges to be applied. Without pro-rata, then for a monthly subscription, if you use one day of the period, the full monthly rate is charged.

If you use pro-rata however and you have a synchronizable billing cycle, then the system should normally attempt to calculate, pro-rata, the charge up to the first synchronizable billing date, then start charging for whole billing periods after that.

A really helpful framework for doing this does not yet exist, but the initial structure for determining chargeable periods does. For example, suppose that the 'billing period' is from 01/01/2006 to 01/04/2006 (so 3-monthly billing cycle), and a subscription is charged pro-rata, starting at 03/02/2006. You'd typically use MoneyJar as follows (Java months start at 0, not 1):

GregorianCalendar calSubscriptionStart = (GregorianCalendar)Calendar.getInstance();
GregorianCalendar calEndBillingCycle = (GregorianCalendar)Calendar.getInstance();
GregorianCalendar calbillingPeriod = (GregorianCalendar)Calendar.getInstance();
calSubscriptionStart.set( 2006, 1, 3, 00, 00, 00 );
calEndBillingCycle.set( 2006, 3, 1, 00, 00, 00 );
calbillingPeriod.set( 2006, 0, 1, 00, 00, 00 );

BigDecimal elapsed = FinancialCalendar.getElapsed(
	calSubscriptionStart.getTime(),
	calEndBillingCycle.getTime(),
	calbillingPeriod.getTime(),
	3,
	Calendar.MONTH );
   	

The 'elapsed' in the above example uses the correct days to calculate the pro-rata fraction (that is actually the hardest part of the whole problem). It is also possible that a subscription is only valid up to a certain time. In the above example, especially when issuing "termination bills", it is possible that the system needs to calculate the charges for an incomplete subscription cycle at the end. This can be done the same way as with the start date as follows:

GregorianCalendar calSubscriptionStart = (GregorianCalendar)Calendar.getInstance();
GregorianCalendar calSubscriptionEnd = (GregorianCalendar)Calendar.getInstance();
GregorianCalendar calbillingPeriod = (GregorianCalendar)Calendar.getInstance();
calSubscriptionStart.set( 2006, 1, 3, 00, 00, 00 );
calSubscriptionEnd.set( 2006, 4, 22, 00, 00, 00 );
calbillingPeriod.set( 2006, 0, 1, 00, 00, 00 );

BigDecimal elapsed = FinancialCalendar.getElapsed(
	calSubscriptionStart.getTime(),
	calSubscriptionEnd.getTime(),
	calbillingPeriod.getTime(),
	3,
	Calendar.MONTH );
   	

In the above example, the charges applied are:

   	2006/01/01 up to 2006/02/03 : no charges
   	2006/02/03 up to 2006/04/01 : pro-rata subscription rate.
   	2006/04/01 up to 2006/05/22 : pro-rata subscription rate.
   	

It is important to understand in the above that, if the subscription billing cycle would simply start at 2006/02/03 and then in whole billing periods after that, the pro-rata charges itself would likely be very different, because the number of days in a particular 3-monthly billing period are different. This is why it is important to choose a date from where the billing periods start (2006/01/01), which is normally a date after which a particular billing run is executed.