package com.code44.finance.data.providers; import android.content.ContentValues; import android.database.Cursor; import android.net.Uri; import com.code44.finance.R; import com.code44.finance.common.model.ModelState; import com.code44.finance.common.model.TransactionState; import com.code44.finance.common.model.TransactionType; import com.code44.finance.common.utils.StringUtils; import com.code44.finance.data.DataStore; import com.code44.finance.data.Query; import com.code44.finance.data.db.Column; import com.code44.finance.data.db.Tables; import com.code44.finance.data.model.Account; import com.code44.finance.data.model.Transaction; import com.code44.finance.utils.IOUtils; import java.util.List; import java.util.Map; public class AccountsProvider extends BaseModelProvider { private static final String EXTRA_BALANCE_DELTA = "balance_delta"; public static Uri uriAccounts() { return uriModels(AccountsProvider.class, Tables.Accounts.TABLE_NAME); } public static Uri uriAccount(String accountServerId) { return uriModel(AccountsProvider.class, Tables.Accounts.TABLE_NAME, accountServerId); } @Override protected String getModelTable() { return Tables.Accounts.TABLE_NAME; } @Override protected String getQueryTables(Uri uri) { return getModelTable() + " inner join " + Tables.Currencies.TABLE_NAME + " on " + Tables.Currencies.ID + "=" + Tables.Accounts.CURRENCY_ID; } @Override protected Column getIdColumn() { return Tables.Accounts.ID; } @Override protected void onBeforeInsertItem(Uri uri, ContentValues values, String serverId, Map<String, Object> outExtras) { super.onBeforeInsertItem(uri, values, serverId, outExtras); final long currentBalance = getCurrentBalance(values); //noinspection ConstantConditions final long newBalance = values.getAsLong(Tables.Accounts.BALANCE.getName()); outExtras.put(EXTRA_BALANCE_DELTA, newBalance - currentBalance); values.remove(Tables.Accounts.BALANCE.getName()); } @Override protected void onAfterInsertItem(Uri uri, ContentValues values, String serverId, Map<String, Object> extras) { super.onAfterInsertItem(uri, values, serverId, extras); final Account account = new Account(); account.setId(serverId); long balanceDelta = (long) extras.get(EXTRA_BALANCE_DELTA); final Transaction transaction = createBalanceTransaction(account, balanceDelta); if (transaction != null) { DataStore.insert().model(transaction).into(getContext(), TransactionsProvider.uriTransactions()); } } @Override protected void onBeforeUpdateItems(Uri uri, ContentValues values, String selection, String[] selectionArgs, Map<String, Object> outExtras) { super.onBeforeUpdateItems(uri, values, selection, selectionArgs, outExtras); throw new IllegalArgumentException("Update is not supported."); } @Override protected void onBeforeDeleteItems(Uri uri, String selection, String[] selectionArgs, ModelState modelState, Map<String, Object> outExtras) { super.onBeforeDeleteItems(uri, selection, selectionArgs, modelState, outExtras); final List<String> affectedIds = getIdList(getIdColumn(), selection, selectionArgs); outExtras.put("affectedIds", affectedIds); } @Override protected void onAfterDeleteItems(Uri uri, String selection, String[] selectionArgs, ModelState modelState, Map<String, Object> extras) { super.onAfterDeleteItems(uri, selection, selectionArgs, modelState, extras); //noinspection unchecked final List<String> affectedIds = (List<String>) extras.get("affectedIds"); if (affectedIds.size() > 0) { final Uri transactionsUri = uriForDeleteFromItemState(TransactionsProvider.uriTransactions(), modelState); Query query = Query.create().selectionInClause(Tables.Transactions.ACCOUNT_FROM_ID.getName(), affectedIds); getContext().getContentResolver().delete(transactionsUri, query.getSelection(), query.getSelectionArgs()); query = Query.create().selectionInClause(Tables.Transactions.ACCOUNT_TO_ID.getName(), affectedIds); getContext().getContentResolver().delete(transactionsUri, query.getSelection(), query.getSelectionArgs()); } } private long getCurrentBalance(ContentValues values) { final String accountId = values.getAsString(Tables.Accounts.ID.getName()); if (StringUtils.isEmpty(accountId)) { return 0; } final Cursor cursor = Query.create() .projection(Tables.Accounts.BALANCE.getName()) .selection(Tables.Accounts.ID + "=?", String.valueOf(accountId)) .from(getDatabase(), Tables.Accounts.TABLE_NAME) .execute(); final long balance = cursor.moveToFirst() ? cursor.getLong(cursor.getColumnIndex(Tables.Accounts.BALANCE.getName())) : 0; IOUtils.closeQuietly(cursor); return balance; } private Transaction createBalanceTransaction(Account account, long balanceDelta) { Transaction transaction = null; if (balanceDelta > 0) { transaction = new Transaction(); transaction.setAccountTo(account); transaction.setTransactionType(TransactionType.Income); } else if (balanceDelta < 0) { transaction = new Transaction(); transaction.setAccountFrom(account); transaction.setTransactionType(TransactionType.Expense); } if (transaction != null) { transaction.setAmount(Math.abs(balanceDelta)); transaction.setNote(getContext().getString(R.string.account_balance_update)); transaction.setIncludeInReports(false); transaction.setTransactionState(TransactionState.Confirmed); } return transaction; } }