From 61d48977201eaba1a0e7d99817926f920c0560d5 Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Wed, 18 Jul 2018 18:17:14 +0300 Subject: [PATCH] part 1 --- .../ru/dmitriymx/corrector1s/Corrector1S.java | 400 +++--------------- .../java/ru/dmitriymx/corrector1s/Main.java | 97 ----- .../java/ru/dmitriymx/corrector1s/Utils.java | 15 + .../corrector1s/config/ColumnData.java | 16 + .../corrector1s/config/XmlConfig.java | 75 ++++ .../corrector1s/excel/ExcelDocument.java | 150 +++++++ .../corrector1s/excel/ExcelRecord.java | 20 + .../corrector1s/excel/ExcelRecordData.java | 36 ++ .../corrector1s/excel/ExcelRecordFormula.java | 37 ++ .../ru/dmitriymx/corrector1s/gui/MainApp.java | 34 -- .../corrector1s/gui/MainController.java | 232 ---------- .../java/ru/dmitriymx/corrector1s/Test.java | 17 + src/test/resources/config.xml | 33 ++ 13 files changed, 464 insertions(+), 698 deletions(-) delete mode 100644 src/main/java/ru/dmitriymx/corrector1s/Main.java create mode 100644 src/main/java/ru/dmitriymx/corrector1s/Utils.java create mode 100644 src/main/java/ru/dmitriymx/corrector1s/config/ColumnData.java create mode 100644 src/main/java/ru/dmitriymx/corrector1s/config/XmlConfig.java create mode 100644 src/main/java/ru/dmitriymx/corrector1s/excel/ExcelDocument.java create mode 100644 src/main/java/ru/dmitriymx/corrector1s/excel/ExcelRecord.java create mode 100644 src/main/java/ru/dmitriymx/corrector1s/excel/ExcelRecordData.java create mode 100644 src/main/java/ru/dmitriymx/corrector1s/excel/ExcelRecordFormula.java delete mode 100644 src/main/java/ru/dmitriymx/corrector1s/gui/MainApp.java delete mode 100644 src/main/java/ru/dmitriymx/corrector1s/gui/MainController.java create mode 100644 src/test/java/ru/dmitriymx/corrector1s/Test.java create mode 100644 src/test/resources/config.xml diff --git a/src/main/java/ru/dmitriymx/corrector1s/Corrector1S.java b/src/main/java/ru/dmitriymx/corrector1s/Corrector1S.java index a4b29e6..2f09587 100644 --- a/src/main/java/ru/dmitriymx/corrector1s/Corrector1S.java +++ b/src/main/java/ru/dmitriymx/corrector1s/Corrector1S.java @@ -1,39 +1,28 @@ package ru.dmitriymx.corrector1s; -import lombok.NoArgsConstructor; import lombok.Setter; -import lombok.extern.slf4j.Slf4j; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; -import org.apache.poi.ss.usermodel.*; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.usermodel.WorkbookFactory; import org.apache.poi.ss.util.CellRangeAddress; -import org.apache.poi.ss.util.CellReference; +import org.xml.sax.SAXException; +import ru.dmitriymx.corrector1s.config.ColumnData; +import ru.dmitriymx.corrector1s.config.XmlConfig; +import ru.dmitriymx.corrector1s.excel.ExcelDocument; +import ru.dmitriymx.corrector1s.excel.ExcelRecord; +import ru.dmitriymx.corrector1s.excel.ExcelRecordData; +import javax.xml.parsers.ParserConfigurationException; import java.io.File; import java.io.FileNotFoundException; -import java.io.FileOutputStream; import java.io.IOException; -import java.util.ArrayList; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; -@Slf4j -@NoArgsConstructor public class Corrector1S { - private class ExcelRecord { - private String name; - private Integer addition; - private List dataList; - - ExcelRecord(int capacity) { - dataList = new ArrayList<>(capacity); - } - - boolean isEmptyDataList() { - return dataList.stream().mapToDouble(Double::doubleValue).sum() == 0.0d; - } - } - @Setter private File sourceFile; @Setter @@ -44,16 +33,11 @@ public class Corrector1S { private double fss; private Map mapRecords; - private Workbook workbook; - private Sheet sheet; - /** номер колонки "Итого начислено" */ - private int cellNumSum = 0; - /** номер колонки с больничными */ - private int cellBol = 0; - /** последняя колонка в исходном документе */ - private int lastColumn = 0; - private CellStyle cellStyle1, cellStyle2; - private int maxLines = 0; + private ExcelDocument document; + + private String buildKeyRecord(String title, Object addon) { + return title.trim() + (Utils.isEmptyObject(addon) ? "" : "_" + addon.toString()); + } private String getFileExtension(File file) { int i = file.getAbsolutePath().lastIndexOf('.'); @@ -64,11 +48,13 @@ public class Corrector1S { } } - private String buildSumFormula(int column, int startLine, int endLine) { - String columnChar = CellReference.convertNumToColString(column); - return String.format("SUM(%s%d:%s%d)", columnChar, startLine+1, columnChar, endLine+1); - } - + /** + * Проверяем входные параметры и формат файла + * @throws Exception + * {@link AssertionError} - если ошибка во входных параметрах; + * {@link FileNotFoundException} - если входной вайл не найден; + * {@link FormatFileException} - если данные в Excel не соответствуют ожиданиям. + */ public void check() throws Exception { if (honorarium == 0.0d) throw new AssertionError("Honorarium not be 0.0%!"); if (fss == 0.0d) throw new AssertionError("FSS not be 0.0%!"); @@ -76,333 +62,77 @@ public class Corrector1S { if (sourceFile == null) throw new AssertionError("Source file not be null!"); else if (!sourceFile.exists()) throw new FileNotFoundException(sourceFile.getAbsolutePath()); else if (!getFileExtension(sourceFile).equalsIgnoreCase("xls") - && !getFileExtension(sourceFile).equalsIgnoreCase("xlsx")) throw new AssertionError("Source file must be XLS/XLSX format"); + && !getFileExtension(sourceFile).equalsIgnoreCase("xlsx")) throw new AssertionError("Source file must be XLS/XLSX format"); if (targetFile == null) throw new AssertionError("Target file not be null!"); - Workbook workbook = WorkbookFactory.create(sourceFile); - Sheet sheet = workbook.getSheetAt(0); - try { + try (Workbook workbook = WorkbookFactory.create(sourceFile)) { + Sheet sheet = workbook.getSheetAt(0); + sheet.getRow(6).getCell(8); sheet.getRow(7).getCell(8); sheet.getRow(8).getCell(8); } catch (Exception e) { - workbook.close(); throw new FormatFileException(e); } - - // get max lines - for (int line = 8; true; line++) { - if (sheet.getRow(line) == null) { - this.maxLines = line - 2; - break; - } - } - - workbook.close(); } + /** + * Сохраняем данные по всем колонкам + */ public void createSnapshotData() throws IOException, InvalidFormatException { - workbook = WorkbookFactory.create(sourceFile); - sheet = workbook.getSheetAt(0); - mapRecords = new HashMap<>(20); + this.document = new ExcelDocument(this.sourceFile); + this.mapRecords = new HashMap<>(this.document.getMaxColumn() - ExcelDocument.START_COLUMN); - Cell cell; - loop: - for (int column = 8; true; column++) { - ExcelRecord record = new ExcelRecord(this.maxLines); + for (int column = ExcelDocument.START_COLUMN; column <= this.document.getMaxColumn(); column++) { + ExcelRecordData record = this.document.getColumnRecord(column); + String key = buildKeyRecord(record.getTitle(), record.getAddon()); - for (int line = 6; line <= this.maxLines; line++) { - cell = sheet.getRow(line).getCell(column); - if (cell == null) break loop; - - if (line == 6) { - record.name = cell.getStringCellValue(); - } else if (line == 7) { - if (cell.getCellTypeEnum().equals(CellType.NUMERIC)) { - record.addition = (int) cell.getNumericCellValue(); - } else { - record.addition = 0; - } - } else { - record.dataList.add(cell.getNumericCellValue()); - } - } - - String key = (record.addition == 0 ? record.name.trim() : record.name.trim() + "_" + record.addition); - mapRecords.put(key, record); + this.mapRecords.put(key, record); } } + /** + * Убираем объединение ячеек + */ public void removeMergedCells() { - while (sheet.getNumMergedRegions() > 5) { - for(int m = 0; m < sheet.getNumMergedRegions(); m++) { - CellRangeAddress cellRangeAddress = sheet.getMergedRegion(m); + /* + Опытным путём было выявлено, что должно остаться 5 объединённых регионов, если у всех остальных колонок + убрать объединение. + */ + while (this.document.getNumMergedCells() > 5) { + for(int m = 0; m < this.document.getNumMergedCells(); m++) { + CellRangeAddress cellRangeAddress = this.document.getMergedCells(m); if (cellRangeAddress.getFirstColumn() >= 8) { - sheet.removeMergedRegion(m); + this.document.removeMergedCells(m); break; } } } } - private void setCellRecord(ExcelRecord record, int column) { - // NAME - Cell cell = sheet.getRow(6).getCell(column, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK); - cell.setCellType(CellType.STRING); - cell.setCellValue(record.name); - - // ADDITION - cell = sheet.getRow(7).getCell(column, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK); - if (record.addition == 0) { - cell.setCellType(CellType.BLANK); - } else { - cell.setCellType(CellType.NUMERIC); - cell.setCellValue(record.addition); - } - - // DATA - for (int line = 8; line <= this.maxLines; line++) { - cell = sheet.getRow(line).getCell(column, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK); - double data = record.dataList.get(line-8); - if (data == 0) { - cell.setCellType(CellType.BLANK); - } else { - cell.setCellType(CellType.NUMERIC); - cell.setCellValue(data); - } - } - - // SUM - cell = sheet.getRow(this.maxLines+1).getCell(column, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK); - if (record.isEmptyDataList()) { - cell.setCellType(CellType.BLANK); - } else { - cell.setCellType(CellType.FORMULA); - cell.setCellFormula(buildSumFormula(column, 8, this.maxLines)); - } - } - + /** + * Производим сортировку колонок (по сути переписывая текущие значения) + */ public void replaceData() { - int column = 8; - ExcelRecord record; - if ((record = mapRecords.remove("Отпуск очередной_104")) != null) setCellRecord(record, column++); - if ((record = mapRecords.remove("Больничный за счет работодателя_105")) != null) { - setCellRecord(record, column++); - this.cellBol = column-1; + XmlConfig config = null; + try { + config = new XmlConfig(new File("config.xml")); + } catch (ParserConfigurationException | IOException | SAXException e) { + e.printStackTrace(); //FIXME логирование ошибки } - if ((record = mapRecords.remove("Дневные")) != null) setCellRecord(record, column++); - if ((record = mapRecords.remove("Ночные")) != null) setCellRecord(record, column++); - if ((record = mapRecords.remove("Ночные_123")) != null) setCellRecord(record, column++); - if ((record = mapRecords.remove("питание_124")) != null) setCellRecord(record, column++); - if ((record = mapRecords.remove("Выходные")) != null) setCellRecord(record, column++); - if ((record = mapRecords.remove("выходные_135")) != null) setCellRecord(record, column++); - if ((record = mapRecords.remove("пересчет за прошлый месяц_141")) != null) setCellRecord(record, column++); - if ((record = mapRecords.remove("вредность_145")) != null) setCellRecord(record, column++); - if ((record = mapRecords.remove("Материальная помощь при рождении ребенка_148")) != null) setCellRecord(record, column++); - if ((record = mapRecords.remove("компенсация за задержку зарплаты_169")) != null) setCellRecord(record, column++); - if ((record = mapRecords.remove("Оплата по среднему (донорство, посещение врача)_174")) != null) setCellRecord(record, column++); - if ((record = mapRecords.remove("Компенсация проезда при использовании личного транспорта_176")) != null) setCellRecord(record, column++); - if ((record = mapRecords.remove("сверхурочные_133")) != null) setCellRecord(record, column++); - if ((record = mapRecords.remove("Сверхурочные 1.5 ставки")) != null) setCellRecord(record, column++); - if ((record = mapRecords.remove("сверхурочные 1,5_180")) != null) setCellRecord(record, column++); - if ((record = mapRecords.remove("Сверхурочные 2 ставки")) != null) setCellRecord(record, column++); - if ((record = mapRecords.remove("сверхурочные 2_181")) != null) setCellRecord(record, column++); - if ((record = mapRecords.remove("Компенсация за неиспользованный отпуск (с декабря 2017)_184")) != null) setCellRecord(record, column++); - if ((record = mapRecords.remove("Месячная премия 2018_186")) != null) setCellRecord(record, column++); - if (mapRecords.size() > 1) { - for (ExcelRecord rec : mapRecords.values()) { - if (!rec.name.equalsIgnoreCase("Итого начислено")) { - setCellRecord(rec, column++); - } + List columnDataList = config.getColumnDataList(); + Iterator itrColumnDataList = columnDataList.iterator(); + for (int column = ExcelDocument.START_COLUMN; column <= document.getMaxColumn(); column++) { + if (itrColumnDataList.hasNext()) { + ColumnData columnData = itrColumnDataList.next(); + ExcelRecord record = mapRecords.remove(buildKeyRecord(columnData.getTitle(), columnData.getAddon())); + if (record == null) continue; //FIXME логирование отсутствия нужной колонки + + this.document.setColumnRecord(column, record); + //TODO не забываем, нужно еще составить мапу id-column для дальнейшей генерации формул } } - - if ((record = mapRecords.remove("Итого начислено")) != null) setCellRecord(record, column++); - cellNumSum = column-1; - - this.lastColumn = column; - } - - private void initStyle() { - Font font = workbook.createFont(); - font.setFontName("Arial"); - font.setBold(true); - font.setFontHeightInPoints((short) 8); - - cellStyle1 = workbook.createCellStyle(); - cellStyle1.setAlignment(HorizontalAlignment.CENTER); - cellStyle1.setVerticalAlignment(VerticalAlignment.CENTER); - cellStyle1.setFont(font); - cellStyle1.setWrapText(true); - cellStyle1.setBorderTop(BorderStyle.THIN); - cellStyle1.setBorderBottom(BorderStyle.THIN); - cellStyle1.setBorderLeft(BorderStyle.THIN); - cellStyle1.setBorderRight(BorderStyle.THIN); - - cellStyle2 = workbook.createCellStyle(); - cellStyle2.setBorderTop(BorderStyle.THIN); - cellStyle2.setBorderBottom(BorderStyle.THIN); - cellStyle2.setBorderLeft(BorderStyle.THIN); - cellStyle2.setBorderRight(BorderStyle.THIN); - } - - private void insertCell(String title, String addition, String formulaTemplate, String formulaSum, int column) { - // NAME - Cell cell = sheet.getRow(6).createCell(column); - cell.setCellType(CellType.STRING); - cell.setCellValue(title); - cell.setCellStyle(cellStyle1); - - // ADDITION - cell = sheet.getRow(7).createCell(column); - cell.setCellType(CellType.STRING); - cell.setCellValue(addition); - cell.setCellStyle(cellStyle1); - - // DATA - for (int line = 8; line <= this.maxLines; line++) { - cell = sheet.getRow(line).createCell(column); - cell.setCellStyle(cellStyle2); - cell.setCellType(CellType.FORMULA); - cell.setCellFormula(formulaTemplate.replace("{L}", String.valueOf(line+1)).replace(',','.')); - } - - // SUM - cell = sheet.getRow(this.maxLines+1).createCell(column); - cell.setCellStyle(cellStyle2); - cell.setCellType(CellType.FORMULA); - cell.setCellFormula(formulaSum); - } - - public void insertNewCols() { - initStyle(); - int column = lastColumn; - - // FSS - String formulaTemplate; - if (cellBol == 0) { - formulaTemplate = String.format( - "(%s{L})*%.2f%%", - CellReference.convertNumToColString(cellNumSum), - this.fss - ); - } else { - formulaTemplate = String.format( - "(%s{L}-%s{L})*%.2f%%", - CellReference.convertNumToColString(cellNumSum), - CellReference.convertNumToColString(cellBol), - this.fss - ); - } - insertCell( - String.format("Страховые взносы + ФСС НС (%.2f%%)", this.fss), - "Начислено", - formulaTemplate, - buildSumFormula(column, 8, this.maxLines), - column++ - ); - - // BASE - insertCell( - "БАЗА", - "для начисления стоимости услуг", - String.format("%s{L}+%s{L}", - CellReference.convertNumToColString(cellNumSum), - CellReference.convertNumToColString(column-1)), - buildSumFormula(column, 8, this.maxLines), - column++ - ); - - // HONORARIUM - insertCell( - "Гонорар", - String.format("%.2f%%", this.honorarium), - String.format("%s{L}*%.2f%%", - CellReference.convertNumToColString(column-1), - this.honorarium), - buildSumFormula(column, 8, this.maxLines), - column++ - ); - - // SUM ALL - insertCell( - "Итого, для счета", - "Результат", - String.format("%s{L}+%s{L}", - CellReference.convertNumToColString(column-2), - CellReference.convertNumToColString(column-1)), - buildSumFormula(column, 8, this.maxLines), - column - ); - } - - public void insertNewLines() { - int line = maxLines+3; - - int t1 = line; - Cell cell = sheet.createRow(line).getCell(2, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK); - cell.setCellType(CellType.STRING); - cell.setCellValue("Итого по справке:"); - cell = sheet.getRow(line++).getCell(6, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK); - cell.setCellType(CellType.FORMULA); - cell.setCellFormula(String.format("%s%d", CellReference.convertNumToColString(this.lastColumn+3), this.maxLines+2)); - - cell = sheet.createRow(line++).getCell(2, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK); - cell.setCellType(CellType.STRING); - cell.setCellValue("Дополнительные страховые взносы за сварщиков"); - - cell = sheet.createRow(line++).getCell(2, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK); - cell.setCellType(CellType.STRING); - cell.setCellValue("Корпоративный транспорт"); - - cell = sheet.createRow(line++).getCell(2, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK); - cell.setCellType(CellType.STRING); - cell.setCellValue("Подбор персонала сторонним агентством"); - - cell = sheet.createRow(line++).getCell(2, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK); - cell.setCellType(CellType.STRING); - cell.setCellValue("Страхование жизни"); - - cell = sheet.createRow(line++).getCell(2, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK); - cell.setCellType(CellType.STRING); - cell.setCellValue("Страхование НС"); - - int t2 = line; - cell = sheet.createRow(line).getCell(2, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK); - cell.setCellType(CellType.STRING); - cell.setCellValue("Итого без НДС"); - cell = sheet.getRow(line++).getCell(6, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK); - cell.setCellType(CellType.FORMULA); - cell.setCellFormula(String.format("%s%d", CellReference.convertNumToColString(6), t1+1)); - - int t3 = line; - cell = sheet.createRow(line).getCell(2, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK); - cell.setCellType(CellType.STRING); - cell.setCellValue("Сумма НДС:"); - cell = sheet.getRow(line++).getCell(6, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK); - cell.setCellType(CellType.FORMULA); - cell.setCellFormula(String.format("%s%d*18%%", CellReference.convertNumToColString(6), t2+1)); - - cell = sheet.createRow(line).getCell(2, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK); - cell.setCellType(CellType.STRING); - cell.setCellValue("Всего (с учетом НДС):"); - cell = sheet.getRow(line).getCell(6, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK); - cell.setCellType(CellType.FORMULA); - cell.setCellFormula(String.format("%s%d+%s%d", - CellReference.convertNumToColString(6), - t2+1, - CellReference.convertNumToColString(6), - t3+1)); - } - - public void saveFile() throws IOException { - workbook.getCreationHelper().createFormulaEvaluator().evaluateAll(); - - try (FileOutputStream fos = new FileOutputStream(targetFile)) { - workbook.write(fos); - workbook.close(); - } } } diff --git a/src/main/java/ru/dmitriymx/corrector1s/Main.java b/src/main/java/ru/dmitriymx/corrector1s/Main.java deleted file mode 100644 index 2200156..0000000 --- a/src/main/java/ru/dmitriymx/corrector1s/Main.java +++ /dev/null @@ -1,97 +0,0 @@ -package ru.dmitriymx.corrector1s; - -import joptsimple.OptionParser; -import joptsimple.OptionSet; -import lombok.extern.slf4j.Slf4j; -import ru.dmitriymx.corrector1s.gui.MainApp; - -import java.io.File; -import java.nio.file.Files; -import java.util.Arrays; -import java.util.List; - -@Slf4j -public class Main { - public static final String VERSION = "1.0.5"; - - private static OptionParser buildOptionParser() { - final OptionParser optionParser = new OptionParser(); - - optionParser.acceptsAll(Arrays.asList("s", "source"), "Source Excel file") - .withRequiredArg() - .ofType(File.class) - .required(); - optionParser.acceptsAll(Arrays.asList("t", "target"), "Target Excel file (default: target = source)") - .withRequiredArg() - .ofType(File.class); - optionParser.acceptsAll(Arrays.asList("h", "honorarium"), "Honorarium (in percentages)") - .withRequiredArg() - .ofType(Double.class) - .defaultsTo(9.0d); - optionParser.acceptsAll(Arrays.asList("f", "fss"), "FSS (in percentages)") - .withRequiredArg() - .ofType(Double.class) - .defaultsTo(31.0d); - optionParser.acceptsAll(Arrays.asList("h", "help"), "Help page. Display this message") - .forHelp(); - optionParser.acceptsAll(Arrays.asList("v", "version"), "Version") - .forHelp(); - - return optionParser; - } - - public static void main(String[] args) throws Exception { - if (args.length == 0) { - MainApp.main(args); - } else { - OptionParser parser = buildOptionParser(); - OptionSet optionSet; - try { - optionSet = parser.parse(args); - } catch (joptsimple.OptionException e) { - if (e.getMessage().contains("Missing")) { - log.error(e.getMessage()); - } else { - log.error("", e); - } - return; - } - - if (optionSet.has("help")) { - System.out.printf("Version: %s\n", VERSION); - parser.printHelpOn(System.out); - return; - } else if (optionSet.has("version")) { - System.out.printf("Version: %s\n", VERSION); - return; - } - - File sourceFile, targetFile; - sourceFile = (File) optionSet.valueOf("source"); - if (optionSet.has("target")) { - targetFile = (File) optionSet.valueOf("target"); - } else { - targetFile = sourceFile; - } - - log.debug("Source file: {} (exists: {})", sourceFile.getAbsolutePath(), Files.exists(sourceFile.toPath())); - log.debug("Target file: {}", targetFile.getAbsolutePath()); - log.debug("Honorarium: {}%", optionSet.valueOf("honorarium")); - log.debug("FSS: {}%", optionSet.valueOf("fss")); - - Corrector1S corrector1S = new Corrector1S(); - corrector1S.setSourceFile(sourceFile); - corrector1S.setTargetFile(targetFile); - corrector1S.setHonorarium((Double) optionSet.valueOf("honorarium")); - corrector1S.setFss((Double) optionSet.valueOf("fss")); - - corrector1S.check(); - corrector1S.createSnapshotData(); - corrector1S.removeMergedCells(); - corrector1S.replaceData(); - corrector1S.insertNewCols(); - corrector1S.insertNewLines(); - corrector1S.saveFile(); - } - } -} diff --git a/src/main/java/ru/dmitriymx/corrector1s/Utils.java b/src/main/java/ru/dmitriymx/corrector1s/Utils.java new file mode 100644 index 0000000..bfeeb38 --- /dev/null +++ b/src/main/java/ru/dmitriymx/corrector1s/Utils.java @@ -0,0 +1,15 @@ +/* + * DmitriyMX + * 2018-07-18 + */ +package ru.dmitriymx.corrector1s; + +public class Utils { + public static boolean isEmptyObject(Object obj) { + if (obj instanceof Number) { + return (Integer) obj == 0; + } else { + return obj == null || obj.toString().trim().isEmpty(); + } + } +} diff --git a/src/main/java/ru/dmitriymx/corrector1s/config/ColumnData.java b/src/main/java/ru/dmitriymx/corrector1s/config/ColumnData.java new file mode 100644 index 0000000..3dc56da --- /dev/null +++ b/src/main/java/ru/dmitriymx/corrector1s/config/ColumnData.java @@ -0,0 +1,16 @@ +/* + * DmitriyMX + * 2018-07-18 + */ +package ru.dmitriymx.corrector1s.config; + +import lombok.Data; +import lombok.ToString; + +@Data +@ToString +public class ColumnData { + private String title; + private String addon; + private String id; +} diff --git a/src/main/java/ru/dmitriymx/corrector1s/config/XmlConfig.java b/src/main/java/ru/dmitriymx/corrector1s/config/XmlConfig.java new file mode 100644 index 0000000..eeda815 --- /dev/null +++ b/src/main/java/ru/dmitriymx/corrector1s/config/XmlConfig.java @@ -0,0 +1,75 @@ +/* + * DmitriyMX + * 2018-07-18 + */ +package ru.dmitriymx.corrector1s.config; + +import jdk.nashorn.internal.runtime.regexp.joni.constants.NodeType; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +public class XmlConfig { + private Document document; + private Node rootNode; + + private DocumentBuilder init() throws ParserConfigurationException { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setValidating(false); + return dbf.newDocumentBuilder(); + } + + public XmlConfig(File file) throws ParserConfigurationException, IOException, SAXException { + DocumentBuilder db = init(); + this.document = db.parse(file); + this.rootNode = this.document.getChildNodes().item(0); + } + + public XmlConfig(InputStream inputStream) throws ParserConfigurationException, IOException, SAXException { + DocumentBuilder db = init(); + this.document = db.parse(inputStream); + this.rootNode = this.document.getChildNodes().item(0); + } + + public List getColumnDataList() { + NodeList childNodes = this.rootNode.getChildNodes().item(/*columns*/1).getChildNodes(); + List result = new ArrayList<>((childNodes.getLength()-1)/2); + + for(int i = 0; i < childNodes.getLength(); i++) { + Node node = childNodes.item(i); + if (node.getNodeType() == NodeType.CCLASS) { + ColumnData columnData = new ColumnData(); + + Node attrTitle = node.getAttributes().getNamedItem("title"); + if (attrTitle != null) { + columnData.setTitle(attrTitle.getNodeValue()); + } + Node attrAddon = node.getAttributes().getNamedItem("addon"); + if (attrAddon != null) { + columnData.setAddon(attrAddon.getNodeValue()); + } else { + columnData.setAddon(""); + } + Node attrId = node.getAttributes().getNamedItem("id"); + if (attrId != null) { + columnData.setId(attrId.getNodeValue()); + } + + result.add(columnData); + } + } + + return result; + } + +} diff --git a/src/main/java/ru/dmitriymx/corrector1s/excel/ExcelDocument.java b/src/main/java/ru/dmitriymx/corrector1s/excel/ExcelDocument.java new file mode 100644 index 0000000..2dbf291 --- /dev/null +++ b/src/main/java/ru/dmitriymx/corrector1s/excel/ExcelDocument.java @@ -0,0 +1,150 @@ +/* + * DmitriyMX + * 2018-07-18 + */ +package ru.dmitriymx.corrector1s.excel; + +import lombok.Getter; +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.ss.util.CellReference; +import ru.dmitriymx.corrector1s.Utils; + +import java.io.*; + +public class ExcelDocument implements Closeable { + public static final int LINE_TITLE = 6, + LINE_ADDON = 7, + LINE_DATA = 8, + START_COLUMN = 7; + + private Workbook workbook; + private Sheet sheet; + @Getter + private int maxLines; + /** последняя колонка в исходном документе */ + @Getter + private int maxColumn; + + public ExcelDocument(File excelFile) throws IOException, InvalidFormatException { + this.workbook = WorkbookFactory.create(excelFile); + this.sheet = this.workbook.getSheetAt(0); + + // get max lines + for (int line = LINE_DATA; true; line++) { + if (this.sheet.getRow(line) == null) { + this.maxLines = line - 2; + break; + } + } + + // get max column + for (int column = START_COLUMN; true; column++) { + if (this.sheet.getRow(LINE_ADDON).getCell(column) == null) { + this.maxColumn = column - 1; + break; + } + } + } + + private String buildSumFormula(int column, int startLine, int endLine) { + String columnChar = CellReference.convertNumToColString(column); + return String.format("SUM(%s%d:%s%d)", columnChar, startLine + 1, columnChar, endLine + 1); + } + + public ExcelRecordData getColumnRecord(int column) { + ExcelRecordData record = new ExcelRecordData(this.maxLines); + + Cell cell; + for (int line = LINE_TITLE; line <= this.maxLines; line++) { + cell = this.sheet.getRow(line).getCell(column); + if (cell == null) break; + + if (line == LINE_TITLE) { + record.setTitle(cell.getStringCellValue()); + } else if (line == LINE_ADDON) { + if (cell.getCellTypeEnum().equals(CellType.NUMERIC)) { + record.setAddon(new Double(cell.getNumericCellValue()).intValue()); + } else { + record.setAddon(0); + } + } else { + record.getDataList().add(cell.getNumericCellValue()); + } + } + + return record; + } + + public void setColumnRecord(int column, ExcelRecord record) { + // TITLE + Cell cell = this.sheet.getRow(LINE_TITLE).getCell(column, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK); + cell.setCellType(CellType.STRING); + cell.setCellValue(record.getTitle()); + + // ADDON + cell = this.sheet.getRow(LINE_ADDON).getCell(column, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK); + if (record.isEmptyAddon()) { + cell.setCellType(CellType.BLANK); + } + // TODO мне очень не нравится такой подход. Нужно что-то более универсальное + else if (record instanceof ExcelRecordData){ + cell.setCellType(CellType.NUMERIC); + cell.setCellValue((Integer)record.getAddon()); + } else { + cell.setCellType(CellType.STRING); + cell.setCellValue((String)record.getAddon()); + } + + // DATA + for (int line = LINE_DATA; line <= this.maxLines; line++) { + cell = this.sheet.getRow(line).getCell(column, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK); + Object data = record.getDataList().get(line - LINE_DATA); + if (Utils.isEmptyObject(data)) { + cell.setCellType(CellType.BLANK); + } + // TODO мне очень не нравится такой подход. Нужно что-то более универсальное + else if (record instanceof ExcelRecordData) { + cell.setCellType(CellType.NUMERIC); + cell.setCellValue((Double) data); + } else { + cell.setCellType(CellType.FORMULA); + cell.setCellValue((String) data); + } + } + + // SUM + cell = this.sheet.getRow(this.maxLines + 1).getCell(column, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK); + if (record.isEmptyData()) { + cell.setCellType(CellType.BLANK); + } else { + cell.setCellType(CellType.FORMULA); + cell.setCellFormula(buildSumFormula(column, LINE_DATA, this.maxLines)); + } + } + + public int getNumMergedCells() { + return this.sheet.getNumMergedRegions(); + } + + public CellRangeAddress getMergedCells(int id) { + return this.sheet.getMergedRegion(id); + } + + public void removeMergedCells(int id) { + + } + + public void saveToFile(File file) throws IOException { + this.workbook.getCreationHelper().createFormulaEvaluator().evaluateAll(); + try (FileOutputStream fos = new FileOutputStream(file)) { + this.workbook.write(fos); + } + } + + @Override + public void close() throws IOException { + this.workbook.close(); + } +} diff --git a/src/main/java/ru/dmitriymx/corrector1s/excel/ExcelRecord.java b/src/main/java/ru/dmitriymx/corrector1s/excel/ExcelRecord.java new file mode 100644 index 0000000..b55a85c --- /dev/null +++ b/src/main/java/ru/dmitriymx/corrector1s/excel/ExcelRecord.java @@ -0,0 +1,20 @@ +/* + * DmitriyMX + * 2018-07-18 + */ +package ru.dmitriymx.corrector1s.excel; + +import java.util.List; + +public interface ExcelRecord { + String getTitle(); + void setTitle(String value); + + T1 getAddon(); + void setAddon(T1 value); + + List getDataList(); + + boolean isEmptyAddon(); + boolean isEmptyData(); +} diff --git a/src/main/java/ru/dmitriymx/corrector1s/excel/ExcelRecordData.java b/src/main/java/ru/dmitriymx/corrector1s/excel/ExcelRecordData.java new file mode 100644 index 0000000..f28012c --- /dev/null +++ b/src/main/java/ru/dmitriymx/corrector1s/excel/ExcelRecordData.java @@ -0,0 +1,36 @@ +/* + * DmitriyMX + * 2018-07-18 + */ +package ru.dmitriymx.corrector1s.excel; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.List; + +@Getter +@EqualsAndHashCode +public class ExcelRecordData implements ExcelRecord { + @Setter + private String title; + @Setter + private Integer addon; + private List dataList; + + ExcelRecordData(int capacity) { + this.dataList = new ArrayList<>(capacity); + } + + @Override + public boolean isEmptyAddon() { + return this.addon == 0; + } + + @Override + public boolean isEmptyData() { + return this.dataList.stream().mapToDouble(Double::doubleValue).sum() == 0.0d; + } +} diff --git a/src/main/java/ru/dmitriymx/corrector1s/excel/ExcelRecordFormula.java b/src/main/java/ru/dmitriymx/corrector1s/excel/ExcelRecordFormula.java new file mode 100644 index 0000000..a391bd0 --- /dev/null +++ b/src/main/java/ru/dmitriymx/corrector1s/excel/ExcelRecordFormula.java @@ -0,0 +1,37 @@ +/* + * DmitriyMX + * 2018-07-18 + */ +package ru.dmitriymx.corrector1s.excel; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Getter +@EqualsAndHashCode +public class ExcelRecordFormula implements ExcelRecord { + @Setter + private String title; + @Setter + private String addon; + private List dataList; + + @Override + public boolean isEmptyAddon() { + return this.addon == null || this.addon.trim().isEmpty(); + } + + @Override + public boolean isEmptyData() { + int i = 0; + + for (String data : this.dataList) { + i += (data.trim().isEmpty() ? 1 : 0); + } + + return i > 0; + } +} diff --git a/src/main/java/ru/dmitriymx/corrector1s/gui/MainApp.java b/src/main/java/ru/dmitriymx/corrector1s/gui/MainApp.java deleted file mode 100644 index 9b7bca8..0000000 --- a/src/main/java/ru/dmitriymx/corrector1s/gui/MainApp.java +++ /dev/null @@ -1,34 +0,0 @@ -package ru.dmitriymx.corrector1s.gui; - -import javafx.application.Application; -import javafx.fxml.FXMLLoader; -import javafx.scene.Scene; -import javafx.scene.image.Image; -import javafx.stage.Stage; -import ru.dmitriymx.corrector1s.Main; - -import java.io.IOException; - -public class MainApp extends Application { - - public static void main(String[] args) { - launch(args); - } - - @Override - public void start(Stage primaryStage) throws Exception { - primaryStage.setTitle(String.format("1С корректор (v%s)", Main.VERSION)); - primaryStage.setResizable(false); - primaryStage.getIcons().add(new Image(getClass().getResourceAsStream("excel.png"))); - primaryStage.setScene(loadScene(primaryStage)); - primaryStage.show(); - } - - private Scene loadScene(Stage stage) throws IOException { - FXMLLoader loader = new FXMLLoader(getClass().getResource("single_layout.fxml")); - Scene scene = new Scene(loader.load(), 350-10, 203-10); - MainController controller = loader.getController(); - controller.setStage(stage); - return scene; - } -} diff --git a/src/main/java/ru/dmitriymx/corrector1s/gui/MainController.java b/src/main/java/ru/dmitriymx/corrector1s/gui/MainController.java deleted file mode 100644 index 7dd3766..0000000 --- a/src/main/java/ru/dmitriymx/corrector1s/gui/MainController.java +++ /dev/null @@ -1,232 +0,0 @@ -package ru.dmitriymx.corrector1s.gui; - -import javafx.application.Platform; -import javafx.event.ActionEvent; -import javafx.fxml.FXML; -import javafx.scene.control.*; -import javafx.scene.layout.Pane; -import javafx.scene.layout.VBox; -import javafx.stage.FileChooser; -import javafx.stage.Stage; -import javafx.util.StringConverter; -import lombok.extern.slf4j.Slf4j; -import ru.dmitriymx.corrector1s.Corrector1S; - -import java.io.*; -import java.nio.file.Files; -import java.nio.file.Paths; - -@Slf4j -public class MainController { - private Stage stage; - @FXML - private TextField sourceFilePath; - @FXML - private TextField saveAsFilePath; - @FXML - private CheckBox saveAsCheckBox; - @FXML - private Button btnSourceFilePath; - @FXML - private Button btnSaveAsFilePath; - @FXML - private Pane mainPane; - @FXML - private Pane waitPane; - @FXML - private ProgressIndicator progressBar; - @FXML - private Button btnStartCorrect; - @FXML - private Spinner honorarium; - private SpinnerValueFactory honorariumValueFactory = new SpinnerValueFactory.DoubleSpinnerValueFactory(0.01d, 100.0d, 9.0d); - @FXML - private Spinner fss; - private SpinnerValueFactory fssValueFactory = new SpinnerValueFactory.DoubleSpinnerValueFactory(0.01d, 100.0d, 31.0d); - - private FileChooser buildFileChooser(TextField textField) { - FileChooser fileChooser = new FileChooser(); - - if (!textField.getText().isEmpty() && Files.exists(Paths.get(textField.getText()))) { - File file = new File(textField.getText()); - if (file.isDirectory()) { - fileChooser.setInitialDirectory(file); - } else { - fileChooser.setInitialDirectory(file.getParentFile()); - } - } - - fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter( - "Таблицы Excel (*.xls, *.xlsx)", - "*.xls", "*.xlsx")); - - return fileChooser; - } - - private void enableInterface(boolean value) { - if (value) { - waitPane.setVisible(false); - mainPane.setOpacity(1.0d); - mainPane.setDisable(false); - } else { - mainPane.setDisable(true); - mainPane.setOpacity(0.3d); - waitPane.setVisible(true); - progressBar.setProgress(0d); - } - } - - private Alert buildErrorDialog(String title, String text, Exception e) { - Alert alert = new Alert(Alert.AlertType.ERROR); - alert.setTitle(title); - alert.setHeaderText(text); - - VBox dialogPaneContent = new VBox(); - Label label = new Label("Stack Trace:"); - - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); - e.printStackTrace(pw); - String stackTrace = sw.toString(); - - TextArea textArea = new TextArea(); - textArea.setText(stackTrace); - - dialogPaneContent.getChildren().addAll(label, textArea); - alert.getDialogPane().setContent(dialogPaneContent); - - return alert; - } - - private Alert buildInfoDialog(String title, String text) { - Alert alert = new Alert(Alert.AlertType.INFORMATION); - alert.setTitle(title); - alert.setHeaderText(null); - alert.setContentText(text); - return alert; - } - - private void commitEditorText(Spinner spinner) { - if (!spinner.isEditable()) return; - String text = spinner.getEditor().getText(); - SpinnerValueFactory valueFactory = spinner.getValueFactory(); - if (valueFactory != null) { - StringConverter converter = valueFactory.getConverter(); - if (converter != null) { - T value = converter.fromString(text); - valueFactory.setValue(value); - } - } - } - - @FXML - public void initialize() { - honorarium.setValueFactory(honorariumValueFactory); - honorarium.focusedProperty().addListener((observable, oldValue, newValue) -> { - if (newValue) return; // what? - commitEditorText(honorarium); - }); - - fss.setValueFactory(fssValueFactory); - fss.focusedProperty().addListener((observable, oldValue, newValue) -> { - if (newValue) return; // what? - commitEditorText(fss); - }); - - saveAsCheckBox.selectedProperty().addListener((observable, oldValue, newValue) -> { - saveAsFilePath.setDisable(!newValue); - btnSaveAsFilePath.setDisable(!newValue); - }); - - btnSourceFilePath.addEventHandler(ActionEvent.ACTION, event -> { - File file = buildFileChooser(sourceFilePath).showOpenDialog(stage); - if (file != null) { - sourceFilePath.setText(file.getAbsolutePath()); - if (!saveAsCheckBox.selectedProperty().get()) { - saveAsFilePath.setText(file.getAbsolutePath()); - } - } - }); - - btnSaveAsFilePath.addEventHandler(ActionEvent.ACTION, event -> { - File file = buildFileChooser(saveAsFilePath).showSaveDialog(stage); - if (file != null) { - saveAsFilePath.setText(file.getAbsolutePath()); - } - }); - - btnStartCorrect.addEventHandler(ActionEvent.ACTION, event -> { - enableInterface(false); - - Runnable runnable = () -> { - Corrector1S corrector = new Corrector1S(); - corrector.setSourceFile(new File(sourceFilePath.getText())); - if (saveAsCheckBox.isSelected()) { - corrector.setTargetFile(new File(saveAsFilePath.getText())); - } else { - corrector.setTargetFile(new File(sourceFilePath.getText())); - } - log.info("Honorarium: {}", honorarium.getValue()); - corrector.setHonorarium(honorarium.getValue()); - log.info("FSS: {}", fss.getValue()); - corrector.setFss(fss.getValue()); - - progressBar.setProgress(0.14d); - - try { - corrector.check(); - } catch (Exception e) { - log.error("Analize input data", e); - buildErrorDialog("Ошибка", "Ошибка при анализе входных данных", e) - .showAndWait(); - enableInterface(true); - return; - } - - progressBar.setProgress(0.28d); - - try { - corrector.createSnapshotData(); - } catch (Exception e) { - log.error("createSnapshotData", e); - buildErrorDialog("Ошибка", "Ошибка при обработке данных", e) - .showAndWait(); - enableInterface(true); - return; - } - - progressBar.setProgress(0.42d); - corrector.removeMergedCells(); - - progressBar.setProgress(0.56d); - corrector.replaceData(); - - progressBar.setProgress(0.70d); - corrector.insertNewCols(); - corrector.insertNewLines(); - - progressBar.setProgress(0.99d); - try { - corrector.saveFile(); - } catch (IOException e) { - log.error("saveFile", e); - buildErrorDialog("Ошибка", "Ошибка при сохранении файла", e) - .showAndWait(); - enableInterface(true); - return; - } - - progressBar.setProgress(1.0d); - buildInfoDialog("Информация", "Преобразование завершено успешно") - .showAndWait(); - enableInterface(true); - }; - - Platform.runLater(runnable); - }); - } - - void setStage(Stage stage) { - this.stage = stage; - } -} diff --git a/src/test/java/ru/dmitriymx/corrector1s/Test.java b/src/test/java/ru/dmitriymx/corrector1s/Test.java new file mode 100644 index 0000000..11abd12 --- /dev/null +++ b/src/test/java/ru/dmitriymx/corrector1s/Test.java @@ -0,0 +1,17 @@ +/* + * DmitriyMX + * 2018-07-18 + */ +package ru.dmitriymx.corrector1s; + +import ru.dmitriymx.corrector1s.config.XmlConfig; + +import java.io.InputStream; + +public class Test { + public static void main(String[] args) throws Exception { + InputStream stream = Test.class.getResourceAsStream("/config.xml"); + XmlConfig config = new XmlConfig(stream); + config.getColumnDataList().forEach(System.out::println); + } +} diff --git a/src/test/resources/config.xml b/src/test/resources/config.xml new file mode 100644 index 0000000..bfdeb44 --- /dev/null +++ b/src/test/resources/config.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file