From 70218ad7dca9f87486a9ea5790d6d96ab61c55ac Mon Sep 17 00:00:00 2001 From: MK13 Date: Wed, 1 Sep 2021 22:41:18 +0200 Subject: [PATCH] #23 Transaction import: new period --- .../CreateUploadedTransactionsRequestDto.java | 147 ++++++++++-------- .../financer/controller/PeriodController.java | 5 +- .../controller/TransactionController.java | 6 +- .../de/financer/service/PeriodService.java | 7 +- .../financer/service/TransactionService.java | 20 ++- .../controller/TransactionController.java | 76 +++++++-- .../form/CreateUploadedTransactionForm.java | 12 +- .../financer/form/UploadTransactionsForm.java | 18 +++ .../main/resources/i18n/message.properties | 5 +- .../resources/i18n/message_de_DE.properties | 4 + .../src/main/resources/static/changelog.txt | 2 + .../src/main/resources/static/css/main.css | 9 ++ .../createUploadedTransactions.html | 1 + .../transaction/uploadTransactions.html | 13 ++ 14 files changed, 235 insertions(+), 90 deletions(-) diff --git a/financer-common/src/main/java/de/financer/dto/CreateUploadedTransactionsRequestDto.java b/financer-common/src/main/java/de/financer/dto/CreateUploadedTransactionsRequestDto.java index a627b6e..03d4e20 100644 --- a/financer-common/src/main/java/de/financer/dto/CreateUploadedTransactionsRequestDto.java +++ b/financer-common/src/main/java/de/financer/dto/CreateUploadedTransactionsRequestDto.java @@ -2,91 +2,114 @@ package de.financer.dto; import org.apache.commons.lang3.builder.ReflectionToStringBuilder; +import java.util.List; + public class CreateUploadedTransactionsRequestDto { - private String fromAccountKey; - private String toAccountKey; - private String amount; - private String date; - private String description; - private Boolean taxRelevant; - private String fileContent; - private String fileName; - private String recurringTransactionId; + private List entries; + private Long newPeriodOnRecurringTransactionId; - @Override - public String toString() { - return ReflectionToStringBuilder.toString(this); + public List getEntries() { + return entries; } - public String getFromAccountKey() { - return fromAccountKey; + public void setEntries(List entries) { + this.entries = entries; } - public void setFromAccountKey(String fromAccountKey) { - this.fromAccountKey = fromAccountKey; + public Long getNewPeriodOnRecurringTransactionId() { + return newPeriodOnRecurringTransactionId; } - public String getToAccountKey() { - return toAccountKey; + public void setNewPeriodOnRecurringTransactionId(Long newPeriodOnRecurringTransactionId) { + this.newPeriodOnRecurringTransactionId = newPeriodOnRecurringTransactionId; } - public void setToAccountKey(String toAccountKey) { - this.toAccountKey = toAccountKey; - } + public static class CreateUploadedTransactionsRequestEntry { + private String fromAccountKey; + private String toAccountKey; + private String amount; + private String date; + private String description; + private Boolean taxRelevant; + private String fileContent; + private String fileName; + private String recurringTransactionId; - public String getAmount() { - return amount; - } + @Override + public String toString() { + return ReflectionToStringBuilder.toString(this); + } - public void setAmount(String amount) { - this.amount = amount; - } + public String getFromAccountKey() { + return fromAccountKey; + } - public String getDate() { - return date; - } + public void setFromAccountKey(String fromAccountKey) { + this.fromAccountKey = fromAccountKey; + } - public void setDate(String date) { - this.date = date; - } + public String getToAccountKey() { + return toAccountKey; + } - public String getDescription() { - return description; - } + public void setToAccountKey(String toAccountKey) { + this.toAccountKey = toAccountKey; + } - public void setDescription(String description) { - this.description = description; - } + public String getAmount() { + return amount; + } - public Boolean getTaxRelevant() { - return taxRelevant; - } + public void setAmount(String amount) { + this.amount = amount; + } - public void setTaxRelevant(Boolean taxRelevant) { - this.taxRelevant = taxRelevant; - } + public String getDate() { + return date; + } - public String getFileContent() { - return fileContent; - } + public void setDate(String date) { + this.date = date; + } - public void setFileContent(String fileContent) { - this.fileContent = fileContent; - } + public String getDescription() { + return description; + } - public String getFileName() { - return fileName; - } + public void setDescription(String description) { + this.description = description; + } - public void setFileName(String fileName) { - this.fileName = fileName; - } + public Boolean getTaxRelevant() { + return taxRelevant; + } - public String getRecurringTransactionId() { - return recurringTransactionId; - } + public void setTaxRelevant(Boolean taxRelevant) { + this.taxRelevant = taxRelevant; + } - public void setRecurringTransactionId(String recurringTransactionId) { - this.recurringTransactionId = recurringTransactionId; + public String getFileContent() { + return fileContent; + } + + public void setFileContent(String fileContent) { + this.fileContent = fileContent; + } + + public String getFileName() { + return fileName; + } + + public void setFileName(String fileName) { + this.fileName = fileName; + } + + public String getRecurringTransactionId() { + return recurringTransactionId; + } + + public void setRecurringTransactionId(String recurringTransactionId) { + this.recurringTransactionId = recurringTransactionId; + } } } diff --git a/financer-server/src/main/java/de/financer/controller/PeriodController.java b/financer-server/src/main/java/de/financer/controller/PeriodController.java index d652126..064df2d 100644 --- a/financer-server/src/main/java/de/financer/controller/PeriodController.java +++ b/financer-server/src/main/java/de/financer/controller/PeriodController.java @@ -10,7 +10,9 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import java.time.LocalDateTime; import java.util.List; +import java.util.Optional; @RestController @RequestMapping("periods") @@ -26,7 +28,8 @@ public class PeriodController { LOGGER.debug("/periods/closeCurrentExpensePeriod called"); } - final ResponseEntity responseEntity = this.periodService.closeCurrentExpensePeriod().toResponseEntity(); + final ResponseEntity responseEntity = this.periodService.closeCurrentExpensePeriod(Optional.empty()) + .toResponseEntity(); if (LOGGER.isDebugEnabled()) { LOGGER.debug(String.format("/periods/closeCurrentExpensePeriod returns with %s", responseEntity)); diff --git a/financer-server/src/main/java/de/financer/controller/TransactionController.java b/financer-server/src/main/java/de/financer/controller/TransactionController.java index 4c92f18..4c40d91 100644 --- a/financer-server/src/main/java/de/financer/controller/TransactionController.java +++ b/financer-server/src/main/java/de/financer/controller/TransactionController.java @@ -168,12 +168,12 @@ public class TransactionController { } @PostMapping(value = "/transactions/upload") - public ResponseEntity createTransaction(@RequestBody Collection requestDtos) { + public ResponseEntity createTransaction(@RequestBody CreateUploadedTransactionsRequestDto requestDto) { if (LOGGER.isDebugEnabled()) { - LOGGER.debug(String.format("POST /transactions/upload got parameters: %s", requestDtos)); + LOGGER.debug(String.format("POST /transactions/upload got parameters: %s", requestDto)); } - final ResponseReason responseReason = this.transactionService.createTransactions(requestDtos); + final ResponseReason responseReason = this.transactionService.createTransactions(requestDto); if (LOGGER.isDebugEnabled()) { LOGGER.debug(String.format("POST /transactions/upload returns with %s", responseReason.name())); diff --git a/financer-server/src/main/java/de/financer/service/PeriodService.java b/financer-server/src/main/java/de/financer/service/PeriodService.java index 5c4a93c..dc37e1a 100644 --- a/financer-server/src/main/java/de/financer/service/PeriodService.java +++ b/financer-server/src/main/java/de/financer/service/PeriodService.java @@ -45,14 +45,17 @@ public class PeriodService { /** * This method closes the current expense period and opens a new one. * + * @param endDate an optional end date timestamp for the currently open expense period. If none is given the current + * timestamp is used + * * @return {@link ResponseReason#OK} if the operation succeeded, {@link ResponseReason#UNKNOWN_ERROR} if an * unexpected exception occurred. */ @Transactional(propagation = Propagation.REQUIRED) - public ResponseReason closeCurrentExpensePeriod() { + public ResponseReason closeCurrentExpensePeriod(Optional endDate) { final Period currentPeriod = this.getCurrentExpensePeriod(); final Period nextPeriod = new Period(); - final LocalDateTime now = LocalDateTime.now(); + final LocalDateTime now = endDate.orElse(LocalDateTime.now()); ResponseReason response; currentPeriod.setEnd(now); diff --git a/financer-server/src/main/java/de/financer/service/TransactionService.java b/financer-server/src/main/java/de/financer/service/TransactionService.java index 31ef1f0..22161ed 100644 --- a/financer-server/src/main/java/de/financer/service/TransactionService.java +++ b/financer-server/src/main/java/de/financer/service/TransactionService.java @@ -22,6 +22,8 @@ import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.util.*; @@ -418,15 +420,16 @@ public class TransactionService { /** * This method creates multiple transactions in one batch based on the given parameters. In case of invalid - * parameters the creation aborts and the first issue found is returned. + * parameters the creation aborts and the first issue found is returned. If requested via the given parameter + * the method will also close and open expense periods. * - * @param requestDtos parameters to create the transactions for + * @param requestDto parameter to create the transactions for * @return {@link ResponseReason#CREATED CREATED} in case of success, another instance of {@link ResponseReason} * otherwise. Never null */ @Transactional(propagation = Propagation.REQUIRED) - public ResponseReason createTransactions(Collection requestDtos) { - requestDtos.stream().forEach(dto -> { + public ResponseReason createTransactions(CreateUploadedTransactionsRequestDto requestDto) { + requestDto.getEntries().stream().forEach(dto -> { final ResponseReason responseReason; if(StringUtils.isNotEmpty(dto.getRecurringTransactionId())) { @@ -435,6 +438,15 @@ public class TransactionService { .orElseThrow(() -> new FinancerServiceException( ResponseReason.RECURRING_TRANSACTION_NOT_FOUND)); + if(recurringTransaction.getId().equals(requestDto.getNewPeriodOnRecurringTransactionId())) { + this.periodService + .closeCurrentExpensePeriod(Optional.of( + LocalDateTime.of( + LocalDate.parse(dto.getDate(), + DateTimeFormatter.ofPattern(this.financerConfig.getDateFormat())), + LocalTime.now()))); + } + responseReason = this.createTransaction(dto.getFromAccountKey(), dto.getToAccountKey(), Long.valueOf(dto.getAmount()), diff --git a/financer-web-client/src/main/java/de/financer/controller/TransactionController.java b/financer-web-client/src/main/java/de/financer/controller/TransactionController.java index 361b7c9..b72b487 100644 --- a/financer-web-client/src/main/java/de/financer/controller/TransactionController.java +++ b/financer-web-client/src/main/java/de/financer/controller/TransactionController.java @@ -19,8 +19,12 @@ import de.financer.transactionUpload.TransactionUploadWorker; import de.financer.transactionUpload.TransactionUploadWorkerFactory; import de.financer.transactionUpload.UploadedTransaction; import de.financer.util.ControllerUtils; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.IterableUtils; +import org.apache.commons.collections4.ListUtils; +import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.math.NumberUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.ResponseEntity; @@ -180,6 +184,17 @@ public class TransactionController { @GetMapping("/uploadTransactions") public String uploadTransaction(Model model) { + final ResponseEntity> response = new GetAllRecurringTransactionsTemplate() + .exchange(this.financerConfig); + final List recurringTransactionList = new ArrayList<>(); + final RecurringTransaction emptyRecurring = new RecurringTransaction(); + + emptyRecurring.setDescription("-"); + + recurringTransactionList.add(emptyRecurring); + recurringTransactionList.addAll(IterableUtils.toList(response.getBody())); + + model.addAttribute("recurringTransactions", recurringTransactionList); model.addAttribute("form", new UploadTransactionsForm()); model.addAttribute("formats", TransactionUploadFormat.values()); @@ -201,10 +216,30 @@ public class TransactionController { final Collection uploadedTransactions = worker.process(form.getFile()); - return _uploadTransactions(model, Optional.empty(), CreateUploadedTransactionForm.of(uploadedTransactions)); + Long newPeriodOnRecurringTransaction = null; + + if(BooleanUtils.isTrue(form.getEnableNewPeriodOnRecurringTransaction())) { + if(NumberUtils.isCreatable(form.getNewPeriodOnRecurringTransaction())) { + newPeriodOnRecurringTransaction = Long.valueOf(form.getNewPeriodOnRecurringTransaction()); + } + } + + return _uploadTransactions(model, Optional.empty(), + CreateUploadedTransactionForm.of(uploadedTransactions, newPeriodOnRecurringTransaction)); } catch(Exception e) { // TODO + final ResponseEntity> response = new GetAllRecurringTransactionsTemplate() + .exchange(this.financerConfig); + final List recurringTransactionList = new ArrayList<>(); + final RecurringTransaction emptyRecurring = new RecurringTransaction(); + + emptyRecurring.setDescription("-"); + + recurringTransactionList.add(emptyRecurring); + recurringTransactionList.addAll(IterableUtils.toList(response.getBody())); + + model.addAttribute("recurringTransactions", recurringTransactionList); model.addAttribute("errorMessage", "KAPUTT"); model.addAttribute("formats", TransactionUploadFormat.values()); model.addAttribute("form", form); @@ -220,38 +255,47 @@ public class TransactionController { @PostMapping("/createUploadedTransactions") public String createUploadedTransactions(CreateUploadedTransactionForm form, Model model) { try { - final Collection dtos = + final List entries = form.getEntries() .stream() .filter(CreateUploadedTransactionForm.CreateUploadedTransactionFormEntry::getCreate) .map(e -> { - final CreateUploadedTransactionsRequestDto dto = new CreateUploadedTransactionsRequestDto(); + final CreateUploadedTransactionsRequestDto.CreateUploadedTransactionsRequestEntry entry = + new CreateUploadedTransactionsRequestDto.CreateUploadedTransactionsRequestEntry(); - dto.setAmount(e.getAmount().toString()); - dto.setDate(ControllerUtils.formatDate(this.financerConfig, e.getDate())); - dto.setDescription(e.getDescription()); - dto.setFromAccountKey(e.getFromAccountKey()); - dto.setToAccountKey(e.getToAccountKey()); - dto.setRecurringTransactionId(Optional.ofNullable(e.getRecurringTransactionId()) - .map(id -> id.toString()) - .orElse(null)); - dto.setTaxRelevant(e.getTaxRelevant()); + entry.setAmount(e.getAmount().toString()); + entry.setDate(ControllerUtils.formatDate(this.financerConfig, e.getDate())); + entry.setDescription(e.getDescription()); + entry.setFromAccountKey(e.getFromAccountKey()); + entry.setToAccountKey(e.getToAccountKey()); + entry.setRecurringTransactionId(Optional.ofNullable(e.getRecurringTransactionId()) + .map(id -> id.toString()) + .orElse(null)); + entry.setTaxRelevant(e.getTaxRelevant()); if (e.getFile() != null && StringUtils.isNotEmpty(e.getFile().getOriginalFilename())) { try { - dto.setFileContent(Base64.getEncoder().encodeToString(e.getFile().getBytes())); - dto.setFileName(e.getFile().getOriginalFilename()); + entry.setFileContent(Base64.getEncoder().encodeToString(e.getFile().getBytes())); + entry.setFileName(e.getFile().getOriginalFilename()); } catch (IOException ioe) { // TODO No file for us :( } } - return dto; + return entry; }) .collect(Collectors.toList()); + final CreateUploadedTransactionsRequestDto dto = new CreateUploadedTransactionsRequestDto(); + + // We need to reverse the entries because of the actual booking order + // In the UI it is sorted descending, but we need to book ascending + Collections.reverse(entries); + dto.setEntries(entries); + dto.setNewPeriodOnRecurringTransactionId(form.getNewPeriodOnRecurringTransaction()); + final ResponseReason responseReason = FinancerRestTemplate - .exchangePost(this.financerConfig, Function.TR_CREATE_UPLOADED_TRANSACTIONS, dtos); + .exchangePost(this.financerConfig, Function.TR_CREATE_UPLOADED_TRANSACTIONS, dto); if (!ResponseReason.CREATED.equals(responseReason)) { return _uploadTransactions(model, Optional.of(responseReason), form); diff --git a/financer-web-client/src/main/java/de/financer/form/CreateUploadedTransactionForm.java b/financer-web-client/src/main/java/de/financer/form/CreateUploadedTransactionForm.java index 976def3..8a33355 100644 --- a/financer-web-client/src/main/java/de/financer/form/CreateUploadedTransactionForm.java +++ b/financer-web-client/src/main/java/de/financer/form/CreateUploadedTransactionForm.java @@ -9,11 +9,13 @@ import java.util.List; public class CreateUploadedTransactionForm { private List entries = new ArrayList<>(); + private Long newPeriodOnRecurringTransaction; - public static CreateUploadedTransactionForm of(Collection uploadedTransactions) { + public static CreateUploadedTransactionForm of(Collection uploadedTransactions, Long newPeriodOnRecurringTransaction) { final CreateUploadedTransactionForm form = new CreateUploadedTransactionForm(); uploadedTransactions.stream().forEach(e -> form.getEntries().add(CreateUploadedTransactionFormEntry.of(e))); + form.setNewPeriodOnRecurringTransaction(newPeriodOnRecurringTransaction); return form; } @@ -26,6 +28,14 @@ public class CreateUploadedTransactionForm { this.entries = entries; } + public Long getNewPeriodOnRecurringTransaction() { + return newPeriodOnRecurringTransaction; + } + + public void setNewPeriodOnRecurringTransaction(Long newPeriodOnRecurringTransaction) { + this.newPeriodOnRecurringTransaction = newPeriodOnRecurringTransaction; + } + public static final class CreateUploadedTransactionFormEntry { private Boolean create; private String fromAccountKey; diff --git a/financer-web-client/src/main/java/de/financer/form/UploadTransactionsForm.java b/financer-web-client/src/main/java/de/financer/form/UploadTransactionsForm.java index 4cc5794..6ee6ced 100644 --- a/financer-web-client/src/main/java/de/financer/form/UploadTransactionsForm.java +++ b/financer-web-client/src/main/java/de/financer/form/UploadTransactionsForm.java @@ -5,6 +5,8 @@ import org.springframework.web.multipart.MultipartFile; public class UploadTransactionsForm { private String format; private MultipartFile file; + private String newPeriodOnRecurringTransaction; + private Boolean enableNewPeriodOnRecurringTransaction; public String getFormat() { return format; @@ -21,4 +23,20 @@ public class UploadTransactionsForm { public void setFile(MultipartFile file) { this.file = file; } + + public String getNewPeriodOnRecurringTransaction() { + return newPeriodOnRecurringTransaction; + } + + public void setNewPeriodOnRecurringTransaction(String newPeriodOnRecurringTransaction) { + this.newPeriodOnRecurringTransaction = newPeriodOnRecurringTransaction; + } + + public Boolean getEnableNewPeriodOnRecurringTransaction() { + return enableNewPeriodOnRecurringTransaction; + } + + public void setEnableNewPeriodOnRecurringTransaction(Boolean enableNewPeriodOnRecurringTransaction) { + this.enableNewPeriodOnRecurringTransaction = enableNewPeriodOnRecurringTransaction; + } } diff --git a/financer-web-client/src/main/resources/i18n/message.properties b/financer-web-client/src/main/resources/i18n/message.properties index 8ad5566..ea4041c 100644 --- a/financer-web-client/src/main/resources/i18n/message.properties +++ b/financer-web-client/src/main/resources/i18n/message.properties @@ -176,6 +176,9 @@ financer.upload-transactions.label.format=Format\: financer.upload-transactions.format.MT940_CSV=MT940 CSV financer.upload-transactions.label.file=File\: financer.upload-transactions.submit=Upload transactions +financer.upload-transactions.label.new-period-on-recurring-transaction-summary=New period on encountering a certain recurring transaction +financer.upload-transactions.label.new-period-on-recurring-transaction=Recurring transaction\: +financer.upload-transactions.label.new-period-on-recurring-transaction-enable=Enable\: financer.create-upload-transactions.title=financer\: create uploaded transactions financer.create-upload-transactions.table-header.create=Create @@ -272,8 +275,8 @@ financer.heading.account-edit=financer\: edit account financer.cancel-back-to-overview=Cancel and back to overview financer.back-to-overview=Back to overview - financer.show-actions=Show... +financer.show-options=Show options... financer.chart.account-group-expenses-current-period.title=Expenses of the current period grouped by account group financer.chart.account-group-expenses-for-period.title=Expenses for period from {0} to {1} grouped by account group diff --git a/financer-web-client/src/main/resources/i18n/message_de_DE.properties b/financer-web-client/src/main/resources/i18n/message_de_DE.properties index 58fe6c4..1e121c5 100644 --- a/financer-web-client/src/main/resources/i18n/message_de_DE.properties +++ b/financer-web-client/src/main/resources/i18n/message_de_DE.properties @@ -176,6 +176,9 @@ financer.upload-transactions.label.format=Format\: financer.upload-transactions.format.MT940_CSV=MT940 CSV financer.upload-transactions.label.file=Datei\: financer.upload-transactions.submit=Buchungen hochladen +financer.upload-transactions.label.new-period-on-recurring-transaction-summary=Neue Periode bei wiederkehrender Buchung +financer.upload-transactions.label.new-period-on-recurring-transaction=Wiederkehrende Buchung\: +financer.upload-transactions.label.new-period-on-recurring-transaction-enable=Aktiv\: financer.create-upload-transactions.title=financer\: Erstelle hochgeladene Buchungen financer.create-upload-transactions.table-header.create=Erstellen @@ -272,6 +275,7 @@ financer.heading.account-edit=financer\: Bearbeite Konto financer.cancel-back-to-overview=Abbrechen und zur\u00FCck zur \u00DCbersicht financer.back-to-overview=Zur\u00FCck zur \u00DCbersicht financer.show-actions=Anzeigen... +financer.show-options=Zeige Optionen... financer.chart.account-group-expenses-current-period.title=Ausgaben in der aktuellen Periode gruppiert nach Konto-Gruppe financer.chart.account-group-expenses-for-period.title=Ausgaben in der Periode vom {0} bis {1} gruppiert nach Konto-Gruppe diff --git a/financer-web-client/src/main/resources/static/changelog.txt b/financer-web-client/src/main/resources/static/changelog.txt index a35a83a..58a95ee 100644 --- a/financer-web-client/src/main/resources/static/changelog.txt +++ b/financer-web-client/src/main/resources/static/changelog.txt @@ -3,6 +3,8 @@ v48 -> v49: only the active ones - #11 It is now possible to specify a date during creation of a transaction from a recurring transaction - #25 Added the possibility to edit accounts +- #23 Added an option to specify a recurring transaction during transaction upload that will close and open expense + periods v47 -> v48: - #20 Added new property 'transaction type' to a transaction, denoting the type of the transaction, e.g. asset swap, diff --git a/financer-web-client/src/main/resources/static/css/main.css b/financer-web-client/src/main/resources/static/css/main.css index aceecf0..dedaaf3 100644 --- a/financer-web-client/src/main/resources/static/css/main.css +++ b/financer-web-client/src/main/resources/static/css/main.css @@ -388,4 +388,13 @@ input[type=submit] { .cal-day-con-today { background-color: #f5e5b8; +} + +.transaction-upload-fieldset { + margin-left: 1em; + margin-top: 0px !important; +} + +.transaction-upload-fieldset * { + width: 17.75em !important; } \ No newline at end of file diff --git a/financer-web-client/src/main/resources/templates/transaction/createUploadedTransactions.html b/financer-web-client/src/main/resources/templates/transaction/createUploadedTransactions.html index cd76045..6854b07 100644 --- a/financer-web-client/src/main/resources/templates/transaction/createUploadedTransactions.html +++ b/financer-web-client/src/main/resources/templates/transaction/createUploadedTransactions.html @@ -66,6 +66,7 @@ +
diff --git a/financer-web-client/src/main/resources/templates/transaction/uploadTransactions.html b/financer-web-client/src/main/resources/templates/transaction/uploadTransactions.html index 04c9e6e..205acd3 100644 --- a/financer-web-client/src/main/resources/templates/transaction/uploadTransactions.html +++ b/financer-web-client/src/main/resources/templates/transaction/uploadTransactions.html @@ -22,6 +22,19 @@