/*
- * Copyright © 2021 Damyan Ivanov.
+ * Copyright © 2022 Damyan Ivanov.
* This file is part of MoLe.
* MoLe is free software: you can distribute it and/or modify it
* under the term of the GNU General Public License as published by
package net.ktnx.mobileledger.ui.new_transaction;
import android.annotation.SuppressLint;
+import android.os.Build;
import android.text.TextUtils;
import androidx.annotation.NonNull;
private final AtomicInteger busyCounter = new AtomicInteger(0);
private final MutableLiveData<Boolean> busyFlag = new InertMutableLiveData<>(false);
private final Observer<Profile> profileObserver = profile -> {
- showCurrency.postValue(profile.getShowCommodityByDefault());
- showComments.postValue(profile.getShowCommentsByDefault());
+ if (profile != null) {
+ showCurrency.postValue(profile.getShowCommodityByDefault());
+ showComments.postValue(profile.getShowCommentsByDefault());
+ }
};
private final MutableLiveData<FocusInfo> focusInfo = new MutableLiveData<>();
private boolean observingDataProfile;
public NewTransactionModel() {
- reset();
}
public LiveData<Boolean> getShowCurrency() {
return showCurrency;
return copy;
}
private List<Item> shallowCopyList() {
- return new ArrayList<>(items.getValue());
+ return new ArrayList<>(Objects.requireNonNull(items.getValue()));
}
LiveData<Boolean> getShowComments() {
return showComments;
noteFocusChanged(fi.position - 1, fi.element);
setItems(newList);
}
- void noteFocusChanged(int position, FocusedElement element) {
+ void noteFocusChanged(int position, @Nullable FocusedElement element) {
FocusInfo present = focusInfo.getValue();
if (present == null || present.position != position || present.element != element)
focusInfo.setValue(new FocusInfo(position, element));
LedgerTransaction tr = head.asLedgerTransaction();
tr.setComment(head.getComment());
- LedgerTransactionAccount emptyAmountAccount = null;
- float emptyAmountAccountBalance = 0;
+ HashMap<String, List<LedgerTransactionAccount>> emptyAmountAccounts = new HashMap<>();
+ HashMap<String, Float> emptyAmountAccountBalance = new HashMap<>();
for (int i = 1; i < list.size(); i++) {
TransactionAccount item = list.get(i)
.toTransactionAccount();
+ String currency = item.getCurrency();
LedgerTransactionAccount acc = new LedgerTransactionAccount(item.getAccountName()
- .trim(),
- item.getCurrency());
+ .trim(), currency);
if (acc.getAccountName()
.isEmpty())
continue;
if (item.isAmountSet()) {
acc.setAmount(item.getAmount());
- emptyAmountAccountBalance += item.getAmount();
+ Float emptyCurrBalance = emptyAmountAccountBalance.get(currency);
+ if (emptyCurrBalance == null) {
+ emptyAmountAccountBalance.put(currency, item.getAmount());
+ }
+ else {
+ emptyAmountAccountBalance.put(currency, emptyCurrBalance + item.getAmount());
+ }
}
else {
- emptyAmountAccount = acc;
+ List<LedgerTransactionAccount> emptyCurrAccounts =
+ emptyAmountAccounts.get(currency);
+ if (emptyCurrAccounts == null)
+ emptyAmountAccounts.put(currency, emptyCurrAccounts = new ArrayList<>());
+ emptyCurrAccounts.add(acc);
}
tr.addAccount(acc);
}
- if (emptyAmountAccount != null)
- emptyAmountAccount.setAmount(-emptyAmountAccountBalance);
+ if (emptyAmountAccounts.size() > 0) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ emptyAmountAccounts.forEach((currency, accounts) -> {
+ final Float balance = emptyAmountAccountBalance.get(currency);
+
+ if (balance != null && !Misc.isZero(balance) && accounts.size() != 1) {
+ throw new RuntimeException(String.format(Locale.US,
+ "Should not happen: approved transaction has %d accounts " +
+ "without amounts for currency '%s'", accounts.size(), currency));
+ }
+ accounts.forEach(acc -> acc.setAmount(balance == null ? 0 : -balance));
+ });
+ }
+ else {
+ for (String currency : emptyAmountAccounts.keySet()) {
+ List<LedgerTransactionAccount> accounts =
+ Objects.requireNonNull(emptyAmountAccounts.get(currency));
+ final Float balance = emptyAmountAccountBalance.get(currency);
+ if (balance != null && !Misc.isZero(balance) && accounts.size() != 1)
+ throw new RuntimeException(String.format(Locale.US,
+ "Should not happen: approved transaction has %d accounts for " +
+ "currency %s", accounts.size(), currency));
+ for (LedgerTransactionAccount acc : accounts) {
+ acc.setAmount(balance == null ? 0 : -balance);
+ }
+ }
+ }
+ }
return tr;
}
int singleNegativeIndex = -1;
int singlePositiveIndex = -1;
int negativeCount = 0;
+ boolean hasCurrency = false;
for (int i = 0; i < accounts.size(); i++) {
LedgerTransactionAccount acc = accounts.get(i);
- TransactionAccount item =
- new TransactionAccount(acc.getAccountName(), acc.getCurrency());
+ TransactionAccount item = new TransactionAccount(acc.getAccountName(),
+ Misc.nullIsEmpty(acc.getCurrency()));
newList.add(item);
item.setAccountName(acc.getAccountName());
}
else
item.resetAmount();
+
+ if (item.getCurrency()
+ .length() > 0)
+ hasCurrency = true;
}
if (BuildConfig.DEBUG)
dumpItemList("Loaded previous transaction", newList);
moveItemLast(newList, singlePositiveIndex);
}
+ final boolean foundTransactionHasCurrency = hasCurrency;
Misc.onMainThread(() -> {
setItems(newList);
noteFocusChanged(1, FocusedElement.Amount);
+ if (foundTransactionHasCurrency)
+ showCurrency.setValue(true);
});
}
/**
itemsWithAccountForCurrency.add(currName, item);
}
- if (item.isAmountSet()) {
+ if (item.isAmountSet() && item.isAmountValid()) {
itemsWithAmountForCurrency.add(currName, item);
balance.add(currName, item.getAmount());
}
else {
if (!item.isAmountValid()) {
Logger.debug("submittable",
- String.format("Not submittable: row %d has an invalid amount",
- i + 1));
+ String.format("Not submittable: row %d has an invalid amount", i));
submittable = false;
hasInvalidAmount = true;
}
continue;
if (item == receiver) {
- final String hint = String.format("%1.2f", -currencyBalance);
+ final String hint = Data.formatNumber(-currencyBalance);
if (!acc.isAmountHintSet() ||
!Misc.equalStrings(acc.getAmountHint(), hint))
{
Logger.debug("submittable",
- String.format("Setting amount hint of {%s} to %s [%s]",
- acc.toString(), hint, balCurrency));
+ String.format("Setting amount hint of {%s} to %s [%s]", acc,
+ hint, balCurrency));
acc.setAmountHint(hint);
listChanged = true;
}
// if (Misc.isZero(balance.get(itemCurrencyName))) {
// item.setCurrency(Currency.loadByName(balCurrency));
// item.setAmountHint(
-// String.format("%1.2f", -balance.get(balCurrency)));
+// Data.formatNumber(-balance.get(balCurrency)));
// foundIt = true;
// break;
// }
final TransactionAccount newAcc = new TransactionAccount("", balCurrency);
final float bal = balance.get(balCurrency);
if (!Misc.isZero(bal) && currAmounts == currRows)
- newAcc.setAmountHint(String.format("%4.2f", -bal));
+ newAcc.setAmountHint(Data.formatNumber(-bal));
Logger.debug("submittable",
String.format("Adding new item with %s for currency %s",
newAcc.getAmountHint(), balCurrency));
public static class FocusInfo {
int position;
FocusedElement element;
- public FocusInfo(int position, FocusedElement element) {
+ public FocusInfo(int position, @Nullable FocusedElement element) {
this.position = position;
this.element = element;
}
b.append(String.format(" '%s'", description));
if (date != null)
- b.append(String.format("@%s", date.toString()));
+ b.append(String.format("@%s", date));
if (!TextUtils.isEmpty(comment))
b.append(String.format(" /%s/", comment));
private String accountName;
private String amountHint;
private String comment;
+ @NotNull
private String currency = "";
private float amount;
private boolean amountSet;
private boolean amountValid = true;
+ @NotNull
+ private String amountText = "";
private FocusedElement focusedElement = FocusedElement.Account;
private boolean amountHintIsSet = false;
private boolean isLast = false;
amountSet = origin.amountSet;
amountHint = origin.amountHint;
amountHintIsSet = origin.amountHintIsSet;
+ amountText = origin.amountText;
comment = origin.comment;
currency = origin.currency;
amountValid = origin.amountValid;
isLast = origin.isLast;
accountNameCursorPosition = origin.accountNameCursorPosition;
}
- public TransactionAccount(LedgerTransactionAccount account) {
- super();
- currency = account.getCurrency();
- amount = account.getAmount();
- }
public TransactionAccount(String accountName) {
super();
this.accountName = accountName;
this.accountName = accountName;
this.currency = currency;
}
+ public @NotNull String getAmountText() {
+ return amountText;
+ }
+ public void setAmountText(@NotNull String amountText) {
+ this.amountText = amountText;
+ }
+ public boolean setAndCheckAmountText(@NotNull String amountText) {
+ String amtText = amountText.trim();
+ this.amountText = amtText;
+
+ boolean significantChange = false;
+
+ if (amtText.isEmpty()) {
+ if (amountSet) {
+ significantChange = true;
+ }
+ resetAmount();
+ }
+ else {
+ try {
+ amtText = amtText.replace(Data.getDecimalSeparator(), Data.decimalDot);
+ final float parsedAmount = Float.parseFloat(amtText);
+ if (!amountSet || !amountValid || !Misc.equalFloats(parsedAmount, amount))
+ significantChange = true;
+ amount = parsedAmount;
+ amountSet = true;
+ amountValid = true;
+ }
+ catch (NumberFormatException e) {
+ Logger.debug("new-trans", String.format(
+ "assuming amount is not set due to number format exception. " +
+ "input was '%s'", amtText));
+ if (amountValid) // it was valid and now it's not
+ significantChange = true;
+ amountValid = false;
+ }
+ }
+
+ return significantChange;
+ }
public boolean isLast() {
return isLast;
}
public void setAmount(float amount) {
this.amount = amount;
amountSet = true;
+ amountValid = true;
+ amountText = Data.formatNumber(amount);
}
public void resetAmount() {
amountSet = false;
+ amountValid = true;
+ amountText = "";
}
@Override
public ItemType getType() {
}
@SuppressLint("DefaultLocale")
@Override
+ @NotNull
public String toString() {
StringBuilder b = new StringBuilder();
b.append(String.format("id:%d/%s", id, Integer.toHexString(hashCode())));
b.append(String.format(" acc'%s'", accountName));
if (amountSet)
- b.append(String.format(" %4.2f", amount));
+ b.append(amountText)
+ .append(" [")
+ .append(amountValid ? "valid" : "invalid")
+ .append("] ")
+ .append(String.format(Locale.ROOT, " {raw %4.2f}", amount));
else if (amountHintIsSet)
- b.append(String.format(" (%s)", amountHint));
+ b.append(String.format(" (hint %s)", amountHint));
if (!TextUtils.isEmpty(currency))
b.append(" ")
boolean equal = Misc.equalStrings(accountName, other.accountName);
equal = equal && Misc.equalStrings(comment, other.comment) &&
- (amountSet ? other.amountSet && amount == other.amount : !other.amountSet);
+ (amountSet ? other.amountSet && amountValid == other.amountValid &&
+ Misc.equalStrings(amountText, other.amountText)
+ : !other.amountSet);
// compare amount hint only if there is no amount
if (!amountSet)
equal = equal && Misc.equalStrings(currency, other.currency) && isLast == other.isLast;
Logger.debug("new-trans",
- String.format("Comparing {%s} and {%s}: %s", this.toString(), other.toString(),
- equal));
+ String.format("Comparing {%s} and {%s}: %s", this, other, equal));
return equal;
}
public int getAccountNameCursorPosition() {