diff --git a/clearing-parent/dbf-loader/pom.xml b/clearing-parent/dbf-loader/pom.xml
new file mode 100644
index 000000000..7f5b5e8f1
--- /dev/null
+++ b/clearing-parent/dbf-loader/pom.xml
@@ -0,0 +1,87 @@
+
+
+
+ 4.0.0
+
+ dbf-loader
+ dbf-loader
+ DBF loader for DBF files
+ SPCEX-1.0.0.0
+
+
+ clearing-parent
+ ru.spcex.clearing
+ SPCEX-1.0.0.0
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure
+
+
+
+
+ org.springframework
+ spring-jdbc
+
+
+ com.mchange
+ c3p0
+ 0.9.5.2
+
+
+ org.postgresql
+ postgresql
+
+
+
+
+ com.github.albfernandez
+ javadbf
+ 1.13.1
+
+
+
+
+
+ jar/${project.artifactId}
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+
+
+ ru.spcex.clearing.dbf.loader.DBFLoaderApplication
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ repackage
+
+
+
+
+ true
+ exec
+
+
+
+
+
+
\ No newline at end of file
diff --git a/clearing-parent/dbf-loader/src/main/java/ru/spcex/clearing/dbf/loader/DBFLoaderApplication.java b/clearing-parent/dbf-loader/src/main/java/ru/spcex/clearing/dbf/loader/DBFLoaderApplication.java
new file mode 100644
index 000000000..942869b03
--- /dev/null
+++ b/clearing-parent/dbf-loader/src/main/java/ru/spcex/clearing/dbf/loader/DBFLoaderApplication.java
@@ -0,0 +1,18 @@
+package ru.spcex.clearing.dbf.loader;
+
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+
+@SpringBootApplication
+public class DBFLoaderApplication {
+ public static void main(String[] args) {
+ try {
+ SpringApplicationBuilder builder = new SpringApplicationBuilder(DBFLoaderApplication.class);
+ builder.run(args);
+ } catch (Throwable e) {
+ LoggerFactory.getLogger(DBFLoaderApplication.class).error("DBF-Loader start failed: {} -> {}", e.getClass().getSimpleName(), e.getMessage());
+ System.exit(-1);
+ }
+ }
+}
diff --git a/clearing-parent/dbf-loader/src/main/java/ru/spcex/clearing/dbf/loader/config/DBFLoaderConfig.java b/clearing-parent/dbf-loader/src/main/java/ru/spcex/clearing/dbf/loader/config/DBFLoaderConfig.java
new file mode 100644
index 000000000..3a385d450
--- /dev/null
+++ b/clearing-parent/dbf-loader/src/main/java/ru/spcex/clearing/dbf/loader/config/DBFLoaderConfig.java
@@ -0,0 +1,56 @@
+package ru.spcex.clearing.dbf.loader.config;
+
+import com.mchange.v2.c3p0.ComboPooledDataSource;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.jdbc.core.JdbcTemplate;
+import ru.spcex.clearing.dbf.loader.logic.stages.ImportToDB;
+import ru.spcex.clearing.dbf.loader.logic.stages.Stage;
+import ru.spcex.clearing.dbf.loader.logic.stages.ValidateFields;
+import ru.spcex.clearing.dbf.loader.properties.AProperties;
+
+import javax.sql.DataSource;
+import java.beans.PropertyVetoException;
+import java.util.LinkedList;
+import java.util.List;
+
+@Configuration
+@ComponentScan(basePackages = {"ru.spcex.clearing.dbf.loader"})
+public class DBFLoaderConfig {
+ private final AProperties properties;
+ private final ApplicationContext context;
+
+ public DBFLoaderConfig(@Qualifier("dbfLoaderProperties") AProperties properties, ApplicationContext context) {
+ this.properties = properties;
+ this.context = context;
+ }
+
+ @Bean("dbfDataSource")
+ public DataSource dataSource() throws PropertyVetoException {
+ ComboPooledDataSource dataSource = new ComboPooledDataSource();
+ dataSource.setDriverClass(properties.getDbDriver());
+ dataSource.setJdbcUrl(properties.getJdbcUrl());
+ dataSource.setUser(properties.getDbLogin());
+ dataSource.setPassword(properties.getDbPassword());
+ return dataSource;
+ }
+
+ @Bean("dbfJdbcTemplate")
+ public JdbcTemplate jdbcTemplate(@Qualifier("dbfDataSource") DataSource dataSource) {
+ return new JdbcTemplate(dataSource);
+ }
+
+ @Bean("pipeline")
+ public List pipeline() {
+ List pipeline = new LinkedList<>();
+
+ pipeline.add(context.getBean(ValidateFields.class));
+ pipeline.add(context.getBean(ImportToDB.class));
+
+ return pipeline;
+ }
+
+}
diff --git a/clearing-parent/dbf-loader/src/main/java/ru/spcex/clearing/dbf/loader/exceptions/ConfigException.java b/clearing-parent/dbf-loader/src/main/java/ru/spcex/clearing/dbf/loader/exceptions/ConfigException.java
new file mode 100644
index 000000000..95aa73185
--- /dev/null
+++ b/clearing-parent/dbf-loader/src/main/java/ru/spcex/clearing/dbf/loader/exceptions/ConfigException.java
@@ -0,0 +1,6 @@
+package ru.spcex.clearing.dbf.loader.exceptions;
+
+public class ConfigException extends RuntimeException {
+ public ConfigException(String msg) { super(msg); }
+ public ConfigException(String msg, Throwable cause) { super(msg, cause); }
+}
diff --git a/clearing-parent/dbf-loader/src/main/java/ru/spcex/clearing/dbf/loader/logic/data/ColumnStructureDBF.java b/clearing-parent/dbf-loader/src/main/java/ru/spcex/clearing/dbf/loader/logic/data/ColumnStructureDBF.java
new file mode 100644
index 000000000..78e051c77
--- /dev/null
+++ b/clearing-parent/dbf-loader/src/main/java/ru/spcex/clearing/dbf/loader/logic/data/ColumnStructureDBF.java
@@ -0,0 +1,51 @@
+package ru.spcex.clearing.dbf.loader.logic.data;
+
+import ru.spcex.clearing.dbf.loader.logic.data.enums.ColumnType;
+
+public class ColumnStructureDBF {
+ private String name;
+ private ColumnType type;
+ private int length;
+ private int decimalDigits;
+ private String comment;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public ColumnType getType() {
+ return type;
+ }
+
+ public void setType(ColumnType type) {
+ this.type = type;
+ }
+
+ public int getLength() {
+ return length;
+ }
+
+ public void setLength(int length) {
+ this.length = length;
+ }
+
+ public int getDecimalDigits() {
+ return decimalDigits;
+ }
+
+ public void setDecimalDigits(int decimalDigits) {
+ this.decimalDigits = decimalDigits;
+ }
+
+ public String getComment() {
+ return comment;
+ }
+
+ public void setComment(String comment) {
+ this.comment = comment;
+ }
+}
diff --git a/clearing-parent/dbf-loader/src/main/java/ru/spcex/clearing/dbf/loader/logic/data/DBStructureDBF.java b/clearing-parent/dbf-loader/src/main/java/ru/spcex/clearing/dbf/loader/logic/data/DBStructureDBF.java
new file mode 100644
index 000000000..b384fb518
--- /dev/null
+++ b/clearing-parent/dbf-loader/src/main/java/ru/spcex/clearing/dbf/loader/logic/data/DBStructureDBF.java
@@ -0,0 +1,18 @@
+package ru.spcex.clearing.dbf.loader.logic.data;
+
+import ru.spcex.clearing.dbf.loader.logic.data.enums.Table;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class DBStructureDBF {
+ private Map tables = new HashMap<>();
+
+ public Map getTables() {
+ return tables;
+ }
+
+ public void setTables(Map tables) {
+ this.tables = tables;
+ }
+}
diff --git a/clearing-parent/dbf-loader/src/main/java/ru/spcex/clearing/dbf/loader/logic/data/ResultContainer.java b/clearing-parent/dbf-loader/src/main/java/ru/spcex/clearing/dbf/loader/logic/data/ResultContainer.java
new file mode 100644
index 000000000..f6c1264a2
--- /dev/null
+++ b/clearing-parent/dbf-loader/src/main/java/ru/spcex/clearing/dbf/loader/logic/data/ResultContainer.java
@@ -0,0 +1,36 @@
+package ru.spcex.clearing.dbf.loader.logic.data;
+
+import ru.spcex.clearing.dbf.loader.logic.data.enums.Table;
+
+/**
+ * Контейнер для передачи результата между стадиями
+ */
+public class ResultContainer {
+ private Table dbfTable;
+ private byte[] dbfSource;
+
+ protected ResultContainer() {}
+
+ public static ResultContainer createNewTask(Table dbfTable, byte[] dbfSource) {
+ ResultContainer container = new ResultContainer();
+ container.dbfTable = dbfTable;
+ container.dbfSource = dbfSource;
+ return container;
+ }
+
+ public Table getDbfTable() {
+ return dbfTable;
+ }
+
+ public void setDbfTable(Table dbfTable) {
+ this.dbfTable = dbfTable;
+ }
+
+ public byte[] getDbfSource() {
+ return dbfSource;
+ }
+
+ public void setDbfSource(byte[] dbfSource) {
+ this.dbfSource = dbfSource;
+ }
+}
diff --git a/clearing-parent/dbf-loader/src/main/java/ru/spcex/clearing/dbf/loader/logic/data/TableStructureDBF.java b/clearing-parent/dbf-loader/src/main/java/ru/spcex/clearing/dbf/loader/logic/data/TableStructureDBF.java
new file mode 100644
index 000000000..cb5c20728
--- /dev/null
+++ b/clearing-parent/dbf-loader/src/main/java/ru/spcex/clearing/dbf/loader/logic/data/TableStructureDBF.java
@@ -0,0 +1,25 @@
+package ru.spcex.clearing.dbf.loader.logic.data;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class TableStructureDBF {
+ private Map columns = new HashMap<>();
+
+ public Map getColumns() {
+ return columns;
+ }
+
+ public void setColumns(Map columns) {
+ this.columns = columns;
+ }
+
+ public ColumnStructureDBF getIgnoreCase(String columnName) {
+ for (Map.Entry entry : columns.entrySet()) {
+ String currColumnName = entry.getKey();
+ if (currColumnName.equalsIgnoreCase(columnName)) return entry.getValue();
+ }
+ return null;
+ }
+
+}
diff --git a/clearing-parent/dbf-loader/src/main/java/ru/spcex/clearing/dbf/loader/logic/data/enums/ColumnType.java b/clearing-parent/dbf-loader/src/main/java/ru/spcex/clearing/dbf/loader/logic/data/enums/ColumnType.java
new file mode 100644
index 000000000..2fab3a8cb
--- /dev/null
+++ b/clearing-parent/dbf-loader/src/main/java/ru/spcex/clearing/dbf/loader/logic/data/enums/ColumnType.java
@@ -0,0 +1,46 @@
+package ru.spcex.clearing.dbf.loader.logic.data.enums;
+
+import com.linuxense.javadbf.DBFDataType;
+
+import java.sql.Types;
+
+/**
+ * Типы данных в таблицах.
+ * Ставит в соответствие типы PostGRE и DBF
+ */
+public enum ColumnType {
+ VARCHAR(DBFDataType.CHARACTER, Types.VARCHAR),
+ CHARACTER(DBFDataType.CHARACTER, Types.CHAR),
+ NUMERIC(DBFDataType.NUMERIC, Types.NUMERIC),
+ DATE(DBFDataType.DATE, Types.DATE);
+
+ private final DBFDataType dbfType;
+ private final int sqlType;
+
+ ColumnType(DBFDataType dbfType, int postgreSqlType) {
+ this.dbfType = dbfType;
+ this.sqlType = postgreSqlType;
+ }
+
+ public DBFDataType getDbfType() {
+ return dbfType;
+ }
+
+ public int getSqlType() {
+ return sqlType;
+ }
+
+ public static ColumnType getForSQLType(int sqlType) {
+ for (ColumnType columnType : values()) {
+ if (columnType.sqlType == sqlType) return columnType;
+ }
+ return null;
+ }
+
+ public static ColumnType getForDBFType(DBFDataType dbfType) {
+ for (ColumnType columnType : values()) {
+ if (columnType.dbfType == dbfType) return columnType;
+ }
+ return null;
+ }
+}
diff --git a/clearing-parent/dbf-loader/src/main/java/ru/spcex/clearing/dbf/loader/logic/data/enums/StageResult.java b/clearing-parent/dbf-loader/src/main/java/ru/spcex/clearing/dbf/loader/logic/data/enums/StageResult.java
new file mode 100644
index 000000000..9558147d2
--- /dev/null
+++ b/clearing-parent/dbf-loader/src/main/java/ru/spcex/clearing/dbf/loader/logic/data/enums/StageResult.java
@@ -0,0 +1,7 @@
+package ru.spcex.clearing.dbf.loader.logic.data.enums;
+
+public enum StageResult {
+ OK,
+ ERROR,
+ COMPLETE
+}
diff --git a/clearing-parent/dbf-loader/src/main/java/ru/spcex/clearing/dbf/loader/logic/data/enums/Table.java b/clearing-parent/dbf-loader/src/main/java/ru/spcex/clearing/dbf/loader/logic/data/enums/Table.java
new file mode 100644
index 000000000..536e48625
--- /dev/null
+++ b/clearing-parent/dbf-loader/src/main/java/ru/spcex/clearing/dbf/loader/logic/data/enums/Table.java
@@ -0,0 +1,35 @@
+package ru.spcex.clearing.dbf.loader.logic.data.enums;
+
+public enum Table {
+ DF_01("DF-01"),
+ DF_02("DF-02"),
+ DF_03("DF-03"),
+ DF_04("DF-04"),
+ DF_05("DF-05"),
+ DF_06("DF-06");
+
+ private final String prefix;
+
+ Table(String prefix) {
+ this.prefix = prefix;
+ }
+
+ public boolean fileForThisTable(String filename) {
+ return filename != null && filename.startsWith(prefix);
+ }
+
+ public static Table getTableForFilename(String filename) {
+ for (Table table : Table.values()) {
+ if (table.fileForThisTable(filename)) return table;
+ }
+ return null;
+ }
+
+ public static Table tableForName(String name) {
+ for (Table table : values()) {
+ if (name.equalsIgnoreCase(table.name()))
+ return table;
+ }
+ return null;
+ }
+}
diff --git a/clearing-parent/dbf-loader/src/main/java/ru/spcex/clearing/dbf/loader/logic/stages/ImportToDB.java b/clearing-parent/dbf-loader/src/main/java/ru/spcex/clearing/dbf/loader/logic/stages/ImportToDB.java
new file mode 100644
index 000000000..82245314c
--- /dev/null
+++ b/clearing-parent/dbf-loader/src/main/java/ru/spcex/clearing/dbf/loader/logic/stages/ImportToDB.java
@@ -0,0 +1,107 @@
+package ru.spcex.clearing.dbf.loader.logic.stages;
+
+import com.linuxense.javadbf.DBFField;
+import com.linuxense.javadbf.DBFReader;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.jdbc.core.BatchPreparedStatementSetter;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.stereotype.Component;
+import ru.spcex.clearing.dbf.loader.logic.data.ResultContainer;
+import ru.spcex.clearing.dbf.loader.logic.data.enums.ColumnType;
+import ru.spcex.clearing.dbf.loader.logic.data.enums.StageResult;
+import ru.spcex.clearing.dbf.loader.logic.data.enums.Table;
+import ru.spcex.clearing.dbf.loader.properties.AProperties;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Заливка проверенных данных в базу
+ */
+@Component
+public class ImportToDB extends Stage {
+ private final AProperties properties;
+ private final JdbcTemplate dbfJdbcTemplate;
+
+ public ImportToDB(@Qualifier("dbfLoaderProperties") AProperties properties,
+ @Qualifier("dbfJdbcTemplate") JdbcTemplate dbfJdbcTemplate) {
+ this.properties = properties;
+ this.dbfJdbcTemplate = dbfJdbcTemplate;
+ }
+
+ @Override
+ public StageResult process(ResultContainer resultContainer) {
+ Objects.requireNonNull(resultContainer.getDbfTable());
+ Objects.requireNonNull(resultContainer.getDbfSource());
+
+ Table currTable = resultContainer.getDbfTable();
+ byte[] source = resultContainer.getDbfSource();
+ Charset sourceCharset = Charset.forName(properties.getDbfEncoding());
+
+ try (InputStream is = new ByteArrayInputStream(source);
+ DBFReader dbfReader = new DBFReader(is, sourceCharset)) {
+ int columnCount = dbfReader.getFieldCount();
+
+ List columnNames = new ArrayList<>(columnCount);
+ List columnTypes = new ArrayList<>(columnCount);
+ for (int columnIdx = 0; columnIdx < columnCount; columnIdx++) {
+ DBFField currField = dbfReader.getField(columnIdx);
+ String fieldName = currField.getName();
+ ColumnType columnType = ColumnType.getForDBFType(currField.getType());
+ columnNames.add(fieldName);
+ columnTypes.add(columnType.getSqlType());
+ }
+
+ int recordCount = dbfReader.getRecordCount();
+ List