From ee38e21aa7318a51f9f3e62788d920e13b7be620 Mon Sep 17 00:00:00 2001 From: Damyan Ivanov Date: Sun, 15 Mar 2020 07:52:48 +0200 Subject: [PATCH] NT: new rules for determining whether transaction can be submitted (not quite finished) --- .../model/LedgerTransactionAccount.java | 4 + .../ui/activity/NewTransactionItemHolder.java | 7 +- .../activity/NewTransactionItemsAdapter.java | 5 +- .../ui/activity/NewTransactionModel.java | 151 ++++++++++++------ 4 files changed, 111 insertions(+), 56 deletions(-) diff --git a/app/src/main/java/net/ktnx/mobileledger/model/LedgerTransactionAccount.java b/app/src/main/java/net/ktnx/mobileledger/model/LedgerTransactionAccount.java index ba80ac61..28931b8c 100644 --- a/app/src/main/java/net/ktnx/mobileledger/model/LedgerTransactionAccount.java +++ b/app/src/main/java/net/ktnx/mobileledger/model/LedgerTransactionAccount.java @@ -37,6 +37,10 @@ public class LedgerTransactionAccount { public LedgerTransactionAccount(String accountName) { this.accountName = accountName; } + public LedgerTransactionAccount(String accountName, String currency) { + this.accountName = accountName; + this.currency = currency; + } public LedgerTransactionAccount(LedgerTransactionAccount origin) { // copy constructor setAccountName(origin.getAccountName()); diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/activity/NewTransactionItemHolder.java b/app/src/main/java/net/ktnx/mobileledger/ui/activity/NewTransactionItemHolder.java index 37506c04..53e97e82 100644 --- a/app/src/main/java/net/ktnx/mobileledger/ui/activity/NewTransactionItemHolder.java +++ b/app/src/main/java/net/ktnx/mobileledger/ui/activity/NewTransactionItemHolder.java @@ -353,7 +353,10 @@ class NewTransactionItemHolder extends RecyclerView.ViewHolder tvAmount.setKeyListener(DigitsKeyListener.getInstance(locale, true, true)); }; - currencyObserver = this::setCurrency; + currencyObserver = currency -> { + setCurrency(currency); + adapter.model.checkTransactionSubmittable(adapter); + }; showCurrencyObserver = showCurrency -> { if (item.getType() == ItemType.transactionRow) { @@ -502,7 +505,7 @@ class NewTransactionItemHolder extends RecyclerView.ViewHolder if (amount.isEmpty()) { account.resetAmount(); - account.setCurrency(null); +// account.setCurrency(null); } else { try { diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/activity/NewTransactionItemsAdapter.java b/app/src/main/java/net/ktnx/mobileledger/ui/activity/NewTransactionItemsAdapter.java index 5f2f4c6c..ef5947af 100644 --- a/app/src/main/java/net/ktnx/mobileledger/ui/activity/NewTransactionItemsAdapter.java +++ b/app/src/main/java/net/ktnx/mobileledger/ui/activity/NewTransactionItemsAdapter.java @@ -116,7 +116,10 @@ class NewTransactionItemsAdapter extends RecyclerView.Adapter showCurrency = new MutableLiveData<>(false); private final Item header = new Item(this, null, ""); private final Item trailer = new Item(this); private final ArrayList items = new ArrayList<>(); @@ -56,7 +59,6 @@ public class NewTransactionModel extends ViewModel { private final MutableLiveData focusedItem = new MutableLiveData<>(0); private final MutableLiveData accountCount = new MutableLiveData<>(0); private final MutableLiveData simulateSave = new MutableLiveData<>(false); - final MutableLiveData showCurrency = new MutableLiveData<>(false); public boolean getSimulateSave() { return simulateSave.getValue(); } @@ -148,11 +150,12 @@ public class NewTransactionModel extends ViewModel { A transaction is submittable if: 0) has description 1) has at least two account names - 2) each amount has account name - 3) amounts must balance to 0, or - 3a) there must be exactly one empty amount (with account) + 2) each row with amount has account name + 3) for each commodity: + 3a) amounts must balance to 0, or + 3b) there must be exactly one empty amount (with account) 4) empty accounts with empty amounts are ignored - 5) a row with an empty account name or empty amount is guaranteed to exist + 5) a row with an empty account name or empty amount is guaranteed to exist for each commodity 6) at least two rows need to be present in the ledger */ @@ -161,11 +164,11 @@ public class NewTransactionModel extends ViewModel { int accounts = 0; int amounts = 0; int empty_rows = 0; - float balance = 0f; + final Map balance = new HashMap<>(); final String descriptionText = getDescription(); boolean submittable = true; - List itemsWithEmptyAmount = new ArrayList<>(); - List itemsWithAccountAndEmptyAmount = new ArrayList<>(); + final Map> itemsWithEmptyAmountForCurrency = new HashMap<>(); + final Map> itemsWithAccountAndEmptyAmountForCurrency = new HashMap<>(); try { if ((descriptionText == null) || descriptionText.trim() @@ -181,6 +184,8 @@ public class NewTransactionModel extends ViewModel { LedgerTransactionAccount acc = item.getAccount(); String acc_name = acc.getAccountName() .trim(); + String currName = acc.getCurrency(); + if (acc_name.isEmpty()) { empty_rows++; @@ -198,70 +203,110 @@ public class NewTransactionModel extends ViewModel { if (acc.isAmountSet()) { amounts++; - balance += acc.getAmount(); + Float bal = balance.get(currName); + if (bal == null) + bal = 0f; + bal += acc.getAmount(); + balance.put(currName, bal); } else { - itemsWithEmptyAmount.add(item); + { + List list = itemsWithEmptyAmountForCurrency.get(currName); + if (list == null) + list = new ArrayList<>(); + itemsWithEmptyAmountForCurrency.put(currName, list); + list.add(item); + } if (!acc_name.isEmpty()) { - itemsWithAccountAndEmptyAmount.add(item); + List list = + itemsWithAccountAndEmptyAmountForCurrency.get(acc.getCurrency()); + if (list == null) { + list = new ArrayList<>(); + itemsWithAccountAndEmptyAmountForCurrency.put(acc.getCurrency(), list); + } + list.add(item); } } } // 1) has at least two account names if (accounts < 2) { - Logger.debug("submittable", - String.format("Transaction not submittable: only %d account names", - accounts)); + if (accounts == 0) + Logger.debug("submittable", "Transaction not submittable: no account names"); + else if (accounts == 1) + Logger.debug("submittable", + "Transaction not submittable: only one account name"); + else + Logger.debug("submittable", + String.format("Transaction not submittable: only %d account names", + accounts)); submittable = false; } - // 3) amount must balance to 0, or - // 3a) there must be exactly one empty amount (with account) - if (Misc.isZero(balance)) { - for (Item item : items) { - item.setAmountHint(null); - } - } - else { - int balanceReceiversCount = itemsWithAccountAndEmptyAmount.size(); - if (balanceReceiversCount != 1) { - Logger.debug("submittable", (balanceReceiversCount == 0) ? - "Transaction not submittable: non-zero balance " + - "with no empty amounts with accounts" : - "Transaction not submittable: non-zero balance " + - "with multiple empty amounts with accounts"); - submittable = false; + // 3) for each commodity: + // 3a) amount must balance to 0, or + // 3b) there must be exactly one empty amount (with account) + for (String balCurrency : balance.keySet()) { + Float bal = balance.get(balCurrency); + float currencyBalance = (bal == null) ? 0f : bal; + if (Misc.isZero(currencyBalance)) { + // remove hints from all amount inputs in that currency + for (Item item : items) { + final String itemCurrency = item.getCurrency() + .getName(); + if (((balCurrency == null) && (null == itemCurrency)) || + ((balCurrency != null) && balCurrency.equals(itemCurrency))) + item.setAmountHint(null); + } } + else { + List list = itemsWithAccountAndEmptyAmountForCurrency.get(balCurrency); + int balanceReceiversCount = (list == null) ? 0 : list.size(); + if (balanceReceiversCount != 1) { + Logger.debug("submittable", (balanceReceiversCount == 0) ? String.format( + "Transaction not submittable [%s]: non-zero balance " + + "with no empty amounts with accounts", balCurrency) : String.format( + "Transaction not submittable [%s]: non-zero balance " + + "with multiple empty amounts with accounts", balCurrency)); + submittable = false; + } - // suggest off-balance amount to a row and remove hints on other rows - Item receiver = null; - if (!itemsWithAccountAndEmptyAmount.isEmpty()) - receiver = itemsWithAccountAndEmptyAmount.get(0); - else if (!itemsWithEmptyAmount.isEmpty()) - receiver = itemsWithEmptyAmount.get(0); - - for (Item item : items) { - if (item.equals(receiver)) { - Logger.debug("submittable", - String.format("Setting amount hint to %1.2f", -balance)); - item.setAmountHint(String.format("%1.2f", -balance)); + List emptyAmountList = itemsWithEmptyAmountForCurrency.get(balCurrency); + + // suggest off-balance amount to a row and remove hints on other rows + Item receiver = null; + if ((list != null) && !list.isEmpty()) + receiver = list.get(0); + else if ((emptyAmountList != null) && !emptyAmountList.isEmpty()) + receiver = emptyAmountList.get(0); + + for (Item item : items) { + if (item.equals(receiver)) { + Logger.debug("submittable", + String.format("Setting amount hint to %1.2f", + -currencyBalance)); + item.setAmountHint(String.format("%1.2f", -currencyBalance)); + } + else + item.setAmountHint(null); } - else - item.setAmountHint(null); } } - // 5) a row with an empty account name or empty amount is guaranteed to exist - if ((empty_rows == 0) && - ((this.items.size() == accounts) || (this.items.size() == amounts))) - { - adapter.addRow(); + // 5) a row with an empty account name or empty amount is guaranteed to exist for + // each commodity + for (String balCurrency : balance.keySet()) { + if ((empty_rows == 0) && + ((this.items.size() == accounts) || (this.items.size() == amounts))) + { + adapter.addRow(balCurrency); + } } // 6) at least two rows need to be present in the ledger - while (this.items.size() < 2) adapter.addRow(); + while (this.items.size() < 2) + adapter.addRow(); debug("submittable", submittable ? "YES" : "NO"); @@ -272,9 +317,9 @@ public class NewTransactionModel extends ViewModel { for (int i = 0; i < items.size(); i++) { Item item = items.get(i); LedgerTransactionAccount acc = item.getAccount(); - debug("submittable", String.format("Item %2d: [%4.2f] %s (%s)", i, - acc.isAmountSet() ? acc.getAmount() : 0, acc.getAccountName(), - acc.getComment())); + debug("submittable", String.format("Item %2d: [%4.2f %s] %s // %s", i, + acc.isAmountSet() ? acc.getAmount() : 0, acc.getCurrency(), + acc.getAccountName(), acc.getComment())); } } } -- 2.39.2