diff --git a/name.abuchen.portfolio.ui.tests/src/name/abuchen/portfolio/ui/dialogs/transactions/AbstractSecurityTransactionModelTest.java b/name.abuchen.portfolio.ui.tests/src/name/abuchen/portfolio/ui/dialogs/transactions/AbstractSecurityTransactionModelTest.java new file mode 100644 index 0000000000..f7ec698798 --- /dev/null +++ b/name.abuchen.portfolio.ui.tests/src/name/abuchen/portfolio/ui/dialogs/transactions/AbstractSecurityTransactionModelTest.java @@ -0,0 +1,159 @@ +package name.abuchen.portfolio.ui.dialogs.transactions; + +import static org.junit.Assert.assertEquals; + +import java.math.BigDecimal; +import java.text.MessageFormat; + +import org.eclipse.core.databinding.validation.ValidationStatus; +import org.junit.Before; +import org.junit.Test; + +import name.abuchen.portfolio.model.Client; +import name.abuchen.portfolio.model.PortfolioTransaction; +import name.abuchen.portfolio.model.PortfolioTransaction.Type; +import name.abuchen.portfolio.ui.Messages; + +@SuppressWarnings("nls") +public class AbstractSecurityTransactionModelTest +{ + private AbstractSecurityTransactionModel model; + + @Before + public void setup() + { + Client client = new Client(); + PortfolioTransaction.Type type = PortfolioTransaction.Type.BUY; + + model = new AbstractSecurityTransactionModel(client, type) + { + @Override + public boolean accepts(Type type) + { + return true; + } + + @Override + public void setSource(Object source) + { + } + + @Override + public boolean hasSource() + { + return false; + } + + @Override + public String getTransactionCurrencyCode() + { + return null; + } + + @Override + public void applyChanges() + { + } + }; + } + + @Test + public void testCalculationStatus_WithValidData() + { + model.setShares(1); + model.setQuote(BigDecimal.valueOf(100)); + model.setGrossValue(100); + model.setExchangeRate(BigDecimal.valueOf(0.5)); + model.setConvertedGrossValue(200); + model.setTotal(800); + model.setFees(50); + model.setTaxes(100); + model.setForexFees(150); + model.setForexTaxes(200); + + assertEquals(model.calculateStatus(), ValidationStatus.ok()); + + // 800 = 1650 - (200 / 0.5) - (150 / 0.5) - 100 - 50 + assertEquals(800L, model.calculateConvertedGrossValue()); + + // 1650 = setTotal(800) + (200 / 0.5) + (150 / 0.5) + 100 + 50 + assertEquals(1650L, model.calculateTotal()); + } + + @Test + public void testCalculationStatus_WithInvalidData() + { + model.setShares(0); + model.setQuote(BigDecimal.valueOf(0)); + model.setGrossValue(0); + model.setExchangeRate(BigDecimal.valueOf(0)); + model.setConvertedGrossValue(0); + model.setTotal(0); + + assertEquals(model.calculateStatus(), ValidationStatus + .error(MessageFormat.format(Messages.MsgDialogInputRequired, Messages.ColumnShares))); + } + + @Test + public void testCalculationStatus_WithInvalidDataTotalAndSubTotal() + { + model.setShares(1); + model.setQuote(BigDecimal.valueOf(100)); + model.setGrossValue(0); + model.setConvertedGrossValue(0); + model.setTotal(0); + + assertEquals(model.calculateStatus(), ValidationStatus + .error(MessageFormat.format(Messages.MsgDialogInputRequired, Messages.ColumnSubTotal))); + } + + @Test + public void testCalculationStatus_WithInvalidDataSubTotal() + { + model.setShares(1); + model.setQuote(BigDecimal.valueOf(100)); + model.setGrossValue(100); + model.setConvertedGrossValue(0); + model.setTotal(0); + + assertEquals(model.calculateStatus(), ValidationStatus + .error(MessageFormat.format(Messages.MsgDialogInputRequired, Messages.ColumnSubTotal))); + } + + @Test + public void testCalculationStatus_WithInvalidDataQuote() + { + model.setShares(1); + model.setQuote(BigDecimal.valueOf(0)); + model.setGrossValue(100); + model.setConvertedGrossValue(0); + model.setTotal(100); + + assertEquals(model.calculateStatus(), + ValidationStatus.error(String.format(Messages.CellEditor_NotANumber, model.getQuote()))); + } + + @Test + public void testCalculateStatus_WithNegativeShares() + { + model.setShares(-1); + model.setQuote(BigDecimal.valueOf(100)); + + assertEquals(model.calculateStatus(), + ValidationStatus.error(String.format(Messages.CellEditor_NotANumber, model.getShares()))); + + assertEquals(0L, model.getTotal()); + } + + @Test + public void testCalculateStatus_WithNegativeQuote() + { + model.setShares(1); + model.setQuote(BigDecimal.valueOf(-100)); + + assertEquals(model.calculateStatus(), + ValidationStatus.error(String.format(Messages.CellEditor_NotANumber, model.getQuote()))); + + assertEquals(0L, model.getTotal()); + } +} diff --git a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/dialogs/transactions/AbstractSecurityTransactionModel.java b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/dialogs/transactions/AbstractSecurityTransactionModel.java index dce4132a8d..5d49deef9c 100644 --- a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/dialogs/transactions/AbstractSecurityTransactionModel.java +++ b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/dialogs/transactions/AbstractSecurityTransactionModel.java @@ -196,11 +196,17 @@ public IStatus getCalculationStatus() * quote (2 digits currently) and the exchange rate (4 digits), the gross * value and converted gross value are checked against a range. */ - private IStatus calculateStatus() + protected IStatus calculateStatus() { + if (shares < 0L) + return ValidationStatus.error(String.format(Messages.CellEditor_NotANumber, shares)); + if (shares == 0L) return ValidationStatus.error(MessageFormat.format(Messages.MsgDialogInputRequired, Messages.ColumnShares)); + if (quote.signum() == -1) + return ValidationStatus.error(String.format(Messages.CellEditor_NotANumber, quote)); + if ((grossValue == 0L || convertedGrossValue == 0L) && type != PortfolioTransaction.Type.DELIVERY_OUTBOUND) return ValidationStatus .error(MessageFormat.format(Messages.MsgDialogInputRequired, Messages.ColumnSubTotal)); @@ -274,7 +280,7 @@ protected void updateSharesAndQuote() // do not auto-suggest shares and quote when editing an existing transaction if (hasSource()) return; - + if (type == PortfolioTransaction.Type.SELL || type == PortfolioTransaction.Type.DELIVERY_OUTBOUND) { boolean hasPosition = false; @@ -622,7 +628,7 @@ protected long calculateConvertedGrossValue() } } - private long calculateTotal() + protected long calculateTotal() { long feesAndTaxes = fees + taxes + Math.round(exchangeRate.doubleValue() * (forexFees + forexTaxes));