Skip to content

Commit fc1a97b

Browse files
committed
Lists of accounts are now sorted by the fully qualified account name
Closes #129
1 parent 9162bd0 commit fc1a97b

File tree

10 files changed

+212
-50
lines changed

10 files changed

+212
-50
lines changed

app/src/org/gnucash/android/db/AccountsDbAdapter.java

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -69,28 +69,35 @@ public void close() {
6969
*/
7070
public long addAccount(Account account){
7171
ContentValues contentValues = new ContentValues();
72-
contentValues.put(DatabaseHelper.KEY_NAME, account.getName());
73-
contentValues.put(DatabaseHelper.KEY_TYPE, account.getAccountType().name());
74-
contentValues.put(DatabaseHelper.KEY_UID, account.getUID());
72+
contentValues.put(DatabaseHelper.KEY_NAME, account.getName());
73+
contentValues.put(DatabaseHelper.KEY_TYPE, account.getAccountType().name());
74+
contentValues.put(DatabaseHelper.KEY_UID, account.getUID());
7575
contentValues.put(DatabaseHelper.KEY_CURRENCY_CODE, account.getCurrency().getCurrencyCode());
76-
contentValues.put(DatabaseHelper.KEY_PARENT_ACCOUNT_UID, account.getParentUID());
77-
contentValues.put(DatabaseHelper.KEY_DEFAULT_TRANSFER_ACCOUNT_UID, account.getDefaultTransferAccountUID());
78-
contentValues.put(DatabaseHelper.KEY_PLACEHOLDER, account.isPlaceholderAccount() ? 1 : 0);
79-
contentValues.put(DatabaseHelper.KEY_COLOR_CODE, account.getColorHexCode());
80-
contentValues.put(DatabaseHelper.KEY_FAVORITE, account.isFavorite() ? 1 : 0);
76+
contentValues.put(DatabaseHelper.KEY_PLACEHOLDER, account.isPlaceholderAccount() ? 1 : 0);
77+
contentValues.put(DatabaseHelper.KEY_COLOR_CODE, account.getColorHexCode());
78+
contentValues.put(DatabaseHelper.KEY_FAVORITE, account.isFavorite() ? 1 : 0);
79+
contentValues.put(DatabaseHelper.KEY_FULL_NAME, account.getFullName());
80+
contentValues.put(DatabaseHelper.KEY_PARENT_ACCOUNT_UID, account.getParentUID());
81+
contentValues.put(DatabaseHelper.KEY_DEFAULT_TRANSFER_ACCOUNT_UID, account.getDefaultTransferAccountUID());
82+
8183
long rowId = -1;
8284
if ((rowId = getAccountID(account.getUID())) > 0){
8385
//if account already exists, then just update
8486
Log.d(TAG, "Updating existing account");
85-
mDb.update(DatabaseHelper.ACCOUNTS_TABLE_NAME, contentValues,
86-
DatabaseHelper.KEY_ROW_ID + " = " + rowId, null);
87+
int rowsAffected = mDb.update(DatabaseHelper.ACCOUNTS_TABLE_NAME, contentValues,
88+
DatabaseHelper.KEY_ROW_ID + " = " + rowId, null);
89+
if (rowsAffected == 1){
90+
updateAccount(rowId, DatabaseHelper.KEY_FULL_NAME, getFullyQualifiedAccountName(rowId));
91+
}
8792
} else {
8893
Log.d(TAG, "Adding new account to db");
8994
rowId = mDb.insert(DatabaseHelper.ACCOUNTS_TABLE_NAME, null, contentValues);
9095
}
9196

9297
//now add transactions if there are any
9398
if (rowId > 0){
99+
//update the fully qualified account name
100+
updateAccount(rowId, DatabaseHelper.KEY_FULL_NAME, getFullyQualifiedAccountName(rowId));
94101
for (Transaction t : account.getTransactions()) {
95102
mTransactionsAdapter.addTransaction(t);
96103
}
@@ -237,6 +244,7 @@ public Account buildAccountInstance(Cursor c){
237244
account.setDefaultTransferAccountUID(c.getString(DatabaseAdapter.COLUMN_DEFAULT_TRANSFER_ACCOUNT_UID));
238245
account.setColorCode(c.getString(DatabaseAdapter.COLUMN_COLOR_CODE));
239246
account.setFavorite(c.getInt(DatabaseAdapter.COLUMN_FAVORITE) == 1);
247+
account.setFullName(c.getString(DatabaseAdapter.COLUMN_FULL_NAME));
240248
return account;
241249
}
242250

@@ -441,6 +449,21 @@ public Cursor fetchAllRecords(){
441449
return cursor;
442450
}
443451

452+
/**
453+
* Returns a cursor to all account records in the database ordered by full name.
454+
* GnuCash ROOT accounts are ignored
455+
* @return {@link Cursor} to all account records
456+
*/
457+
public Cursor fetchAllRecordsOrderedByFullName(){
458+
Log.v(TAG, "Fetching all accounts from db");
459+
String selection = DatabaseHelper.KEY_TYPE + " != ?" ;
460+
return mDb.query(DatabaseHelper.ACCOUNTS_TABLE_NAME,
461+
null,
462+
selection,
463+
new String[]{AccountType.ROOT.name()},
464+
null, null,
465+
DatabaseHelper.KEY_FULL_NAME + " ASC");
466+
}
444467

445468
@Override
446469
public Cursor fetchRecord(long rowId) {
@@ -471,7 +494,19 @@ public Cursor fetchAccounts(String condition){
471494
DatabaseHelper.KEY_NAME + " ASC");
472495
return cursor;
473496
}
474-
497+
498+
/**
499+
* Returns a Cursor set of accounts which fulfill <code>condition</code>
500+
* <p>This method returns the accounts list sorted by the full account name</p>
501+
* @param condition SQL WHERE statement without the 'WHERE' itself
502+
* @return Cursor set of accounts which fulfill <code>condition</code>
503+
*/
504+
public Cursor fetchAccountsOrderedByFullName(String condition){
505+
Log.v(TAG, "Fetching all accounts from db where " + condition);
506+
return mDb.query(DatabaseHelper.ACCOUNTS_TABLE_NAME,
507+
null, condition, null, null, null,
508+
DatabaseHelper.KEY_FULL_NAME + " ASC");
509+
}
475510
/**
476511
* Returns the balance of an account while taking sub-accounts into consideration
477512
* @return Account Balance of an account including sub-accounts

app/src/org/gnucash/android/db/DatabaseAdapter.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ public abstract class DatabaseAdapter {
5858
public static final int COLUMN_DEFAULT_TRANSFER_ACCOUNT_UID = 7;
5959
public static final int COLUMN_COLOR_CODE = 8;
6060
public static final int COLUMN_FAVORITE = 9;
61+
public static final int COLUMN_FULL_NAME = 10;
6162

6263
/**
6364
* {@link DatabaseHelper} for creating and opening the database

app/src/org/gnucash/android/db/DatabaseHelper.java

Lines changed: 111 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@
1616

1717
package org.gnucash.android.db;
1818

19-
import org.gnucash.android.model.Account.AccountType;
20-
2119
import android.content.ContentValues;
2220
import android.content.Context;
21+
import android.database.Cursor;
2322
import android.database.sqlite.SQLiteDatabase;
2423
import android.database.sqlite.SQLiteOpenHelper;
2524
import android.util.Log;
25+
import org.gnucash.android.model.Account.AccountType;
2626

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

5151
/**
5252
* Name of accounts table
@@ -70,7 +70,13 @@ public class DatabaseHelper extends SQLiteOpenHelper {
7070
* Currently used by all tables
7171
*/
7272
public static final String KEY_NAME = "name";
73-
73+
74+
/**
75+
* Key for fully qualified name of the account column
76+
* This name includes the parent hierarchy
77+
*/
78+
public static final String KEY_FULL_NAME = "full_name";
79+
7480
/**
7581
* Unique Identifier.
7682
*/
@@ -170,6 +176,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
170176
+ KEY_DEFAULT_TRANSFER_ACCOUNT_UID + " varchar(255), "
171177
+ KEY_COLOR_CODE + " varchar(255), "
172178
+ KEY_FAVORITE + " tinyint default 0, "
179+
+ KEY_FULL_NAME + " varchar(255), "
173180
+ "UNIQUE (" + KEY_UID + ")"
174181
+ ");";
175182

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

276283
oldVersion = 5;
277284
}
285+
286+
if (oldVersion == 5 && newVersion >= 6){
287+
Log.i(TAG, "Upgrading database to version 6");
288+
String addFullAccountNameQuery = " ALTER TABLE " + ACCOUNTS_TABLE_NAME
289+
+ " ADD COLUMN " + KEY_FULL_NAME + " varchar(255) ";
290+
db.execSQL(addFullAccountNameQuery);
291+
292+
//update all existing accounts with their fully qualified name
293+
Cursor cursor = db.query(DatabaseHelper.ACCOUNTS_TABLE_NAME,
294+
new String[]{KEY_ROW_ID, KEY_UID},
295+
null, null, null, null, null);
296+
while(cursor != null && cursor.moveToNext()){
297+
String uid = cursor.getString(cursor.getColumnIndexOrThrow(KEY_UID));
298+
String fullName = getFullyQualifiedAccountName(db, uid);
299+
300+
if (fullName == null)
301+
continue;
302+
303+
ContentValues contentValues = new ContentValues();
304+
contentValues.put(KEY_FULL_NAME, fullName);
305+
306+
long id = cursor.getLong(cursor.getColumnIndexOrThrow(KEY_ROW_ID));
307+
db.update(ACCOUNTS_TABLE_NAME, contentValues, KEY_ROW_ID + " = " + id, null);
308+
}
309+
310+
if (cursor != null) {
311+
cursor.close();
312+
}
313+
314+
oldVersion = 6;
315+
}
278316
}
279317

280318
if (oldVersion != newVersion) {
281-
Log.i(TAG, "Upgrade for the database failed. The Database is currently at version " + oldVersion);
319+
Log.w(TAG, "Upgrade for the database failed. The Database is currently at version " + oldVersion);
282320
}
283321
}
322+
323+
/**
324+
* Performs same functtion as {@link org.gnucash.android.db.AccountsDbAdapter#getFullyQualifiedAccountName(String)}
325+
* <p>This method is only necessary because we cannot open the database again (by instantiating {@link AccountsDbAdapter}
326+
* while it is locked for upgrades. So we reimplement the method here.</p>
327+
* @param db SQLite database
328+
* @param accountUID Unique ID of account whose fully qualified name is to be determined
329+
* @return Fully qualified (colon-sepaated) account name
330+
* @see org.gnucash.android.db.AccountsDbAdapter#getFullyQualifiedAccountName(String)
331+
*/
332+
private String getFullyQualifiedAccountName(SQLiteDatabase db, String accountUID){
333+
//get the parent account UID of the account
334+
Cursor cursor = db.query(DatabaseHelper.ACCOUNTS_TABLE_NAME,
335+
new String[] {DatabaseHelper.KEY_ROW_ID, DatabaseHelper.KEY_PARENT_ACCOUNT_UID},
336+
DatabaseHelper.KEY_UID + " = ?",
337+
new String[]{accountUID},
338+
null, null, null, null);
339+
340+
String parentAccountUID = null;
341+
if (cursor != null && cursor.moveToFirst()){
342+
parentAccountUID = cursor.getString(cursor.getColumnIndexOrThrow(DatabaseHelper.KEY_PARENT_ACCOUNT_UID));
343+
cursor.close();
344+
}
345+
346+
//get the name of the account
347+
cursor = db.query(DatabaseHelper.ACCOUNTS_TABLE_NAME,
348+
new String[]{DatabaseHelper.KEY_ROW_ID, DatabaseHelper.KEY_NAME},
349+
DatabaseHelper.KEY_UID + " = '" + accountUID + "'",
350+
null, null, null, null);
351+
352+
String accountName = null;
353+
if (cursor != null && cursor.moveToFirst()){
354+
accountName = cursor.getString(cursor.getColumnIndexOrThrow(DatabaseHelper.KEY_NAME));
355+
cursor.close();
356+
}
357+
358+
String gnucashRootAccountUID = getGnuCashRootAccountUID(db);
359+
if (parentAccountUID == null || accountName == null
360+
|| parentAccountUID.equalsIgnoreCase(gnucashRootAccountUID)){
361+
return accountName;
362+
}
363+
364+
String parentAccountName = getFullyQualifiedAccountName(db, parentAccountUID);
365+
366+
return parentAccountName + AccountsDbAdapter.ACCOUNT_NAME_SEPARATOR + accountName;
367+
}
368+
369+
/**
370+
* Returns the GnuCash ROOT account UID.
371+
* <p>In GnuCash desktop account structure, there is a root account (which is not visible in the UI) from which
372+
* other top level accounts derive. GnuCash Android does not have this ROOT account by default unless the account
373+
* structure was imported from GnuCash for desktop. Hence this method also returns <code>null</code> as an
374+
* acceptable result.</p>
375+
* <p><b>Note:</b> NULL is an acceptable response, be sure to check for it</p>
376+
* @return Unique ID of the GnuCash root account.
377+
*/
378+
private String getGnuCashRootAccountUID(SQLiteDatabase db){
379+
String condition = DatabaseHelper.KEY_TYPE + "= '" + AccountType.ROOT.name() + "'";
380+
Cursor cursor = db.query(DatabaseHelper.ACCOUNTS_TABLE_NAME,
381+
null, condition, null, null, null,
382+
DatabaseHelper.KEY_NAME + " ASC");
383+
String rootUID = null;
384+
if (cursor != null && cursor.moveToFirst()){
385+
rootUID = cursor.getString(DatabaseAdapter.COLUMN_UID);
386+
cursor.close();
387+
}
388+
return rootUID;
389+
}
284390
}

app/src/org/gnucash/android/model/Account.java

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,13 @@ public enum OfxAccountType {CHECKING, SAVINGS, MONEYMRKT, CREDITLINE }
117117
* Name of this account
118118
*/
119119
private String mName;
120-
120+
121+
/**
122+
* Fully qualified name of this account including the parent hierarchy.
123+
* On instantiation of an account, the full name is set to the name by default
124+
*/
125+
private String mFullName;
126+
121127
/**
122128
* Currency used by transactions in this account
123129
*/
@@ -179,8 +185,9 @@ public enum OfxAccountType {CHECKING, SAVINGS, MONEYMRKT, CREDITLINE }
179185
*/
180186
public Account(String name) {
181187
setName(name);
182-
this.mUID = generateUID();
183-
this.mCurrency = Currency.getInstance(Money.DEFAULT_CURRENCY_CODE);
188+
this.mFullName = mName;
189+
this.mUID = generateUID();
190+
this.mCurrency = Currency.getInstance(Money.DEFAULT_CURRENCY_CODE);
184191
}
185192

186193
/**
@@ -190,8 +197,9 @@ public Account(String name) {
190197
*/
191198
public Account(String name, Currency currency){
192199
setName(name);
193-
this.mUID = generateUID();
194-
this.mCurrency = currency;
200+
this.mFullName = mName;
201+
this.mUID = generateUID();
202+
this.mCurrency = currency;
195203
}
196204

197205
/**
@@ -209,8 +217,25 @@ public void setName(String name) {
209217
public String getName() {
210218
return mName;
211219
}
212-
213-
/**
220+
221+
/**
222+
* Returns the full name of this account.
223+
* The full name is the full account hierarchy name
224+
* @return Fully qualified name of the account
225+
*/
226+
public String getFullName() {
227+
return mFullName;
228+
}
229+
230+
/**
231+
* Sets the fully qualified name of the account
232+
* @param fullName Fully qualified account name
233+
*/
234+
public void setFullName(String fullName) {
235+
this.mFullName = fullName;
236+
}
237+
238+
/**
214239
* Generates a unique ID for the account based on the name and a random string.
215240
* This represents the ACCTID in the exported OFX and should have a maximum of 22 alphanumeric characters
216241
* @return Generated Unique ID string

app/src/org/gnucash/android/ui/account/AccountFormFragment.java

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -141,11 +141,6 @@ public class AccountFormFragment extends SherlockFragment {
141141
*/
142142
private Spinner mDefaulTransferAccountSpinner;
143143

144-
/**
145-
* Cursor holding data set of eligible transfer accounts
146-
*/
147-
private Cursor mDefaultTransferAccountCursor;
148-
149144
/**
150145
* Checkbox indicating if account is a placeholder account
151146
*/
@@ -486,15 +481,18 @@ private void loadDefaultTransferAccoutList(){
486481
String condition = DatabaseHelper.KEY_ROW_ID + " != " + mSelectedAccountId
487482
+ " AND " + DatabaseHelper.KEY_PLACEHOLDER + "=0"
488483
+ " AND " + DatabaseHelper.KEY_UID + " != '" + mAccountsDbAdapter.getGnuCashRootAccountUID() + "'";
489-
mDefaultTransferAccountCursor = mAccountsDbAdapter.fetchAccounts(condition);
484+
/*
485+
Cursor holding data set of eligible transfer accounts
486+
*/
487+
Cursor defaultTransferAccountCursor = mAccountsDbAdapter.fetchAccountsOrderedByFullName(condition);
490488

491-
if (mDefaultTransferAccountCursor == null || mDefaulTransferAccountSpinner.getCount() <= 0){
489+
if (defaultTransferAccountCursor == null || mDefaulTransferAccountSpinner.getCount() <= 0){
492490
setDefaultTransferAccountInputsVisible(false);
493491
}
494492

495493
mDefaultTransferAccountCursorAdapter = new QualifiedAccountNameCursorAdapter(getActivity(),
496494
android.R.layout.simple_spinner_item,
497-
mDefaultTransferAccountCursor);
495+
defaultTransferAccountCursor);
498496
mParentAccountCursorAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
499497
mDefaulTransferAccountSpinner.setAdapter(mParentAccountCursorAdapter);
500498
}
@@ -512,7 +510,7 @@ private void loadParentAccountList(){
512510
//TODO: Limit all descendants of the account to eliminate the possibility of cyclic hierarchy
513511
}
514512

515-
mParentAccountCursor = mAccountsDbAdapter.fetchAccounts(condition);
513+
mParentAccountCursor = mAccountsDbAdapter.fetchAccountsOrderedByFullName(condition);
516514
if (mParentAccountCursor == null || mParentAccountCursor.getCount() <= 0){
517515
final View view = getView();
518516
view.findViewById(R.id.layout_parent_account).setVisibility(View.GONE);

app/src/org/gnucash/android/ui/transaction/BulkMoveDialogFragment.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ public void onActivityCreated(Bundle savedInstanceState) {
102102
getDialog().setTitle(title);
103103

104104
mAccountsDbAdapter = new AccountsDbAdapter(getActivity());
105-
Cursor cursor = mAccountsDbAdapter.fetchAllRecords();
105+
Cursor cursor = mAccountsDbAdapter.fetchAllRecordsOrderedByFullName();
106106

107107
SimpleCursorAdapter mCursorAdapter = new QualifiedAccountNameCursorAdapter(getActivity(),
108108
android.R.layout.simple_spinner_item, cursor);

app/src/org/gnucash/android/ui/transaction/TransactionFormFragment.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,7 @@ private void updateTransferAccountsList(){
420420
+ "' AND " + DatabaseHelper.KEY_PLACEHOLDER + " = 0"
421421
+ ")";
422422

423-
mCursor = mAccountsDbAdapter.fetchAccounts(conditions);
423+
mCursor = mAccountsDbAdapter.fetchAccountsOrderedByFullName(conditions);
424424

425425
mCursorAdapter = new QualifiedAccountNameCursorAdapter(getActivity(),
426426
android.R.layout.simple_spinner_item, mCursor);

0 commit comments

Comments
 (0)