From 033e7ec16eb46e542a442c302a4d8606154f0c7b Mon Sep 17 00:00:00 2001 From: =?utf8?q?V=C3=A1s=C3=A1ry=20D=C3=A1niel?= Date: Wed, 15 Nov 2023 11:58:49 +0100 Subject: [PATCH] Bank statement parser --- .../user/lis/workflow/StatementParserIT.java | 40 ++++++++++++ .../src/test/resources/mbh/STATEMENT_1.STM | 5 ++ .../src/test/resources/mbh/STATEMENT_2.STM | 6 ++ .../src/test/resources/mbh/STATEMENT_3.STM | 11 ++++ .../lis/service/mbh/StatementDataRecord.java | 61 +++++++++++++++++++ .../service/mbh/StatementHeaderRecord.java | 26 ++++++++ .../user/lis/service/mbh/StatementItem.java | 4 ++ .../user/lis/service/mbh/StatementParser.java | 54 ++++++++++++++++ .../user/lis/service/mbh/StatementRecord.java | 5 ++ .../lis/service/mbh/StatementRecordType.java | 23 +++++++ .../user/lis/service/mbh/StatementToken.java | 11 ++++ .../builder/StatementDataRecordBuilder.java | 37 +++++++++++ .../builder/StatementHeaderRecordBuilder.java | 27 ++++++++ .../mbh/builder/StatementRecordBuilder.java | 50 +++++++++++++++ 14 files changed, 360 insertions(+) create mode 100644 lis-app/src/test/java/hu/user/lis/workflow/StatementParserIT.java create mode 100644 lis-app/src/test/resources/mbh/STATEMENT_1.STM create mode 100644 lis-app/src/test/resources/mbh/STATEMENT_2.STM create mode 100644 lis-app/src/test/resources/mbh/STATEMENT_3.STM create mode 100644 lis-service/src/main/java/hu/user/lis/service/mbh/StatementDataRecord.java create mode 100644 lis-service/src/main/java/hu/user/lis/service/mbh/StatementHeaderRecord.java create mode 100644 lis-service/src/main/java/hu/user/lis/service/mbh/StatementItem.java create mode 100644 lis-service/src/main/java/hu/user/lis/service/mbh/StatementParser.java create mode 100644 lis-service/src/main/java/hu/user/lis/service/mbh/StatementRecord.java create mode 100644 lis-service/src/main/java/hu/user/lis/service/mbh/StatementRecordType.java create mode 100644 lis-service/src/main/java/hu/user/lis/service/mbh/StatementToken.java create mode 100644 lis-service/src/main/java/hu/user/lis/service/mbh/builder/StatementDataRecordBuilder.java create mode 100644 lis-service/src/main/java/hu/user/lis/service/mbh/builder/StatementHeaderRecordBuilder.java create mode 100644 lis-service/src/main/java/hu/user/lis/service/mbh/builder/StatementRecordBuilder.java diff --git a/lis-app/src/test/java/hu/user/lis/workflow/StatementParserIT.java b/lis-app/src/test/java/hu/user/lis/workflow/StatementParserIT.java new file mode 100644 index 0000000..2b046cc --- /dev/null +++ b/lis-app/src/test/java/hu/user/lis/workflow/StatementParserIT.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) $today.year-$today.month-24. + * By elGekko + */ + +package hu.user.lis.workflow; + +import hu.user.lis.service.mbh.StatementParser; +import lombok.extern.log4j.Log4j2; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringRunner; + +import java.io.IOException; +import java.nio.file.Paths; + + +@Log4j2 +@RunWith(SpringRunner.class) +@ComponentScan("hu.user.lis") +@SpringBootTest +@ActiveProfiles("dev") +//@TestPropertySource("classpath:application-dev.yaml") +//@AutoConfigureMockMvc +public class StatementParserIT { + @Autowired + private StatementParser statementParser; + + @Test + public void testParse() throws IOException { + statementParser.parse(Paths.get("src/test/resources/mbh/STATEMENT_1.STM")); + statementParser.parse(Paths.get("src/test/resources/mbh/STATEMENT_2.STM")); + statementParser.parse(Paths.get("src/test/resources/mbh/STATEMENT_3.STM")); + } + +} diff --git a/lis-app/src/test/resources/mbh/STATEMENT_1.STM b/lis-app/src/test/resources/mbh/STATEMENT_1.STM new file mode 100644 index 0000000..a01812a --- /dev/null +++ b/lis-app/src/test/resources/mbh/STATEMENT_1.STM @@ -0,0 +1,5 @@ +11002 101022442428160001004996USDUSER Rendszerház Kft 2023110220231102 2271166 3312115USER RENDSZERHÁZ INFORMATIKAI KORLÁTOLT FELELÕSSÉGBUDAPEST PÜNKÖSDFÜRDÕ UTCA 52. 1039 HU +1200100 000BICH23304000 000000001042035USD Umbrella Kreativ Muhely HU32108000077000000014144021 E-USER-2023-236 USER RENDSZERHÁZ KFT 101022442428160001004996 101022442428160001004996USD000000000104203520231101 +12JTERH 000BICH23304000-000000000001086USD USER RENDSZERHÁZ KFT 101022442428160001004996 E-USER-2023-236 USER RENDSZERHAZ KFT HU23101022442428160001004996 101022442428160001004996USD000000000000108620231101 +13 +14 diff --git a/lis-app/src/test/resources/mbh/STATEMENT_2.STM b/lis-app/src/test/resources/mbh/STATEMENT_2.STM new file mode 100644 index 0000000..35a0030 --- /dev/null +++ b/lis-app/src/test/resources/mbh/STATEMENT_2.STM @@ -0,0 +1,6 @@ +11007 101022442428160001004305EURUSER Rendszerház Kft 2023110220231102 6915989 4819787USER RENDSZERHÁZ INFORMATIKAI KORLÁTOLT FELELÕSSÉGBUDAPEST PÜNKÖSDFÜRDÕ UTCA 52. 1039 HU +1200100 000FNER23306200-000000002092960EUR USER RENDSZERHÁZ KFT 101022442428160001004305 INV019/2023 CNW Rendszerintegrcis Zrt. HU59101008400321720001003300 101022442428160001004305EUR000000000209296020231102 +12JTERH 000FNER23306200-000000000002614EUR USER RENDSZERHÁZ KFT 101022442428160001004305 INV019/2023 CNW Rendszerintegrcis Zrt. HU59101008400321720001003300 101022442428160001004305EUR000000000000261420231102 +12JTERH 000FNER23306200-000000000000628EUR USER RENDSZERHÁZ KFT 101022442428160001004305 INV019/2023 CNW Rendszerintegrcis Zrt. HU59101008400321720001003300 101022442428160001004305EUR000000000000062820231102 +13 +14 diff --git a/lis-app/src/test/resources/mbh/STATEMENT_3.STM b/lis-app/src/test/resources/mbh/STATEMENT_3.STM new file mode 100644 index 0000000..f5f3acf --- /dev/null +++ b/lis-app/src/test/resources/mbh/STATEMENT_3.STM @@ -0,0 +1,11 @@ +11018 101022442428160001004006HUFUSER Rendszerház Kft 2023110220231102 2414568500 10723405400USER RENDSZERHÁZ INFORMATIKAI KORLÁTOLT FELELÕSSÉGBUDAPEST PÜNKÖSDFÜRDÕ UTCA 52. 1039 HU +1200100 000TIGT23306169 000000010953800HUF Mitra Informatikai Zrt. HU54182030330600075140010013 E-USER-2023-242 AIX, PowerVM, Po werHA,Tivoli tam. 2023.09.ho USER RENDSZERHÁZ KFT 101022442428160001004006 17 101022442428160001004006HUF000000001095380020231102 MKKBHUHB 18203033 +1200100 007FTRQ23306002 000000134094600HUF USER RENDSZERHÁZ KFT USER RENDSZERHÁZ KFT LÉVAI TIBOR SÁNDORFund Transfer from - 2428160002005000 - to - 2428160001004006 USER RENDSZERHÁZ KFT 101022442428160001004006 101022442428160001004006HUF000000013409460020231102 +1200100 007NSCB23298003 000000015111100HUF USER RENDSZERHÁZ KFT 101022442428160001004006 Kamat elszámolás 101022442428160001004006 101022442428160001004006HUF000000001511110020231102 +1200100 000TIGT23306169 000000069850000HUF Mitra Informatikai Zrt. HU54182030330600075140010013 E-USER-2023-240 IBM Spectrum Pro tect uzemeltetes-tamogat 2023.09 USER RENDSZERHÁZ KFT 101022442428160001004006 18 101022442428160001004006HUF000000006985000020231102 MKKBHUHB 18203033 +1200100 000TIGT23306169 000000078105000HUF Mitra Informatikai Zrt. HU54182030330600075140010013 E-USER-2023-239 Takarekinfo Powe res mentes support 2023.09. USER RENDSZERHÁZ KFT 101022442428160001004006 16 101022442428160001004006HUF000000007810500020231102 MKKBHUHB 18203033 +1200100 007NSCB23298003 000008000000000HUF USER RENDSZERHÁZ KFT 101022442428160001004006 Tõke elszámolása 101022442428160001004006 101022442428160001004006HUF000000800000000020231102 +1200100 000PIGT23306AG3 000000087376000HUF Erste Bank Hungary Zrt. Budapest HU41116990063791200000000008 E-USER-2023-238 USER RENDSZERHÁZ KFT 101022442428160001004006 101022442428160001004006HUF000000008737600020231102 MKKBHUHB KÁLLÓI ÚT 63.C.ÉP NYÍREGYHÁZA 11699006 + +13 +14 diff --git a/lis-service/src/main/java/hu/user/lis/service/mbh/StatementDataRecord.java b/lis-service/src/main/java/hu/user/lis/service/mbh/StatementDataRecord.java new file mode 100644 index 0000000..f4bd6bd --- /dev/null +++ b/lis-service/src/main/java/hu/user/lis/service/mbh/StatementDataRecord.java @@ -0,0 +1,61 @@ +package hu.user.lis.service.mbh; + +import lombok.Builder; +import lombok.ToString; + +import java.time.LocalDate; + +@ToString +@Builder +public class StatementDataRecord extends StatementRecord { + + private String transactionType; + + private String id; + + private float amount; + + private String currency; + + private String principalBankName; + + private String principalName; + + private String principalAccount; + + private String notice; + + private String assignBankName; + + private String assignName; + + private String assignAccount; + + private String receiptNr; + + private String creditAccount; + + private String creditCurrency; + + private float creditAmount; + + private LocalDate creditDate; + + private String chargingAccount; + + private String chargingCurrency; + + private float chargingAmount; + + private LocalDate chargingDate; + + private String principalCountry; + + private String assignCountry; + + private String code; + + private String transactionId; + + private String giroId; +} diff --git a/lis-service/src/main/java/hu/user/lis/service/mbh/StatementHeaderRecord.java b/lis-service/src/main/java/hu/user/lis/service/mbh/StatementHeaderRecord.java new file mode 100644 index 0000000..ea1cb45 --- /dev/null +++ b/lis-service/src/main/java/hu/user/lis/service/mbh/StatementHeaderRecord.java @@ -0,0 +1,26 @@ +package hu.user.lis.service.mbh; + +import lombok.Builder; +import lombok.ToString; + +import java.time.LocalDate; + +@ToString +@Builder +public class StatementHeaderRecord extends StatementRecord { + + private String id; + + private String account; + + private String currency; + + private String accountName; + + private LocalDate startDay; + + private LocalDate endDay; + + private String clientName; + +} diff --git a/lis-service/src/main/java/hu/user/lis/service/mbh/StatementItem.java b/lis-service/src/main/java/hu/user/lis/service/mbh/StatementItem.java new file mode 100644 index 0000000..5cdc615 --- /dev/null +++ b/lis-service/src/main/java/hu/user/lis/service/mbh/StatementItem.java @@ -0,0 +1,4 @@ +package hu.user.lis.service.mbh; + +public class StatementItem { +} diff --git a/lis-service/src/main/java/hu/user/lis/service/mbh/StatementParser.java b/lis-service/src/main/java/hu/user/lis/service/mbh/StatementParser.java new file mode 100644 index 0000000..dcdd189 --- /dev/null +++ b/lis-service/src/main/java/hu/user/lis/service/mbh/StatementParser.java @@ -0,0 +1,54 @@ +package hu.user.lis.service.mbh; + +import hu.user.lis.service.mbh.builder.StatementRecordBuilder; +import lombok.extern.log4j.Log4j2; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +@Log4j2 +@Service +public class StatementParser { + private static final String CHARSET_ISO_8859_2 = "ISO-8859-2"; + + @Autowired + private List> builders; + + public List parse(Path inputFile) throws IOException { + List lines = Files.readAllLines(inputFile, Charset.forName(CHARSET_ISO_8859_2)); + return lines.stream().map(this::processLine).collect(Collectors.toList()); + } + + Optional getRecordType(String line) { + return StatementRecordType.valueOfLabel(line.substring(0, 2)); + } + + private StatementRecord processLine(final String line) { + try { + String encodedLine = new String(line.getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8); + Optional recordType = getRecordType(encodedLine); + if (recordType.isPresent()) { + for (StatementRecordBuilder builder : builders) { + if (builder.canBuild(recordType.get())) { + StatementRecord result = builder.build(encodedLine); + log.info(result); + return result; + } + } + } + } catch (Exception e) { + log.error(e); + } + return null; + } + + +} diff --git a/lis-service/src/main/java/hu/user/lis/service/mbh/StatementRecord.java b/lis-service/src/main/java/hu/user/lis/service/mbh/StatementRecord.java new file mode 100644 index 0000000..824a1ee --- /dev/null +++ b/lis-service/src/main/java/hu/user/lis/service/mbh/StatementRecord.java @@ -0,0 +1,5 @@ +package hu.user.lis.service.mbh; + +public class StatementRecord { + +} diff --git a/lis-service/src/main/java/hu/user/lis/service/mbh/StatementRecordType.java b/lis-service/src/main/java/hu/user/lis/service/mbh/StatementRecordType.java new file mode 100644 index 0000000..2428432 --- /dev/null +++ b/lis-service/src/main/java/hu/user/lis/service/mbh/StatementRecordType.java @@ -0,0 +1,23 @@ +package hu.user.lis.service.mbh; + + +import lombok.Getter; + +import java.util.Arrays; +import java.util.Optional; + +@Getter +public enum StatementRecordType { + HEADER("11"), DATA("12"), FOOTER("13"), EOF("14"); + + private final String val; + + StatementRecordType(String val) { + this.val = val; + } + + public static Optional valueOfLabel(String label) { + return Arrays.stream(values()).filter(en -> label.equals(en.getVal())).findFirst(); + } + +} diff --git a/lis-service/src/main/java/hu/user/lis/service/mbh/StatementToken.java b/lis-service/src/main/java/hu/user/lis/service/mbh/StatementToken.java new file mode 100644 index 0000000..90d9fc1 --- /dev/null +++ b/lis-service/src/main/java/hu/user/lis/service/mbh/StatementToken.java @@ -0,0 +1,11 @@ +package hu.user.lis.service.mbh; + +import lombok.Builder; + +@Builder +public class StatementToken { + + private String data; + + +} diff --git a/lis-service/src/main/java/hu/user/lis/service/mbh/builder/StatementDataRecordBuilder.java b/lis-service/src/main/java/hu/user/lis/service/mbh/builder/StatementDataRecordBuilder.java new file mode 100644 index 0000000..5ebf7d6 --- /dev/null +++ b/lis-service/src/main/java/hu/user/lis/service/mbh/builder/StatementDataRecordBuilder.java @@ -0,0 +1,37 @@ +package hu.user.lis.service.mbh.builder; + +import hu.user.lis.service.mbh.StatementDataRecord; +import hu.user.lis.service.mbh.StatementRecordType; +import org.springframework.stereotype.Component; + +@Component +public class StatementDataRecordBuilder extends StatementRecordBuilder { + + @Override + public boolean canBuild(StatementRecordType type) { + return StatementRecordType.DATA.equals(type); + } + + @Override + public StatementDataRecord build(String data) { + return StatementDataRecord.builder() + .transactionType(getString(data, 3, 6)) + .id(getString(data, 9, 15)) + .amount(getFloat(data, 24, 16)) + .currency(getString(data, 40, 3)) + .principalBankName(getString(data, 43, 35)) + .principalName(getString(data, 183, 35)) + .principalAccount(getString(data, 323, 34)) + .notice(getString(data, 357, 35)) + .assignBankName(getString(data, 497, 35)) + .assignName(getString(data, 637, 35)) + .assignAccount(getString(data, 777, 34)) + .receiptNr(getString(data, 811, 6)) + .creditAccount(getString(data, 825, 24)) + .creditCurrency(getString(data, 849, 3)) + .creditAmount(getFloat(data, 852, 16)) + .build(); + } + + +} diff --git a/lis-service/src/main/java/hu/user/lis/service/mbh/builder/StatementHeaderRecordBuilder.java b/lis-service/src/main/java/hu/user/lis/service/mbh/builder/StatementHeaderRecordBuilder.java new file mode 100644 index 0000000..09e4723 --- /dev/null +++ b/lis-service/src/main/java/hu/user/lis/service/mbh/builder/StatementHeaderRecordBuilder.java @@ -0,0 +1,27 @@ +package hu.user.lis.service.mbh.builder; + +import hu.user.lis.service.mbh.StatementHeaderRecord; +import hu.user.lis.service.mbh.StatementRecordType; +import org.springframework.stereotype.Component; + +@Component +public class StatementHeaderRecordBuilder extends StatementRecordBuilder { + + @Override + public boolean canBuild(StatementRecordType type) { + return StatementRecordType.HEADER.equals(type); + } + + @Override + public StatementHeaderRecord build(String data) { + return StatementHeaderRecord.builder() + .id(getString(data, 3, 8)) + .account(getString(data, 11, 24)) + .currency(getString(data, 35, 3)) + .accountName(getString(data, 38, 20)) + .startDay(getLocalDate(data, 86, 8)) + .endDay(getLocalDate(data, 94, 8)) + .clientName(getString(data, 140, 50)) + .build(); + } +} diff --git a/lis-service/src/main/java/hu/user/lis/service/mbh/builder/StatementRecordBuilder.java b/lis-service/src/main/java/hu/user/lis/service/mbh/builder/StatementRecordBuilder.java new file mode 100644 index 0000000..50e7b6b --- /dev/null +++ b/lis-service/src/main/java/hu/user/lis/service/mbh/builder/StatementRecordBuilder.java @@ -0,0 +1,50 @@ +package hu.user.lis.service.mbh.builder; + +import hu.user.lis.service.mbh.StatementRecord; +import hu.user.lis.service.mbh.StatementRecordType; +import lombok.extern.log4j.Log4j2; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + +@Log4j2 +public abstract class StatementRecordBuilder { + + public abstract boolean canBuild(StatementRecordType type); + + public abstract T build(String data); + + String getString(String data, int pos, int length) { + return data.substring(pos - 1, pos - 1 + length).trim(); + } + + int getInt(String data, int pos, int length) { + int result = 0; + try { + result = Integer.parseInt(getString(data, pos, length)); + } catch (Exception e) { + log.debug(e); + } + return result; + } + + LocalDate getLocalDate(String data, int pos, int length) { + LocalDate result = null; + try { + result = LocalDate.parse(getString(data, pos, length), DateTimeFormatter.ofPattern("yyyyMMdd")); + } catch (Exception e) { + log.debug(e); + } + return result; + } + + float getFloat(String data, int pos, int length) { + float result = 0; + try { + result = Float.parseFloat(getString(data, pos, length)); + } catch (Exception e) { + log.debug(e); + } + return result; + } +} -- 2.54.0