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
}
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);
@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
" 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);
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);
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;
String accNameFilter = model.getAccountFilter()
.getValue();
- TransactionAccumulator acc = new TransactionAccumulator(accNameFilter);
+ TransactionAccumulator acc = new TransactionAccumulator(accNameFilter, accNameFilter);
for (LedgerTransaction tr : list) {
if (isInterrupted()) {
return;
}
@Override
public void run() {
- TransactionAccumulator accumulator = new TransactionAccumulator(accFilter);
+ TransactionAccumulator accumulator = new TransactionAccumulator(accFilter, accFilter);
for (TransactionWithAccounts tr : list) {
if (isInterrupted()) {
switch (newType) {
case TRANSACTION:
- LedgerTransaction tr = item.getTransaction();
holder.asTransaction()
- .bind(tr, item.getBoldAccountName());
+ .bind(item, item.getBoldAccountName());
break;
case DELIMITER:
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)
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();
<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>
<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