/* This file is part of Cyclos (www.cyclos.org). A project of the Social Trade Organisation (www.socialtrade.org). Cyclos is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Cyclos is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Cyclos; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package nl.strohalm.cyclos.services.stats.finances; import java.util.Collection; import nl.strohalm.cyclos.dao.accounts.transactions.TransferDAO; import nl.strohalm.cyclos.entities.accounts.SystemAccountType; import nl.strohalm.cyclos.entities.accounts.transactions.PaymentFilter; import nl.strohalm.cyclos.entities.accounts.transactions.TransferType; import nl.strohalm.cyclos.entities.reports.StatisticalNumber; import nl.strohalm.cyclos.utils.NamedPeriod; import nl.strohalm.cyclos.utils.Period; /** * Takes care of the calculations for finance statistics for a single period. * * @author Rinke * */ public class FinanceStatsSinglePeriod extends FinanceStats { private enum FlowType { INCOME, EXPENDITURE; } private Period period; private SystemAccountType systemAccountFilter; /** * constructor receiving the transfer dao */ public FinanceStatsSinglePeriod(final TransferDAO transferDao) { super(transferDao); } /** * gives the resulting table data for expenditure single period. It first checks if the period, paymentFilters and systemAccountType are the same, * and if so, it will return a possibly already available result, rather than calculating it anew. * * <code>TransferType</code>s * @return a Number[][] containing the table data for overview single period. */ public Number[][] getTableCellsExpenditureSinglePeriod(final NamedPeriod aPeriod, final Collection<PaymentFilter> aPaymentFilters, final SystemAccountType aSystemAccountFilter) { if (!hasEqualParameters(aPeriod, aPaymentFilters, aSystemAccountFilter) || tableCells == null) { tableCells = getTableCellsOverviewSinglePeriod(aPeriod, aPaymentFilters, aSystemAccountFilter); } return getTableCellsInOrOut(FlowType.EXPENDITURE); } /** * gives the resulting table data for income single period. It first checks if the period, paymentFilters and systemAccountType are the same, and * if so, it will return a possibly already available result, rather than calculating it anew. * * @param aPeriod * @param aPaymentFilters a collection of paymentFilters. If only one paymentFilter is selected, this filter will be split up into its containing * <code>TransferType</code>s * @param aSystemAccountFilter * @return a Number[][] containing the table data for overview single period. */ public Number[][] getTableCellsIncomeSinglePeriod(final NamedPeriod aPeriod, final Collection<PaymentFilter> aPaymentFilters, final SystemAccountType aSystemAccountFilter) { if (!hasEqualParameters(aPeriod, aPaymentFilters, aSystemAccountFilter) || tableCells == null) { tableCells = getTableCellsOverviewSinglePeriod(aPeriod, aPaymentFilters, aSystemAccountFilter); } return getTableCellsInOrOut(FlowType.INCOME); } /** * gives the resulting table data for overview single period. It first checks if the period, paymentFilters and systemAccountType are the same, * and if so, it will return a possibly already available result, rather than calculating it anew. * * @param aPeriod * @param aPaymentFilters a collection of paymentFilters. If only one paymentFilter is selected, this filter will be split up into its containing * <code>TransferType</code>s * @param aSystemAccountFilter * @return a Number[][] containing the table data for overview single period. */ public Number[][] getTableCellsOverviewSinglePeriod(final Period aPeriod, final Collection<PaymentFilter> aPaymentFilters, final SystemAccountType aSystemAccountFilter) { Number[][] lTableCells = tableCells; if (!hasEqualParameters(aPeriod, aPaymentFilters, aSystemAccountFilter) || tableCells == null) { if (aPaymentFilters.size() == 1) { for (final PaymentFilter paymentFilter : aPaymentFilters) { lTableCells = new Number[paymentFilter.getTransferTypes().size() + 2][3]; int i = 0; for (final TransferType transferType : paymentFilter.getTransferTypes()) { lTableCells[i][0] = getIncome(aPeriod, transferType, aSystemAccountFilter); lTableCells[i][1] = getExpenditure(aPeriod, transferType, aSystemAccountFilter); i++; } } } else { lTableCells = new Number[aPaymentFilters.size() + 2][3]; // Assign table values int i = 0; for (final PaymentFilter paymentFilter : aPaymentFilters) { lTableCells[i][0] = getIncome(aPeriod, paymentFilter, aSystemAccountFilter); lTableCells[i][1] = getExpenditure(aPeriod, paymentFilter, aSystemAccountFilter); i++; } } lTableCells[lTableCells.length - 2][0] = getIncomeRest(aPeriod, aPaymentFilters, aSystemAccountFilter); lTableCells[lTableCells.length - 2][1] = getExpenditureRest(aPeriod, aPaymentFilters, aSystemAccountFilter); assignRowDifferences(lTableCells); assignColumnSums(lTableCells); tableCells = lTableCells; // reset fields period = aPeriod; paymentFilters = aPaymentFilters; systemAccountFilter = aSystemAccountFilter; paymentFilterNames = null; } return lTableCells; } /** * calculates the sums of the columns, and saves these to the last row of the input parameter * * @param tableCells */ private void assignColumnSums(final Number[][] tableCells) { final int rows = tableCells.length; final int cols = tableCells[0].length; for (int j = 0; j < cols; j++) { double sum = 0; for (int i = 0; i < rows - 1; i++) { sum += tableCells[i][j].doubleValue(); } tableCells[rows - 1][j] = new StatisticalNumber(sum, (byte) 2); } } /** * calculates the difference between column 0 and column 1, and saves these to the last column of the input param * * @param tableCells */ private void assignRowDifferences(final Number[][] tableCells) { final int rows = tableCells.length; final int cols = tableCells[0].length; for (int i = 0; i < rows - 1; i++) { double sum = 0; for (int j = 0; j < cols - 1; j++) { if (j == 0) { sum = tableCells[i][j].doubleValue(); } else { sum -= tableCells[i][j].doubleValue(); } } tableCells[i][cols - 1] = new StatisticalNumber(sum, (byte) 2); } } /** * retrieves the income or expenditure from the 2 dimensional array containing both of them. * * @param flowType INCOME or EXPENDITURE * @return the <code>Number</code> two dimensional array */ private Number[][] getTableCellsInOrOut(final FlowType flowType) { int col = 1; if (flowType == FlowType.INCOME) { col = 0; } final Number[][] lTableCells = new Number[tableCells.length - 1][1]; for (int i = 0; i < lTableCells.length; i++) { lTableCells[i][0] = tableCells[i][col]; } return lTableCells; } /** * checks if the parameters are changed, and if a local cached result is available. If parameters haven't changed and a cached value of the * results is available, the calling method should read results from local field values rather than reading them anew from the database. * Overloaded versions may exists for different sets of params.<br> * The method does <b>NOT</b> update the values it checks, that is the responsibility of the calling method, after having redone all calculations. * * @param aPeriod * @param aPaymentFilters * @param aSystemAccountFilter * @return <code>false</code> if the input parameters are different from the corresponding class instance variables, or if there is no cached * result available. */ private boolean hasEqualParameters(final Period aPeriod, final Collection<PaymentFilter> aPaymentFilters, final SystemAccountType aSystemAccountFilter) { boolean nulls = false; if (period == null || paymentFilters == null || systemAccountFilter == null) { nulls = true; } if (!nulls && period.equals(aPeriod) && systemAccountFilter.equals(aSystemAccountFilter) && paymentFilters.equals(aPaymentFilters)) { return true; } return false; } }