diff --git a/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionImportController.java b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionImportController.java index 502eccdec42788aa4cf2e746a7d1fcd6a03c252a..65919d5fe94e85ae54708cf298f0fdba99c8cce9 100644 --- a/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionImportController.java +++ b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionImportController.java @@ -161,8 +161,16 @@ public class TransactionImportController extends BaseController { final String date = csvRow.getColumns().get(csvColumnSettings.columnDate() - 1); final String name = csvRow.getColumns().get(csvColumnSettings.columnName() - 1); + final String amount = csvRow.getColumns().get(csvColumnSettings.columnAmount() - 1); - csvTransactions.add(new CsvTransaction(date, name, amount, CsvTransactionStatus.PENDING)); + final Optional<Integer> parsedAmountOptional = AmountParser.parse(amount); + if(parsedAmountOptional.isEmpty()) + { + errors.add(Localization.getString("transactions.import.error.parse.amount", i, csvRow)); + continue; + } + + csvTransactions.add(new CsvTransaction(date, name, parsedAmountOptional.get(), CsvTransactionStatus.PENDING)); } catch(IndexOutOfBoundsException e) { @@ -253,8 +261,7 @@ public class TransactionImportController extends BaseController // TODO parse first // newTransaction.setDate(csvTransaction.getDate()); newTransaction.setName(csvTransaction.getName()); - // TODO parse first -// newTransaction.setAmount(csvTransaction.getAmount()); + newTransaction.setAmount(csvTransaction.getAmount()); newTransaction.setIsExpenditure(true); newTransaction.setAccount(helpers.getCurrentAccountOrDefault()); newTransaction.setCategory(categoryService.findByType(CategoryType.NONE)); diff --git a/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/transactions/csvimport/AmountParser.java b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/transactions/csvimport/AmountParser.java new file mode 100644 index 0000000000000000000000000000000000000000..2d87372d59dcbf11bc9c20a788cdd185f2ce357d --- /dev/null +++ b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/transactions/csvimport/AmountParser.java @@ -0,0 +1,47 @@ +package de.deadlocker8.budgetmaster.transactions.csvimport; + +import java.text.MessageFormat; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class AmountParser +{ + private static final Pattern PATTERN_AMOUNT = Pattern.compile("^\\s*([-+]?)\\s*(\\d+(,\\d+)?(\\.\\d+)?)"); + + private AmountParser() + { + } + + public static Optional<Integer> parse(String amountString) + { + if(amountString == null) + { + return Optional.empty(); + } + + final Matcher matcher = PATTERN_AMOUNT.matcher(amountString); + boolean matchFound = matcher.find(); + if(matchFound) + { + final String sign = matcher.group(1); + String amount = matcher.group(2); + amount = amount.replace(',', '.'); + + final String parseableString = MessageFormat.format("{0}{1}", sign, amount); + try + { + final double parseDouble = Double.parseDouble(parseableString); + return Optional.of((int) (parseDouble * 100)); + } + catch(NumberFormatException e) + { + return Optional.empty(); + } + } + else + { + return Optional.empty(); + } + } +} diff --git a/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/transactions/csvimport/CsvTransaction.java b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/transactions/csvimport/CsvTransaction.java index 0e698def17a8aa03ec900a9fcda55f98d4b8d9e2..7473d5f3ca659adb399ef33ff012b03161f789c7 100644 --- a/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/transactions/csvimport/CsvTransaction.java +++ b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/transactions/csvimport/CsvTransaction.java @@ -6,10 +6,10 @@ public final class CsvTransaction { private final String date; private String name; - private final String amount; + private final Integer amount; private CsvTransactionStatus status; - public CsvTransaction(String date, String name, String amount, CsvTransactionStatus status) + public CsvTransaction(String date, String name, Integer amount, CsvTransactionStatus status) { this.date = date; this.name = name; @@ -32,7 +32,7 @@ public final class CsvTransaction this.name = name; } - public String getAmount() + public Integer getAmount() { return amount; } diff --git a/BudgetMasterServer/src/main/resources/languages/base_de.properties b/BudgetMasterServer/src/main/resources/languages/base_de.properties index b2bbdc2fc536b3b2d04a208925edeb4e3e1b71e8..19640400c38097981f5d9b21b85244f338930d7e 100644 --- a/BudgetMasterServer/src/main/resources/languages/base_de.properties +++ b/BudgetMasterServer/src/main/resources/languages/base_de.properties @@ -381,6 +381,7 @@ transactions.import.status.imported=importiert transactions.import.status.skipped=übersprungen transactions.import.actions=Aktionen transactions.import.error.column=Zugeordnete Spalten in Zeile {0} (Zählung beginnt relativ zu Anzahl übersprungener Zeilen) nicht gefunden: {1} +transactions.import.error.parse.amount=Fehler beim Parsen des Betrags in Zeile {0} (Zählung beginnt relativ zu Anzahl übersprungener Zeilen) repeating.button.add=Wiederholung hinzufügen repeating.button.remove=Wiederholung entfernen diff --git a/BudgetMasterServer/src/main/resources/languages/base_en.properties b/BudgetMasterServer/src/main/resources/languages/base_en.properties index 1ea66828fe851020af69aadc80ec29af6e9f9a9f..58d90b49a17fbb99c1ee3cb812c2023425b1dedc 100644 --- a/BudgetMasterServer/src/main/resources/languages/base_en.properties +++ b/BudgetMasterServer/src/main/resources/languages/base_en.properties @@ -380,6 +380,7 @@ transactions.import.status.imported=imported transactions.import.status.skipped=skipped transactions.import.actions=Actions transactions.import.error.column=Associated columns not found in row {0} (counting starts relative to the number of skipped rows): {1} +transactions.import.error.parse.amount=Error parsing the amount in line {0} (counting starts relative to number of skipped lines) repeating.button.add=Add repetition repeating.button.remove=Remove repetition diff --git a/BudgetMasterServer/src/main/resources/templates/transactions/transactionImport.ftl b/BudgetMasterServer/src/main/resources/templates/transactions/transactionImport.ftl index 0b360a88bf883f3f7af1d8bd823da478184293cb..538736e0e029f5a2996603d42ada68bc10825985 100644 --- a/BudgetMasterServer/src/main/resources/templates/transactions/transactionImport.ftl +++ b/BudgetMasterServer/src/main/resources/templates/transactions/transactionImport.ftl @@ -217,7 +217,7 @@ <input class="no-margin-bottom" type="text" name="name" required value="${csvTransaction.getName()}"> </div> </td> - <td>${csvTransaction.getAmount()}</td> + <td>${currencyService.getCurrencyString(csvTransaction.getAmount())}</td> <td> <@header.buttonSubmit name='action' icon='save' localizationKey='' classes='text-white'/> <div class="fixed-action-btn edit-transaction-button"> diff --git a/BudgetMasterServer/src/test/java/de/deadlocker8/budgetmaster/unit/transaction/csvimport/AmountParserTest.java b/BudgetMasterServer/src/test/java/de/deadlocker8/budgetmaster/unit/transaction/csvimport/AmountParserTest.java new file mode 100644 index 0000000000000000000000000000000000000000..5fdf610d80920043f34d79942337928f808fed9a --- /dev/null +++ b/BudgetMasterServer/src/test/java/de/deadlocker8/budgetmaster/unit/transaction/csvimport/AmountParserTest.java @@ -0,0 +1,208 @@ +package de.deadlocker8.budgetmaster.unit.transaction.csvimport; + +import de.deadlocker8.budgetmaster.transactions.csvimport.AmountParser; +import de.deadlocker8.budgetmaster.unit.helpers.LocalizedTest; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import static org.assertj.core.api.Assertions.assertThat; + +class AmountParserTest +{ + @Test + void test_dot_positive_noCurrency() + { + assertThat(AmountParser.parse("12.03")) + .isPresent() + .get().isEqualTo(1203); + } + + @Test + void test_dot_negative_noCurrency() + { + assertThat(AmountParser.parse("-18.41")) + .isPresent() + .get().isEqualTo(-1841); + } + + @Test + void test_dot_negativeWithSpace_noCurrency() + { + assertThat(AmountParser.parse("- 200.30")) + .isPresent() + .get().isEqualTo(-20030); + } + + @Test + void test_dot_positive_currency() + { + assertThat(AmountParser.parse("12.03 €")) + .isPresent() + .get().isEqualTo(1203); + } + + @Test + void test_dot_negative_currency() + { + assertThat(AmountParser.parse("-18.41€")) + .isPresent() + .get().isEqualTo(-1841); + } + + @Test + void test_dot_positiveWithSign_noCurrency() + { + assertThat(AmountParser.parse("+12.03")) + .isPresent() + .get().isEqualTo(1203); + } + + @Test + void test_dot_positiveWithSignWithSpace_noCurrency() + { + assertThat(AmountParser.parse("+ 12.03")) + .isPresent() + .get().isEqualTo(1203); + } + + @Test + void test_comma_positive_noCurrency() + { + assertThat(AmountParser.parse("12,03")) + .isPresent() + .get().isEqualTo(1203); + } + + @Test + void test_comma_negative_noCurrency() + { + assertThat(AmountParser.parse("-18,41")) + .isPresent() + .get().isEqualTo(-1841); + } + + @Test + void test_comma_negativeWithSpace_noCurrency() + { + assertThat(AmountParser.parse("- 200,30")) + .isPresent() + .get().isEqualTo(-20030); + } + + @Test + void test_comma_positive_currency() + { + assertThat(AmountParser.parse("12,03 €")) + .isPresent() + .get().isEqualTo(1203); + } + + @Test + void test_comma_negative_currency() + { + assertThat(AmountParser.parse("-18,41€")) + .isPresent() + .get().isEqualTo(-1841); + } + + @Test + void test_comma_positiveWithSign_noCurrency() + { + assertThat(AmountParser.parse("+12,03")) + .isPresent() + .get().isEqualTo(1203); + } + + @Test + void test_comma_positiveWithSignWithSpace_noCurrency() + { + assertThat(AmountParser.parse("+12,03")) + .isPresent() + .get().isEqualTo(1203); + } + + @Test + void test_invalid_null() + { + assertThat(AmountParser.parse(null)) + .isEmpty(); + } + + @Test + void test_invalid_empty() + { + assertThat(AmountParser.parse("")) + .isEmpty(); + } + + @Test + void test_invalid_empty2() + { + assertThat(AmountParser.parse(" ")) + .isEmpty(); + } + + @Test + void test_invalid() + { + assertThat(AmountParser.parse("abc.42€")) + .isEmpty(); + } + + @Test + void test_integer_positive_noCurrency() + { + assertThat(AmountParser.parse("12")) + .isPresent() + .get().isEqualTo(1200); + } + + @Test + void test_integer_negative_noCurrency() + { + assertThat(AmountParser.parse("-18")) + .isPresent() + .get().isEqualTo(-1800); + } + + @Test + void test_integer_negativeWithSpace_noCurrency() + { + assertThat(AmountParser.parse("- 200")) + .isPresent() + .get().isEqualTo(-20000); + } + + @Test + void test_integer_positive_currency() + { + assertThat(AmountParser.parse("12 €")) + .isPresent() + .get().isEqualTo(1200); + } + + @Test + void test_integer_negative_currency() + { + assertThat(AmountParser.parse("-18€")) + .isPresent() + .get().isEqualTo(-1800); + } + + @Test + void test_integer_positiveWithSign_noCurrency() + { + assertThat(AmountParser.parse("+12")) + .isPresent() + .get().isEqualTo(1200); + } + + @Test + void test_integer_positiveWithSignWithSpace_noCurrency() + { + assertThat(AmountParser.parse("+ 12")) + .isPresent() + .get().isEqualTo(1200); + } +}