]> git.ktnx.net Git - mobile-ledger.git/commitdiff
running totals when filtering transactions by account
authorDamyan Ivanov <dam+mobileledger@ktnx.net>
Fri, 7 May 2021 04:43:12 +0000 (04:43 +0000)
committerDamyan Ivanov <dam+mobileledger@ktnx.net>
Fri, 7 May 2021 04:43:12 +0000 (04:43 +0000)
this requires traversing the transaction list in the opposite order - from
oldest to newest and corresponding corner cases

app/src/main/java/net/ktnx/mobileledger/async/TransactionAccumulator.java
app/src/main/java/net/ktnx/mobileledger/dao/TransactionDAO.java
app/src/main/java/net/ktnx/mobileledger/model/LedgerAccount.java
app/src/main/java/net/ktnx/mobileledger/model/TransactionListItem.java
app/src/main/java/net/ktnx/mobileledger/ui/MainModel.java
app/src/main/java/net/ktnx/mobileledger/ui/activity/MainActivity.java
app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionListAdapter.java
app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionRowHolder.java
app/src/main/res/layout/transaction_list_row.xml
app/src/main/res/values/dimens.xml

index 1d86dbf629028ca417f1eee8587c8e704230c25a..d3131f776e2181807193f494be38b32c4ad44fe0 100644 (file)
 
 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<TransactionListItem> list = new ArrayList<>();
     private final String boldAccountName;
+    private final String accumulateAccount;
+    private final HashMap<String, BigDecimal> 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<String, BigDecimal> 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);
index 78be12abf0fc4912916426930cfbf10332b2f397..9d7fdeae4813171c7a697065c59ce2016435f843 100644 (file)
@@ -108,12 +108,7 @@ public abstract class TransactionDAO extends BaseDAO<Transaction> {
 
     @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<TransactionWithAccounts> 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<List<TransactionWithAccounts>> getAllWithAccounts(long profileId);
 
     @androidx.room.Transaction
@@ -121,16 +116,7 @@ public abstract class TransactionDAO extends BaseDAO<Transaction> {
            " 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<TransactionWithAccounts> 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<List<TransactionWithAccounts>> getAllWithAccountsFiltered(
             long profileId, String accountName);
 
index 06ac4da110f61acb3a73c1309f3da1332a88f671..1be684cf1b0d1bcb700485384a3a068963576610 100644 (file)
@@ -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);
index 82a15f36f8b61c3a3cdfb44d668f3380684d347a..7c3520919232c10c7fc44c42fe19957c649231e4 100644 (file)
@@ -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;
index a94887824d12dc324a90a3b2d843cd36f5a17956..e95aae8c2bc17864d819ae47628bb1966462271e 100644 (file)
@@ -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;
index d5d0b71005b4cc48818fb55e0ddc8a521fc1969d..1350daf03cec69026355282dbc4f57474539e535 100644 (file)
@@ -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()) {
index 6ba456416c42e02c9838c9a89b9c5bd7b24d9e9e..0c0ff6a7e3163f1d2ed82eeaff47c56b0a883702 100644 (file)
@@ -129,9 +129,8 @@ public class TransactionListAdapter extends RecyclerView.Adapter<TransactionRowH
 
         switch (newType) {
             case TRANSACTION:
-                LedgerTransaction tr = item.getTransaction();
                 holder.asTransaction()
-                      .bind(tr, item.getBoldAccountName());
+                      .bind(item, item.getBoldAccountName());
 
                 break;
             case DELIMITER:
index 85d36911a6e772421b0fff015039cc281493cd02..35fdcecf342eb2c6aac1e9471950a214ef84196f 100644 (file)
@@ -50,7 +50,8 @@ class TransactionRowHolder extends TransactionRowHolderBase {
         super(binding.getRoot());
         b = binding;
     }
-    public void bind(@NonNull LedgerTransaction tr, @Nullable String boldAccountName) {
+    public void bind(@NonNull TransactionListItem item, @Nullable String boldAccountName) {
+        LedgerTransaction tr = item.getTransaction();
         b.transactionRowDescription.setText(tr.getDescription());
         String trComment = Misc.emptyIsNull(tr.getComment());
         if (trComment == null)
@@ -60,6 +61,16 @@ class TransactionRowHolder extends TransactionRowHolderBase {
             b.transactionComment.setVisibility(View.VISIBLE);
         }
 
+        if (Misc.emptyIsNull(item.getRunningTotal()) != null) {
+            b.transactionRunningTotal.setText(item.getRunningTotal());
+            b.transactionRunningTotal.setVisibility(View.VISIBLE);
+            b.transactionRunningTotalDivider.setVisibility(View.VISIBLE);
+        }
+        else {
+            b.transactionRunningTotal.setVisibility(View.GONE);
+            b.transactionRunningTotalDivider.setVisibility(View.GONE);
+        }
+
         int rowIndex = 0;
         Context ctx = b.getRoot()
                        .getContext();
index 86b571692148942e553c106f0bc1e58a521b2c87..155cdc1042b319cd21a156709b5acab82f778647 100644 (file)
@@ -47,7 +47,7 @@
 
             <LinearLayout
                 android:id="@+id/transaction_row_head"
-                android:layout_width="match_parent"
+                android:layout_width="0dp"
                 android:layout_height="wrap_content"
                 android:orientation="vertical"
                 app:layout_constraintEnd_toEndOf="parent"
 
             <LinearLayout
                 android:id="@+id/transaction_row_acc_amounts"
-                android:layout_width="match_parent"
+                android:layout_width="0dp"
                 android:layout_height="wrap_content"
                 android:layout_marginTop="8dp"
                 android:orientation="vertical"
-                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintEnd_toStartOf="@id/transaction_running_total"
                 app:layout_constraintStart_toStartOf="parent"
                 app:layout_constraintTop_toBottomOf="@+id/transaction_row_head"
                 >
                 <include layout="@layout/transaction_list_row_accounts_table_row" />
 
             </LinearLayout>
+            <TextView
+                android:id="@+id/transaction_running_total"
+                style="@style/transaction_list_comment"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="@dimen/half_text_margin"
+                android:gravity="top|end"
+                android:text="one two"
+                android:visibility="visible"
+                app:layout_constraintBottom_toBottomOf="@id/transaction_row_acc_amounts"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toEndOf="@id/transaction_row_acc_amounts"
+                app:layout_goneMarginStart="0dp"
+                />
+            <View
+                android:id="@+id/transaction_running_total_divider"
+                android:layout_width="1dp"
+                android:layout_height="0dp"
+                android:background="?commentColor"
+                android:layout_marginStart="@dimen/quarter_text_margin"
+                app:layout_goneMarginStart="0dp"
+                app:layout_constraintBottom_toBottomOf="@id/transaction_running_total"
+                app:layout_constraintStart_toEndOf="@id/transaction_row_acc_amounts"
+                app:layout_constraintTop_toTopOf="@id/transaction_running_total"
+                />
 
         </androidx.constraintlayout.widget.ConstraintLayout>
     </com.google.android.material.card.MaterialCardView>
index 2a9579792e3ac24675786b0f473aff6b3b5bd838..5e5853575e65199b7ba37bde589b31e2ffc3f8fb 100644 (file)
@@ -23,4 +23,5 @@
     <dimen name="app_bar_height">200dp</dimen>
     <dimen name="text_margin">16dp</dimen>
     <dimen name="half_text_margin">8dp</dimen>
+    <dimen name="quarter_text_margin">4dp</dimen>
 </resources>
\ No newline at end of file