// CategoryReport package org.javamoney.examples.ez.money.report; import static org.javamoney.examples.ez.money.ApplicationProperties.UI_CURRENCY_SYMBOL; import static org.javamoney.examples.ez.money.KeywordKeys.NOT_CATEGORIZED; import static org.javamoney.examples.ez.money.importexport.QIFConstants.CATEGORY_SEPARATOR; 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.model.dynamic.total.CategoryTotalTypeKeys.EXPENSE; import static org.javamoney.examples.ez.money.model.dynamic.total.CategoryTotalTypeKeys.INCOME; import static org.javamoney.examples.ez.money.model.dynamic.total.IncomeExpenseTotal.getExpenseTotal; import static org.javamoney.examples.ez.money.model.dynamic.total.IncomeExpenseTotal.getIncomeTotal; import static org.javamoney.examples.ez.money.model.dynamic.total.IncomeExpenseTotal.setExpenseTotal; import static org.javamoney.examples.ez.money.model.dynamic.total.IncomeExpenseTotal.setIncomeTotal; import static org.javamoney.examples.ez.money.utility.TransactionDateHelper.isInRange; import static org.javamoney.examples.ez.money.utility.TransactionHelper.isIncome; import static org.javamoney.examples.ez.money.utility.TransactionHelper.isSplit; import static org.javamoney.examples.ez.money.utility.TransactionHelper.isTransfer; import java.util.Collection; import java.util.Date; import java.util.LinkedList; import java.util.StringTokenizer; import org.javamoney.examples.ez.money.model.DataElement; import org.javamoney.examples.ez.money.model.dynamic.total.CategoryTotalTypeKeys; import org.javamoney.examples.ez.money.model.dynamic.total.IncomeExpenseTotal; import org.javamoney.examples.ez.money.model.dynamic.total.TotalFilter; import org.javamoney.examples.ez.money.model.dynamic.total.TransactionDetail; import org.javamoney.examples.ez.money.model.dynamic.total.TransferTotal; 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.transaction.Transaction; import org.javamoney.moneta.Money; /** * This class facilitates maintaining data pertaining to a category report. */ public final class CategoryReport extends Report { /** * This method creates a category report according to the specified * criteria. * <p> * <b>Note:</b> The report is not sorted according to the sorting preference * selected in the category report dialog. It is up to the classes that * utilize the report to sort it accordingly. * * @param start * The report's start date. * @param end * The report's end date. * @param filter * The filter to use. * * @return A category report. */ public static CategoryReport createReport(Date start, Date end, TotalFilter filter) { CategoryReport report = new CategoryReport(); report.setEndDate(end); report.setExpenseGroups(new LinkedList<IncomeExpenseTotal>()); report.setExpenses(new LinkedList<IncomeExpenseTotal>()); report.setFilter(filter); report.setIncome(new LinkedList<IncomeExpenseTotal>()); report.setIncomeGroups(new LinkedList<IncomeExpenseTotal>()); report.setStartDate(start); report.setTransfers(new LinkedList<TransferTotal>()); // Clear previous totals. setExpenseTotal(0.0); setIncomeTotal(0.0); // Iterate all the accounts and calculate the totals. for (DataElement element : getAccounts().getCollection()) { Account account = (Account) element; TransferTotal transferTotal = new TransferTotal(account); if (filter.allowsAccount(account) == false) { continue; } // Iterate all transactions and calculate the totals. for (Transaction trans : account.getTransactions()) { if (isInRange(trans, start, end) == true) { if (filter.allowsPayee(trans) == true && filter.allowsReconciledStatus(trans) == true) { if (isTransfer(trans) == false) { if (isSplit(trans) == true) { report.addSplit(trans, account); } else { report.addCategory(trans, account); } } else { report.addTransfer(transferTotal, trans); } } } } report.getTransfers().add(transferTotal); } return report; } /** * This method returns the report's expense groups. * * @return The report's expense groups. */ public Collection<IncomeExpenseTotal> getExpenseGroups() { return itsExpenseGroups; } /** * This method returns the report's expense categories. * * @return The report's expense categories. */ public Collection<IncomeExpenseTotal> getExpenses() { return itsExpenses; } /** * This method returns the report's income categories. * * @return The report's income categories. */ public Collection<IncomeExpenseTotal> getIncome() { return itsIncome; } /** * This method returns the report's income groups. * * @return The report's income groups. */ public Collection<IncomeExpenseTotal> getIncomeGroups() { return itsIncomeGroups; } /** * This method returns the report's transfer totals. * * @return The report's transfer totals. */ public Collection<TransferTotal> getTransfers() { return itsTransfers; } // //////////////////////////////////////////////////////////////////////////// // Start of private methods. // //////////////////////////////////////////////////////////////////////////// private void addCategory(Transaction trans, Account account) { if (getFilter().allowsCategory(trans) == true) { if (isIncome(trans) == true) { addCategoryTotalToGroup(getIncomeGroups(), INCOME, trans, account); addCategoryTotalToSet(getIncome(), INCOME, trans, account); setIncomeTotal(getIncomeTotal() + trans.getAmount().getNumber().doubleValue()); } else { addCategoryTotalToGroup(getExpenseGroups(), EXPENSE, trans, account); addCategoryTotalToSet(getExpenses(), EXPENSE, trans, account); setExpenseTotal(getExpenseTotal() - trans.getAmount().getNumber().doubleValue()); } } } private void addCategoryTotalToGroup(Collection<IncomeExpenseTotal> group, CategoryTotalTypeKeys key, Transaction trans, Account account) { String qif = trans.getCategory(); int index = qif.lastIndexOf(CATEGORY_SEPARATOR_CHAR); if (qif.length() != 0 && index != -1) { StringTokenizer tokens = null; IncomeExpenseTotal total = null; double amount = trans.getAmount().getNumber().doubleValue(); // Get the group name. qif = qif.substring(0, index); tokens = new StringTokenizer(qif, CATEGORY_SEPARATOR); qif = ""; while (tokens.hasMoreTokens() == true) { qif += tokens.nextToken(); total = getCategoryTotal(group, qif); if (total == null) { total = new IncomeExpenseTotal(key, qif); group.add(total); } total.setAmount(total.getAmount() + amount); total.getTransactionDetails().add( new TransactionDetail(trans, account)); qif += CATEGORY_SEPARATOR; } } } private void addCategoryTotalToSet( Collection<IncomeExpenseTotal> collection, CategoryTotalTypeKeys type, Transaction trans, Account account) { IncomeExpenseTotal total = null; String qif = trans.getCategory(); double amount = trans.getAmount().getNumber().doubleValue(); // If the transaction is not categorized, then show it as so. if (qif.length() == 0) { qif = NOT_CATEGORIZED.toString(); } total = getCategoryTotal(collection, qif); if (total == null) { total = new IncomeExpenseTotal(type, qif); collection.add(total); } total.setAmount(total.getAmount() + amount); total.getTransactionDetails() .add(new TransactionDetail(trans, account)); } private void addSplit(Transaction trans, Account account) { Split split = new Split(trans); for (int len = 0; len < split.size(); ++len) { trans = trans.clone(); trans.setAmount(Money.of(split.getAmount(len), UI_CURRENCY_SYMBOL.getCurrency())); trans.setCategory(split.getCategory(len)); addCategory(trans, account); } } private void addTransfer(TransferTotal total, Transaction trans) { if (isIncome(trans) == true) { total.setToTotal(total.getToTotal() + trans.getAmount().getNumber().doubleValue()); } else { total.setFromTotal(total.getFromTotal() + Math.abs(trans.getAmount().getNumber().doubleValue())); } total.getTransactionDetails().add( new TransactionDetail(trans, total.getAccount())); } private IncomeExpenseTotal getCategoryTotal( Collection<IncomeExpenseTotal> collection, String identifier) { IncomeExpenseTotal totalAt = null; for (IncomeExpenseTotal total : collection) { if (total.getIdentifier().equals(identifier) == true) { totalAt = total; break; } } return totalAt; } private void setExpenseGroups(Collection<IncomeExpenseTotal> collection) { itsExpenseGroups = collection; } private void setExpenses(Collection<IncomeExpenseTotal> collection) { itsExpenses = collection; } private void setIncome(Collection<IncomeExpenseTotal> collection) { itsIncome = collection; } private void setIncomeGroups(Collection<IncomeExpenseTotal> collection) { itsIncomeGroups = collection; } private void setTransfers(Collection<TransferTotal> collection) { itsTransfers = collection; } // //////////////////////////////////////////////////////////////////////////// // Start of class members. // //////////////////////////////////////////////////////////////////////////// private Collection<IncomeExpenseTotal> itsExpenseGroups; private Collection<IncomeExpenseTotal> itsExpenses; private Collection<IncomeExpenseTotal> itsIncome; private Collection<IncomeExpenseTotal> itsIncomeGroups; private Collection<TransferTotal> itsTransfers; }