Skip to content

Commit 1b224e5

Browse files
committed
Added support for placeholder accounts (closes #113)
Placeholder accounts do not allow transactions to be added to them. They will also properly be detected during import of the GnuCash account structure Filter out placeholder accounts from double account spinner field
1 parent 84aba53 commit 1b224e5

8 files changed

Lines changed: 151 additions & 41 deletions

File tree

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,12 @@ public enum OfxAccountType {CHECKING, SAVINGS, MONEYMRKT, CREDITLINE };
9191
*/
9292
private String mParentAccountUID;
9393

94+
/**
95+
* Flag for placeholder accounts.
96+
* These accounts cannot have transactions
97+
*/
98+
private boolean mPlaceholderAccount;
99+
94100
/**
95101
* An extra key for passing the currency code (according ISO 4217) in an intent
96102
*/
@@ -315,6 +321,23 @@ public String getParentUID() {
315321

316322
}
317323

324+
/**
325+
* Returns <code>true</code> if this account is a placeholder account, <code>false</code> otherwise.
326+
* @return <code>true</code> if this account is a placeholder account, <code>false</code> otherwise
327+
*/
328+
public boolean isPlaceholderAccount(){
329+
return mPlaceholderAccount;
330+
}
331+
332+
/**
333+
* Sets the placeholder flag for this account.
334+
* Placeholder accounts cannot have transactions
335+
* @param isPlaceholder Boolean flag indicating if the account is a placeholder account or not
336+
*/
337+
public void setPlaceHolderFlag(boolean isPlaceholder){
338+
mPlaceholderAccount = isPlaceholder;
339+
}
340+
318341
/**
319342
* Maps the <code>accountType</code> to the corresponding account type.
320343
* <code>accountType</code> have corresponding values to GnuCash desktop

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

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ public long addAccount(Account account){
7474
contentValues.put(DatabaseHelper.KEY_UID, account.getUID());
7575
contentValues.put(DatabaseHelper.KEY_CURRENCY_CODE, account.getCurrency().getCurrencyCode());
7676
contentValues.put(DatabaseHelper.KEY_PARENT_ACCOUNT_UID, account.getParentUID());
77-
77+
contentValues.put(DatabaseHelper.KEY_PLACEHOLDER, account.isPlaceholderAccount() ? 1 : 0);
78+
7879
long rowId = -1;
7980
if ((rowId = getAccountID(account.getUID())) > 0){
8081
//if account already exists, then just update
@@ -178,6 +179,7 @@ public Account buildAccountInstance(Cursor c){
178179
//else the transactions end up with a different currency from the account
179180
account.setCurrency(Currency.getInstance(c.getString(DatabaseAdapter.COLUMN_CURRENCY_CODE)));
180181
account.setTransactions(mTransactionsAdapter.getAllTransactionsForAccount(uid));
182+
account.setPlaceHolderFlag(c.getInt(DatabaseAdapter.COLUMN_PLACEHOLDER) == 1);
181183
return account;
182184
}
183185

@@ -596,6 +598,35 @@ public String getFullyQualifiedAccountName(long accountId){
596598
return getFullyQualifiedAccountName(getAccountUID(accountId));
597599
}
598600

601+
/**
602+
* Returns <code>true</code> if the account with unique ID <code>accountUID</code> is a placeholder account.
603+
* @param accountUID Unique identifier of the account
604+
* @return <code>true</code> if the account is a placeholder account, <code>false</code> otherwise
605+
*/
606+
public boolean isPlaceholderAccount(String accountUID){
607+
Cursor cursor = mDb.query(DatabaseHelper.ACCOUNTS_TABLE_NAME,
608+
new String[]{DatabaseHelper.KEY_ROW_ID, DatabaseHelper.KEY_PLACEHOLDER},
609+
DatabaseHelper.KEY_UID + " = ?",
610+
new String[]{accountUID}, null, null, null);
611+
612+
if (cursor == null || !cursor.moveToFirst()){
613+
return false;
614+
}
615+
boolean isPlaceholder = cursor.getInt(cursor.getColumnIndexOrThrow(DatabaseHelper.KEY_PLACEHOLDER)) == 1;
616+
cursor.close();
617+
618+
return isPlaceholder;
619+
}
620+
621+
/**
622+
* Convenience method, resolves the account unique ID and calls {@link #isPlaceholderAccount(String)}
623+
* @param accountId Database row ID of the account
624+
* @return <code>true</code> if the account is a placeholder account, <code>false</code> otherwise
625+
*/
626+
public boolean isPlaceholderAccount(long accountId){
627+
return isPlaceholderAccount(getAccountUID(accountId));
628+
}
629+
599630
/**
600631
* Deletes all accounts and their transactions from the database
601632
*/

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,17 +42,18 @@ public abstract class DatabaseAdapter {
4242
public static final int COLUMN_NAME = 2;
4343
public static final int COLUMN_TYPE = 3;
4444

45-
//columns specific to transactions
45+
//columns indices specific to transactions
4646
public static final int COLUMN_AMOUNT = 4;
4747
public static final int COLUMN_DESCRIPTION = 5;
4848
public static final int COLUMN_TIMESTAMP = 6;
4949
public static final int COLUMN_ACCOUNT_UID = 7;
5050
public static final int COLUMN_EXPORTED = 8;
5151
public static final int COLUMN_DOUBLE_ENTRY_ACCOUNT_UID = 9;
5252

53-
//columns specific to accounts
54-
public static final int COLUMN_CURRENCY_CODE = 4;
55-
public static final int COLUMN_PARENT_ACCOUNT_UID = 5;
53+
//columns indices specific to accounts
54+
public static final int COLUMN_CURRENCY_CODE = 4;
55+
public static final int COLUMN_PARENT_ACCOUNT_UID = 5;
56+
public static final int COLUMN_PLACEHOLDER = 6;
5657

5758
/**
5859
* {@link DatabaseHelper} for creating and opening the database

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

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -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 = 2;
49+
private static final int DATABASE_VERSION = 3;
5050

5151
/**
5252
* Name of accounts table
@@ -123,7 +123,9 @@ public class DatabaseHelper extends SQLiteOpenHelper {
123123
* Flag for exported transactions in the database
124124
*/
125125
public static final String KEY_EXPORTED = "is_exported";
126-
126+
127+
public static final String KEY_PLACEHOLDER = "is_placeholder";
128+
127129
/**********************************************************************************************************
128130
//if you modify the order of the columns (i.e. the way they are created),
129131
//make sure to modify the indices in DatabaseAdapter
@@ -139,6 +141,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
139141
+ KEY_TYPE + " varchar(255) not null, "
140142
+ KEY_CURRENCY_CODE + " varchar(255) not null, "
141143
+ KEY_PARENT_ACCOUNT_UID + " varchar(255), "
144+
+ KEY_PLACEHOLDER + " tinyint default 0, "
142145
+ "UNIQUE (" + KEY_UID + ")"
143146
+ ");";
144147

@@ -204,7 +207,14 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
204207
ContentValues cv = new ContentValues();
205208
cv.put(KEY_TYPE, AccountType.CASH.toString());
206209
db.update(ACCOUNTS_TABLE_NAME, cv, null, null);
207-
210+
211+
if (oldVersion == 2 && newVersion == 3){
212+
Log.i(TAG, "Adding flag for placeholder accounts");
213+
String addPlaceHolderAccountFlagSql = "ALTER TABLE " + ACCOUNTS_TABLE_NAME +
214+
" ADD COLUMN " + KEY_PLACEHOLDER + " tinyint default 0";
215+
216+
db.execSQL(addPlaceHolderAccountFlagSql);
217+
}
208218
} else {
209219
Log.i(TAG, "Cannot downgrade database.");
210220
}

app/src/org/gnucash/android/ui/accounts/AccountsListFragment.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -575,8 +575,10 @@ public void bindView(View v, Context context, Cursor cursor) {
575575
.findViewById(R.id.transactions_summary);
576576
new AccountBalanceTask(summary, getActivity()).execute(accountId);
577577

578+
boolean isPlaceholderAccount = mAccountsDbAdapter.isPlaceholderAccount(accountId);
579+
578580
ImageButton newTransactionButton = (ImageButton) v.findViewById(R.id.btn_new_transaction);
579-
if (inSubAcccount()){
581+
if (isPlaceholderAccount){
580582
newTransactionButton.setVisibility(View.GONE);
581583
v.findViewById(R.id.vertical_line).setVisibility(View.GONE);
582584
} else {

app/src/org/gnucash/android/ui/transactions/NewTransactionFragment.java

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import java.text.ParseException;
2525
import java.util.*;
2626

27-
import android.os.Handler;
2827
import android.widget.*;
2928
import org.gnucash.android.R;
3029
import org.gnucash.android.data.Money;
@@ -90,14 +89,9 @@ public class NewTransactionFragment extends SherlockFragment implements
9089
/**
9190
* Cursor for transfer account spinner
9291
*/
93-
private Cursor mCursor;
94-
95-
/**
96-
* Holds database ID of transaction to be edited (if in edit mode)
97-
*/
98-
private long mTransactionId = 0;
99-
100-
/**
92+
private Cursor mCursor;
93+
94+
/**
10195
* Transaction to be created/updated
10296
*/
10397
private Transaction mTransaction;
@@ -202,17 +196,17 @@ public void onActivityCreated(Bundle savedInstanceState) {
202196

203197
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
204198
mUseDoubleEntry = sharedPrefs.getBoolean(getString(R.string.key_use_double_entry), false);
205-
if (mUseDoubleEntry == false){
199+
if (!mUseDoubleEntry){
206200
getView().findViewById(R.id.layout_double_entry).setVisibility(View.GONE);
207201
}
208202

209203
//updateTransferAccountsList must only be called after creating mAccountsDbAdapter
210204
mAccountsDbAdapter = new AccountsDbAdapter(getActivity());
211205
updateTransferAccountsList();
212206

213-
mTransactionId = getArguments().getLong(SELECTED_TRANSACTION_ID);
207+
long transactionId = getArguments().getLong(SELECTED_TRANSACTION_ID);
214208
mTransactionsDbAdapter = new TransactionsDbAdapter(getActivity());
215-
mTransaction = mTransactionsDbAdapter.getTransaction(mTransactionId);
209+
mTransaction = mTransactionsDbAdapter.getTransaction(transactionId);
216210

217211
setListeners();
218212
if (mTransaction == null)
@@ -330,8 +324,11 @@ private void initalizeViews() {
330324
private void updateTransferAccountsList(){
331325
long accountId = ((TransactionsActivity)getActivity()).getCurrentAccountID();
332326

333-
String conditions = "(" + DatabaseHelper.KEY_ROW_ID + " != " + accountId + ") AND " + "(" +
334-
DatabaseHelper.KEY_CURRENCY_CODE + " = '" + mAccountsDbAdapter.getCurrencyCode(accountId) + "')";
327+
String conditions = "(" + DatabaseHelper.KEY_ROW_ID + " != " + accountId + " AND "
328+
+ DatabaseHelper.KEY_CURRENCY_CODE + " = '" + mAccountsDbAdapter.getCurrencyCode(accountId)
329+
+ "' AND " + DatabaseHelper.KEY_UID + " != '" + mAccountsDbAdapter.getGnuCashRootAccountUID()
330+
+ "' AND " + DatabaseHelper.KEY_PLACEHOLDER + " = 0"
331+
+ ")";
335332

336333
mCursor = mAccountsDbAdapter.fetchAccounts(conditions);
337334

@@ -415,12 +412,12 @@ private void setSelectedTransferAccount(long accountId){
415412
for (int pos = 0; pos < mCursorAdapter.getCount(); pos++) {
416413
if (mCursorAdapter.getItemId(pos) == accountId){
417414
final int position = pos;
418-
new Handler().postDelayed(new Runnable() {
415+
mDoubleAccountSpinner.postDelayed(new Runnable() {
419416
@Override
420417
public void run() {
421418
mDoubleAccountSpinner.setSelection(position);
422419
}
423-
}, 100);
420+
}, 500);
424421
break;
425422
}
426423
}

app/src/org/gnucash/android/ui/transactions/TransactionsActivity.java

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ public class TransactionsActivity extends SherlockFragmentActivity implements
9595
TextView mSectionHeaderSubAccounts;
9696
TextView mSectionHeaderTransactions;
9797
View mSubAccountsContainer;
98+
View mTransactionsContainer;
9899

99100
private OnNavigationListener mTransactionListNavigationListener = new OnNavigationListener() {
100101

@@ -154,6 +155,7 @@ protected void onCreate(Bundle savedInstanceState) {
154155
mSectionHeaderSubAccounts = (TextView) findViewById(R.id.section_header_sub_accounts);
155156
mSectionHeaderTransactions = (TextView) findViewById(R.id.section_header_transactions);
156157
mSubAccountsContainer = findViewById(R.id.sub_accounts_container);
158+
mTransactionsContainer = findViewById(R.id.transactions_container);
157159

158160
final Intent intent = getIntent();
159161
mAccountId = intent.getLongExtra(
@@ -311,7 +313,9 @@ protected void showTransactionsList(){
311313
.beginTransaction();
312314

313315
int subAccountCount = mAccountsDbAdapter.getSubAccountCount(mAccountId);
314-
if (subAccountCount > 0){
316+
boolean isPlaceholderAccount = mAccountsDbAdapter.isPlaceholderAccount(mAccountId);
317+
318+
if (subAccountCount > 0 || isPlaceholderAccount){
315319
mSubAccountsContainer.setVisibility(View.VISIBLE);
316320
mSectionHeaderSubAccounts.setVisibility(View.VISIBLE);
317321
String subAccountSectionText = getResources().getQuantityString(R.plurals.label_sub_accounts, subAccountCount, subAccountCount);
@@ -323,16 +327,21 @@ protected void showTransactionsList(){
323327
fragmentTransaction.replace(R.id.sub_accounts_container, subAccountsListFragment, AccountsActivity.FRAGMENT_ACCOUNTS_LIST);
324328
}
325329

326-
TransactionsListFragment transactionsListFragment = new TransactionsListFragment();
327-
Bundle args = new Bundle();
328-
args.putLong(TransactionsListFragment.SELECTED_ACCOUNT_ID,
329-
mAccountId);
330-
transactionsListFragment.setArguments(args);
331-
Log.i(TAG, "Opening transactions for account id " + mAccountId);
332-
333-
fragmentTransaction.replace(R.id.transactions_container,
334-
transactionsListFragment, FRAGMENT_TRANSACTIONS_LIST);
330+
//only load transactions if it is not a placeholder account
331+
if (!isPlaceholderAccount){
332+
TransactionsListFragment transactionsListFragment = new TransactionsListFragment();
333+
Bundle args = new Bundle();
334+
args.putLong(TransactionsListFragment.SELECTED_ACCOUNT_ID,
335+
mAccountId);
336+
transactionsListFragment.setArguments(args);
337+
Log.i(TAG, "Opening transactions for account id " + mAccountId);
335338

339+
fragmentTransaction.replace(R.id.transactions_container,
340+
transactionsListFragment, FRAGMENT_TRANSACTIONS_LIST);
341+
} else {
342+
mSectionHeaderTransactions.setVisibility(View.GONE);
343+
mTransactionsContainer.setVisibility(View.GONE);
344+
}
336345
fragmentTransaction.commit();
337346
}
338347

app/src/org/gnucash/android/util/GnucashAccountXmlHandler.java

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,22 +44,45 @@ public class GnucashAccountXmlHandler extends DefaultHandler {
4444
/*
4545
* GnuCash account XML file qualified tag names. Used for matching tags
4646
*/
47-
public static final String TAG_NAME = "act:name";
48-
public static final String TAG_UID = "act:id";
49-
public static final String TAG_TYPE = "act:type";
50-
public static final String TAG_CURRENCY = "cmdty:id";
51-
public static final String TAG_PARENT_UID = "act:parent";
52-
public static final String TAG_ACCOUNT = "gnc:account";
47+
public static final String TAG_NAME = "act:name";
48+
public static final String TAG_UID = "act:id";
49+
public static final String TAG_TYPE = "act:type";
50+
public static final String TAG_CURRENCY = "cmdty:id";
5351
public static final String TAG_COMMODITY_SPACE = "cmdty:space";
52+
public static final String TAG_PARENT_UID = "act:parent";
53+
public static final String TAG_ACCOUNT = "gnc:account";
54+
public static final String TAG_SLOT_KEY = "slot:key";
55+
public static final String TAG_SLOT_VALUE = "slot:value";
56+
57+
/**
58+
* ISO 4217 currency code for "No Currency"
59+
*/
5460
private static final String NO_CURRENCY_CODE = "XXX";
5561

62+
/**
63+
* Tag for logging
64+
*/
5665
private static final String LOG_TAG = "GnuCashAccountImporter";
5766

67+
/**
68+
* Value for placeholder slots in GnuCash account structure file
69+
*/
70+
private static final String PLACEHOLDER_KEY = "placeholder";
71+
5872

5973
AccountsDbAdapter mDatabaseAdapter;
74+
75+
/**
76+
* StringBuilder for accumulating characters between XML tags
77+
*/
6078
StringBuilder mContent;
79+
80+
/**
81+
* Reference to account which is built when each account tag is parsed in the XML file
82+
*/
6183
Account mAccount;
6284

85+
boolean mInPlaceHolderSlot = false;
6386
boolean mISO4217Currency = false;
6487

6588
public GnucashAccountXmlHandler(Context context) {
@@ -116,6 +139,20 @@ public void endElement(String uri, String localName, String qualifiedName) throw
116139
mISO4217Currency = false;
117140
}
118141

142+
if (qualifiedName.equalsIgnoreCase(TAG_SLOT_KEY)){
143+
if (characterString.equals(PLACEHOLDER_KEY)){
144+
mInPlaceHolderSlot = true;
145+
}
146+
}
147+
148+
if (qualifiedName.equalsIgnoreCase(TAG_SLOT_VALUE)){
149+
if (mInPlaceHolderSlot){
150+
if (characterString.equals("true")){
151+
mAccount.setPlaceHolderFlag(true);
152+
};
153+
mInPlaceHolderSlot = false;
154+
}
155+
}
119156
//reset the accumulated characters
120157
mContent.setLength(0);
121158
}

0 commit comments

Comments
 (0)