/* * Copyright (C) 2012-2016 The Android Money Manager Ex Project Team * * This program 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 3 * of the License, or (at your option) any later version. * * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ package com.money.manager.ex.servicelayer; import android.content.Context; import android.database.Cursor; import android.database.DatabaseUtils; import android.database.MatrixCursor; import android.database.MergeCursor; import android.widget.SimpleCursorAdapter; import android.widget.Spinner; import com.money.manager.ex.Constants; import com.money.manager.ex.R; import com.money.manager.ex.account.AccountStatuses; import com.money.manager.ex.account.AccountTypes; import com.money.manager.ex.core.ToolbarSpinnerAdapter; import com.money.manager.ex.core.TransactionStatuses; import com.money.manager.ex.core.TransactionTypes; import com.money.manager.ex.core.UIHelper; import com.money.manager.ex.currency.CurrencyService; import com.money.manager.ex.database.ITransactionEntity; import com.money.manager.ex.datalayer.AccountRepository; import com.money.manager.ex.database.QueryAccountBills; import com.money.manager.ex.database.QueryAllData; import com.money.manager.ex.database.WhereStatementGenerator; import com.money.manager.ex.datalayer.AccountTransactionRepository; import com.money.manager.ex.datalayer.StockFields; import com.money.manager.ex.datalayer.StockRepository; import com.money.manager.ex.domainmodel.Account; import com.money.manager.ex.settings.AppSettings; import com.money.manager.ex.settings.LookAndFeelSettings; import com.money.manager.ex.viewmodels.AccountTransactionDisplay; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; import info.javaperformance.money.Money; import info.javaperformance.money.MoneyFactory; import timber.log.Timber; /** * Various business logic pieces related to Account(s). */ public class AccountService extends ServiceBase { public AccountService(Context context) { super(context); } public Account createAccount(String name, AccountTypes accountType, AccountStatuses status, boolean favourite, int currencyId) { Account account = Account.create(name, accountType, status, favourite, currencyId); // update AccountRepository repo = new AccountRepository(getContext()); repo.save(account); return account; } /** * Loads account list, applying the current preferences for Open & Favourite accounts. * @return List of accounts */ public List<Account> getAccountList() { AppSettings settings = new AppSettings(getContext()); boolean favourite = settings.getLookAndFeelSettings().getViewFavouriteAccounts(); boolean open = settings.getLookAndFeelSettings().getViewOpenAccounts(); return getAccountList(open, favourite); } /** * Load account list with given parameters. * Includes all account types. * @param open show open accounts * @param favorite show favorite account * @return List<Account> list of accounts selected */ public List<Account> getAccountList(boolean open, boolean favorite) { // create a return list return loadAccounts(open, favorite, null); } /** * Calculate simple balance by adding together all transactions before and on the * given date. To get the real balance, this amount should be subtracted from the * account initial balance. * @param isoDate date in ISO format */ public Money calculateBalanceOn(int accountId, String isoDate) { Money total = MoneyFactory.fromBigDecimal(BigDecimal.ZERO); WhereStatementGenerator where = new WhereStatementGenerator(); // load all transactions on the account before and on given date. where.addStatement( where.concatenateOr( where.getStatement(ITransactionEntity.ACCOUNTID, "=", accountId), where.getStatement(ITransactionEntity.TOACCOUNTID, "=", accountId) ) ); where.addStatement(ITransactionEntity.TRANSDATE, "<=", isoDate); where.addStatement(ITransactionEntity.STATUS, "<>", TransactionStatuses.VOID.getCode()); String selection = where.getWhere(); AccountTransactionRepository repo = new AccountTransactionRepository(getContext()); Cursor cursor = getContext().getContentResolver().query(repo.getUri(), null, selection, null, null); if (cursor == null) return total; AccountTransactionDisplay tx = new AccountTransactionDisplay(); Money amount; // calculate balance. while (cursor.moveToNext()) { tx.contentValues.clear(); String transType = cursor.getString(cursor.getColumnIndex(ITransactionEntity.TRANSCODE)); // Some users have invalid Transaction Type. Should we check .contains()? switch (TransactionTypes.valueOf(transType)) { case Withdrawal: DatabaseUtils.cursorDoubleToContentValues(cursor, ITransactionEntity.TRANSAMOUNT, tx.contentValues, QueryAllData.Amount); amount = tx.getAmount(); total = total.subtract(amount); break; case Deposit: DatabaseUtils.cursorDoubleToContentValues(cursor, ITransactionEntity.TRANSAMOUNT, tx.contentValues, QueryAllData.Amount); amount = tx.getAmount(); total = total.add(amount); break; case Transfer: DatabaseUtils.cursorDoubleToContentValues(cursor, ITransactionEntity.ACCOUNTID, tx.contentValues, QueryAllData.ACCOUNTID); if (tx.getAccountId().equals(accountId)) { DatabaseUtils.cursorDoubleToContentValues(cursor, ITransactionEntity.TRANSAMOUNT, tx.contentValues, QueryAllData.Amount); amount = tx.getAmount(); total = total.subtract(amount); } else { DatabaseUtils.cursorDoubleToContentValues(cursor, ITransactionEntity.TOTRANSAMOUNT, tx.contentValues, QueryAllData.Amount); amount = tx.getAmount(); total = total.add(amount); } break; } } cursor.close(); return total; } public String getAccountCurrencyCode(int accountId) { AccountRepository repo = new AccountRepository(getContext()); Account account = (Account) repo.first(Account.class, new String[] {Account.CURRENCYID}, Account.ACCOUNTID + "=?", new String[] { Integer.toString(accountId)}, null); int currencyId = account.getCurrencyId(); CurrencyService currencyService = new CurrencyService(getContext()); return currencyService.getCurrency(currencyId).getCode(); } public Cursor getCursor(boolean open, boolean favorite, List<String> accountTypes) { try { return getCursorInternal(open, favorite, accountTypes); } catch (Exception ex) { Timber.e(ex, "getting cursor in account repository"); } return null; } public List<String> getTransactionAccountTypeNames() { List<String> accountTypeNames = new ArrayList<>(); List<AccountTypes> accountTypes = getTransactionAccountTypes(); for (AccountTypes type : accountTypes) { accountTypeNames.add(type.toString()); } return accountTypeNames; } public List<AccountTypes> getTransactionAccountTypes() { List<AccountTypes> list = new ArrayList<>(); list.add(AccountTypes.CASH); list.add(AccountTypes.CHECKING); list.add(AccountTypes.TERM); list.add(AccountTypes.CREDIT_CARD); return list; } public List<Account> getTransactionAccounts(boolean openOnly, boolean favoriteOnly) { List<String> accountTypeNames = getTransactionAccountTypeNames(); List<Account> result = loadAccounts(openOnly, favoriteOnly, accountTypeNames); return result; } /** * Check if the account is used in any of the transactions. * @param accountId id of the account * @return a boolean indicating if there are any transactions using this account. */ public boolean isAccountUsed(int accountId) { WhereStatementGenerator where = new WhereStatementGenerator(); // transactional accounts where.addStatement( where.concatenateOr( where.getStatement(ITransactionEntity.ACCOUNTID, "=", accountId), where.getStatement(ITransactionEntity.TOACCOUNTID, "=", accountId) ) ); AccountTransactionRepository repo = new AccountTransactionRepository(getContext()); int txCount = repo.count(where.getWhere(), null); // investment accounts StockRepository stockRepository = new StockRepository(getContext()); where.clear(); where.addStatement(StockFields.HELDAT, "=", accountId); int investmentCount = stockRepository.count(where.getWhere(), null); return (txCount + investmentCount) > 0; } public void loadTransactionAccountsToSpinner(Spinner spinner) { if (spinner == null) return; LookAndFeelSettings settings = new AppSettings(getContext()).getLookAndFeelSettings(); Cursor cursor = this.getCursor(settings.getViewOpenAccounts(), settings.getViewFavouriteAccounts(), this.getTransactionAccountTypeNames()); int[] adapterRowViews = new int[] { android.R.id.text1 }; ToolbarSpinnerAdapter cursorAdapter = new ToolbarSpinnerAdapter(getContext(), android.R.layout.simple_spinner_item, cursor, new String[] { Account.ACCOUNTNAME, Account.ACCOUNTID }, adapterRowViews, SimpleCursorAdapter.NO_SELECTION); // cursorAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); cursorAdapter.setDropDownViewResource(R.layout.toolbar_spinner_item_dropdown); spinner.setAdapter(cursorAdapter); } public void loadInvestmentAccountsToSpinner(Spinner spinner, boolean showAllAccountsItem) { if (spinner == null) return; AccountRepository repo = new AccountRepository(getContext()); Cursor cursor = repo.getInvestmentAccountsCursor(true); Cursor finalCursor = cursor; if (showAllAccountsItem) { // append All Accounts item MatrixCursor extras = new MatrixCursor(new String[]{"_id", Account.ACCOUNTID, Account.ACCOUNTNAME, Account.INITIALBAL}); String title = getContext().getString(R.string.all_accounts); extras.addRow(new String[]{Integer.toString(Constants.NOT_SET), Integer.toString(Constants.NOT_SET), title, "0.0"}); Cursor[] cursors = {extras, cursor}; finalCursor = new MergeCursor(cursors); } int[] adapterRowViews = new int[] { android.R.id.text1 }; ToolbarSpinnerAdapter cursorAdapter = new ToolbarSpinnerAdapter(getContext(), android.R.layout.simple_spinner_item, finalCursor, new String[] { Account.ACCOUNTNAME, Account.ACCOUNTID }, adapterRowViews, SimpleCursorAdapter.NO_SELECTION); cursorAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinner.setAdapter(cursorAdapter); } public List<Account> loadAccounts(boolean openOnly, boolean favoriteOnly, List<String> accountTypes) { List<Account> result = new ArrayList<>(); Cursor cursor = getCursor(openOnly, favoriteOnly, accountTypes); if (cursor == null) { new UIHelper(getContext()).showToast("Error reading accounts list!"); return result; } while (cursor.moveToNext()) { Account account = new Account(); account.loadFromCursor(cursor); result.add(account); } cursor.close(); return result; } public Money loadInitialBalance(int accountId) { AccountRepository repo = new AccountRepository(getContext()); Account account = repo.load(accountId); return account.getInitialBalance(); } /** * Loads account details with balances. * Needs to be better organized to limit the where clause. * @param where selection criteria for Select Account Bills * @return current balance in the currency of the account. */ public Money loadBalance(String where) { Money curTotal = MoneyFactory.fromString("0"); QueryAccountBills accountBills = new QueryAccountBills(getContext()); Cursor cursor = getContext().getContentResolver().query(accountBills.getUri(), null, where, null, null); if (cursor == null) return curTotal; // calculate summary while (cursor.moveToNext()) { // curTotal = curTotal.add(MoneyFactory.fromDouble(cursor.getDouble(cursor.getColumnIndex(QueryAccountBills.TOTAL)))); curTotal = curTotal.add(MoneyFactory.fromString(cursor.getString(cursor.getColumnIndex(QueryAccountBills.TOTAL)))); } cursor.close(); return curTotal; } // Private private Cursor getCursorInternal(boolean openOnly, boolean favoriteOnly, List<String> accountTypes) { AccountRepository repo = new AccountRepository(getContext()); String where = getWhereFilterFor(openOnly, favoriteOnly); if (accountTypes != null && accountTypes.size() > 0) { where = DatabaseUtils.concatenateWhere(where, getWherePartFor(accountTypes)); } Cursor cursor = getContext().getContentResolver().query(repo.getUri(), repo.getAllColumns(), where, null, "lower (" + Account.ACCOUNTNAME + ")" ); return cursor; } private String getWhereFilterFor(boolean openOnly, boolean favoriteOnly) { StringBuilder where = new StringBuilder(); if (openOnly) { where.append("LOWER(STATUS)='open'"); } if (favoriteOnly) { if (openOnly) { where.append(" AND "); } where.append("LOWER(FAVORITEACCT)='true'"); } return where.toString(); } private String getWherePartFor(List<String> accountTypes) { StringBuilder where = new StringBuilder(); where.append(Account.ACCOUNTTYPE); where.append(" IN ("); for(String type : accountTypes) { if (accountTypes.indexOf(type) > 0) { // if not first, add comma before the type name where.append(','); } where.append("'"); where.append(type); where.append("'"); } where.append(")"); return where.toString(); } }