diff --git a/pom.xml b/pom.xml
index 46d0e7d..07d5222 100644
--- a/pom.xml
+++ b/pom.xml
@@ -12,6 +12,7 @@
UTF-8
5.12.1
+ 5.2.5
@@ -61,17 +62,19 @@
bootstrapfx-core
0.4.0
+
+
- eu.hansolo
- tilesfx
- 21.0.9
-
-
- org.openjfx
- *
-
-
+ org.apache.poi
+ poi
+ ${poi.version}
+
+ org.apache.poi
+ poi-ooxml
+ ${poi.version}
+
+
org.junit.jupiter
junit-jupiter-api
@@ -103,10 +106,9 @@
0.0.8
-
default-cli
- com.dsol.pki_management/com.dsol.pki_management.HelloApplication
+ com.dsol.pki_management.app/com.dsol.pki_management.app.PKIApplication
app
app
app
diff --git a/src/main/java/com/dsol/pki_management/components/TableViewEnhancer.java b/src/main/java/com/dsol/pki_management/components/TableViewEnhancer.java
new file mode 100644
index 0000000..f49468f
--- /dev/null
+++ b/src/main/java/com/dsol/pki_management/components/TableViewEnhancer.java
@@ -0,0 +1,393 @@
+package com.dsol.pki_management.components;
+
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import javafx.scene.control.*;
+import javafx.scene.input.Clipboard;
+import javafx.scene.input.ClipboardContent;
+import javafx.scene.input.KeyCode;
+import javafx.scene.input.KeyCodeCombination;
+import javafx.scene.input.KeyCombination;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Утилитный класс для настройки TableView с поддержкой выделения ячеек и копирования (как в Excel)
+ */
+public class TableViewEnhancer {
+
+ // Хранилище для отслеживания выделенных ячеек
+ private static final java.util.Map, ObservableList>> selectedCellsMap = new java.util.concurrent.ConcurrentHashMap<>();
+
+ // Хранилище для отслеживания начальной позиции при drag selection
+ private static final java.util.Map, TablePosition, ?>> dragStartMap = new java.util.concurrent.ConcurrentHashMap<>();
+
+ /**
+ * Настраивает таблицу для выделения ячеек и копирования
+ * @param tableView Таблица для настройки
+ * @param Тип данных строки таблицы
+ */
+ public static void enhance(TableView tableView) {
+ if (tableView == null) {
+ return;
+ }
+
+ // Создаем список для отслеживания выделенных ячеек
+ @SuppressWarnings("unchecked")
+ ObservableList> selectedCells = FXCollections.observableArrayList();
+ selectedCellsMap.put(tableView, selectedCells);
+
+ setupCellSelection(tableView);
+ setupCopyHandler(tableView);
+ }
+
+ /**
+ * Настраивает режим выделения ячеек вместо строк
+ */
+ @SuppressWarnings("unchecked")
+ private static void setupCellSelection(TableView tableView) {
+ TableSelectionModel selectionModel = tableView.getSelectionModel();
+
+ // Включаем выделение ячеек
+ selectionModel.setCellSelectionEnabled(true);
+
+ // Разрешаем множественное выделение
+ selectionModel.setSelectionMode(SelectionMode.MULTIPLE);
+
+ // Отслеживаем изменения выделения
+ ObservableList> trackedCells = selectedCellsMap.get(tableView);
+
+ // Обработчик начала выделения (mouse pressed)
+ tableView.setOnMousePressed(event -> {
+ if (event.getTarget() instanceof TableCell) {
+ @SuppressWarnings("unchecked")
+ TableCell clickedCell = (TableCell) event.getTarget();
+ TableRow row = clickedCell.getTableRow();
+
+ if (row != null) {
+ int clickedRow = row.getIndex();
+ TableColumn clickedColumn = clickedCell.getTableColumn();
+
+ if (clickedRow >= 0 && clickedColumn != null) {
+ // Создаем начальную позицию для drag selection
+ TablePosition startPos = new TablePosition<>(tableView, clickedRow, clickedColumn);
+ dragStartMap.put(tableView, startPos);
+
+ // Если не Shift+Click, очищаем выделение и выделяем одну ячейку
+ if (!event.isShiftDown() && !event.isControlDown()) {
+ selectionModel.clearSelection();
+ selectionModel.select(clickedRow, clickedColumn);
+ trackedCells.clear();
+ trackedCells.add(startPos);
+ } else if (event.isShiftDown()) {
+ // Shift+Click: выделяем диапазон от последней выделенной ячейки
+ TablePosition currentSelection = trackedCells != null && !trackedCells.isEmpty()
+ ? (TablePosition) trackedCells.get(trackedCells.size() - 1)
+ : null;
+
+ if (currentSelection != null) {
+ int startRow = Math.min(currentSelection.getRow(), clickedRow);
+ int endRow = Math.max(currentSelection.getRow(), clickedRow);
+ int startCol = Math.min(currentSelection.getColumn(), tableView.getColumns().indexOf(clickedColumn));
+ int endCol = Math.max(currentSelection.getColumn(), tableView.getColumns().indexOf(clickedColumn));
+
+ selectionModel.clearSelection();
+ trackedCells.clear();
+ for (int r = startRow; r <= endRow; r++) {
+ for (int c = startCol; c <= endCol; c++) {
+ if (r >= 0 && r < tableView.getItems().size()
+ && c >= 0 && c < tableView.getColumns().size()) {
+ TableColumn col = tableView.getColumns().get(c);
+ selectionModel.select(r, col);
+ TablePosition pos = new TablePosition<>(tableView, r, col);
+ trackedCells.add(pos);
+ }
+ }
+ }
+ event.consume();
+ }
+ } else if (event.isControlDown()) {
+ // Ctrl+Click: добавляем/удаляем ячейку из выделения
+ TablePosition pos = new TablePosition<>(tableView, clickedRow, clickedColumn);
+ if (trackedCells.contains(pos)) {
+ selectionModel.clearSelection(clickedRow, clickedColumn);
+ trackedCells.remove(pos);
+ } else {
+ selectionModel.select(clickedRow, clickedColumn);
+ trackedCells.add(pos);
+ }
+ event.consume();
+ }
+ }
+ }
+ }
+ });
+
+ // Обработчик перетаскивания мыши для выделения диапазона (drag selection)
+ tableView.setOnMouseDragged(event -> {
+ TablePosition, ?> startPos = dragStartMap.get(tableView);
+ if (startPos == null) return;
+
+ // Находим ячейку под курсором мыши
+ javafx.geometry.Point2D localPoint = tableView.screenToLocal(event.getScreenX(), event.getScreenY());
+ int draggedRow = -1;
+ int draggedCol = -1;
+
+ // Вычисляем строку на основе Y координаты
+ // Учитываем заголовок таблицы (если есть)
+ double headerHeight = tableView.lookup(".column-header-background") != null
+ ? tableView.lookup(".column-header-background").getBoundsInLocal().getHeight()
+ : 0;
+ double y = localPoint.getY() - headerHeight;
+
+ // Вычисляем индекс строки
+ double rowHeight = tableView.getFixedCellSize() > 0
+ ? tableView.getFixedCellSize()
+ : 25.0; // примерная высота строки по умолчанию
+
+ if (y >= 0) {
+ draggedRow = (int) (y / rowHeight);
+ }
+
+ // Вычисляем колонку на основе X координаты
+ double x = localPoint.getX();
+ double currentX = 0;
+ for (int i = 0; i < tableView.getColumns().size(); i++) {
+ TableColumn col = tableView.getColumns().get(i);
+ double colWidth = col.getWidth();
+ if (x >= currentX && x < currentX + colWidth) {
+ draggedCol = i;
+ break;
+ }
+ currentX += colWidth;
+ }
+
+ // Ограничиваем индексы валидными значениями
+ if (draggedRow < 0) draggedRow = 0;
+ if (draggedRow >= tableView.getItems().size()) draggedRow = tableView.getItems().size() - 1;
+ if (draggedCol < 0) draggedCol = 0;
+ if (draggedCol >= tableView.getColumns().size()) draggedCol = tableView.getColumns().size() - 1;
+
+ // Выделяем диапазон
+ if (draggedRow >= 0 && draggedRow < tableView.getItems().size()
+ && draggedCol >= 0 && draggedCol < tableView.getColumns().size()) {
+ int startRow = Math.min(startPos.getRow(), draggedRow);
+ int endRow = Math.max(startPos.getRow(), draggedRow);
+ int startCol = Math.min(startPos.getColumn(), draggedCol);
+ int endCol = Math.max(startPos.getColumn(), draggedCol);
+
+ selectionModel.clearSelection();
+ trackedCells.clear();
+
+ for (int r = startRow; r <= endRow; r++) {
+ for (int c = startCol; c <= endCol; c++) {
+ if (r >= 0 && r < tableView.getItems().size()
+ && c >= 0 && c < tableView.getColumns().size()) {
+ TableColumn col = tableView.getColumns().get(c);
+ selectionModel.select(r, col);
+ TablePosition pos = new TablePosition<>(tableView, r, col);
+ trackedCells.add(pos);
+ }
+ }
+ }
+ event.consume();
+ }
+ });
+
+ // Обработчик окончания перетаскивания
+ tableView.setOnMouseReleased(event -> {
+ dragStartMap.remove(tableView);
+ });
+
+ // Сброс выделения при клике вне таблицы
+ // Устанавливаем обработчик на сцену, если она доступна
+ tableView.sceneProperty().addListener((obs, oldScene, newScene) -> {
+ if (newScene != null && oldScene == null) {
+ // Добавляем обработчик только один раз при первой установке сцены
+ newScene.addEventFilter(javafx.scene.input.MouseEvent.MOUSE_CLICKED, event -> {
+ // Проверяем, был ли клик вне таблицы
+ javafx.scene.Node target = (javafx.scene.Node) event.getTarget();
+ if (!isNodeInTableView(tableView, target)) {
+ selectionModel.clearSelection();
+ if (trackedCells != null) {
+ trackedCells.clear();
+ }
+ }
+ });
+ }
+ });
+ }
+
+ /**
+ * Проверяет, находится ли узел внутри TableView
+ */
+ private static boolean isNodeInTableView(TableView tableView, javafx.scene.Node node) {
+ javafx.scene.Node current = node;
+ while (current != null) {
+ if (current == tableView) {
+ return true;
+ }
+ current = current.getParent();
+ }
+ return false;
+ }
+
+ /**
+ * Обновляет отслеживаемый список выделенных ячеек на основе текущего выделения модели
+ * Используется как fallback, если trackedCells не синхронизирован
+ */
+ @SuppressWarnings("unchecked")
+ private static void updateTrackedCells(TableView tableView, ObservableList> trackedCells) {
+ if (trackedCells == null) return;
+
+ // Обновляем на основе текущего выделения в модели
+ // В режиме cell selection нужно проверять каждую ячейку отдельно
+ TableSelectionModel selectionModel = tableView.getSelectionModel();
+ ObservableList selectedIndices = selectionModel.getSelectedIndices();
+
+ trackedCells.clear();
+
+ // Проходим по всем выделенным строкам и проверяем каждую колонку
+ for (Integer rowIndex : selectedIndices) {
+ if (rowIndex >= 0 && rowIndex < tableView.getItems().size()) {
+ for (TableColumn column : tableView.getColumns()) {
+ // Проверяем, выделена ли конкретная ячейка
+ // В JavaFX с cell selection это можно проверить через попытку выделения
+ // или через отслеживание состояния
+ try {
+ // Пытаемся использовать рефлексию для проверки выделения ячейки
+ java.lang.reflect.Method isSelectedMethod = selectionModel.getClass()
+ .getMethod("isSelected", int.class, TableColumn.class);
+ Boolean isSelected = (Boolean) isSelectedMethod.invoke(selectionModel, rowIndex, column);
+ if (Boolean.TRUE.equals(isSelected)) {
+ TablePosition pos = new TablePosition<>(tableView, rowIndex, column);
+ if (!trackedCells.contains(pos)) {
+ trackedCells.add(pos);
+ }
+ }
+ } catch (Exception e) {
+ // Если метод недоступен, добавляем все ячейки выделенных строк
+ // Это не идеально, но лучше чем ничего
+ TablePosition pos = new TablePosition<>(tableView, rowIndex, column);
+ if (!trackedCells.contains(pos)) {
+ trackedCells.add(pos);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Настраивает обработчик копирования (Ctrl+C)
+ */
+ private static void setupCopyHandler(TableView tableView) {
+ KeyCodeCombination copyKey = new KeyCodeCombination(KeyCode.C, KeyCombination.CONTROL_DOWN);
+
+ tableView.setOnKeyPressed(event -> {
+ if (copyKey.match(event)) {
+ copySelectedCells(tableView);
+ event.consume();
+ }
+ });
+ }
+
+ /**
+ * Копирует выделенные ячейки в буфер обмена
+ */
+ @SuppressWarnings("unchecked")
+ private static void copySelectedCells(TableView tableView) {
+ TableSelectionModel selectionModel = tableView.getSelectionModel();
+
+ if (!selectionModel.isCellSelectionEnabled()) {
+ return;
+ }
+
+ // Получаем отслеживаемые выделенные ячейки
+ ObservableList> trackedCells = selectedCellsMap.get(tableView);
+
+ // Если список пуст или не синхронизирован, обновляем его
+ if (trackedCells == null || trackedCells.isEmpty()) {
+ if (trackedCells == null) {
+ trackedCells = FXCollections.observableArrayList();
+ selectedCellsMap.put(tableView, trackedCells);
+ }
+ updateTrackedCells(tableView, trackedCells);
+ }
+
+ // Используем отслеживаемые ячейки для копирования
+ List> selectedCells = new ArrayList<>(trackedCells);
+
+ if (selectedCells.isEmpty()) {
+ return;
+ }
+
+ // Сортируем ячейки по строкам и колонкам
+ selectedCells.sort((a, b) -> {
+ int rowCompare = Integer.compare(a.getRow(), b.getRow());
+ if (rowCompare != 0) return rowCompare;
+ return Integer.compare(a.getColumn(), b.getColumn());
+ });
+
+ // Группируем по строкам
+ StringBuilder clipboardText = new StringBuilder();
+ int currentRow = -1;
+ boolean firstCell = true;
+
+ for (TablePosition, ?> pos : selectedCells) {
+ if (currentRow != pos.getRow() && !firstCell) {
+ clipboardText.append("\n");
+ }
+
+ if (currentRow != pos.getRow()) {
+ currentRow = pos.getRow();
+ firstCell = true;
+ } else {
+ clipboardText.append("\t");
+ }
+
+ // Получаем значение ячейки
+ @SuppressWarnings("unchecked")
+ TablePosition typedPos = (TablePosition) pos;
+ Object cellValue = getCellValue(tableView, typedPos);
+ clipboardText.append(cellValue != null ? cellValue.toString() : "");
+
+ firstCell = false;
+ }
+
+ // Копируем в буфер обмена
+ Clipboard clipboard = Clipboard.getSystemClipboard();
+ ClipboardContent content = new ClipboardContent();
+ content.putString(clipboardText.toString());
+ clipboard.setContent(content);
+ }
+
+ /**
+ * Получает значение ячейки по позиции
+ */
+ private static Object getCellValue(TableView tableView, TablePosition pos) {
+ if (pos.getRow() < 0 || pos.getRow() >= tableView.getItems().size()) {
+ return "";
+ }
+
+ T rowItem = tableView.getItems().get(pos.getRow());
+ if (rowItem == null) {
+ return "";
+ }
+
+ if (pos.getColumn() < 0 || pos.getColumn() >= tableView.getColumns().size()) {
+ return "";
+ }
+
+ TableColumn column = tableView.getColumns().get(pos.getColumn());
+ if (column == null) {
+ return "";
+ }
+
+ // Получаем значение через cell value factory
+ Object cellValue = column.getCellData(rowItem);
+ return cellValue != null ? cellValue : "";
+ }
+}
+
diff --git a/src/main/java/com/dsol/pki_management/config/AppConfig.java b/src/main/java/com/dsol/pki_management/config/AppConfig.java
index bcac8c5..18c1cf1 100644
--- a/src/main/java/com/dsol/pki_management/config/AppConfig.java
+++ b/src/main/java/com/dsol/pki_management/config/AppConfig.java
@@ -12,6 +12,10 @@ public class AppConfig {
public static final String KEY_WINDOW_HEIGHT = "window.height";
public static final String KEY_WINDOW_X = "window.x";
public static final String KEY_WINDOW_Y = "window.y";
+ public static final String KEY_HIDDEN_SUBMENUS = "submenu.hidden"; // comma-separated texts
+ public static final String KEY_PURCHASES_EXCEL_PATH = "purchases.excel.path";
+ public static final String KEY_MES_URL = "mes.url";
+ public static final String KEY_MES_MATCH_ERROR_URL = "mes.match_error.url";
// Значения по умолчанию
public static final String DEFAULT_ADMIN_PASSWORD = "admin123";
diff --git a/src/main/java/com/dsol/pki_management/controllers/MainController.java b/src/main/java/com/dsol/pki_management/controllers/MainController.java
index 7cbed6a..68ffa22 100644
--- a/src/main/java/com/dsol/pki_management/controllers/MainController.java
+++ b/src/main/java/com/dsol/pki_management/controllers/MainController.java
@@ -3,6 +3,11 @@ package com.dsol.pki_management.controllers;
import com.dsol.pki_management.components.MenuItem;
import com.dsol.pki_management.components.SubMenuItem;
import com.dsol.pki_management.modules.test1.Test1Controller;
+import com.dsol.pki_management.config.ConfigManager;
+import com.dsol.pki_management.config.AppConfig;
+import com.dsol.pki_management.modules.purchases.PurchasesExcelValidator;
+import com.dsol.pki_management.modules.purchases.PurchasesExcelParser;
+import com.dsol.pki_management.modules.purchases.PurchasesDataStore;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Button;
@@ -11,10 +16,18 @@ import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import java.util.Arrays;
import javafx.scene.input.MouseEvent;
+import javafx.concurrent.Task;
+import javafx.application.Platform;
/**
* Главный контроллер приложения
@@ -41,6 +54,8 @@ public class MainController {
menuOverlay.setOnMouseClicked(e -> closeMenu());
setupMenuItems();
+ applyHiddenSubMenusFromConfig();
+ startPurchasesScanIfConfigured();
}
private void setupMenuItems() {
@@ -59,6 +74,13 @@ public class MainController {
createSubMenuItem("Отчеты", e -> System.out.println("Открыты отчеты"))
);
+ MenuItem purchases = createMenuItem("Закупки",
+ createSubMenuItem("Закупочный перечень", e -> {
+ loadPurchasesListModule();
+ closeMenu();
+ })
+ );
+
MenuItem testMenu = createMenuItem("Test",
createSubMenuItem("Тест 1", e -> {
loadTest1Module();
@@ -68,10 +90,77 @@ public class MainController {
// Добавляем блоки меню в контент меню
if (menuContent != null) {
- menuContent.getChildren().addAll(mainFunctions, management, testMenu);
+ menuContent.getChildren().addAll(mainFunctions, management, purchases, testMenu);
}
}
+ /**
+ * Применяет скрытые подменю из конфигурации
+ */
+ private void applyHiddenSubMenusFromConfig() {
+ ConfigManager config = ConfigManager.getInstance();
+ String raw = config.getProperty(AppConfig.KEY_HIDDEN_SUBMENUS, "");
+ if (raw == null || raw.isBlank()) {
+ return;
+ }
+ Set hidden = Arrays.stream(raw.split(","))
+ .map(String::trim)
+ .filter(s -> !s.isEmpty())
+ .collect(Collectors.toCollection(HashSet::new));
+
+ for (SubMenuItem item : allSubMenuItems) {
+ if (hidden.contains(item.getText())) {
+ item.setItemVisible(false);
+ }
+ }
+ for (MenuItem menuItem : allMenuItems) {
+ menuItem.updateVisibility();
+ }
+ }
+
+ /**
+ * Запускает фоновую проверку Excel закупок при старте приложения (если путь задан)
+ */
+ private void startPurchasesScanIfConfigured() {
+ String pathStr = ConfigManager.getInstance().getProperty(AppConfig.KEY_PURCHASES_EXCEL_PATH, "");
+ if (pathStr == null || pathStr.isBlank()) {
+ return;
+ }
+ Path path = Paths.get(pathStr);
+ Task task = new Task<>() {
+ @Override
+ protected Void call() {
+ try {
+ PurchasesDataStore.getInstance().setStatus("Сканирование файла...");
+ PurchasesExcelValidator validator = new PurchasesExcelValidator();
+ var results = validator.validate(path);
+ results.forEach(r -> {
+ if (r.valid) {
+ System.out.println("[OK] Лист '" + r.sheetName + "' — все обязательные заголовки найдены");
+ } else {
+ System.out.println("[WARN] Лист '" + r.sheetName + "' — отсутствуют заголовки: " + String.join(", ", r.missingHeaders));
+ }
+ });
+ // Парсим данные и кладём в стор
+ PurchasesExcelParser parser = new PurchasesExcelParser();
+ var rows = parser.parse(path);
+ Platform.runLater(() -> {
+ PurchasesDataStore.getInstance().setRows(rows);
+ PurchasesDataStore.getInstance().setStatus("Загружено записей: " + rows.size());
+ });
+ } catch (Exception ex) {
+ System.err.println("[ERROR] Ошибка валидации/парсинга Excel при старте: " + ex.getMessage());
+ ex.printStackTrace();
+ Platform.runLater(() -> PurchasesDataStore.getInstance().setStatus("Ошибка: " + ex.getMessage()));
+ }
+ return null;
+ }
+ };
+ Thread t = new Thread(task, "startup-purchases-excel-validate");
+ t.setDaemon(true);
+ t.start();
+ }
+
/**
* Создает MenuItem с указанным названием и подменю
*/
@@ -142,6 +231,24 @@ public class MainController {
e.printStackTrace();
}
}
+
+ /**
+ * Загружает модуль "Закупочный перечень" в центральную область
+ */
+ private void loadPurchasesListModule() {
+ try {
+ FXMLLoader loader = new FXMLLoader(getClass().getResource("/com/dsol/pki_management/modules/purchases/purchases-list-view.fxml"));
+ VBox purchasesView = loader.load();
+
+ if (contentArea != null) {
+ contentArea.getChildren().clear();
+ contentArea.getChildren().add(purchasesView);
+ }
+ } catch (IOException e) {
+ System.err.println("Ошибка загрузки модуля Закупочный перечень: " + e.getMessage());
+ e.printStackTrace();
+ }
+ }
/**
* Загружает модуль настроек в центральную область
diff --git a/src/main/java/com/dsol/pki_management/controllers/SettingsController.java b/src/main/java/com/dsol/pki_management/controllers/SettingsController.java
index 0760057..b8d87f4 100644
--- a/src/main/java/com/dsol/pki_management/controllers/SettingsController.java
+++ b/src/main/java/com/dsol/pki_management/controllers/SettingsController.java
@@ -12,9 +12,14 @@ import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Modality;
+import javafx.stage.FileChooser;
+import java.io.File;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
/**
* Контроллер для модуля настроек
@@ -32,6 +37,12 @@ public class SettingsController {
private Label statusLabel;
@FXML
private VBox subMenuItemsContainer;
+ @FXML
+ private TextField purchasesExcelPathField;
+ @FXML
+ private TextField mesUrlField;
+ @FXML
+ private TextField mesMatchErrorUrlField;
private boolean isAdminMode = false;
private final ConfigManager configManager = ConfigManager.getInstance();
@@ -54,8 +65,69 @@ public class SettingsController {
if (exitAdminButton != null) {
exitAdminButton.setOnAction(e -> disableAdminMode());
}
+
+ // Инициализация поля пути к Excel файлу из конфига
+ if (purchasesExcelPathField != null) {
+ String path = configManager.getProperty(AppConfig.KEY_PURCHASES_EXCEL_PATH, "");
+ purchasesExcelPathField.setText(path);
+ purchasesExcelPathField.setOnAction(e -> savePurchasesExcelPath());
+ purchasesExcelPathField.focusedProperty().addListener((obs, oldV, newV) -> {
+ if (!newV) savePurchasesExcelPath();
+ });
+ }
+
+ // Инициализация полей MES из конфига
+ if (mesUrlField != null) {
+ String url = configManager.getProperty(AppConfig.KEY_MES_URL, "");
+ mesUrlField.setText(url);
+ mesUrlField.setOnAction(e -> saveMesUrls());
+ mesUrlField.focusedProperty().addListener((obs, oldV, newV) -> {
+ if (!newV) saveMesUrls();
+ });
+ }
+ if (mesMatchErrorUrlField != null) {
+ String url = configManager.getProperty(AppConfig.KEY_MES_MATCH_ERROR_URL, "");
+ mesMatchErrorUrlField.setText(url);
+ mesMatchErrorUrlField.setOnAction(e -> saveMesUrls());
+ mesMatchErrorUrlField.focusedProperty().addListener((obs, oldV, newV) -> {
+ if (!newV) saveMesUrls();
+ });
+ }
}
+ private void savePurchasesExcelPath() {
+ if (purchasesExcelPathField == null) return;
+ String path = purchasesExcelPathField.getText() == null ? "" : purchasesExcelPathField.getText().trim();
+ configManager.setProperty(AppConfig.KEY_PURCHASES_EXCEL_PATH, path);
+ configManager.saveConfig();
+ }
+
+ private void saveMesUrls() {
+ if (mesUrlField != null) {
+ String url = mesUrlField.getText() == null ? "" : mesUrlField.getText().trim();
+ configManager.setProperty(AppConfig.KEY_MES_URL, url);
+ }
+ if (mesMatchErrorUrlField != null) {
+ String url = mesMatchErrorUrlField.getText() == null ? "" : mesMatchErrorUrlField.getText().trim();
+ configManager.setProperty(AppConfig.KEY_MES_MATCH_ERROR_URL, url);
+ }
+ configManager.saveConfig();
+ }
+
+ @FXML
+ private void choosePurchasesExcelFile() {
+ FileChooser chooser = new FileChooser();
+ chooser.setTitle("Выберите Excel файл закупок");
+ chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Excel (*.xlsx)", "*.xlsx"));
+ File initialDir = new File(System.getProperty("user.home"));
+ if (initialDir.exists()) chooser.setInitialDirectory(initialDir);
+ File file = chooser.showOpenDialog(null);
+ if (file != null) {
+ purchasesExcelPathField.setText(file.getAbsolutePath());
+ savePurchasesExcelPath();
+ }
+ }
+
/**
* Устанавливает видимость контейнера
*/
@@ -130,7 +202,7 @@ public class SettingsController {
subMenuItemsContainer.setPadding(new Insets(15, 0, 15, 0));
subMenuItemsContainer.setSpacing(8);
- // Получаем все MenuItem и SubMenuItem из HelloController
+ // Получаем все MenuItem и SubMenuItem из MainController
List