Skip to content

Commit

Permalink
Add DKB2 and ING format
Browse files Browse the repository at this point in the history
  • Loading branch information
MarvinKlar committed Jul 6, 2024
1 parent 2ed9792 commit 4d522ea
Show file tree
Hide file tree
Showing 5 changed files with 199 additions and 15 deletions.
91 changes: 91 additions & 0 deletions src/main/java/link/biosmarcel/baka/bankimport/DKB2CSV.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package link.biosmarcel.baka.bankimport;

import link.biosmarcel.baka.data.Account;
import link.biosmarcel.baka.data.Payment;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.QuoteMode;

import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

/**
* Format for new DKB UI.
*/
public class DKB2CSV {
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("dd.MM.yy");
private static final DecimalFormat CURRENCY_FORMAT;

static {
CURRENCY_FORMAT = (DecimalFormat) DecimalFormat.getNumberInstance(Locale.GERMAN);
CURRENCY_FORMAT.setParseBigDecimal(true);
}

public static List<Payment> parse(final Account account, final File file) {
try (Reader reader = Files.newBufferedReader(file.toPath(), StandardCharsets.ISO_8859_1)) {
final var format = CSVFormat.Builder
.create()
.setDelimiter(';')
.setQuote('"')
// Haven't figured out the escape character yet.
.setQuoteMode(QuoteMode.MINIMAL)
.setIgnoreSurroundingSpaces(true)
.setIgnoreEmptyLines(true)
.setAllowMissingColumnNames(true)
.build();
final var records = format.parse(reader).iterator();
// Skip header row; Format.setSkipHeaderRecord doesn't seem to work.
// Also skip rows that just contain meta data. Empty rows don't count, so we skip 5.
for (int i = 0; i < 5; i++) {
records.next();
}

final List<Payment> newPayments = new ArrayList<>();
records.forEachRemaining(record -> {
final var reference = record.get(5);
final BigDecimal amount;
try {
amount = (BigDecimal) CURRENCY_FORMAT.parse(record.get(8));
} catch (final ParseException exception) {
throw new RuntimeException(exception);
}

final var bookingDate = LocalDate.parse(record.get(0), DATE_FORMAT).atStartOfDay();
final var effectiveDate = switch (record.get(1)) {
// EffectiveDate is optional, so to avoid confusion, we just set it to the same as bookingDate.
case null -> bookingDate;
case "" -> bookingDate;
default -> LocalDate.parse(record.get(1), DATE_FORMAT).atStartOfDay();
};
final String name = record.get(3);

final Payment payment = new Payment(
account,
amount,
reference,
name,
bookingDate,
effectiveDate
);

payment.participant = Import.prepareIBAN(record.get(7));

newPayments.add(payment);
});

return newPayments;
} catch (final IOException exception) {
throw new RuntimeException(exception);
}
}
}
4 changes: 3 additions & 1 deletion src/main/java/link/biosmarcel/baka/bankimport/DKBCSV.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
import java.util.List;
import java.util.Locale;

/**
* Format for old DKB UI. The new UI uses a new format.
*/
public class DKBCSV {
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("dd.MM.yyyy");
private static final DecimalFormat CURRENCY_FORMAT;
Expand Down Expand Up @@ -75,7 +78,6 @@ public static List<Payment> parse(final Account account, final File file) {
effectiveDate
);

// Revolut CSV doesn't supply this, we just got the reference, which is called "description"
payment.participant = Import.prepareIBAN(record.get(5));

newPayments.add(payment);
Expand Down
88 changes: 88 additions & 0 deletions src/main/java/link/biosmarcel/baka/bankimport/INGCSV.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package link.biosmarcel.baka.bankimport;

import link.biosmarcel.baka.data.Account;
import link.biosmarcel.baka.data.Payment;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.QuoteMode;

import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

public class INGCSV {
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("dd.MM.yyyy");
private static final DecimalFormat CURRENCY_FORMAT;

static {
CURRENCY_FORMAT = (DecimalFormat) DecimalFormat.getNumberInstance(Locale.GERMAN);
CURRENCY_FORMAT.setParseBigDecimal(true);
}

public static List<Payment> parse(final Account account, final File file) {
try (Reader reader = Files.newBufferedReader(file.toPath(), StandardCharsets.ISO_8859_1)) {
final var format = CSVFormat.Builder
.create()
.setDelimiter(';')
.setQuote('"')
// Haven't figured out the escape character yet.
.setQuoteMode(QuoteMode.MINIMAL)
.setIgnoreSurroundingSpaces(true)
.setIgnoreEmptyLines(true)
.setAllowMissingColumnNames(true)
.build();
final var records = format.parse(reader).iterator();
// Skip header row; Format.setSkipHeaderRecord doesn't seem to work.
// Also skip rows that just contain meta data. Empty rows don't count, so we skip 10.
for (int i = 0; i < 10; i++) {
records.next();
}

final List<Payment> newPayments = new ArrayList<>();
records.forEachRemaining(record -> {
final var reference = record.get(4);
final BigDecimal amount;
try {
amount = (BigDecimal) CURRENCY_FORMAT.parse(record.get(7));
} catch (final ParseException exception) {
throw new RuntimeException(exception);
}

final var bookingDate = LocalDate.parse(record.get(0), DATE_FORMAT).atStartOfDay();
final var effectiveDate = switch (record.get(1)) {
// EffectiveDate is optional, so to avoid confusion, we just set it to the same as bookingDate.
case null -> bookingDate;
case "" -> bookingDate;
default -> LocalDate.parse(record.get(1), DATE_FORMAT).atStartOfDay();
};
final String name = record.get(2);

final Payment payment = new Payment(
account,
amount,
reference,
name,
bookingDate,
effectiveDate
);

payment.participant = ""; // Not included in ING format

newPayments.add(payment);
});

return newPayments;
} catch (final IOException exception) {
throw new RuntimeException(exception);
}
}
}
18 changes: 15 additions & 3 deletions src/main/java/link/biosmarcel/baka/data/ImportFormat.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
package link.biosmarcel.baka.data;

import java.io.File;
import java.util.List;
import java.util.function.BiFunction;

public enum ImportFormat {
SparkasseCSV,
DKBCSV,
RevolutCSV,
SparkasseCSV(link.biosmarcel.baka.bankimport.SparkasseCSV::parse),
DKBCSV(link.biosmarcel.baka.bankimport.DKBCSV::parse),
RevolutCSV(link.biosmarcel.baka.bankimport.RevolutCSV::parse),
DKB2CSV(link.biosmarcel.baka.bankimport.DKB2CSV::parse),
INGCSV(link.biosmarcel.baka.bankimport.INGCSV::parse),
;

public final transient BiFunction<Account, File, List<Payment>> func;

ImportFormat(final BiFunction<Account, File, List<Payment>> func) {
this.func = func;
}
}
13 changes: 2 additions & 11 deletions src/main/java/link/biosmarcel/baka/view/PaymentsView.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@
import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import link.biosmarcel.baka.ApplicationState;
import link.biosmarcel.baka.bankimport.DKBCSV;
import link.biosmarcel.baka.bankimport.RevolutCSV;
import link.biosmarcel.baka.bankimport.SparkasseCSV;
import link.biosmarcel.baka.bankimport.*;
import link.biosmarcel.baka.data.Account;
import link.biosmarcel.baka.data.Payment;

Expand Down Expand Up @@ -137,18 +135,11 @@ protected void onTabActivated() {
}

final var menuItem = new MenuItem(account.name);
menuItem.setOnAction(__ -> {
switch (account.importFormat) {
case SparkasseCSV -> importHandler(account, SparkasseCSV::parse);
case DKBCSV -> importHandler(account, DKBCSV::parse);
case RevolutCSV -> importHandler(account, RevolutCSV::parse);
}
});
menuItem.setOnAction(_ -> importHandler(account, account.importFormat.func));
importButton.getItems().add(menuItem);
}
}


@Override
protected void onTabDeactivated() {
details.activePayment.unbind();
Expand Down

0 comments on commit 4d522ea

Please sign in to comment.