diff --git a/app/src/main/java/org/gnucash/android/db/adapter/AccountsDbAdapter.java b/app/src/main/java/org/gnucash/android/db/adapter/AccountsDbAdapter.java index e19138306..c5a950e1a 100644 --- a/app/src/main/java/org/gnucash/android/db/adapter/AccountsDbAdapter.java +++ b/app/src/main/java/org/gnucash/android/db/adapter/AccountsDbAdapter.java @@ -74,7 +74,7 @@ public class AccountsDbAdapter extends DatabaseAdapter { /** * Transactions database adapter for manipulating transactions associated with accounts */ - private final TransactionsDbAdapter mTransactionsAdapter; + private final TransactionsDbAdapter mTransactionsDbAdapter; /** * Commodities database adapter for commodity manipulation @@ -101,7 +101,7 @@ public AccountsDbAdapter(SQLiteDatabase db, TransactionsDbAdapter transactionsDb AccountEntry.COLUMN_PARENT_ACCOUNT_UID, AccountEntry.COLUMN_DEFAULT_TRANSFER_ACCOUNT_UID }); - mTransactionsAdapter = transactionsDbAdapter; + mTransactionsDbAdapter = transactionsDbAdapter; mCommoditiesDbAdapter = new CommoditiesDbAdapter(db); } @@ -129,7 +129,7 @@ public AccountsDbAdapter(SQLiteDatabase db){ AccountEntry.COLUMN_DEFAULT_TRANSFER_ACCOUNT_UID }); - mTransactionsAdapter = new TransactionsDbAdapter(db, new SplitsDbAdapter(db)); + mTransactionsDbAdapter = new TransactionsDbAdapter(db, new SplitsDbAdapter(db)); mCommoditiesDbAdapter = new CommoditiesDbAdapter(db); } @@ -150,7 +150,7 @@ public static AccountsDbAdapter getInstance(){ public void addRecord(@NonNull Account account, UpdateMethod updateMethod){ Log.d(LOG_TAG, "Replace account to db"); //in-case the account already existed, we want to update the templates based on it as well - List templateTransactions = mTransactionsAdapter.getScheduledTransactionsForAccount(account.getUID()); + List templateTransactions = mTransactionsDbAdapter.getScheduledTransactionsForAccount(account.getUID()); super.addRecord(account, updateMethod); String accountUID = account.getUID(); //now add transactions if there are any @@ -159,10 +159,10 @@ public void addRecord(@NonNull Account account, UpdateMethod updateMethod){ updateRecord(accountUID, AccountEntry.COLUMN_FULL_NAME, getFullyQualifiedAccountName(accountUID)); for (Transaction t : account.getTransactions()) { t.setCommodity(account.getCommodity()); - mTransactionsAdapter.addRecord(t, updateMethod); + mTransactionsDbAdapter.addRecord(t, updateMethod); } for (Transaction transaction : templateTransactions) { - mTransactionsAdapter.addRecord(transaction, UpdateMethod.update); + mTransactionsDbAdapter.addRecord(transaction, UpdateMethod.update); } } } @@ -187,12 +187,12 @@ public long bulkAddRecords(@NonNull List accountList, UpdateMethod upda List transactionList = new ArrayList<>(accountList.size()*2); for (Account account : accountList) { transactionList.addAll(account.getTransactions()); - transactionList.addAll(mTransactionsAdapter.getScheduledTransactionsForAccount(account.getUID())); + transactionList.addAll(mTransactionsDbAdapter.getScheduledTransactionsForAccount(account.getUID())); } long nRow = super.bulkAddRecords(accountList, updateMethod); if (nRow > 0 && !transactionList.isEmpty()){ - mTransactionsAdapter.bulkAddRecords(transactionList, updateMethod); + mTransactionsDbAdapter.bulkAddRecords(transactionList, updateMethod); } return nRow; } @@ -374,7 +374,7 @@ public boolean recursiveDeleteAccount(long accountId){ try { descendantAccountUIDs.add(accountUID); //add account to descendants list just for convenience for (String descendantAccountUID : descendantAccountUIDs) { - mTransactionsAdapter.deleteTransactionsForAccount(descendantAccountUID); + mTransactionsDbAdapter.deleteTransactionsForAccount(descendantAccountUID); } String accountUIDList = "'" + TextUtils.join("','", descendantAccountUIDs) + "'"; @@ -413,7 +413,7 @@ public boolean recursiveDeleteAccount(long accountId){ @Override public Account buildModelInstance(@NonNull final Cursor c){ Account account = buildSimpleAccountInstance(c); - account.setTransactions(mTransactionsAdapter.getAllTransactionsForAccount(account.getUID())); + account.setTransactions(mTransactionsDbAdapter.getAllTransactionsForAccount(account.getUID())); return account; } @@ -766,7 +766,7 @@ public Cursor fetchAccountsOrderedByFavoriteAndFullName(String where, String[] w * @return Account Balance of an account including sub-accounts */ public Money getAccountBalance(String accountUID){ - return computeBalance(accountUID, -1, -1); + return getAccountBalance(accountUID, -1, -1); } /** @@ -801,7 +801,7 @@ public Money getAccountBalance(AccountType accountType, long startTimestamp, lon String currencyCode = GnuCashApplication.getDefaultCurrencyCode(); Log.d(LOG_TAG, "all account list : " + accountUidList.size()); - SplitsDbAdapter splitsDbAdapter = mTransactionsAdapter.getSplitDbAdapter(); + SplitsDbAdapter splitsDbAdapter = mTransactionsDbAdapter.getSplitDbAdapter(); return (startTimestamp == -1 && endTimestamp == -1) ? splitsDbAdapter.computeSplitBalance(accountUidList, currencyCode, hasDebitNormalBalance) @@ -825,7 +825,7 @@ public Money getAccountBalance(List accountTypes, long start, long private Money computeBalance(String accountUID, long startTimestamp, long endTimestamp) { Log.d(LOG_TAG, "Computing account balance for account ID " + accountUID); - String currencyCode = mTransactionsAdapter.getAccountCurrencyCode(accountUID); + String currencyCode = mTransactionsDbAdapter.getAccountCurrencyCode(accountUID); boolean hasDebitNormalBalance = getAccountType(accountUID).hasDebitNormalBalance(); List accountsList = getDescendantAccountUIDs(accountUID, @@ -834,7 +834,7 @@ private Money computeBalance(String accountUID, long startTimestamp, long endTim accountsList.add(0, accountUID); Log.d(LOG_TAG, "all account list : " + accountsList.size()); - SplitsDbAdapter splitsDbAdapter = mTransactionsAdapter.getSplitDbAdapter(); + SplitsDbAdapter splitsDbAdapter = mTransactionsDbAdapter.getSplitDbAdapter(); return (startTimestamp == -1 && endTimestamp == -1) ? splitsDbAdapter.computeSplitBalance(accountsList, currencyCode, hasDebitNormalBalance) : splitsDbAdapter.computeSplitBalance(accountsList, currencyCode, hasDebitNormalBalance, startTimestamp, endTimestamp); @@ -858,7 +858,7 @@ public Money getAccountsBalance(@NonNull List accountUIDList, long star boolean hasDebitNormalBalance = getAccountType(accountUIDList.get(0)).hasDebitNormalBalance(); - SplitsDbAdapter splitsDbAdapter = mTransactionsAdapter.getSplitDbAdapter(); + SplitsDbAdapter splitsDbAdapter = mTransactionsDbAdapter.getSplitDbAdapter(); Money splitSum = (startTimestamp == -1 && endTimestamp == -1) ? splitsDbAdapter.computeSplitBalance(accountUIDList, currencyCode, hasDebitNormalBalance) : splitsDbAdapter.computeSplitBalance(accountUIDList, currencyCode, hasDebitNormalBalance, startTimestamp, endTimestamp); @@ -1145,7 +1145,7 @@ public List getAllOpeningBalanceTransactions(){ Cursor cursor = fetchAccounts(null, null, null); List openingTransactions = new ArrayList<>(); try { - SplitsDbAdapter splitsDbAdapter = mTransactionsAdapter.getSplitDbAdapter(); + SplitsDbAdapter splitsDbAdapter = mTransactionsDbAdapter.getSplitDbAdapter(); while (cursor.moveToNext()) { long id = cursor.getLong(cursor.getColumnIndexOrThrow(AccountEntry._ID)); String accountUID = getUID(id); diff --git a/app/src/main/java/org/gnucash/android/db/adapter/DatabaseAdapter.java b/app/src/main/java/org/gnucash/android/db/adapter/DatabaseAdapter.java index ccaa4e18c..ca958c29e 100644 --- a/app/src/main/java/org/gnucash/android/db/adapter/DatabaseAdapter.java +++ b/app/src/main/java/org/gnucash/android/db/adapter/DatabaseAdapter.java @@ -531,17 +531,26 @@ public int deleteAllRecords(){ * @throws IllegalArgumentException if the GUID does not exist in the database */ public long getID(@NonNull String uid){ + Cursor cursor = mDb.query(mTableName, - new String[] {DatabaseSchema.CommonColumns._ID}, - DatabaseSchema.CommonColumns.COLUMN_UID + " = ?", - new String[]{uid}, - null, null, null); + new String[]{DatabaseSchema.CommonColumns._ID}, + DatabaseSchema.CommonColumns.COLUMN_UID + " = ?", + new String[]{uid}, + null, null, null); + long result = -1; + try{ if (cursor.moveToFirst()) { result = cursor.getLong(cursor.getColumnIndexOrThrow(DatabaseSchema.CommonColumns._ID)); } else { - throw new IllegalArgumentException(mTableName + " with GUID " + uid + " does not exist in the db"); + throw new IllegalArgumentException("UID (" + + uid + + ") does not exist in Table (" + + mTableName + + ") of db (" + + mDb.getPath() + + ")"); } } finally { cursor.close(); diff --git a/app/src/main/java/org/gnucash/android/db/adapter/SplitsDbAdapter.java b/app/src/main/java/org/gnucash/android/db/adapter/SplitsDbAdapter.java index 9750504de..c74a32c51 100644 --- a/app/src/main/java/org/gnucash/android/db/adapter/SplitsDbAdapter.java +++ b/app/src/main/java/org/gnucash/android/db/adapter/SplitsDbAdapter.java @@ -158,8 +158,15 @@ public Split buildModelInstance(@NonNull final Cursor cursor){ * @param hasDebitNormalBalance Does the final balance has normal debit credit meaning * @return Balance of the splits for this account */ - public Money computeSplitBalance(List accountUIDList, String currencyCode, boolean hasDebitNormalBalance){ - return calculateSplitBalance(accountUIDList, currencyCode, hasDebitNormalBalance, -1, -1); + public Money computeSplitBalance(List accountUIDList, + String currencyCode, + boolean hasDebitNormalBalance) { + + return computeSplitBalance(accountUIDList, + currencyCode, + hasDebitNormalBalance, + -1, + -1); } /** @@ -173,87 +180,152 @@ public Money computeSplitBalance(List accountUIDList, String currencyCod * @param endTimestamp the end timestamp of the time range * @return Balance of the splits for this account within the specified time range */ - public Money computeSplitBalance(List accountUIDList, String currencyCode, boolean hasDebitNormalBalance, - long startTimestamp, long endTimestamp){ - return calculateSplitBalance(accountUIDList, currencyCode, hasDebitNormalBalance, startTimestamp, endTimestamp); + public Money computeSplitBalance(List accountUIDList, + String currencyCode, + boolean hasDebitNormalBalance, + long startTimestamp, + long endTimestamp) { + + return calculateSplitBalance(accountUIDList, + currencyCode, + hasDebitNormalBalance, + startTimestamp, + endTimestamp); } - private Money calculateSplitBalance(List accountUIDList, String currencyCode, boolean hasDebitNormalBalance, - long startTimestamp, long endTimestamp){ - if (accountUIDList.size() == 0){ - return new Money("0", currencyCode); + private Money calculateSplitBalance(List accountUIDList, + String currencyCode, + boolean hasDebitNormalBalance, + long startTimestamp, + long endTimestamp) { + + if (accountUIDList.size() == 0) { + return new Money("0", + currencyCode); } - Cursor cursor; + Cursor cursor; String[] selectionArgs = null; - String selection = DatabaseSchema.AccountEntry.TABLE_NAME + "_" + DatabaseSchema.CommonColumns.COLUMN_UID + " in ( '" + TextUtils.join("' , '", accountUIDList) + "' ) AND " + - TransactionEntry.TABLE_NAME + "_" + TransactionEntry.COLUMN_TEMPLATE + " = 0"; + String selection = DatabaseSchema.AccountEntry.TABLE_NAME + + "_" + + DatabaseSchema.CommonColumns.COLUMN_UID + + " in ( '" + + TextUtils.join("' , '", + accountUIDList) + + "' ) AND " + + TransactionEntry.TABLE_NAME + + "_" + + TransactionEntry.COLUMN_TEMPLATE + + " = 0"; if (startTimestamp != -1 && endTimestamp != -1) { + selection += " AND " + TransactionEntry.TABLE_NAME + "_" + TransactionEntry.COLUMN_TIMESTAMP + " BETWEEN ? AND ? "; - selectionArgs = new String[]{String.valueOf(startTimestamp), String.valueOf(endTimestamp)}; + selectionArgs = new String[]{String.valueOf(startTimestamp), + String.valueOf(endTimestamp)}; + } else if (startTimestamp == -1 && endTimestamp != -1) { + selection += " AND " + TransactionEntry.TABLE_NAME + "_" + TransactionEntry.COLUMN_TIMESTAMP + " <= ?"; selectionArgs = new String[]{String.valueOf(endTimestamp)}; + } else if (startTimestamp != -1/* && endTimestamp == -1*/) { + selection += " AND " + TransactionEntry.TABLE_NAME + "_" + TransactionEntry.COLUMN_TIMESTAMP + " >= ?"; selectionArgs = new String[]{String.valueOf(startTimestamp)}; } cursor = mDb.query("trans_split_acct", - new String[]{"TOTAL ( CASE WHEN " + SplitEntry.TABLE_NAME + "_" + SplitEntry.COLUMN_TYPE + " = 'DEBIT' THEN " + - SplitEntry.TABLE_NAME + "_" + SplitEntry.COLUMN_QUANTITY_NUM + " ELSE - " + - SplitEntry.TABLE_NAME + "_" + SplitEntry.COLUMN_QUANTITY_NUM + " END )", - SplitEntry.TABLE_NAME + "_" + SplitEntry.COLUMN_QUANTITY_DENOM, - DatabaseSchema.AccountEntry.TABLE_NAME + "_" + DatabaseSchema.AccountEntry.COLUMN_CURRENCY}, - selection, selectionArgs, DatabaseSchema.AccountEntry.TABLE_NAME + "_" + DatabaseSchema.AccountEntry.COLUMN_CURRENCY, null, null); + new String[]{"TOTAL ( CASE WHEN " + + SplitEntry.TABLE_NAME + + "_" + + SplitEntry.COLUMN_TYPE + + " = 'DEBIT' THEN " + + SplitEntry.TABLE_NAME + + "_" + + SplitEntry.COLUMN_QUANTITY_NUM + + " ELSE - " + + SplitEntry.TABLE_NAME + + "_" + + SplitEntry.COLUMN_QUANTITY_NUM + + " END )", + SplitEntry.TABLE_NAME + "_" + SplitEntry.COLUMN_QUANTITY_DENOM, + DatabaseSchema.AccountEntry.TABLE_NAME + + "_" + + DatabaseSchema.AccountEntry.COLUMN_CURRENCY}, + selection, + selectionArgs, + DatabaseSchema.AccountEntry.TABLE_NAME + "_" + DatabaseSchema.AccountEntry.COLUMN_CURRENCY, + null, + null); try { - Money total = Money.createZeroInstance(currencyCode); + Money total = Money.createZeroInstance(currencyCode); CommoditiesDbAdapter commoditiesDbAdapter = null; - PricesDbAdapter pricesDbAdapter = null; - Commodity commodity = null; - String currencyUID = null; + PricesDbAdapter pricesDbAdapter = null; + Commodity commodity = null; + String currencyUID = null; + while (cursor.moveToNext()) { - long amount_num = cursor.getLong(0); - long amount_denom = cursor.getLong(1); + long amount_num = cursor.getLong(0); + long amount_denom = cursor.getLong(1); String commodityCode = cursor.getString(2); + //Log.d(getClass().getName(), commodity + " " + amount_num + "/" + amount_denom); if (commodityCode.equals("XXX") || amount_num == 0) { // ignore custom currency continue; } - if (!hasDebitNormalBalance) { - amount_num = -amount_num; - } + + // #876 +// if (!hasDebitNormalBalance) { +// // The account has usually DEBIT < CREDIT +// +// // Invert signum to get a positive amount +// amount_num = -amount_num; +// } + if (commodityCode.equals(currencyCode)) { // currency matches - total = total.add(new Money(amount_num, amount_denom, currencyCode)); + + total = total.add(new Money(amount_num, + amount_denom, + currencyCode)); //Log.d(getClass().getName(), "currency " + commodity + " sub - total " + total); + } else { // there is a second currency involved + if (commoditiesDbAdapter == null) { commoditiesDbAdapter = new CommoditiesDbAdapter(mDb); pricesDbAdapter = new PricesDbAdapter(mDb); commodity = commoditiesDbAdapter.getCommodity(currencyCode); currencyUID = commoditiesDbAdapter.getCommodityUID(currencyCode); } + // get price String commodityUID = commoditiesDbAdapter.getCommodityUID(commodityCode); - Pair price = pricesDbAdapter.getPrice(commodityUID, currencyUID); + Pair price = pricesDbAdapter.getPrice(commodityUID, + currencyUID); if (price.first <= 0 || price.second <= 0) { // no price exists, just ignore it continue; } - BigDecimal amount = Money.getBigDecimal(amount_num, amount_denom); + BigDecimal amount = Money.getBigDecimal(amount_num, + amount_denom); BigDecimal amountConverted = amount.multiply(new BigDecimal(price.first)) - .divide(new BigDecimal(price.second), commodity.getSmallestFractionDigits(), BigDecimal.ROUND_HALF_EVEN); - total = total.add(new Money(amountConverted, commodity)); + .divide(new BigDecimal(price.second), + commodity.getSmallestFractionDigits(), + BigDecimal.ROUND_HALF_EVEN); + total = total.add(new Money(amountConverted, + commodity)); //Log.d(getClass().getName(), "currency " + commodity + " sub - total " + total); } - } + } // while + return total; + } finally { cursor.close(); } diff --git a/app/src/main/java/org/gnucash/android/export/csv/CsvTransactionsExporter.java b/app/src/main/java/org/gnucash/android/export/csv/CsvTransactionsExporter.java index f298cd201..25a8e704c 100644 --- a/app/src/main/java/org/gnucash/android/export/csv/CsvTransactionsExporter.java +++ b/app/src/main/java/org/gnucash/android/export/csv/CsvTransactionsExporter.java @@ -26,7 +26,6 @@ import org.gnucash.android.R; import org.gnucash.android.export.ExportParams; import org.gnucash.android.export.Exporter; -import org.gnucash.android.model.Account; import org.gnucash.android.model.Split; import org.gnucash.android.model.Transaction; import org.gnucash.android.model.TransactionType; diff --git a/app/src/main/java/org/gnucash/android/model/AccountType.java b/app/src/main/java/org/gnucash/android/model/AccountType.java index 171156cdc..fa51113f0 100644 --- a/app/src/main/java/org/gnucash/android/model/AccountType.java +++ b/app/src/main/java/org/gnucash/android/model/AccountType.java @@ -1,39 +1,357 @@ package org.gnucash.android.model; +import android.support.annotation.ColorInt; +import android.support.annotation.ColorRes; +import android.widget.TextView; + +import org.gnucash.android.R; +import org.gnucash.android.app.GnuCashApplication; +import org.gnucash.android.ui.settings.PreferenceActivity; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + /** * The type of account * This are the different types specified by the OFX format and * they are currently not used except for exporting */ public enum AccountType { - CASH(TransactionType.DEBIT), BANK(TransactionType.DEBIT), CREDIT, ASSET(TransactionType.DEBIT), LIABILITY, - INCOME, EXPENSE(TransactionType.DEBIT), PAYABLE, RECEIVABLE(TransactionType.DEBIT), EQUITY, CURRENCY, - STOCK(TransactionType.DEBIT), MUTUAL(TransactionType.DEBIT), TRADING, ROOT; + + CASH(TransactionType.DEBIT), + BANK(TransactionType.DEBIT), + CREDIT, + ASSET(TransactionType.DEBIT), + LIABILITY, + INCOME, + EXPENSE(TransactionType.DEBIT), + PAYABLE, + RECEIVABLE(TransactionType.DEBIT), + EQUITY, + CURRENCY, + STOCK(TransactionType.DEBIT), + MUTUAL(TransactionType.DEBIT), + TRADING, + ROOT; + + public final static List ASSET_ACCOUNT_TYPES = new ArrayList(Arrays.asList(AccountType.ASSET, + AccountType.CASH, + AccountType.BANK)); + + public final static List LIABLITY_ACCOUNT_TYPES = new ArrayList(Arrays.asList(AccountType.LIABILITY, + AccountType.CREDIT)); + + public final static List EQUITY_ACCOUNT_TYPES = new ArrayList(Arrays.asList(AccountType.EQUITY)); + + // + // Preference Key (must be the same as in donottranslate.xml) + // + + public static final String KEY_USE_NORMAL_BALANCE_EXPENSE = "KEY_USE_NORMAL_BALANCE_EXPENSE"; + public static final String KEY_USE_NORMAL_BALANCE_INCOME = "KEY_USE_NORMAL_BALANCE_INCOME"; + public static final String KEY_DEBIT = "KEY_DEBIT"; + public static final String KEY_CREDIT = "KEY_CREDIT"; + /** * Indicates that this type of normal balance the account type has *

To increase the value of an account with normal balance of credit, one would credit the account. * To increase the value of an account with normal balance of debit, one would likewise debit the account.

*/ - private TransactionType mNormalBalance = TransactionType.CREDIT; + private TransactionType mNormalBalance; + + AccountType(TransactionType normalBalance) { - AccountType(TransactionType normalBalance){ this.mNormalBalance = normalBalance; } AccountType() { - //nothing to see here, move along + + this(TransactionType.CREDIT); + } + + /** + * @return + */ + public TransactionType getDefaultTransactionType() { + + + String transactionTypePref = PreferenceActivity.getActiveBookSharedPreferences() + .getString(GnuCashApplication.getAppContext() + .getString(R.string.key_default_transaction_type), + KEY_USE_NORMAL_BALANCE_EXPENSE); + + final TransactionType transactionType; + + if (KEY_USE_NORMAL_BALANCE_EXPENSE.equals(transactionTypePref)) { + // Use Normal Balance (Expense Mode) + + // Use Account Normal Balance as default, except for Asset which are CREDIT by default + transactionType = isAssetAccount() + ? TransactionType.CREDIT + : getNormalBalanceType(); + + } else if (KEY_USE_NORMAL_BALANCE_INCOME.equals(transactionTypePref)) { + // Use Normal Balance (Income Mode) + + // Use Account Normal Balance as default + transactionType = getNormalBalanceType(); + + } else { + // Not Automatic mode + + // Convert String to Enum + transactionType = KEY_DEBIT.equals(transactionTypePref) + ? TransactionType.DEBIT + : TransactionType.CREDIT; + } + return transactionType; + } + + + /** + * Display the balance of a transaction in a text view and format the text color to match the sign of the amount + * + * @param balanceTextView {@link android.widget.TextView} where balance is to be displayed + * @param balance {@link org.gnucash.android.model.Money} balance (>0 or <0) to display + * @param shallDisplayNegativeSignum if true will display Negative signum (often bind to Preference R.string.key_display_negative_signum_in_splits + * @param shallDisplayZero if true will display 0, else will display nothing (usefull when first creation of a Transaction) + * @param shallDisplayCurrency if true will display Currency as well as amount (usefull when displaying balance in + * TransactionListFragment, BalanceSheetFragment...) + * else will not display Currency (usefull in SplitEditorFragment or TransactionFormFragment) + * because this allows the value to be an arithmetic expression (ex. 130.67+3) that can be + * evluated when loosing focus + */ + public void displayBalance(final TextView balanceTextView, + final Money balance, + final boolean shallDisplayNegativeSignum, + final boolean shallDisplayZero, + final boolean shallDisplayCurrency) { + + // + // Display amount + // + + final Money balanceToDisplay = getBalanceWithSignumForDisplay(balance); + + if (shallDisplayCurrency) { + // Shall display currency + + // Display currency + balanceTextView.setText(!shallDisplayNegativeSignum + ? balanceToDisplay.abs() + .formattedString() + : balanceToDisplay.formattedString()); + + } else { + // Shall not display currency, because the value may be an aritmetic expression (ex. 130.67+3) + // and must not contain currency to be evaluated + + // Shall display value in all other cases than zero balance and shall not display zero balance + final boolean shallDisplayValue = !(balanceToDisplay.isAmountZero() && !shallDisplayZero); + + if (shallDisplayValue) { + // Shall display balance + + // Display value without currency but with 2 decimals, in order to be able to be evaluated + // in the case where the value is an arithmetic expression like 130.67+3 + balanceTextView.setText(!shallDisplayNegativeSignum + ? balanceToDisplay.abs() + .toLocaleString() + : balanceToDisplay.toLocaleString()); + + } else { + // Shall not display balance + + balanceTextView.setText(""); + } + } + + // + // Define amount color + // + + @ColorInt int fontColor; + + final boolean isCreditBalance = balance.isNegative(); + + fontColor = getAmountColor(isCreditBalance); + + balanceTextView.setTextColor(fontColor); + } + + /** + * Display the balance of an account in a text view and format the text color to match the sign of the amount + * + * @param balanceTextView + * {@link android.widget.TextView} where balance is to be displayed + * @param balance + * {@link org.gnucash.android.model.Money} balance (>0 or <0) to display + */ + public void displayBalance(final TextView balanceTextView, + final Money balance) { + + displayBalance(balanceTextView, + balance, + true, + true, + true); } - public boolean hasDebitNormalBalance(){ + /** + * Display the balance of a transaction in a text view and format the text color to match the sign of the amount + * + * @param transactionBalanceTextView + * {@link android.widget.TextView} where balance is to be displayed + * @param transactionBalance + * {@link org.gnucash.android.model.Money} balance (>0 or <0) to display + */ + public void displayBalanceWithoutCurrency(final TextView transactionBalanceTextView, + final Money transactionBalance, + final boolean shallDisplayNegativeSignumInSplits, + final boolean shallDisplayZero) { + + displayBalance(transactionBalanceTextView, + transactionBalance, + shallDisplayNegativeSignumInSplits, + shallDisplayZero, + false); + } + /** + * Compute red/green color according to accountType and isCreditAmount + * + * @param isCreditAmount + * + * @return + */ + @ColorInt + public int getAmountColor(final boolean isCreditAmount) { + + @ColorRes final int colorRes; + + // Accounts for which + final boolean debitCreditInvertedColorAccountType = isExpenseOrIncomeAccount() || isEquityAccount(); + + if ((isCreditAmount && !debitCreditInvertedColorAccountType) || (!isCreditAmount && debitCreditInvertedColorAccountType)) { + // Credit amount and account like Assets, Bank, Cash..., or Debit amount and account like Expense/Income + + if (isExpenseOrIncomeAccount()) { + // It is an Expense/Income account + + // BLUE + colorRes = R.color.debit_expense_income; + + } else if(isEquityAccount()) { + // It is an Equity account + + colorRes = R.color.debit_equity; + + } else { + // It is not an Expense/Income account + + // RED + colorRes = R.color.debit_red; + } + + } else { + // Credit amount and account like Expense/Income, or Debit amount and account like Assets, Bank, Cash...) + + if (isExpenseOrIncomeAccount()) { + // It is an Expense/Income account + + // BLUE + colorRes = R.color.credit_expense_income; + + } else if(isEquityAccount()) { + // It is an Equity account + + colorRes = R.color.credit_equity; + + } else { + // It is not an Expense/Income account + + // GREEN + colorRes = R.color.credit_green; + } + + } + + return GnuCashApplication.getAppContext() + .getResources() + .getColor(colorRes); + } + + public boolean isAssetAccount() { + + return ASSET.equals(this) || BANK.equals(this) || CASH.equals(this); + } + + public boolean isEquityAccount() { + + return EQUITY.equals(this); + } + + public boolean isExpenseOrIncomeAccount() { + + return EXPENSE.equals(this) || INCOME.equals(this); + } + + public boolean hasDebitNormalBalance() { + return mNormalBalance == TransactionType.DEBIT; } + /** + * Returns balance with the right signum to be displayed + * + * A Debit is always the addition of a positive amount + * A credit is always the substraction of a positive amount + * The balance is always Debit - Credit + * Therefore : + * Debit > Credit => balance is > 0 + * Debit < Credit => balance is < 0 + * + * But for display, habit is to reduce the use of negative numbers + * To achieve this, for accounts which USUALLY have : + * Debit > Credit => compute balance as usual + * Debit < Credit => negate balance + * + * @return + * balance with the right signum to be displayed + */ + public Money getBalanceWithSignumForDisplay(final Money balance) { + + final Money balanceWithSignumForDisplay; + + if (hasDebitNormalBalance()) { + // Account usually debitor + + // balance = debit - credit => usually > 0 if hasDebitNormalBalance() + balanceWithSignumForDisplay = balance; + + } else { + // account usually creditor + + // balance = debit - credit => usually < 0 if !hasDebitNormalBalance() => negate() to get a usually > 0 value + balanceWithSignumForDisplay = balance.negate(); + } + + return balanceWithSignumForDisplay; + } + + + // + // Getters/Setters + // + /** * Returns the type of normal balance this account possesses * @return TransactionType balance of the account type */ - public TransactionType getNormalBalanceType(){ + public TransactionType getNormalBalanceType() { + return mNormalBalance; } + } diff --git a/app/src/main/java/org/gnucash/android/model/Money.java b/app/src/main/java/org/gnucash/android/model/Money.java index eedc67ac1..e2df19714 100644 --- a/app/src/main/java/org/gnucash/android/model/Money.java +++ b/app/src/main/java/org/gnucash/android/model/Money.java @@ -50,7 +50,7 @@ public final class Money implements Comparable{ private Commodity mCommodity; /** - * Amount value held by this object + * Amount value held by this object (can be > 0 or < 0) */ private BigDecimal mAmount; @@ -257,7 +257,7 @@ public double asDouble(){ public String asString(){ return toPlainString(); } - + /** * Returns a string representation of the Money object formatted according to * the locale and includes the currency symbol. @@ -315,6 +315,19 @@ public Money negate(){ * @param amount {@link BigDecimal} amount to be set */ private void setAmount(@NonNull BigDecimal amount) { + + if (amount != null) { + // Amount is not null + + // NTD + + } else { + // Amount is null + + // init to 0 + amount = new BigDecimal(0); + } + mAmount = amount.setScale(mCommodity.getSmallestFractionDigits(), ROUNDING_MODE); } @@ -435,6 +448,15 @@ public String toPlainString(){ return mAmount.setScale(mCommodity.getSmallestFractionDigits(), ROUNDING_MODE).toPlainString(); } + /** + * Returns a locale-specific representation of the amount of the Money object (excluding the currency) + * + * @return String representation of the amount (without currency) of the Money object + */ + public String toShortString(){ + return String.format(Locale.getDefault(), "%.0f", asDouble()); + } + /** * Returns a locale-specific representation of the amount of the Money object (excluding the currency) * @@ -444,6 +466,7 @@ public String toLocaleString(){ return String.format(Locale.getDefault(), "%.2f", asDouble()); } + /** * Returns the string representation of the Money object (value + currency) formatted according * to the default locale diff --git a/app/src/main/java/org/gnucash/android/model/Split.java b/app/src/main/java/org/gnucash/android/model/Split.java index 27f9fb7df..5816b950d 100644 --- a/app/src/main/java/org/gnucash/android/model/Split.java +++ b/app/src/main/java/org/gnucash/android/model/Split.java @@ -41,14 +41,14 @@ public class Split extends BaseModel implements Parcelable{ /** - * Amount value of this split which is in the currency of the transaction + * Amount absolute value of this split which is in the currency of the transaction */ - private Money mValue; + private Money mAmountAbsValue; /** * Amount of the split in the currency of the account to which the split belongs */ - private Money mQuantity; + private Money mQuantityAbsValue; /** * Transaction UID which this split belongs to @@ -88,8 +88,11 @@ public class Split extends BaseModel implements Parcelable{ * @param accountUID String UID of transfer account */ public Split(@NonNull Money value, @NonNull Money quantity, String accountUID){ - setQuantity(quantity); + + // Store absolute value setValue(value); + setQuantity(quantity); + setAccountUID(accountUID); } @@ -110,6 +113,7 @@ public Split(@NonNull Money amount, String accountUID){ /** * Clones the sourceSplit to create a new instance with same fields + * * @param sourceSplit Split to be cloned * @param generateUID Determines if the clone should have a new UID or should * maintain the one from source @@ -119,8 +123,8 @@ public Split(Split sourceSplit, boolean generateUID){ this.mAccountUID = sourceSplit.mAccountUID; this.mSplitType = sourceSplit.mSplitType; this.mTransactionUID = sourceSplit.mTransactionUID; - this.mValue = new Money(sourceSplit.mValue); - this.mQuantity = new Money(sourceSplit.mQuantity); + setValue(new Money(sourceSplit.mAmountAbsValue)); + setQuantity(new Money(sourceSplit.mQuantityAbsValue)); //todo: clone reconciled status if (generateUID){ @@ -131,12 +135,32 @@ public Split(Split sourceSplit, boolean generateUID){ } /** - * Returns the value amount of the split + * Returns the value (= signed amount) of the split + * + * @return Money amount of the split with the currency of the transaction + * + * @see #getQuantity() + */ + public Money getValueWithSignum() { + + // splitAmount (positive or negative number) + Money signedValue = TransactionType.DEBIT.equals(getType()) + ? getValue() + : getValue().negate(); + + return signedValue; + } + + + /** + * Returns the absolute value of the amount of the split + * * @return Money amount of the split with the currency of the transaction * @see #getQuantity() */ public Money getValue() { - return mValue; + + return mAmountAbsValue; } /** @@ -145,11 +169,12 @@ public Money getValue() { *

The value is in the currency of the containing transaction. * It's stored unsigned.

* - * @param value Money value of this split + * @param amountValue Money value of this split * @see #setQuantity(Money) */ - public void setValue(Money value) { - mValue = value.abs(); + public void setValue(Money amountValue) { + + mAmountAbsValue = amountValue.abs(); } /** @@ -159,7 +184,8 @@ public void setValue(Money value) { * @see #getValue() */ public Money getQuantity() { - return mQuantity; + + return mQuantityAbsValue; } /** @@ -168,11 +194,12 @@ public Money getQuantity() { *

The quantity is in the currency of the owning account. * It will be stored unsigned.

* - * @param quantity Money quantity amount + * @param quantityAbsValue Money quantity amount * @see #setValue(Money) */ - public void setQuantity(Money quantity) { - this.mQuantity = quantity.abs(); + public void setQuantity(Money quantityAbsValue) { + + this.mQuantityAbsValue = quantityAbsValue.abs(); } /** @@ -247,12 +274,14 @@ public void setMemo(String memo) { * @return New split pair of current split * @see TransactionType#invert() */ - public Split createPair(String accountUID){ - Split pair = new Split(mValue, accountUID); + public Split createPair(String accountUID) { + + Split pair = new Split(mAmountAbsValue, + accountUID); pair.setType(mSplitType.invert()); pair.setMemo(mMemo); pair.setTransactionUID(mTransactionUID); - pair.setQuantity(mQuantity); + pair.setQuantity(mQuantityAbsValue); return pair; } @@ -262,12 +291,13 @@ public Split createPair(String accountUID){ */ protected Split clone() throws CloneNotSupportedException { super.clone(); - Split split = new Split(mValue, mAccountUID); + Split split = new Split(mAmountAbsValue, + mAccountUID); split.setUID(getUID()); split.setType(mSplitType); split.setMemo(mMemo); split.setTransactionUID(mTransactionUID); - split.setQuantity(mQuantity); + split.setQuantity(mQuantityAbsValue); return split; } @@ -279,57 +309,9 @@ protected Split clone() throws CloneNotSupportedException { * @return whether the two splits are a pair */ public boolean isPairOf(Split other) { - return mValue.equals(other.mValue) - && mSplitType.invert().equals(other.mSplitType); - } - /** - * Returns the formatted amount (with or without negation sign) for the split value - * @return Money amount of value - * @see #getFormattedAmount(Money, String, TransactionType) - */ - public Money getFormattedValue(){ - return getFormattedAmount(mValue, mAccountUID, mSplitType); - } - - /** - * Returns the formatted amount (with or without negation sign) for the quantity - * @return Money amount of quantity - * @see #getFormattedAmount(Money, String, TransactionType) - */ - public Money getFormattedQuantity(){ - return getFormattedAmount(mQuantity, mAccountUID, mSplitType); - } - - /** - * Splits are saved as absolute values to the database, with no negative numbers. - * The type of movement the split causes to the balance of an account determines - * its sign, and that depends on the split type and the account type - * @param amount Money amount to format - * @param accountUID GUID of the account - * @param splitType Transaction type of the split - * @return -{@code amount} if the amount would reduce the balance of - * {@code account}, otherwise +{@code amount} - */ - private static Money getFormattedAmount(Money amount, String accountUID, TransactionType - splitType){ - boolean isDebitAccount = AccountsDbAdapter.getInstance().getAccountType(accountUID).hasDebitNormalBalance(); - Money absAmount = amount.abs(); - - boolean isDebitSplit = splitType == TransactionType.DEBIT; - if (isDebitAccount) { - if (isDebitSplit) { - return absAmount; - } else { - return absAmount.negate(); - } - } else { - if (isDebitSplit) { - return absAmount.negate(); - } else { - return absAmount; - } - } + return mAmountAbsValue.equals(other.mAmountAbsValue) && mSplitType.invert() + .equals(other.mSplitType); } /** @@ -394,7 +376,14 @@ public void setReconcileDate(Timestamp reconcileDate) { @Override public String toString() { - return mSplitType.name() + " of " + mValue.toString() + " in account: " + mAccountUID; + + return mSplitType.name() + " of " + mAmountAbsValue.toString() + + " in account: " + + mAccountUID + + " (" + + AccountsDbAdapter.getInstance() + .getAccountFullName(mAccountUID) + + ")"; } /** @@ -412,10 +401,27 @@ public String toString() { public String toCsv(){ String sep = ";"; //TODO: add reconciled state and date - String splitString = getUID() + sep + mValue.getNumerator() + sep + mValue.getDenominator() - + sep + mValue.getCommodity().getCurrencyCode() + sep + mQuantity.getNumerator() - + sep + mQuantity.getDenominator() + sep + mQuantity.getCommodity().getCurrencyCode() - + sep + mTransactionUID + sep + mAccountUID + sep + mSplitType.name(); + String splitString = getUID() + + sep + + mAmountAbsValue.getNumerator() + + sep + + mAmountAbsValue.getDenominator() + + sep + + mAmountAbsValue.getCommodity() + .getCurrencyCode() + + sep + + mQuantityAbsValue.getNumerator() + + sep + + mQuantityAbsValue.getDenominator() + + sep + + mQuantityAbsValue.getCommodity() + .getCurrencyCode() + + sep + + mTransactionUID + + sep + + mAccountUID + + sep + + mSplitType.name(); if (mMemo != null){ splitString = splitString + sep + mMemo; } @@ -489,8 +495,12 @@ public boolean isEquivalentTo(Split split){ if (super.equals(split)) return true; if (mReconcileState != split.mReconcileState) return false; - if (!mValue.equals(split.mValue)) return false; - if (!mQuantity.equals(split.mQuantity)) return false; + if (!mAmountAbsValue.equals(split.mAmountAbsValue)) { + return false; + } + if (!mQuantityAbsValue.equals(split.mQuantityAbsValue)) { + return false; + } if (!mTransactionUID.equals(split.mTransactionUID)) return false; if (!mAccountUID.equals(split.mAccountUID)) return false; if (mSplitType != split.mSplitType) return false; @@ -514,8 +524,12 @@ public boolean equals(Object o) { Split split = (Split) o; if (mReconcileState != split.mReconcileState) return false; - if (!mValue.equals(split.mValue)) return false; - if (!mQuantity.equals(split.mQuantity)) return false; + if (!mAmountAbsValue.equals(split.mAmountAbsValue)) { + return false; + } + if (!mQuantityAbsValue.equals(split.mQuantityAbsValue)) { + return false; + } if (!mTransactionUID.equals(split.mTransactionUID)) return false; if (!mAccountUID.equals(split.mAccountUID)) return false; if (mSplitType != split.mSplitType) return false; @@ -525,8 +539,8 @@ public boolean equals(Object o) { @Override public int hashCode() { int result = super.hashCode(); - result = 31 * result + mValue.hashCode(); - result = 31 * result + mQuantity.hashCode(); + result = 31 * result + mAmountAbsValue.hashCode(); + result = 31 * result + mQuantityAbsValue.hashCode(); result = 31 * result + mTransactionUID.hashCode(); result = 31 * result + mAccountUID.hashCode(); result = 31 * result + mSplitType.hashCode(); @@ -547,13 +561,15 @@ public void writeToParcel(Parcel dest, int flags) { dest.writeString(mTransactionUID); dest.writeString(mSplitType.name()); - dest.writeLong(mValue.getNumerator()); - dest.writeLong(mValue.getDenominator()); - dest.writeString(mValue.getCommodity().getCurrencyCode()); + dest.writeLong(mAmountAbsValue.getNumerator()); + dest.writeLong(mAmountAbsValue.getDenominator()); + dest.writeString(mAmountAbsValue.getCommodity() + .getCurrencyCode()); - dest.writeLong(mQuantity.getNumerator()); - dest.writeLong(mQuantity.getDenominator()); - dest.writeString(mQuantity.getCommodity().getCurrencyCode()); + dest.writeLong(mQuantityAbsValue.getNumerator()); + dest.writeLong(mQuantityAbsValue.getDenominator()); + dest.writeString(mQuantityAbsValue.getCommodity() + .getCurrencyCode()); dest.writeString(mMemo == null ? "" : mMemo); dest.writeString(String.valueOf(mReconcileState)); @@ -574,12 +590,16 @@ private Split(Parcel source){ long valueNum = source.readLong(); long valueDenom = source.readLong(); String valueCurrency = source.readString(); - mValue = new Money(valueNum, valueDenom, valueCurrency).abs(); + setValue(new Money(valueNum, + valueDenom, + valueCurrency)); long qtyNum = source.readLong(); long qtyDenom = source.readLong(); String qtyCurrency = source.readString(); - mQuantity = new Money(qtyNum, qtyDenom, qtyCurrency).abs(); + setQuantity(new Money(qtyNum, + qtyDenom, + qtyCurrency)); String memo = source.readString(); mMemo = memo.isEmpty() ? null : memo; diff --git a/app/src/main/java/org/gnucash/android/model/Transaction.java b/app/src/main/java/org/gnucash/android/model/Transaction.java index 21ba696aa..4587aa3b4 100644 --- a/app/src/main/java/org/gnucash/android/model/Transaction.java +++ b/app/src/main/java/org/gnucash/android/model/Transaction.java @@ -290,36 +290,84 @@ private Money getImbalance(){ * @return Money list of splits */ public static Money computeBalance(String accountUID, List splitList) { + AccountsDbAdapter accountsDbAdapter = AccountsDbAdapter.getInstance(); - AccountType accountType = accountsDbAdapter.getAccountType(accountUID); + String accountCurrencyCode = accountsDbAdapter.getAccountCurrencyCode(accountUID); - boolean isDebitAccount = accountType.hasDebitNormalBalance(); Money balance = Money.createZeroInstance(accountCurrencyCode); + for (Split split : splitList) { - if (!split.getAccountUID().equals(accountUID)) + + if (!split.getAccountUID() + .equals(accountUID)) { + continue; - Money amount; - if (split.getValue().getCommodity().getCurrencyCode().equals(accountCurrencyCode)){ - amount = split.getValue(); - } else { //if this split belongs to the account, then either its value or quantity is in the account currency - amount = split.getQuantity(); - } - boolean isDebitSplit = split.getType() == TransactionType.DEBIT; - if (isDebitAccount) { - if (isDebitSplit) { - balance = balance.add(amount); + + } else { + + // + // Get Amount absolute value + // + + Money amount; + + if (split.getValue() + .getCommodity() + .getCurrencyCode() + .equals(accountCurrencyCode)) { + + amount = split.getValue(); + } else { - balance = balance.subtract(amount); + // this split belongs to the account, then either its value or quantity is in the account currency + + amount = split.getQuantity(); } - } else { + + // + // Compute balance + // + + boolean isDebitSplit = (split.getType() == TransactionType.DEBIT); + + // #876 +// if (isDebitAccount) { +// if (isDebitSplit) { +// balance = balance.add(amount); +// } else { +// balance = balance.subtract(amount); +// } +// } else { +// if (isDebitSplit) { +// balance = balance.subtract(amount); +// } else { +// balance = balance.add(amount); +// } +// } + if (isDebitSplit) { - balance = balance.subtract(amount); - } else { + // DEBIT + balance = balance.add(amount); + + } else { + // CREDIT + + balance = balance.subtract(amount); } } - } + + } // for + +// // +// // Negate balance if account is usually creditor +// // +// +// AccountType accountType = accountsDbAdapter.getAccountType(accountUID); +// +// balance = accountType.getBalanceWithSignumForDisplay(balance); + return balance; } @@ -420,16 +468,16 @@ public static TransactionType getTypeForBalance(AccountType accountType, boolean return type; } + // #876 /** * Returns true if the transaction type represents a decrease for the account balance for the accountType, false otherwise * @return true if the amount represents a decrease in the account balance, false otherwise * @see #getTypeForBalance(AccountType, boolean) */ - public static boolean shouldDecreaseBalance(AccountType accountType, TransactionType transactionType) { - if (accountType.hasDebitNormalBalance()) { - return transactionType == TransactionType.CREDIT; - } else - return transactionType == TransactionType.DEBIT; + public static boolean wouldDecreaseBalance(AccountType accountType, + TransactionType transactionType) { + + return transactionType == TransactionType.CREDIT; } /** diff --git a/app/src/main/java/org/gnucash/android/model/TransactionType.java b/app/src/main/java/org/gnucash/android/model/TransactionType.java index 0306a4c06..2e4613fb2 100644 --- a/app/src/main/java/org/gnucash/android/model/TransactionType.java +++ b/app/src/main/java/org/gnucash/android/model/TransactionType.java @@ -17,13 +17,17 @@ package org.gnucash.android.model; /** - * Type of transaction, a credit or a debit + * Type of a Split of a Transaction + * + * It is a credit or a debit * * @author Ngewi Fet * @author Jesse Shieh */ +// TW m 2020-03-03 : Should be named SplitType public enum TransactionType { - DEBIT, CREDIT; + DEBIT, + CREDIT; private TransactionType opposite; diff --git a/app/src/main/java/org/gnucash/android/ui/account/AccountsActivity.java b/app/src/main/java/org/gnucash/android/ui/account/AccountsActivity.java index 5e8a94026..ae16ef372 100644 --- a/app/src/main/java/org/gnucash/android/ui/account/AccountsActivity.java +++ b/app/src/main/java/org/gnucash/android/ui/account/AccountsActivity.java @@ -154,7 +154,9 @@ public AccountViewPagerAdapter(FragmentManager fm){ @Override public Fragment getItem(int i) { AccountsListFragment currentFragment = (AccountsListFragment) mFragmentPageReferenceMap.get(i); + if (currentFragment == null) { + switch (i) { case INDEX_RECENT_ACCOUNTS_FRAGMENT: currentFragment = AccountsListFragment.newInstance(AccountsListFragment.DisplayMode.RECENT); @@ -169,13 +171,16 @@ public Fragment getItem(int i) { currentFragment = AccountsListFragment.newInstance(AccountsListFragment.DisplayMode.TOP_LEVEL); break; } + mFragmentPageReferenceMap.put(i, currentFragment); + } return currentFragment; } @Override public void destroyItem(ViewGroup container, int position, Object object) { +// #586 By putting this in comment, there is no more crash, but I don't know if there are side effects super.destroyItem(container, position, object); mFragmentPageReferenceMap.remove(position); } @@ -221,6 +226,7 @@ public int getTitleRes() { @Override public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); final Intent intent = getIntent(); @@ -280,6 +286,7 @@ protected void onResume() { @Override protected void onStart() { + super.onStart(); if (BuildConfig.CAN_REQUEST_RATING) { @@ -287,6 +294,11 @@ protected void onStart() { RateThisApp.onStart(this); RateThisApp.showRateDialogIfNeeded(this); } + + Log.i(LOG_TAG, + "New active db (" + GnuCashApplication.getActiveDb() + .getPath() + ")"); + } /** @@ -333,18 +345,29 @@ public void setCurrentTab(){ *

Also handles displaying the What's New dialog

*/ private void init() { - PreferenceManager.setDefaultValues(this, BooksDbAdapter.getInstance().getActiveBookUID(), - Context.MODE_PRIVATE, R.xml.fragment_transaction_preferences, true); - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); - boolean firstRun = prefs.getBoolean(getString(R.string.key_first_run), true); + PreferenceManager.setDefaultValues(this, + BooksDbAdapter.getInstance() + .getActiveBookUID(), + Context.MODE_PRIVATE, + R.xml.fragment_transaction_preferences, + true); + + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); + boolean firstRun = prefs.getBoolean(getString(R.string.key_first_run), + true); if (firstRun){ startActivity(new Intent(GnuCashApplication.getAppContext(), FirstRunWizardActivity.class)); - //default to using double entry and save the preference explicitly + // Default Preference to using double entry and save the preference explicitly prefs.edit().putBoolean(getString(R.string.key_use_double_entry), true).apply(); + + // Default preference not to show negative number in splits + prefs.edit().putBoolean(getString(R.string.key_display_negative_signum_in_splits), false).apply(); + finish(); + return; } @@ -358,6 +381,7 @@ private void init() { @Override protected void onDestroy() { super.onDestroy(); + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); preferences.edit().putInt(LAST_OPEN_TAB_INDEX, mViewPager.getCurrentItem()).apply(); } diff --git a/app/src/main/java/org/gnucash/android/ui/account/AccountsListFragment.java b/app/src/main/java/org/gnucash/android/ui/account/AccountsListFragment.java index a83ab10bc..aa934eb4a 100644 --- a/app/src/main/java/org/gnucash/android/ui/account/AccountsListFragment.java +++ b/app/src/main/java/org/gnucash/android/ui/account/AccountsListFragment.java @@ -25,7 +25,6 @@ import android.database.Cursor; import android.graphics.Color; import android.os.AsyncTask; -import android.os.Build; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.LoaderManager.LoaderCallbacks; @@ -484,26 +483,39 @@ public AccountViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { @Override public void onBindViewHolderCursor(final AccountViewHolder holder, final Cursor cursor) { + final String accountUID = cursor.getString(cursor.getColumnIndexOrThrow(DatabaseSchema.AccountEntry.COLUMN_UID)); + mAccountsDbAdapter = AccountsDbAdapter.getInstance(); - holder.accoundId = mAccountsDbAdapter.getID(accountUID); + holder.accoundId = mAccountsDbAdapter.getID(accountUID); holder.accountName.setText(cursor.getString(cursor.getColumnIndexOrThrow(DatabaseSchema.AccountEntry.COLUMN_NAME))); + int subAccountCount = mAccountsDbAdapter.getSubAccountCount(accountUID); + if (subAccountCount > 0) { holder.description.setVisibility(View.VISIBLE); - String text = getResources().getQuantityString(R.plurals.label_sub_accounts, subAccountCount, subAccountCount); + String text = getResources().getQuantityString(R.plurals.label_sub_accounts, + subAccountCount, + subAccountCount); holder.description.setText(text); - } else + + } else { holder.description.setVisibility(View.GONE); + } // add a summary of transactions to the account view - // Make sure the balance task is truly multithread - new AccountBalanceTask(holder.accountBalance).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, accountUID); + // Make sure the balance task is truly multithread + new AccountBalanceTask(holder.accountBalance).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, + accountUID); String accountColor = cursor.getString(cursor.getColumnIndexOrThrow(DatabaseSchema.AccountEntry.COLUMN_COLOR_CODE)); - int colorCode = accountColor == null ? Color.TRANSPARENT : Color.parseColor(accountColor); + + int colorCode = accountColor == null + ? Color.TRANSPARENT + : Color.parseColor(accountColor); + holder.colorStripView.setBackgroundColor(colorCode); boolean isPlaceholderAccount = mAccountsDbAdapter.isPlaceholderAccount(accountUID); @@ -514,21 +526,30 @@ public void onBindViewHolderCursor(final AccountViewHolder holder, final Cursor @Override public void onClick(View v) { - Intent intent = new Intent(getActivity(), FormActivity.class); + + Intent intent = new Intent(getActivity(), + FormActivity.class); intent.setAction(Intent.ACTION_INSERT_OR_EDIT); - intent.putExtra(UxArgument.SELECTED_ACCOUNT_UID, accountUID); - intent.putExtra(UxArgument.FORM_TYPE, FormActivity.FormType.TRANSACTION.name()); + intent.putExtra(UxArgument.SELECTED_ACCOUNT_UID, + accountUID); + intent.putExtra(UxArgument.FORM_TYPE, + FormActivity.FormType.TRANSACTION.name()); getActivity().startActivity(intent); } }); } - List budgets = BudgetsDbAdapter.getInstance().getAccountBudgets(accountUID); + List budgets = BudgetsDbAdapter.getInstance() + .getAccountBudgets(accountUID); //TODO: include fetch only active budgets - if (budgets.size() == 1){ - Budget budget = budgets.get(0); - Money balance = mAccountsDbAdapter.getAccountBalance(accountUID, budget.getStartofCurrentPeriod(), budget.getEndOfCurrentPeriod()); - double budgetProgress = balance.divide(budget.getAmount(accountUID)).asBigDecimal().doubleValue() * 100; + if (budgets.size() == 1) { + Budget budget = budgets.get(0); + Money balance = mAccountsDbAdapter.getAccountBalance(accountUID, + budget.getStartofCurrentPeriod(), + budget.getEndOfCurrentPeriod()); + double budgetProgress = balance.divide(budget.getAmount(accountUID)) + .asBigDecimal() + .doubleValue() * 100; holder.budgetIndicator.setVisibility(View.VISIBLE); holder.budgetIndicator.setProgress((int) budgetProgress); @@ -537,7 +558,7 @@ public void onClick(View v) { } - if (mAccountsDbAdapter.isFavoriteAccount(accountUID)){ + if (mAccountsDbAdapter.isFavoriteAccount(accountUID)) { holder.favoriteStatus.setImageResource(R.drawable.ic_star_black_24dp); } else { holder.favoriteStatus.setImageResource(R.drawable.ic_star_border_black_24dp); @@ -546,23 +567,29 @@ public void onClick(View v) { holder.favoriteStatus.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { + boolean isFavoriteAccount = mAccountsDbAdapter.isFavoriteAccount(accountUID); ContentValues contentValues = new ContentValues(); - contentValues.put(DatabaseSchema.AccountEntry.COLUMN_FAVORITE, !isFavoriteAccount); - mAccountsDbAdapter.updateRecord(accountUID, contentValues); - - int drawableResource = !isFavoriteAccount ? - R.drawable.ic_star_black_24dp : R.drawable.ic_star_border_black_24dp; + contentValues.put(DatabaseSchema.AccountEntry.COLUMN_FAVORITE, + !isFavoriteAccount); + mAccountsDbAdapter.updateRecord(accountUID, + contentValues); + + int drawableResource = !isFavoriteAccount + ? R.drawable.ic_star_black_24dp + : R.drawable.ic_star_border_black_24dp; holder.favoriteStatus.setImageResource(drawableResource); - if (mDisplayMode == DisplayMode.FAVORITES) + if (mDisplayMode == DisplayMode.FAVORITES) { refresh(); + } } }); holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { + onListItemClick(accountUID); } }); @@ -581,7 +608,9 @@ class AccountViewHolder extends RecyclerView.ViewHolder implements PopupMenu.OnM long accoundId; public AccountViewHolder(View itemView) { + super(itemView); + ButterKnife.bind(this, itemView); optionsMenu.setOnClickListener(new View.OnClickListener() { diff --git a/app/src/main/java/org/gnucash/android/ui/common/BaseDrawerActivity.java b/app/src/main/java/org/gnucash/android/ui/common/BaseDrawerActivity.java index 83c409d95..76382c25a 100644 --- a/app/src/main/java/org/gnucash/android/ui/common/BaseDrawerActivity.java +++ b/app/src/main/java/org/gnucash/android/ui/common/BaseDrawerActivity.java @@ -16,6 +16,7 @@ package org.gnucash.android.ui.common; import android.app.Activity; +import android.app.ActivityManager; import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Configuration; @@ -32,11 +33,14 @@ import android.support.v7.app.ActionBarDrawerToggle; import android.support.v7.widget.PopupMenu; import android.support.v7.widget.Toolbar; +import android.util.Log; +import android.view.Gravity; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.ProgressBar; import android.widget.TextView; +import android.widget.Toast; import com.uservoice.uservoicesdk.UserVoice; @@ -51,6 +55,8 @@ import org.gnucash.android.ui.transaction.ScheduledActionsActivity; import org.gnucash.android.util.BookUtils; +import java.util.List; + import butterknife.BindView; import butterknife.ButterKnife; @@ -210,10 +216,11 @@ public void onConfigurationChanged(Configuration newConfig) { @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == android.R.id.home){ - if (!mDrawerLayout.isDrawerOpen(mNavigationView)) + if (!isNavigationViewOpen()) { mDrawerLayout.openDrawer(mNavigationView); - else - mDrawerLayout.closeDrawer(mNavigationView); + } else { + closeNavigationView(); + } return true; } @@ -286,7 +293,7 @@ protected void onDrawerMenuItemClicked(int itemId) { UserVoice.launchUserVoice(this); break; } - mDrawerLayout.closeDrawer(mNavigationView); + closeNavigationView(); } @Override @@ -314,27 +321,114 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { @Override public boolean onMenuItemClick(MenuItem item) { - long id = item.getItemId(); - if (id == ID_MANAGE_BOOKS){ + + closeNavigationView(); + + long itemId = item.getItemId(); + + if (itemId == ID_MANAGE_BOOKS){ + // Click on "Manage books..." item + + // Start "Manage books" Activity Intent intent = new Intent(this, PreferenceActivity.class); intent.setAction(PreferenceActivity.ACTION_MANAGE_BOOKS); startActivity(intent); - mDrawerLayout.closeDrawer(mNavigationView); - return true; - } - BooksDbAdapter booksDbAdapter = BooksDbAdapter.getInstance(); - String bookUID = booksDbAdapter.getUID(id); - if (!bookUID.equals(booksDbAdapter.getActiveBookUID())){ - BookUtils.loadBook(bookUID); - finish(); + + } else { + // Click on an existing book item + + BooksDbAdapter booksDbAdapter = BooksDbAdapter.getInstance(); + + String selectedBookUID = booksDbAdapter.getUID(itemId); + + if (!selectedBookUID.equals(booksDbAdapter.getActiveBookUID())) { + // Selected Book is not the active one + + // + // Check if current Activity is the first Activity + // + + Log.d("BaseDrawerActivity", + "This is (" + this.getClass() + .getName() + ")"); + + ActivityManager mngr = (ActivityManager) getSystemService(ACTIVITY_SERVICE); + List taskList = mngr.getRunningTasks(10); + final ActivityManager.RunningTaskInfo task0RunningInfo = taskList.get(0); + + if (task0RunningInfo.numActivities <= 1 || task0RunningInfo.baseActivity.getClassName() + .equals(this.getClass() + .getName())) { + // This is the first Activity + + // Close current Activity (pop Activity stack) + finish(); + + // + // load selected book and Start Account Activity and reset Activity Stack + // + + BookUtils.loadBook(selectedBookUID); + + } else { + // This is not the first Activity + + Toast toast = Toast.makeText(this, + R.string.toast_must_be_on_account_page_to_change_book, + Toast.LENGTH_LONG); + + // + // Align-Center text inside the Toast + // + + TextView toastTextView = (TextView) toast.getView() + .findViewById(android.R.id.message); + if (toastTextView != null) { + toastTextView.setGravity(Gravity.CENTER); + } + + // Show toast + toast.show(); + } + +// // Android handler to delay actions +// Handler handler = new Handler(); +// +// // After two seconds, it is not more considered as already pressed +// handler.postDelayed(new Runnable() { +// @Override +// public void run() { +// +// // load book and Start Account Activity and reset Activity Stack +// BookUtils.loadBook(selectedBookUID); +// } +// }, +// 5000); + + } else { + // Selected Book is the current one + + // Start Account Activity and reset Activity Stack + AccountsActivity.start(GnuCashApplication.getAppContext()); + } + } - AccountsActivity.start(GnuCashApplication.getAppContext()); + + return true; } - public void onClickAppTitle(View view){ + protected void onClickAppTitle(View view) { + + closeNavigationView(); + + // Do not launch AccountsActivity to stay on current Activity +// AccountsActivity.start(this); + } + + protected void closeNavigationView() { + mDrawerLayout.closeDrawer(mNavigationView); - AccountsActivity.start(this); } public void onClickBook(View view){ @@ -354,4 +448,14 @@ public void onClickBook(View view){ popup.show(); } + + /** + * Return true if main navigation menu is open + * + * @return true if main navigation menu is open + */ + protected boolean isNavigationViewOpen() { + + return mDrawerLayout.isDrawerOpen(mNavigationView); + } } diff --git a/app/src/main/java/org/gnucash/android/ui/report/ReportsOverviewFragment.java b/app/src/main/java/org/gnucash/android/ui/report/ReportsOverviewFragment.java index 9b81fc5b6..3b230eeed 100644 --- a/app/src/main/java/org/gnucash/android/ui/report/ReportsOverviewFragment.java +++ b/app/src/main/java/org/gnucash/android/ui/report/ReportsOverviewFragment.java @@ -22,6 +22,7 @@ import android.support.v4.app.FragmentManager; import android.support.v4.content.ContextCompat; import android.support.v4.view.ViewCompat; +import android.support.v7.preference.PreferenceManager; import android.support.v7.widget.AppCompatButton; import android.view.Menu; import android.view.View; @@ -44,7 +45,6 @@ import org.gnucash.android.ui.report.linechart.CashFlowLineChartFragment; import org.gnucash.android.ui.report.piechart.PieChartFragment; import org.gnucash.android.ui.report.sheet.BalanceSheetFragment; -import org.gnucash.android.ui.transaction.TransactionsActivity; import org.joda.time.LocalDate; import java.util.ArrayList; @@ -213,9 +213,13 @@ protected void displayReport() { mChart.highlightValues(null); mChart.invalidate(); - TransactionsActivity.displayBalance(mTotalAssets, mAssetsBalance); - TransactionsActivity.displayBalance(mTotalLiabilities, mLiabilitiesBalance); - TransactionsActivity.displayBalance(mNetWorth, mAssetsBalance.subtract(mLiabilitiesBalance)); + AccountType.ASSET.displayBalance(mTotalAssets, + mAssetsBalance); + AccountType.LIABILITY.displayBalance(mTotalLiabilities, + mLiabilitiesBalance); + // #8xx + AccountType.ASSET.displayBalance(mNetWorth, + mAssetsBalance.add(mLiabilitiesBalance)); } /** diff --git a/app/src/main/java/org/gnucash/android/ui/report/sheet/BalanceSheetFragment.java b/app/src/main/java/org/gnucash/android/ui/report/sheet/BalanceSheetFragment.java index 4ca91569d..36d8082db 100644 --- a/app/src/main/java/org/gnucash/android/ui/report/sheet/BalanceSheetFragment.java +++ b/app/src/main/java/org/gnucash/android/ui/report/sheet/BalanceSheetFragment.java @@ -33,13 +33,15 @@ import org.gnucash.android.model.Money; import org.gnucash.android.ui.report.BaseReportFragment; import org.gnucash.android.ui.report.ReportType; -import org.gnucash.android.ui.transaction.TransactionsActivity; -import java.util.ArrayList; import java.util.List; import butterknife.BindView; +import static org.gnucash.android.model.AccountType.ASSET_ACCOUNT_TYPES; +import static org.gnucash.android.model.AccountType.EQUITY_ACCOUNT_TYPES; +import static org.gnucash.android.model.AccountType.LIABLITY_ACCOUNT_TYPES; + /** * Balance sheet report fragment * @author Ngewi Fet @@ -56,9 +58,6 @@ public class BalanceSheetFragment extends BaseReportFragment { private Money mAssetsBalance; private Money mLiabilitiesBalance; - private List mAssetAccountTypes; - private List mLiabilityAccountTypes; - private List mEquityAccountTypes; @Override public int getLayoutResource() { @@ -78,17 +77,6 @@ public ReportType getReportType() { @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - mAssetAccountTypes = new ArrayList<>(); - mAssetAccountTypes.add(AccountType.ASSET); - mAssetAccountTypes.add(AccountType.CASH); - mAssetAccountTypes.add(AccountType.BANK); - - mLiabilityAccountTypes = new ArrayList<>(); - mLiabilityAccountTypes.add(AccountType.LIABILITY); - mLiabilityAccountTypes.add(AccountType.CREDIT); - - mEquityAccountTypes = new ArrayList<>(); - mEquityAccountTypes.add(AccountType.EQUITY); } @Override @@ -103,17 +91,20 @@ public boolean requiresTimeRangeOptions() { @Override protected void generateReport() { - mAssetsBalance = mAccountsDbAdapter.getAccountBalance(mAssetAccountTypes, -1, System.currentTimeMillis()); - mLiabilitiesBalance = mAccountsDbAdapter.getAccountBalance(mLiabilityAccountTypes, -1, System.currentTimeMillis()); + mAssetsBalance = mAccountsDbAdapter.getAccountBalance(ASSET_ACCOUNT_TYPES, -1, System.currentTimeMillis()); + mLiabilitiesBalance = mAccountsDbAdapter.getAccountBalance(LIABLITY_ACCOUNT_TYPES, -1, System.currentTimeMillis()); } @Override protected void displayReport() { - loadAccountViews(mAssetAccountTypes, mAssetsTableLayout); - loadAccountViews(mLiabilityAccountTypes, mLiabilitiesTableLayout); - loadAccountViews(mEquityAccountTypes, mEquityTableLayout); - TransactionsActivity.displayBalance(mNetWorth, mAssetsBalance.subtract(mLiabilitiesBalance)); + loadAccountViews(ASSET_ACCOUNT_TYPES, mAssetsTableLayout); + loadAccountViews(LIABLITY_ACCOUNT_TYPES, mLiabilitiesTableLayout); + loadAccountViews(EQUITY_ACCOUNT_TYPES, mEquityTableLayout); + + AccountType.ASSET.displayBalance(mNetWorth, + // #8xx + mAssetsBalance.add(mLiabilitiesBalance)); } @Override @@ -128,6 +119,7 @@ public void onPrepareOptionsMenu(Menu menu) { * @param tableLayout Table layout into which to load the rows */ private void loadAccountViews(List accountTypes, TableLayout tableLayout){ + LayoutInflater inflater = LayoutInflater.from(getActivity()); Cursor cursor = mAccountsDbAdapter.fetchAccounts(DatabaseSchema.AccountEntry.COLUMN_TYPE @@ -135,14 +127,20 @@ private void loadAccountViews(List accountTypes, TableLayout tableL + DatabaseSchema.AccountEntry.COLUMN_PLACEHOLDER + " = 0", null, DatabaseSchema.AccountEntry.COLUMN_FULL_NAME + " ASC"); + AccountType accountType = null; + while (cursor.moveToNext()){ String accountUID = cursor.getString(cursor.getColumnIndexOrThrow(DatabaseSchema.AccountEntry.COLUMN_UID)); String name = cursor.getString(cursor.getColumnIndexOrThrow(DatabaseSchema.AccountEntry.COLUMN_NAME)); Money balance = mAccountsDbAdapter.getAccountBalance(accountUID); View view = inflater.inflate(R.layout.row_balance_sheet, tableLayout, false); ((TextView)view.findViewById(R.id.account_name)).setText(name); - TextView balanceTextView = (TextView) view.findViewById(R.id.account_balance); - TransactionsActivity.displayBalance(balanceTextView, balance); + TextView balanceTextView = (TextView) view.findViewById(R.id.account_balance); + accountType = AccountType.valueOf(cursor.getString(cursor.getColumnIndexOrThrow(DatabaseSchema.AccountEntry.COLUMN_TYPE))); + + accountType.displayBalance(balanceTextView, + balance); + tableLayout.addView(view); } @@ -157,7 +155,11 @@ private void loadAccountViews(List accountTypes, TableLayout tableL TextView accountBalance = (TextView) totalView.findViewById(R.id.account_balance); accountBalance.setTextSize(16); accountBalance.setTypeface(null, Typeface.BOLD); - TransactionsActivity.displayBalance(accountBalance, mAccountsDbAdapter.getAccountBalance(accountTypes, -1, System.currentTimeMillis())); + + accountType.displayBalance(accountBalance, + mAccountsDbAdapter.getAccountBalance(accountTypes, + -1, + System.currentTimeMillis())); tableLayout.addView(totalView); } diff --git a/app/src/main/java/org/gnucash/android/ui/settings/GeneralPreferenceFragment.java b/app/src/main/java/org/gnucash/android/ui/settings/GeneralPreferenceFragment.java index db07e2753..124494760 100644 --- a/app/src/main/java/org/gnucash/android/ui/settings/GeneralPreferenceFragment.java +++ b/app/src/main/java/org/gnucash/android/ui/settings/GeneralPreferenceFragment.java @@ -107,24 +107,43 @@ public boolean onPreferenceClick(Preference preference) { } @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - if (preference.getKey().equals(getString(R.string.key_enable_passcode))) { + public boolean onPreferenceChange(Preference preference, + Object newValue) { + + if (preference.getKey() + .equals(getString(R.string.key_enable_passcode))) { + if ((Boolean) newValue) { - startActivityForResult(new Intent(getActivity(), PasscodePreferenceActivity.class), - GeneralPreferenceFragment.PASSCODE_REQUEST_CODE); + + startActivityForResult(new Intent(getActivity(), + PasscodePreferenceActivity.class), + GeneralPreferenceFragment.PASSCODE_REQUEST_CODE); + } else { - Intent passIntent = new Intent(getActivity(), PasscodeLockScreenActivity.class); - passIntent.putExtra(UxArgument.DISABLE_PASSCODE, UxArgument.DISABLE_PASSCODE); - startActivityForResult(passIntent, GeneralPreferenceFragment.REQUEST_DISABLE_PASSCODE); + + Intent passIntent = new Intent(getActivity(), + PasscodeLockScreenActivity.class); + passIntent.putExtra(UxArgument.DISABLE_PASSCODE, + UxArgument.DISABLE_PASSCODE); + startActivityForResult(passIntent, + GeneralPreferenceFragment.REQUEST_DISABLE_PASSCODE); } } - if (preference.getKey().equals(getString(R.string.key_use_account_color))) { + // + // Set Preference : use_color_in_reports + // + + if (preference.getKey() + .equals(getString(R.string.key_use_account_color))) { + getPreferenceManager().getSharedPreferences() - .edit() - .putBoolean(getString(R.string.key_use_account_color), Boolean.valueOf(newValue.toString())) - .commit(); + .edit() + .putBoolean(getString(R.string.key_use_account_color), + Boolean.valueOf(newValue.toString())) + .commit(); } + return true; } diff --git a/app/src/main/java/org/gnucash/android/ui/settings/TransactionsPreferenceFragment.java b/app/src/main/java/org/gnucash/android/ui/settings/TransactionsPreferenceFragment.java index a388e2632..5878f83f7 100644 --- a/app/src/main/java/org/gnucash/android/ui/settings/TransactionsPreferenceFragment.java +++ b/app/src/main/java/org/gnucash/android/ui/settings/TransactionsPreferenceFragment.java @@ -22,17 +22,18 @@ import android.support.v7.app.AppCompatActivity; import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceFragmentCompat; +import android.support.v7.preference.PreferenceManager; import android.support.v7.preference.SwitchPreferenceCompat; import org.gnucash.android.R; +import org.gnucash.android.app.GnuCashApplication; import org.gnucash.android.db.DatabaseSchema; import org.gnucash.android.db.adapter.AccountsDbAdapter; import org.gnucash.android.db.adapter.BooksDbAdapter; -import org.gnucash.android.db.adapter.CommoditiesDbAdapter; +import org.gnucash.android.model.AccountType; import org.gnucash.android.model.Commodity; import org.gnucash.android.ui.settings.dialog.DeleteAllTransactionsConfirmationDialog; -import java.util.Currency; import java.util.List; /** @@ -44,6 +45,7 @@ public class TransactionsPreferenceFragment extends PreferenceFragmentCompat imp @Override public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); getPreferenceManager().setSharedPreferencesName(BooksDbAdapter.getInstance().getActiveBookUID()); @@ -55,29 +57,62 @@ public void onCreate(Bundle savedInstanceState) { } @Override - public void onCreatePreferences(Bundle bundle, String s) { + public void onCreatePreferences(Bundle bundle, + String s) { + addPreferencesFromResource(R.xml.fragment_transaction_preferences); } @Override public void onResume() { + super.onResume(); - - SharedPreferences sharedPreferences = getPreferenceManager().getSharedPreferences(); - String defaultTransactionType = sharedPreferences.getString( - getString(R.string.key_default_transaction_type), - getString(R.string.label_debit)); - Preference pref = findPreference(getString(R.string.key_default_transaction_type)); - setLocalizedSummary(pref, defaultTransactionType); + + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(GnuCashApplication.getAppContext()); + + // + // Default Transaction Type computing mode + // + + String keyDefaultTransactionType = getString(R.string.key_default_transaction_type); + String defaultTransactionTypeKey = sharedPreferences.getString(keyDefaultTransactionType, + AccountType.KEY_USE_NORMAL_BALANCE_EXPENSE); + + Preference pref = findPreference(keyDefaultTransactionType); + setPrefSummary(pref, + defaultTransactionTypeKey); + pref.setOnPreferenceChangeListener(this); - pref = findPreference(getString(R.string.key_use_double_entry)); - pref.setOnPreferenceChangeListener(this); + // + // Double entry + // - String keyCompactView = getString(R.string.key_use_compact_list); - SwitchPreferenceCompat switchPref = (SwitchPreferenceCompat) findPreference(keyCompactView); + pref = findPreference(getString(R.string.key_use_double_entry)); + pref.setOnPreferenceChangeListener(this); + + // + // Compact list + // + + String keyCompactView = getString(R.string.key_use_compact_list); + SwitchPreferenceCompat switchPref = (SwitchPreferenceCompat) findPreference(keyCompactView); switchPref.setChecked(sharedPreferences.getBoolean(keyCompactView, false)); + // + // Display negative signums + // + + String keyDisplayNegativeSignumInSplits = getString(R.string.key_display_negative_signum_in_splits); + switchPref = (SwitchPreferenceCompat) findPreference(keyDisplayNegativeSignumInSplits); + switchPref.setChecked(sharedPreferences.getBoolean(keyDisplayNegativeSignumInSplits, + false)); + switchPref.setOnPreferenceChangeListener(this); + + // + // Save opening balance + // + String keySaveBalance = getString(R.string.key_save_opening_balances); switchPref = (SwitchPreferenceCompat) findPreference(keySaveBalance); switchPref.setChecked(sharedPreferences.getBoolean(keySaveBalance, false)); @@ -97,13 +132,54 @@ public boolean onPreferenceClick(Preference preference) { } @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - if (preference.getKey().equals(getString(R.string.key_use_double_entry))){ + public boolean onPreferenceChange(Preference preference, + Object newValue) { + + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(GnuCashApplication.getAppContext()); + + // + // Preference : key_default_transaction_type + // + + if (preference.getKey() + .equals(getString(R.string.key_default_transaction_type))) { + + // Store the new value of the Preference + sharedPreferences.edit() + .putString(getString(R.string.key_default_transaction_type), + newValue.toString()) + .commit(); + + setPrefSummary(preference, + ((String) newValue)); + } + + // + // Preference : key_use_double_entry + // + + if (preference.getKey() + .equals(getString(R.string.key_use_double_entry))) { + boolean useDoubleEntry = (Boolean) newValue; setImbalanceAccountsHidden(useDoubleEntry); - } else { - setLocalizedSummary(preference, newValue.toString()); - } + + } + + // + // Preference : key_display_negative_signum_in_splits + // + + if (preference.getKey() + .equals(getString(R.string.key_display_negative_signum_in_splits))) { + + // Store the new value of the Preference + sharedPreferences.edit() + .putBoolean(getString(R.string.key_display_negative_signum_in_splits), + Boolean.valueOf(newValue.toString())) + .commit(); + } + return true; } @@ -133,12 +209,23 @@ private void setImbalanceAccountsHidden(boolean useDoubleEntry) { } } /** - * Localizes the label for DEBIT/CREDIT in the settings summary + * Localizes the label for AUTOMATIC/DEBIT/CREDIT in the settings summary + * * @param preference Preference whose summary is to be localized - * @param value New value for the preference summary + * @param defaultTransactionTypeKey New defaultTransactionTypeKey for the preference summary */ - private void setLocalizedSummary(Preference preference, String value){ - String localizedLabel = value.equals("DEBIT") ? getString(R.string.label_debit) : getActivity().getString(R.string.label_credit); + private void setPrefSummary(Preference preference, + String defaultTransactionTypeKey) { + + String localizedLabel = AccountType.KEY_USE_NORMAL_BALANCE_EXPENSE.equals(defaultTransactionTypeKey) + ? getString(R.string.label_use_account_usual_balance_expense_mode) + : AccountType.KEY_USE_NORMAL_BALANCE_INCOME.equals(defaultTransactionTypeKey) + ? getString(R.string.label_use_account_usual_balance_income_mode) + : AccountType.KEY_DEBIT.equals(defaultTransactionTypeKey) + ? getString(R.string.label_debit) + : getString(R.string.label_credit); + + preference.setSummary(localizedLabel); } diff --git a/app/src/main/java/org/gnucash/android/ui/transaction/SplitEditorFragment.java b/app/src/main/java/org/gnucash/android/ui/transaction/SplitEditorFragment.java index 1494b16c7..d4e3503f2 100644 --- a/app/src/main/java/org/gnucash/android/ui/transaction/SplitEditorFragment.java +++ b/app/src/main/java/org/gnucash/android/ui/transaction/SplitEditorFragment.java @@ -25,6 +25,7 @@ import android.support.v4.widget.SimpleCursorAdapter; import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; +import android.support.v7.preference.PreferenceManager; import android.text.Editable; import android.text.TextWatcher; import android.util.Log; @@ -47,6 +48,7 @@ import net.objecthunter.exp4j.ExpressionBuilder; import org.gnucash.android.R; +import org.gnucash.android.app.GnuCashApplication; import org.gnucash.android.db.DatabaseSchema; import org.gnucash.android.db.adapter.AccountsDbAdapter; import org.gnucash.android.db.adapter.CommoditiesDbAdapter; @@ -79,6 +81,10 @@ */ public class SplitEditorFragment extends Fragment { + // + // SplitEditorFragment + // + @BindView(R.id.split_list_layout) LinearLayout mSplitsLinearLayout; @BindView(R.id.calculator_keyboard) KeyboardView mKeyboardView; @BindView(R.id.imbalance_textview) TextView mImbalanceTextView; @@ -147,7 +153,11 @@ public void onActivityCreated(Bundle savedInstanceState) { View view = addSplitView(split); view.findViewById(R.id.input_accounts_spinner).setEnabled(false); view.findViewById(R.id.btn_remove_split).setVisibility(View.GONE); - TransactionsActivity.displayBalance(mImbalanceTextView, new Money(mBaseAmount.negate(), mCommodity)); + + // Display imbalance with signum and currency as if it was an asset + AccountType.ASSET.displayBalance(mImbalanceTextView, + new Money(mBaseAmount.negate(), + mCommodity)); } } @@ -197,13 +207,23 @@ public boolean onOptionsItemSelected(MenuItem item) { * @return Returns the split view which was added */ private View addSplitView(Split split){ + LayoutInflater layoutInflater = getActivity().getLayoutInflater(); - View splitView = layoutInflater.inflate(R.layout.item_split_entry, mSplitsLinearLayout, false); - mSplitsLinearLayout.addView(splitView,0); - SplitViewHolder viewHolder = new SplitViewHolder(splitView, split); - splitView.setTag(viewHolder); - mSplitItemViewList.add(splitView); - return splitView; + + View splitEntryView = layoutInflater.inflate(R.layout.item_split_entry, + mSplitsLinearLayout, + false); + + // Respect sort list order + mSplitsLinearLayout.addView(splitEntryView); + + SplitViewHolder viewHolder = new SplitViewHolder(splitEntryView, + split); + splitEntryView.setTag(viewHolder); + + mSplitItemViewList.add(splitEntryView); + + return splitEntryView; } /** @@ -216,111 +236,14 @@ private void initArgs() { mAccountUID = ((FormActivity) getActivity()).getCurrentAccountUID(); mBaseAmount = new BigDecimal(args.getString(UxArgument.AMOUNT_STRING)); - String conditions = "(" + // Get account list that are not hidden nor placeholder, and sort them with Favorites first + String where = "(" + DatabaseSchema.AccountEntry.COLUMN_HIDDEN + " = 0 AND " + DatabaseSchema.AccountEntry.COLUMN_PLACEHOLDER + " = 0" + ")"; - mCursor = mAccountsDbAdapter.fetchAccountsOrderedByFullName(conditions, null); - mCommodity = CommoditiesDbAdapter.getInstance().getCommodity(mAccountsDbAdapter.getCurrencyCode(mAccountUID)); - } - - /** - * Holds a split item view and binds the items in it - */ - class SplitViewHolder implements OnTransferFundsListener{ - @BindView(R.id.input_split_memo) EditText splitMemoEditText; - @BindView(R.id.input_split_amount) CalculatorEditText splitAmountEditText; - @BindView(R.id.btn_remove_split) ImageView removeSplitButton; - @BindView(R.id.input_accounts_spinner) Spinner accountsSpinner; - @BindView(R.id.split_currency_symbol) TextView splitCurrencyTextView; - @BindView(R.id.split_uid) TextView splitUidTextView; - @BindView(R.id.btn_split_type) TransactionTypeSwitch splitTypeSwitch; - - View splitView; - Money quantity; - - public SplitViewHolder(View splitView, Split split){ - ButterKnife.bind(this, splitView); - this.splitView = splitView; - if (split != null && !split.getQuantity().equals(split.getValue())) - this.quantity = split.getQuantity(); - setListeners(split); - } - - @Override - public void transferComplete(Money amount) { - quantity = amount; - } - - private void setListeners(Split split){ - splitAmountEditText.bindListeners(mCalculatorKeyboard); - - removeSplitButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - mSplitsLinearLayout.removeView(splitView); - mSplitItemViewList.remove(splitView); - mImbalanceWatcher.afterTextChanged(null); - } - }); - - updateTransferAccountsList(accountsSpinner); - - splitCurrencyTextView.setText(mCommodity.getSymbol()); - splitTypeSwitch.setAmountFormattingListener(splitAmountEditText, splitCurrencyTextView); - splitTypeSwitch.setChecked(mBaseAmount.signum() > 0); - splitUidTextView.setText(BaseModel.generateUID()); - - if (split != null) { - splitAmountEditText.setCommodity(split.getValue().getCommodity()); - splitAmountEditText.setValue(split.getFormattedValue().asBigDecimal()); - splitCurrencyTextView.setText(split.getValue().getCommodity().getSymbol()); - splitMemoEditText.setText(split.getMemo()); - splitUidTextView.setText(split.getUID()); - String splitAccountUID = split.getAccountUID(); - setSelectedTransferAccount(mAccountsDbAdapter.getID(splitAccountUID), accountsSpinner); - splitTypeSwitch.setAccountType(mAccountsDbAdapter.getAccountType(splitAccountUID)); - splitTypeSwitch.setChecked(split.getType()); - } + mCursor = mAccountsDbAdapter.fetchAccountsOrderedByFavoriteAndFullName(where, null); - accountsSpinner.setOnItemSelectedListener(new SplitAccountListener(splitTypeSwitch, this)); - splitTypeSwitch.addOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - mImbalanceWatcher.afterTextChanged(null); - } - }); - splitAmountEditText.addTextChangedListener(mImbalanceWatcher); - } - - /** - * Returns the value of the amount in the splitAmountEditText field without setting the value to the view - *

If the expression in the view is currently incomplete or invalid, null is returned. - * This method is used primarily for computing the imbalance

- * @return Value in the split item amount field, or {@link BigDecimal#ZERO} if the expression is empty or invalid - */ - public BigDecimal getAmountValue(){ - String amountString = splitAmountEditText.getCleanString(); - if (amountString.isEmpty()) - return BigDecimal.ZERO; - - ExpressionBuilder expressionBuilder = new ExpressionBuilder(amountString); - Expression expression; - - try { - expression = expressionBuilder.build(); - } catch (RuntimeException e) { - return BigDecimal.ZERO; - } - - if (expression != null && expression.validate().isValid()) { - return new BigDecimal(expression.evaluate()); - } else { - Log.v(SplitEditorFragment.this.getClass().getSimpleName(), - "Incomplete expression for updating imbalance: " + expression); - return BigDecimal.ZERO; - } - } + mCommodity = CommoditiesDbAdapter.getInstance().getCommodity(mAccountsDbAdapter.getCurrencyCode(mAccountUID)); } /** @@ -335,6 +258,7 @@ private void setSelectedTransferAccount(long accountId, final Spinner accountsSp } } } + /** * Updates the list of possible transfer accounts. * Only accounts with the same currency can be transferred to @@ -381,117 +305,449 @@ private void saveSplits() { * Extracts the input from the views and builds {@link org.gnucash.android.model.Split}s to correspond to the input. * @return List of {@link org.gnucash.android.model.Split}s represented in the view */ - private ArrayList extractSplitsFromView(){ + private ArrayList extractSplitsFromView() { + ArrayList splitList = new ArrayList<>(); + for (View splitView : mSplitItemViewList) { + SplitViewHolder viewHolder = (SplitViewHolder) splitView.getTag(); - if (viewHolder.splitAmountEditText.getValue() == null) + + if (viewHolder.splitAmountEditText.getValue() == null) { + // + continue; - BigDecimal amountBigDecimal = viewHolder.splitAmountEditText.getValue(); + } else { + // - String currencyCode = mAccountsDbAdapter.getCurrencyCode(mAccountUID); - Money valueAmount = new Money(amountBigDecimal.abs(), Commodity.getInstance(currencyCode)); + BigDecimal amountBigDecimal = viewHolder.splitAmountEditText.getValue(); + + String currencyCode = mAccountsDbAdapter.getCurrencyCode(mAccountUID); + + Money valueAmount = new Money(amountBigDecimal.abs(), + Commodity.getInstance(currencyCode)); + + String accountUID = mAccountsDbAdapter.getUID(viewHolder.accountsSpinner.getSelectedItemId()); + + Split split = new Split(valueAmount, + accountUID); + + split.setMemo(viewHolder.splitMemoEditText.getText() + .toString()); + split.setType(viewHolder.splitTypeSwitch.getTransactionType()); + split.setUID(viewHolder.splitUidTextView.getText() + .toString() + .trim()); + if (viewHolder.quantity != null) { + split.setQuantity(viewHolder.quantity.abs()); + } + + splitList.add(split); + } + + } // for - String accountUID = mAccountsDbAdapter.getUID(viewHolder.accountsSpinner.getSelectedItemId()); - Split split = new Split(valueAmount, accountUID); - split.setMemo(viewHolder.splitMemoEditText.getText().toString()); - split.setType(viewHolder.splitTypeSwitch.getTransactionType()); - split.setUID(viewHolder.splitUidTextView.getText().toString().trim()); - if (viewHolder.quantity != null) - split.setQuantity(viewHolder.quantity.abs()); - splitList.add(split); - } return splitList; } + // + // SplitViewHolder + // + + /** + * Holds a split item view and binds the items in it + */ + class SplitViewHolder + implements OnTransferFundsListener { + + @BindView(R.id.split_currency_symbol) + TextView splitCurrencyTextView; + @BindView(R.id.input_split_amount) + CalculatorEditText splitAmountEditText; + @BindView(R.id.btn_split_type) + TransactionTypeSwitch splitTypeSwitch; + @BindView(R.id.btn_remove_split) + ImageView removeSplitButton; + @BindView(R.id.input_accounts_spinner) + Spinner accountsSpinner; + @BindView(R.id.input_split_memo) + EditText splitMemoEditText; + @BindView(R.id.split_uid) + TextView splitUidTextView; + + private View splitView; + private Money quantity; + + public SplitViewHolder(View splitView, + Split split) { + + ButterKnife.bind(this, + splitView); + + this.splitView = splitView; + + // Set Listeners + setListeners(split); + + if (split != null && !split.getQuantity() + .equals(split.getValue())) { + this.quantity = split.getQuantity(); + } + + // Init Views from split + initViews(split); + } + + private void setListeners(Split split) { + + // + // Listeners on splitAmountEditText + // + + splitAmountEditText.bindListeners(mCalculatorKeyboard); + + splitAmountEditText.addTextChangedListener(mImbalanceWatcher); + + // + // Listeners on removeSplitButton + // + + removeSplitButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + + mSplitsLinearLayout.removeView(splitView); + mSplitItemViewList.remove(splitView); + mImbalanceWatcher.afterTextChanged(null); + } + }); + + // + // Listeners on accountsSpinner + // + + accountsSpinner.setOnItemSelectedListener(new SplitTransferAccountSelectedListener(splitTypeSwitch, + this)); + + // + // Listeners on splitTypeSwitch + // + + // Set a ColorizeOnTransactionTypeChange listener + splitTypeSwitch.setColorizeOnCheckedChangeListener(); + + splitTypeSwitch.addOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, + boolean isChecked) { + + if (split != null) { + // Split not null + + // Change Transaction Type according to splitTypeSwitch + split.setType(splitTypeSwitch.getTransactionType()); + + // Update Split Amount Signum + updateSplitAmountEditText(split); + + // Recompute Split List Balance + mImbalanceWatcher.afterTextChanged(null); + + } else { + // Split is null + + // RAF + } + } + }); + } + + private void initViews(final Split split) { + + // + // splitTypeSwitch + // + + splitTypeSwitch.setViewsToColorize(splitAmountEditText, + splitCurrencyTextView); + + // Switch on/off according to amount signum + splitTypeSwitch.setChecked(mBaseAmount.signum() > 0); + + // + // Fill spinner + // + + updateTransferAccountsList(accountsSpinner); + + // + // Display Currency + // + + splitCurrencyTextView.setText(mCommodity.getSymbol()); + + // + // uid + // + + splitUidTextView.setText(BaseModel.generateUID()); + + // + // Fill views from Split data + // + + if (split != null) { + // There is a valid Split + + splitAmountEditText.setCommodity(split.getValue() + .getCommodity()); + + // Update Split Amount EditText + updateSplitAmountEditText(split); + + splitCurrencyTextView.setText(split.getValue() + .getCommodity() + .getSymbol()); + + splitMemoEditText.setText(split.getMemo()); + + splitUidTextView.setText(split.getUID()); + + String splitAccountUID = split.getAccountUID(); + setSelectedTransferAccount(mAccountsDbAdapter.getID(splitAccountUID), + accountsSpinner); + + splitTypeSwitch.setAccountType(mAccountsDbAdapter.getAccountType(splitAccountUID)); + + splitTypeSwitch.setChecked(split.getType()); + } + } + + private void updateSplitAmountEditText(final Split split) { + + // Get Preference about showing signum in Splits + boolean shallDisplayNegativeSignumInSplits = PreferenceManager.getDefaultSharedPreferences(getActivity()) + .getBoolean(getString(R.string.key_display_negative_signum_in_splits), + false); + + final Money splitValueWithSignum = split.getValueWithSignum(); + + AccountType accountType = GnuCashApplication.getAccountsDbAdapter() + .getAccountType(split.getAccountUID()); + + // Display abs value because switch button is visible + accountType.displayBalanceWithoutCurrency(splitAmountEditText, + splitValueWithSignum, + shallDisplayNegativeSignumInSplits, + false); + } + + /** + * Returns the value of the amount in the splitAmountEditText field without setting the value to the view + *

If the expression in the view is currently incomplete or invalid, null is returned. + * This method is used primarily for computing the imbalance

+ * + * @return Value in the split item amount field, or {@link BigDecimal#ZERO} if the expression is empty or invalid + */ + public BigDecimal getAmountValue() { + + String amountString = splitAmountEditText.getCleanString(); + if (amountString.isEmpty()) { + return BigDecimal.ZERO; + } + + ExpressionBuilder expressionBuilder = new ExpressionBuilder(amountString); + Expression expression; + + try { + expression = expressionBuilder.build(); + } catch (RuntimeException e) { + return BigDecimal.ZERO; + } + + if (expression != null && expression.validate() + .isValid()) { + return new BigDecimal(expression.evaluate()); + } else { + Log.v(SplitEditorFragment.this.getClass() + .getSimpleName(), + "Incomplete expression for updating imbalance: " + expression); + return BigDecimal.ZERO; + } + } + + @Override + public void transferComplete(Money amount) { + + quantity = amount; + } + + } + + // + // BalanceTextWatcher + // + /** - * Updates the displayed balance of the accounts when the amount of a split is changed + * Updates the displayed balance of the list of Splits when the amount of a split is changed */ - private class BalanceTextWatcher implements TextWatcher { + private class BalanceTextWatcher + implements TextWatcher { @Override - public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) { + public void beforeTextChanged(CharSequence charSequence, + int i, + int i2, + int i3) { //nothing to see here, move along } @Override - public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) { + public void onTextChanged(CharSequence charSequence, + int i, + int i2, + int i3) { //nothing to see here, move along } @Override public void afterTextChanged(Editable editable) { + + // + // Compute Split balance + // + BigDecimal imbalance = BigDecimal.ZERO; for (View splitItem : mSplitItemViewList) { + SplitViewHolder viewHolder = (SplitViewHolder) splitItem.getTag(); - BigDecimal amount = viewHolder.getAmountValue().abs(); + + // Get the absolute value of the amount + BigDecimal absAmount = viewHolder.getAmountValue() + .abs(); + long accountId = viewHolder.accountsSpinner.getSelectedItemId(); - boolean hasDebitNormalBalance = AccountsDbAdapter.getInstance() - .getAccountType(accountId).hasDebitNormalBalance(); + + // #876 May be usefull for debug +// String accountFullName = AccountsDbAdapter.getInstance() +// .getAccountFullName(AccountsDbAdapter.getInstance() +// .getUID(accountId)); + + // #876 +// boolean hasDebitNormalBalance = AccountsDbAdapter.getInstance() +// .getAccountType(accountId) +// .hasDebitNormalBalance(); if (viewHolder.splitTypeSwitch.isChecked()) { - if (hasDebitNormalBalance) - imbalance = imbalance.add(amount); - else - imbalance = imbalance.subtract(amount); + // Switch is CREDIT + + // #876 +// if (hasDebitNormalBalance) { +// imbalance = imbalance.add(absAmount); +// } else { +// imbalance = imbalance.subtract(absAmount); +// } + imbalance = imbalance.add(absAmount); + } else { - if (hasDebitNormalBalance) - imbalance = imbalance.subtract(amount); - else - imbalance = imbalance.add(amount); + // Switch is DEBIT + + // #876 +// if (hasDebitNormalBalance) { +// imbalance = imbalance.subtract(absAmount); +// } else { +// imbalance = imbalance.add(absAmount); +// } + imbalance = imbalance.subtract(absAmount); + } - } + } // for + +// AccountType accountType = mAccountsDbAdapter.getAccountType(mAccountUID); - TransactionsActivity.displayBalance(mImbalanceTextView, new Money(imbalance, mCommodity)); + // Display imbalance with signum and currency as if it was an asset + AccountType.ASSET.displayBalance(mImbalanceTextView, + new Money(imbalance, + mCommodity)); } } + // + // SplitTransferAccountSelectedListener + // + /** * Listens to changes in the transfer account and updates the currency symbol, the label of the * transaction type and if neccessary */ - private class SplitAccountListener implements AdapterView.OnItemSelectedListener { - TransactionTypeSwitch mTypeToggleButton; - SplitViewHolder mSplitViewHolder; + private class SplitTransferAccountSelectedListener + implements AdapterView.OnItemSelectedListener { + + private TransactionTypeSwitch mTransactionTypeSwitch; + private SplitViewHolder mSplitViewHolder; /** * Flag to know when account spinner callback is due to user interaction or layout of components */ boolean userInteraction = false; - public SplitAccountListener(TransactionTypeSwitch typeToggleButton, SplitViewHolder viewHolder){ - this.mTypeToggleButton = typeToggleButton; + public SplitTransferAccountSelectedListener(TransactionTypeSwitch transactionTypeSwitch, + SplitViewHolder viewHolder) { + this.mSplitViewHolder = viewHolder; + + this.mTransactionTypeSwitch = transactionTypeSwitch; + this.mTransactionTypeSwitch.setViewsToColorize(mSplitViewHolder.splitAmountEditText, + mSplitViewHolder.splitCurrencyTextView); } + /** + * Called when user has chosen a new Account for the split + * using the spinner + * + * @param parentView + * @param selectedItemView + * @param position + * @param id + */ @Override - public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { + public void onItemSelected(AdapterView parentView, + View selectedItemView, + int position, + long id) { + AccountType accountType = mAccountsDbAdapter.getAccountType(id); - mTypeToggleButton.setAccountType(accountType); + + mTransactionTypeSwitch.setAccountType(accountType); //refresh the imbalance amount if we change the account mImbalanceWatcher.afterTextChanged(null); - String fromCurrencyCode = mAccountsDbAdapter.getCurrencyCode(mAccountUID); + String fromCurrencyCode = mAccountsDbAdapter.getCurrencyCode(mAccountUID); String targetCurrencyCode = mAccountsDbAdapter.getCurrencyCode(mAccountsDbAdapter.getUID(id)); - if (!userInteraction || fromCurrencyCode.equals(targetCurrencyCode)){ + if (!userInteraction || fromCurrencyCode.equals(targetCurrencyCode)) { //first call is on layout, subsequent calls will be true and transfer will work as usual userInteraction = true; return; } BigDecimal amountBigD = mSplitViewHolder.splitAmountEditText.getValue(); - if (amountBigD == null) + if (amountBigD == null) { return; + } + + Money amount = new Money(amountBigD, + Commodity.getInstance(fromCurrencyCode)); - Money amount = new Money(amountBigD, Commodity.getInstance(fromCurrencyCode)); - TransferFundsDialogFragment fragment - = TransferFundsDialogFragment.getInstance(amount, targetCurrencyCode, mSplitViewHolder); - fragment.show(getFragmentManager(), "tranfer_funds_editor"); + TransferFundsDialogFragment fragment = TransferFundsDialogFragment.getInstance(amount, + targetCurrencyCode, + mSplitViewHolder); + fragment.show(getFragmentManager(), + "tranfer_funds_editor"); } @Override diff --git a/app/src/main/java/org/gnucash/android/ui/transaction/TransactionDetailActivity.java b/app/src/main/java/org/gnucash/android/ui/transaction/TransactionDetailActivity.java index 44771f872..62b210d8e 100644 --- a/app/src/main/java/org/gnucash/android/ui/transaction/TransactionDetailActivity.java +++ b/app/src/main/java/org/gnucash/android/ui/transaction/TransactionDetailActivity.java @@ -6,6 +6,7 @@ import android.os.Build; import android.os.Bundle; import android.support.v7.app.ActionBar; +import android.support.v7.preference.PreferenceManager; import android.support.v7.widget.Toolbar; import android.view.LayoutInflater; import android.view.MenuItem; @@ -18,6 +19,7 @@ import org.gnucash.android.db.adapter.AccountsDbAdapter; import org.gnucash.android.db.adapter.ScheduledActionDbAdapter; import org.gnucash.android.db.adapter.TransactionsDbAdapter; +import org.gnucash.android.model.AccountType; import org.gnucash.android.model.Money; import org.gnucash.android.model.ScheduledAction; import org.gnucash.android.model.Split; @@ -40,14 +42,56 @@ */ public class TransactionDetailActivity extends PasscodeLockActivity { - @BindView(R.id.trn_description) TextView mTransactionDescription; - @BindView(R.id.trn_time_and_date) TextView mTimeAndDate; - @BindView(R.id.trn_recurrence) TextView mRecurrence; - @BindView(R.id.trn_notes) TextView mNotes; + class SplitAmountViewHolder { + @BindView(R.id.split_account_name) TextView accountName; + @BindView(R.id.split_debit) TextView splitDebitView; + @BindView(R.id.split_credit) TextView splitCreditView; + + View itemView; + + public SplitAmountViewHolder(View view, + Split split) { + + itemView = view; + + ButterKnife.bind(this, + view); + + AccountsDbAdapter accountsDbAdapter = AccountsDbAdapter.getInstance(); + + final String accountUID = split.getAccountUID(); + + accountName.setText(accountsDbAdapter.getAccountFullName(accountUID)); + + // splitSignedAmount (positive or negative number) + Money splitSignedAmount = split.getValueWithSignum(); + + // Use debit or credit view + TextView balanceView = splitSignedAmount.isNegative() + ? splitCreditView + : splitDebitView; + + final AccountType accountType = AccountsDbAdapter.getInstance() + .getAccountType(split.getAccountUID()); + + // Display absolute value because it is displayed either in debit or credit column + accountType.displayBalance(balanceView, + splitSignedAmount, + false, + true, + true); + } + + } // Class SplitAmountViewHolder + @BindView(R.id.toolbar) Toolbar mToolBar; + @BindView(R.id.trn_description) TextView mTransactionDescription; @BindView(R.id.transaction_account) TextView mTransactionAccount; @BindView(R.id.balance_debit) TextView mDebitBalance; @BindView(R.id.balance_credit) TextView mCreditBalance; + @BindView(R.id.trn_time_and_date) TextView mTimeAndDate; + @BindView(R.id.trn_recurrence) TextView mRecurrence; + @BindView(R.id.trn_notes) TextView mNotes; @BindView(R.id.fragment_transaction_details) TableLayout mDetailTableLayout; @@ -92,61 +136,120 @@ protected void onCreate(Bundle savedInstanceState) { } - class SplitAmountViewHolder { - @BindView(R.id.split_account_name) TextView accountName; - @BindView(R.id.split_debit) TextView splitDebit; - @BindView(R.id.split_credit) TextView splitCredit; - - View itemView; - - public SplitAmountViewHolder(View view, Split split){ - itemView = view; - ButterKnife.bind(this, view); - - AccountsDbAdapter accountsDbAdapter = AccountsDbAdapter.getInstance(); - accountName.setText(accountsDbAdapter.getAccountFullName(split.getAccountUID())); - Money quantity = split.getFormattedQuantity(); - TextView balanceView = quantity.isNegative() ? splitDebit : splitCredit; - TransactionsActivity.displayBalance(balanceView, quantity); - } - } - /** * Reads the transaction information from the database and binds it to the views */ - private void bindViews(){ + private void bindViews() { + TransactionsDbAdapter transactionsDbAdapter = TransactionsDbAdapter.getInstance(); Transaction transaction = transactionsDbAdapter.getRecord(mTransactionUID); + // Transaction description mTransactionDescription.setText(transaction.getDescription()); - mTransactionAccount.setText(getString(R.string.label_inside_account_with_name, AccountsDbAdapter.getInstance().getAccountFullName(mAccountUID))); - AccountsDbAdapter accountsDbAdapter = AccountsDbAdapter.getInstance(); + // Account Full Name + mTransactionAccount.setText(getString(R.string.label_inside_account_with_name, AccountsDbAdapter.getInstance().getAccountFullName(mAccountUID))); - Money accountBalance = accountsDbAdapter.getAccountBalance(mAccountUID, -1, transaction.getTimeMillis()); - TextView balanceTextView = accountBalance.isNegative() ? mDebitBalance : mCreditBalance; - TransactionsActivity.displayBalance(balanceTextView, accountBalance); + // + // Add Debit/Credit Labels + // mDetailTableRows = mDetailTableLayout.getChildCount(); - boolean useDoubleEntry = GnuCashApplication.isDoubleEntryEnabled(); - LayoutInflater inflater = LayoutInflater.from(this); int index = 0; + + LayoutInflater inflater = LayoutInflater.from(this); + View view = inflater.inflate(R.layout.item_split_amount_info, + mDetailTableLayout, + false); + ((TextView) view.findViewById(R.id.split_debit)).setText(getString(R.string.label_debit)); + ((TextView) view.findViewById(R.id.split_credit)).setText(getString(R.string.label_credit)); + mDetailTableLayout.addView(view, + index++); + + // + // Détails + // + + AccountsDbAdapter accountsDbAdapter = AccountsDbAdapter.getInstance(); + + boolean useDoubleEntry = GnuCashApplication.isDoubleEntryEnabled(); + for (Split split : transaction.getSplits()) { - if (!useDoubleEntry && split.getAccountUID().equals( - accountsDbAdapter.getImbalanceAccountUID(split.getValue().getCommodity()))) { - //do now show imbalance accounts for single entry use case - continue; + + if (!useDoubleEntry && split.getAccountUID() + .equals(accountsDbAdapter.getImbalanceAccountUID(split.getValue() + .getCommodity()))) { + //do not show imbalance accounts for single entry use case + + } else { + +// // +// // Add Account Specific Debit/Credit Labels +// // +// +// view = inflater.inflate(R.layout.item_split_amount_info, +// mDetailTableLayout, +// false); +// +// AccountType accountType = AccountsDbAdapter.getInstance().getAccountType(split.getAccountUID()); +// +// ((TextView) view.findViewById(R.id.split_debit)).setText(AccountTypeUtils.getLabelDebit(accountType)); +// ((TextView) view.findViewById(R.id.split_credit)).setText(AccountTypeUtils.getLabelCredit(accountType)); +// +// mDetailTableLayout.addView(view, +// index++); + + // + // Display Debit/Credit amount + // + + view = inflater.inflate(R.layout.item_split_amount_info, + mDetailTableLayout, + false); + + SplitAmountViewHolder viewHolder = new SplitAmountViewHolder(view, + split); + mDetailTableLayout.addView(viewHolder.itemView, + index++); } - View view = inflater.inflate(R.layout.item_split_amount_info, mDetailTableLayout, false); - SplitAmountViewHolder viewHolder = new SplitAmountViewHolder(view, split); - mDetailTableLayout.addView(viewHolder.itemView, index++); - } + } // for + + // + // Account balance at Transaction time + // + + // Compute balance at Transaction time + Money accountBalance = accountsDbAdapter.getAccountBalance(mAccountUID, + -1, + transaction.getTimeMillis()); + + // #8xx + // Use Debit TextView to display the account balance (with signum) +// TextView balanceTextView = accountBalance.isNegative() +// ? mCreditBalance +// : mDebitBalance; + TextView balanceTextView = mDebitBalance; + final AccountType accountType = accountsDbAdapter.getAccountType(mAccountUID); + + accountType.displayBalance(balanceTextView, + accountBalance, + true, + true, + true); + + // + // Date + // Date trnDate = new Date(transaction.getTimeMillis()); String timeAndDate = DateFormat.getDateInstance(DateFormat.FULL).format(trnDate); mTimeAndDate.setText(timeAndDate); + // + // + // + if (transaction.getScheduledActionUID() != null){ ScheduledAction scheduledAction = ScheduledActionDbAdapter.getInstance().getRecord(transaction.getScheduledActionUID()); mRecurrence.setText(scheduledAction.getRepeatString()); diff --git a/app/src/main/java/org/gnucash/android/ui/transaction/TransactionFormFragment.java b/app/src/main/java/org/gnucash/android/ui/transaction/TransactionFormFragment.java index ad9f36e03..8712fb804 100644 --- a/app/src/main/java/org/gnucash/android/ui/transaction/TransactionFormFragment.java +++ b/app/src/main/java/org/gnucash/android/ui/transaction/TransactionFormFragment.java @@ -29,6 +29,7 @@ import android.support.v4.widget.SimpleCursorAdapter; import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; +import android.support.v7.preference.PreferenceManager; import android.text.format.DateUtils; import android.util.Log; import android.util.Pair; @@ -43,6 +44,7 @@ import android.widget.AdapterView; import android.widget.AutoCompleteTextView; import android.widget.CheckBox; +import android.widget.CompoundButton; import android.widget.EditText; import android.widget.FilterQueryProvider; import android.widget.ImageView; @@ -251,19 +253,28 @@ public class TransactionFormFragment extends Fragment implements * Create the view and retrieve references to the UI elements */ @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - View v = inflater.inflate(R.layout.fragment_transaction_form, container, false); - ButterKnife.bind(this, v); + public View onCreateView(LayoutInflater inflater, + ViewGroup container, + Bundle savedInstanceState) { + + View v = inflater.inflate(R.layout.fragment_transaction_form, + container, + false); + + ButterKnife.bind(this, + v); + mAmountEditText.bindListeners(mKeyboardView); + mOpenSplitEditor.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { + openSplitEditor(); } }); return v; - } + } /** * Starts the transfer of funds from one currency to another @@ -296,7 +307,9 @@ public void onConfigurationChanged(Configuration newConfig) { @Override public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + setHasOptionsMenu(true); SharedPreferences sharedPrefs = PreferenceActivity.getActiveBookSharedPreferences(); @@ -318,8 +331,10 @@ public void onActivityCreated(Bundle savedInstanceState) { } setListeners(); + //updateTransferAccountsList must only be called after initializing mAccountsDbAdapter updateTransferAccountsList(); + mTransferAccountSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { /** * Flag for ignoring first call to this listener. @@ -427,25 +442,51 @@ public Cursor runQuery(CharSequence name) { }); mDescriptionEditText.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override - public void onItemClick(AdapterView adapterView, View view, int position, long id) { - mTransaction = new Transaction(mTransactionsDbAdapter.getRecord(id), true); + public void onItemClick(AdapterView adapterView, + View view, + int position, + long id) { + + mTransaction = new Transaction(mTransactionsDbAdapter.getRecord(id), + true); + mTransaction.setTime(System.currentTimeMillis()); + //we check here because next method will modify it and we want to catch user-modification boolean amountEntered = mAmountEditText.isInputModified(); + initializeViewsWithTransaction(); + List splitList = mTransaction.getSplits(); - boolean isSplitPair = splitList.size() == 2 && splitList.get(0).isPairOf(splitList.get(1)); - if (isSplitPair){ + boolean isSplitPair = splitList.size() == 2 && splitList.get(0) + .isPairOf(splitList.get(1)); + if (isSplitPair) { + mSplitsList.clear(); - if (!amountEntered) //if user already entered an amount - mAmountEditText.setValue(splitList.get(0).getValue().asBigDecimal()); + + if (!amountEntered) { + // user already entered an amount + + final Money firstSplitAmount = splitList.get(0) + .getValue(); + updateAmountEditText(firstSplitAmount); + } + } else { - if (amountEntered){ //if user entered own amount, clear loaded splits and use the user value + + if (amountEntered) { + //if user entered own amount, clear loaded splits and use the user value + mSplitsList.clear(); setDoubleEntryViewsVisibility(View.VISIBLE); + } else { - if (mUseDoubleEntry) { //don't hide the view in single entry mode + + if (mUseDoubleEntry) { + //don't hide the view in single entry mode + setDoubleEntryViewsVisibility(View.GONE); } } @@ -462,16 +503,10 @@ public void onItemClick(AdapterView adapterView, View view, int position, lon * This method is called if the fragment is used for editing a transaction */ private void initializeViewsWithTransaction(){ + mDescriptionEditText.setText(mTransaction.getDescription()); mDescriptionEditText.setSelection(mDescriptionEditText.getText().length()); - mTransactionTypeSwitch.setAccountType(mAccountType); - mTransactionTypeSwitch.setChecked(mTransaction.getBalance(mAccountUID).isNegative()); - - if (!mAmountEditText.isInputModified()){ - //when autocompleting, only change the amount if the user has not manually changed it already - mAmountEditText.setValue(mTransaction.getBalance(mAccountUID).asBigDecimal()); - } mCurrencyTextView.setText(mTransaction.getCommodity().getSymbol()); mNotesEditText.setText(mTransaction.getNote()); mDateTextView.setText(DATE_FORMATTER.format(mTransaction.getTimeMillis())); @@ -482,7 +517,10 @@ private void initializeViewsWithTransaction(){ //TODO: deep copy the split list. We need a copy so we can modify with impunity mSplitsList = new ArrayList<>(mTransaction.getSplits()); - toggleAmountInputEntryMode(mSplitsList.size() <= 2); + + final boolean isSimpleSplit = mSplitsList.size() <= 2; + + setAllowAmountEdit(isSimpleSplit); if (mSplitsList.size() == 2){ for (Split split : mSplitsList) { @@ -493,20 +531,33 @@ private void initializeViewsWithTransaction(){ } } } + //if there are more than two splits (which is the default for one entry), then //disable editing of the transfer account. User should open editor if (mSplitsList.size() == 2 && mSplitsList.get(0).isPairOf(mSplitsList.get(1))) { + for (Split split : mTransaction.getSplits()) { //two splits, one belongs to this account and the other to another account if (mUseDoubleEntry && !split.getAccountUID().equals(mAccountUID)) { setSelectedTransferAccount(mAccountsDbAdapter.getID(split.getAccountUID())); } } + } else { setDoubleEntryViewsVisibility(View.GONE); } - String currencyCode = mTransactionsDbAdapter.getAccountCurrencyCode(mAccountUID); + if (!mAmountEditText.isInputModified()) { + //when autocompleting, only change the amount if the user has not manually changed it already + + // Compute balance signed value of saved transaction + final Money signedTransactionBalance = Transaction.computeBalance(mAccountUID, + mSplitsList); + + updateAmountEditText(signedTransactionBalance); + } + + String currencyCode = mTransactionsDbAdapter.getAccountCurrencyCode(mAccountUID); Commodity accountCommodity = Commodity.getInstance(currencyCode); mCurrencyTextView.setText(accountCommodity.getSymbol()); @@ -514,6 +565,7 @@ private void initializeViewsWithTransaction(){ mAmountEditText.setCommodity(commodity); mSaveTemplateCheckbox.setChecked(mTransaction.isTemplate()); + String scheduledActionUID = getArguments().getString(UxArgument.SCHEDULED_ACTION_UID); if (scheduledActionUID != null && !scheduledActionUID.isEmpty()) { ScheduledAction scheduledAction = ScheduledActionDbAdapter.getInstance().getRecord(scheduledActionUID); @@ -521,6 +573,43 @@ private void initializeViewsWithTransaction(){ mEventRecurrence.parse(mRecurrenceRule); mRecurrenceTextView.setText(scheduledAction.getRepeatString()); } + + mTransactionTypeSwitch.setViewsToColorize(mAmountEditText, + mCurrencyTextView); + mTransactionTypeSwitch.setAccountType(mAccountType); + mTransactionTypeSwitch.setChecked(mTransaction.getBalance(mAccountUID) + .isNegative()); + + + } + + private void updateAmountEditText(final Money signedTransactionBalance) { + + // Get Preference about showing signum in Splits + boolean shallDisplayNegativeSignumInSplits = PreferenceManager.getDefaultSharedPreferences(getActivity()) + .getBoolean(getString(R.string.key_display_negative_signum_in_splits), + false); + + final boolean isCredit = signedTransactionBalance.isNegative(); + + // + // Negate balance if account is usually creditor + // + + AccountType accountType = GnuCashApplication.getAccountsDbAdapter() + .getAccountType(mAccountUID); + + // New signed transaction balance if typeSwitch has changed + final Money newSignedTransactionBalance = mTransactionTypeSwitch.isChecked() != isCredit + ? signedTransactionBalance.negate() + : signedTransactionBalance; + + final boolean isTransactionTypeSwitchVisible = mTransactionTypeSwitch.getVisibility() != View.GONE; + + accountType.displayBalanceWithoutCurrency(mAmountEditText, + newSignedTransactionBalance, + shallDisplayNegativeSignumInSplits || !isTransactionTypeSwitchVisible, + !isTransactionTypeSwitchVisible); } private void setDoubleEntryViewsVisibility(int visibility) { @@ -528,15 +617,25 @@ private void setDoubleEntryViewsVisibility(int visibility) { mTransactionTypeSwitch.setVisibility(visibility); } - private void toggleAmountInputEntryMode(boolean enabled){ - if (enabled){ + private void setAllowAmountEdit(boolean isAmountEditAllowed){ + + if (isAmountEditAllowed){ + // Amount is allowed to be edited using keyboard + mAmountEditText.setFocusable(true); + + // Use Keyboard to edit amount mAmountEditText.bindListeners(mKeyboardView); + } else { + // Amount is not allowed to be edited, but can be clicked to open SplitEditor + mAmountEditText.setFocusable(false); + mAmountEditText.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { + openSplitEditor(); } }); @@ -547,14 +646,29 @@ public void onClick(View v) { * Initialize views with default data for new transactions */ private void initalizeViews() { + Date time = new Date(System.currentTimeMillis()); mDateTextView.setText(DATE_FORMATTER.format(time)); mTimeTextView.setText(TIME_FORMATTER.format(time)); mTime = mDate = Calendar.getInstance(); + mTransactionTypeSwitch.setViewsToColorize(mAmountEditText, + mCurrencyTextView); + mTransactionTypeSwitch.setAccountType(mAccountType); - String typePref = PreferenceActivity.getActiveBookSharedPreferences().getString(getString(R.string.key_default_transaction_type), "DEBIT"); - mTransactionTypeSwitch.setChecked(TransactionType.valueOf(typePref)); + + // + // According to the Default Transaction Type Preference and the AccountType + // Check or Uncheck the TransactionTypeSwitch + // + + final TransactionType transactionType = mAccountType.getDefaultTransactionType(); + + mTransactionTypeSwitch.setChecked(transactionType); + + // + // Display Transaction Amount and Currency + // String code = GnuCashApplication.getDefaultCurrencyCode(); if (mAccountUID != null){ @@ -562,9 +676,15 @@ private void initalizeViews() { } Commodity commodity = Commodity.getInstance(code); + mCurrencyTextView.setText(commodity.getSymbol()); + mAmountEditText.setCommodity(commodity); + // + // Select Transfer Other Account + // + if (mUseDoubleEntry){ String currentAccountUID = mAccountUID; long defaultTransferAccountID; @@ -572,7 +692,10 @@ private void initalizeViews() { do { defaultTransferAccountID = mAccountsDbAdapter.getDefaultTransferAccountID(mAccountsDbAdapter.getID(currentAccountUID)); if (defaultTransferAccountID > 0) { + + // Select Other Account setSelectedTransferAccount(defaultTransferAccountID); + break; //we found a parent with default transfer setting } currentAccountUID = mAccountsDbAdapter.getParentAccountUID(currentAccountUID); @@ -602,40 +725,77 @@ private void updateTransferAccountsList(){ /** * Opens the split editor dialog */ - private void openSplitEditor(){ - if (mAmountEditText.getValue() == null){ - Toast.makeText(getActivity(), R.string.toast_enter_amount_to_split, Toast.LENGTH_SHORT).show(); - return; - } + private void openSplitEditor() { String baseAmountString; - if (mTransaction == null){ //if we are creating a new transaction (not editing an existing one) - BigDecimal enteredAmount = mAmountEditText.getValue(); + if (mTransaction == null) { + // we are creating a new transaction (not editing an existing one) + + BigDecimal enteredAmount = mAmountEditText.getValue() != null + ? mAmountEditText.getValue() + : new BigDecimal(0); + baseAmountString = enteredAmount.toPlainString(); + } else { + // we are editing an existing transaction + + // + // Find splits biggest amount (in absolute value) + // + Money biggestAmount = Money.createZeroInstance(mTransaction.getCurrencyCode()); + for (Split split : mTransaction.getSplits()) { - if (split.getValue().asBigDecimal().compareTo(biggestAmount.asBigDecimal()) > 0) + if (split.getValue() + .asBigDecimal() + .compareTo(biggestAmount.asBigDecimal()) > 0) { biggestAmount = split.getValue(); - } + } + } // for + baseAmountString = biggestAmount.toPlainString(); } - Intent intent = new Intent(getActivity(), FormActivity.class); - intent.putExtra(UxArgument.FORM_TYPE, FormActivity.FormType.SPLIT_EDITOR.name()); - intent.putExtra(UxArgument.SELECTED_ACCOUNT_UID, mAccountUID); - intent.putExtra(UxArgument.AMOUNT_STRING, baseAmountString); - intent.putParcelableArrayListExtra(UxArgument.SPLIT_LIST, (ArrayList) extractSplitsFromView()); + Intent intent = new Intent(getActivity(), + FormActivity.class); + intent.putExtra(UxArgument.FORM_TYPE, + FormActivity.FormType.SPLIT_EDITOR.name()); + intent.putExtra(UxArgument.SELECTED_ACCOUNT_UID, + mAccountUID); + intent.putExtra(UxArgument.AMOUNT_STRING, + baseAmountString); + + intent.putParcelableArrayListExtra(UxArgument.SPLIT_LIST, + (ArrayList) extractSplitsFromView()); - startActivityForResult(intent, REQUEST_SPLIT_EDITOR); + startActivityForResult(intent, + REQUEST_SPLIT_EDITOR); } /** * Sets click listeners for the dialog buttons */ - private void setListeners() { - mTransactionTypeSwitch.setAmountFormattingListener(mAmountEditText, mCurrencyTextView); + private void setListeners() { + + mTransactionTypeSwitch.setColorizeOnCheckedChangeListener(); + + mTransactionTypeSwitch.addOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, + boolean isChecked) { + + // UI to Transaction + Transaction transaction = extractTransactionFromView(); + + // Compute balance signed value of transaction + final Money signedTransactionBalance = transaction.getBalance(mAccountUID); + + // Update Amount Signum + updateAmountEditText(signedTransactionBalance); + } + }); mDateTextView.setOnClickListener(new View.OnClickListener() { @@ -699,35 +859,46 @@ private void setSelectedTransferAccount(long accountId){ /** * Returns a list of splits based on the input in the transaction form. + * * This only gets the splits from the simple view, and not those from the Split Editor. * If the Split Editor has been used and there is more than one split, then it returns {@link #mSplitsList} * @return List of splits in the view or {@link #mSplitsList} is there are more than 2 splits in the transaction */ - private List extractSplitsFromView(){ - if (mTransactionTypeSwitch.getVisibility() != View.VISIBLE){ + private List extractSplitsFromView() { + + if (mTransactionTypeSwitch.getVisibility() != View.VISIBLE) { return mSplitsList; } - BigDecimal amountBigd = mAmountEditText.getValue(); + BigDecimal amountBigD = mAmountEditText.getValue(); + String baseCurrencyCode = mTransactionsDbAdapter.getAccountCurrencyCode(mAccountUID); - Money value = new Money(amountBigd, Commodity.getInstance(baseCurrencyCode)); + + // Store signed amount + Money value = new Money(amountBigD, + Commodity.getInstance(baseCurrencyCode)); + Money quantity = new Money(value); - String transferAcctUID = getTransferAccountUID(); - CommoditiesDbAdapter cmdtyDbAdapter = CommoditiesDbAdapter.getInstance(); + String transferAcctUID = getTransferAccountUID(); + CommoditiesDbAdapter cmdtyDbAdapter = CommoditiesDbAdapter.getInstance(); + + if (isMultiCurrencyTransaction()) { + // multi-currency transaction - if (isMultiCurrencyTransaction()){ //if multi-currency transaction String transferCurrencyCode = mAccountsDbAdapter.getCurrencyCode(transferAcctUID); - String commodityUID = cmdtyDbAdapter.getCommodityUID(baseCurrencyCode); - String targetCmdtyUID = cmdtyDbAdapter.getCommodityUID(transferCurrencyCode); - + String commodityUID = cmdtyDbAdapter.getCommodityUID(baseCurrencyCode); + String targetCmdtyUID = cmdtyDbAdapter.getCommodityUID(transferCurrencyCode); + Pair pricePair = PricesDbAdapter.getInstance() - .getPrice(commodityUID, targetCmdtyUID); + .getPrice(commodityUID, + targetCmdtyUID); if (pricePair.first > 0 && pricePair.second > 0) { + quantity = quantity.multiply(pricePair.first.intValue()) - .divide(pricePair.second.intValue()) - .withCurrency(cmdtyDbAdapter.getRecord(targetCmdtyUID)); + .divide(pricePair.second.intValue()) + .withCurrency(cmdtyDbAdapter.getRecord(targetCmdtyUID)); } } @@ -735,21 +906,38 @@ private List extractSplitsFromView(){ Split split2; // Try to preserve the other split attributes. if (mSplitsList.size() >= 2) { + // Multi-split + split1 = mSplitsList.get(0); + // Store absolute value split1.setValue(value); + // Store absolute value split1.setQuantity(value); split1.setAccountUID(mAccountUID); split2 = mSplitsList.get(1); + // Store absolute value split2.setValue(value); + // Store absolute value split2.setQuantity(quantity); split2.setAccountUID(transferAcctUID); + } else { - split1 = new Split(value, mAccountUID); - split2 = new Split(value, quantity, transferAcctUID); + // Only 2 splits (usual case) + + // Store absolute value + split1 = new Split(value, + mAccountUID); + + // Store absolute value + split2 = new Split(value, + quantity, + transferAcctUID); } + split1.setType(mTransactionTypeSwitch.getTransactionType()); - split2.setType(mTransactionTypeSwitch.getTransactionType().invert()); + split2.setType(mTransactionTypeSwitch.getTransactionType() + .invert()); List splitList = new ArrayList<>(); splitList.add(split1); @@ -780,6 +968,7 @@ private List extractSplitsFromView(){ * @return New transaction object containing all info in the form */ private @NonNull Transaction extractTransactionFromView(){ + Calendar cal = new GregorianCalendar( mDate.get(Calendar.YEAR), mDate.get(Calendar.MONTH), @@ -801,6 +990,13 @@ private List extractSplitsFromView(){ transaction.setSplits(splits); transaction.setExported(false); //not necessary as exports use timestamps now. Because, legacy + if (mEditMode) { + // Editing an existing transaction + + // reset the transaction UID + transaction.setUID(mTransaction.getUID()); + } + return transaction; } @@ -835,6 +1031,7 @@ private boolean isMultiCurrencyTransaction(){ * and save a transaction */ private void saveNewTransaction() { + mAmountEditText.getCalculatorKeyboard().hideCustomKeyboard(); //determine whether we need to do currency conversion @@ -845,12 +1042,16 @@ private void saveNewTransaction() { return; } - Transaction transaction = extractTransactionFromView(); - if (mEditMode) { //if editing an existing transaction - transaction.setUID(mTransaction.getUID()); - } + // + // UI to Transaction, with same UID if transaction edit mode + // + + mTransaction = extractTransactionFromView(); + + // + // Save transaction in DB + // - mTransaction = transaction; mAccountsDbAdapter.beginTransaction(); try { @@ -978,11 +1179,14 @@ private boolean canSave(){ * @param splitList List of splits produced in the fragment */ public void setSplitList(List splitList){ + mSplitsList = splitList; + Money balance = Transaction.computeBalance(mAccountUID, mSplitsList); - mAmountEditText.setValue(balance.asBigDecimal()); mTransactionTypeSwitch.setChecked(balance.isNegative()); + + updateAmountEditText(balance); } @@ -1075,14 +1279,24 @@ public void onRecurrenceSet(String rrule) { @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { + if (resultCode == Activity.RESULT_OK){ - List splitList = data.getParcelableArrayListExtra(UxArgument.SPLIT_LIST); - setSplitList(splitList); + // Splits have been saved => Use - //once split editor has been used and saved, only allow editing through it - toggleAmountInputEntryMode(false); - setDoubleEntryViewsVisibility(View.GONE); + // Once split editor has been used and saved, only allow editing through it + + // Do not allow keyboard anymore to edit amount + setAllowAmountEdit(false); + + // Display Split Editor button mOpenSplitEditor.setVisibility(View.VISIBLE); + + // Hide double Entry + setDoubleEntryViewsVisibility(View.GONE); + + // Set Split list from intent data coming from Split Editor + List splitList = data.getParcelableArrayListExtra(UxArgument.SPLIT_LIST); + setSplitList(splitList); } } } diff --git a/app/src/main/java/org/gnucash/android/ui/transaction/TransactionsActivity.java b/app/src/main/java/org/gnucash/android/ui/transaction/TransactionsActivity.java index fb4ba91ef..609eef7d6 100644 --- a/app/src/main/java/org/gnucash/android/ui/transaction/TransactionsActivity.java +++ b/app/src/main/java/org/gnucash/android/ui/transaction/TransactionsActivity.java @@ -32,6 +32,7 @@ import android.support.v4.app.FragmentStatePagerAdapter; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; +import android.support.v7.preference.PreferenceManager; import android.text.format.DateUtils; import android.util.Log; import android.util.SparseArray; @@ -50,7 +51,6 @@ import org.gnucash.android.db.adapter.AccountsDbAdapter; import org.gnucash.android.db.adapter.TransactionsDbAdapter; import org.gnucash.android.model.Account; -import org.gnucash.android.model.Money; import org.gnucash.android.ui.account.AccountsActivity; import org.gnucash.android.ui.account.AccountsListFragment; import org.gnucash.android.ui.account.OnAccountClickedListener; @@ -62,7 +62,6 @@ import org.gnucash.android.util.QualifiedAccountNameCursorAdapter; import org.joda.time.LocalDate; -import java.math.BigDecimal; import java.text.SimpleDateFormat; import java.util.Date; @@ -78,7 +77,7 @@ public class TransactionsActivity extends BaseDrawerActivity implements /** * Logging tag */ - protected static final String TAG = "TransactionsActivity"; + protected static final String LOG_TAG = "TransactionsActivity"; /** * ViewPager index for sub-accounts fragment @@ -120,7 +119,7 @@ public class TransactionsActivity extends BaseDrawerActivity implements private SparseArray mFragmentPageReferenceMap = new SparseArray<>(); /** - * Flag for determining is the currently displayed account is a placeholder account or not. + * Flag for determining if the currently displayed account is a placeholder account or not. * This will determine if the transactions tab is displayed or not */ private boolean mIsPlaceholderAccount; @@ -128,10 +127,18 @@ public class TransactionsActivity extends BaseDrawerActivity implements private AdapterView.OnItemSelectedListener mTransactionListNavigationListener = new AdapterView.OnItemSelectedListener() { @Override - public void onItemSelected(AdapterView parent, View view, int position, long id) { + public void onItemSelected(AdapterView parent, View spinnerSelectedItemView, int position, long id) { + mAccountUID = mAccountsDbAdapter.getUID(id); - getIntent().putExtra(UxArgument.SELECTED_ACCOUNT_UID, mAccountUID); //update the intent in case the account gets rotated - mIsPlaceholderAccount = mAccountsDbAdapter.isPlaceholderAccount(mAccountUID); + getIntent().putExtra(UxArgument.SELECTED_ACCOUNT_UID, + getCurrentAccountUID()); //update the intent in case the account gets rotated + + // + // Show Transaction Page if not a PlaceHolder, hide otherwise + // + + mIsPlaceholderAccount = mAccountsDbAdapter.isPlaceholderAccount(getCurrentAccountUID()); + if (mIsPlaceholderAccount){ if (mTabLayout.getTabCount() > 1) { mPagerAdapter.notifyDataSetChanged(); @@ -143,9 +150,9 @@ public void onItemSelected(AdapterView parent, View view, int position, long mTabLayout.addTab(mTabLayout.newTab().setText(R.string.section_header_transactions)); } } - if (view != null) { + if (spinnerSelectedItemView != null) { // Hide the favorite icon of the selected account to avoid clutter - ((TextView) view).setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); + ((TextView) spinnerSelectedItemView).setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); } //refresh any fragments in the tab with the new account UID refresh(); @@ -229,7 +236,8 @@ public int getCount() { private AccountsListFragment prepareSubAccountsListFragment(){ AccountsListFragment subAccountsListFragment = new AccountsListFragment(); Bundle args = new Bundle(); - args.putString(UxArgument.PARENT_ACCOUNT_UID, mAccountUID); + args.putString(UxArgument.PARENT_ACCOUNT_UID, + getCurrentAccountUID()); subAccountsListFragment.setArguments(args); return subAccountsListFragment; } @@ -238,12 +246,17 @@ private AccountsListFragment prepareSubAccountsListFragment(){ * Creates and initializes fragment for displaying transactions * @return {@link TransactionsListFragment} initialized with the current account transactions */ - private TransactionsListFragment prepareTransactionsListFragment(){ + private TransactionsListFragment prepareTransactionsListFragment() { + TransactionsListFragment transactionsListFragment = new TransactionsListFragment(); - Bundle args = new Bundle(); - args.putString(UxArgument.SELECTED_ACCOUNT_UID, mAccountUID); + Bundle args = new Bundle(); + args.putString(UxArgument.SELECTED_ACCOUNT_UID, + getCurrentAccountUID()); transactionsListFragment.setArguments(args); - Log.i(TAG, "Opening transactions for account: " + mAccountUID); + + Log.i(LOG_TAG, + "Opening transactions for account: " + getCurrentAccountUID()); + return transactionsListFragment; } } @@ -260,13 +273,15 @@ public void refresh(String accountUID) { if (mPagerAdapter != null) mPagerAdapter.notifyDataSetChanged(); - new AccountBalanceTask(mSumTextView).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, mAccountUID); + new AccountBalanceTask(mSumTextView).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, + getCurrentAccountUID()); } @Override public void refresh(){ - refresh(mAccountUID); + + refresh(getCurrentAccountUID()); setTitleIndicatorColor(); } @@ -282,6 +297,7 @@ public int getTitleRes() { @Override protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); getSupportActionBar().setDisplayShowTitleEnabled(false); @@ -289,7 +305,11 @@ protected void onCreate(Bundle savedInstanceState) { mAccountUID = getIntent().getStringExtra(UxArgument.SELECTED_ACCOUNT_UID); mAccountsDbAdapter = AccountsDbAdapter.getInstance(); - mIsPlaceholderAccount = mAccountsDbAdapter.isPlaceholderAccount(mAccountUID); + // + // Add Transaction Page + // + + mIsPlaceholderAccount = mAccountsDbAdapter.isPlaceholderAccount(getCurrentAccountUID()); mTabLayout.addTab(mTabLayout.newTab().setText(R.string.section_header_subaccounts)); if (!mIsPlaceholderAccount) { @@ -320,8 +340,9 @@ public void onTabReselected(TabLayout.Tab tab) { }); //if there are no transactions, and there are sub-accounts, show the sub-accounts - if (TransactionsDbAdapter.getInstance().getTransactionsCount(mAccountUID) == 0 - && mAccountsDbAdapter.getSubAccountCount(mAccountUID) > 0){ + if (TransactionsDbAdapter.getInstance() + .getTransactionsCount(getCurrentAccountUID()) == 0 + && mAccountsDbAdapter.getSubAccountCount(getCurrentAccountUID()) > 0) { mViewPager.setCurrentItem(INDEX_SUB_ACCOUNTS_FRAGMENT); } else { mViewPager.setCurrentItem(INDEX_TRANSACTIONS_FRAGMENT); @@ -335,13 +356,14 @@ public void onClick(View v) { Intent addAccountIntent = new Intent(TransactionsActivity.this, FormActivity.class); addAccountIntent.setAction(Intent.ACTION_INSERT_OR_EDIT); addAccountIntent.putExtra(UxArgument.FORM_TYPE, FormActivity.FormType.ACCOUNT.name()); - addAccountIntent.putExtra(UxArgument.PARENT_ACCOUNT_UID, mAccountUID); + addAccountIntent.putExtra(UxArgument.PARENT_ACCOUNT_UID, + getCurrentAccountUID()); startActivityForResult(addAccountIntent, AccountsActivity.REQUEST_EDIT_ACCOUNT); ; break; case INDEX_TRANSACTIONS_FRAGMENT: - createNewTransaction(mAccountUID); + createNewTransaction(getCurrentAccountUID()); break; } @@ -359,7 +381,10 @@ protected void onResume() { * Sets the color for the ViewPager title indicator to match the account color */ private void setTitleIndicatorColor() { - int iColor = AccountsDbAdapter.getActiveAccountColorResource(mAccountUID); + + final String currentAccountUID = getCurrentAccountUID(); + + int iColor = AccountsDbAdapter.getActiveAccountColorResource(currentAccountUID); mTabLayout.setBackgroundColor(iColor); @@ -374,7 +399,11 @@ private void setTitleIndicatorColor() { * Set up action bar navigation list and listener callbacks */ private void setupActionBarNavigation() { + + // // set up spinner adapter for navigation list + // + if (mAccountsCursor != null) { mAccountsCursor.close(); } @@ -387,17 +416,17 @@ private void setupActionBarNavigation() { mToolbarSpinner.setOnItemSelectedListener(mTransactionListNavigationListener); getSupportActionBar().setDisplayHomeAsUpEnabled(true); - updateNavigationSelection(); + selectCurrentAccountInToolbarSpinner(); } /** * Updates the action bar navigation list selection to that of the current account * whose transactions are being displayed/manipulated */ - public void updateNavigationSelection() { - // set the selected item in the spinner - int i = 0; - Cursor accountsCursor = mAccountsDbAdapter.fetchAllRecordsOrderedByFullName(); + public void selectCurrentAccountInToolbarSpinner() { + // set the selected item in the spinner + int i = 0; + Cursor accountsCursor = mAccountsDbAdapter.fetchAllRecordsOrderedByFullName(); while (accountsCursor.moveToNext()) { String uid = accountsCursor.getString(accountsCursor.getColumnIndexOrThrow(DatabaseSchema.AccountEntry.COLUMN_UID)); if (mAccountUID.equals(uid)) { @@ -407,7 +436,7 @@ public void updateNavigationSelection() { ++i; } accountsCursor.close(); - } + } @Override public boolean onPrepareOptionsMenu(Menu menu) { @@ -416,7 +445,8 @@ public boolean onPrepareOptionsMenu(Menu menu) { if (favoriteAccountMenuItem == null) //when the activity is used to edit a transaction return super.onPrepareOptionsMenu(menu); - boolean isFavoriteAccount = AccountsDbAdapter.getInstance().isFavoriteAccount(mAccountUID); + boolean isFavoriteAccount = AccountsDbAdapter.getInstance() + .isFavoriteAccount(getCurrentAccountUID()); int favoriteIcon = isFavoriteAccount ? R.drawable.ic_star_white_24dp : R.drawable.ic_star_border_white_24dp; favoriteAccountMenuItem.setIcon(favoriteIcon); @@ -432,8 +462,8 @@ public boolean onOptionsItemSelected(MenuItem item) { case R.id.menu_favorite_account: AccountsDbAdapter accountsDbAdapter = AccountsDbAdapter.getInstance(); - long accountId = accountsDbAdapter.getID(mAccountUID); - boolean isFavorite = accountsDbAdapter.isFavoriteAccount(mAccountUID); + long accountId = accountsDbAdapter.getID(getCurrentAccountUID()); + boolean isFavorite = accountsDbAdapter.isFavoriteAccount(getCurrentAccountUID()); //toggle favorite preference accountsDbAdapter.updateAccount(accountId, DatabaseSchema.AccountEntry.COLUMN_FAVORITE, isFavorite ? "0" : "1"); supportInvalidateOptionsMenu(); @@ -442,7 +472,8 @@ public boolean onOptionsItemSelected(MenuItem item) { case R.id.menu_edit_account: Intent editAccountIntent = new Intent(this, FormActivity.class); editAccountIntent.setAction(Intent.ACTION_INSERT_OR_EDIT); - editAccountIntent.putExtra(UxArgument.SELECTED_ACCOUNT_UID, mAccountUID); + editAccountIntent.putExtra(UxArgument.SELECTED_ACCOUNT_UID, + getCurrentAccountUID()); editAccountIntent.putExtra(UxArgument.FORM_TYPE, FormActivity.FormType.ACCOUNT.name()); startActivityForResult(editAccountIntent, AccountsActivity.REQUEST_EDIT_ACCOUNT); return true; @@ -465,7 +496,7 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { @Override protected void onDestroy() { super.onDestroy(); - mAccountsCursor.close(); + getAccountsCursor().close(); } /** @@ -476,20 +507,9 @@ public String getCurrentAccountUID(){ return mAccountUID; } - /** - * Display the balance of a transaction in a text view and format the text color to match the sign of the amount - * @param balanceTextView {@link android.widget.TextView} where balance is to be displayed - * @param balance {@link org.gnucash.android.model.Money} balance to display - */ - public static void displayBalance(TextView balanceTextView, Money balance){ - balanceTextView.setText(balance.formattedString()); - Context context = GnuCashApplication.getAppContext(); - int fontColor = balance.isNegative() ? - context.getResources().getColor(R.color.debit_red) : - context.getResources().getColor(R.color.credit_green); - if (balance.asBigDecimal().compareTo(BigDecimal.ZERO) == 0) - fontColor = context.getResources().getColor(android.R.color.black); - balanceTextView.setTextColor(fontColor); + public Cursor getAccountsCursor() { + + return mAccountsCursor; } /** @@ -528,7 +548,8 @@ public void createNewTransaction(String accountUID) { public void editTransaction(String transactionUID){ Intent createTransactionIntent = new Intent(this.getApplicationContext(), FormActivity.class); createTransactionIntent.setAction(Intent.ACTION_INSERT_OR_EDIT); - createTransactionIntent.putExtra(UxArgument.SELECTED_ACCOUNT_UID, mAccountUID); + createTransactionIntent.putExtra(UxArgument.SELECTED_ACCOUNT_UID, + getCurrentAccountUID()); createTransactionIntent.putExtra(UxArgument.SELECTED_TRANSACTION_UID, transactionUID); createTransactionIntent.putExtra(UxArgument.FORM_TYPE, FormActivity.FormType.TRANSACTION.name()); startActivity(createTransactionIntent); diff --git a/app/src/main/java/org/gnucash/android/ui/transaction/TransactionsListFragment.java b/app/src/main/java/org/gnucash/android/ui/transaction/TransactionsListFragment.java index 9ea0847e9..22c4d3ed2 100644 --- a/app/src/main/java/org/gnucash/android/ui/transaction/TransactionsListFragment.java +++ b/app/src/main/java/org/gnucash/android/ui/transaction/TransactionsListFragment.java @@ -27,6 +27,7 @@ import android.support.v4.content.Loader; import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; +import android.support.v7.preference.PreferenceManager; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.PopupMenu; @@ -49,6 +50,7 @@ import org.gnucash.android.db.adapter.DatabaseAdapter; import org.gnucash.android.db.adapter.SplitsDbAdapter; import org.gnucash.android.db.adapter.TransactionsDbAdapter; +import org.gnucash.android.model.AccountType; import org.gnucash.android.model.Money; import org.gnucash.android.model.Split; import org.gnucash.android.model.Transaction; @@ -165,7 +167,7 @@ public void refresh(){ @Override public void onResume() { super.onResume(); - ((TransactionsActivity)getActivity()).updateNavigationSelection(); + ((TransactionsActivity)getActivity()).selectCurrentAccountInToolbarSpinner(); refresh(); } @@ -268,6 +270,7 @@ public int getItemViewType(int position) { @Override public void onBindViewHolderCursor(ViewHolder holder, Cursor cursor) { + holder.transactionId = cursor.getLong(cursor.getColumnIndexOrThrow(DatabaseSchema.TransactionEntry._ID)); String description = cursor.getString(cursor.getColumnIndexOrThrow(DatabaseSchema.TransactionEntry.COLUMN_DESCRIPTION)); @@ -275,7 +278,12 @@ public void onBindViewHolderCursor(ViewHolder holder, Cursor cursor) { final String transactionUID = cursor.getString(cursor.getColumnIndexOrThrow(DatabaseSchema.TransactionEntry.COLUMN_UID)); Money amount = mTransactionsDbAdapter.getBalance(transactionUID, mAccountUID); - TransactionsActivity.displayBalance(holder.transactionAmount, amount); + + final AccountType accountType = GnuCashApplication.getAccountsDbAdapter() + .getAccountType(mAccountUID); + + accountType.displayBalance(holder.transactionAmount, + amount); long dateMillis = cursor.getLong(cursor.getColumnIndexOrThrow(DatabaseSchema.TransactionEntry.COLUMN_TIMESTAMP)); String dateText = TransactionsActivity.getPrettyDateFormat(getActivity(), dateMillis); diff --git a/app/src/main/java/org/gnucash/android/ui/transaction/dialog/TransferFundsDialogFragment.java b/app/src/main/java/org/gnucash/android/ui/transaction/dialog/TransferFundsDialogFragment.java index 55bccd9c8..64bf63ec6 100644 --- a/app/src/main/java/org/gnucash/android/ui/transaction/dialog/TransferFundsDialogFragment.java +++ b/app/src/main/java/org/gnucash/android/ui/transaction/dialog/TransferFundsDialogFragment.java @@ -22,6 +22,7 @@ import android.support.annotation.Nullable; import android.support.design.widget.TextInputLayout; import android.support.v4.app.DialogFragment; +import android.support.v7.preference.PreferenceManager; import android.text.Editable; import android.text.TextWatcher; import android.util.Pair; @@ -37,11 +38,11 @@ import org.gnucash.android.R; import org.gnucash.android.db.adapter.CommoditiesDbAdapter; import org.gnucash.android.db.adapter.PricesDbAdapter; +import org.gnucash.android.model.AccountType; import org.gnucash.android.model.Commodity; import org.gnucash.android.model.Money; import org.gnucash.android.model.Price; import org.gnucash.android.ui.transaction.OnTransferFundsListener; -import org.gnucash.android.ui.transaction.TransactionsActivity; import org.gnucash.android.util.AmountParser; import java.math.BigDecimal; @@ -97,7 +98,9 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa View view = inflater.inflate(R.layout.dialog_transfer_funds, container, false); ButterKnife.bind(this, view); - TransactionsActivity.displayBalance(mStartAmountLabel, mOriginAmount); + AccountType.ASSET.displayBalance(mStartAmountLabel, + mOriginAmount); + String fromCurrencyCode = mOriginAmount.getCommodity().getCurrencyCode(); mFromCurrencyLabel.setText(fromCurrencyCode); mToCurrencyLabel.setText(mTargetCommodity.getCurrencyCode()); diff --git a/app/src/main/java/org/gnucash/android/ui/util/AccountBalanceTask.java b/app/src/main/java/org/gnucash/android/ui/util/AccountBalanceTask.java index 8bef261af..23966b580 100644 --- a/app/src/main/java/org/gnucash/android/ui/util/AccountBalanceTask.java +++ b/app/src/main/java/org/gnucash/android/ui/util/AccountBalanceTask.java @@ -24,8 +24,8 @@ import com.crashlytics.android.Crashlytics; import org.gnucash.android.db.adapter.AccountsDbAdapter; +import org.gnucash.android.model.AccountType; import org.gnucash.android.model.Money; -import org.gnucash.android.ui.transaction.TransactionsActivity; import java.lang.ref.WeakReference; @@ -38,9 +38,10 @@ public class AccountBalanceTask extends AsyncTask { public static final String LOG_TAG = AccountBalanceTask.class.getName(); private final WeakReference accountBalanceTextViewReference; - private final AccountsDbAdapter accountsDbAdapter; + private final AccountsDbAdapter accountsDbAdapter; + private String mAccountUID; - public AccountBalanceTask(TextView balanceTextView){ + public AccountBalanceTask(TextView balanceTextView) { accountBalanceTextViewReference = new WeakReference<>(balanceTextView); accountsDbAdapter = AccountsDbAdapter.getInstance(); } @@ -55,7 +56,10 @@ protected Money doInBackground(String... params) { Money balance = Money.getZeroInstance(); try { - balance = accountsDbAdapter.getAccountBalance(params[0], -1, -1); + mAccountUID = params[0]; + balance = accountsDbAdapter.getAccountBalance(mAccountUID, + -1, + -1); } catch (Exception ex) { Log.e(LOG_TAG, "Error computing account balance ", ex); Crashlytics.logException(ex); @@ -65,10 +69,18 @@ protected Money doInBackground(String... params) { @Override protected void onPostExecute(Money balance) { - if (accountBalanceTextViewReference.get() != null && balance != null){ + + if (accountBalanceTextViewReference.get() != null && balance != null) { + final TextView balanceTextView = accountBalanceTextViewReference.get(); - if (balanceTextView != null){ - TransactionsActivity.displayBalance(balanceTextView, balance); + + if (balanceTextView != null) { + + final AccountType accountType = accountsDbAdapter.getAccountType(mAccountUID); + + // Get Preference about showing signum in Splits + accountType.displayBalance(balanceTextView, + balance); } } } diff --git a/app/src/main/java/org/gnucash/android/ui/util/AccountTypeUtils.java b/app/src/main/java/org/gnucash/android/ui/util/AccountTypeUtils.java new file mode 100644 index 000000000..4992b63da --- /dev/null +++ b/app/src/main/java/org/gnucash/android/ui/util/AccountTypeUtils.java @@ -0,0 +1,170 @@ +package org.gnucash.android.ui.util; + +import android.content.Context; + +import org.gnucash.android.R; +import org.gnucash.android.app.GnuCashApplication; +import org.gnucash.android.db.DatabaseSchema; +import org.gnucash.android.model.AccountType; + +/** + * Utilities for AccountType UI + * + * @author JeanGarf + */ +public class AccountTypeUtils { + + /** + * Get the debit label customized for the account type + * + * @param accountType + * Account Type + * + * @return + * The debit label customized for the account type + * + * @author JeanGarf + */ + public static String getLabelDebit(final AccountType accountType) { + + final String label; + + Context context = GnuCashApplication.getAppContext().getApplicationContext(); + + switch (accountType) { + + case CASH: + label = context.getString(R.string.label_receive); // DEBIT + break; + + case BANK: + label = context.getString(R.string.label_deposit); // DEBIT + break; + + case ASSET: + // #876 Change according to GnuCash on Windows + label = context.getString(R.string.label_increase); // DEBIT + break; + + case CREDIT: + label = context.getString(R.string.label_payment); // DEBIT + break; + + case EQUITY: + case LIABILITY: + // #876 Change according to GnuCash on Windows + label = context.getString(R.string.label_decrease); // DEBIT + break; + + case INCOME: + // #876 Change according to GnuCash on Windows + label = context.getString(R.string.label_charge); // DEBIT + break; + + case EXPENSE: + label = context.getString(R.string.label_expense); // DEBIT + break; + + case PAYABLE: + // #876 Change according to GnuCash on Windows + label = context.getString(R.string.label_payment); // DEBIT + break; + + case RECEIVABLE: + label = context.getString(R.string.label_invoice); // DEBIT + break; + + case STOCK: + case MUTUAL: + // #876 Change according to GnuCash on Windows + label = context.getString(R.string.label_buy); // DEBIT + break; + + case CURRENCY: + case ROOT: + default: + // #876 Change according to GnuCash on Windows + label = context.getString(R.string.label_debit); // DEBIT + break; + } + + return label; + } + + /** + * Get the credit label customized for the account type + * + * @param accountType + * Account Type + * + * @return + * The credit label customized for the account type + * + * @author JeanGarf + */ + public static String getLabelCredit(final AccountType accountType) { + + final String label; + + Context context = GnuCashApplication.getAppContext().getApplicationContext(); + + switch (accountType) { + + case CASH: + label = context.getString(R.string.label_spend); // CREDIT + break; + + case BANK: + label = context.getString(R.string.label_withdrawal); // CREDIT + break; + + case ASSET: + // #876 Change according to GnuCash on Windows + label = context.getString(R.string.label_decrease); // CREDIT + break; + + case CREDIT: + label = context.getString(R.string.label_charge); // CREDIT + break; + + case EQUITY: + case LIABILITY: + // #876 Change according to GnuCash on Windows + label = context.getString(R.string.label_increase); // CREDIT + break; + + case INCOME: + // #876 Change according to GnuCash on Windows + label = context.getString(R.string.label_income); // CREDIT + break; + + case EXPENSE: + label = context.getString(R.string.label_rebate); // CREDIT + break; + + case PAYABLE: + // #876 Change according to GnuCash on Windows + label = context.getString(R.string.label_bill); // CREDIT + break; + + case RECEIVABLE: + label = context.getString(R.string.label_payment); // CREDIT + break; + + case STOCK: + case MUTUAL: + // #876 Change according to GnuCash on Windows + label = context.getString(R.string.label_sell); // CREDIT + break; + + case CURRENCY: + case ROOT: + default: + // #876 Change according to GnuCash on Windows + label = context.getString(R.string.label_credit); // CREDIT + break; + } + + return label; + } +} diff --git a/app/src/main/java/org/gnucash/android/ui/util/AccountUtils.java b/app/src/main/java/org/gnucash/android/ui/util/AccountUtils.java new file mode 100644 index 000000000..a72e813c2 --- /dev/null +++ b/app/src/main/java/org/gnucash/android/ui/util/AccountUtils.java @@ -0,0 +1,44 @@ +package org.gnucash.android.ui.util; + +import org.gnucash.android.db.DatabaseSchema; +import org.gnucash.android.model.AccountType; + +/** + * Utilities for Accounts UI + * + * @author JeanGarf + */ +public class AccountUtils { + + /** + * Build the where clause to select Accounts allowed for Transfer + * for the given accountUID + * + * @param accountUID + * The account UID for which we want to collect account allowed for transfer + * May be null (to allow all non special accounts) + * + * @return + * the where clause + * + * @author JeanGarf + */ + public static String getTransfertAccountWhereClause(final String accountUID) { + + return "(" + + DatabaseSchema.AccountEntry.COLUMN_UID + + " != '" + + ((accountUID != null) ? accountUID : "") + + "' AND " + + DatabaseSchema.AccountEntry.COLUMN_TYPE + + " != '" + + AccountType.ROOT.name() + + "' AND " + + DatabaseSchema.AccountEntry.COLUMN_PLACEHOLDER + + " = 0" + + " AND " + + DatabaseSchema.AccountEntry.COLUMN_HIDDEN + + " = 0" + + ")"; + } +} diff --git a/app/src/main/java/org/gnucash/android/ui/util/widget/TransactionTypeSwitch.java b/app/src/main/java/org/gnucash/android/ui/util/widget/TransactionTypeSwitch.java index 9a06fe3ad..a9bf752a5 100644 --- a/app/src/main/java/org/gnucash/android/ui/util/widget/TransactionTypeSwitch.java +++ b/app/src/main/java/org/gnucash/android/ui/util/widget/TransactionTypeSwitch.java @@ -17,104 +17,111 @@ package org.gnucash.android.ui.util.widget; import android.content.Context; -import android.support.v4.content.ContextCompat; +import android.support.annotation.ColorInt; import android.support.v7.widget.SwitchCompat; import android.util.AttributeSet; import android.widget.CompoundButton; import android.widget.TextView; -import org.gnucash.android.R; import org.gnucash.android.model.AccountType; -import org.gnucash.android.model.Transaction; import org.gnucash.android.model.TransactionType; +import org.gnucash.android.ui.util.AccountTypeUtils; -import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; /** - * A special type of {@link android.widget.ToggleButton} which displays the appropriate CREDIT/DEBIT labels for the - * different account types. + * A special type of {@link android.widget.ToggleButton} which displays the appropriate DEBIT/CREDIT labels for the + * linked account type and update the color of the amount and currency fields as well + * + * checked means CREDIT + * unchecked means DEBIT + * * @author Ngewi Fet */ public class TransactionTypeSwitch extends SwitchCompat { + private AccountType mAccountType = AccountType.EXPENSE; + // View to update and colorize + private CalculatorEditText mAmountEditText; + private TextView mCurrencyTextView; + + // Listeners to call in case of change in Transaction Type switch List mOnCheckedChangeListeners = new ArrayList<>(); - public TransactionTypeSwitch(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); + public TransactionTypeSwitch(Context context, + AttributeSet attrs, + int defStyle) { + + super(context, + attrs, + defStyle); } - public TransactionTypeSwitch(Context context, AttributeSet attrs) { - super(context, attrs); + public TransactionTypeSwitch(Context context, + AttributeSet attrs) { + + super(context, + attrs); } public TransactionTypeSwitch(Context context) { + super(context); } - public void setAccountType(AccountType accountType){ - this.mAccountType = accountType; - Context context = getContext().getApplicationContext(); - switch (mAccountType) { - case CASH: - setTextOn(context.getString(R.string.label_spend)); - setTextOff(context.getString(R.string.label_receive)); - break; - case BANK: - setTextOn(context.getString(R.string.label_withdrawal)); - setTextOff(context.getString(R.string.label_deposit)); - break; - case CREDIT: - setTextOn(context.getString(R.string.label_payment)); - setTextOff(context.getString(R.string.label_charge)); - break; - case ASSET: - case EQUITY: - case LIABILITY: - setTextOn(context.getString(R.string.label_decrease)); - setTextOff(context.getString(R.string.label_increase)); - break; - case INCOME: - setTextOn(context.getString(R.string.label_charge)); - setTextOff(context.getString(R.string.label_income)); - break; - case EXPENSE: - setTextOn(context.getString(R.string.label_rebate)); - setTextOff(context.getString(R.string.label_expense)); - break; - case PAYABLE: - setTextOn(context.getString(R.string.label_payment)); - setTextOff(context.getString(R.string.label_bill)); - break; - case RECEIVABLE: - setTextOn(context.getString(R.string.label_payment)); - setTextOff(context.getString(R.string.label_invoice)); - break; - case STOCK: - case MUTUAL: - setTextOn(context.getString(R.string.label_buy)); - setTextOff(context.getString(R.string.label_sell)); - break; - case CURRENCY: - case ROOT: - default: - setTextOn(context.getString(R.string.label_debit)); - setTextOff(context.getString(R.string.label_credit)); - break; - } - setText(isChecked() ? getTextOn() : getTextOff()); + /** + * Store views to colorize (green/red) accordingly to TransactionType and AccountType + * in addition to the current switch button + * + * @param amountEditText + * EditText displaying the amount value + * + * @param currencyTextView + * Currency symbol text view + */ + public void setViewsToColorize(CalculatorEditText amountEditText, + TextView currencyTextView) { + + mAmountEditText = amountEditText; + mCurrencyTextView = currencyTextView; + } + + /** + * Store the accountType + * and define switch on/off texts accordingly + * + * @param accountType + */ + public void setAccountType(AccountType accountType) { + + mAccountType = accountType; + + // + // Set switch button text + // + + setTextOff(AccountTypeUtils.getLabelDebit(mAccountType)); // DEBIT + setTextOn(AccountTypeUtils.getLabelCredit(mAccountType)); // CREDIT + + // + // Set switch text and color + // + + setWidgetTextColor(isChecked()); + invalidate(); } /** - * Set a checked change listener to monitor the amount view and currency views and update the display (color & balance accordingly) - * @param amoutView Amount string {@link android.widget.EditText} - * @param currencyTextView Currency symbol text view + * Bind a ColorizeOnTransactionTypeChangeListener as a switch checked change listener + * to update the signum of amount view, colorize amount, + * currency and switch views accordingly */ - public void setAmountFormattingListener(CalculatorEditText amoutView, TextView currencyTextView){ - setOnCheckedChangeListener(new OnTypeChangedListener(amoutView, currencyTextView)); + public void setColorizeOnCheckedChangeListener() { + + setOnCheckedChangeListener(new ColorizeOnTransactionTypeChangeListener()); } /** @@ -130,65 +137,127 @@ public void addOnCheckedChangeListener(OnCheckedChangeListener checkedChangeList * @param transactionType {@link org.gnucash.android.model.TransactionType} of the split */ public void setChecked(TransactionType transactionType){ - setChecked(Transaction.shouldDecreaseBalance(mAccountType, transactionType)); + // #876 +// setChecked(Transaction.shouldDecreaseBalance(mAccountType, transactionType)); + setChecked(TransactionType.CREDIT.equals(transactionType)); } /** * Returns the account type associated with this button * @return Type of account */ - public AccountType getAccountType(){ + public AccountType getAccountType() { + return mAccountType; } - public TransactionType getTransactionType(){ - if (mAccountType.hasDebitNormalBalance()){ - return isChecked() ? TransactionType.CREDIT : TransactionType.DEBIT; - } else { - return isChecked() ? TransactionType.DEBIT : TransactionType.CREDIT; - } + public TransactionType getTransactionType() { + + // #876 +// if (mAccountType.hasDebitNormalBalance()) { +// +// return isChecked() +// ? TransactionType.CREDIT +// : TransactionType.DEBIT; +// +// } else { +// +// return isChecked() +// ? TransactionType.DEBIT +// : TransactionType.CREDIT; +// } + return isChecked() + ? TransactionType.CREDIT + : TransactionType.DEBIT; } - private class OnTypeChangedListener implements OnCheckedChangeListener{ - private CalculatorEditText mAmountEditText; - private TextView mCurrencyTextView; + /** + * Set the text color of the 3 views of the widget + * according to is a Credit amount + * and the Account type + * + * @param isCredit + * true if amount is negative + */ + private void setWidgetTextColor(final boolean isCredit) { + + // + // Set switch text + // + + setText(isCredit + ? getTextOn() // CREDIT + : getTextOff() // DEBIT + ); + + // + // Change signum if needed + // + +// BigDecimal amount = mAmountEditText.getValue(); +// +// if (amount != null) { +// if ((isCredit && amount.signum() > 0) //we switched to debit but the amount is +ve +// || (!isCredit && amount.signum() < 0)) { //credit but amount is -ve +// +// mAmountEditText.setValue(amount.negate()); +// } +// +// } + + // + // Set text color of views + // + + @ColorInt final int color = getAccountType().getAmountColor(isCredit); + + // Set switch color + TransactionTypeSwitch.this.setTextColor(color); + + // Set Currency color + mAmountEditText.setTextColor(color); + mCurrencyTextView.setTextColor(color); + } + + // + // Inner Class OnTypeChangedListener + // + + /** + * Listener on change on Transaction Type (DEBIT turned to CREDIT or vice-versa) + * which update displayed amount signum and colorize, accordingly + */ + private class ColorizeOnTransactionTypeChangeListener + implements OnCheckedChangeListener{ + /** * Constructor with the amount view - * @param amountEditText EditText displaying the amount value - * @param currencyTextView Currency symbol text view */ - public OnTypeChangedListener(CalculatorEditText amountEditText, TextView currencyTextView){ - this.mAmountEditText = amountEditText; - this.mCurrencyTextView = currencyTextView; + public ColorizeOnTransactionTypeChangeListener(){ + } @Override - public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) { - setText(isChecked ? getTextOn() : getTextOff()); - if (isChecked){ - int red = ContextCompat.getColor(getContext(), R.color.debit_red); - TransactionTypeSwitch.this.setTextColor(red); - mAmountEditText.setTextColor(red); - mCurrencyTextView.setTextColor(red); - } - else { - int green = ContextCompat.getColor(getContext(), R.color.credit_green); - TransactionTypeSwitch.this.setTextColor(green); - mAmountEditText.setTextColor(green); - mCurrencyTextView.setTextColor(green); - } - BigDecimal amount = mAmountEditText.getValue(); - if (amount != null){ - if ((isChecked && amount.signum() > 0) //we switched to debit but the amount is +ve - || (!isChecked && amount.signum() < 0)){ //credit but amount is -ve - mAmountEditText.setValue(amount.negate()); - } - - } + public void onCheckedChanged(CompoundButton compoundButton, + boolean isChecked) { + + final boolean isCredit = isChecked; + + // + // Set switch text and color + // + + setWidgetTextColor(isCredit); + + // + // Call other listeners + // for (OnCheckedChangeListener listener : mOnCheckedChangeListeners) { - listener.onCheckedChanged(compoundButton, isChecked); - } + listener.onCheckedChanged(compoundButton, + isChecked); + } // for } + } } diff --git a/app/src/main/java/org/gnucash/android/util/BookUtils.java b/app/src/main/java/org/gnucash/android/util/BookUtils.java index dbd308d7e..f0e6de4f3 100644 --- a/app/src/main/java/org/gnucash/android/util/BookUtils.java +++ b/app/src/main/java/org/gnucash/android/util/BookUtils.java @@ -25,7 +25,9 @@ public static void activateBook(@NonNull String bookUID){ * @param bookUID GUID of the book to be loaded */ public static void loadBook(@NonNull String bookUID){ + activateBook(bookUID); + AccountsActivity.start(GnuCashApplication.getAppContext()); } } diff --git a/app/src/main/res/layout/activity_transaction_detail.xml b/app/src/main/res/layout/activity_transaction_detail.xml index f0a2d3739..b46f5c272 100644 --- a/app/src/main/res/layout/activity_transaction_detail.xml +++ b/app/src/main/res/layout/activity_transaction_detail.xml @@ -25,6 +25,7 @@ android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> + + + + - - - - - - - - - - - - - - - - - - - + android:stretchColumns="1" + android:orientation="vertical"> - - + - + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + - - + + + + android:layout_height="wrap_content" + android:orientation="horizontal"> + + + - - Préférences des transactions
Préférences du compte Type de transaction par défaut - Le type de transaction à utiliser par défaut, CRÉDIT ou DÉBIT + @string/label_credit + Utiliser le Solde habituel du Compte (sauf pour les Actifs) + Utiliser le Solde habituel du Compte - CRÉDIT - DÉBIT + @string/label_use_account_usual_balance_expense_mode + @string/label_use_account_usual_balance_income_mode + @string/label_debit + @string/label_credit Êtes vous sûre de vouloir supprimer TOUTES les transactions ? Êtes vous sûre de vouloir supprimer cette transaction ? @@ -109,7 +113,7 @@ Email par défaut pour les exports. Vous pourrez toujours le changer lors de votre prochain export. Toutes les transactions seront un transfert d’un compte à un autre Activer la Double entrée - Solde + Solde du Compte à la date indiquée ci-dessous Entrer un nom de compte pour créer un compte Monnaie Compte parent @@ -197,7 +201,7 @@ Augmenter Revenu Remboursement - Frais + Dépense Facture Facture d\'achat Achat @@ -446,4 +450,7 @@ Sélectionnez la destination une fois l\'exportation terminée Exporter dans le dossier \'/Apps/GnuCash Android/\' sur Dropbox Préférences + Afficher le signe - dans les transactions splittées + Autoriser l\'affichage du signe - même lorsque le bouton Débit/Crédit est affiché + Vous devez être sur la page des Comptes pour pouvoir changer de Comptabilité diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 9122dd7d7..80048cb78 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -15,8 +15,12 @@ limitations under the License. --> + #8E24AA + #1E88E5 #c11b17 #4cc552 + #F4511E + #00897B #FFAAAAAA #00000000 #ff33b5e5 diff --git a/app/src/main/res/values/donottranslate.xml b/app/src/main/res/values/donottranslate.xml index 39cae7266..b0e02f15f 100644 --- a/app/src/main/res/values/donottranslate.xml +++ b/app/src/main/res/values/donottranslate.xml @@ -33,12 +33,15 @@ use_account_color last_export_destination use_compact_list + key_display_negative_signum_in_splits prefs_header_general dropbox_access_token backup_location - - CREDIT - DEBIT + + KEY_USE_NORMAL_BALANCE_EXPENSE + KEY_USE_NORMAL_BALANCE_INCOME + KEY_DEBIT + KEY_CREDIT CASH diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4ef7ae17e..74c22cb3d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -95,10 +95,14 @@ Transaction Preferences Account Preferences Default Transaction Type - The type of transaction to use by default, CREDIT or DEBIT + @string/label_credit + Use Account usual Balance (Exept for Assets) + Use Account usual Balance - CREDIT - DEBIT + @string/label_use_account_usual_balance_expense_mode + @string/label_use_account_usual_balance_income_mode + @string/label_debit + @string/label_credit Are you sure you want to delete ALL transactions? Are you sure you want to delete this transaction? @@ -109,7 +113,7 @@ The default email address to send exports to. You can still change this when you export. All transactions will be a transfer from one account to another Activate Double Entry - Balance + Account Balance at Date below Enter an account name to create an account Currency Parent account @@ -370,6 +374,8 @@ Budgets Enable compact view Enable to always use compact view for transactions list + Display negative signum in splits + Enable to display negative signum amount even when debit/credit toggle button is displayed Invalid exchange rate e.g. 1 %1$s = x.xx %2$s Invalid amount @@ -433,6 +439,7 @@ INCOME + You must be on the Account Page to change Book Connected to Google Drive Unable to connect to Google Drive Please enter an amount to split diff --git a/app/src/main/res/xml/fragment_general_preferences.xml b/app/src/main/res/xml/fragment_general_preferences.xml index fb170c2b9..2b1602a47 100644 --- a/app/src/main/res/xml/fragment_general_preferences.xml +++ b/app/src/main/res/xml/fragment_general_preferences.xml @@ -1,18 +1,23 @@ + + + + + - + \ No newline at end of file diff --git a/app/src/main/res/xml/fragment_transaction_preferences.xml b/app/src/main/res/xml/fragment_transaction_preferences.xml index b565114d3..e73300b26 100644 --- a/app/src/main/res/xml/fragment_transaction_preferences.xml +++ b/app/src/main/res/xml/fragment_transaction_preferences.xml @@ -15,16 +15,20 @@ limitations under the License. --> - + android:title="@string/title_default_transaction_type" + android:summary="@string/summary_default_transaction_type" + android:entryValues="@array/key_transaction_types" + android:entries="@array/transaction_types" + /> - + + + +