diff --git a/clearing-parent/reports-service/pom.xml b/clearing-parent/reports-service/pom.xml
index 4edc035a2..323090060 100644
--- a/clearing-parent/reports-service/pom.xml
+++ b/clearing-parent/reports-service/pom.xml
@@ -33,11 +33,23 @@
com.fasterxml.jackson.dataformat
jackson-dataformat-xml
+
com.thoughtworks.xstream
xstream
1.4.19
+
+ org.apache.poi
+ poi-ooxml
+ 5.1.0
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
diff --git a/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/services/DOCXService.java b/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/services/DOCXService.java
new file mode 100644
index 000000000..20012d16f
--- /dev/null
+++ b/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/services/DOCXService.java
@@ -0,0 +1,90 @@
+package ru.spcex.clearing.reports.services;
+
+import org.apache.poi.xwpf.usermodel.*;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Сервис для работы с DOCX файлами
+ * Читает шаблон .docx и во всех текстовых элементах меняет переданные ключи на соответствующие им значения
+ */
+public class DOCXService {
+ /**
+ * При инициализации сохраняет все имеющиеся ключи (ищет по маске keyPattern), найденные в текстовых элементах документа
+ */
+ private final Map bodyElementForKey = new HashMap<>();
+ private final Pattern KEY_PATTERN = Pattern.compile("\\$\\{.*?}");
+ private final int MAX_POSITION = 128;
+
+ /**
+ * Инициализация шаблона
+ *
+ * @param templateFile файл шаблона
+ */
+ public void init(File templateFile) throws IOException {
+ assert templateFile != null && templateFile.exists() && templateFile.isFile();
+ try (InputStream inputStream = new FileInputStream(templateFile)) {
+ XWPFDocument doc = new XWPFDocument(inputStream);
+
+ for (IBodyElement element : doc.getBodyElements()) {
+ if (element instanceof XWPFTable table) {
+ collectKeysFromTable(table);
+ } else if (element instanceof XWPFParagraph paragraph) {
+ collectKeysFromParagraph(paragraph);
+ }
+ }
+
+ System.out.println("hello");
+ }
+ }
+
+ private void collectKeysFromTable(XWPFTable srcTable) {
+ for (XWPFTableRow row : srcTable.getRows()) {
+ for (XWPFTableCell cell : row.getTableCells()) {
+ for (IBodyElement element : cell.getBodyElements()) {
+ if (element instanceof XWPFTable table) {
+ collectKeysFromTable(table);
+ } else if (element instanceof XWPFParagraph paragraph) {
+ collectKeysFromParagraph(paragraph);
+ }
+ }
+ }
+ }
+ }
+
+ private void collectKeysFromParagraph(XWPFParagraph paragraph) {
+
+ for (XWPFRun run : paragraph.getRuns()) {
+ try {
+ for (int currPos = 0; currPos < MAX_POSITION; currPos++) {
+ String currText = run.getText(currPos);
+ if (currText == null) break;
+ Matcher matcher = KEY_PATTERN.matcher(currText);
+ while (matcher.find()) {
+ String key = matcher.group();
+ bodyElementForKey.put(key, new TextPositionOnRun(run, currPos));
+ }
+ }
+ } catch (IndexOutOfBoundsException ignored) {
+ // can't get all max text position for XWPFRun
+ }
+ }
+ }
+
+ static class TextPositionOnRun {
+ XWPFRun run;
+ int pos;
+
+ public TextPositionOnRun(XWPFRun run, int pos) {
+ this.run = run;
+ this.pos = pos;
+ }
+ }
+}
diff --git a/clearing-parent/reports-service/src/test/java/ru/spcex/clearing/reports/services/DOCXServiceTest.java b/clearing-parent/reports-service/src/test/java/ru/spcex/clearing/reports/services/DOCXServiceTest.java
new file mode 100644
index 000000000..6aad9d549
--- /dev/null
+++ b/clearing-parent/reports-service/src/test/java/ru/spcex/clearing/reports/services/DOCXServiceTest.java
@@ -0,0 +1,31 @@
+package ru.spcex.clearing.reports.services;
+
+import org.apache.poi.xwpf.usermodel.XWPFDocument;
+import org.junit.jupiter.api.Test;
+
+import java.io.*;
+
+class DOCXServiceTest {
+ @Test
+ public void testREC_ACTV() {
+ String inputFilename = "REC.ACTV_TEST.docx";
+ try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(inputFilename);
+ InputStream testStream = getClass().getClassLoader().getResourceAsStream("1.docx");
+ OutputStream outputStream = new FileOutputStream("test.docx")) {
+ XWPFDocument templateDoc = new XWPFDocument(inputStream);
+ XWPFDocument testDoc = new XWPFDocument(testStream);
+ testDoc.getParagraphs().get(6).getRuns().get(1).setText("========АОВЛДЫАОЫВЛДОАЛЫВДАОЫВЛДАОЫВ========");
+ testDoc.write(outputStream);
+ System.out.println("h");
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Test
+ public void test() throws IOException {
+ DOCXService service = new DOCXService();
+ service.init(new File(getClass().getClassLoader().getResource("REC.ACTV_TEST.docx").getPath()));
+ System.out.println("p");
+ }
+}
\ No newline at end of file
diff --git a/clearing-parent/reports-service/src/test/resources/REC.ACTV_TEST.docx b/clearing-parent/reports-service/src/test/resources/REC.ACTV_TEST.docx
new file mode 100644
index 000000000..aca71b0e5
Binary files /dev/null and b/clearing-parent/reports-service/src/test/resources/REC.ACTV_TEST.docx differ