From 64413271ef4ed943ae29e9cf9115c1bb77053278 Mon Sep 17 00:00:00 2001 From: Damyan Ivanov Date: Fri, 7 May 2021 04:43:12 +0000 Subject: [PATCH] running totals when filtering transactions by account this requires traversing the transaction list in the opposite order - from oldest to newest and corresponding corner cases --- .../async/TransactionAccumulator.java | 69 ++++++++++++++++--- .../ktnx/mobileledger/dao/TransactionDAO.java | 18 +---- .../mobileledger/model/LedgerAccount.java | 3 + .../model/TransactionListItem.java | 7 +- .../net/ktnx/mobileledger/ui/MainModel.java | 2 +- .../ui/activity/MainActivity.java | 2 +- .../TransactionListAdapter.java | 3 +- .../TransactionRowHolder.java | 13 +++- .../main/res/layout/transaction_list_row.xml | 31 ++++++++- app/src/main/res/values/dimens.xml | 1 + 10 files changed, 115 insertions(+), 34 deletions(-) diff --git a/app/src/main/java/net/ktnx/mobileledger/async/TransactionAccumulator.java b/app/src/main/java/net/ktnx/mobileledger/async/TransactionAccumulator.java index 1d86dbf6..d3131f77 100644 --- a/app/src/main/java/net/ktnx/mobileledger/async/TransactionAccumulator.java +++ b/app/src/main/java/net/ktnx/mobileledger/async/TransactionAccumulator.java @@ -17,21 +17,34 @@ package net.ktnx.mobileledger.async; +import androidx.annotation.Nullable; + +import net.ktnx.mobileledger.model.Data; +import net.ktnx.mobileledger.model.LedgerAccount; import net.ktnx.mobileledger.model.LedgerTransaction; +import net.ktnx.mobileledger.model.LedgerTransactionAccount; import net.ktnx.mobileledger.model.TransactionListItem; import net.ktnx.mobileledger.ui.MainModel; +import net.ktnx.mobileledger.utils.Misc; import net.ktnx.mobileledger.utils.SimpleDate; +import java.math.BigDecimal; +import java.math.RoundingMode; import java.util.ArrayList; +import java.util.HashMap; public class TransactionAccumulator { private final ArrayList list = new ArrayList<>(); private final String boldAccountName; + private final String accumulateAccount; + private final HashMap runningTotal = new HashMap<>(); private SimpleDate earliestDate, latestDate; private SimpleDate lastDate; private int transactionCount = 0; - public TransactionAccumulator(String boldAccountName) { + public TransactionAccumulator(@Nullable String boldAccountName, + @Nullable String accumulateAccount) { this.boldAccountName = boldAccountName; + this.accumulateAccount = accumulateAccount; list.add(new TransactionListItem()); // head item } @@ -42,22 +55,60 @@ public class TransactionAccumulator { transactionCount++; // first item - if (null == latestDate) - latestDate = date; - earliestDate = date; + if (null == earliestDate) + earliestDate = date; + latestDate = date; - if (!date.equals(lastDate)) { - if (lastDate == null) - lastDate = SimpleDate.today(); + if (lastDate != null && !date.equals(lastDate)) { boolean showMonth = date.month != lastDate.month || date.year != lastDate.year; - list.add(new TransactionListItem(date, showMonth)); + list.add(1, new TransactionListItem(lastDate, showMonth)); } - list.add(new TransactionListItem(transaction, boldAccountName)); + String currentTotal = null; + if (accumulateAccount != null) { + for (LedgerTransactionAccount acc : transaction.getAccounts()) { + if (acc.getAccountName() + .equals(accumulateAccount) || + LedgerAccount.isParentOf(accumulateAccount, acc.getAccountName())) + { + BigDecimal amt = runningTotal.get(acc.getCurrency()); + if (amt == null) + amt = BigDecimal.ZERO; + BigDecimal newAmount = BigDecimal.valueOf(acc.getAmount()); + newAmount = newAmount.setScale(2, RoundingMode.HALF_EVEN); + amt = amt.add(newAmount); + runningTotal.put(acc.getCurrency(), amt); + } + } + + currentTotal = summarizeRunningTotal(runningTotal); + } + list.add(1, new TransactionListItem(transaction, boldAccountName, currentTotal)); lastDate = date; } + private String summarizeRunningTotal(HashMap runningTotal) { + StringBuilder b = new StringBuilder(); + for (String currency : runningTotal.keySet()) { + if (b.length() != 0) + b.append('\n'); + if (Misc.emptyIsNull(currency) != null) + b.append(currency) + .append(' '); + BigDecimal val = runningTotal.get(currency); + b.append(Data.formatNumber((val == null) ? 0f : val.floatValue())); + } + return b.toString(); + } public void publishResults(MainModel model) { + if (lastDate != null) { + SimpleDate today = SimpleDate.today(); + if (!lastDate.equals(today)) { + boolean showMonth = today.month != lastDate.month || today.year != lastDate.year; + list.add(1, new TransactionListItem(lastDate, showMonth)); + } + } + model.setDisplayedTransactions(list, transactionCount); model.setFirstTransactionDate(earliestDate); model.setLastTransactionDate(latestDate); diff --git a/app/src/main/java/net/ktnx/mobileledger/dao/TransactionDAO.java b/app/src/main/java/net/ktnx/mobileledger/dao/TransactionDAO.java index 78be12ab..9d7fdeae 100644 --- a/app/src/main/java/net/ktnx/mobileledger/dao/TransactionDAO.java +++ b/app/src/main/java/net/ktnx/mobileledger/dao/TransactionDAO.java @@ -108,12 +108,7 @@ public abstract class TransactionDAO extends BaseDAO { @androidx.room.Transaction @Query("SELECT * FROM transactions WHERE profile_id = :profileId ORDER BY year " + - " desc, month desc, day desc, ledger_id desc") - public abstract List getAllWithAccountsSync(long profileId); - - @androidx.room.Transaction - @Query("SELECT * FROM transactions WHERE profile_id = :profileId ORDER BY year " + - " desc, month desc, day desc, ledger_id desc") + " asc, month asc, day asc, ledger_id asc") public abstract LiveData> getAllWithAccounts(long profileId); @androidx.room.Transaction @@ -121,16 +116,7 @@ public abstract class TransactionDAO extends BaseDAO { " tr.day, tr.description, tr.comment, tr.generation FROM transactions tr JOIN " + "transaction_accounts ta ON ta.transaction_id=tr.id WHERE ta.account_name LIKE " + ":accountName||'%' AND ta.amount <> 0 AND tr.profile_id = :profileId ORDER BY tr.year " + - "desc, tr.month desc, tr.day desc, tr.ledger_id desc") - public abstract List getAllWithAccountsFilteredSync(long profileId, - String accountName); - - @androidx.room.Transaction - @Query("SELECT distinct(tr.id), tr.ledger_id, tr.profile_id, tr.data_hash, tr.year, tr.month," + - " tr.day, tr.description, tr.comment, tr.generation FROM transactions tr JOIN " + - "transaction_accounts ta ON ta.transaction_id=tr.id WHERE ta.account_name LIKE " + - ":accountName||'%' AND ta.amount <> 0 AND tr.profile_id = :profileId ORDER BY tr.year " + - "desc, tr.month desc, tr.day desc, tr.ledger_id desc") + "asc, tr.month asc, tr.day asc, tr.ledger_id asc") public abstract LiveData> getAllWithAccountsFiltered( long profileId, String accountName); diff --git a/app/src/main/java/net/ktnx/mobileledger/model/LedgerAccount.java b/app/src/main/java/net/ktnx/mobileledger/model/LedgerAccount.java index 06ac4da1..1be684cf 100644 --- a/app/src/main/java/net/ktnx/mobileledger/model/LedgerAccount.java +++ b/app/src/main/java/net/ktnx/mobileledger/model/LedgerAccount.java @@ -58,6 +58,9 @@ public class LedgerAccount { else return accName.substring(0, colonPos); } + public static boolean isParentOf(@NonNull String possibleParent, @NonNull String accountName) { + return accountName.startsWith(possibleParent + ':'); + } @NonNull static public LedgerAccount fromDBO(AccountWithAmounts in, LedgerAccount parent) { LedgerAccount res = new LedgerAccount(in.account.getName(), parent); diff --git a/app/src/main/java/net/ktnx/mobileledger/model/TransactionListItem.java b/app/src/main/java/net/ktnx/mobileledger/model/TransactionListItem.java index 82a15f36..7c352091 100644 --- a/app/src/main/java/net/ktnx/mobileledger/model/TransactionListItem.java +++ b/app/src/main/java/net/ktnx/mobileledger/model/TransactionListItem.java @@ -30,20 +30,25 @@ public class TransactionListItem { private boolean monthShown; private LedgerTransaction transaction; private String boldAccountName; + private String runningTotal; public TransactionListItem(@NotNull SimpleDate date, boolean monthShown) { this.type = Type.DELIMITER; this.date = date; this.monthShown = monthShown; } public TransactionListItem(@NotNull LedgerTransaction transaction, - @Nullable String boldAccountName) { + @Nullable String boldAccountName, @Nullable String runningTotal) { this.type = Type.TRANSACTION; this.transaction = transaction; this.boldAccountName = boldAccountName; + this.runningTotal = runningTotal; } public TransactionListItem() { this.type = Type.HEADER; } + public String getRunningTotal() { + return runningTotal; + } @NonNull public Type getType() { return type; diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/MainModel.java b/app/src/main/java/net/ktnx/mobileledger/ui/MainModel.java index a9488782..e95aae8c 100644 --- a/app/src/main/java/net/ktnx/mobileledger/ui/MainModel.java +++ b/app/src/main/java/net/ktnx/mobileledger/ui/MainModel.java @@ -127,7 +127,7 @@ public class MainModel extends ViewModel { String accNameFilter = model.getAccountFilter() .getValue(); - TransactionAccumulator acc = new TransactionAccumulator(accNameFilter); + TransactionAccumulator acc = new TransactionAccumulator(accNameFilter, accNameFilter); for (LedgerTransaction tr : list) { if (isInterrupted()) { return; diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/activity/MainActivity.java b/app/src/main/java/net/ktnx/mobileledger/ui/activity/MainActivity.java index d5d0b710..1350daf0 100644 --- a/app/src/main/java/net/ktnx/mobileledger/ui/activity/MainActivity.java +++ b/app/src/main/java/net/ktnx/mobileledger/ui/activity/MainActivity.java @@ -773,7 +773,7 @@ public class MainActivity extends ProfileThemedActivity implements FabManager.Fa } @Override public void run() { - TransactionAccumulator accumulator = new TransactionAccumulator(accFilter); + TransactionAccumulator accumulator = new TransactionAccumulator(accFilter, accFilter); for (TransactionWithAccounts tr : list) { if (isInterrupted()) { diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionListAdapter.java b/app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionListAdapter.java index 6ba45641..0c0ff6a7 100644 --- a/app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionListAdapter.java +++ b/app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionListAdapter.java @@ -129,9 +129,8 @@ public class TransactionListAdapter extends RecyclerView.Adapter @@ -91,6 +91,31 @@ + + diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 2a957979..5e585357 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -23,4 +23,5 @@ 200dp 16dp 8dp + 4dp \ No newline at end of file -- 2.39.2