- /*
- A transaction is submittable if:
- 0) has description
- 1) has at least two account names
- 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 for each commodity
- 6) at least two rows need to be present in the ledger
-
- */
- @SuppressLint("DefaultLocale")
- public void checkTransactionSubmittable(NewTransactionItemsAdapter adapter) {
- int accounts = 0;
- int amounts = 0;
- int empty_rows = 0;
- final Map<String, Float> balance = new HashMap<>();
- final String descriptionText = getDescription();
- boolean submittable = true;
- final Map<String, List<Item>> itemsWithEmptyAmountForCurrency = new HashMap<>();
- final Map<String, List<Item>> itemsWithAccountAndEmptyAmountForCurrency = new HashMap<>();
-
- try {
- if ((descriptionText == null) || descriptionText.trim()
- .isEmpty())
- {
- Logger.debug("submittable", "Transaction not submittable: missing description");
- submittable = false;
- }
-
- for (int i = 0; i < this.items.size(); i++) {
- Item item = this.items.get(i);
-
- LedgerTransactionAccount acc = item.getAccount();
- String acc_name = acc.getAccountName()
- .trim();
- String currName = acc.getCurrency();
-
- if (acc_name.isEmpty()) {
- empty_rows++;
-
- if (acc.isAmountSet()) {
- // 2) each amount has account name
- Logger.debug("submittable", String.format(
- "Transaction not submittable: row %d has no account name, but has" +
- " amount %1.2f", i + 1, acc.getAmount()));
- submittable = false;
- }
- }
- else {
- accounts++;
- }
-
- if (acc.isAmountSet()) {
- amounts++;
- Float bal = balance.get(currName);
- if (bal == null)
- bal = 0f;
- bal += acc.getAmount();
- balance.put(currName, bal);
- }
- else {
- {
- List<Item> list = itemsWithEmptyAmountForCurrency.get(currName);
- if (list == null)
- list = new ArrayList<>();
- itemsWithEmptyAmountForCurrency.put(currName, list);
- list.add(item);
- }
-
- if (!acc_name.isEmpty()) {
- List<Item> 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) {
- 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) 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<Item> 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;
- }
-
- List<Item> 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);
- }
- }
- }
-
- // 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();
-
-
- debug("submittable", submittable ? "YES" : "NO");
- isSubmittable.setValue(submittable);
-
- if (BuildConfig.DEBUG) {
- debug("submittable", "== Dump of all items");
- 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 // %s", i,
- acc.isAmountSet() ? acc.getAmount() : 0, acc.getCurrency(),
- acc.getAccountName(), acc.getComment()));
- }
- }
- }
- catch (NumberFormatException e) {
- debug("submittable", "NO (because of NumberFormatException)");
- isSubmittable.setValue(false);
- }
- catch (Exception e) {
- e.printStackTrace();
- debug("submittable", "NO (because of an Exception)");
- isSubmittable.setValue(false);