/*******************************************************************************
* Copyright (c) 2010 Denis Solonenko.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Denis Solonenko - initial API and implementation
******************************************************************************/
package ru.orangesoftware.financisto2.report;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import ru.orangesoftware.financisto2.activity.BlotterActivity;
import ru.orangesoftware.financisto2.blotter.BlotterFilter;
import ru.orangesoftware.financisto2.filter.WhereFilter;
import ru.orangesoftware.financisto2.filter.Criteria;
import ru.orangesoftware.financisto2.db.*;
import ru.orangesoftware.financisto2.db.DatabaseHelper.ReportColumns;
import ru.orangesoftware.financisto2.graph.Amount;
import ru.orangesoftware.financisto2.graph.GraphStyle;
import ru.orangesoftware.financisto2.graph.GraphUnit;
import ru.orangesoftware.financisto2.model.Currency;
import ru.orangesoftware.financisto2.model.Total;
import ru.orangesoftware.financisto2.model.TotalError;
import ru.orangesoftware.financisto2.rates.ExchangeRateProvider;
import ru.orangesoftware.financisto2.utils.MyPreferences;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
public abstract class Report {
public final GraphStyle style;
public final ReportType reportType;
protected final Context context;
protected final boolean skipTransfers;
protected final Currency currency;
protected IncomeExpense incomeExpense = IncomeExpense.BOTH;
public Report(ReportType reportType, Context context, Currency currency) {
this.reportType = reportType;
this.context = context;
this.skipTransfers = !MyPreferences.isIncludeTransfersIntoReports(context);
this.style = new GraphStyle.Builder(context).build();
this.currency = currency;
}
public void setIncomeExpense(IncomeExpense incomeExpense) {
this.incomeExpense = incomeExpense;
}
protected String alterName(long id, String name) {
return name;
}
public abstract ReportData getReport(DatabaseAdapter db, WhereFilter filter);
public ReportData getReportForChart(DatabaseAdapter db, WhereFilter filter) {
return getReport(db, filter);
}
protected ReportData queryReport(DatabaseAdapter db, String table, WhereFilter filter) {
filterTransfers(filter);
Cursor c = db.db().query(table, DatabaseHelper.ReportColumns.NORMAL_PROJECTION,
filter.getSelection(), filter.getSelectionArgs(), null, null, "_id");
ArrayList<GraphUnit> units = getUnitsFromCursor(db, c);
Total total = calculateTotal(units);
return new ReportData(units, total);
}
protected void filterTransfers(WhereFilter filter) {
if (skipTransfers) {
filter.put(Criteria.eq(ReportColumns.IS_TRANSFER, "0"));
}
}
protected ArrayList<GraphUnit> getUnitsFromCursor(DatabaseAdapter db, Cursor c) {
try {
ExchangeRateProvider rates = db.getHistoryRates();
ArrayList<GraphUnit> units = new ArrayList<GraphUnit>();
GraphUnit u = null;
long lastId = -1;
while (c.moveToNext()) {
long id = getId(c);
long isTransfer = c.getLong(c.getColumnIndex(ReportColumns.IS_TRANSFER));
if (id != lastId) {
if (u != null) {
units.add(u);
}
String name = c.getString(c.getColumnIndex(ReportColumns.NAME));
u = new GraphUnit(id, alterName(id, name), currency, style);
lastId = id;
}
BigDecimal amount;
try {
amount = TransactionsTotalCalculator.getAmountFromCursor(db, c, currency, rates, c.getColumnIndex(ReportColumns.DATETIME));
} catch (UnableToCalculateRateException e) {
amount = BigDecimal.ZERO;
u.error = TotalError.atDateRateError(e.fromCurrency, e.datetime);
}
u.addAmount(amount, skipTransfers && isTransfer != 0);
}
if (u != null) {
units.add(u);
}
for (GraphUnit unit : units) {
unit.flatten(incomeExpense);
}
removeEmptyUnits(units);
Collections.sort(units);
return units;
} finally {
c.close();
}
}
private void removeEmptyUnits(ArrayList<GraphUnit> units) {
Iterator<GraphUnit> unit = units.iterator();
while (unit.hasNext()) {
GraphUnit u = unit.next();
if (u.maxAmount == 0) {
unit.remove();
}
}
}
protected Total calculateTotal(List<? extends GraphUnit> units) {
Total total = new Total(currency, true);
for (GraphUnit u : units) {
for (Amount a : u) {
if (u.error != null) {
return new Total(currency, u.error);
}
long amount = a.amount;
if (amount > 0) {
total.amount += amount;
} else {
total.balance += amount;
}
}
}
return total;
}
protected long getId(Cursor c) {
return c.getLong(0);
}
public Intent createActivityIntent(Context context, CategoryRepository categoryRepository, WhereFilter parentFilter, long id) {
WhereFilter filter = WhereFilter.empty();
Criteria c = parentFilter.get(BlotterFilter.DATETIME);
if (c != null) {
filter.put(c);
}
c = getCriteriaForId(categoryRepository, id);
if (c != null) {
filter.put(c);
}
filter.eq("from_account_is_include_into_totals", "1");
Intent intent = new Intent(context, getBlotterActivityClass());
filter.toIntent(intent);
return intent;
}
protected abstract Criteria getCriteriaForId(CategoryRepository categoryRepository, long id);
protected Class<? extends BlotterActivity> getBlotterActivityClass() {
return BlotterActivity.class;
}
protected void cleanupFilter(WhereFilter filter) {
// fixing a bug with saving incorrect filter fot this report have to remove it here
filter.remove("left");
filter.remove("right");
}
public boolean shouldDisplayTotal() {
return true;
}
}