Skip to content

Commit

Permalink
Lists of accounts are now sorted by the fully qualified account name
Browse files Browse the repository at this point in the history
  • Loading branch information
codinguser committed Feb 13, 2014
1 parent 9162bd0 commit fc1a97b
Show file tree
Hide file tree
Showing 10 changed files with 212 additions and 50 deletions.
57 changes: 46 additions & 11 deletions app/src/org/gnucash/android/db/AccountsDbAdapter.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,28 +69,35 @@ public void close() {
*/
public long addAccount(Account account){
ContentValues contentValues = new ContentValues();
contentValues.put(DatabaseHelper.KEY_NAME, account.getName());
contentValues.put(DatabaseHelper.KEY_TYPE, account.getAccountType().name());
contentValues.put(DatabaseHelper.KEY_UID, account.getUID());
contentValues.put(DatabaseHelper.KEY_NAME, account.getName());
contentValues.put(DatabaseHelper.KEY_TYPE, account.getAccountType().name());
contentValues.put(DatabaseHelper.KEY_UID, account.getUID());
contentValues.put(DatabaseHelper.KEY_CURRENCY_CODE, account.getCurrency().getCurrencyCode());
contentValues.put(DatabaseHelper.KEY_PARENT_ACCOUNT_UID, account.getParentUID());
contentValues.put(DatabaseHelper.KEY_DEFAULT_TRANSFER_ACCOUNT_UID, account.getDefaultTransferAccountUID());
contentValues.put(DatabaseHelper.KEY_PLACEHOLDER, account.isPlaceholderAccount() ? 1 : 0);
contentValues.put(DatabaseHelper.KEY_COLOR_CODE, account.getColorHexCode());
contentValues.put(DatabaseHelper.KEY_FAVORITE, account.isFavorite() ? 1 : 0);
contentValues.put(DatabaseHelper.KEY_PLACEHOLDER, account.isPlaceholderAccount() ? 1 : 0);
contentValues.put(DatabaseHelper.KEY_COLOR_CODE, account.getColorHexCode());
contentValues.put(DatabaseHelper.KEY_FAVORITE, account.isFavorite() ? 1 : 0);
contentValues.put(DatabaseHelper.KEY_FULL_NAME, account.getFullName());
contentValues.put(DatabaseHelper.KEY_PARENT_ACCOUNT_UID, account.getParentUID());
contentValues.put(DatabaseHelper.KEY_DEFAULT_TRANSFER_ACCOUNT_UID, account.getDefaultTransferAccountUID());

long rowId = -1;
if ((rowId = getAccountID(account.getUID())) > 0){
//if account already exists, then just update
Log.d(TAG, "Updating existing account");
mDb.update(DatabaseHelper.ACCOUNTS_TABLE_NAME, contentValues,
DatabaseHelper.KEY_ROW_ID + " = " + rowId, null);
int rowsAffected = mDb.update(DatabaseHelper.ACCOUNTS_TABLE_NAME, contentValues,
DatabaseHelper.KEY_ROW_ID + " = " + rowId, null);
if (rowsAffected == 1){
updateAccount(rowId, DatabaseHelper.KEY_FULL_NAME, getFullyQualifiedAccountName(rowId));
}
} else {
Log.d(TAG, "Adding new account to db");
rowId = mDb.insert(DatabaseHelper.ACCOUNTS_TABLE_NAME, null, contentValues);
}

//now add transactions if there are any
if (rowId > 0){
//update the fully qualified account name
updateAccount(rowId, DatabaseHelper.KEY_FULL_NAME, getFullyQualifiedAccountName(rowId));
for (Transaction t : account.getTransactions()) {
mTransactionsAdapter.addTransaction(t);
}
Expand Down Expand Up @@ -237,6 +244,7 @@ public Account buildAccountInstance(Cursor c){
account.setDefaultTransferAccountUID(c.getString(DatabaseAdapter.COLUMN_DEFAULT_TRANSFER_ACCOUNT_UID));
account.setColorCode(c.getString(DatabaseAdapter.COLUMN_COLOR_CODE));
account.setFavorite(c.getInt(DatabaseAdapter.COLUMN_FAVORITE) == 1);
account.setFullName(c.getString(DatabaseAdapter.COLUMN_FULL_NAME));
return account;
}

Expand Down Expand Up @@ -441,6 +449,21 @@ public Cursor fetchAllRecords(){
return cursor;
}

/**
* Returns a cursor to all account records in the database ordered by full name.
* GnuCash ROOT accounts are ignored
* @return {@link Cursor} to all account records
*/
public Cursor fetchAllRecordsOrderedByFullName(){
Log.v(TAG, "Fetching all accounts from db");
String selection = DatabaseHelper.KEY_TYPE + " != ?" ;
return mDb.query(DatabaseHelper.ACCOUNTS_TABLE_NAME,
null,
selection,
new String[]{AccountType.ROOT.name()},
null, null,
DatabaseHelper.KEY_FULL_NAME + " ASC");
}

@Override
public Cursor fetchRecord(long rowId) {
Expand Down Expand Up @@ -471,7 +494,19 @@ public Cursor fetchAccounts(String condition){
DatabaseHelper.KEY_NAME + " ASC");
return cursor;
}


/**
* Returns a Cursor set of accounts which fulfill <code>condition</code>
* <p>This method returns the accounts list sorted by the full account name</p>
* @param condition SQL WHERE statement without the 'WHERE' itself
* @return Cursor set of accounts which fulfill <code>condition</code>
*/
public Cursor fetchAccountsOrderedByFullName(String condition){
Log.v(TAG, "Fetching all accounts from db where " + condition);
return mDb.query(DatabaseHelper.ACCOUNTS_TABLE_NAME,
null, condition, null, null, null,
DatabaseHelper.KEY_FULL_NAME + " ASC");
}
/**
* Returns the balance of an account while taking sub-accounts into consideration
* @return Account Balance of an account including sub-accounts
Expand Down
1 change: 1 addition & 0 deletions app/src/org/gnucash/android/db/DatabaseAdapter.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public abstract class DatabaseAdapter {
public static final int COLUMN_DEFAULT_TRANSFER_ACCOUNT_UID = 7;
public static final int COLUMN_COLOR_CODE = 8;
public static final int COLUMN_FAVORITE = 9;
public static final int COLUMN_FULL_NAME = 10;

/**
* {@link DatabaseHelper} for creating and opening the database
Expand Down
116 changes: 111 additions & 5 deletions app/src/org/gnucash/android/db/DatabaseHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@

package org.gnucash.android.db;

import org.gnucash.android.model.Account.AccountType;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import org.gnucash.android.model.Account.AccountType;

/**
* Helper class for managing the SQLite database.
Expand All @@ -46,7 +46,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
* Database version.
* With any change to the database schema, this number must increase
*/
private static final int DATABASE_VERSION = 5;
private static final int DATABASE_VERSION = 6;

/**
* Name of accounts table
Expand All @@ -70,7 +70,13 @@ public class DatabaseHelper extends SQLiteOpenHelper {
* Currently used by all tables
*/
public static final String KEY_NAME = "name";


/**
* Key for fully qualified name of the account column
* This name includes the parent hierarchy
*/
public static final String KEY_FULL_NAME = "full_name";

/**
* Unique Identifier.
*/
Expand Down Expand Up @@ -170,6 +176,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
+ KEY_DEFAULT_TRANSFER_ACCOUNT_UID + " varchar(255), "
+ KEY_COLOR_CODE + " varchar(255), "
+ KEY_FAVORITE + " tinyint default 0, "
+ KEY_FULL_NAME + " varchar(255), "
+ "UNIQUE (" + KEY_UID + ")"
+ ");";

Expand Down Expand Up @@ -275,10 +282,109 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

oldVersion = 5;
}

if (oldVersion == 5 && newVersion >= 6){
Log.i(TAG, "Upgrading database to version 6");
String addFullAccountNameQuery = " ALTER TABLE " + ACCOUNTS_TABLE_NAME
+ " ADD COLUMN " + KEY_FULL_NAME + " varchar(255) ";
db.execSQL(addFullAccountNameQuery);

//update all existing accounts with their fully qualified name
Cursor cursor = db.query(DatabaseHelper.ACCOUNTS_TABLE_NAME,
new String[]{KEY_ROW_ID, KEY_UID},
null, null, null, null, null);
while(cursor != null && cursor.moveToNext()){
String uid = cursor.getString(cursor.getColumnIndexOrThrow(KEY_UID));
String fullName = getFullyQualifiedAccountName(db, uid);

if (fullName == null)
continue;

ContentValues contentValues = new ContentValues();
contentValues.put(KEY_FULL_NAME, fullName);

long id = cursor.getLong(cursor.getColumnIndexOrThrow(KEY_ROW_ID));
db.update(ACCOUNTS_TABLE_NAME, contentValues, KEY_ROW_ID + " = " + id, null);
}

if (cursor != null) {
cursor.close();
}

oldVersion = 6;
}
}

if (oldVersion != newVersion) {
Log.i(TAG, "Upgrade for the database failed. The Database is currently at version " + oldVersion);
Log.w(TAG, "Upgrade for the database failed. The Database is currently at version " + oldVersion);
}
}

/**
* Performs same functtion as {@link org.gnucash.android.db.AccountsDbAdapter#getFullyQualifiedAccountName(String)}
* <p>This method is only necessary because we cannot open the database again (by instantiating {@link AccountsDbAdapter}
* while it is locked for upgrades. So we reimplement the method here.</p>
* @param db SQLite database
* @param accountUID Unique ID of account whose fully qualified name is to be determined
* @return Fully qualified (colon-sepaated) account name
* @see org.gnucash.android.db.AccountsDbAdapter#getFullyQualifiedAccountName(String)
*/
private String getFullyQualifiedAccountName(SQLiteDatabase db, String accountUID){
//get the parent account UID of the account
Cursor cursor = db.query(DatabaseHelper.ACCOUNTS_TABLE_NAME,
new String[] {DatabaseHelper.KEY_ROW_ID, DatabaseHelper.KEY_PARENT_ACCOUNT_UID},
DatabaseHelper.KEY_UID + " = ?",
new String[]{accountUID},
null, null, null, null);

String parentAccountUID = null;
if (cursor != null && cursor.moveToFirst()){
parentAccountUID = cursor.getString(cursor.getColumnIndexOrThrow(DatabaseHelper.KEY_PARENT_ACCOUNT_UID));
cursor.close();
}

//get the name of the account
cursor = db.query(DatabaseHelper.ACCOUNTS_TABLE_NAME,
new String[]{DatabaseHelper.KEY_ROW_ID, DatabaseHelper.KEY_NAME},
DatabaseHelper.KEY_UID + " = '" + accountUID + "'",
null, null, null, null);

String accountName = null;
if (cursor != null && cursor.moveToFirst()){
accountName = cursor.getString(cursor.getColumnIndexOrThrow(DatabaseHelper.KEY_NAME));
cursor.close();
}

String gnucashRootAccountUID = getGnuCashRootAccountUID(db);
if (parentAccountUID == null || accountName == null
|| parentAccountUID.equalsIgnoreCase(gnucashRootAccountUID)){
return accountName;
}

String parentAccountName = getFullyQualifiedAccountName(db, parentAccountUID);

return parentAccountName + AccountsDbAdapter.ACCOUNT_NAME_SEPARATOR + accountName;
}

/**
* Returns the GnuCash ROOT account UID.
* <p>In GnuCash desktop account structure, there is a root account (which is not visible in the UI) from which
* other top level accounts derive. GnuCash Android does not have this ROOT account by default unless the account
* structure was imported from GnuCash for desktop. Hence this method also returns <code>null</code> as an
* acceptable result.</p>
* <p><b>Note:</b> NULL is an acceptable response, be sure to check for it</p>
* @return Unique ID of the GnuCash root account.
*/
private String getGnuCashRootAccountUID(SQLiteDatabase db){
String condition = DatabaseHelper.KEY_TYPE + "= '" + AccountType.ROOT.name() + "'";
Cursor cursor = db.query(DatabaseHelper.ACCOUNTS_TABLE_NAME,
null, condition, null, null, null,
DatabaseHelper.KEY_NAME + " ASC");
String rootUID = null;
if (cursor != null && cursor.moveToFirst()){
rootUID = cursor.getString(DatabaseAdapter.COLUMN_UID);
cursor.close();
}
return rootUID;
}
}
39 changes: 32 additions & 7 deletions app/src/org/gnucash/android/model/Account.java
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,13 @@ public enum OfxAccountType {CHECKING, SAVINGS, MONEYMRKT, CREDITLINE }
* Name of this account
*/
private String mName;


/**
* Fully qualified name of this account including the parent hierarchy.
* On instantiation of an account, the full name is set to the name by default
*/
private String mFullName;

/**
* Currency used by transactions in this account
*/
Expand Down Expand Up @@ -179,8 +185,9 @@ public enum OfxAccountType {CHECKING, SAVINGS, MONEYMRKT, CREDITLINE }
*/
public Account(String name) {
setName(name);
this.mUID = generateUID();
this.mCurrency = Currency.getInstance(Money.DEFAULT_CURRENCY_CODE);
this.mFullName = mName;
this.mUID = generateUID();
this.mCurrency = Currency.getInstance(Money.DEFAULT_CURRENCY_CODE);
}

/**
Expand All @@ -190,8 +197,9 @@ public Account(String name) {
*/
public Account(String name, Currency currency){
setName(name);
this.mUID = generateUID();
this.mCurrency = currency;
this.mFullName = mName;
this.mUID = generateUID();
this.mCurrency = currency;
}

/**
Expand All @@ -209,8 +217,25 @@ public void setName(String name) {
public String getName() {
return mName;
}

/**

/**
* Returns the full name of this account.
* The full name is the full account hierarchy name
* @return Fully qualified name of the account
*/
public String getFullName() {
return mFullName;
}

/**
* Sets the fully qualified name of the account
* @param fullName Fully qualified account name
*/
public void setFullName(String fullName) {
this.mFullName = fullName;
}

/**
* Generates a unique ID for the account based on the name and a random string.
* This represents the ACCTID in the exported OFX and should have a maximum of 22 alphanumeric characters
* @return Generated Unique ID string
Expand Down
16 changes: 7 additions & 9 deletions app/src/org/gnucash/android/ui/account/AccountFormFragment.java
Original file line number Diff line number Diff line change
Expand Up @@ -141,11 +141,6 @@ public class AccountFormFragment extends SherlockFragment {
*/
private Spinner mDefaulTransferAccountSpinner;

/**
* Cursor holding data set of eligible transfer accounts
*/
private Cursor mDefaultTransferAccountCursor;

/**
* Checkbox indicating if account is a placeholder account
*/
Expand Down Expand Up @@ -486,15 +481,18 @@ private void loadDefaultTransferAccoutList(){
String condition = DatabaseHelper.KEY_ROW_ID + " != " + mSelectedAccountId
+ " AND " + DatabaseHelper.KEY_PLACEHOLDER + "=0"
+ " AND " + DatabaseHelper.KEY_UID + " != '" + mAccountsDbAdapter.getGnuCashRootAccountUID() + "'";
mDefaultTransferAccountCursor = mAccountsDbAdapter.fetchAccounts(condition);
/*
Cursor holding data set of eligible transfer accounts
*/
Cursor defaultTransferAccountCursor = mAccountsDbAdapter.fetchAccountsOrderedByFullName(condition);

if (mDefaultTransferAccountCursor == null || mDefaulTransferAccountSpinner.getCount() <= 0){
if (defaultTransferAccountCursor == null || mDefaulTransferAccountSpinner.getCount() <= 0){
setDefaultTransferAccountInputsVisible(false);
}

mDefaultTransferAccountCursorAdapter = new QualifiedAccountNameCursorAdapter(getActivity(),
android.R.layout.simple_spinner_item,
mDefaultTransferAccountCursor);
defaultTransferAccountCursor);
mParentAccountCursorAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mDefaulTransferAccountSpinner.setAdapter(mParentAccountCursorAdapter);
}
Expand All @@ -512,7 +510,7 @@ private void loadParentAccountList(){
//TODO: Limit all descendants of the account to eliminate the possibility of cyclic hierarchy
}

mParentAccountCursor = mAccountsDbAdapter.fetchAccounts(condition);
mParentAccountCursor = mAccountsDbAdapter.fetchAccountsOrderedByFullName(condition);
if (mParentAccountCursor == null || mParentAccountCursor.getCount() <= 0){
final View view = getView();
view.findViewById(R.id.layout_parent_account).setVisibility(View.GONE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ public void onActivityCreated(Bundle savedInstanceState) {
getDialog().setTitle(title);

mAccountsDbAdapter = new AccountsDbAdapter(getActivity());
Cursor cursor = mAccountsDbAdapter.fetchAllRecords();
Cursor cursor = mAccountsDbAdapter.fetchAllRecordsOrderedByFullName();

SimpleCursorAdapter mCursorAdapter = new QualifiedAccountNameCursorAdapter(getActivity(),
android.R.layout.simple_spinner_item, cursor);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ private void updateTransferAccountsList(){
+ "' AND " + DatabaseHelper.KEY_PLACEHOLDER + " = 0"
+ ")";

mCursor = mAccountsDbAdapter.fetchAccounts(conditions);
mCursor = mAccountsDbAdapter.fetchAccountsOrderedByFullName(conditions);

mCursorAdapter = new QualifiedAccountNameCursorAdapter(getActivity(),
android.R.layout.simple_spinner_item, mCursor);
Expand Down
Loading

0 comments on commit fc1a97b

Please sign in to comment.