// TransactionHelper
package org.javamoney.examples.ez.money.utility;
import static org.javamoney.examples.ez.money.KeywordKeys.TRANSFER_FROM;
import static org.javamoney.examples.ez.money.KeywordKeys.TRANSFER_TO;
import static org.javamoney.examples.ez.money.importexport.QIFConstants.CATEGORY_SEPARATOR_CHAR;
import static org.javamoney.examples.ez.money.model.DataManager.getAccounts;
import static org.javamoney.examples.ez.money.utility.DialogHelper.error;
import javax.money.MonetaryAmount;
import org.javamoney.examples.ez.money.locale.CurrencyFormat;
import org.javamoney.examples.ez.money.locale.CurrencyFormatKeys;
import org.javamoney.examples.ez.money.model.DataElement;
import org.javamoney.examples.ez.money.model.dynamic.transaction.Split;
import org.javamoney.examples.ez.money.model.persisted.account.Account;
import org.javamoney.examples.ez.money.model.persisted.account.AccountCollection;
import org.javamoney.examples.ez.money.model.persisted.transaction.Transaction;
import org.javamoney.examples.ez.common.utility.I18NHelper;
/**
* This class provides convenience methods for dealing with transactions. All
* methods in this class are static.
*/
public
final
class
TransactionHelper
{
/**
* This method creates a new transfer transaction that will correspond to the
* passed in transaction and will have the specified payee.
*
* @param trans The transaction the transfer will correspond to.
* @param payee The account that either received or sent the transfer.
*
* @return A transfer transaction tailored for the recipient account.
*/
public
static
Transaction
createCorrespondingTransfer(Transaction trans, Account payee)
{
String category = null;
if(trans.getCategory().equals(TRANSFER_TO.toString()) == true)
{
category = TRANSFER_FROM.toString();
}
else
{
category = TRANSFER_TO.toString();
}
trans = trans.clone();
trans.setAmount(trans.getAmount().negate());
trans.setCategory(category);
trans.setPayee(payee.getIdentifier());
return trans;
}
/**
* This method returns the reference to the transaction that will correspond
* to the passed in transaction and will have the specified payee, or null if
* the passed in transaction is not a transfer, or the payee no longer exists.
*
* @param trans The transaction to create a recipient transfer with.
* @param payee The account that either received or sent the transfer.
*
* @return The reference to the transaction that corresponds to the passed in
* transaction
*/
public
static
Transaction
getCorrespondingTransferReference(Transaction trans, Account payee)
{
Transaction transAt = null;
if(isTransfer(trans) == true)
{
Account account = (Account)getAccounts().get(trans.getPayee());
if(account != null)
{
trans = createCorrespondingTransfer(trans, payee);
// Iterate all transactions until a match is found.
for(Transaction nextTrans : account.getTransactions())
{
if(nextTrans.compareTo(trans) == 0)
{
transAt = nextTrans;
break;
}
}
}
}
return transAt;
}
/**
* This method returns true if the amount is considered an expense, otherwise
* false.
*
* @param amount The amount to check.
*
* @return true or false.
*/
public
static
boolean
isExpense(double amount)
{
return amount <= 0.0;
}
/**
* This method returns true if the amount is considered an expense, otherwise
* false.
*
* @param amount The amount to check.
*
* @return true or false.
*/
public
static
boolean
isExpense(MonetaryAmount amount) {
return isExpense(amount.getNumber().doubleValue());
}
/**
* This method returns true if the transaction is considered an expense,
* otherwise false.
*
* @param trans The transaction to check.
*
* @return true or false.
*/
public
static
boolean
isExpense(Transaction trans)
{
return isExpense(trans.getAmount());
}
/**
* This method returns true if the amount is considered an income, otherwise
* false.
*
* @param amount The amount to check.
*
* @return true or false.
*/
public
static
boolean
isIncome(double amount)
{
return isExpense(amount) == false;
}
/**
* This method returns true if the amount is considered an income, otherwise
* false.
*
* @param amount The amount to check.
*
* @return true or false.
*/
public
static
boolean
isIncome(MonetaryAmount amount)
{
return isIncome(amount.getNumber().doubleValue());
}
/**
* This method returns true if the transaction is considered an income,
* otherwise false.
*
* @param trans The transaction to check.
*
* @return true or false.
*/
public
static
boolean
isIncome(Transaction trans)
{
return isIncome(trans.getAmount());
}
/**
* This method returns true if the category is a split, otherwise false.
*
* @param category The category to check.
*
* @return true or false.
*/
public
static
boolean
isSplit(String category)
{
return category.length() != 0 && category.charAt(0) == Split.ITEM_SEPARATOR_CHAR;
}
/**
* This method returns true if the transaction has a split, otherwise false.
*
* @param trans The transaction to check.
*
* @return true or false.
*/
public
static
boolean
isSplit(Transaction trans)
{
return isSplit(trans.getCategory());
}
/**
* This method returns true if the category is a transfer, otherwise false.
*
* @param category The category to check.
*
* @return true or false.
*/
public
static
boolean
isTransfer(String category)
{
boolean result = false;
// All transactions that are transfers have their category start with a '['.
if(category.length() != 0 && category.charAt(0) == '[')
{
result = true;
}
return result;
}
/**
* This method returns true if the transaction is a transfer, otherwise false.
*
* @param trans The transaction to check.
*
* @return true or false.
*/
public
static
boolean
isTransfer(Transaction trans)
{
return isTransfer(trans.getCategory());
}
/**
* This method iterates over all the transactions for all the accounts,
* replacing the old value with the new value when it occurs in the specified
* field.
*
* @param key The field to mass update.
* @param oldValue The value to look for.
* @param newValue The value to change the old one with.
*/
public
static
void
massUpdate(MassUpdateFieldKeys key, String oldValue, String newValue)
{
AccountCollection collection = getAccounts();
// Iterate all accounts.
for(DataElement account : collection.getCollection())
{
massUpdateFor((Account)account, key, oldValue, newValue);
}
}
/**
* This method removes the transaction from its account. If the transaction is
* a transfer, then the corresponding transaction is also removed.
*
* @param trans The transaction to remove.
*/
public
static
void
removeFrom(Account account, Transaction trans)
{
if(account.removeTransaction(trans) == true)
{
// Apply to transfer if applicable.
if(isTransfer(trans) == true)
{
Account recipient = (Account)getAccounts().get(trans.getPayee());
if(recipient != null)
{
recipient.removeTransaction(createCorrespondingTransfer(trans, account));
}
}
}
else
{
error(getProperty("remove.title"),
getProperty("remove.description") + " \"" + account.getIdentifier() + "\".");
}
}
//////////////////////////////////////////////////////////////////////////////
// Start of private methods.
//////////////////////////////////////////////////////////////////////////////
private
static
String
getProperty(String key)
{
return I18NHelper.getProperty("TransactionHelper." + key);
}
/*
* This method iterates over all the transactions for the specified account,
* replacing the old value with the new value when it occurs in the specified
* field.
*/
private
static
void
massUpdateFor(Account account, MassUpdateFieldKeys key, String oldValue, String newValue)
{
for(Transaction trans : account.getTransactions())
{
if(key == MassUpdateFieldKeys.ACCOUNT && isTransfer(trans) == true)
{
// For transfers, the payee is the account.
if(trans.getPayee().equals(oldValue) == true)
{
trans.setPayee(newValue);
}
}
else if(key == MassUpdateFieldKeys.PAYEE && isTransfer(trans) == false)
{
if(trans.getPayee().equals(oldValue) == true)
{
trans.setPayee(newValue);
}
}
else if(key == MassUpdateFieldKeys.EXPENSE && isExpense(trans) == true)
{
if(isSplit(trans) == true)
{
updateSplit(trans, oldValue, newValue);
}
else
{
String category = updateCategory(trans.getCategory(), oldValue, newValue);
trans.setCategory(category);
}
}
else if(key == MassUpdateFieldKeys.INCOME && isIncome(trans) == true)
{
if(isSplit(trans) == true)
{
updateSplit(trans, oldValue, newValue);
}
else
{
String category = updateCategory(trans.getCategory(), oldValue, newValue);
trans.setCategory(category);
}
}
}
}
/*
* This method updates the specified category if it equals the old value or
* the old value is apart of the category's group. If neither applies, then
* the category is returned unchanged. 1 of 2 things needs to be true to
* update the category. Either the category equals the old value, or the
* category starts with the old value including the category separator.
*/
private
static
String
updateCategory(String category, String oldValue, String newValue)
{
if(category.startsWith(oldValue) == true)
{
int len = oldValue.length();
char separator = CATEGORY_SEPARATOR_CHAR;
// Make sure they match or the next character is the category separator.
if(category.length() == len || category.charAt(len) == separator)
{
if(newValue.length() == 0)
{
category = newValue;
}
else
{
category = newValue + category.substring(len);
}
}
}
return category;
}
private
static
void
updateSplit(Transaction trans, String oldSplit, String newSplit)
{
Split split = new Split(trans);
CurrencyFormat dollar = CurrencyFormatKeys.US_DOLLAR.getFormat();
String category = "";
for(int index = 0; index < split.size(); ++index)
{
category += Split.ITEM_SEPARATOR + updateCategory(split.getCategory(index),
oldSplit, newSplit);
// Amounts in splits are always positive.
category += Split.AMOUNT_SEPARATOR + dollar.format(Math.abs(split.getAmount(index)));
}
trans.setCategory(category);
}
//////////////////////////////////////////////////////////////////////////////
// Start of inner classes.
//////////////////////////////////////////////////////////////////////////////
/**
* This enumerated class provides keys for transaction fields that are mass
* updatable.
*/
public
enum
MassUpdateFieldKeys
{
/**
* The transaction's field that contains the account.
*/
ACCOUNT,
/**
* The transaction's field that contains the amount that is below zero
* inclusive.
*/
EXPENSE,
/**
* The transaction's field that contains the amount that is above zero
* exclusive.
*/
INCOME,
/**
* The transaction's field that contains the payee that is not a transfer.
*/
PAYEE;
}
}