Skip to content

Commit

Permalink
#724 - match columns from csv with BudgetMaster attributes (date, tit…
Browse files Browse the repository at this point in the history
…le and amount) + show status for each row
  • Loading branch information
deadlocker8 committed Jan 8, 2023
1 parent 1ac54dc commit d0dd8a8
Show file tree
Hide file tree
Showing 8 changed files with 251 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@

import de.deadlocker8.budgetmaster.controller.BaseController;
import de.deadlocker8.budgetmaster.services.HelpersService;
import de.deadlocker8.budgetmaster.transactions.csvImport.CsvImport;
import de.deadlocker8.budgetmaster.transactions.csvImport.CsvParser;
import de.deadlocker8.budgetmaster.transactions.csvImport.CsvRow;
import de.deadlocker8.budgetmaster.transactions.csvImport.*;
import de.deadlocker8.budgetmaster.utils.Mappings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
Expand All @@ -19,7 +17,9 @@
import org.springframework.web.context.request.WebRequest;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

@Controller
@RequestMapping(Mappings.TRANSACTION_IMPORT)
Expand All @@ -41,6 +41,7 @@ private static class RequestAttributeNames
{
public static final String CSV_IMPORT = "csvImport";
public static final String CSV_ROWS = "csvRows";
public static final String CSV_TRANSACTIONS = "csvTransactions";
public static final String ERROR_UPLOAD = "errorUpload";
public static final String ERROR_UPLOAD_FILE = "errorUploadFile";
}
Expand Down Expand Up @@ -119,6 +120,38 @@ public String upload(WebRequest request,
return ReturnValues.REDIRECT_IMPORT;
}

@PostMapping("/columnSettings")
public String columnSettings(WebRequest request,
@ModelAttribute("CsvColumnSettings") CsvColumnSettings csvColumnSettings,
BindingResult bindingResult)
{
if(bindingResult.hasErrors())
{
request.setAttribute(RequestAttributeNames.ERROR_UPLOAD, bindingResult, RequestAttributes.SCOPE_SESSION);
return ReturnValues.REDIRECT_IMPORT;
}

final Object attribute = request.getAttribute(RequestAttributeNames.CSV_ROWS, RequestAttributes.SCOPE_SESSION);
if(attribute == null)
{
return ReturnValues.REDIRECT_CANCEL;
}

final List<CsvRow> csvRows = (List<CsvRow>) attribute;
final List<CsvTransaction> csvTransactions = new ArrayList<>();
for(CsvRow csvRow : csvRows)
{
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));
}

request.setAttribute(RequestAttributeNames.CSV_TRANSACTIONS, csvTransactions, RequestAttributes.SCOPE_SESSION);

return ReturnValues.REDIRECT_IMPORT;
}

@GetMapping("/cancel")
public String cancel(WebRequest request)
{
Expand All @@ -130,6 +163,7 @@ private void removeAllAttributes(WebRequest request)
{
request.removeAttribute(RequestAttributeNames.CSV_IMPORT, RequestAttributes.SCOPE_SESSION);
request.removeAttribute(RequestAttributeNames.CSV_ROWS, RequestAttributes.SCOPE_SESSION);
request.removeAttribute(RequestAttributeNames.CSV_TRANSACTIONS, RequestAttributes.SCOPE_SESSION);
request.removeAttribute(RequestAttributeNames.ERROR_UPLOAD, RequestAttributes.SCOPE_SESSION);
request.removeAttribute(RequestAttributeNames.ERROR_UPLOAD_FILE, RequestAttributes.SCOPE_SESSION);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package de.deadlocker8.budgetmaster.transactions.csvImport;

public record CsvColumnSettings(int columnDate, int columnName, int columnAmount)
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package de.deadlocker8.budgetmaster.transactions.csvImport;

import java.util.Objects;

public final class CsvTransaction
{
private final String date;
private final String name;
private final String amount;
private CsvTransactionStatus status;

public CsvTransaction(String date, String name, String amount, CsvTransactionStatus status)
{
this.date = date;
this.name = name;
this.amount = amount;
this.status = status;
}

public String getDate()
{
return date;
}

public String getName()
{
return name;
}

public String getAmount()
{
return amount;
}

public CsvTransactionStatus getStatus()
{
return status;
}

public void setStatus(CsvTransactionStatus status)
{
this.status = status;
}

@Override
public boolean equals(Object o)
{
if(this == o) return true;
if(o == null || getClass() != o.getClass()) return false;
CsvTransaction that = (CsvTransaction) o;
return Objects.equals(date, that.date) && Objects.equals(name, that.name) && Objects.equals(amount, that.amount) && status == that.status;
}

@Override
public int hashCode()
{
return Objects.hash(date, name, amount, status);
}

@Override
public String toString()
{
return "CsvTransaction{" +
"date='" + date + '\'' +
", name='" + name + '\'' +
", amount='" + amount + '\'' +
", status=" + status +
'}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package de.deadlocker8.budgetmaster.transactions.csvImport;

public enum CsvTransactionStatus
{
PENDING,
IMPORTED,
SKIPPED;
}
Original file line number Diff line number Diff line change
Expand Up @@ -370,11 +370,15 @@ transaction.new.label.repeating=Wiederholung
transaction.new.label.repeating.all=Alle
transactions.recurring.headline=Aktive wiederholende Buchungen
transactions.recurring.placeholder=Keine aktiven wiederholenden Buchungen
transactions.import.overview=Übersicht
transactions.import.matchColumns=Spalten zuordnen
transactions.import.column=Spalte
transactions.import.separator=Trennzeichen
transactions.import.encoding=Kodierung
transactions.import.numberOfLinesToSkip=Zeilen überspringen
transactions.import.status=Status
transactions.import.status.pending=ausstehend
transactions.import.status.imported=importiert
transactions.import.status.skipped=übersprungen

repeating.button.add=Wiederholung hinzufügen
repeating.button.remove=Wiederholung entfernen
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -369,11 +369,15 @@ transaction.new.label.repeating=Repeating
transaction.new.label.repeating.all=Every
transactions.recurring.headline=Active Recurring Transactions
transactions.recurring.placeholder=No active recurring transactions
transactions.import.overview=Overview
transactions.import.matchColumns=Match columns
transactions.import.column=Column
transactions.import.separator=Separator
transactions.import.encoding=Encoding
transactions.import.numberOfLinesToSkip=Skip lines
transactions.import.status=Status
transactions.import.status.pending=pending
transactions.import.status.imported=imported
transactions.import.status.skipped=skipped

repeating.button.add=Add repetition
repeating.button.remove=Remove repetition
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
#transaction-import-overview {
overflow: auto;
}

.transaction-import-text-with-icon {
display: flex;
flex-direction: row;
align-items: center;
margin-top: 1rem;
}

.transaction-import-text-with-icon i {
margin-right: 1.3rem;
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

<@header.content>
<div class="container">
<#if !error?? && csvImport.getFileName()??>
<#if csvRows??>
<div class="row center-align">
<div class="col s12 m12 l8 offset-l2 headline-small text-green truncate">
<i class="fas fa-file-csv"></i> ${csvImport.getFileName()}
Expand All @@ -40,7 +40,15 @@
</#if>
</div>

<#if csvRows??>
<#if csvTransactions??>
<@renderCsvTransactions/>
<#elseif csvRows?? >
<div class="container">
<div class="section center-align">
<div class="headline-small">${locale.getString("transactions.import.matchColumns")}</div>
</div>
</div>
<@columnSettings/>
<@renderCsvRows/>
</#if>
</@header.content>
Expand Down Expand Up @@ -92,13 +100,68 @@
</form>
</#macro>

<#macro renderCsvRows>
<#macro columnSettings>
<div class="container">
<div class="section center-align">
<div class="headline-small">${locale.getString("transactions.import.overview")}</div>
</div>
<form id="form-csv-column-settings" name="CsvColumnSettings" method="POST" action="<@s.url '/transactionImport/columnSettings'/>">
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
<div class="row">
<div class="col s6 m4 offset-m2 l3 offset-l3 bold">
BudgetMaster
</div>
<div class="col s6 m4 l3 bold">
CSV
</div>
</div>

<div class="row">
<div class="col s6 m4 offset-m2 l3 offset-l3">
<div class="transaction-import-text-with-icon">
<i class="material-icons">event</i>
${locale.getString("transaction.new.label.date")}
</div>
</div>
<div class="input-field col s6 m4 l3 no-margin-top no-margin-bottom">
<input id="columnDate" type="number" min="1" max="${csvRows?size}" name="columnDate" <@validation.validation "columnDate"/> value="<#if csvColumnSettings??>${csvColumnSettings.columnDate()}</#if>">
<label class="input-label" for="columnDate">${locale.getString("transactions.import.column")}</label>
</div>
</div>
<div class="row">
<div class="col s6 m4 offset-m2 l3 offset-l3">
<div class="transaction-import-text-with-icon">
<i class="material-icons">edit</i>
${locale.getString("transaction.new.label.name")}
</div>
</div>
<div class="input-field col s6 m4 l3 no-margin-top no-margin-bottom">
<input id="columnName" type="number" min="1" max="${csvRows?size}" name="columnName" <@validation.validation "columnName"/> value="<#if csvColumnSettings??>${csvColumnSettings.columnName()}</#if>">
<label class="input-label" for="columnName">${locale.getString("transactions.import.column")}</label>
</div>
</div>
<div class="row">
<div class="col s6 m4 offset-m2 l3 offset-l3">
<div class="transaction-import-text-with-icon">
<i class="material-icons">euro</i>
${locale.getString("transaction.new.label.amount")}
</div>
</div>
<div class="input-field col s6 m4 l3 no-margin-top no-margin-bottom">
<input id="columnAmount" type="number" min="1" max="${csvRows?size}" name="columnAmount" <@validation.validation "columnAmount"/> value="<#if csvColumnSettings??>${csvColumnSettings.columnAmount()}</#if>">
<label class="input-label" for="columnAmount">${locale.getString("transactions.import.column")}</label>
</div>
</div>

<br>

<div class="row">
<div class="col s12 center-align">
<@header.buttonSubmit name='action' icon='save' localizationKey='save' id='button-confirm-csv-column-settings' classes='text-white'/>
</div>
</div>
</form>
</div>
</#macro>

<#macro renderCsvRows>
<div class="container" id="transaction-import-overview">
<table class="bordered centered">
<tr>
Expand All @@ -119,4 +182,45 @@
</#list>
</table>
</div>
</#macro>

<#macro renderCsvTransactions>
<div class="container" id="transaction-import-list">
<table class="bordered centered">
<tr>
<td class="bold">${locale.getString("transactions.import.status")}</td>
<td class="bold">${locale.getString("transaction.new.label.date")}</td>
<td class="bold">${locale.getString("transaction.new.label.name")}</td>
<td class="bold">${locale.getString("transaction.new.label.amount")}</td>
</tr>

<#list csvTransactions as csvTransaction>
<tr>
<td><@statusBanner csvTransaction.getStatus()/></td>
<td>${csvTransaction.getDate()}</td>
<td>${csvTransaction.getName()}</td>
<td>${csvTransaction.getAmount()}</td>
</tr>
</#list>
</table>
</div>
</#macro>

<#macro statusBanner status>
<#if status.name() == "PENDING">
<#assign bannerClasses="background-blue text-white">
<#assign bannerText=locale.getString("transactions.import.status.pending")>
<#elseif status.name() == "IMPORTED">
<#assign bannerClasses="background-green text-white">
<#assign bannerText=locale.getString("transactions.import.status.imported")>
<#elseif status.name() == "SKIPPED">
<#if settings.isUseDarkTheme()>
<#assign bannerClasses="background-grey text-black">
<#else>
<#assign bannerClasses="background-grey text-white">
</#if>
<#assign bannerText=locale.getString("transactions.import.status.skipped")>
</#if>

<div class="banner ${bannerClasses}">${bannerText}</div>
</#macro>

0 comments on commit d0dd8a8

Please sign in to comment.