package com.code44.finance.data.providers;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.text.TextUtils;
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.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.utils.IOUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class TransactionsProvider extends BaseModelProvider {
public static final String URI_PARAM_JOIN_TABLE = "join_table";
public static final String URI_VALUE_JOIN_TABLE_ACCOUNTS_FROM = "accounts_from";
public static final String URI_VALUE_JOIN_TABLE_ACCOUNTS_TO = "accounts_to";
public static final String URI_VALUE_JOIN_TABLE_CATEGORIES = "categories";
public static final String URI_VALUE_JOIN_TABLE_CURRENCIES_FROM = "currencies_from";
public static final String URI_VALUE_JOIN_TABLE_CURRENCIES_TO = "currencies_to";
public static final String URI_VALUE_JOIN_TABLE_TAGS = "tags";
public static Uri uriTransactions() {
return uriModels(TransactionsProvider.class, Tables.Transactions.TABLE_NAME);
}
public static Uri uriTransaction(String transactionServerId) {
return uriModel(TransactionsProvider.class, Tables.Transactions.TABLE_NAME, transactionServerId);
}
public static void updateAccountBalance(SQLiteDatabase database, String accountId) {
final Cursor cursor = Query.create()
.projection("sum( case " +
" when (" + Tables.Transactions.TYPE + "=? or " + Tables.Transactions.TYPE + "=?) and " + Tables.Transactions.ACCOUNT_FROM_ID + "=? then -" + Tables.Transactions.AMOUNT + "" +
" when " + Tables.Transactions.TYPE + "=? then " + Tables.Transactions.AMOUNT + "*" + Tables.Transactions.EXCHANGE_RATE +
" else " + Tables.Transactions.AMOUNT + " end)")
.args(TransactionType.Expense.asString(), TransactionType.Transfer.asString(), accountId, TransactionType.Transfer.asString())
.selection(Tables.Transactions.MODEL_STATE + "=?", ModelState.Normal.asString())
.selection(" and " + Tables.Transactions.STATE + "=?", TransactionState.Confirmed.asString())
.selection(" and (" + Tables.Transactions.ACCOUNT_FROM_ID + "=? or " + Tables.Transactions.ACCOUNT_TO_ID + "=?)", accountId, accountId)
.from(database, Tables.Transactions.TABLE_NAME)
.execute();
long balance = 0;
if (cursor.moveToFirst()) {
balance = cursor.getLong(0);
}
IOUtils.closeQuietly(cursor);
final ContentValues values = new ContentValues();
values.put(Tables.Accounts.BALANCE.getName(), balance);
DataStore.update()
.values(values)
.withSelection(Tables.Accounts.ID + "=?", accountId)
.into(database, Tables.Accounts.TABLE_NAME);
}
public static void updateAllAccountsBalances(SQLiteDatabase database) {
final Cursor cursor = Query.create()
.projection(Tables.Accounts.ID.getName())
.selection(Tables.Accounts.MODEL_STATE + "=?", String.valueOf(ModelState.Normal.asInt()))
.from(database, Tables.Accounts.TABLE_NAME)
.execute();
if (cursor.moveToFirst()) {
final int iId = cursor.getColumnIndex(Tables.Accounts.ID.getName());
do {
updateAccountBalance(database, cursor.getString(iId));
} while (cursor.moveToNext());
}
IOUtils.closeQuietly(cursor);
}
@Override protected String getModelTable() {
return Tables.Transactions.TABLE_NAME;
}
@Override protected String getQueryTables(Uri uri) {
final List<String> joinTables = new ArrayList<>();
if (uri.getQueryParameterNames().contains(URI_PARAM_JOIN_TABLE)) {
// Join specific tables
joinTables.addAll(uri.getQueryParameters(URI_PARAM_JOIN_TABLE));
} else {
// Join all the things!
joinTables.add(URI_VALUE_JOIN_TABLE_ACCOUNTS_FROM);
joinTables.add(URI_VALUE_JOIN_TABLE_ACCOUNTS_TO);
joinTables.add(URI_VALUE_JOIN_TABLE_CATEGORIES);
joinTables.add(URI_VALUE_JOIN_TABLE_CURRENCIES_FROM);
joinTables.add(URI_VALUE_JOIN_TABLE_CURRENCIES_TO);
joinTables.add(URI_VALUE_JOIN_TABLE_TAGS);
}
final StringBuilder sb = new StringBuilder();
sb.append(getModelTable());
if (joinTables.contains(URI_VALUE_JOIN_TABLE_ACCOUNTS_FROM)) {
sb.append(" left join ").append(Tables.Accounts.TABLE_NAME).append(" as ").append(Tables.Accounts.TEMP_TABLE_NAME_FROM_ACCOUNT)
.append(" on ").append(Tables.Accounts.ID.getNameWithTable(Tables.Accounts.TEMP_TABLE_NAME_FROM_ACCOUNT)).append("=").append(Tables.Transactions.ACCOUNT_FROM_ID);
}
if (joinTables.contains(URI_VALUE_JOIN_TABLE_ACCOUNTS_TO)) {
sb.append(" left join ").append(Tables.Accounts.TABLE_NAME).append(" as ").append(Tables.Accounts.TEMP_TABLE_NAME_TO_ACCOUNT)
.append(" on ").append(Tables.Accounts.ID.getNameWithTable(Tables.Accounts.TEMP_TABLE_NAME_TO_ACCOUNT)).append("=").append(Tables.Transactions.ACCOUNT_TO_ID);
}
if (joinTables.contains(URI_VALUE_JOIN_TABLE_CATEGORIES)) {
sb.append(" left join ").append(Tables.Categories.TABLE_NAME)
.append(" on ").append(Tables.Categories.ID.getNameWithTable()).append("=").append(Tables.Transactions.CATEGORY_ID);
}
if (joinTables.contains(URI_VALUE_JOIN_TABLE_CURRENCIES_FROM)) {
sb.append(" left join ").append(Tables.Currencies.TABLE_NAME).append(" as ").append(Tables.Currencies.TEMP_TABLE_NAME_FROM_CURRENCY)
.append(" on ").append(Tables.Currencies.ID.getNameWithTable(Tables.Currencies.TEMP_TABLE_NAME_FROM_CURRENCY)).append("=").append(Tables.Accounts.CURRENCY_ID.getNameWithTable(Tables.Accounts.TEMP_TABLE_NAME_FROM_ACCOUNT));
}
if (joinTables.contains(URI_VALUE_JOIN_TABLE_CURRENCIES_TO)) {
sb.append(" left join ").append(Tables.Currencies.TABLE_NAME).append(" as ").append(Tables.Currencies.TEMP_TABLE_NAME_TO_CURRENCY)
.append(" on ").append(Tables.Currencies.ID.getNameWithTable(Tables.Currencies.TEMP_TABLE_NAME_TO_CURRENCY)).append("=").append(Tables.Accounts.CURRENCY_ID.getNameWithTable(Tables.Accounts.TEMP_TABLE_NAME_TO_ACCOUNT));
}
if (joinTables.contains(URI_VALUE_JOIN_TABLE_TAGS)) {
sb.append(" left join ").append(Tables.TransactionTags.TABLE_NAME)
.append(" on ").append(Tables.TransactionTags.TRANSACTION_ID).append("=").append(Tables.Transactions.ID.getNameWithTable());
sb.append(" left join ").append(Tables.Tags.TABLE_NAME)
.append(" on ").append(Tables.Tags.ID.getNameWithTable()).append("=").append(Tables.TransactionTags.TAG_ID);
}
return sb.toString();
}
@Override protected Column getIdColumn() {
return Tables.Transactions.ID;
}
@Override protected void onBeforeInsertItem(Uri uri, ContentValues values, String serverId, Map<String, Object> outExtras) {
super.onBeforeInsertItem(uri, values, serverId, outExtras);
updateTransactionTags(values);
}
@Override protected void onAfterInsertItem(Uri uri, ContentValues values, String serverId, Map<String, Object> extras) {
super.onAfterInsertItem(uri, values, serverId, extras);
updateAllAccountsBalances(database);
}
@Override protected void onBeforeUpdateItems(Uri uri, ContentValues values, String selection, String[] selectionArgs, Map<String, Object> outExtras) {
throw new IllegalArgumentException("Update is not supported.");
}
@Override protected void onAfterDeleteItems(Uri uri, String selection, String[] selectionArgs, ModelState modelState, Map<String, Object> extras) {
super.onAfterDeleteItems(uri, selection, selectionArgs, modelState, extras);
updateAllAccountsBalances(getDatabase());
}
@Override protected void onBeforeBulkInsertIteration(Uri uri, ContentValues values, Map<String, Object> extras) {
super.onBeforeBulkInsertIteration(uri, values, extras);
updateTransactionTags(values);
}
@Override protected void onAfterBulkInsertItems(Uri uri, ContentValues[] valuesArray, Map<String, Object> extras) {
super.onAfterBulkInsertItems(uri, valuesArray, extras);
updateAllAccountsBalances(getDatabase());
}
@Override protected Uri[] getOtherUrisToNotify() {
return new Uri[]{AccountsProvider.uriAccounts()};
}
private void updateTransactionTags(ContentValues values) {
// Remove current tags
final String transactionId = values.getAsString(Tables.Transactions.ID.getName());
getDatabase().delete(Tables.TransactionTags.TABLE_NAME, Tables.TransactionTags.TRANSACTION_ID + "=?", new String[]{transactionId});
// Add new tags
if (values.containsKey(Tables.Tags.ID.getName())) {
final String[] tagIds = TextUtils.split(values.getAsString(Tables.Tags.ID.getName()), Tables.CONCAT_SEPARATOR);
values.remove(Tables.Tags.ID.getName());
if (tagIds != null) {
for (String tagId : tagIds) {
final ContentValues tagValues = new ContentValues();
tagValues.put(Tables.TransactionTags.TRANSACTION_ID.getName(), transactionId);
tagValues.put(Tables.TransactionTags.TAG_ID.getName(), tagId);
getDatabase().insert(Tables.TransactionTags.TABLE_NAME, null, tagValues);
}
}
}
}
}