Add source

This commit is contained in:
2025-10-07 21:57:07 +03:00
parent 3b3ed0ae09
commit a891bebb10
27 changed files with 8458 additions and 0 deletions

128
pom.xml Normal file
View File

@@ -0,0 +1,128 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>AccessLZK</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>24</maven.compiler.source>
<maven.compiler.target>24</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<main.class>org.example.Main</main.class>
</properties>
<dependencies>
<!-- JavaFX Controls -->
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>21.0.1</version>
</dependency>
<!-- JavaFX FXML -->
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>21.0.1</version>
</dependency>
<!-- MS Access Database Driver -->
<dependency>
<groupId>net.sf.ucanaccess</groupId>
<artifactId>ucanaccess</artifactId>
<version>5.0.1</version>
</dependency>
<!-- PostgreSQL Database Driver -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.7.3</version>
</dependency>
<!-- JFreeChart for data visualization -->
<dependency>
<groupId>org.jfree</groupId>
<artifactId>jfreechart</artifactId>
<version>1.5.4</version>
</dependency>
<!-- Jackson for JSON processing -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>
<!-- JFXtras for advanced JavaFX controls -->
<dependency>
<groupId>org.jfxtras</groupId>
<artifactId>jfxtras-controls</artifactId>
<version>17-r1</version>
</dependency>
<!-- Apache POI for Excel export -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.2.4</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.4</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<source>21</source>
<target>21</target>
</configuration>
</plugin>
<plugin>
<groupId>org.openjfx</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>0.0.8</version>
<configuration>
<mainClass>org.example.Main</mainClass>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.5.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>org.example.Main</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,27 @@
package org.example;
public class Component {
private String libRef;
private int totalComponents;
public Component(String libRef, int totalComponents) {
this.libRef = libRef;
this.totalComponents = totalComponents;
}
public String getLibRef() {
return libRef;
}
public void setLibRef(String libRef) {
this.libRef = libRef;
}
public int getTotalComponents() {
return totalComponents;
}
public void setTotalComponents(int totalComponents) {
this.totalComponents = totalComponents;
}
}

View File

@@ -0,0 +1,180 @@
package org.example;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
public class ConnectionManager {
public static class ConnectionResult {
private final boolean success;
private final String message;
private final long connectionTime;
public ConnectionResult(boolean success, String message, long connectionTime) {
this.success = success;
this.message = message;
this.connectionTime = connectionTime;
}
public boolean isSuccess() { return success; }
public String getMessage() { return message; }
public long getConnectionTime() { return connectionTime; }
}
public static ConnectionResult testAccessConnection(String connectionString) {
long startTime = System.currentTimeMillis();
try {
// Загружаем драйвер UCanAccess
Class.forName("net.ucanaccess.jdbc.UcanaccessDriver");
// Проверяем и исправляем строку подключения
String correctedConnectionString = connectionString;
if (!connectionString.startsWith("jdbc:ucanaccess://")) {
correctedConnectionString = "jdbc:ucanaccess://" + connectionString.replaceFirst("^jdbc:ucanaccess://", "");
}
// Тестируем подключение
try (Connection conn = DriverManager.getConnection(correctedConnectionString)) {
long endTime = System.currentTimeMillis();
long connectionTime = endTime - startTime;
if (conn != null && !conn.isClosed()) {
return new ConnectionResult(true,
"MS Access подключение успешно установлено\n" +
"Использованная строка: " + correctedConnectionString, connectionTime);
} else {
return new ConnectionResult(false,
"Не удалось установить подключение к MS Access", connectionTime);
}
}
} catch (ClassNotFoundException e) {
long endTime = System.currentTimeMillis();
return new ConnectionResult(false,
"Ошибка: Драйвер UCanAccess не найден. Проверьте зависимости.\n" +
"Убедитесь, что в pom.xml добавлена зависимость:\n" +
"<dependency>\n" +
" <groupId>net.sf.ucanaccess</groupId>\n" +
" <artifactId>ucanaccess</artifactId>\n" +
" <version>5.0.1</version>\n" +
"</dependency>",
endTime - startTime);
} catch (SQLException e) {
long endTime = System.currentTimeMillis();
return new ConnectionResult(false,
"Ошибка подключения к MS Access: " + e.getMessage() + "\n" +
"Проверьте:\n" +
"1. Существует ли файл базы данных\n" +
"2. Правильность пути к файлу\n" +
"3. Доступность файла для чтения",
endTime - startTime);
} catch (Exception e) {
long endTime = System.currentTimeMillis();
return new ConnectionResult(false,
"Неожиданная ошибка: " + e.getMessage(),
endTime - startTime);
}
}
public static ConnectionResult testPostgreSQLConnection(String connectionString,
String username, String password) {
long startTime = System.currentTimeMillis();
try {
// Загружаем драйвер PostgreSQL
Class.forName("org.postgresql.Driver");
// Проверяем и исправляем строку подключения
String correctedConnectionString = connectionString;
if (!connectionString.startsWith("jdbc:postgresql://")) {
correctedConnectionString = "jdbc:postgresql://" + connectionString;
} else {
// Если строка уже содержит правильный префикс, используем как есть
correctedConnectionString = connectionString;
}
// Тестируем подключение
try (Connection conn = DriverManager.getConnection(correctedConnectionString, username, password)) {
long endTime = System.currentTimeMillis();
long connectionTime = endTime - startTime;
if (conn != null && !conn.isClosed()) {
return new ConnectionResult(true,
"PostgreSQL подключение успешно установлено\n" +
"Использованная строка: " + correctedConnectionString, connectionTime);
} else {
return new ConnectionResult(false,
"Не удалось установить подключение к PostgreSQL", connectionTime);
}
}
} catch (ClassNotFoundException e) {
long endTime = System.currentTimeMillis();
return new ConnectionResult(false,
"Ошибка: Драйвер PostgreSQL не найден. Проверьте зависимости.\n" +
"Убедитесь, что в pom.xml добавлена зависимость:\n" +
"<dependency>\n" +
" <groupId>org.postgresql</groupId>\n" +
" <artifactId>postgresql</artifactId>\n" +
" <version>42.7.3</version>\n" +
"</dependency>",
endTime - startTime);
} catch (SQLException e) {
long endTime = System.currentTimeMillis();
return new ConnectionResult(false,
"Ошибка подключения к PostgreSQL: " + e.getMessage() + "\n" +
"Проверьте:\n" +
"1. Правильность URL подключения\n" +
"2. Доступность сервера PostgreSQL\n" +
"3. Правильность имени пользователя и пароля",
endTime - startTime);
} catch (Exception e) {
long endTime = System.currentTimeMillis();
return new ConnectionResult(false,
"Неожиданная ошибка: " + e.getMessage(),
endTime - startTime);
}
}
public static ConnectionResult testAllConnections(Properties config) {
long startTime = System.currentTimeMillis();
StringBuilder results = new StringBuilder();
boolean allSuccess = true;
// Тестируем MS Access
String accessConnection = config.getProperty("access.connection.string", "");
if (!accessConnection.isEmpty()) {
ConnectionResult accessResult = testAccessConnection(accessConnection);
results.append("MS Access: ").append(accessResult.getMessage())
.append(" (").append(accessResult.getConnectionTime()).append("ms)\n");
if (!accessResult.isSuccess()) allSuccess = false;
} else {
results.append("MS Access: Строка подключения не указана\n");
allSuccess = false;
}
// Тестируем PostgreSQL
String postgresConnection = config.getProperty("postgresql.connection.string", "");
String postgresUsername = config.getProperty("postgresql.username", "");
String postgresPassword = config.getProperty("postgresql.password", "");
if (!postgresConnection.isEmpty() && !postgresUsername.isEmpty()) {
ConnectionResult postgresResult = testPostgreSQLConnection(
postgresConnection, postgresUsername, postgresPassword);
results.append("PostgreSQL: ").append(postgresResult.getMessage())
.append(" (").append(postgresResult.getConnectionTime()).append("ms)\n");
if (!postgresResult.isSuccess()) allSuccess = false;
} else {
results.append("PostgreSQL: Не все параметры подключения указаны\n");
allSuccess = false;
}
long endTime = System.currentTimeMillis();
String message = allSuccess ?
"Все подключения успешно установлены!\n" + results.toString() :
"Обнаружены проблемы с подключениями:\n" + results.toString();
return new ConnectionResult(allSuccess, message, endTime - startTime);
}
}

View File

@@ -0,0 +1,154 @@
package org.example;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class DatabaseInitializer {
public static class InitializationResult {
private final boolean success;
private final String message;
private final long executionTime;
public InitializationResult(boolean success, String message, long executionTime) {
this.success = success;
this.message = message;
this.executionTime = executionTime;
}
public boolean isSuccess() { return success; }
public String getMessage() { return message; }
public long getExecutionTime() { return executionTime; }
}
public static InitializationResult initializePostgreSQLDatabase(String connectionString,
String username, String password) {
long startTime = System.currentTimeMillis();
try {
// Загружаем драйвер PostgreSQL
Class.forName("org.postgresql.Driver");
// Проверяем и исправляем строку подключения
String correctedConnectionString = connectionString;
if (!connectionString.startsWith("jdbc:postgresql://")) {
correctedConnectionString = "jdbc:postgresql://" + connectionString;
} else {
// Если строка уже содержит правильный префикс, используем как есть
correctedConnectionString = connectionString;
}
try (Connection conn = DriverManager.getConnection(correctedConnectionString, username, password)) {
if (conn != null && !conn.isClosed()) {
// Создаем таблицы
createTables(conn);
long endTime = System.currentTimeMillis();
return new InitializationResult(true,
"База данных PostgreSQL успешно инициализирована!\n" +
"Созданы таблицы: limit_drawing_card, stage, components\n" +
"Установлены связи между таблицами",
endTime - startTime);
} else {
return new InitializationResult(false,
"Не удалось подключиться к базе данных PostgreSQL",
System.currentTimeMillis() - startTime);
}
}
} catch (ClassNotFoundException e) {
return new InitializationResult(false,
"Ошибка: Драйвер PostgreSQL не найден",
System.currentTimeMillis() - startTime);
} catch (SQLException e) {
return new InitializationResult(false,
"Ошибка инициализации PostgreSQL: " + e.getMessage(),
System.currentTimeMillis() - startTime);
} catch (Exception e) {
return new InitializationResult(false,
"Неожиданная ошибка: " + e.getMessage(),
System.currentTimeMillis() - startTime);
}
}
private static void createTables(Connection conn) throws SQLException {
try (Statement stmt = conn.createStatement()) {
// Создаем таблицу limit_drawing_card
String createLimitDrawingCard = """
CREATE TABLE IF NOT EXISTS limit_drawing_card (
id SERIAL PRIMARY KEY,
order_number INTEGER NOT NULL,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
responsible VARCHAR(255) NOT NULL
)
""";
// Создаем таблицу stage
String createStage = """
CREATE TABLE IF NOT EXISTS stage (
id SERIAL PRIMARY KEY,
stage_number INTEGER NOT NULL,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
responsible VARCHAR(255) NOT NULL,
number_picking_card VARCHAR(255) NOT NULL,
limit_drawing_card_id INTEGER NOT NULL,
FOREIGN KEY (limit_drawing_card_id) REFERENCES limit_drawing_card(id) ON DELETE CASCADE
)
""";
// Создаем таблицу components
String createComponents = """
CREATE TABLE IF NOT EXISTS components (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
name_supplier VARCHAR(255),
project VARCHAR(255),
category VARCHAR(255),
article VARCHAR(255),
date DATE,
id_components INTEGER,
expend INTEGER,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
picking_card VARCHAR(255),
type_expend VARCHAR(255),
id_transaction VARCHAR(255),
stage_id INTEGER NOT NULL,
FOREIGN KEY (stage_id) REFERENCES stage(id) ON DELETE CASCADE
)
""";
// Выполняем создание таблиц
stmt.execute(createLimitDrawingCard);
stmt.execute(createStage);
stmt.execute(createComponents);
// Создаем индексы для оптимизации
stmt.execute("CREATE INDEX IF NOT EXISTS idx_stage_limit_drawing_card_id ON stage(limit_drawing_card_id)");
stmt.execute("CREATE INDEX IF NOT EXISTS idx_components_stage_id ON components(stage_id)");
stmt.execute("CREATE INDEX IF NOT EXISTS idx_limit_drawing_card_order_number ON limit_drawing_card(order_number)");
stmt.execute("CREATE INDEX IF NOT EXISTS idx_stage_stage_number ON stage(stage_number)");
stmt.execute("CREATE INDEX IF NOT EXISTS idx_components_id_transaction ON components(id_transaction)");
}
}
public static InitializationResult initializePostgreSQLDatabase(Properties config) {
long startTime = System.currentTimeMillis();
// Получаем параметры PostgreSQL
String postgresConnection = config.getProperty("postgresql.connection.string", "");
String postgresUsername = config.getProperty("postgresql.username", "");
String postgresPassword = config.getProperty("postgresql.password", "");
if (postgresConnection.isEmpty() || postgresUsername.isEmpty()) {
return new InitializationResult(false,
"Не все параметры подключения к PostgreSQL указаны.\n" +
"Проверьте настройки подключения к базе данных.",
System.currentTimeMillis() - startTime);
}
return initializePostgreSQLDatabase(postgresConnection, postgresUsername, postgresPassword);
}
}

View File

@@ -0,0 +1,263 @@
package org.example;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.net.URI;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
public class DsolApiService {
private final Properties config;
private final HttpClient httpClient;
private final ObjectMapper objectMapper;
public DsolApiService(Properties config) {
this.config = config;
this.httpClient = HttpClient.newHttpClient();
this.objectMapper = new ObjectMapper();
}
public List<OrderQuantity> getOrderQuantities(String basketNumber) throws IOException, InterruptedException {
String baseUrl = config.getProperty("dsol.url", "");
String apiEndpoint = config.getProperty("dsol.api.quantity", "");
if (baseUrl.isEmpty() || apiEndpoint.isEmpty()) {
throw new RuntimeException("Dsol-Factory API настройки не заданы");
}
// Разделяем номера корзин по запятой или слэшу
String[] basketNumbers = basketNumber.split("[,/]");
List<OrderQuantity> allOrders = new ArrayList<>();
// Делаем запрос для каждого номера корзины
for (String singleBasketNumber : basketNumbers) {
String trimmedBasketNumber = singleBasketNumber.trim();
if (!trimmedBasketNumber.isEmpty()) {
try {
List<OrderQuantity> ordersForBasket = getOrderQuantitiesForSingleBasket(baseUrl, apiEndpoint, trimmedBasketNumber);
allOrders.addAll(ordersForBasket);
} catch (Exception e) {
// Логируем ошибку, но продолжаем с другими корзинами
System.err.println("Error requesting basket '" + trimmedBasketNumber + "': " + e.getMessage());
}
}
}
return allOrders;
}
public List<OrderQuantity> getOrderQuantitiesForUniqueBaskets(List<String> basketNumbers) throws IOException, InterruptedException {
String baseUrl = config.getProperty("dsol.url", "");
String apiEndpoint = config.getProperty("dsol.api.quantity", "");
if (baseUrl.isEmpty() || apiEndpoint.isEmpty()) {
throw new RuntimeException("Dsol-Factory API настройки не заданы");
}
// Собираем все уникальные номера корзин
Set<String> uniqueBasketNumbers = new HashSet<>();
for (String basketNumber : basketNumbers) {
if (basketNumber != null && !basketNumber.trim().isEmpty()) {
// Разделяем каждый номер корзины по запятой или слэшу
String[] splitNumbers = basketNumber.split("[,/]");
for (String singleNumber : splitNumbers) {
String trimmedNumber = singleNumber.trim();
if (!trimmedNumber.isEmpty()) {
uniqueBasketNumbers.add(trimmedNumber);
}
}
}
}
List<OrderQuantity> allOrders = new ArrayList<>();
// Делаем запрос только для уникальных номеров корзин
for (String uniqueBasketNumber : uniqueBasketNumbers) {
try {
List<OrderQuantity> ordersForBasket = getOrderQuantitiesForSingleBasket(baseUrl, apiEndpoint, uniqueBasketNumber);
allOrders.addAll(ordersForBasket);
} catch (Exception e) {
// Логируем ошибку, но продолжаем с другими корзинами
System.err.println("Error requesting basket '" + uniqueBasketNumber + "': " + e.getMessage());
}
}
return allOrders;
}
public List<Component> getComponentsForUniqueBaskets(List<String> basketNumbers) throws IOException, InterruptedException {
String baseUrl = config.getProperty("dsol.url", "");
String apiEndpoint = config.getProperty("dsol.api.kk", "");
if (baseUrl.isEmpty() || apiEndpoint.isEmpty()) {
throw new RuntimeException("Dsol-Factory API настройки не заданы");
}
// Собираем все уникальные номера корзин
Set<String> uniqueBasketNumbers = new HashSet<>();
for (String basketNumber : basketNumbers) {
if (basketNumber != null && !basketNumber.trim().isEmpty()) {
// Разделяем каждый номер корзины по запятой или слэшу
String[] splitNumbers = basketNumber.split("[,/]");
for (String singleNumber : splitNumbers) {
String trimmedNumber = singleNumber.trim();
if (!trimmedNumber.isEmpty()) {
uniqueBasketNumbers.add(trimmedNumber);
}
}
}
}
List<Component> allComponents = new ArrayList<>();
// Делаем запрос только для уникальных номеров корзин
for (String uniqueBasketNumber : uniqueBasketNumbers) {
try {
List<Component> componentsForBasket = getComponentsForSingleBasket(baseUrl, apiEndpoint, uniqueBasketNumber);
allComponents.addAll(componentsForBasket);
} catch (Exception e) {
// Логируем ошибку, но продолжаем с другими корзинами
System.err.println("Error requesting picking card for basket '" + uniqueBasketNumber + "': " + e.getMessage());
}
}
return allComponents;
}
// Новый метод для поиска комплектовочных карт только по введенному запросу
public List<Component> getComponentsForSearchQuery(String searchQuery) throws IOException, InterruptedException {
String baseUrl = config.getProperty("dsol.url", "");
String apiEndpoint = config.getProperty("dsol.api.kk", "");
if (baseUrl.isEmpty() || apiEndpoint.isEmpty()) {
throw new RuntimeException("Dsol-Factory API настройки не заданы");
}
if (searchQuery == null || searchQuery.trim().isEmpty()) {
return new ArrayList<>();
}
List<Component> allComponents = new ArrayList<>();
// Разделяем поисковый запрос по запятой или слэшу для поиска по нескольким корзинам
String[] searchTerms = searchQuery.split("[,/]");
for (String searchTerm : searchTerms) {
String trimmedTerm = searchTerm.trim();
if (!trimmedTerm.isEmpty()) {
try {
List<Component> componentsForTerm = getComponentsForSingleBasket(baseUrl, apiEndpoint, trimmedTerm);
allComponents.addAll(componentsForTerm);
} catch (Exception e) {
// Логируем ошибку, но продолжаем с другими терминами
System.err.println("Error searching picking card for '" + trimmedTerm + "': " + e.getMessage());
}
}
}
return allComponents;
}
private List<Component> getComponentsForSingleBasket(String baseUrl, String apiEndpoint, String basketNumber) throws IOException, InterruptedException {
// Правильно кодируем параметр basketNumber для URL
String encodedBasketNumber = URLEncoder.encode(basketNumber, StandardCharsets.UTF_8);
String url = baseUrl + apiEndpoint + "?order_number=" + encodedBasketNumber;
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.GET()
.build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() != 200) {
throw new RuntimeException("Ошибка API для комплектовочной карты корзины '" + basketNumber + "': " + response.statusCode() + " - " + response.body());
}
JsonNode jsonNode = objectMapper.readTree(response.body());
if (!jsonNode.get("success").asBoolean()) {
throw new RuntimeException("API вернул ошибку для комплектовочной карты корзины '" + basketNumber + "': " + jsonNode.get("error").asText());
}
List<Component> components = new ArrayList<>();
JsonNode componentsNode = jsonNode.get("components");
if (componentsNode.isArray()) {
for (JsonNode component : componentsNode) {
String libRef = component.has("lib_ref") ? component.get("lib_ref").asText() : "";
int totalComponents = component.has("total_components") ? component.get("total_components").asInt() : 0;
if (!libRef.isEmpty() && totalComponents > 0) {
components.add(new Component(libRef, totalComponents));
}
}
}
return components;
}
private List<OrderQuantity> getOrderQuantitiesForSingleBasket(String baseUrl, String apiEndpoint, String basketNumber) throws IOException, InterruptedException {
// Правильно кодируем параметр basketNumber для URL
String encodedBasketNumber = URLEncoder.encode(basketNumber, StandardCharsets.UTF_8);
String url = baseUrl + apiEndpoint + "?order_number=" + encodedBasketNumber;
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.GET()
.build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() != 200) {
throw new RuntimeException("Ошибка API для корзины '" + basketNumber + "': " + response.statusCode() + " - " + response.body());
}
JsonNode jsonNode = objectMapper.readTree(response.body());
if (!jsonNode.get("success").asBoolean()) {
throw new RuntimeException("API вернул ошибку для корзины '" + basketNumber + "': " + jsonNode.get("error").asText());
}
List<OrderQuantity> orders = new ArrayList<>();
JsonNode serials = jsonNode.get("serials");
if (serials.isArray()) {
// Группируем по order_num и считаем количество
for (JsonNode serial : serials) {
String orderNum = serial.get("order_num").asText();
// Ищем существующий заказ
OrderQuantity existingOrder = null;
for (OrderQuantity order : orders) {
if (order.getOrderNumber().equals(orderNum)) {
existingOrder = order;
break;
}
}
if (existingOrder != null) {
existingOrder.setQuantity(existingOrder.getQuantity() + 1);
} else {
orders.add(new OrderQuantity(orderNum, 1));
}
}
}
return orders;
}
}

View File

@@ -0,0 +1,162 @@
package org.example;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
public class DsolFactoryAPI {
private final String baseUrl;
private final String kkApiPath;
private final HttpClient httpClient;
private final ObjectMapper objectMapper;
public DsolFactoryAPI(Properties config) {
this.baseUrl = config.getProperty("dsol.url");
this.kkApiPath = config.getProperty("dsol.api.kk");
this.httpClient = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.build();
this.objectMapper = new ObjectMapper();
}
public APIResponse getAPIResponse(String orderNumber) throws IOException, InterruptedException {
String url = baseUrl + kkApiPath + "?order_number=" + orderNumber;
System.out.println("=== Отладка HTTP запроса ===");
System.out.println("URL: " + url);
System.out.println("Base URL: " + baseUrl);
System.out.println("KK API Path: " + kkApiPath);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.header("Accept", "application/json")
.GET()
.build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println("HTTP Status: " + response.statusCode());
System.out.println("Response Body: " + response.body());
System.out.println("=============================");
if (response.statusCode() != 200) {
throw new IOException("API request failed with status: " + response.statusCode());
}
return parseAPIResponse(response.body());
}
private APIResponse parseAPIResponse(String jsonResponse) throws IOException {
JsonNode rootNode = objectMapper.readTree(jsonResponse);
System.out.println("=== Отладка парсинга API ответа ===");
System.out.println("Root node keys: " + rootNode.fieldNames());
APIResponse apiResponse = new APIResponse();
// Парсим данные проекта
if (rootNode.has("project") && rootNode.get("project").isObject()) {
JsonNode projectNode = rootNode.get("project");
apiResponse.projectInfo = new ProjectInfo();
apiResponse.projectInfo.docNumber = projectNode.path("doc_number").asText();
apiResponse.projectInfo.originalFilename = projectNode.path("original_filename").asText();
}
// Парсим количество компонентов
if (rootNode.has("components_count")) {
if (apiResponse.projectInfo == null) {
apiResponse.projectInfo = new ProjectInfo();
}
apiResponse.projectInfo.componentsCount = rootNode.path("components_count").asInt();
}
// Парсим компоненты
if (rootNode.has("components") && rootNode.get("components").isArray()) {
JsonNode componentsArray = rootNode.get("components");
System.out.println("Найден массив components с " + componentsArray.size() + " элементами");
// Группируем компоненты по lib_ref для устранения дублирования
Map<String, KKComponent> uniqueComponents = new HashMap<>();
for (JsonNode componentNode : componentsArray) {
String libRef = componentNode.path("lib_ref").asText();
int totalComponents = componentNode.path("total_components").asInt();
System.out.println(" Обработка компонента: lib_ref=" + libRef +
", quantity=" + componentNode.path("quantity").asInt() +
", total_components=" + totalComponents);
// Если компонент уже есть, обновляем количество на total_components
if (uniqueComponents.containsKey(libRef)) {
KKComponent existing = uniqueComponents.get(libRef);
existing.setQuantity(totalComponents);
System.out.println(" Обновлено количество для " + libRef + " на " + totalComponents);
} else {
// Создаем новый компонент
KKComponent component = new KKComponent();
component.setLibRef(libRef);
component.setName(libRef); // Используем lib_ref как имя
component.setQuantity(totalComponents); // Используем total_components
component.setOrderNumber(componentNode.path("project_order_number").asText());
uniqueComponents.put(libRef, component);
System.out.println(" Добавлен новый компонент: " + libRef + " с количеством " + totalComponents);
}
}
// Преобразуем Map в List
apiResponse.components = new ArrayList<>(uniqueComponents.values());
System.out.println("Уникальных компонентов после группировки: " + apiResponse.components.size());
}
System.out.println("Проект: doc_number=" + (apiResponse.projectInfo != null ? apiResponse.projectInfo.docNumber : "null") +
", original_filename=" + (apiResponse.projectInfo != null ? apiResponse.projectInfo.originalFilename : "null") +
", components_count=" + (apiResponse.projectInfo != null ? apiResponse.projectInfo.componentsCount : "null"));
System.out.println("Компонентов: " + (apiResponse.components != null ? apiResponse.components.size() : 0));
System.out.println("=============================");
return apiResponse;
}
public static class KKComponent {
private String libRef;
private String name;
private int quantity;
private String orderNumber;
// Getters and setters
public String getLibRef() { return libRef; }
public void setLibRef(String libRef) { this.libRef = libRef; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getQuantity() { return quantity; }
public void setQuantity(int quantity) { this.quantity = quantity; }
public String getOrderNumber() { return orderNumber; }
public void setOrderNumber(String orderNumber) { this.orderNumber = orderNumber; }
}
public static class ProjectInfo {
public String docNumber;
public String originalFilename;
public int componentsCount;
}
public static class APIResponse {
public List<KKComponent> components;
public ProjectInfo projectInfo;
}
}

View File

@@ -0,0 +1,335 @@
package org.example;
import javafx.scene.control.*;
import javafx.scene.input.Clipboard;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public class EnhancedTableView<T> extends TableView<T> {
private final int[] startRow = {-1};
private final int[] startCol = {-1};
private final boolean[] isDragging = {false};
// Для хранения расчетных данных
private java.util.Map<String, Integer> calculatedQuantities = new java.util.HashMap<>();
public EnhancedTableView() {
super();
initializeSelection();
}
// Методы для работы с расчетными данными
public void setCalculatedQuantities(java.util.Map<String, Integer> calculatedQuantities) {
this.calculatedQuantities = calculatedQuantities != null ? calculatedQuantities : new java.util.HashMap<>();
}
public java.util.Map<String, Integer> getCalculatedQuantities() {
return calculatedQuantities;
}
private void initializeSelection() {
// Настройка выделения ячеек
this.getSelectionModel().setCellSelectionEnabled(true);
this.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
this.setTableMenuButtonVisible(false);
// Отключаем возможность перетаскивания колонок
this.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
// Отключаем перетаскивание для существующих колонок
for (TableColumn<T, ?> column : this.getColumns()) {
column.setReorderable(false);
}
// Слушатель для отключения перетаскивания новых колонок
this.getColumns().addListener((javafx.collections.ListChangeListener<TableColumn<T, ?>>) change -> {
while (change.next()) {
if (change.wasAdded()) {
for (TableColumn<T, ?> column : change.getAddedSubList()) {
column.setReorderable(false);
}
}
}
});
// Слушатель для обработки сортировки и восстановления выделения ячеек
this.getSortOrder().addListener((javafx.collections.ListChangeListener<TableColumn<T, ?>>) change -> {
// Просто принудительно возвращаемся к выделению ячеек после сортировки
javafx.application.Platform.runLater(() -> {
this.getSelectionModel().setCellSelectionEnabled(true);
this.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
});
});
// Применяем стили
this.getStyleClass().add("table-view");
// Принудительно применяем стили для предотвращения проблем с цветом текста
this.setStyle("-fx-selection-bar: #0078d4; -fx-selection-bar-non-focused: #0078d4;");
// Обработчик для копирования
this.setOnKeyPressed(this::handleKeyPressed);
// Фильтр событий мыши отключен для избежания конфликтов
// Стандартное выделение JavaFX будет работать параллельно с нашим
// Обработчики мыши для выделения прямоугольником
this.setOnMousePressed(this::handleMousePressed);
this.setOnMouseDragged(this::handleMouseDragged);
this.setOnMouseReleased(this::handleMouseReleased);
// Добавляем обработку клика по пустому месту для очистки выделения
this.setOnMouseClicked(event -> {
if (event.getClickCount() == 1 && findTableCell(event) == null) {
this.getSelectionModel().clearSelection();
}
});
}
private void handleKeyPressed(KeyEvent event) {
if (event.isControlDown() && event.getCode().toString().equals("C")) {
copySelectedCells();
} else if (event.getCode().toString().equals("ESCAPE")) {
// Очищаем выделение при нажатии Escape
this.getSelectionModel().clearSelection();
}
}
private void handleMousePressed(MouseEvent event) {
TableCell<T, ?> cell = findTableCell(event);
if (cell != null) {
int row = cell.getIndex();
int col = getColumnIndex(cell.getTableColumn());
if (row >= 0 && col >= 0) {
startRow[0] = row;
startCol[0] = col;
isDragging[0] = true;
// Если не зажат Ctrl или Shift, очищаем предыдущее выделение
if (!event.isControlDown() && !event.isShiftDown()) {
this.getSelectionModel().clearSelection();
}
this.getSelectionModel().select(row, cell.getTableColumn());
}
}
}
private void handleMouseDragged(MouseEvent event) {
if (isDragging[0] && startRow[0] >= 0 && startCol[0] >= 0) {
TableCell<T, ?> cell = findTableCell(event);
if (cell != null) {
int currentRow = cell.getIndex();
int currentCol = getColumnIndex(cell.getTableColumn());
if (currentRow >= 0 && currentCol >= 0) {
selectRectangle(this, startRow[0], startCol[0], currentRow, currentCol);
}
}
}
}
private void handleMouseReleased(MouseEvent event) {
isDragging[0] = false;
}
private TableCell<T, ?> findTableCell(MouseEvent event) {
var pickResult = event.getPickResult();
var node = pickResult.getIntersectedNode();
// Ищем TableCell в иерархии узлов
while (node != null) {
if (node instanceof TableCell) {
return (TableCell<T, ?>) node;
}
node = node.getParent();
}
return null;
}
private int getColumnIndex(TableColumn<T, ?> column) {
return this.getVisibleLeafIndex(column);
}
private void selectRectangle(TableView<T> table, int startRow, int startCol, int endRow, int endCol) {
// Определяем границы прямоугольника
int minRow = Math.min(startRow, endRow);
int maxRow = Math.max(startRow, endRow);
int minCol = Math.min(startCol, endCol);
int maxCol = Math.max(startCol, endCol);
// Сначала снимаем выделение со всех ячеек
table.getSelectionModel().clearSelection();
// Затем выделяем ячейки в новом прямоугольнике
for (int row = minRow; row <= maxRow; row++) {
for (int col = minCol; col <= maxCol; col++) {
if (row >= 0 && row < table.getItems().size() &&
col >= 0 && col < table.getColumns().size()) {
table.getSelectionModel().select(row, table.getColumns().get(col));
}
}
}
}
private void copySelectedCells() {
if (this.getSelectionModel().getSelectedCells().isEmpty()) {
return;
}
StringBuilder clipboardContent = new StringBuilder();
// Берём все выделенные ячейки и сортируем
List<TablePosition> selectedCells = new ArrayList<>(this.getSelectionModel().getSelectedCells());
selectedCells.sort(
Comparator.comparingInt((TablePosition cell) -> cell.getRow())
.thenComparingInt((TablePosition cell) -> cell.getColumn())
);
// Определяем диапазон строк и колонок
int minRow = selectedCells.stream().mapToInt(TablePosition::getRow).min().orElse(0);
int maxRow = selectedCells.stream().mapToInt(TablePosition::getRow).max().orElse(0);
int minCol = selectedCells.stream().mapToInt(TablePosition::getColumn).min().orElse(0);
int maxCol = selectedCells.stream().mapToInt(TablePosition::getColumn).max().orElse(0);
// Добавляем заголовки колонок
for (int col = minCol; col <= maxCol; col++) {
if (col > minCol) {
clipboardContent.append("\t");
}
String columnName = getColumnName(col);
clipboardContent.append(columnName);
}
clipboardContent.append("\n");
// Добавляем данные
for (int row = minRow; row <= maxRow; row++) {
boolean firstCell = true;
for (int col = minCol; col <= maxCol; col++) {
if (!firstCell) {
clipboardContent.append("\t");
}
String cellValue = getCellValue(row, col);
clipboardContent.append(cellValue);
firstCell = false;
}
if (row < maxRow) {
clipboardContent.append("\n");
}
}
// Копируем в буфер обмена
Clipboard clipboard = Clipboard.getSystemClipboard();
ClipboardContent content = new ClipboardContent();
content.putString(clipboardContent.toString());
clipboard.setContent(content);
}
private String getCellValue(int row, int col) {
if (row >= 0 && row < this.getItems().size() &&
col >= 0 && col < this.getColumns().size()) {
T item = this.getItems().get(row);
TableColumn<T, ?> column = this.getColumns().get(col);
// Получаем значение через PropertyValueFactory
try {
Object value = column.getCellData(item);
if (value != null) {
String cellText = value.toString();
// Проверяем, есть ли расчетные данные для колонки "Израсходовано"
if (isConsumedColumn(column) && hasCalculatedData(item, column)) {
return getCalculatedValue(item, column, cellText);
}
return cellText;
}
return "";
} catch (Exception e) {
return "";
}
}
return "";
}
private boolean isConsumedColumn(TableColumn<T, ?> column) {
String columnName = column.getText();
return columnName != null && columnName.contains("Израсходовано");
}
private boolean hasCalculatedData(T item, TableColumn<T, ?> column) {
if (calculatedQuantities.isEmpty()) {
return false;
}
// Проверяем, есть ли расчетные данные для этого элемента
// Нужно получить имя компонента и номер корзины из item
try {
// Используем рефлексию для получения полей item
String componentName = getFieldValue(item, "name");
String basketNumber = getFieldValue(item, "basketNumber");
if (componentName != null && basketNumber != null) {
String key = componentName.trim() + "|" + basketNumber.trim();
return calculatedQuantities.containsKey(key);
}
} catch (Exception e) {
// Игнорируем ошибки рефлексии
}
return false;
}
private String getCalculatedValue(T item, TableColumn<T, ?> column, String originalValue) {
if (calculatedQuantities.isEmpty()) {
return originalValue;
}
try {
// Получаем имя компонента и номер корзины
String componentName = getFieldValue(item, "name");
String basketNumber = getFieldValue(item, "basketNumber");
if (componentName != null && basketNumber != null) {
String key = componentName.trim() + "|" + basketNumber.trim();
Integer calculatedValue = calculatedQuantities.get(key);
if (calculatedValue != null) {
return calculatedValue + " (" + originalValue + ")";
}
}
} catch (Exception e) {
// Игнорируем ошибки рефлексии
}
return originalValue;
}
private String getFieldValue(T item, String fieldName) {
try {
java.lang.reflect.Field field = item.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
Object value = field.get(item);
return value != null ? value.toString() : null;
} catch (Exception e) {
return null;
}
}
private String getColumnName(int col) {
if (col >= 0 && col < this.getColumns().size()) {
TableColumn<T, ?> column = this.getColumns().get(col);
String columnName = column.getText();
return columnName != null && !columnName.isEmpty() ? columnName : "Column " + (col + 1);
}
return "Column " + (col + 1);
}
}

View File

@@ -0,0 +1,189 @@
package org.example;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import java.io.FileOutputStream;
import java.io.IOException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
public class ExcelExporter {
public static void exportWeeklyReport(List<WeeklyReportController.WeeklyTransaction> data,
LocalDate startDate, LocalDate endDate, Stage stage,
boolean useAPI, String excelFilePath) {
try {
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("Еженедельный отчет");
// Создаем заголовки
createHeaders(sheet, startDate, endDate);
// Добавляем информацию о режиме КК
addKKInfo(sheet, useAPI, excelFilePath);
// Заполняем данными
fillData(sheet, data);
// Настраиваем лист
autoSizeColumns(sheet);
addSearchAndFilters(sheet);
// Сохраняем файл
saveFile(workbook, stage);
} catch (IOException e) {
e.printStackTrace();
}
}
private static void createHeaders(Sheet sheet, LocalDate startDate, LocalDate endDate) {
// Заголовок отчета
sheet.createRow(0).createCell(0).setCellValue("Еженедельный отчет по списанию");
// Период отчета
sheet.createRow(1).createCell(0).setCellValue(String.format("Период: %s - %s",
startDate.format(DateTimeFormatter.ofPattern("dd.MM.yyyy")),
endDate.format(DateTimeFormatter.ofPattern("dd.MM.yyyy"))));
// Пустая строка
sheet.createRow(2);
// Заголовки колонок
Row headerRow = sheet.createRow(3);
String[] headers = {"#", "Артикул", "Наименование у поставщика", "Проект", "Израсходовано",
"Тип расхода", "№ Задачи", "Комплектовочная корзина"};
// Создаем стиль для заголовков
CellStyle headerStyle = createHeaderStyle(sheet.getWorkbook());
for (int i = 0; i < headers.length; i++) {
Cell cell = headerRow.createCell(i);
cell.setCellValue(headers[i]);
cell.setCellStyle(headerStyle);
}
}
private static CellStyle createHeaderStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderTop(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
Font font = workbook.createFont();
font.setBold(true);
style.setFont(font);
return style;
}
private static void fillData(Sheet sheet, List<WeeklyReportController.WeeklyTransaction> data) {
CellStyle dataStyle = createDataStyle(sheet.getWorkbook());
int rowNum = 4;
int rowCounter = 1;
for (WeeklyReportController.WeeklyTransaction transaction : data) {
Row row = sheet.createRow(rowNum++);
// Заполняем все колонки
setNumericCellValue(row, 0, rowCounter++, dataStyle); // Номер строки как число
setCellValue(row, 1, transaction.getArticle(), dataStyle);
setCellValue(row, 2, transaction.getSupplierName(), dataStyle);
setCellValue(row, 3, transaction.getProject(), dataStyle);
setNumericCellValue(row, 4, transaction.getConsumed(), dataStyle); // Израсходовано как число
setCellValue(row, 5, transaction.getExpenseType(), dataStyle);
setCellValue(row, 6, transaction.getTaskNumber(), dataStyle);
setCellValue(row, 7, transaction.getBasketNumber(), dataStyle);
}
}
private static CellStyle createDataStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
style.setBorderBottom(BorderStyle.THIN);
style.setBorderTop(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
return style;
}
private static void setCellValue(Row row, int column, Object value, CellStyle style) {
Cell cell = row.createCell(column);
if (value != null) {
if (value instanceof Number) {
cell.setCellValue(((Number) value).doubleValue());
} else {
cell.setCellValue(value.toString());
}
}
cell.setCellStyle(style);
}
private static void setNumericCellValue(Row row, int column, int value, CellStyle style) {
Cell cell = row.createCell(column);
cell.setCellValue(value);
cell.setCellStyle(style);
}
private static void autoSizeColumns(Sheet sheet) {
for (int i = 0; i < 8; i++) { // 8 колонок (добавили колонку "#")
sheet.autoSizeColumn(i);
}
}
private static void addSearchAndFilters(Sheet sheet) {
// Автофильтры для поиска и сортировки
sheet.setAutoFilter(new CellRangeAddress(3, 3, 0, 7));
// Замораживаем заголовки
sheet.createFreezePane(0, 4);
}
private static void saveFile(Workbook workbook, Stage stage) throws IOException {
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Сохранить отчет в Excel");
fileChooser.getExtensionFilters().add(
new FileChooser.ExtensionFilter("Excel файлы", "*.xlsx")
);
// Устанавливаем имя файла по умолчанию
String defaultFileName = "Еженедельный_отчет_" +
LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) + ".xlsx";
fileChooser.setInitialFileName(defaultFileName);
java.io.File file = fileChooser.showSaveDialog(stage);
if (file != null) {
try (FileOutputStream fileOut = new FileOutputStream(file)) {
workbook.write(fileOut);
workbook.close();
System.out.println("Отчет успешно сохранен: " + file.getAbsolutePath());
}
}
}
private static void addKKInfo(Sheet sheet, boolean useAPI, String excelFilePath) {
// Добавляем информацию о режиме КК в строку 3
Row kkInfoRow = sheet.createRow(2);
Cell kkLabelCell = kkInfoRow.createCell(0);
kkLabelCell.setCellValue("Режим КК:");
kkLabelCell.setCellStyle(createHeaderStyle(sheet.getWorkbook()));
Cell kkValueCell = kkInfoRow.createCell(1);
if (useAPI) {
kkValueCell.setCellValue("API (Dsol-Factory)");
} else if (excelFilePath != null && !excelFilePath.trim().isEmpty()) {
kkValueCell.setCellValue("Файл: " + excelFilePath);
} else {
kkValueCell.setCellValue("Не указан");
}
kkValueCell.setCellStyle(createDataStyle(sheet.getWorkbook()));
}
}

View File

@@ -0,0 +1,191 @@
package org.example;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class ExcelKKReader {
public static KKData readKKFromExcel(String filePath) throws IOException {
try (FileInputStream fis = new FileInputStream(new File(filePath));
Workbook workbook = new XSSFWorkbook(fis)) {
// Ищем лист "Общая карта"
Sheet sheet = workbook.getSheet("Общая карта");
if (sheet == null) {
throw new IOException("Лист 'Общая карта' не найден в файле");
}
KKData kkData = new KKData();
// Читаем заголовочные данные
readHeaderData(sheet, kkData);
// Читаем данные компонентов
readComponentsData(sheet, kkData);
return kkData;
}
}
private static void readHeaderData(Sheet sheet, KKData kkData) {
// B1 - наименование файла
Cell fileNameCell = sheet.getRow(0).getCell(1);
if (fileNameCell != null) {
kkData.fileName = getCellValueAsString(fileNameCell);
}
// B2 - номер карты
Cell cardNumberCell = sheet.getRow(1).getCell(1);
if (cardNumberCell != null) {
kkData.cardNumber = getCellValueAsString(cardNumberCell);
}
// I2 - номер заказа
Cell orderNumberCell = sheet.getRow(1).getCell(8);
if (orderNumberCell != null) {
kkData.orderNumber = getCellValueAsString(orderNumberCell);
}
// J2 - кол-во изделий
Cell productCountCell = sheet.getRow(1).getCell(9);
if (productCountCell != null) {
kkData.productCount = getCellValueAsInt(productCountCell);
}
System.out.println("=== Заголовочные данные из Excel ===");
System.out.println("Наименование файла: " + kkData.fileName);
System.out.println("Номер карты: " + kkData.cardNumber);
System.out.println("Номер заказа: " + kkData.orderNumber);
System.out.println("Количество изделий: " + kkData.productCount);
System.out.println("=====================================");
}
private static void readComponentsData(Sheet sheet, KKData kkData) {
List<KKComponent> components = new ArrayList<>();
// Начинаем с 6 строки (индекс 5), так как 5 строка - заголовки
int rowIndex = 5;
Row headerRow = sheet.getRow(4); // 5 строка (индекс 4)
if (headerRow == null) {
System.out.println("Заголовочная строка не найдена");
return;
}
// Ищем колонки "Наименование компонента" (C5) и "Количество на заказ" (F5)
int nameColumn = -1;
int quantityColumn = -1;
for (int col = 0; col < headerRow.getLastCellNum(); col++) {
Cell cell = headerRow.getCell(col);
if (cell != null) {
String cellValue = getCellValueAsString(cell);
if ("Наименование компонента".equals(cellValue)) {
nameColumn = col;
} else if ("Количество на заказ".equals(cellValue)) {
quantityColumn = col;
}
}
}
System.out.println("Найдены колонки: Наименование=" + nameColumn + ", Количество=" + quantityColumn);
if (nameColumn == -1 || quantityColumn == -1) {
System.out.println("Не найдены необходимые колонки");
return;
}
// Читаем данные компонентов
while (rowIndex <= sheet.getLastRowNum()) {
Row row = sheet.getRow(rowIndex);
if (row == null) {
rowIndex++;
continue;
}
Cell nameCell = row.getCell(nameColumn);
Cell quantityCell = row.getCell(quantityColumn);
if (nameCell != null && quantityCell != null) {
String componentName = getCellValueAsString(nameCell);
int quantity = getCellValueAsInt(quantityCell);
// Пропускаем пустые строки
if (componentName != null && !componentName.trim().isEmpty() && quantity > 0) {
KKComponent component = new KKComponent();
component.name = componentName.trim();
component.quantity = quantity;
components.add(component);
System.out.println("Компонент: " + componentName + " = " + quantity);
}
}
rowIndex++;
}
kkData.components = components;
System.out.println("Загружено компонентов из Excel: " + components.size());
}
private static String getCellValueAsString(Cell cell) {
if (cell == null) {
return null;
}
switch (cell.getCellType()) {
case STRING:
return cell.getStringCellValue();
case NUMERIC:
if (DateUtil.isCellDateFormatted(cell)) {
return cell.getDateCellValue().toString();
} else {
return String.valueOf((int) cell.getNumericCellValue());
}
case BOOLEAN:
return String.valueOf(cell.getBooleanCellValue());
case FORMULA:
return cell.getCellFormula();
default:
return null;
}
}
private static int getCellValueAsInt(Cell cell) {
if (cell == null) {
return 0;
}
switch (cell.getCellType()) {
case NUMERIC:
return (int) cell.getNumericCellValue();
case STRING:
try {
return Integer.parseInt(cell.getStringCellValue().trim());
} catch (NumberFormatException e) {
return 0;
}
default:
return 0;
}
}
public static class KKData {
public String fileName;
public String cardNumber;
public String orderNumber;
public int productCount;
public List<KKComponent> components;
}
public static class KKComponent {
public String name;
public int quantity;
}
}

View File

@@ -0,0 +1,118 @@
package org.example;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.TextAlignment;
import java.util.Properties;
public class HomeController {
private VBox view;
private Properties config;
private Runnable onStartWork;
public HomeController(Properties config) {
this.config = config;
createView();
}
public void setOnStartWork(Runnable onStartWork) {
this.onStartWork = onStartWork;
}
private void createView() {
view = new VBox();
view.setSpacing(30);
view.setPadding(new Insets(40));
view.setStyle("-fx-background-color: #f8f9fa;");
view.setAlignment(Pos.CENTER);
// Заголовок
Label titleLabel = new Label("AccessLZK");
titleLabel.setStyle("-fx-font-family: 'Segoe UI'; -fx-font-size: 48px; -fx-font-weight: bold; -fx-text-fill: #2c3e50;");
titleLabel.setTextAlignment(TextAlignment.CENTER);
// Подзаголовок
Label subtitleLabel = new Label("Система для работы со складскими данными");
subtitleLabel.setStyle("-fx-font-family: 'Segoe UI'; -fx-font-size: 18px; -fx-text-fill: #7f8c8d;");
subtitleLabel.setTextAlignment(TextAlignment.CENTER);
// Информационные карточки
VBox cardsContainer = new VBox();
cardsContainer.setSpacing(20);
cardsContainer.setAlignment(Pos.CENTER);
// Карточка 1 - Назначение системы
VBox purposeCard = createInfoCard(
"🎯 Назначение системы",
"Система AccessLZK создана специально для сотрудников склада\n" +
"для упрощения работы со складскими данными.\n\n" +
"Позволяет быстро находить информацию о комплектовочных корзинах,\n" +
"анализировать расход материалов и контролировать складские операции."
);
// Карточка 2 - Основные функции
VBox functionsCard = createInfoCard(
"📋 Основные функции",
"• Поиск и анализ комплектовочных корзин\n" +
"• Просмотр детальной информации о транзакциях\n" +
"• Отслеживание расхода материалов по проектам\n" +
"• Экспорт отчетов для дальнейшего анализа\n" +
"• Удобная работа с большими объемами данных"
);
// Карточка 3 - Преимущества
VBox benefitsCard = createInfoCard(
"✨ Преимущества для склада",
"• Быстрый доступ к складской информации\n" +
"• Упрощение рутинных операций\n" +
"• Снижение времени на поиск данных\n" +
"• Повышение эффективности работы склада\n" +
"• Современный и понятный интерфейс"
);
cardsContainer.getChildren().addAll(purposeCard, functionsCard, benefitsCard);
// Кнопка быстрого доступа
Button quickStartButton = new Button("🚀 Начать работу");
quickStartButton.setStyle("-fx-background-color: #3498db; -fx-text-fill: white; -fx-font-family: 'Segoe UI'; -fx-font-weight: bold; -fx-font-size: 16px; -fx-padding: 15 30; -fx-background-radius: 8; -fx-cursor: hand;");
quickStartButton.setPrefWidth(200);
quickStartButton.setOnAction(e -> {
if (onStartWork != null) {
onStartWork.run();
}
});
// Версия
Label versionLabel = new Label("Версия 1.0.0");
versionLabel.setStyle("-fx-font-family: 'Segoe UI'; -fx-font-size: 12px; -fx-text-fill: #95a5a6;");
view.getChildren().addAll(titleLabel, subtitleLabel, cardsContainer, quickStartButton, versionLabel);
}
private VBox createInfoCard(String title, String content) {
VBox card = new VBox();
card.setSpacing(10);
card.setPadding(new Insets(20));
card.setStyle("-fx-background-color: white; -fx-background-radius: 10; -fx-border-color: #e1e8ed; -fx-border-width: 1; -fx-border-radius: 10; -fx-effect: dropshadow(gaussian, rgba(0,0,0,0.1), 5, 0, 0, 2);");
card.setPrefWidth(600);
card.setMaxWidth(600);
Label titleLabel = new Label(title);
titleLabel.setStyle("-fx-font-family: 'Segoe UI'; -fx-font-size: 18px; -fx-font-weight: bold; -fx-text-fill: #2c3e50;");
Label contentLabel = new Label(content);
contentLabel.setStyle("-fx-font-family: 'Segoe UI'; -fx-font-size: 14px; -fx-text-fill: #34495e; -fx-line-spacing: 2;");
contentLabel.setWrapText(true);
card.getChildren().addAll(titleLabel, contentLabel);
return card;
}
public VBox getView() {
return view;
}
}

View File

@@ -0,0 +1,918 @@
package org.example;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import javafx.stage.Stage;
import javafx.stage.FileChooser;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.sql.*;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import javafx.application.Platform;
import javafx.scene.control.Alert;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Font;
public class LZKExcelGenerator {
public static void generateLZKDocument(Properties config, String orderNumber, int stageNumber, String stageResponsible, String stageTimestamp, int stageId) {
generateLZKDocument(config, orderNumber, stageNumber, stageResponsible, stageTimestamp, stageId, false, null);
}
public static void generateLZKDocument(Properties config, String orderNumber, int stageNumber, String stageResponsible, String stageTimestamp, int stageId, boolean useAPI, String excelFilePath) {
try (Workbook workbook = new XSSFWorkbook()) {
Sheet sheet = workbook.createSheet("ЛЗК");
// Создаем стили
CellStyle headerStyle = createHeaderStyle(workbook);
CellStyle titleStyle = createTitleStyle(workbook);
CellStyle labelStyle = createLabelStyle(workbook);
CellStyle dataStyle = createDataStyle(workbook);
// B1:F1 - Заголовок "Лимитно-заборная карта № {номер заказа}.{номер этапа}"
CellRangeAddress titleRange = new CellRangeAddress(0, 0, 1, 5);
sheet.addMergedRegion(titleRange);
Cell titleCell = sheet.createRow(0).createCell(1);
titleCell.setCellValue("Лимитно-заборная карта № " + orderNumber + "." + stageNumber);
titleCell.setCellStyle(titleStyle);
// F3 - "Отправитель", G3 - "Получатель"
Row row3 = sheet.createRow(2);
Cell senderCell = row3.createCell(5);
senderCell.setCellValue("Отправитель");
senderCell.setCellStyle(headerStyle);
Cell receiverCell = row3.createCell(6);
receiverCell.setCellValue("Получатель");
receiverCell.setCellStyle(headerStyle);
// D4 - "Дата создания ЛЗК", E4 - "Вид деятельности", F4 - "Структурное подразделение", G4 - "Структурное подразделение"
Row row4 = sheet.createRow(3);
Cell dateLabelCell = row4.createCell(3);
dateLabelCell.setCellValue("Дата создания ЛЗК");
dateLabelCell.setCellStyle(headerStyle);
Cell activityLabelCell = row4.createCell(4);
activityLabelCell.setCellValue("Вид деятельности");
activityLabelCell.setCellStyle(headerStyle);
Cell deptLabelCell1 = row4.createCell(5);
deptLabelCell1.setCellValue("Структурное подразделение");
deptLabelCell1.setCellStyle(headerStyle);
Cell deptLabelCell2 = row4.createCell(6);
deptLabelCell2.setCellValue("Структурное подразделение");
deptLabelCell2.setCellStyle(headerStyle);
// D5 - дата, E5 - "Заказ №{номер заказа}", F5 - "Склад", G5 - "Производство"
Row row5 = sheet.createRow(4);
Cell dateCell = row5.createCell(3);
dateCell.setCellValue(stageTimestamp);
dateCell.setCellStyle(dataStyle);
Cell orderCell = row5.createCell(4);
orderCell.setCellValue("Заказ №" + orderNumber);
orderCell.setCellStyle(dataStyle);
Cell warehouseCell = row5.createCell(5);
warehouseCell.setCellValue("Склад");
warehouseCell.setCellStyle(dataStyle);
Cell productionCell = row5.createCell(6);
productionCell.setCellValue("Производство");
productionCell.setCellStyle(dataStyle);
// E7:G7 - "Комплектовочная карта"
CellRangeAddress pickingCardRange = new CellRangeAddress(6, 6, 4, 6);
sheet.addMergedRegion(pickingCardRange);
Cell pickingCardCell = sheet.createRow(6).createCell(4);
pickingCardCell.setCellValue("Комплектовочная карта");
pickingCardCell.setCellStyle(headerStyle);
// E8 - "Номер", F8 - "Наименование", G8 - "Количество изделий"
Row row8 = sheet.createRow(7);
Cell numberCell = row8.createCell(4);
numberCell.setCellValue("Номер");
numberCell.setCellStyle(headerStyle);
Cell nameCell = row8.createCell(5);
nameCell.setCellValue("Наименование");
nameCell.setCellStyle(headerStyle);
Cell quantityCell = row8.createCell(6);
quantityCell.setCellValue("Количество изделий");
quantityCell.setCellStyle(headerStyle);
// E9 - project.doc_number, F9 - project.original_filename, G9 - components_count
Row row9 = sheet.createRow(8);
Cell docNumberCell = row9.createCell(4);
docNumberCell.setCellValue(""); // Будет заполнено из API
docNumberCell.setCellStyle(dataStyle);
Cell filenameCell = row9.createCell(5);
filenameCell.setCellValue(""); // Будет заполнено из API
filenameCell.setCellStyle(dataStyle);
Cell componentsCountCell = row9.createCell(6);
componentsCountCell.setCellValue(""); // Будет заполнено из API
componentsCountCell.setCellStyle(dataStyle);
// A12 - "№ п/п", B12 - "Наименование материала", C12 - "Код/Артикул", D12 - "Наименование склада/проекта", E12 - "Наименование компонента по КК", F12 - "Затребованное количество", G12 - "Количество"
Row headerRow = sheet.createRow(11);
String[] headers = {
"№ п/п", "Наименование материала", "Код/Артикул", "Наименование склада/проекта",
"Наименование компонента по КК", "Затребованное количество", "Количество"
};
for (int i = 0; i < headers.length; i++) {
Cell cell = headerRow.createCell(i);
cell.setCellValue(headers[i]);
cell.setCellStyle(headerStyle);
}
// Данные проекта будут заполнены в loadComponentsWithAPI
// Загружаем данные компонентов из PostgreSQL
loadComponentsData(sheet, config, stageId, dataStyle, useAPI, orderNumber, excelFilePath);
// Автоматически подгоняем ширину колонок
for (int i = 0; i < 7; i++) {
sheet.autoSizeColumn(i);
}
// Сохраняем файл
if (excelFilePath != null && !excelFilePath.trim().isEmpty()) {
// Режим Excel КК файла - показываем диалог сохранения (не перезаписываем исходный файл!)
saveWorkbook(workbook, orderNumber, stageNumber);
} else {
// Режим API - показываем диалог сохранения
saveWorkbook(workbook, orderNumber, stageNumber);
}
} catch (Exception e) {
throw new RuntimeException("Ошибка при создании Excel документа: " + e.getMessage(), e);
}
}
private static void loadComponentsData(Sheet sheet, Properties config, int stageId, CellStyle dataStyle, boolean useAPI, String orderNumber, String excelFilePath) {
if (useAPI) {
// Режим с API - загружаем все данные из API и дополняем PostgreSQL
loadComponentsWithAPI(sheet, config, stageId, dataStyle, orderNumber);
} else if (excelFilePath != null && !excelFilePath.trim().isEmpty()) {
// Режим с Excel файлом - загружаем данные из Excel КК
loadComponentsFromExcel(sheet, config, stageId, dataStyle, excelFilePath);
} else {
// Режим без API - только PostgreSQL
loadComponentsFromPostgreSQL(sheet, config, stageId, dataStyle);
}
}
private static void loadComponentsWithAPI(Sheet sheet, Properties config, int stageId, CellStyle dataStyle, String orderNumber) {
// Создаем стили для подсветки
Workbook workbook = sheet.getWorkbook();
CellStyle greenStyle = createGreenStyle(workbook);
CellStyle yellowStyle = createYellowStyle(workbook);
CellStyle redStyle = createRedStyle(workbook);
CellStyle redTextStyle = createRedTextStyle(workbook);
// 1. Загружаем данные из API
DsolFactoryAPI.APIResponse apiResponse = null;
try {
System.out.println("=== Загрузка данных из API ===");
DsolFactoryAPI api = new DsolFactoryAPI(config);
apiResponse = api.getAPIResponse(orderNumber);
System.out.println("Получено компонентов из API: " + (apiResponse.components != null ? apiResponse.components.size() : 0));
} catch (Exception e) {
System.err.println("Ошибка при получении данных из API: " + e.getMessage());
e.printStackTrace();
// Показываем окно ошибки пользователю
Platform.runLater(() -> {
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setTitle("Ошибка API");
alert.setHeaderText("Не удалось подключиться к Dsol-Factory API");
alert.setContentText("Проверьте:\n" +
"1. Доступность сервера " + config.getProperty("dsol.url") + "\n" +
"2. Правильность настроек в config.properties\n" +
"3. Сетевое подключение\n\n" +
"Детали ошибки: " + e.getMessage());
alert.showAndWait();
});
return;
}
if (apiResponse == null || apiResponse.components == null || apiResponse.components.isEmpty()) {
System.out.println("API не вернул данных, используем только PostgreSQL");
loadComponentsFromPostgreSQL(sheet, config, stageId, dataStyle);
return;
}
// 2. Загружаем данные из PostgreSQL
List<PostgreSQLComponent> pgComponents = new ArrayList<>();
try (Connection conn = getConnection(config)) {
if (conn != null) {
String sql = "SELECT c.name, c.article, c.project, c.category, c.name_supplier, c.expend FROM components c WHERE c.stage_id = ? ORDER BY c.id";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setInt(1, stageId);
try (ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
PostgreSQLComponent pgComp = new PostgreSQLComponent();
pgComp.name = rs.getString("name");
pgComp.article = rs.getString("article");
pgComp.project = rs.getString("project");
pgComp.expend = rs.getInt("expend");
pgComponents.add(pgComp);
}
}
}
}
} catch (SQLException e) {
System.err.println("Ошибка при загрузке данных из PostgreSQL: " + e.getMessage());
e.printStackTrace();
}
System.out.println("Загружено компонентов из PostgreSQL: " + pgComponents.size());
// 3. Сортируем компоненты: сначала найденные в PostgreSQL, потом остальные
List<DsolFactoryAPI.KKComponent> sortedComponents = new ArrayList<>();
List<DsolFactoryAPI.KKComponent> foundComponents = new ArrayList<>();
List<DsolFactoryAPI.KKComponent> notFoundComponents = new ArrayList<>();
for (DsolFactoryAPI.KKComponent kkComp : apiResponse.components) {
boolean found = false;
for (PostgreSQLComponent pgComp : pgComponents) {
if (pgComp.name != null && pgComp.name.equals(kkComp.getLibRef())) {
foundComponents.add(kkComp);
found = true;
System.out.println("✓ Компонент найден в PostgreSQL: " + kkComp.getLibRef());
break;
}
}
if (!found) {
notFoundComponents.add(kkComp);
}
}
// Сначала добавляем найденные, потом остальные
sortedComponents.addAll(foundComponents);
sortedComponents.addAll(notFoundComponents);
System.out.println("Найдено в PostgreSQL: " + foundComponents.size() + " компонентов");
System.out.println("Не найдено в PostgreSQL: " + notFoundComponents.size() + " компонентов");
System.out.println("Всего компонентов: " + sortedComponents.size());
// 4. Создаем строки в Excel на основе отсортированных данных
int rowNum = 12;
int itemNumber = 1;
for (DsolFactoryAPI.KKComponent kkComp : sortedComponents) {
Row row = sheet.createRow(rowNum++);
// Ищем соответствие в PostgreSQL по названию
PostgreSQLComponent matchingPgComp = null;
for (PostgreSQLComponent pgComp : pgComponents) {
if (pgComp.name != null && pgComp.name.equals(kkComp.getLibRef())) {
matchingPgComp = pgComp;
break;
}
}
// Заполняем строку
// № п/п
Cell itemCell = row.createCell(0);
itemCell.setCellValue(itemNumber++);
itemCell.setCellStyle(dataStyle);
// Наименование материала
Cell nameCell = row.createCell(1);
if (matchingPgComp != null) {
nameCell.setCellValue(matchingPgComp.name);
System.out.println("✓ Найдено соответствие: " + kkComp.getLibRef() + " -> " + matchingPgComp.article);
} else {
nameCell.setCellValue(""); // Пустое, если нет в PostgreSQL
}
nameCell.setCellStyle(dataStyle);
// Код/Артикул
Cell articleCell = row.createCell(2);
if (matchingPgComp != null) {
articleCell.setCellValue(matchingPgComp.article);
} else {
articleCell.setCellValue(""); // Пустое, если нет в PostgreSQL
}
articleCell.setCellStyle(dataStyle);
// Наименование склада/проекта
Cell projectCell = row.createCell(3);
if (matchingPgComp != null) {
projectCell.setCellValue(matchingPgComp.project);
} else {
projectCell.setCellValue(""); // Пустое, если нет в PostgreSQL
}
projectCell.setCellStyle(dataStyle);
// Наименование компонента по КК (всегда из API)
Cell kkCell = row.createCell(4);
kkCell.setCellValue(kkComp.getName());
kkCell.setCellStyle(dataStyle);
// Затребованное количество (всегда из API) - числовое значение
Cell requestedCell = row.createCell(5);
requestedCell.setCellValue(kkComp.getQuantity()); // Числовое значение, не строка
requestedCell.setCellStyle(dataStyle);
// Количество
Cell quantityCell = row.createCell(6);
int requestedQuantity = kkComp.getQuantity(); // Затребованное количество из API
int actualQuantity = 0; // Количество из PostgreSQL
if (matchingPgComp != null) {
actualQuantity = matchingPgComp.expend;
quantityCell.setCellValue(actualQuantity);
} else {
quantityCell.setCellValue(0); // Числовое значение 0, если нет в PostgreSQL
}
// Применяем подсветку в зависимости от соотношения значений
if (matchingPgComp == null) {
// Компонент не найден в PostgreSQL - красный шрифт
quantityCell.setCellStyle(redTextStyle);
} else if (requestedQuantity == actualQuantity) {
// Идентичные значения - зеленый фон
quantityCell.setCellStyle(greenStyle);
} else if (requestedQuantity > actualQuantity) {
// Затребованное больше фактического - желтый фон
quantityCell.setCellStyle(yellowStyle);
} else if (actualQuantity > requestedQuantity) {
// Фактическое больше затребованного - красный фон
quantityCell.setCellStyle(redStyle);
} else {
// В ином случае - обычный стиль
quantityCell.setCellStyle(dataStyle);
}
}
System.out.println("Создано строк в Excel: " + (itemNumber - 1));
// Заполняем данные проекта в строку 9
if (apiResponse.projectInfo != null) {
Row row9 = sheet.getRow(8);
if (row9 != null) {
// E9 - project.doc_number
Cell docNumberCell = row9.getCell(4);
if (docNumberCell != null) {
docNumberCell.setCellValue(apiResponse.projectInfo.docNumber);
}
// F9 - project.original_filename
Cell filenameCell = row9.getCell(5);
if (filenameCell != null) {
filenameCell.setCellValue(apiResponse.projectInfo.originalFilename);
}
// G9 - components_count
Cell componentsCountCell = row9.getCell(6);
if (componentsCountCell != null) {
componentsCountCell.setCellValue(apiResponse.projectInfo.componentsCount);
}
}
}
}
private static void loadComponentsFromExcel(Sheet sheet, Properties config, int stageId, CellStyle dataStyle, String excelFilePath) {
try {
System.out.println("=== Загрузка данных из Excel КК ===");
ExcelKKReader.KKData kkData = ExcelKKReader.readKKFromExcel(excelFilePath);
if (kkData == null || kkData.components == null || kkData.components.isEmpty()) {
System.out.println("Excel файл не содержит данных компонентов");
return;
}
// Загружаем данные из PostgreSQL для сравнения
List<PostgreSQLComponent> pgComponents = new ArrayList<>();
try (Connection conn = getConnection(config)) {
if (conn != null) {
String sql = "SELECT c.name, c.article, c.project, c.category, c.name_supplier, c.expend FROM components c WHERE c.stage_id = ? ORDER BY c.id";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setInt(1, stageId);
try (ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
PostgreSQLComponent pgComp = new PostgreSQLComponent();
pgComp.name = rs.getString("name");
pgComp.article = rs.getString("article");
pgComp.project = rs.getString("project");
pgComp.expend = rs.getInt("expend");
pgComponents.add(pgComp);
}
}
}
}
} catch (SQLException e) {
System.err.println("Ошибка при загрузке данных из PostgreSQL: " + e.getMessage());
e.printStackTrace();
}
System.out.println("Загружено компонентов из PostgreSQL: " + pgComponents.size());
// Создаем стили для подсветки
Workbook workbook = sheet.getWorkbook();
CellStyle greenStyle = createGreenStyle(workbook);
CellStyle yellowStyle = createYellowStyle(workbook);
CellStyle redStyle = createRedStyle(workbook);
CellStyle redTextStyle = createRedTextStyle(workbook);
// Сортируем компоненты: сначала найденные в PostgreSQL, потом остальные
List<ExcelKKReader.KKComponent> sortedComponents = new ArrayList<>();
List<ExcelKKReader.KKComponent> foundComponents = new ArrayList<>();
List<ExcelKKReader.KKComponent> notFoundComponents = new ArrayList<>();
for (ExcelKKReader.KKComponent excelComp : kkData.components) {
boolean found = false;
for (PostgreSQLComponent pgComp : pgComponents) {
if (pgComp.name != null && pgComp.name.equals(excelComp.name)) {
foundComponents.add(excelComp);
found = true;
System.out.println("✓ Компонент найден в PostgreSQL: " + excelComp.name);
break;
}
}
if (!found) {
notFoundComponents.add(excelComp);
}
}
// Сначала добавляем найденные, потом остальные
sortedComponents.addAll(foundComponents);
sortedComponents.addAll(notFoundComponents);
System.out.println("Найдено в PostgreSQL: " + foundComponents.size() + " компонентов");
System.out.println("Не найдено в PostgreSQL: " + notFoundComponents.size() + " компонентов");
System.out.println("Всего компонентов: " + sortedComponents.size());
// Создаем строки в Excel на основе отсортированных данных
int rowNum = 12;
int itemNumber = 1;
for (ExcelKKReader.KKComponent excelComp : sortedComponents) {
Row row = sheet.createRow(rowNum++);
// Ищем соответствие в PostgreSQL по названию
PostgreSQLComponent matchingPgComp = null;
for (PostgreSQLComponent pgComp : pgComponents) {
if (pgComp.name != null && pgComp.name.equals(excelComp.name)) {
matchingPgComp = pgComp;
break;
}
}
// Заполняем строку
// № п/п
Cell itemCell = row.createCell(0);
itemCell.setCellValue(itemNumber++);
itemCell.setCellStyle(dataStyle);
// Наименование материала
Cell nameCell = row.createCell(1);
if (matchingPgComp != null) {
nameCell.setCellValue(matchingPgComp.name);
} else {
nameCell.setCellValue(""); // Пустое, если нет в PostgreSQL
}
nameCell.setCellStyle(dataStyle);
// Код/Артикул
Cell articleCell = row.createCell(2);
if (matchingPgComp != null) {
articleCell.setCellValue(matchingPgComp.article);
} else {
articleCell.setCellValue(""); // Пустое, если нет в PostgreSQL
}
articleCell.setCellStyle(dataStyle);
// Наименование склада/проекта
Cell projectCell = row.createCell(3);
if (matchingPgComp != null) {
projectCell.setCellValue(matchingPgComp.project);
} else {
projectCell.setCellValue(""); // Пустое, если нет в PostgreSQL
}
projectCell.setCellStyle(dataStyle);
// Наименование компонента по КК (всегда из Excel)
Cell kkCell = row.createCell(4);
kkCell.setCellValue(excelComp.name);
kkCell.setCellStyle(dataStyle);
// Затребованное количество (всегда из Excel) - числовое значение
Cell requestedCell = row.createCell(5);
requestedCell.setCellValue(excelComp.quantity); // Числовое значение, не строка
requestedCell.setCellStyle(dataStyle);
// Количество
Cell quantityCell = row.createCell(6);
int requestedQuantity = excelComp.quantity; // Затребованное количество из Excel
int actualQuantity = 0; // Количество из PostgreSQL
if (matchingPgComp != null) {
actualQuantity = matchingPgComp.expend;
quantityCell.setCellValue(actualQuantity);
} else {
quantityCell.setCellValue(0); // Числовое значение 0, если нет в PostgreSQL
}
// Применяем подсветку в зависимости от соотношения значений
if (matchingPgComp == null) {
// Компонент не найден в PostgreSQL - красный шрифт
quantityCell.setCellStyle(redTextStyle);
} else if (requestedQuantity == actualQuantity) {
// Идентичные значения - зеленый фон
quantityCell.setCellStyle(greenStyle);
} else if (requestedQuantity > actualQuantity) {
// Затребованное больше фактического - желтый фон
quantityCell.setCellStyle(yellowStyle);
} else if (actualQuantity > requestedQuantity) {
// Фактическое больше затребованного - красный фон
quantityCell.setCellStyle(redStyle);
} else {
// В ином случае - обычный стиль
quantityCell.setCellStyle(dataStyle);
}
}
System.out.println("Создано строк в Excel: " + (itemNumber - 1));
} catch (Exception e) {
System.err.println("Ошибка при загрузке данных из Excel: " + e.getMessage());
e.printStackTrace();
}
}
private static void loadComponentsFromPostgreSQL(Sheet sheet, Properties config, int stageId, CellStyle dataStyle) {
// В режиме без API подсветка не применяется, так как нет данных для сравнения
try (Connection conn = getConnection(config)) {
if (conn != null) {
String sql = "SELECT c.name, c.article, c.project, c.category, c.name_supplier, c.expend FROM components c WHERE c.stage_id = ? ORDER BY c.id";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setInt(1, stageId);
try (ResultSet rs = stmt.executeQuery()) {
int rowNum = 12;
int itemNumber = 1;
while (rs.next()) {
Row row = sheet.createRow(rowNum++);
// № п/п
Cell itemCell = row.createCell(0);
itemCell.setCellValue(itemNumber++);
itemCell.setCellStyle(dataStyle);
// Наименование материала
Cell nameCell = row.createCell(1);
nameCell.setCellValue(rs.getString("name"));
nameCell.setCellStyle(dataStyle);
// Код/Артикул
Cell articleCell = row.createCell(2);
articleCell.setCellValue(rs.getString("article"));
articleCell.setCellStyle(dataStyle);
// Наименование склада/проекта
Cell projectCell = row.createCell(3);
projectCell.setCellValue(rs.getString("project"));
projectCell.setCellStyle(dataStyle);
// Наименование компонента по КК (пустое)
Cell kkCell = row.createCell(4);
kkCell.setCellValue("");
kkCell.setCellStyle(dataStyle);
// Затребованное количество (пустое) - числовое значение
Cell requestedCell = row.createCell(5);
requestedCell.setCellValue(0); // Числовое значение 0 вместо пустой строки
requestedCell.setCellStyle(dataStyle);
// Количество
Cell quantityCell = row.createCell(6);
quantityCell.setCellValue(rs.getInt("expend"));
quantityCell.setCellStyle(dataStyle);
}
}
}
}
} catch (SQLException e) {
System.err.println("Ошибка при загрузке данных компонентов: " + e.getMessage());
e.printStackTrace();
}
}
private static class PostgreSQLComponent {
String name;
String article;
String project;
int expend;
}
private static CellStyle createHeaderStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
Font font = workbook.createFont();
font.setBold(true);
font.setFontHeightInPoints((short) 12);
style.setFont(font);
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderTop(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
return style;
}
private static CellStyle createTitleStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
Font font = workbook.createFont();
font.setBold(true);
font.setFontHeightInPoints((short) 16);
style.setFont(font);
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
return style;
}
private static CellStyle createLabelStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
Font font = workbook.createFont();
font.setBold(true);
font.setFontHeightInPoints((short) 10);
style.setFont(font);
style.setAlignment(HorizontalAlignment.LEFT);
style.setVerticalAlignment(VerticalAlignment.CENTER);
return style;
}
private static CellStyle createDataStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
Font font = workbook.createFont();
font.setFontHeightInPoints((short) 10);
style.setFont(font);
style.setAlignment(HorizontalAlignment.LEFT);
style.setVerticalAlignment(VerticalAlignment.CENTER);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderTop(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
return style;
}
// Стили для подсветки колонки "Количество"
private static CellStyle createGreenStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
Font font = workbook.createFont();
font.setFontHeightInPoints((short) 10);
style.setFont(font);
style.setAlignment(HorizontalAlignment.LEFT);
style.setVerticalAlignment(VerticalAlignment.CENTER);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderTop(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setFillForegroundColor(IndexedColors.LIGHT_GREEN.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
return style;
}
private static CellStyle createYellowStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
Font font = workbook.createFont();
font.setFontHeightInPoints((short) 10);
style.setFont(font);
style.setAlignment(HorizontalAlignment.LEFT);
style.setVerticalAlignment(VerticalAlignment.CENTER);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderTop(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setFillForegroundColor(IndexedColors.YELLOW.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
return style;
}
private static CellStyle createRedStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
Font font = workbook.createFont();
font.setFontHeightInPoints((short) 10);
style.setFont(font);
style.setAlignment(HorizontalAlignment.LEFT);
style.setVerticalAlignment(VerticalAlignment.CENTER);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderTop(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setFillForegroundColor(IndexedColors.RED.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
return style;
}
private static CellStyle createRedTextStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
Font font = workbook.createFont();
font.setFontHeightInPoints((short) 10);
font.setColor(IndexedColors.RED.getIndex());
style.setFont(font);
style.setAlignment(HorizontalAlignment.LEFT);
style.setVerticalAlignment(VerticalAlignment.CENTER);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderTop(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
return style;
}
private static Connection getConnection(Properties config) {
try {
// Явно загружаем PostgreSQL драйвер
Class.forName("org.postgresql.Driver");
String connectionString = config.getProperty("postgresql.connection.string");
String username = config.getProperty("postgresql.username");
String password = config.getProperty("postgresql.password");
if (connectionString == null || connectionString.isEmpty()) {
System.err.println("Ошибка: Не настроено подключение к PostgreSQL в config.properties");
return null;
}
return DriverManager.getConnection(connectionString, username, password);
} catch (ClassNotFoundException e) {
System.err.println("Ошибка: PostgreSQL драйвер не найден в classpath");
e.printStackTrace();
return null;
} catch (SQLException e) {
System.err.println("Ошибка подключения к PostgreSQL: " + e.getMessage());
e.printStackTrace();
return null;
}
}
public static void generateLZKDocumentToFile(Properties config, String orderNumber, int stageNumber, String stageResponsible, String stageTimestamp, int stageId, String filePath) {
try (Workbook workbook = new XSSFWorkbook()) {
Sheet sheet = workbook.createSheet("ЛЗК");
// Создаем стили
CellStyle headerStyle = createHeaderStyle(workbook);
CellStyle titleStyle = createTitleStyle(workbook);
CellStyle labelStyle = createLabelStyle(workbook);
CellStyle dataStyle = createDataStyle(workbook);
// B1:F1 - Заголовок "Лимитно-заборная карта № {номер заказа}.{номер этапа}"
CellRangeAddress titleRange = new CellRangeAddress(0, 0, 1, 5);
sheet.addMergedRegion(titleRange);
Cell titleCell = sheet.createRow(0).createCell(1);
titleCell.setCellValue("Лимитно-заборная карта № " + orderNumber + "." + stageNumber);
titleCell.setCellStyle(titleStyle);
// F3 - "Отправитель", G3 - "Получатель"
Row row3 = sheet.createRow(2);
Cell senderCell = row3.createCell(5);
senderCell.setCellValue("Отправитель");
senderCell.setCellStyle(headerStyle);
Cell receiverCell = row3.createCell(6);
receiverCell.setCellValue("Получатель");
receiverCell.setCellStyle(headerStyle);
// D4 - "Дата создания ЛЗК", E4 - "Вид деятельности", F4 - "Структурное подразделение", G4 - "Структурное подразделение"
Row row4 = sheet.createRow(3);
Cell dateLabelCell = row4.createCell(3);
dateLabelCell.setCellValue("Дата создания ЛЗК");
dateLabelCell.setCellStyle(headerStyle);
Cell activityLabelCell = row4.createCell(4);
activityLabelCell.setCellValue("Вид деятельности");
activityLabelCell.setCellStyle(headerStyle);
Cell deptLabelCell1 = row4.createCell(5);
deptLabelCell1.setCellValue("Структурное подразделение");
deptLabelCell1.setCellStyle(headerStyle);
Cell deptLabelCell2 = row4.createCell(6);
deptLabelCell2.setCellValue("Структурное подразделение");
deptLabelCell2.setCellStyle(headerStyle);
// D5 - дата, E5 - "Заказ №{номер заказа}", F5 - "Склад", G5 - "Производство"
Row row5 = sheet.createRow(4);
Cell dateCell = row5.createCell(3);
dateCell.setCellValue(stageTimestamp);
dateCell.setCellStyle(dataStyle);
Cell orderCell = row5.createCell(4);
orderCell.setCellValue("Заказ №" + orderNumber);
orderCell.setCellStyle(dataStyle);
Cell warehouseCell = row5.createCell(5);
warehouseCell.setCellValue("Склад");
warehouseCell.setCellStyle(dataStyle);
Cell productionCell = row5.createCell(6);
productionCell.setCellValue("Производство");
productionCell.setCellStyle(dataStyle);
// E7:G7 - "Комплектовочная карта"
CellRangeAddress pickingCardRange = new CellRangeAddress(6, 6, 4, 6);
sheet.addMergedRegion(pickingCardRange);
Cell pickingCardCell = sheet.createRow(6).createCell(4);
pickingCardCell.setCellValue("Комплектовочная карта");
pickingCardCell.setCellStyle(headerStyle);
// E8 - "Номер", F8 - "Наименование", G8 - "Количество изделий"
Row row8 = sheet.createRow(7);
Cell numberCell = row8.createCell(4);
numberCell.setCellValue("Номер");
numberCell.setCellStyle(headerStyle);
Cell nameCell = row8.createCell(5);
nameCell.setCellValue("Наименование");
nameCell.setCellStyle(headerStyle);
Cell quantityCell = row8.createCell(6);
quantityCell.setCellValue("Количество изделий");
quantityCell.setCellStyle(headerStyle);
// E9 - project.doc_number, F9 - project.original_filename, G9 - components_count
Row row9 = sheet.createRow(8);
Cell docNumberCell = row9.createCell(4);
docNumberCell.setCellValue(""); // Будет заполнено из API
docNumberCell.setCellStyle(dataStyle);
Cell filenameCell = row9.createCell(5);
filenameCell.setCellValue(""); // Будет заполнено из API
filenameCell.setCellStyle(dataStyle);
Cell componentsCountCell = row9.createCell(6);
componentsCountCell.setCellValue(""); // Будет заполнено из API
componentsCountCell.setCellStyle(dataStyle);
// A12 - "№ п/п", B12 - "Наименование материала", C12 - "Код/Артикул", D12 - "Наименование склада/проекта", E12 - "Наименование компонента по КК", F12 - "Затребованное количество", G12 - "Количество"
Row headerRow = sheet.createRow(11);
String[] headers = {
"№ п/п", "Наименование материала", "Код/Артикул", "Наименование склада/проекта",
"Наименование компонента по КК", "Затребованное количество", "Количество"
};
for (int i = 0; i < headers.length; i++) {
Cell cell = headerRow.createCell(i);
cell.setCellValue(headers[i]);
cell.setCellStyle(headerStyle);
}
// Загружаем данные компонентов из PostgreSQL
loadComponentsData(sheet, config, stageId, dataStyle, false, orderNumber, null);
// Автоматически подгоняем ширину колонок
for (int i = 0; i < 7; i++) {
sheet.autoSizeColumn(i);
}
// Сохраняем в указанный файл
try (FileOutputStream fileOut = new FileOutputStream(filePath)) {
workbook.write(fileOut);
}
} catch (Exception e) {
throw new RuntimeException("Ошибка при создании Excel документа: " + e.getMessage(), e);
}
}
private static void saveWorkbook(Workbook workbook, String orderNumber, int stageNumber) throws IOException {
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Сохранить Лимитно-заборную карту");
fileChooser.getExtensionFilters().addAll(
new FileChooser.ExtensionFilter("Excel файлы", "*.xlsx"),
new FileChooser.ExtensionFilter("Все файлы", "*.*")
);
// Устанавливаем имя файла по шаблону
String defaultFileName = "Лимитно-заборная карта " + orderNumber + "." + stageNumber + ".xlsx";
fileChooser.setInitialFileName(defaultFileName);
// Получаем Stage из текущего контекста
Stage stage = new Stage();
File file = fileChooser.showSaveDialog(stage);
if (file != null) {
try (FileOutputStream fileOut = new FileOutputStream(file)) {
workbook.write(fileOut);
}
}
}
}

View File

@@ -0,0 +1,541 @@
package org.example;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import java.sql.*;
import java.util.Properties;
public class LZKGeneratorController {
private Properties config;
private TextField searchField;
private EnhancedTableView<Stage> stagesTable;
private ObservableList<Stage> stagesData;
// Поля для правого фрейма
private RadioButton apiRadioButton;
private RadioButton fileRadioButton;
private ToggleGroup modeToggleGroup;
private TextField filePathField;
private Button browseButton;
// Поля для Radio Button'ов в таблице
private ToggleGroup tableToggleGroup;
public LZKGeneratorController(Properties config) {
this.config = config;
this.stagesData = FXCollections.observableArrayList();
this.tableToggleGroup = new ToggleGroup();
}
public VBox getView() {
VBox mainView = new VBox();
mainView.setSpacing(20);
mainView.setPadding(new Insets(20));
mainView.setStyle("-fx-background-color: #f8f9fa;");
// Заголовок
Label title = new Label("Генератор ЛЗК");
title.setFont(Font.font("Segoe UI", FontWeight.BOLD, 24));
title.setTextFill(Color.web("#2c3e50"));
title.setPadding(new Insets(0, 0, 20, 0));
// Поиск по заказам
HBox searchBox = createSearchBox();
// Основной контент в горизонтальном разделении
HBox contentArea = new HBox(20);
contentArea.setAlignment(Pos.TOP_LEFT);
// Левый фрейм - Таблица этапов
VBox leftFrame = createLeftFrame();
// Правый фрейм - Dsol-Factory
VBox rightFrame = createRightFrame();
contentArea.getChildren().addAll(leftFrame, rightFrame);
HBox.setHgrow(leftFrame, Priority.ALWAYS);
HBox.setHgrow(rightFrame, Priority.NEVER);
mainView.getChildren().addAll(title, searchBox, contentArea);
return mainView;
}
private HBox createSearchBox() {
HBox searchBox = new HBox(10);
searchBox.setAlignment(Pos.CENTER_LEFT);
Label searchLabel = new Label("Поиск по заказам:");
searchLabel.setFont(Font.font("Segoe UI", FontWeight.BOLD, 14));
searchLabel.setTextFill(Color.web("#2c3e50"));
searchField = new TextField();
searchField.setPromptText("Введите номер заказа (можно частично)...");
searchField.setPrefWidth(300);
searchField.setStyle("""
-fx-background-color: white;
-fx-border-color: #bdc3c7;
-fx-border-radius: 5;
-fx-background-radius: 5;
-fx-padding: 8 12;
-fx-font-size: 14px;
""");
Button searchButton = new Button("🔍 Поиск");
searchButton.setStyle("""
-fx-background-color: linear-gradient(to right, #3498db, #2980b9);
-fx-text-fill: white;
-fx-background-radius: 5;
-fx-cursor: hand;
-fx-font-family: 'Segoe UI Semibold';
-fx-padding: 8 16;
-fx-font-weight: bold;
-fx-font-size: 14px;
""");
searchButton.setOnAction(e -> searchLZKCards());
Button clearButton = new Button("🗑 Очистить");
clearButton.setStyle("""
-fx-background-color: linear-gradient(to right, #e74c3c, #c0392b);
-fx-text-fill: white;
-fx-background-radius: 5;
-fx-cursor: hand;
-fx-font-family: 'Segoe UI Semibold';
-fx-padding: 8 16;
-fx-font-weight: bold;
-fx-font-size: 14px;
""");
clearButton.setOnAction(e -> clearSearch());
searchBox.getChildren().addAll(searchLabel, searchField, searchButton, clearButton);
return searchBox;
}
private VBox createLeftFrame() {
VBox leftFrame = new VBox(15);
leftFrame.setPadding(new Insets(15));
leftFrame.setStyle("""
-fx-background-color: white;
-fx-border-color: #bdc3c7;
-fx-border-radius: 8;
-fx-background-radius: 8;
""");
// Заголовок таблицы
Label stagesLabel = new Label("Этапы ЛЗК карты:");
stagesLabel.setFont(Font.font("Segoe UI", FontWeight.BOLD, 16));
stagesLabel.setTextFill(Color.web("#2c3e50"));
// Таблица этапов
stagesTable = createStagesTable();
// Кнопки управления
HBox buttonBox = createButtonBox();
leftFrame.getChildren().addAll(stagesLabel, stagesTable, buttonBox);
return leftFrame;
}
private VBox createRightFrame() {
VBox rightFrame = new VBox(15);
rightFrame.setPadding(new Insets(15));
rightFrame.setPrefWidth(300);
rightFrame.setStyle("""
-fx-background-color: white;
-fx-border-color: #bdc3c7;
-fx-border-radius: 8;
-fx-background-radius: 8;
""");
// Заголовок Dsol-Factory
Label dsolLabel = new Label("Dsol-Factory");
dsolLabel.setFont(Font.font("Segoe UI", FontWeight.BOLD, 18));
dsolLabel.setTextFill(Color.web("#2c3e50"));
// RadioButton'ы для выбора режима
VBox radioContainer = new VBox(10);
modeToggleGroup = new ToggleGroup();
apiRadioButton = new RadioButton("API");
apiRadioButton.setFont(Font.font("Segoe UI", 14));
apiRadioButton.setTextFill(Color.web("#2c3e50"));
apiRadioButton.setToggleGroup(modeToggleGroup);
apiRadioButton.setSelected(true); // По умолчанию выбран API
apiRadioButton.setOnAction(e -> updateFileFieldState());
fileRadioButton = new RadioButton("Файл");
fileRadioButton.setFont(Font.font("Segoe UI", 14));
fileRadioButton.setTextFill(Color.web("#2c3e50"));
fileRadioButton.setToggleGroup(modeToggleGroup);
fileRadioButton.setOnAction(e -> updateFileFieldState());
radioContainer.getChildren().addAll(apiRadioButton, fileRadioButton);
// Поле выбора файла
VBox fileContainer = new VBox(8);
Label fileLabel = new Label("Файл Excel:");
fileLabel.setFont(Font.font("Segoe UI", 12));
fileLabel.setTextFill(Color.web("#2c3e50"));
HBox fileInputContainer = new HBox(8);
filePathField = new TextField();
filePathField.setPromptText("Выберите файл Excel...");
filePathField.setDisable(true);
browseButton = new Button("📁");
browseButton.setStyle("""
-fx-background-color: linear-gradient(to right, #3498db, #2980b9);
-fx-text-fill: white;
-fx-background-radius: 5;
-fx-cursor: hand;
-fx-font-family: 'Segoe UI Semibold';
-fx-padding: 8 12;
-fx-font-weight: bold;
-fx-font-size: 14px;
""");
browseButton.setDisable(true);
browseButton.setOnAction(e -> browseForFile());
fileInputContainer.getChildren().addAll(filePathField, browseButton);
HBox.setHgrow(filePathField, Priority.ALWAYS);
fileContainer.getChildren().addAll(fileLabel, fileInputContainer);
rightFrame.getChildren().addAll(dsolLabel, radioContainer, fileContainer);
// Инициализируем состояние поля файла
updateFileFieldState();
return rightFrame;
}
private void updateFileFieldState() {
boolean isFileSelected = fileRadioButton.isSelected();
filePathField.setDisable(!isFileSelected);
browseButton.setDisable(!isFileSelected);
if (!isFileSelected) {
filePathField.clear();
}
}
private void browseForFile() {
javafx.stage.FileChooser fileChooser = new javafx.stage.FileChooser();
fileChooser.setTitle("Выберите файл Excel");
fileChooser.getExtensionFilters().addAll(
new javafx.stage.FileChooser.ExtensionFilter("Excel файлы", "*.xlsx", "*.xls"),
new javafx.stage.FileChooser.ExtensionFilter("Все файлы", "*.*")
);
javafx.stage.Stage stage = (javafx.stage.Stage) browseButton.getScene().getWindow();
java.io.File selectedFile = fileChooser.showOpenDialog(stage);
if (selectedFile != null) {
filePathField.setText(selectedFile.getAbsolutePath());
}
}
private EnhancedTableView<Stage> createStagesTable() {
EnhancedTableView<Stage> table = new EnhancedTableView<>();
table.setPrefHeight(200);
table.setStyle("""
-fx-background-color: white;
-fx-border-color: #bdc3c7;
-fx-border-radius: 5;
-fx-background-radius: 5;
""");
// Колонка с Radio Button'ами
TableColumn<Stage, RadioButton> radioColumn = new TableColumn<>("Выбор");
radioColumn.setCellValueFactory(cellData -> {
RadioButton radioButton = new RadioButton();
radioButton.setToggleGroup(tableToggleGroup);
radioButton.setUserData(cellData.getValue());
return new javafx.beans.property.SimpleObjectProperty<>(radioButton);
});
radioColumn.setCellFactory(column -> new TableCell<Stage, RadioButton>() {
@Override
protected void updateItem(RadioButton item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setGraphic(null);
} else {
setGraphic(item);
}
}
});
radioColumn.setPrefWidth(80);
// Колонки для этапов
TableColumn<Stage, String> stageNumberColumn = new TableColumn<>("№ Этапа");
stageNumberColumn.setCellValueFactory(new PropertyValueFactory<>("stageNumber"));
stageNumberColumn.setPrefWidth(100);
TableColumn<Stage, String> responsibleColumn = new TableColumn<>("Ответственный");
responsibleColumn.setCellValueFactory(new PropertyValueFactory<>("responsible"));
responsibleColumn.setPrefWidth(200);
TableColumn<Stage, String> timestampColumn = new TableColumn<>("Дата создания");
timestampColumn.setCellValueFactory(cellData -> {
java.sql.Timestamp timestamp = cellData.getValue().getTimestamp();
if (timestamp != null) {
return new javafx.beans.property.SimpleStringProperty(
timestamp.toLocalDateTime().format(java.time.format.DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm"))
);
}
return new javafx.beans.property.SimpleStringProperty("");
});
timestampColumn.setPrefWidth(200);
table.getColumns().addAll(radioColumn, stageNumberColumn, responsibleColumn, timestampColumn);
table.setItems(stagesData);
return table;
}
private HBox createButtonBox() {
HBox buttonBox = new HBox(10);
buttonBox.setAlignment(Pos.CENTER_LEFT);
Button refreshButton = new Button("🔄 Обновить");
refreshButton.setStyle("""
-fx-background-color: linear-gradient(to right, #27ae60, #229954);
-fx-text-fill: white;
-fx-background-radius: 5;
-fx-cursor: hand;
-fx-font-family: 'Segoe UI Semibold';
-fx-padding: 8 16;
-fx-font-weight: bold;
-fx-font-size: 14px;
""");
refreshButton.setOnAction(e -> refreshData());
Button generateLZKButton = new Button("📋 Генерировать ЛЗК");
generateLZKButton.setStyle("""
-fx-background-color: linear-gradient(to right, #e74c3c, #c0392b);
-fx-text-fill: white;
-fx-background-radius: 5;
-fx-cursor: hand;
-fx-font-family: 'Segoe UI Semibold';
-fx-padding: 8 16;
-fx-font-weight: bold;
-fx-font-size: 14px;
""");
generateLZKButton.setOnAction(e -> generateLZKDocument());
buttonBox.getChildren().addAll(refreshButton, generateLZKButton);
return buttonBox;
}
private void searchLZKCards() {
String orderNumber = searchField.getText().trim();
if (orderNumber.isEmpty()) {
showAlert("Ошибка", "Введите номер заказа для поиска");
return;
}
try (Connection conn = getConnection()) {
if (conn != null) {
// Поиск этапов по номеру заказа через JOIN с limit_drawing_card
String sql = """
SELECT s.* FROM stage s
INNER JOIN limit_drawing_card l ON s.limit_drawing_card_id = l.id
WHERE CAST(l.order_number AS TEXT) ILIKE ?
ORDER BY s.stage_number
""";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, "%" + orderNumber + "%");
try (ResultSet rs = stmt.executeQuery()) {
stagesData.clear();
while (rs.next()) {
Stage stage = new Stage();
stage.setId(rs.getInt("id"));
stage.setName("Этап #" + rs.getInt("stage_number"));
stage.setDescription("Карта комплектовки: " + rs.getString("number_picking_card"));
stage.setStatus("Активен");
stage.setResponsible(rs.getString("responsible"));
stage.setTimestamp(rs.getTimestamp("timestamp"));
stage.setStageNumber(rs.getInt("stage_number"));
stage.setPickingCard(rs.getString("number_picking_card"));
stagesData.add(stage);
}
if (stagesData.isEmpty()) {
showAlert("Результат поиска", "Этапы для заказа '" + orderNumber + "' не найдены");
} else {
// Настраиваем расчетные данные для таблицы этапов
java.util.Map<String, Integer> calculatedData = new java.util.HashMap<>();
stagesTable.setCalculatedQuantities(calculatedData);
}
}
}
}
} catch (SQLException e) {
showAlert("Ошибка базы данных", "Ошибка при поиске этапов: " + e.getMessage());
e.printStackTrace();
}
}
private void clearSearch() {
searchField.clear();
stagesData.clear();
}
private void refreshData() {
if (!searchField.getText().trim().isEmpty()) {
searchLZKCards();
}
}
private void generateLZKDocument() {
if (stagesData.isEmpty()) {
showAlert("Нет данных", "Нет этапов для генерации ЛЗК");
return;
}
// Получаем выбранный этап из Radio Button'ов
Stage selectedStage = null;
if (tableToggleGroup.getSelectedToggle() != null) {
RadioButton selectedRadio = (RadioButton) tableToggleGroup.getSelectedToggle();
selectedStage = (Stage) selectedRadio.getUserData();
}
if (selectedStage == null) {
showAlert("Выберите этап", "Выберите этап с помощью Radio Button для генерации ЛЗК");
return;
}
try {
String orderNumber = searchField.getText().trim();
int stageNumber = selectedStage.getStageNumber();
String responsible = selectedStage.getResponsible();
String timestamp = selectedStage.getTimestamp().toLocalDateTime()
.format(java.time.format.DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm"));
boolean useAPI = apiRadioButton.isSelected();
if (fileRadioButton.isSelected()) {
// Режим "Файл" - используем Excel КК файл как источник данных
String kkFilePath = filePathField.getText().trim();
if (kkFilePath.isEmpty()) {
showAlert("Ошибка", "Выберите Excel КК файл");
return;
}
try {
// Генерируем ЛЗК с диалогом сохранения (не перезаписываем исходный файл!)
LZKExcelGenerator.generateLZKDocument(config, orderNumber, stageNumber, responsible, timestamp, selectedStage.getId(), useAPI, kkFilePath);
showAlert("Успех", "Лимитно-заборная карта успешно сгенерирована!");
} catch (Exception e) {
showAlert("Ошибка", "Ошибка при генерации ЛЗК: " + e.getMessage());
e.printStackTrace();
}
} else {
// Режим "API" - генерируем с диалогом сохранения
try {
LZKExcelGenerator.generateLZKDocument(config, orderNumber, stageNumber, responsible, timestamp, selectedStage.getId(), useAPI, null);
showAlert("Успех", "Лимитно-заборная карта успешно сгенерирована!");
} catch (Exception e) {
showAlert("Ошибка", "Ошибка при генерации ЛЗК: " + e.getMessage());
e.printStackTrace();
}
}
} catch (Exception e) {
showAlert("Ошибка", "Ошибка при генерации ЛЗК: " + e.getMessage());
e.printStackTrace();
}
}
private void exportToExcel() {
if (stagesData.isEmpty()) {
showAlert("Нет данных", "Нет данных для экспорта");
return;
}
// TODO: Реализовать экспорт в Excel
showAlert("Экспорт", "Функция экспорта будет реализована позже");
}
private Connection getConnection() {
try {
// Явно загружаем PostgreSQL драйвер
Class.forName("org.postgresql.Driver");
String connectionString = config.getProperty("postgresql.connection.string");
String username = config.getProperty("postgresql.username");
String password = config.getProperty("postgresql.password");
if (connectionString == null || connectionString.isEmpty()) {
System.err.println("Ошибка: Не настроено подключение к PostgreSQL в config.properties");
return null;
}
return DriverManager.getConnection(connectionString, username, password);
} catch (ClassNotFoundException e) {
System.err.println("Ошибка: PostgreSQL драйвер не найден в classpath");
e.printStackTrace();
return null;
} catch (SQLException e) {
System.err.println("Ошибка подключения к PostgreSQL: " + e.getMessage());
e.printStackTrace();
return null;
}
}
private void showAlert(String title, String message) {
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle(title);
alert.setHeaderText(null);
alert.setContentText(message);
alert.showAndWait();
}
// Внутренние классы для данных
public static class Stage {
private int id;
private String name;
private String description;
private String status;
private String responsible;
private java.sql.Timestamp timestamp;
private int stageNumber;
private String pickingCard;
// Геттеры и сеттеры
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public String getResponsible() { return responsible; }
public void setResponsible(String responsible) { this.responsible = responsible; }
public java.sql.Timestamp getTimestamp() { return timestamp; }
public void setTimestamp(java.sql.Timestamp timestamp) { this.timestamp = timestamp; }
public int getStageNumber() { return stageNumber; }
public void setStageNumber(int stageNumber) { this.stageNumber = stageNumber; }
public String getPickingCard() { return pickingCard; }
public void setPickingCard(String pickingCard) { this.pickingCard = pickingCard; }
}
}

View File

@@ -0,0 +1,421 @@
package org.example;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Cursor;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class Main extends Application {
private static final String CONFIG_FILE = "config.properties";
private Properties config;
private VBox sidebar;
private StackPane contentArea;
private SettingsController settingsController;
private ReportsController reportsController;
private HomeController homeController;
private WeeklyReportController weeklyReportController;
private LZKGeneratorController lzkGeneratorController;
private boolean sidebarVisible = false;
private boolean isMaximized = false;
@Override
public void start(Stage primaryStage) {
loadConfig();
initializeGUI(primaryStage);
}
private void loadConfig() {
config = new Properties();
try (InputStream input = getClass().getClassLoader().getResourceAsStream(CONFIG_FILE)) {
if (input != null) {
config.load(input);
} else {
System.err.println("Config file not found: " + CONFIG_FILE);
}
} catch (IOException e) {
System.err.println("Error loading config: " + e.getMessage());
}
}
private void initializeGUI(Stage primaryStage) {
primaryStage.setTitle("AccessLZK - Управление базами данных");
primaryStage.setWidth(1200);
primaryStage.setHeight(800);
primaryStage.setMinWidth(1000);
primaryStage.setMinHeight(700);
primaryStage.setResizable(true);
// Убираем стандартные рамки окна
primaryStage.initStyle(StageStyle.UNDECORATED);
// Создаем главный контейнер
BorderPane root = new BorderPane();
root.setStyle("-fx-background-color: #ffffff;");
// Создаем заголовок с кнопками управления
HBox titleBar = createTitleBar(primaryStage);
root.setTop(titleBar);
// Добавляем возможность перетаскивания окна
makeDraggable(primaryStage, titleBar);
// Создаем основной контент
HBox mainContent = new HBox();
mainContent.setStyle("-fx-background-color: #f8f9fa;");
// Создаем sidebar (hamburger menu)
sidebar = createSidebar();
sidebar.setVisible(false);
sidebar.setPrefWidth(0);
// Создаем область контента
contentArea = new StackPane();
contentArea.setStyle("-fx-background-color: #ffffff;");
// Домашняя страница
homeController = new HomeController(config);
homeController.setOnStartWork(this::showReports);
contentArea.getChildren().add(homeController.getView());
mainContent.getChildren().addAll(sidebar, contentArea);
HBox.setHgrow(contentArea, Priority.ALWAYS);
root.setCenter(mainContent);
// Создаем сцену
Scene scene = new Scene(root);
scene.getStylesheets().add(getClass().getResource("/styles.css").toExternalForm());
// Добавляем возможность изменения размера по всем краям окна
addResizeFunctionality(primaryStage, root);
primaryStage.setScene(scene);
primaryStage.show();
}
private HBox createTitleBar(Stage primaryStage) {
HBox titleBar = new HBox();
titleBar.setStyle("-fx-background-color: #2c3e50; -fx-padding: 8 16;");
titleBar.setPrefHeight(40);
titleBar.setAlignment(Pos.CENTER_LEFT);
// Hamburger menu button
Button hamburgerBtn = new Button("");
hamburgerBtn.setStyle("-fx-background-color: transparent; -fx-text-fill: white; -fx-font-size: 16px; -fx-cursor: hand;");
hamburgerBtn.setOnAction(e -> toggleSidebar());
// Title
Label title = new Label("Database Management System");
title.setStyle("-fx-text-fill: white; -fx-font-size: 14px; -fx-font-weight: bold;");
HBox.setMargin(title, new Insets(0, 0, 0, 16));
// Window controls
HBox windowControls = new HBox();
windowControls.setAlignment(Pos.CENTER_RIGHT);
HBox.setHgrow(windowControls, Priority.ALWAYS);
Button minimizeBtn = new Button("");
minimizeBtn.setStyle("-fx-background-color: transparent; -fx-text-fill: white; -fx-font-size: 14px; -fx-cursor: hand;");
minimizeBtn.setOnAction(e -> primaryStage.setIconified(true));
Button maximizeBtn = new Button("");
maximizeBtn.setStyle("-fx-background-color: transparent; -fx-text-fill: white; -fx-font-size: 14px; -fx-cursor: hand;");
maximizeBtn.setOnAction(e -> toggleMaximize(primaryStage, maximizeBtn));
Button closeBtn = new Button("×");
closeBtn.setStyle("-fx-background-color: transparent; -fx-text-fill: white; -fx-font-size: 16px; -fx-cursor: hand;");
closeBtn.setOnAction(e -> primaryStage.close());
windowControls.getChildren().addAll(minimizeBtn, maximizeBtn, closeBtn);
titleBar.getChildren().addAll(hamburgerBtn, title, windowControls);
// Добавляем обработку двойного клика по заголовку для максимизации
titleBar.setOnMouseClicked(event -> {
if (event.getClickCount() == 2) {
toggleMaximize(primaryStage, maximizeBtn);
}
});
return titleBar;
}
private VBox createSidebar() {
VBox sidebar = new VBox();
sidebar.setStyle("-fx-background-color: #34495e; -fx-padding: 20;");
sidebar.setSpacing(10);
sidebar.setMinWidth(200); // Увеличиваем минимальную ширину
sidebar.setPrefWidth(250); // Устанавливаем предпочтительную ширину
// Menu items
Button homeBtn = new Button("🏠 Главная");
homeBtn.setStyle("-fx-background-color: transparent; -fx-text-fill: white; -fx-font-size: 14px; -fx-cursor: hand; -fx-alignment: center-left;");
homeBtn.setPrefWidth(Double.MAX_VALUE);
homeBtn.setOnAction(e -> showHome());
// Создаем двухуровневую иерархию для отчетов
VBox reportsSection = new VBox();
reportsSection.setSpacing(5);
// Заголовок раздела
Label reportsHeader = new Label("📊 Отчёты по списанию");
reportsHeader.setStyle("-fx-text-fill: #ecf0f1; -fx-font-size: 14px; -fx-font-weight: bold; -fx-padding: 5 0; -fx-wrap-text: true;");
reportsHeader.setMaxWidth(Double.MAX_VALUE);
// Подразделы
Button weeklyBtn = new Button("📅 Еженедельное списание");
weeklyBtn.setStyle("-fx-background-color: transparent; -fx-text-fill: #bdc3c7; -fx-font-size: 12px; -fx-cursor: hand; -fx-alignment: center-left; -fx-padding: 8 20; -fx-wrap-text: true;");
weeklyBtn.setPrefWidth(Double.MAX_VALUE);
weeklyBtn.setMaxWidth(Double.MAX_VALUE);
weeklyBtn.setOnAction(e -> showWeeklyReports());
Button orderBtn = new Button("📋 Списание по заказам");
orderBtn.setStyle("-fx-background-color: transparent; -fx-text-fill: #bdc3c7; -fx-font-size: 12px; -fx-cursor: hand; -fx-alignment: center-left; -fx-padding: 8 20; -fx-wrap-text: true;");
orderBtn.setPrefWidth(Double.MAX_VALUE);
orderBtn.setMaxWidth(Double.MAX_VALUE);
orderBtn.setOnAction(e -> showReports());
Button lzkBtn = new Button("🔧 Генератор ЛЗК");
lzkBtn.setStyle("-fx-background-color: transparent; -fx-text-fill: #bdc3c7; -fx-font-size: 12px; -fx-cursor: hand; -fx-alignment: center-left; -fx-padding: 8 20; -fx-wrap-text: true;");
lzkBtn.setPrefWidth(Double.MAX_VALUE);
lzkBtn.setMaxWidth(Double.MAX_VALUE);
lzkBtn.setOnAction(e -> showLZKGenerator());
reportsSection.getChildren().addAll(reportsHeader, weeklyBtn, orderBtn, lzkBtn);
Button helpBtn = new Button("❓ Справка");
helpBtn.setStyle("-fx-background-color: transparent; -fx-text-fill: white; -fx-font-size: 14px; -fx-cursor: hand; -fx-alignment: center-left;");
helpBtn.setPrefWidth(Double.MAX_VALUE);
helpBtn.setOnAction(e -> showAboutDialog());
Button settingsBtn = new Button("⚙ Настройки");
settingsBtn.setStyle("-fx-background-color: transparent; -fx-text-fill: white; -fx-font-size: 14px; -fx-cursor: hand; -fx-alignment: center-left;");
settingsBtn.setPrefWidth(Double.MAX_VALUE);
settingsBtn.setOnAction(e -> showSettings());
sidebar.getChildren().addAll(homeBtn, reportsSection, helpBtn, settingsBtn);
return sidebar;
}
private void toggleSidebar() {
sidebarVisible = !sidebarVisible;
if (sidebarVisible) {
sidebar.setVisible(true);
sidebar.setPrefWidth(250); // Увеличиваем размер для длинных названий
} else {
sidebar.setVisible(false);
sidebar.setPrefWidth(0);
}
}
private void showHome() {
contentArea.getChildren().clear();
if (homeController == null) {
homeController = new HomeController(config);
homeController.setOnStartWork(this::showReports);
}
contentArea.getChildren().add(homeController.getView());
if (!sidebarVisible) {
toggleSidebar();
}
}
private void showSettings() {
contentArea.getChildren().clear();
if (settingsController == null) {
settingsController = new SettingsController(config);
}
contentArea.getChildren().add(settingsController.getView());
if (!sidebarVisible) {
toggleSidebar();
}
}
private void showReports() {
contentArea.getChildren().clear();
if (reportsController == null) {
reportsController = new ReportsController(config);
}
contentArea.getChildren().add(reportsController.getView());
if (!sidebarVisible) {
toggleSidebar();
}
}
private void showWeeklyReports() {
contentArea.getChildren().clear();
if (weeklyReportController == null) {
weeklyReportController = new WeeklyReportController();
}
contentArea.getChildren().add(weeklyReportController.getView());
if (!sidebarVisible) {
toggleSidebar();
}
}
private void showLZKGenerator() {
contentArea.getChildren().clear();
if (lzkGeneratorController == null) {
lzkGeneratorController = new LZKGeneratorController(config);
}
contentArea.getChildren().add(lzkGeneratorController.getView());
if (!sidebarVisible) {
toggleSidebar();
}
}
private void showAboutDialog() {
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("О программе");
alert.setHeaderText("Database Management System v1.0");
alert.setContentText("Система управления базами данных\оддержка MS Access и PostgreSQL");
alert.showAndWait();
}
private void makeDraggable(Stage stage, HBox titleBar) {
final double[] xOffset = new double[1];
final double[] yOffset = new double[1];
titleBar.setOnMousePressed(event -> {
xOffset[0] = event.getSceneX();
yOffset[0] = event.getSceneY();
});
titleBar.setOnMouseDragged(event -> {
stage.setX(event.getScreenX() - xOffset[0]);
stage.setY(event.getScreenY() - yOffset[0]);
});
}
private void addResizeFunctionality(Stage stage, Region root) {
final int RESIZE_MARGIN = 6;
root.setOnMouseMoved(event -> {
double x = event.getSceneX();
double y = event.getSceneY();
double width = stage.getWidth();
double height = stage.getHeight();
Cursor cursor = Cursor.DEFAULT;
boolean left = x < RESIZE_MARGIN;
boolean right = x > width - RESIZE_MARGIN;
boolean top = y < RESIZE_MARGIN;
boolean bottom = y > height - RESIZE_MARGIN;
if (left && top) {
cursor = Cursor.NW_RESIZE;
} else if (left && bottom) {
cursor = Cursor.SW_RESIZE;
} else if (right && top) {
cursor = Cursor.NE_RESIZE;
} else if (right && bottom) {
cursor = Cursor.SE_RESIZE;
} else if (right) {
cursor = Cursor.E_RESIZE;
} else if (left) {
cursor = Cursor.W_RESIZE;
} else if (top) {
cursor = Cursor.N_RESIZE;
} else if (bottom) {
cursor = Cursor.S_RESIZE;
}
root.setCursor(cursor);
});
final Delta dragDelta = new Delta();
root.setOnMousePressed(event -> {
dragDelta.startX = stage.getX();
dragDelta.startY = stage.getY();
dragDelta.startWidth = stage.getWidth();
dragDelta.startHeight = stage.getHeight();
dragDelta.mouseX = event.getScreenX();
dragDelta.mouseY = event.getScreenY();
dragDelta.cursor = root.getCursor();
});
root.setOnMouseDragged(event -> {
if (dragDelta.cursor == Cursor.DEFAULT) return;
double dx = event.getScreenX() - dragDelta.mouseX;
double dy = event.getScreenY() - dragDelta.mouseY;
double newX = dragDelta.startX;
double newY = dragDelta.startY;
double newW = dragDelta.startWidth;
double newH = dragDelta.startHeight;
if (dragDelta.cursor == Cursor.E_RESIZE || dragDelta.cursor == Cursor.SE_RESIZE || dragDelta.cursor == Cursor.NE_RESIZE) {
newW = dragDelta.startWidth + dx;
}
if (dragDelta.cursor == Cursor.S_RESIZE || dragDelta.cursor == Cursor.SE_RESIZE || dragDelta.cursor == Cursor.SW_RESIZE) {
newH = dragDelta.startHeight + dy;
}
if (dragDelta.cursor == Cursor.W_RESIZE || dragDelta.cursor == Cursor.SW_RESIZE || dragDelta.cursor == Cursor.NW_RESIZE) {
newW = dragDelta.startWidth - dx;
newX = dragDelta.startX + dx;
}
if (dragDelta.cursor == Cursor.N_RESIZE || dragDelta.cursor == Cursor.NE_RESIZE || dragDelta.cursor == Cursor.NW_RESIZE) {
newH = dragDelta.startHeight - dy;
newY = dragDelta.startY + dy;
}
if (newW >= stage.getMinWidth()) {
stage.setX(newX);
stage.setWidth(newW);
}
if (newH >= stage.getMinHeight()) {
stage.setY(newY);
stage.setHeight(newH);
}
});
}
private void toggleMaximize(Stage stage, Button maximizeBtn) {
if (isMaximized) {
// Восстанавливаем обычный размер
stage.setMaximized(false);
stage.setWidth(1200);
stage.setHeight(800);
stage.centerOnScreen();
isMaximized = false;
maximizeBtn.setText("");
} else {
// Разворачиваем во весь экран с учетом панели задач
// Получаем размеры экрана
javafx.stage.Screen screen = javafx.stage.Screen.getPrimary();
javafx.geometry.Rectangle2D screenBounds = screen.getVisualBounds();
// Устанавливаем размер и позицию окна с учетом панели задач
stage.setX(screenBounds.getMinX());
stage.setY(screenBounds.getMinY());
stage.setWidth(screenBounds.getWidth());
stage.setHeight(screenBounds.getHeight());
isMaximized = true;
maximizeBtn.setText("");
}
}
public static void main(String[] args) {
launch(args);
}
private static class Delta {
double startX, startY, startWidth, startHeight;
double mouseX, mouseY;
Cursor cursor;
}
}

View File

@@ -0,0 +1,27 @@
package org.example;
public class OrderQuantity {
private String orderNumber;
private int quantity;
public OrderQuantity(String orderNumber, int quantity) {
this.orderNumber = orderNumber;
this.quantity = quantity;
}
public String getOrderNumber() {
return orderNumber;
}
public void setOrderNumber(String orderNumber) {
this.orderNumber = orderNumber;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,505 @@
package org.example;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.*;
import javafx.scene.control.ButtonBar;
import javafx.scene.layout.*;
import javafx.scene.Node;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.concurrent.Task;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;
public class SettingsController {
private Properties config;
private VBox view;
private TextField accessConnectionField;
private Button accessFileButton;
private TextField postgresqlConnectionField;
private TextField postgresqlUsernameField;
private PasswordField postgresqlPasswordField;
private TextField dsolUrlField;
private TextField dsolApiKkField;
private TextField dsolApiQuantityField;
private Button unlockButton;
private Button saveButton;
private Button testConnectionButton;
private Button initializeDbButton;
private boolean isUnlocked = false;
public SettingsController(Properties config) {
this.config = config;
initializeComponents();
setupLayout();
setupEventHandlers();
loadCurrentSettings();
updateFieldStates();
}
private void initializeComponents() {
// Поля для подключений
accessConnectionField = new TextField();
accessFileButton = createStyledButton("Выбрать файл", "#6c757d");
postgresqlConnectionField = new TextField();
postgresqlUsernameField = new TextField();
postgresqlPasswordField = new PasswordField();
// Поля для Dsol-Factory API
dsolUrlField = new TextField();
dsolApiKkField = new TextField();
dsolApiQuantityField = new TextField();
// Кнопки
unlockButton = createStyledButton("Разблокировать настройки", "#dc3545");
saveButton = createStyledButton("Сохранить настройки", "#28a745");
testConnectionButton = createStyledButton("Тестировать подключения", "#007bff");
initializeDbButton = createStyledButton("Инициализировать БД", "#17a2b8");
}
private void setupLayout() {
view = new VBox();
view.setSpacing(20);
view.setPadding(new Insets(30));
view.setStyle("-fx-background-color: #f8f9fa;");
// Главный заголовок - фиксированный стиль
Label mainTitle = createFixedTitle("Настройки подключений к базам данных");
// MS Access секция
VBox accessSection = createSection("MS Access Database", createAccessFields());
// PostgreSQL секция
VBox postgresSection = createSection("PostgreSQL Database", createPostgresFields());
// Dsol-Factory API секция
VBox dsolSection = createSection("Dsol-Factory API", createDsolFields());
// Кнопки управления
HBox buttonContainer = createButtonContainer();
// Основной контент в ScrollPane
VBox contentArea = new VBox(20, mainTitle, accessSection, postgresSection, dsolSection);
ScrollPane scrollPane = new ScrollPane(contentArea);
scrollPane.setFitToWidth(true);
scrollPane.setStyle("-fx-background-color: transparent; -fx-border-width: 0;");
scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED);
scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
// Основной layout с BorderPane
BorderPane mainLayout = new BorderPane();
mainLayout.setCenter(scrollPane);
mainLayout.setBottom(buttonContainer);
view.getChildren().add(mainLayout);
}
private Label createFixedTitle(String text) {
Label label = new Label(text);
label.setStyle("-fx-font-family: 'Segoe UI'; -fx-font-size: 18px; -fx-font-weight: bold; -fx-text-fill: #212529;");
label.setPadding(new Insets(0, 0, 20, 0));
return label;
}
private VBox createSection(String title, Node content) {
VBox section = new VBox();
section.setSpacing(10);
section.setPadding(new Insets(15));
section.setStyle("-fx-background-color: white; -fx-background-radius: 5; -fx-border-color: #dee2e6; -fx-border-width: 1;");
// Заголовок секции - фиксированный стиль
Label sectionTitle = new Label(title);
sectionTitle.setStyle("-fx-font-family: 'Segoe UI'; -fx-font-size: 14px; -fx-font-weight: bold; -fx-text-fill: #212529;");
section.getChildren().addAll(sectionTitle, content);
return section;
}
private Node createAccessFields() {
HBox fields = new HBox(10);
fields.setAlignment(Pos.CENTER_LEFT);
fields.setPadding(new Insets(15));
fields.setStyle("-fx-background-color: #f8f9fa; -fx-background-radius: 3;");
Label pathLabel = createFixedLabel("Путь к файлу:");
accessConnectionField.setPromptText("Выберите файл базы данных");
accessConnectionField.setPrefWidth(300);
fields.getChildren().addAll(pathLabel, accessConnectionField, accessFileButton);
return fields;
}
private Node createPostgresFields() {
GridPane fields = new GridPane();
fields.setHgap(15);
fields.setVgap(10);
fields.setPadding(new Insets(15));
fields.setStyle("-fx-background-color: #f8f9fa; -fx-background-radius: 3;");
// URL подключения
fields.add(createFixedLabel("URL подключения:"), 0, 0);
postgresqlConnectionField.setPromptText("localhost:5432/database_name");
postgresqlConnectionField.setPrefWidth(300);
fields.add(postgresqlConnectionField, 1, 0);
// Имя пользователя
fields.add(createFixedLabel("Имя пользователя:"), 0, 1);
postgresqlUsernameField.setPrefWidth(200);
fields.add(postgresqlUsernameField, 1, 1);
// Пароль
fields.add(createFixedLabel("Пароль:"), 0, 2);
postgresqlPasswordField.setPrefWidth(200);
fields.add(postgresqlPasswordField, 1, 2);
return fields;
}
private Node createDsolFields() {
GridPane fields = new GridPane();
fields.setHgap(15);
fields.setVgap(10);
fields.setPadding(new Insets(15));
fields.setStyle("-fx-background-color: #f8f9fa; -fx-background-radius: 3;");
// URL API
fields.add(createFixedLabel("URL API:"), 0, 0);
dsolUrlField.setPromptText("https://api.dsol-factory.com");
dsolUrlField.setPrefWidth(300);
fields.add(dsolUrlField, 1, 0);
// API КК
fields.add(createFixedLabel("API: КК:"), 0, 1);
dsolApiKkField.setPrefWidth(200);
fields.add(dsolApiKkField, 1, 1);
// API количество изделий
fields.add(createFixedLabel("API: количество изделий:"), 0, 2);
dsolApiQuantityField.setPrefWidth(200);
fields.add(dsolApiQuantityField, 1, 2);
return fields;
}
private Label createFixedLabel(String text) {
Label label = new Label(text);
label.setStyle("-fx-font-family: 'Segoe UI'; -fx-font-size: 12px; -fx-font-weight: normal; -fx-text-fill: #495057;");
label.setMinWidth(150);
return label;
}
private HBox createButtonContainer() {
HBox buttonContainer = new HBox(15);
buttonContainer.setAlignment(Pos.CENTER);
buttonContainer.setPadding(new Insets(20));
buttonContainer.setStyle("-fx-background-color: #f8f9fa; -fx-border-color: #dee2e6; -fx-border-width: 1 0 0 0;");
buttonContainer.getChildren().addAll(unlockButton, saveButton, testConnectionButton, initializeDbButton);
return buttonContainer;
}
private Button createStyledButton(String text, String color) {
Button button = new Button(text);
button.setStyle(String.format(
"-fx-background-color: %s; -fx-text-fill: white; -fx-font-family: 'Segoe UI'; -fx-font-size: 12px; -fx-font-weight: bold; -fx-padding: 8 16; -fx-background-radius: 4; -fx-cursor: hand;",
color
));
button.setOnMouseEntered(e -> button.setStyle(String.format(
"-fx-background-color: %s; -fx-text-fill: white; -fx-font-family: 'Segoe UI'; -fx-font-size: 12px; -fx-font-weight: bold; -fx-padding: 8 16; -fx-background-radius: 4; -fx-cursor: hand; -fx-effect: dropshadow(gaussian, rgba(0,0,0,0.2), 3, 0, 0, 1);",
color
)));
button.setOnMouseExited(e -> button.setStyle(String.format(
"-fx-background-color: %s; -fx-text-fill: white; -fx-font-family: 'Segoe UI'; -fx-font-size: 12px; -fx-font-weight: bold; -fx-padding: 8 16; -fx-background-radius: 4; -fx-cursor: hand;",
color
)));
return button;
}
private void setupEventHandlers() {
unlockButton.setOnAction(e -> toggleSettingsLock());
saveButton.setOnAction(e -> saveSettings());
testConnectionButton.setOnAction(e -> testConnections());
initializeDbButton.setOnAction(e -> initializeDatabase());
accessFileButton.setOnAction(e -> selectAccessFile());
}
private void toggleSettingsLock() {
if (isUnlocked) {
// Блокируем настройки
isUnlocked = false;
updateFieldStates();
unlockButton.setText("Разблокировать настройки");
unlockButton.setStyle("-fx-background-color: #dc3545; -fx-text-fill: white; -fx-font-family: 'Segoe UI'; -fx-font-size: 12px; -fx-font-weight: bold; -fx-padding: 8 16; -fx-background-radius: 4; -fx-cursor: hand;");
} else {
// Разблокируем настройки
unlockSettings();
}
}
private void unlockSettings() {
Dialog<String> dialog = new Dialog<>();
dialog.setTitle("Авторизация администратора");
dialog.setHeaderText("Введите пароль администратора");
PasswordField passwordField = new PasswordField();
passwordField.setPromptText("Введите пароль");
dialog.getDialogPane().setContent(passwordField);
// Добавляем кнопки
ButtonType okButtonType = new ButtonType("OK", ButtonBar.ButtonData.OK_DONE);
ButtonType cancelButtonType = new ButtonType("Отмена", ButtonBar.ButtonData.CANCEL_CLOSE);
dialog.getDialogPane().getButtonTypes().addAll(okButtonType, cancelButtonType);
dialog.setOnShown(event -> Platform.runLater(() -> passwordField.requestFocus()));
// Устанавливаем результат при нажатии OK
dialog.setResultConverter(dialogButton -> {
if (dialogButton == okButtonType) {
return passwordField.getText();
}
return null;
});
dialog.showAndWait().ifPresent(password -> {
String adminPassword = config.getProperty("admin.password", "admin123");
if (password.equals(adminPassword)) {
isUnlocked = true;
updateFieldStates();
unlockButton.setText("Заблокировать настройки");
unlockButton.setStyle("-fx-background-color: #dc3545; -fx-text-fill: white; -fx-font-family: 'Segoe UI'; -fx-font-size: 12px; -fx-font-weight: bold; -fx-padding: 8 16; -fx-background-radius: 4; -fx-cursor: hand;");
} else {
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setTitle("Ошибка");
alert.setHeaderText("Неверный пароль");
alert.setContentText("Введенный пароль неверен.");
alert.showAndWait();
}
});
}
private void saveSettings() {
if (!isUnlocked) {
Alert alert = new Alert(Alert.AlertType.WARNING);
alert.setTitle("Предупреждение");
alert.setHeaderText("Настройки заблокированы");
alert.setContentText("Сначала разблокируйте настройки с помощью пароля администратора.");
alert.showAndWait();
return;
}
try {
// Сохраняем настройки MS Access
config.setProperty("access.connection.string", accessConnectionField.getText());
// Сохраняем настройки PostgreSQL
config.setProperty("postgresql.connection.string", postgresqlConnectionField.getText());
config.setProperty("postgresql.username", postgresqlUsernameField.getText());
config.setProperty("postgresql.password", postgresqlPasswordField.getText());
// Сохраняем настройки Dsol-Factory API
String dsolUrl = dsolUrlField.getText();
String dsolApiKk = dsolApiKkField.getText();
String dsolApiQuantity = dsolApiQuantityField.getText();
System.out.println("Saving Dsol-Factory settings:");
System.out.println("URL: " + dsolUrl);
System.out.println("API KK: " + dsolApiKk);
System.out.println("API Quantity: " + dsolApiQuantity);
config.setProperty("dsol.url", dsolUrl);
config.setProperty("dsol.api.kk", dsolApiKk);
config.setProperty("dsol.api.quantity", dsolApiQuantity);
// Записываем в файл
try (FileOutputStream out = new FileOutputStream("src/main/resources/config.properties")) {
config.store(out, "AccessLZK Configuration");
}
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("Успех");
alert.setHeaderText("Настройки сохранены");
alert.setContentText("Все настройки успешно сохранены в файл конфигурации.");
alert.showAndWait();
} catch (IOException e) {
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setTitle("Ошибка");
alert.setHeaderText("Ошибка сохранения");
alert.setContentText("Не удалось сохранить настройки: " + e.getMessage());
alert.showAndWait();
}
}
private void testConnections() {
if (!isUnlocked) {
Alert alert = new Alert(Alert.AlertType.WARNING);
alert.setTitle("Предупреждение");
alert.setHeaderText("Настройки заблокированы");
alert.setContentText("Сначала разблокируйте настройки с помощью пароля администратора.");
alert.showAndWait();
return;
}
Task<Void> testTask = new Task<Void>() {
@Override
protected Void call() throws Exception {
// Тестируем MS Access
if (!accessConnectionField.getText().isEmpty()) {
updateMessage("Тестирование подключения к MS Access...");
ConnectionManager.testAccessConnection(accessConnectionField.getText());
}
// Тестируем PostgreSQL
if (!postgresqlConnectionField.getText().isEmpty()) {
updateMessage("Тестирование подключения к PostgreSQL...");
String connectionString = "jdbc:postgresql://" + postgresqlConnectionField.getText();
ConnectionManager.testPostgreSQLConnection(
connectionString,
postgresqlUsernameField.getText(),
postgresqlPasswordField.getText()
);
}
return null;
}
};
testTask.setOnSucceeded(e -> {
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("Результат тестирования");
alert.setHeaderText("Подключения протестированы");
alert.setContentText("Все подключения работают корректно.");
alert.showAndWait();
});
testTask.setOnFailed(e -> {
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setTitle("Ошибка подключения");
alert.setHeaderText("Не удалось подключиться");
alert.setContentText("Ошибка: " + testTask.getException().getMessage());
alert.showAndWait();
});
Thread testThread = new Thread(testTask);
testThread.setDaemon(true);
testThread.start();
}
private void initializeDatabase() {
if (!isUnlocked) {
Alert alert = new Alert(Alert.AlertType.WARNING);
alert.setTitle("Предупреждение");
alert.setHeaderText("Настройки заблокированы");
alert.setContentText("Сначала разблокируйте настройки с помощью пароля администратора.");
alert.showAndWait();
return;
}
Alert confirmAlert = new Alert(Alert.AlertType.CONFIRMATION);
confirmAlert.setTitle("Подтверждение инициализации");
confirmAlert.setHeaderText("Инициализация базы данных PostgreSQL");
confirmAlert.setContentText("Это действие создаст необходимые таблицы в PostgreSQL. Продолжить?");
confirmAlert.showAndWait().ifPresent(response -> {
if (response == ButtonType.OK) {
Task<Void> initTask = new Task<Void>() {
@Override
protected Void call() throws Exception {
updateMessage("Инициализация базы данных PostgreSQL...");
String connectionString = postgresqlConnectionField.getText();
DatabaseInitializer.initializePostgreSQLDatabase(
connectionString,
postgresqlUsernameField.getText(),
postgresqlPasswordField.getText()
);
return null;
}
};
initTask.setOnSucceeded(e -> {
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("Инициализация завершена");
alert.setHeaderText("База данных инициализирована");
alert.setContentText("Все необходимые таблицы созданы в PostgreSQL.");
alert.showAndWait();
});
initTask.setOnFailed(e -> {
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setTitle("Ошибка инициализации");
alert.setHeaderText("Не удалось инициализировать базу данных");
alert.setContentText("Ошибка: " + initTask.getException().getMessage());
alert.showAndWait();
});
Thread initThread = new Thread(initTask);
initThread.setDaemon(true);
initThread.start();
}
});
}
private void selectAccessFile() {
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Выберите файл базы данных MS Access");
fileChooser.getExtensionFilters().addAll(
new FileChooser.ExtensionFilter("Access Database", "*.mdb", "*.accdb"),
new FileChooser.ExtensionFilter("All Files", "*.*")
);
Stage stage = (Stage) accessFileButton.getScene().getWindow();
File selectedFile = fileChooser.showOpenDialog(stage);
if (selectedFile != null) {
accessConnectionField.setText(selectedFile.getAbsolutePath());
}
}
private void updateFieldStates() {
boolean enabled = isUnlocked;
accessConnectionField.setDisable(!enabled);
accessFileButton.setDisable(!enabled);
postgresqlConnectionField.setDisable(!enabled);
postgresqlUsernameField.setDisable(!enabled);
postgresqlPasswordField.setDisable(!enabled);
dsolUrlField.setDisable(!enabled);
dsolApiKkField.setDisable(!enabled);
dsolApiQuantityField.setDisable(!enabled);
saveButton.setDisable(!enabled);
testConnectionButton.setDisable(!enabled);
initializeDbButton.setDisable(!enabled);
}
private void loadCurrentSettings() {
accessConnectionField.setText(config.getProperty("access.connection.string", ""));
postgresqlConnectionField.setText(config.getProperty("postgresql.connection.string", ""));
postgresqlUsernameField.setText(config.getProperty("postgresql.username", ""));
postgresqlPasswordField.setText(config.getProperty("postgresql.password", ""));
String dsolUrl = config.getProperty("dsol.url", "");
String dsolApiKk = config.getProperty("dsol.api.kk", "");
String dsolApiQuantity = config.getProperty("dsol.api.quantity", "");
System.out.println("Loading Dsol-Factory settings:");
System.out.println("URL: '" + dsolUrl + "'");
System.out.println("API KK: '" + dsolApiKk + "'");
System.out.println("API Quantity: '" + dsolApiQuantity + "'");
dsolUrlField.setText(dsolUrl);
dsolApiKkField.setText(dsolApiKk);
dsolApiQuantityField.setText(dsolApiQuantity);
}
public VBox getView() {
return view;
}
}

View File

@@ -0,0 +1,364 @@
package org.example;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;
public class SettingsPanel extends JPanel {
private Properties config;
private JTextField accessConnectionField;
private JTextField postgresqlConnectionField;
private JTextField postgresqlUsernameField;
private JPasswordField postgresqlPasswordField;
private JButton unlockButton;
private JButton saveButton;
private JButton testConnectionButton;
private boolean isUnlocked = false;
public SettingsPanel(Properties config) {
this.config = config;
initializeComponents();
setupLayout();
setupEventHandlers();
updateFieldStates();
}
private void initializeComponents() {
// Поля для подключений с современным стилем
accessConnectionField = new JTextField(50);
postgresqlConnectionField = new JTextField(50);
postgresqlUsernameField = new JTextField(20);
postgresqlPasswordField = new JPasswordField(20);
// Настройка шрифтов и стилей
Font fieldFont = new Font("Segoe UI", Font.PLAIN, 13);
accessConnectionField.setFont(fieldFont);
postgresqlConnectionField.setFont(fieldFont);
postgresqlUsernameField.setFont(fieldFont);
postgresqlPasswordField.setFont(fieldFont);
// Кнопки с современным дизайном
unlockButton = createStyledButton("Разблокировать настройки", new Color(40, 167, 69));
saveButton = createStyledButton("Сохранить настройки", new Color(0, 123, 255));
testConnectionButton = createStyledButton("Тестировать подключения", new Color(108, 117, 125));
// Загружаем текущие значения
loadCurrentSettings();
}
private JButton createStyledButton(String text, Color backgroundColor) {
JButton button = new JButton(text);
button.setFont(new Font("Segoe UI", Font.BOLD, 12));
button.setBackground(backgroundColor);
button.setForeground(Color.WHITE);
button.setFocusPainted(false);
button.setBorderPainted(false);
button.setOpaque(true);
button.setPreferredSize(new Dimension(200, 35));
button.setCursor(new Cursor(Cursor.HAND_CURSOR));
// Эффект при наведении с сохранением белого текста
button.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseEntered(java.awt.event.MouseEvent evt) {
button.setBackground(backgroundColor.darker());
button.setForeground(Color.WHITE);
}
public void mouseExited(java.awt.event.MouseEvent evt) {
button.setBackground(backgroundColor);
button.setForeground(Color.WHITE);
}
});
return button;
}
private void setupLayout() {
setLayout(new BorderLayout());
setBackground(new Color(248, 249, 250));
setBorder(BorderFactory.createEmptyBorder(30, 30, 30, 30));
// Заголовок
JLabel titleLabel = new JLabel("Настройки подключений к базам данных");
titleLabel.setFont(new Font("Segoe UI", Font.BOLD, 18));
titleLabel.setForeground(new Color(33, 37, 41));
titleLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 20, 0));
// Основная панель с настройками
JPanel mainPanel = new JPanel(new BorderLayout());
mainPanel.setBackground(new Color(248, 249, 250));
// MS Access группа
JPanel accessGroup = createDatabaseGroup("MS Access Database",
"Строка подключения:", accessConnectionField);
// PostgreSQL группа
JPanel postgresGroup = createDatabaseGroup("PostgreSQL Database",
"Параметры подключения:", null);
// PostgreSQL поля
JPanel postgresFields = new JPanel(new GridBagLayout());
postgresFields.setBackground(Color.WHITE);
postgresFields.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(8, 8, 8, 8);
gbc.anchor = GridBagConstraints.WEST;
gbc.gridx = 0; gbc.gridy = 0;
postgresFields.add(createStyledLabel("URL подключения:"), gbc);
gbc.gridx = 1; gbc.gridy = 0;
postgresFields.add(postgresqlConnectionField, gbc);
gbc.gridx = 0; gbc.gridy = 1;
postgresFields.add(createStyledLabel("Имя пользователя:"), gbc);
gbc.gridx = 1; gbc.gridy = 1;
postgresFields.add(postgresqlUsernameField, gbc);
gbc.gridx = 0; gbc.gridy = 2;
postgresFields.add(createStyledLabel("Пароль:"), gbc);
gbc.gridx = 1; gbc.gridy = 2;
postgresFields.add(postgresqlPasswordField, gbc);
postgresGroup.add(postgresFields, BorderLayout.CENTER);
// Панель кнопок с современным дизайном
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 15, 15));
buttonPanel.setBackground(new Color(248, 249, 250));
buttonPanel.add(unlockButton);
buttonPanel.add(saveButton);
buttonPanel.add(testConnectionButton);
// Собираем все вместе
JPanel contentPanel = new JPanel();
contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.Y_AXIS));
contentPanel.setBackground(new Color(248, 249, 250));
contentPanel.add(titleLabel);
contentPanel.add(Box.createVerticalStrut(20));
contentPanel.add(accessGroup);
contentPanel.add(Box.createVerticalStrut(20));
contentPanel.add(postgresGroup);
contentPanel.add(Box.createVerticalStrut(30));
contentPanel.add(buttonPanel);
add(contentPanel, BorderLayout.CENTER);
}
private JPanel createDatabaseGroup(String title, String fieldLabel, JTextField field) {
JPanel group = new JPanel(new BorderLayout());
group.setBackground(Color.WHITE);
group.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(new Color(206, 212, 218), 1),
BorderFactory.createEmptyBorder(15, 15, 15, 15)
));
// Заголовок группы
JLabel groupTitle = new JLabel(title);
groupTitle.setFont(new Font("Segoe UI", Font.BOLD, 14));
groupTitle.setForeground(new Color(33, 37, 41));
groupTitle.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0));
if (field != null) {
JPanel fieldPanel = new JPanel(new BorderLayout());
fieldPanel.setBackground(Color.WHITE);
fieldPanel.add(createStyledLabel(fieldLabel), BorderLayout.WEST);
fieldPanel.add(field, BorderLayout.CENTER);
group.add(fieldPanel, BorderLayout.CENTER);
}
group.add(groupTitle, BorderLayout.NORTH);
return group;
}
private JLabel createStyledLabel(String text) {
JLabel label = new JLabel(text);
label.setFont(new Font("Segoe UI", Font.PLAIN, 13));
label.setForeground(new Color(73, 80, 87));
return label;
}
private void setupEventHandlers() {
unlockButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (!isUnlocked) {
unlockSettings();
} else {
lockSettings();
}
}
});
saveButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
saveSettings();
}
});
testConnectionButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
testConnections();
}
});
}
private void unlockSettings() {
// Создаем кастомный диалог с маскировкой пароля
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
JLabel label = new JLabel("Введите админский пароль:");
label.setFont(new Font("Segoe UI", Font.PLAIN, 13));
label.setForeground(new Color(73, 80, 87));
JPasswordField passwordField = new JPasswordField(20);
passwordField.setFont(new Font("Segoe UI", Font.PLAIN, 13));
passwordField.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(new Color(206, 212, 218), 1),
BorderFactory.createEmptyBorder(8, 12, 8, 12)
));
panel.add(label, BorderLayout.NORTH);
panel.add(passwordField, BorderLayout.CENTER);
int result = JOptionPane.showConfirmDialog(this, panel,
"Аутентификация",
JOptionPane.OK_CANCEL_OPTION,
JOptionPane.QUESTION_MESSAGE);
if (result == JOptionPane.OK_OPTION) {
String password = new String(passwordField.getPassword());
String adminPassword = config.getProperty("admin.password", "admin123");
if (password.equals(adminPassword)) {
isUnlocked = true;
updateFieldStates();
unlockButton.setText("Заблокировать настройки");
unlockButton.setBackground(new Color(220, 53, 69));
// Красивое уведомление об успехе
showSuccessMessage("Настройки разблокированы!\nТеперь вы можете редактировать параметры подключений.");
} else {
showErrorMessage("Неверный пароль!\опробуйте еще раз.");
}
}
}
private void showSuccessMessage(String message) {
JOptionPane.showMessageDialog(this, message, "Успех",
JOptionPane.INFORMATION_MESSAGE);
}
private void showErrorMessage(String message) {
JOptionPane.showMessageDialog(this, message, "Ошибка",
JOptionPane.ERROR_MESSAGE);
}
private void lockSettings() {
isUnlocked = false;
updateFieldStates();
unlockButton.setText("Разблокировать настройки");
unlockButton.setBackground(new Color(40, 167, 69));
showSuccessMessage("Настройки заблокированы!\nДля редактирования введите пароль.");
}
private void updateFieldStates() {
boolean enabled = isUnlocked;
accessConnectionField.setEnabled(enabled);
postgresqlConnectionField.setEnabled(enabled);
postgresqlUsernameField.setEnabled(enabled);
postgresqlPasswordField.setEnabled(enabled);
saveButton.setEnabled(enabled);
testConnectionButton.setEnabled(enabled);
// Современная визуальная индикация блокировки
Color bgColor = enabled ? Color.WHITE : new Color(248, 249, 250);
Color borderColor = enabled ? new Color(206, 212, 218) : new Color(220, 221, 222);
// Применяем стили к полям
styleTextField(accessConnectionField, bgColor, borderColor);
styleTextField(postgresqlConnectionField, bgColor, borderColor);
styleTextField(postgresqlUsernameField, bgColor, borderColor);
styleTextField(postgresqlPasswordField, bgColor, borderColor);
// Добавляем подсказки
if (!enabled) {
accessConnectionField.setToolTipText("Заблокировано - введите пароль для разблокировки");
postgresqlConnectionField.setToolTipText("Заблокировано - введите пароль для разблокировки");
postgresqlUsernameField.setToolTipText("Заблокировано - введите пароль для разблокировки");
postgresqlPasswordField.setToolTipText("Заблокировано - введите пароль для разблокировки");
} else {
accessConnectionField.setToolTipText("Строка подключения к MS Access базе данных");
postgresqlConnectionField.setToolTipText("URL подключения к PostgreSQL (например: jdbc:postgresql://localhost:5432/dbname)");
postgresqlUsernameField.setToolTipText("Имя пользователя PostgreSQL");
postgresqlPasswordField.setToolTipText("Пароль пользователя PostgreSQL");
}
}
private void styleTextField(JTextField field, Color bgColor, Color borderColor) {
field.setBackground(bgColor);
field.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(borderColor, 1),
BorderFactory.createEmptyBorder(8, 12, 8, 12)
));
}
private void loadCurrentSettings() {
accessConnectionField.setText(config.getProperty("access.connection.string", ""));
postgresqlConnectionField.setText(config.getProperty("postgresql.connection.string", ""));
postgresqlUsernameField.setText(config.getProperty("postgresql.username", ""));
postgresqlPasswordField.setText(config.getProperty("postgresql.password", ""));
}
private void saveSettings() {
if (!isUnlocked) {
JOptionPane.showMessageDialog(this,
"Сначала разблокируйте настройки!",
"Ошибка",
JOptionPane.ERROR_MESSAGE);
return;
}
// Обновляем конфигурацию
config.setProperty("access.connection.string", accessConnectionField.getText());
config.setProperty("postgresql.connection.string", postgresqlConnectionField.getText());
config.setProperty("postgresql.username", postgresqlUsernameField.getText());
config.setProperty("postgresql.password", new String(postgresqlPasswordField.getPassword()));
// Сохраняем в файл
try {
config.store(new FileOutputStream("src/main/resources/config.properties"),
"Database Configuration");
JOptionPane.showMessageDialog(this,
"Настройки сохранены успешно!",
"Успех",
JOptionPane.INFORMATION_MESSAGE);
} catch (IOException e) {
JOptionPane.showMessageDialog(this,
"Ошибка сохранения: " + e.getMessage(),
"Ошибка",
JOptionPane.ERROR_MESSAGE);
}
}
private void testConnections() {
if (!isUnlocked) {
JOptionPane.showMessageDialog(this,
"Сначала разблокируйте настройки!",
"Ошибка",
JOptionPane.ERROR_MESSAGE);
return;
}
// Здесь будет логика тестирования подключений
JOptionPane.showMessageDialog(this,
"Функция тестирования подключений будет реализована в следующих версиях",
"Информация",
JOptionPane.INFORMATION_MESSAGE);
}
}

View File

@@ -0,0 +1,389 @@
package org.example;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import org.example.dao.ComponentDAO;
import org.example.dao.StageDAO;
import org.example.dao.LimitDrawingCardDAO;
import java.util.List;
import java.util.Properties;
public class StageController {
private final Properties config;
private final StageManagementService stageService;
private VBox view;
private TextField orderNumberField;
private TextField responsibleField;
private TextField numberPickingCardField;
private Button createStageButton;
private TextArea resultArea;
private TableView<StageDAO.Stage> stageTable;
private TableView<ComponentDAO.Component> componentTable;
private TextField stageIdField;
private Button createComponentsButton;
public StageController(Properties config) {
this.config = config;
this.stageService = new StageManagementService(config);
initializeView();
}
private void initializeView() {
view = new VBox();
view.setSpacing(20);
view.setPadding(new Insets(20));
view.setStyle("-fx-background-color: #ffffff;");
// Заголовок
Label titleLabel = new Label("Управление этапами и компонентами");
titleLabel.setStyle("-fx-font-family: 'Segoe UI'; -fx-font-size: 18px; -fx-font-weight: bold; -fx-text-fill: #2c3e50;");
// Форма создания этапа
VBox createStageForm = createStageForm();
// Результат операции
resultArea = new TextArea();
resultArea.setEditable(false);
resultArea.setPrefHeight(100);
resultArea.setStyle("-fx-font-family: 'Consolas'; -fx-font-size: 12px;");
// Таблица этапов
createStageTable();
// Форма создания компонентов
VBox createComponentsForm = createComponentsForm();
// Таблица компонентов
createComponentTable();
view.getChildren().addAll(
titleLabel,
createStageForm,
resultArea,
stageTable,
createComponentsForm,
componentTable
);
}
private VBox createStageForm() {
VBox form = new VBox();
form.setSpacing(10);
form.setPadding(new Insets(15));
form.setStyle("-fx-background-color: #f8f9fa; -fx-background-radius: 5; -fx-border-color: #dee2e6; -fx-border-width: 1; -fx-border-radius: 5;");
Label formTitle = new Label("Создание этапа");
formTitle.setStyle("-fx-font-family: 'Segoe UI'; -fx-font-size: 14px; -fx-font-weight: bold; -fx-text-fill: #495057;");
// Поле номера заказа
HBox orderNumberBox = new HBox();
orderNumberBox.setSpacing(10);
orderNumberBox.setAlignment(Pos.CENTER_LEFT);
Label orderNumberLabel = new Label("Номер заказа:");
orderNumberLabel.setPrefWidth(120);
orderNumberField = new TextField();
orderNumberField.setPromptText("Введите номер заказа");
orderNumberField.setPrefWidth(150);
orderNumberBox.getChildren().addAll(orderNumberLabel, orderNumberField);
// Поле ответственного
HBox responsibleBox = new HBox();
responsibleBox.setSpacing(10);
responsibleBox.setAlignment(Pos.CENTER_LEFT);
Label responsibleLabel = new Label("Ответственный:");
responsibleLabel.setPrefWidth(120);
responsibleField = new TextField();
responsibleField.setPromptText("Введите ФИО ответственного");
responsibleField.setPrefWidth(200);
responsibleBox.getChildren().addAll(responsibleLabel, responsibleField);
// Поле номера комплектовочной карты
HBox pickingCardBox = new HBox();
pickingCardBox.setSpacing(10);
pickingCardBox.setAlignment(Pos.CENTER_LEFT);
Label pickingCardLabel = new Label("Номер карты:");
pickingCardLabel.setPrefWidth(120);
numberPickingCardField = new TextField();
numberPickingCardField.setPromptText("Введите номер комплектовочной карты");
numberPickingCardField.setPrefWidth(200);
pickingCardBox.getChildren().addAll(pickingCardLabel, numberPickingCardField);
// Кнопка создания этапа
createStageButton = new Button("Создать этап");
createStageButton.setStyle("-fx-background-color: #007bff; -fx-text-fill: white; -fx-font-family: 'Segoe UI'; -fx-font-weight: bold; -fx-font-size: 12px; -fx-padding: 8 16; -fx-background-radius: 4; -fx-cursor: hand;");
createStageButton.setOnAction(e -> createStage());
form.getChildren().addAll(formTitle, orderNumberBox, responsibleBox, pickingCardBox, createStageButton);
return form;
}
private void createStageTable() {
stageTable = new TableView<>();
stageTable.setPrefHeight(200);
TableColumn<StageDAO.Stage, Integer> idColumn = new TableColumn<>("ID");
idColumn.setCellValueFactory(cellData -> new javafx.beans.property.SimpleIntegerProperty(cellData.getValue().getId()).asObject());
idColumn.setPrefWidth(50);
TableColumn<StageDAO.Stage, Integer> stageNumberColumn = new TableColumn<>("№ Этапа");
stageNumberColumn.setCellValueFactory(cellData -> new javafx.beans.property.SimpleIntegerProperty(cellData.getValue().getStageNumber()).asObject());
stageNumberColumn.setPrefWidth(80);
TableColumn<StageDAO.Stage, String> responsibleColumn = new TableColumn<>("Ответственный");
responsibleColumn.setCellValueFactory(cellData -> new javafx.beans.property.SimpleStringProperty(cellData.getValue().getResponsible()));
responsibleColumn.setPrefWidth(150);
TableColumn<StageDAO.Stage, String> pickingCardColumn = new TableColumn<>("Номер карты");
pickingCardColumn.setCellValueFactory(cellData -> new javafx.beans.property.SimpleStringProperty(cellData.getValue().getNumberPickingCard()));
pickingCardColumn.setPrefWidth(120);
TableColumn<StageDAO.Stage, String> timestampColumn = new TableColumn<>("Дата создания");
timestampColumn.setCellValueFactory(cellData -> new javafx.beans.property.SimpleStringProperty(cellData.getValue().getTimestamp()));
timestampColumn.setPrefWidth(150);
stageTable.getColumns().addAll(idColumn, stageNumberColumn, responsibleColumn, pickingCardColumn, timestampColumn);
}
private VBox createComponentsForm() {
VBox form = new VBox();
form.setSpacing(10);
form.setPadding(new Insets(15));
form.setStyle("-fx-background-color: #f8f9fa; -fx-background-radius: 5; -fx-border-color: #dee2e6; -fx-border-width: 1; -fx-border-radius: 5;");
Label formTitle = new Label("Создание компонентов для этапа");
formTitle.setStyle("-fx-font-family: 'Segoe UI'; -fx-font-size: 14px; -fx-font-weight: bold; -fx-text-fill: #495057;");
// Поле ID этапа
HBox stageIdBox = new HBox();
stageIdBox.setSpacing(10);
stageIdBox.setAlignment(Pos.CENTER_LEFT);
Label stageIdLabel = new Label("ID этапа:");
stageIdLabel.setPrefWidth(120);
stageIdField = new TextField();
stageIdField.setPromptText("Введите ID этапа");
stageIdField.setPrefWidth(150);
stageIdBox.getChildren().addAll(stageIdLabel, stageIdField);
// Кнопка создания компонентов
createComponentsButton = new Button("Создать компоненты");
createComponentsButton.setStyle("-fx-background-color: #28a745; -fx-text-fill: white; -fx-font-family: 'Segoe UI'; -fx-font-weight: bold; -fx-font-size: 12px; -fx-padding: 8 16; -fx-background-radius: 4; -fx-cursor: hand;");
createComponentsButton.setOnAction(e -> createComponents());
form.getChildren().addAll(formTitle, stageIdBox, createComponentsButton);
return form;
}
private void createComponentTable() {
componentTable = new TableView<>();
componentTable.setPrefHeight(200);
TableColumn<ComponentDAO.Component, Integer> idColumn = new TableColumn<>("ID");
idColumn.setCellValueFactory(cellData -> new javafx.beans.property.SimpleIntegerProperty(cellData.getValue().getId()).asObject());
idColumn.setPrefWidth(50);
TableColumn<ComponentDAO.Component, String> nameColumn = new TableColumn<>("Наименование");
nameColumn.setCellValueFactory(cellData -> new javafx.beans.property.SimpleStringProperty(cellData.getValue().getName()));
nameColumn.setPrefWidth(200);
TableColumn<ComponentDAO.Component, String> supplierColumn = new TableColumn<>("Поставщик");
supplierColumn.setCellValueFactory(cellData -> new javafx.beans.property.SimpleStringProperty(cellData.getValue().getNameSupplier()));
supplierColumn.setPrefWidth(150);
TableColumn<ComponentDAO.Component, String> articleColumn = new TableColumn<>("Артикул");
articleColumn.setCellValueFactory(cellData -> new javafx.beans.property.SimpleStringProperty(cellData.getValue().getArticle()));
articleColumn.setPrefWidth(100);
TableColumn<ComponentDAO.Component, Integer> expendColumn = new TableColumn<>("Расход");
expendColumn.setCellValueFactory(cellData -> new javafx.beans.property.SimpleIntegerProperty(cellData.getValue().getExpend()).asObject());
expendColumn.setPrefWidth(80);
TableColumn<ComponentDAO.Component, String> transactionColumn = new TableColumn<>("ID транзакции");
transactionColumn.setCellValueFactory(cellData -> new javafx.beans.property.SimpleStringProperty(cellData.getValue().getIdTransaction()));
transactionColumn.setPrefWidth(120);
componentTable.getColumns().addAll(idColumn, nameColumn, supplierColumn, articleColumn, expendColumn, transactionColumn);
}
private void createStage() {
String orderNumberText = orderNumberField.getText().trim();
String responsible = responsibleField.getText().trim();
String numberPickingCard = numberPickingCardField.getText().trim();
if (orderNumberText.isEmpty() || responsible.isEmpty() || numberPickingCard.isEmpty()) {
showMessage("Ошибка: Заполните все поля");
return;
}
try {
int orderNumber = Integer.parseInt(orderNumberText);
createStageButton.setDisable(true);
createStageButton.setText("Создание...");
Task<StageManagementService.StageCreationResult> task = new Task<StageManagementService.StageCreationResult>() {
@Override
protected StageManagementService.StageCreationResult call() throws Exception {
return stageService.createStage(orderNumber, responsible, numberPickingCard);
}
@Override
protected void succeeded() {
Platform.runLater(() -> {
StageManagementService.StageCreationResult result = getValue();
showMessage(result.getMessage());
if (result.isSuccess()) {
// Очищаем поля формы
orderNumberField.clear();
responsibleField.clear();
numberPickingCardField.clear();
// Обновляем таблицу этапов
loadStagesForOrder(orderNumber);
}
createStageButton.setDisable(false);
createStageButton.setText("Создать этап");
});
}
@Override
protected void failed() {
Platform.runLater(() -> {
showMessage("Ошибка: " + getException().getMessage());
createStageButton.setDisable(false);
createStageButton.setText("Создать этап");
});
}
};
Thread thread = new Thread(task);
thread.setDaemon(true);
thread.start();
} catch (NumberFormatException e) {
showMessage("Ошибка: Номер заказа должен быть числом");
}
}
private void createComponents() {
String stageIdText = stageIdField.getText().trim();
if (stageIdText.isEmpty()) {
showMessage("Ошибка: Введите ID этапа");
return;
}
try {
int stageId = Integer.parseInt(stageIdText);
// Создаем тестовые компоненты (в реальном приложении это будет форма ввода)
List<ComponentDAO.Component> components = List.of(
new ComponentDAO.Component(0, "Резистор R1", "ООО Поставщик", "Проект А", "Электроника", "R001", "2025-01-04", 1, 10, null, "PC001", "Расход", "TXN001", stageId),
new ComponentDAO.Component(0, "Конденсатор C1", "ООО Поставщик", "Проект А", "Электроника", "C001", "2025-01-04", 2, 5, null, "PC001", "Расход", "TXN002", stageId),
new ComponentDAO.Component(0, "Микросхема IC1", "ООО Поставщик", "Проект А", "Электроника", "IC001", "2025-01-04", 3, 2, null, "PC001", "Расход", "TXN003", stageId)
);
createComponentsButton.setDisable(true);
createComponentsButton.setText("Создание...");
Task<List<ComponentDAO.Component>> task = new Task<List<ComponentDAO.Component>>() {
@Override
protected List<ComponentDAO.Component> call() throws Exception {
return stageService.createComponentsFromStage(stageId, components);
}
@Override
protected void succeeded() {
Platform.runLater(() -> {
showMessage("Компоненты успешно созданы для этапа " + stageId);
stageIdField.clear();
loadComponentsForStage(stageId);
createComponentsButton.setDisable(false);
createComponentsButton.setText("Создать компоненты");
});
}
@Override
protected void failed() {
Platform.runLater(() -> {
showMessage("Ошибка: " + getException().getMessage());
createComponentsButton.setDisable(false);
createComponentsButton.setText("Создать компоненты");
});
}
};
Thread thread = new Thread(task);
thread.setDaemon(true);
thread.start();
} catch (NumberFormatException e) {
showMessage("Ошибка: ID этапа должен быть числом");
}
}
private void loadStagesForOrder(int orderNumber) {
Task<List<StageDAO.Stage>> task = new Task<List<StageDAO.Stage>>() {
@Override
protected List<StageDAO.Stage> call() throws Exception {
return stageService.getStagesByOrderNumber(orderNumber);
}
@Override
protected void succeeded() {
Platform.runLater(() -> {
stageTable.getItems().clear();
stageTable.getItems().addAll(getValue());
});
}
};
Thread thread = new Thread(task);
thread.setDaemon(true);
thread.start();
}
private void loadComponentsForStage(int stageId) {
Task<List<ComponentDAO.Component>> task = new Task<List<ComponentDAO.Component>>() {
@Override
protected List<ComponentDAO.Component> call() throws Exception {
return stageService.getComponentsByStageId(stageId);
}
@Override
protected void succeeded() {
Platform.runLater(() -> {
componentTable.getItems().clear();
componentTable.getItems().addAll(getValue());
});
}
};
Thread thread = new Thread(task);
thread.setDaemon(true);
thread.start();
}
private void showMessage(String message) {
resultArea.appendText(message + "\n");
resultArea.setScrollTop(Double.MAX_VALUE);
}
public VBox getView() {
return view;
}
}

View File

@@ -0,0 +1,189 @@
package org.example;
import org.example.dao.LimitDrawingCardDAO;
import org.example.dao.StageDAO;
import org.example.dao.ComponentDAO;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.List;
import java.util.Properties;
public class StageManagementService {
private final Properties config;
private final LimitDrawingCardDAO limitDrawingCardDAO;
private final StageDAO stageDAO;
private final ComponentDAO componentDAO;
public StageManagementService(Properties config) {
this.config = config;
// Передаем параметры подключения в системные свойства
System.setProperty("postgresql.connection.string", config.getProperty("postgresql.connection.string", ""));
System.setProperty("postgresql.username", config.getProperty("postgresql.username", ""));
System.setProperty("postgresql.password", config.getProperty("postgresql.password", ""));
this.limitDrawingCardDAO = new LimitDrawingCardDAO(config);
this.stageDAO = new StageDAO(config);
this.componentDAO = new ComponentDAO(config);
}
public static class StageCreationResult {
private final boolean success;
private final String message;
private final StageDAO.Stage createdStage;
private final LimitDrawingCardDAO.LimitDrawingCard limitDrawingCard;
public StageCreationResult(boolean success, String message,
StageDAO.Stage createdStage,
LimitDrawingCardDAO.LimitDrawingCard limitDrawingCard) {
this.success = success;
this.message = message;
this.createdStage = createdStage;
this.limitDrawingCard = limitDrawingCard;
}
public boolean isSuccess() { return success; }
public String getMessage() { return message; }
public StageDAO.Stage getCreatedStage() { return createdStage; }
public LimitDrawingCardDAO.LimitDrawingCard getLimitDrawingCard() { return limitDrawingCard; }
}
public StageCreationResult createStage(int orderNumber, String responsible, String numberPickingCard) {
try (Connection conn = getConnection()) {
conn.setAutoCommit(false);
try {
// 1. Проверяем существование limit_drawing_card по order_number
LimitDrawingCardDAO.LimitDrawingCard existingCard = limitDrawingCardDAO.findByOrderNumber(orderNumber);
LimitDrawingCardDAO.LimitDrawingCard limitDrawingCard;
if (existingCard != null) {
// Запись существует - используем её
limitDrawingCard = existingCard;
} else {
// Записи нет - создаем новую
limitDrawingCard = limitDrawingCardDAO.create(orderNumber, responsible);
if (limitDrawingCard == null) {
throw new SQLException("Не удалось создать limit_drawing_card");
}
}
// 2. Создаем новый этап
StageDAO.Stage newStage = stageDAO.create(
limitDrawingCard.getId(),
responsible,
numberPickingCard
);
if (newStage == null) {
throw new SQLException("Не удалось создать этап");
}
conn.commit();
return new StageCreationResult(
true,
String.format("Этап №%d успешно создан для заказа %d",
newStage.getStageNumber(), orderNumber),
newStage,
limitDrawingCard
);
} catch (SQLException e) {
conn.rollback();
return new StageCreationResult(
false,
"Ошибка создания этапа: " + e.getMessage(),
null,
null
);
}
} catch (SQLException e) {
return new StageCreationResult(
false,
"Ошибка подключения к базе данных: " + e.getMessage(),
null,
null
);
}
}
public List<ComponentDAO.Component> createComponentsFromStage(int stageId, List<ComponentDAO.Component> components) {
try (Connection conn = getConnection()) {
conn.setAutoCommit(false);
try {
// Проверяем существование этапа
StageDAO.Stage stage = stageDAO.findById(stageId);
if (stage == null) {
throw new SQLException("Этап с ID " + stageId + " не найден");
}
// Создаем компоненты для этапа
for (ComponentDAO.Component component : components) {
component.setStageId(stageId);
ComponentDAO.Component createdComponent = componentDAO.create(component);
if (createdComponent == null) {
throw new SQLException("Не удалось создать компонент: " + component.getName());
}
}
conn.commit();
return components;
} catch (SQLException e) {
conn.rollback();
throw new RuntimeException("Ошибка создания компонентов: " + e.getMessage());
}
} catch (SQLException e) {
throw new RuntimeException("Ошибка подключения к базе данных: " + e.getMessage());
}
}
public List<StageDAO.Stage> getStagesByOrderNumber(int orderNumber) {
try {
LimitDrawingCardDAO.LimitDrawingCard card = limitDrawingCardDAO.findByOrderNumber(orderNumber);
if (card == null) {
return List.of(); // Пустой список, если карта не найдена
}
return stageDAO.findByLimitDrawingCardId(card.getId());
} catch (SQLException e) {
throw new RuntimeException("Ошибка получения этапов: " + e.getMessage());
}
}
public List<ComponentDAO.Component> getComponentsByStageId(int stageId) {
try {
return componentDAO.findByStageId(stageId);
} catch (SQLException e) {
throw new RuntimeException("Ошибка получения компонентов: " + e.getMessage());
}
}
private Connection getConnection() throws SQLException {
try {
// Явно загружаем PostgreSQL драйвер
Class.forName("org.postgresql.Driver");
} catch (ClassNotFoundException e) {
throw new SQLException("PostgreSQL драйвер не найден в classpath", e);
}
String connectionString = config.getProperty("postgresql.connection.string", "");
String username = config.getProperty("postgresql.username", "");
String password = config.getProperty("postgresql.password", "");
if (connectionString.isEmpty() || username.isEmpty()) {
throw new SQLException("Не настроено подключение к PostgreSQL");
}
// Исправляем строку подключения если нужно
String correctedConnectionString = connectionString;
if (!connectionString.startsWith("jdbc:postgresql://")) {
correctedConnectionString = "jdbc:postgresql://" + connectionString;
}
return DriverManager.getConnection(correctedConnectionString, username, password);
}
}

View File

@@ -0,0 +1,642 @@
package org.example;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.stage.Stage;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.Properties;
public class WeeklyReportController {
private Properties config;
private EnhancedTableView<WeeklyTransaction> weeklyTable;
private ObservableList<WeeklyTransaction> weeklyData = FXCollections.observableArrayList();
private LocalDate startDate;
private LocalDate endDate;
private LocalDate hoverDate; // Для подсветки при наведении
private YearMonth currentMonth = YearMonth.now();
private HBox calendarContainer;
private Label periodInfoLabel; // сохраняем ссылку, чтобы обновлять текст
public WeeklyReportController() {
loadConfig();
initializeView();
}
private void loadConfig() {
config = new Properties();
try (InputStream input = getClass().getClassLoader().getResourceAsStream("config.properties")) {
if (input != null) {
config.load(input);
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void initializeView() {
createWeeklyTable();
}
private void createWeeklyTable() {
weeklyTable = new EnhancedTableView<>();
// Колонки для еженедельного отчета
TableColumn<WeeklyTransaction, Integer> transactionIdColumn = new TableColumn<>("ID Транзакции");
transactionIdColumn.setCellValueFactory(new PropertyValueFactory<>("transactionId"));
transactionIdColumn.setPrefWidth(120);
transactionIdColumn.setComparator((Integer a, Integer b) -> {
if (a == null && b == null) return 0;
if (a == null) return -1;
if (b == null) return 1;
return Integer.compare(a, b);
});
TableColumn<WeeklyTransaction, Integer> productIdColumn = new TableColumn<>("ID Товара");
productIdColumn.setCellValueFactory(new PropertyValueFactory<>("productId"));
productIdColumn.setPrefWidth(100);
productIdColumn.setComparator((Integer a, Integer b) -> {
if (a == null && b == null) return 0;
if (a == null) return -1;
if (b == null) return 1;
return Integer.compare(a, b);
});
TableColumn<WeeklyTransaction, String> categoryColumn = new TableColumn<>("Категория");
categoryColumn.setCellValueFactory(new PropertyValueFactory<>("category"));
categoryColumn.setPrefWidth(120);
TableColumn<WeeklyTransaction, String> nameColumn = new TableColumn<>("Наименование");
nameColumn.setCellValueFactory(new PropertyValueFactory<>("name"));
nameColumn.setPrefWidth(200);
TableColumn<WeeklyTransaction, String> supplierNameColumn = new TableColumn<>("Наименование у поставщика");
supplierNameColumn.setCellValueFactory(new PropertyValueFactory<>("supplierName"));
supplierNameColumn.setPrefWidth(200);
TableColumn<WeeklyTransaction, String> projectColumn = new TableColumn<>("Проект");
projectColumn.setCellValueFactory(new PropertyValueFactory<>("project"));
projectColumn.setPrefWidth(150);
TableColumn<WeeklyTransaction, String> articleColumn = new TableColumn<>("Артикул");
articleColumn.setCellValueFactory(new PropertyValueFactory<>("article"));
articleColumn.setPrefWidth(120);
TableColumn<WeeklyTransaction, Integer> consumedColumn = new TableColumn<>("Израсходовано");
consumedColumn.setCellValueFactory(new PropertyValueFactory<>("consumed"));
consumedColumn.setPrefWidth(120);
consumedColumn.setComparator((Integer a, Integer b) -> {
if (a == null && b == null) return 0;
if (a == null) return -1;
if (b == null) return 1;
return Integer.compare(a, b);
});
TableColumn<WeeklyTransaction, String> expenseTypeColumn = new TableColumn<>("Тип расхода");
expenseTypeColumn.setCellValueFactory(new PropertyValueFactory<>("expenseType"));
expenseTypeColumn.setPrefWidth(120);
TableColumn<WeeklyTransaction, String> taskNumberColumn = new TableColumn<>("№ Задачи");
taskNumberColumn.setCellValueFactory(new PropertyValueFactory<>("taskNumber"));
taskNumberColumn.setPrefWidth(120);
TableColumn<WeeklyTransaction, String> responsibleColumn = new TableColumn<>("Ответственный");
responsibleColumn.setCellValueFactory(new PropertyValueFactory<>("responsible"));
responsibleColumn.setPrefWidth(150);
// Добавляем колонки в таблицу
weeklyTable.getColumns().addAll(
transactionIdColumn, productIdColumn, categoryColumn, nameColumn,
supplierNameColumn, projectColumn, articleColumn, consumedColumn,
expenseTypeColumn, taskNumberColumn, responsibleColumn
);
// Настройка таблицы
weeklyTable.setItems(weeklyData);
weeklyTable.setPrefHeight(400);
weeklyTable.setStyle("-fx-background-color: white;");
// Загружаем начальные данные (текущая неделя)
loadWeeklyData();
}
private void loadWeeklyData() {
// Используем выбранные даты или текущую неделю по умолчанию
LocalDate startOfWeek, endOfWeek;
if (startDate != null && endDate != null) {
startOfWeek = startDate;
endOfWeek = endDate;
} else {
// Получаем текущую неделю по умолчанию
LocalDate now = LocalDate.now();
startOfWeek = now.minusDays(now.getDayOfWeek().getValue() - 1);
endOfWeek = startOfWeek.plusDays(6);
}
// Обновляем визуальную информацию о периоде
this.startDate = startOfWeek;
this.endDate = endOfWeek;
updatePeriodInfo();
try (Connection conn = getConnection()) {
if (conn != null) {
String sql = """
SELECT s.Наименование, s.[Наименование у поставщика], s.Артикул, s.Категория, s.Проект,
k.[Номер корзины], k.Наименование as Названиеорзины, k.[№Задачи],
r.[Имя ответственного] as Ответственный, s.[ID товара], t.[ID Транзакции], t.Дата, t.[Тип расхода],
(t.Расход - t.Приход) as Израсходовано
FROM Склад s
INNER JOIN Транзакции t ON s.[ID компонента] = t.[ID Компонента]
INNER JOIN [Комплектовочная корзина] k ON t.[Комплектовочная корзина] = k.[Код]
LEFT JOIN Ответственный r ON k.Ответственный = r.Код
WHERE t.Дата >= ? AND t.Дата <= ? AND t.[Тип расхода] = 'Ремонт'
ORDER BY t.Дата DESC, t.[ID Транзакции] DESC
""";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setDate(1, Date.valueOf(startOfWeek));
stmt.setDate(2, Date.valueOf(endOfWeek));
try (ResultSet rs = stmt.executeQuery()) {
weeklyData.clear();
while (rs.next()) {
WeeklyTransaction transaction = new WeeklyTransaction();
transaction.setTransactionId(rs.getInt("ID Транзакции"));
transaction.setProductId(rs.getInt("ID товара"));
transaction.setCategory(rs.getString("Категория"));
transaction.setName(rs.getString("Наименование"));
transaction.setSupplierName(rs.getString("Наименование у поставщика"));
transaction.setProject(rs.getString("Проект"));
transaction.setArticle(rs.getString("Артикул"));
transaction.setConsumed(rs.getInt("Израсходовано"));
transaction.setExpenseType(rs.getString("Тип расхода"));
transaction.setTaskNumber(rs.getString("№Задачи"));
transaction.setResponsible(rs.getString("Ответственный"));
transaction.setBasketNumber(rs.getString("Номер корзины"));
weeklyData.add(transaction);
}
}
}
}
} catch (SQLException e) {
e.printStackTrace();
}
}
private Connection getConnection() {
try {
String connectionString = config.getProperty("access.connection.string");
if (connectionString == null || connectionString.isEmpty()) {
System.err.println("Ошибка: Не настроено подключение к MS Access в config.properties");
return null;
}
// Загружаем драйвер UCanAccess
Class.forName("net.ucanaccess.jdbc.UcanaccessDriver");
return DriverManager.getConnection(connectionString);
} catch (SQLException e) {
System.err.println("Ошибка подключения к MS Access: " + e.getMessage());
e.printStackTrace();
return null;
} catch (ClassNotFoundException e) {
System.err.println("Ошибка: Драйвер UCanAccess не найден");
e.printStackTrace();
return null;
}
}
public VBox getView() {
VBox mainView = new VBox();
mainView.setSpacing(20);
mainView.setPadding(new Insets(20));
mainView.setStyle("-fx-background-color: #f8f9fa;");
// Заголовок
Label title = new Label("Еженедельное списание");
title.setFont(Font.font("Segoe UI", FontWeight.BOLD, 24));
title.setTextFill(Color.web("#2c3e50"));
title.setPadding(new Insets(0, 0, 20, 0));
// Создаем кастомный календарь в стиле Google Calendar
calendarContainer = new HBox(30);
calendarContainer.setAlignment(Pos.CENTER);
// Панель навигации
HBox nav = createNavigationBar();
// Устанавливаем текущую неделю по умолчанию (до отображения календарей)
LocalDate now = LocalDate.now();
LocalDate startOfWeek = now.minusDays(now.getDayOfWeek().getValue() - 1);
LocalDate endOfWeek = startOfWeek.plusDays(6);
startDate = startOfWeek;
endDate = endOfWeek;
// Обновляем календари (после установки start/end)
refreshCalendars();
// Информация о периоде
periodInfoLabel = new Label(String.format("📊 Период: %s - %s",
startOfWeek.format(DateTimeFormatter.ofPattern("dd.MM.yyyy")),
endOfWeek.format(DateTimeFormatter.ofPattern("dd.MM.yyyy"))));
periodInfoLabel.setFont(Font.font("Segoe UI", FontWeight.BOLD, 15));
periodInfoLabel.setTextFill(Color.web("#495057"));
periodInfoLabel.setPadding(new Insets(10, 0, 0, 0));
periodInfoLabel.setStyle("""
-fx-background-color: #f8f9fa;
-fx-border-color: #e9ecef;
-fx-border-radius: 6;
-fx-border-width: 1;
-fx-padding: 8 12;
""");
// Кнопки управления
Button refreshButton = new Button("🔄 Обновить данные");
refreshButton.setStyle("""
-fx-background-color: #007bff;
-fx-text-fill: white;
-fx-font-weight: bold;
-fx-font-size: 14px;
-fx-padding: 10 20;
-fx-background-radius: 6;
-fx-effect: dropshadow(gaussian, rgba(0,123,255,0.3), 4, 0, 0, 2);
""");
refreshButton.setOnAction(e -> loadWeeklyData());
Button resetButton = new Button("🔄 Сбросить");
resetButton.setStyle("""
-fx-background-color: linear-gradient(to right, #74b9ff, #0984e3);
-fx-text-fill: white;
-fx-background-radius: 8;
-fx-cursor: hand;
-fx-font-family: 'Segoe UI Semibold';
-fx-padding: 8 16;
-fx-font-weight: bold;
-fx-font-size: 14px;
""");
resetButton.setOnAction(e -> {
startDate = null;
endDate = null;
hoverDate = null;
updatePeriodInfo();
refreshCalendars();
});
Button exportButton = new Button("📊 Экспорт в Excel");
exportButton.setStyle("""
-fx-background-color: linear-gradient(to right, #28a745, #20c997);
-fx-text-fill: white;
-fx-background-radius: 8;
-fx-cursor: hand;
-fx-font-family: 'Segoe UI Semibold';
-fx-padding: 8 16;
-fx-font-weight: bold;
-fx-font-size: 14px;
""");
exportButton.setOnAction(e -> exportToExcel());
HBox buttonBox = new HBox(refreshButton, resetButton, exportButton);
buttonBox.setSpacing(10);
// Контейнер для выбора дат
VBox dateSelectionBox = new VBox();
dateSelectionBox.setSpacing(15);
dateSelectionBox.setPadding(new Insets(20));
dateSelectionBox.setStyle("""
-fx-background-color: white;
-fx-border-color: #dee2e6;
-fx-border-radius: 12;
-fx-border-width: 1;
-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.08), 8, 0, 0, 2);
""");
Label dateLabel = new Label("📅 Выберите период для отчета:");
dateLabel.setFont(Font.font("Segoe UI", FontWeight.BOLD, 16));
dateLabel.setTextFill(Color.web("#2c3e50"));
// Добавляем навигацию и календари
dateSelectionBox.getChildren().addAll(dateLabel, nav, calendarContainer, periodInfoLabel);
// Добавляем все элементы
mainView.getChildren().addAll(title, dateSelectionBox, buttonBox, weeklyTable);
return mainView;
}
private void updatePeriodInfo() {
if (periodInfoLabel == null) return;
if (startDate != null && endDate != null) {
periodInfoLabel.setText(String.format("📊 Период: %s - %s",
startDate.format(DateTimeFormatter.ofPattern("dd.MM.yyyy")),
endDate.format(DateTimeFormatter.ofPattern("dd.MM.yyyy"))));
} else if (startDate != null) {
periodInfoLabel.setText(String.format("📊 Начата выборка: %s — выберите вторую дату",
startDate.format(DateTimeFormatter.ofPattern("dd.MM.yyyy"))));
} else {
periodInfoLabel.setText("📊 Период: не выбран");
}
}
// Создаем панель навигации с стрелками
private HBox createNavigationBar() {
Button prev = new Button("");
Button next = new Button("");
Label monthLabel = new Label();
updateMonthLabel(monthLabel);
prev.setFont(Font.font("Segoe UI", FontWeight.BOLD, 18));
next.setFont(Font.font("Segoe UI", FontWeight.BOLD, 18));
prev.setStyle("""
-fx-background-color: transparent;
-fx-cursor: hand;
-fx-text-fill: #495057;
-fx-padding: 8 12;
-fx-background-radius: 6;
""");
next.setStyle("""
-fx-background-color: transparent;
-fx-cursor: hand;
-fx-text-fill: #495057;
-fx-padding: 8 12;
-fx-background-radius: 6;
""");
prev.setOnAction(e -> {
currentMonth = currentMonth.minusMonths(1);
updateMonthLabel(monthLabel);
refreshCalendars();
});
next.setOnAction(e -> {
currentMonth = currentMonth.plusMonths(1);
updateMonthLabel(monthLabel);
refreshCalendars();
});
HBox nav = new HBox(20, prev, monthLabel, next);
nav.setAlignment(Pos.CENTER);
nav.setPadding(new Insets(10));
return nav;
}
private void updateMonthLabel(Label monthLabel) {
String name = currentMonth.getMonth().getDisplayName(java.time.format.TextStyle.FULL, java.util.Locale.forLanguageTag("ru"));
monthLabel.setText(Character.toUpperCase(name.charAt(0)) + name.substring(1) + " " + currentMonth.getYear());
monthLabel.setFont(Font.font("Segoe UI", FontWeight.BOLD, 16));
monthLabel.setTextFill(Color.web("#2c3e50"));
}
private GridPane createCalendar(YearMonth month) {
GridPane grid = new GridPane();
grid.setHgap(4);
grid.setVgap(4);
grid.setAlignment(javafx.geometry.Pos.CENTER);
Label monthLabel = new Label(formatMonthName(month));
monthLabel.setFont(Font.font("Segoe UI", FontWeight.BOLD, 15));
monthLabel.setTextFill(Color.web("#2c3e50"));
grid.add(monthLabel, 0, 0, 7, 1);
String[] days = {"Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс"};
for (int i = 0; i < days.length; i++) {
Label dayLabel = new Label(days[i]);
dayLabel.setTextFill(Color.GRAY);
dayLabel.setFont(Font.font("Segoe UI", 12));
dayLabel.setPrefWidth(40);
dayLabel.setAlignment(javafx.geometry.Pos.CENTER);
grid.add(dayLabel, i, 1);
}
LocalDate firstDay = month.atDay(1);
int dayOfWeek = firstDay.getDayOfWeek().getValue(); // 1 = Пн, 7 = Вс
int col = dayOfWeek - 1;
int row = 2;
for (int day = 1; day <= month.lengthOfMonth(); day++) {
LocalDate date = month.atDay(day);
Button dayButton = new Button(String.valueOf(day));
styleDayButton(dayButton, date);
// Клик
dayButton.setOnAction(e -> {
handleDateSelection(date);
refreshCalendars();
});
// Наведение
dayButton.setOnMouseEntered(e -> {
hoverDate = date;
refreshCalendars();
});
dayButton.setOnMouseExited(e -> {
hoverDate = null;
refreshCalendars();
});
grid.add(dayButton, col, row);
col++;
if (col > 6) {
col = 0;
row++;
}
}
grid.setPadding(new Insets(10));
grid.setStyle("-fx-background-color: #f8f9fa; -fx-background-radius: 8;");
return grid;
}
private String formatMonthName(YearMonth month) {
String name = month.getMonth().getDisplayName(java.time.format.TextStyle.FULL, java.util.Locale.forLanguageTag("ru"));
return Character.toUpperCase(name.charAt(0)) + name.substring(1) + " " + month.getYear();
}
private void styleDayButton(Button btn, LocalDate date) {
btn.setFont(Font.font("Segoe UI", FontWeight.MEDIUM, 13));
btn.setAlignment(Pos.CENTER);
// Базовый стиль
String style = "-fx-background-color: transparent; -fx-text-fill: #2c3e50; -fx-cursor: hand;";
// Подсветка диапазона (включая hover). Учитываем случай, когда hoverDate < startDate
if (startDate != null && (endDate != null || hoverDate != null)) {
LocalDate end = (endDate != null) ? endDate : hoverDate;
if (end != null) {
LocalDate from = startDate;
LocalDate to = end;
if (to.isBefore(from)) {
LocalDate tmp = from;
from = to;
to = tmp;
}
if (!date.isBefore(from) && !date.isAfter(to)) {
style = "-fx-background-color: #d0ebff; -fx-text-fill: #1c4966; -fx-background-radius: 6; -fx-border-color: #74c0fc; -fx-border-radius: 6;";
}
}
}
// Начало диапазона
if (startDate != null && date.equals(startDate)) {
style = "-fx-background-color: #339af0; -fx-text-fill: white; -fx-background-radius: 6;";
}
// Конец диапазона
if (endDate != null && date.equals(endDate)) {
style = "-fx-background-color: #1e90ff; -fx-text-fill: white; -fx-background-radius: 6;";
}
// Выделение текущей даты (добавляем к существующему стилю)
if (date.isEqual(LocalDate.now())) {
style = style + " -fx-border-color: #adb5bd; -fx-border-width: 1;";
}
// Отключаем далеко лежащие даты
if (date.isBefore(LocalDate.now().minusYears(1)) || date.isAfter(LocalDate.now().plusYears(1))) {
btn.setDisable(true);
btn.setOpacity(0.4);
}
btn.setStyle(style);
}
private void handleDateSelection(LocalDate clicked) {
if (startDate == null || (startDate != null && endDate != null)) {
startDate = clicked;
endDate = null;
updatePeriodInfo();
} else {
endDate = clicked;
if (endDate.isBefore(startDate)) {
LocalDate temp = startDate;
startDate = endDate;
endDate = temp;
}
updatePeriodInfo();
// Автоматически загружаем данные при выборе диапазона
loadWeeklyData();
}
}
private void refreshCalendars() {
// Обновляем календари
if (calendarContainer == null) return;
calendarContainer.getChildren().clear();
YearMonth nextMonth = currentMonth.plusMonths(1);
calendarContainer.getChildren().addAll(createCalendar(currentMonth), createCalendar(nextMonth));
// Обновляем информацию о периоде
updatePeriodInfo();
}
private void exportToExcel() {
if (weeklyData.isEmpty()) {
// Показываем предупреждение, если нет данных
javafx.scene.control.Alert alert = new javafx.scene.control.Alert(javafx.scene.control.Alert.AlertType.WARNING);
alert.setTitle("Нет данных для экспорта");
alert.setHeaderText("Отсутствуют данные для экспорта");
alert.setContentText("Сначала загрузите данные, выбрав период в календаре.");
alert.showAndWait();
return;
}
try {
// Получаем Stage из любого элемента интерфейса
Stage stage = (Stage) weeklyTable.getScene().getWindow();
// Экспортируем данные (без выбора КК для еженедельных списаний)
ExcelExporter.exportWeeklyReport(weeklyData, startDate, endDate, stage, false, null);
// Показываем сообщение об успехе
javafx.scene.control.Alert successAlert = new javafx.scene.control.Alert(javafx.scene.control.Alert.AlertType.INFORMATION);
successAlert.setTitle("Экспорт завершен");
successAlert.setHeaderText("Отчет успешно экспортирован в Excel");
successAlert.setContentText("Файл сохранен в выбранной папке.");
successAlert.showAndWait();
} catch (Exception e) {
// Показываем ошибку
javafx.scene.control.Alert errorAlert = new javafx.scene.control.Alert(javafx.scene.control.Alert.AlertType.ERROR);
errorAlert.setTitle("Ошибка экспорта");
errorAlert.setHeaderText("Не удалось экспортировать данные");
errorAlert.setContentText("Ошибка: " + e.getMessage());
errorAlert.showAndWait();
e.printStackTrace();
}
}
// Класс для данных еженедельного отчета
public static class WeeklyTransaction {
private int transactionId;
private int productId;
private String category;
private String name;
private String supplierName;
private String project;
private String article;
private int consumed;
private String expenseType;
private String taskNumber;
private String responsible;
private String basketNumber;
// Геттеры и сеттеры
public int getTransactionId() { return transactionId; }
public void setTransactionId(int transactionId) { this.transactionId = transactionId; }
public int getProductId() { return productId; }
public void setProductId(int productId) { this.productId = productId; }
public String getCategory() { return category; }
public void setCategory(String category) { this.category = category; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getSupplierName() { return supplierName; }
public void setSupplierName(String supplierName) { this.supplierName = supplierName; }
public String getProject() { return project; }
public void setProject(String project) { this.project = project; }
public String getArticle() { return article; }
public void setArticle(String article) { this.article = article; }
public int getConsumed() { return consumed; }
public void setConsumed(int consumed) { this.consumed = consumed; }
public String getExpenseType() { return expenseType; }
public void setExpenseType(String expenseType) { this.expenseType = expenseType; }
public String getTaskNumber() { return taskNumber; }
public void setTaskNumber(String taskNumber) { this.taskNumber = taskNumber; }
public String getResponsible() { return responsible; }
public void setResponsible(String responsible) { this.responsible = responsible; }
public String getBasketNumber() { return basketNumber; }
public void setBasketNumber(String basketNumber) { this.basketNumber = basketNumber; }
}
}

View File

@@ -0,0 +1,307 @@
package org.example.dao;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
public class ComponentDAO {
public ComponentDAO(Properties config) {
// Конструктор для совместимости
}
public static class Component {
private int id;
private String name;
private String nameSupplier;
private String project;
private String category;
private String article;
private String date;
private Integer idComponents;
private Integer expend;
private String timestamp;
private String pickingCard;
private String typeExpend;
private String idTransaction;
private int stageId;
public Component() {}
public Component(int id, String name, String nameSupplier, String project, String category,
String article, String date, Integer idComponents, Integer expend,
String timestamp, String pickingCard, String typeExpend,
String idTransaction, int stageId) {
this.id = id;
this.name = name;
this.nameSupplier = nameSupplier;
this.project = project;
this.category = category;
this.article = article;
this.date = date;
this.idComponents = idComponents;
this.expend = expend;
this.timestamp = timestamp;
this.pickingCard = pickingCard;
this.typeExpend = typeExpend;
this.idTransaction = idTransaction;
this.stageId = stageId;
}
// Геттеры и сеттеры
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getNameSupplier() { return nameSupplier; }
public void setNameSupplier(String nameSupplier) { this.nameSupplier = nameSupplier; }
public String getProject() { return project; }
public void setProject(String project) { this.project = project; }
public String getCategory() { return category; }
public void setCategory(String category) { this.category = category; }
public String getArticle() { return article; }
public void setArticle(String article) { this.article = article; }
public String getDate() { return date; }
public void setDate(String date) { this.date = date; }
public Integer getIdComponents() { return idComponents; }
public void setIdComponents(Integer idComponents) { this.idComponents = idComponents; }
public Integer getExpend() { return expend; }
public void setExpend(Integer expend) { this.expend = expend; }
public String getTimestamp() { return timestamp; }
public void setTimestamp(String timestamp) { this.timestamp = timestamp; }
public String getPickingCard() { return pickingCard; }
public void setPickingCard(String pickingCard) { this.pickingCard = pickingCard; }
public String getTypeExpend() { return typeExpend; }
public void setTypeExpend(String typeExpend) { this.typeExpend = typeExpend; }
public String getIdTransaction() { return idTransaction; }
public void setIdTransaction(String idTransaction) { this.idTransaction = idTransaction; }
public int getStageId() { return stageId; }
public void setStageId(int stageId) { this.stageId = stageId; }
}
public Component create(Component component) throws SQLException {
String query = "INSERT INTO components (name, name_supplier, project, category, article, " +
"date, id_components, expend, picking_card, type_expend, id_transaction, stage_id) " +
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING id, timestamp";
try (Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement(query)) {
stmt.setString(1, component.getName());
stmt.setString(2, component.getNameSupplier());
stmt.setString(3, component.getProject());
stmt.setString(4, component.getCategory());
stmt.setString(5, component.getArticle());
// Правильно обрабатываем дату
if (component.getDate() != null && !component.getDate().trim().isEmpty()) {
try {
java.sql.Date date = java.sql.Date.valueOf(component.getDate());
stmt.setDate(6, date);
} catch (IllegalArgumentException e) {
// Если дата в неправильном формате, используем текущую дату
stmt.setDate(6, new java.sql.Date(System.currentTimeMillis()));
}
} else {
// Если дата не указана, используем текущую дату
stmt.setDate(6, new java.sql.Date(System.currentTimeMillis()));
}
stmt.setObject(7, component.getIdComponents());
stmt.setObject(8, component.getExpend());
stmt.setString(9, component.getPickingCard());
stmt.setString(10, component.getTypeExpend());
stmt.setString(11, component.getIdTransaction());
stmt.setInt(12, component.getStageId());
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
component.setId(rs.getInt("id"));
component.setTimestamp(rs.getString("timestamp"));
return component;
}
}
}
return null;
}
public List<Component> findByStageId(int stageId) throws SQLException {
String query = "SELECT id, name, name_supplier, project, category, article, date, " +
"id_components, expend, timestamp, picking_card, type_expend, id_transaction, stage_id " +
"FROM components WHERE stage_id = ? ORDER BY name";
List<Component> components = new ArrayList<>();
try (Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement(query)) {
stmt.setInt(1, stageId);
try (ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
components.add(new Component(
rs.getInt("id"),
rs.getString("name"),
rs.getString("name_supplier"),
rs.getString("project"),
rs.getString("category"),
rs.getString("article"),
rs.getString("date"),
rs.getObject("id_components", Integer.class),
rs.getObject("expend", Integer.class),
rs.getString("timestamp"),
rs.getString("picking_card"),
rs.getString("type_expend"),
rs.getString("id_transaction"),
rs.getInt("stage_id")
));
}
}
}
return components;
}
public List<Component> findAll() throws SQLException {
String query = "SELECT id, name, name_supplier, project, category, article, date, " +
"id_components, expend, timestamp, picking_card, type_expend, id_transaction, stage_id " +
"FROM components ORDER BY stage_id, name";
List<Component> components = new ArrayList<>();
try (Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement(query);
ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
components.add(new Component(
rs.getInt("id"),
rs.getString("name"),
rs.getString("name_supplier"),
rs.getString("project"),
rs.getString("category"),
rs.getString("article"),
rs.getString("date"),
rs.getObject("id_components", Integer.class),
rs.getObject("expend", Integer.class),
rs.getString("timestamp"),
rs.getString("picking_card"),
rs.getString("type_expend"),
rs.getString("id_transaction"),
rs.getInt("stage_id")
));
}
}
return components;
}
public Component findById(int componentId) throws SQLException {
String query = "SELECT id, name, name_supplier, project, category, article, date, " +
"id_components, expend, timestamp, picking_card, type_expend, id_transaction, stage_id " +
"FROM components WHERE id = ?";
try (Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement(query)) {
stmt.setInt(1, componentId);
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
return new Component(
rs.getInt("id"),
rs.getString("name"),
rs.getString("name_supplier"),
rs.getString("project"),
rs.getString("category"),
rs.getString("article"),
rs.getString("date"),
rs.getObject("id_components", Integer.class),
rs.getObject("expend", Integer.class),
rs.getString("timestamp"),
rs.getString("picking_card"),
rs.getString("type_expend"),
rs.getString("id_transaction"),
rs.getInt("stage_id")
);
}
}
}
return null;
}
private Connection getConnection() throws SQLException {
try {
// Явно загружаем PostgreSQL драйвер
Class.forName("org.postgresql.Driver");
} catch (ClassNotFoundException e) {
throw new SQLException("PostgreSQL драйвер не найден в classpath", e);
}
// Получаем параметры подключения из конфигурации
String connectionString = System.getProperty("postgresql.connection.string", "");
String username = System.getProperty("postgresql.username", "");
String password = System.getProperty("postgresql.password", "");
if (connectionString.isEmpty() || username.isEmpty()) {
throw new SQLException("Не настроено подключение к PostgreSQL");
}
// Исправляем строку подключения если нужно
String correctedConnectionString = connectionString;
if (!connectionString.startsWith("jdbc:postgresql://")) {
correctedConnectionString = "jdbc:postgresql://" + connectionString;
}
return DriverManager.getConnection(correctedConnectionString, username, password);
}
public List<Component> getByStageId(int stageId) throws SQLException {
List<Component> components = new ArrayList<>();
String query = "SELECT * FROM components WHERE stage_id = ? ORDER BY name";
try (Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement(query)) {
stmt.setInt(1, stageId);
try (ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
Component component = new Component();
component.setId(rs.getInt("id"));
component.setName(rs.getString("name"));
component.setNameSupplier(rs.getString("name_supplier"));
component.setProject(rs.getString("project"));
component.setCategory(rs.getString("category"));
component.setArticle(rs.getString("article"));
java.sql.Date date = rs.getDate("date");
component.setDate(date != null ? date.toString() : null);
component.setIdComponents(rs.getObject("id_components", Integer.class));
component.setExpend(rs.getObject("expend", Integer.class));
component.setTimestamp(rs.getString("timestamp"));
component.setPickingCard(rs.getString("picking_card"));
component.setTypeExpend(rs.getString("type_expend"));
component.setIdTransaction(rs.getString("id_transaction"));
component.setStageId(rs.getInt("stage_id"));
components.add(component);
}
}
}
return components;
}
}

View File

@@ -0,0 +1,137 @@
package org.example.dao;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
public class LimitDrawingCardDAO {
public LimitDrawingCardDAO(Properties config) {
// Конструктор для совместимости
}
public static class LimitDrawingCard {
private int id;
private int orderNumber;
private String timestamp;
private String responsible;
public LimitDrawingCard() {}
public LimitDrawingCard(int id, int orderNumber, String timestamp, String responsible) {
this.id = id;
this.orderNumber = orderNumber;
this.timestamp = timestamp;
this.responsible = responsible;
}
// Геттеры и сеттеры
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public int getOrderNumber() { return orderNumber; }
public void setOrderNumber(int orderNumber) { this.orderNumber = orderNumber; }
public String getTimestamp() { return timestamp; }
public void setTimestamp(String timestamp) { this.timestamp = timestamp; }
public String getResponsible() { return responsible; }
public void setResponsible(String responsible) { this.responsible = responsible; }
}
public LimitDrawingCard findByOrderNumber(int orderNumber) throws SQLException {
String query = "SELECT id, order_number, timestamp, responsible FROM limit_drawing_card WHERE order_number = ?";
try (Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement(query)) {
stmt.setInt(1, orderNumber);
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
return new LimitDrawingCard(
rs.getInt("id"),
rs.getInt("order_number"),
rs.getString("timestamp"),
rs.getString("responsible")
);
}
}
}
return null;
}
public LimitDrawingCard create(int orderNumber, String responsible) throws SQLException {
String query = "INSERT INTO limit_drawing_card (order_number, responsible) VALUES (?, ?) RETURNING id, timestamp";
try (Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement(query)) {
stmt.setInt(1, orderNumber);
stmt.setString(2, responsible);
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
LimitDrawingCard card = new LimitDrawingCard();
card.setId(rs.getInt("id"));
card.setOrderNumber(orderNumber);
card.setTimestamp(rs.getString("timestamp"));
card.setResponsible(responsible);
return card;
}
}
}
return null;
}
public List<LimitDrawingCard> findAll() throws SQLException {
String query = "SELECT id, order_number, timestamp, responsible FROM limit_drawing_card ORDER BY order_number";
List<LimitDrawingCard> cards = new ArrayList<>();
try (Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement(query);
ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
cards.add(new LimitDrawingCard(
rs.getInt("id"),
rs.getInt("order_number"),
rs.getString("timestamp"),
rs.getString("responsible")
));
}
}
return cards;
}
private Connection getConnection() throws SQLException {
try {
// Явно загружаем PostgreSQL драйвер
Class.forName("org.postgresql.Driver");
} catch (ClassNotFoundException e) {
throw new SQLException("PostgreSQL драйвер не найден в classpath", e);
}
// Получаем параметры подключения из конфигурации
String connectionString = System.getProperty("postgresql.connection.string", "");
String username = System.getProperty("postgresql.username", "");
String password = System.getProperty("postgresql.password", "");
if (connectionString.isEmpty() || username.isEmpty()) {
throw new SQLException("Не настроено подключение к PostgreSQL");
}
// Исправляем строку подключения если нужно
String correctedConnectionString = connectionString;
if (!connectionString.startsWith("jdbc:postgresql://")) {
correctedConnectionString = "jdbc:postgresql://" + connectionString;
}
return DriverManager.getConnection(correctedConnectionString, username, password);
}
}

View File

@@ -0,0 +1,213 @@
package org.example.dao;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
public class StageDAO {
public StageDAO(Properties config) {
// Конструктор для совместимости
}
public static class Stage {
private int id;
private int stageNumber;
private String timestamp;
private String responsible;
private String numberPickingCard;
private int limitDrawingCardId;
private java.sql.Date date;
public Stage() {}
public Stage(int id, int stageNumber, String timestamp, String responsible,
String numberPickingCard, int limitDrawingCardId) {
this.id = id;
this.stageNumber = stageNumber;
this.timestamp = timestamp;
this.responsible = responsible;
this.numberPickingCard = numberPickingCard;
this.limitDrawingCardId = limitDrawingCardId;
}
// Геттеры и сеттеры
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public int getStageNumber() { return stageNumber; }
public void setStageNumber(int stageNumber) { this.stageNumber = stageNumber; }
public String getTimestamp() { return timestamp; }
public void setTimestamp(String timestamp) { this.timestamp = timestamp; }
public String getResponsible() { return responsible; }
public void setResponsible(String responsible) { this.responsible = responsible; }
public String getNumberPickingCard() { return numberPickingCard; }
public void setNumberPickingCard(String numberPickingCard) { this.numberPickingCard = numberPickingCard; }
public int getLimitDrawingCardId() { return limitDrawingCardId; }
public void setLimitDrawingCardId(int limitDrawingCardId) { this.limitDrawingCardId = limitDrawingCardId; }
public java.sql.Date getDate() { return date; }
public void setDate(java.sql.Date date) { this.date = date; }
}
public int getNextStageNumber(int limitDrawingCardId) throws SQLException {
String query = "SELECT COALESCE(MAX(stage_number), 0) + 1 FROM stage WHERE limit_drawing_card_id = ?";
try (Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement(query)) {
stmt.setInt(1, limitDrawingCardId);
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
return rs.getInt(1);
}
}
}
return 1; // Если нет записей, начинаем с 1
}
public Stage create(int limitDrawingCardId, String responsible, String numberPickingCard) throws SQLException {
int nextStageNumber = getNextStageNumber(limitDrawingCardId);
String query = "INSERT INTO stage (stage_number, responsible, number_picking_card, limit_drawing_card_id) " +
"VALUES (?, ?, ?, ?) RETURNING id, timestamp";
try (Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement(query)) {
stmt.setInt(1, nextStageNumber);
stmt.setString(2, responsible);
stmt.setString(3, numberPickingCard);
stmt.setInt(4, limitDrawingCardId);
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
Stage stage = new Stage();
stage.setId(rs.getInt("id"));
stage.setStageNumber(nextStageNumber);
stage.setTimestamp(rs.getString("timestamp"));
stage.setResponsible(responsible);
stage.setNumberPickingCard(numberPickingCard);
stage.setLimitDrawingCardId(limitDrawingCardId);
return stage;
}
}
}
return null;
}
public List<Stage> findByLimitDrawingCardId(int limitDrawingCardId) throws SQLException {
String query = "SELECT id, stage_number, timestamp, responsible, number_picking_card, limit_drawing_card_id " +
"FROM stage WHERE limit_drawing_card_id = ? ORDER BY stage_number";
List<Stage> stages = new ArrayList<>();
try (Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement(query)) {
stmt.setInt(1, limitDrawingCardId);
try (ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
stages.add(new Stage(
rs.getInt("id"),
rs.getInt("stage_number"),
rs.getString("timestamp"),
rs.getString("responsible"),
rs.getString("number_picking_card"),
rs.getInt("limit_drawing_card_id")
));
}
}
}
return stages;
}
public Stage findById(int stageId) throws SQLException {
String query = "SELECT id, stage_number, timestamp, responsible, number_picking_card, limit_drawing_card_id " +
"FROM stage WHERE id = ?";
try (Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement(query)) {
stmt.setInt(1, stageId);
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
return new Stage(
rs.getInt("id"),
rs.getInt("stage_number"),
rs.getString("timestamp"),
rs.getString("responsible"),
rs.getString("number_picking_card"),
rs.getInt("limit_drawing_card_id")
);
}
}
}
return null;
}
private Connection getConnection() throws SQLException {
try {
// Явно загружаем PostgreSQL драйвер
Class.forName("org.postgresql.Driver");
} catch (ClassNotFoundException e) {
throw new SQLException("PostgreSQL драйвер не найден в classpath", e);
}
// Получаем параметры подключения из конфигурации
String connectionString = System.getProperty("postgresql.connection.string", "");
String username = System.getProperty("postgresql.username", "");
String password = System.getProperty("postgresql.password", "");
if (connectionString.isEmpty() || username.isEmpty()) {
throw new SQLException("Не настроено подключение к PostgreSQL");
}
// Исправляем строку подключения если нужно
String correctedConnectionString = connectionString;
if (!connectionString.startsWith("jdbc:postgresql://")) {
correctedConnectionString = "jdbc:postgresql://" + connectionString;
}
return DriverManager.getConnection(correctedConnectionString, username, password);
}
public List<Stage> getByOrderNumber(int orderNumber) throws SQLException {
List<Stage> stages = new ArrayList<>();
String query = "SELECT s.* FROM stage s " +
"INNER JOIN limit_drawing_card ldc ON s.limit_drawing_card_id = ldc.id " +
"WHERE ldc.order_number = ? " +
"ORDER BY s.stage_number";
try (Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement(query)) {
stmt.setInt(1, orderNumber);
try (ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
Stage stage = new Stage();
stage.setId(rs.getInt("id"));
stage.setStageNumber(rs.getInt("stage_number"));
stage.setDate(rs.getDate("date"));
stage.setResponsible(rs.getString("responsible"));
stage.setLimitDrawingCardId(rs.getInt("limit_drawing_card_id"));
stages.add(stage);
}
}
}
return stages;
}
}

View File

@@ -0,0 +1,4 @@
Manifest-Version: 1.0
Main-Class: org.example.Main
Application-Name: AccessLZK
Application-Version: 1.0

View File

@@ -0,0 +1,10 @@
#AccessLZK Configuration
#Thu Oct 02 23:15:55 MSK 2025
access.connection.string=jdbc\:ucanaccess\://C\:\\Users\\qsethuk\\Desktop\\test.accdb
admin.password=admin123
dsol.api.kk=/picking_card/api/components/
dsol.api.quantity=/serials/api/serials/
dsol.url=http\://192.168.0.228\:8000/
postgresql.connection.string=jdbc\:postgresql\://192.168.0.168\:5432/warehouse_db
postgresql.password=admin
postgresql.username=admin

BIN
src/main/resources/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

View File

@@ -0,0 +1,379 @@
/* Основные стили для JavaFX приложения */
.root {
-fx-background-color: #ffffff;
}
/* Стили для заголовков в настройках */
.settings-title {
-fx-font-family: 'Segoe UI';
-fx-font-size: 18px;
-fx-font-weight: bold;
-fx-text-fill: #212529;
}
/* Стили для title bar */
.title-bar {
-fx-background-color: #2c3e50;
-fx-padding: 8 16;
-fx-pref-height: 40;
}
.title-bar .button {
-fx-background-color: transparent;
-fx-text-fill: white;
-fx-cursor: hand;
-fx-border-width: 0;
}
.title-bar .button:hover {
-fx-background-color: rgba(255, 255, 255, 0.1);
}
.title-bar .button:pressed {
-fx-background-color: rgba(255, 255, 255, 0.2);
}
/* Стили для sidebar */
.sidebar {
-fx-background-color: #34495e;
-fx-padding: 20;
}
.sidebar .button {
-fx-background-color: transparent;
-fx-text-fill: white;
-fx-font-size: 14px;
-fx-cursor: hand;
-fx-alignment: center-left;
-fx-pref-width: infinity;
-fx-padding: 10 15;
-fx-border-width: 0;
}
.sidebar .button:hover {
-fx-background-color: rgba(255, 255, 255, 0.1);
}
.sidebar .button:pressed {
-fx-background-color: rgba(255, 255, 255, 0.2);
}
.tab-pane {
-fx-background-color: #f8f9fa;
-fx-tab-min-width: 0px;
-fx-tab-min-height: 30px;
-fx-tab-max-height: 30px;
}
.tab {
-fx-background-color: #ffffff;
-fx-border-color: #dee2e6;
-fx-border-width: 1 1 0 1;
-fx-padding: 5 10;
-fx-font-family: 'Segoe UI';
-fx-font-size: 13px;
-fx-font-weight: normal;
}
.tab:hover {
-fx-background-color: #e9ecef;
}
.tab:selected {
-fx-background-color: #007bff;
-fx-text-fill: white;
-fx-border-color: transparent;
-fx-background-radius: 5 5 0 0;
-fx-border-radius: 5 5 0 0;
}
.tab:selected .tab-label {
-fx-text-fill: white;
-fx-font-weight: bold;
-fx-background-color: transparent;
-fx-background-radius: 0;
-fx-border-color: transparent;
-fx-padding: 0;
-fx-background-insets: 0;
}
.tab:selected .tab-container {
-fx-background-color: transparent;
-fx-background-insets: 0;
}
.tab:selected .tab-header {
-fx-background-color: transparent;
-fx-background-insets: 0;
}
.tab:selected .tab-content {
-fx-background-color: transparent;
-fx-background-insets: 0;
}
.tab:selected .tab-close-button {
-fx-background-color: transparent;
}
.tab:selected .tab-close-button .graphic {
-fx-background-color: transparent;
}
.tab-header-area {
-fx-background-color: #ffffff;
-fx-border-color: #dee2e6;
-fx-border-width: 0 0 1 0;
-fx-padding: 0;
}
.tab-header-background {
-fx-background-color: #ffffff;
}
.tab-content-area {
-fx-background-color: #f8f9fa;
-fx-border-color: #dee2e6;
-fx-border-width: 0 1 1 1;
}
.tab-pane .tab-header-area .tab {
-fx-background-radius: 5 5 0 0;
-fx-border-radius: 5 5 0 0;
}
.tab-pane .tab-header-area .tab:selected {
-fx-background-radius: 5 5 0 0;
-fx-border-radius: 5 5 0 0;
-fx-effect: dropshadow(gaussian, rgba(0,123,255,0.2), 2, 0, 0, 1);
-fx-background-color: #007bff;
-fx-border-color: transparent;
}
.tab-pane .tab-header-area .tab:selected * {
-fx-background-color: transparent;
-fx-background-insets: 0;
-fx-border-color: transparent;
}
.tab-pane .tab-header-area .tab:selected .tab-label {
-fx-background-color: transparent;
-fx-background-insets: 0;
-fx-border-color: transparent;
-fx-padding: 0;
}
.text-field {
-fx-background-color: white;
-fx-border-color: #ced4da;
-fx-border-width: 1;
-fx-border-radius: 3;
-fx-padding: 8 12;
-fx-font-family: 'Segoe UI';
-fx-font-size: 13px;
-fx-font-weight: normal;
}
.text-field:focused {
-fx-background-color: white;
-fx-border-color: #007bff;
-fx-border-width: 2;
-fx-font-size: 13px;
-fx-font-weight: normal;
}
.text-field:disabled {
-fx-background-color: #f8f9fa;
-fx-border-color: #e9ecef;
-fx-text-fill: #6c757d;
-fx-font-size: 13px;
-fx-font-weight: normal;
}
.password-field {
-fx-background-color: white;
-fx-border-color: #ced4da;
-fx-border-width: 1;
-fx-border-radius: 3;
-fx-padding: 8 12;
-fx-font-family: 'Segoe UI';
-fx-font-size: 13px;
-fx-font-weight: normal;
}
.password-field:focused {
-fx-background-color: white;
-fx-border-color: #007bff;
-fx-border-width: 2;
-fx-font-size: 13px;
-fx-font-weight: normal;
}
.password-field:disabled {
-fx-background-color: #f8f9fa;
-fx-border-color: #e9ecef;
-fx-text-fill: #6c757d;
-fx-font-size: 13px;
-fx-font-weight: normal;
}
.button {
-fx-background-color: #007bff;
-fx-text-fill: white;
-fx-font-family: 'Segoe UI';
-fx-font-weight: bold;
-fx-font-size: 12px;
-fx-padding: 10 20;
-fx-background-radius: 5;
-fx-cursor: hand;
}
.button:hover {
-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.3), 5, 0, 0, 2);
}
.button:disabled {
-fx-background-color: #6c757d;
-fx-text-fill: #ffffff;
-fx-opacity: 0.6;
}
.menu-bar {
-fx-background-color: #ffffff;
-fx-border-color: #dee2e6;
-fx-border-width: 0 0 1 0;
}
.menu {
-fx-font-family: 'Segoe UI';
-fx-font-size: 13px;
}
.menu-item {
-fx-font-family: 'Segoe UI';
-fx-font-size: 13px;
}
.label {
-fx-font-family: 'Segoe UI';
-fx-text-fill: #495057;
}
.alert {
-fx-font-family: 'Segoe UI';
}
.dialog-pane {
-fx-background-color: #ffffff;
-fx-border-color: #dee2e6;
-fx-border-width: 1;
-fx-border-radius: 5;
}
.dialog-pane .button-bar .button {
-fx-background-color: #007bff;
-fx-text-fill: white;
-fx-font-family: 'Segoe UI';
-fx-font-weight: bold;
-fx-padding: 8 16;
-fx-background-radius: 3;
}
.dialog-pane .button-bar .button:hover {
-fx-background-color: #0056b3;
}
.scroll-pane {
-fx-background-color: #f8f9fa;
-fx-border-color: transparent;
}
.scroll-pane .viewport {
-fx-background-color: #f8f9fa;
}
.scroll-pane .scroll-bar:vertical {
-fx-background-color: #ffffff;
-fx-border-color: #dee2e6;
-fx-border-width: 1 0 0 0;
}
.scroll-pane .scroll-bar:vertical .track {
-fx-background-color: #ffffff;
}
.scroll-pane .scroll-bar:vertical .thumb {
-fx-background-color: #ced4da;
-fx-background-radius: 5;
}
.scroll-pane .scroll-bar:vertical .thumb:hover {
-fx-background-color: #adb5bd;
}
.scroll-pane .scroll-bar:vertical .increment-button,
.scroll-pane .scroll-bar:vertical .decrement-button {
-fx-background-color: #ffffff;
-fx-border-color: #dee2e6;
-fx-border-width: 1 0 0 0;
}
.scroll-pane .scroll-bar:vertical .increment-arrow,
.scroll-pane .scroll-bar:vertical .decrement-arrow {
-fx-background-color: #6c757d;
}
/* Стили для таблиц */
.table-view {
-fx-background-color: #f9f9fb;
-fx-border-color: #dcdfe6;
-fx-border-radius: 8;
-fx-background-radius: 8;
-fx-padding: 5;
-fx-table-cell-border-color: transparent; /* убираем сетку */
-fx-selection-bar: #4e73df; /* цвет выделения */
-fx-selection-bar-non-focused: #a8c0ff;
}
.table-view .column-header-background {
-fx-background-color: linear-gradient(to bottom, #f0f2f5, #e5e8ec);
-fx-background-radius: 8 8 0 0;
-fx-padding: 8;
-fx-border-color: #dcdfe6;
-fx-border-width: 0 0 1 0;
}
.table-view .column-header,
.table-view .filler {
-fx-size: 40;
-fx-border-width: 0;
-fx-background-color: transparent;
-fx-font-family: "Segoe UI";
-fx-font-size: 13px;
-fx-font-weight: bold;
-fx-text-fill: #2c3e50;
}
.table-cell {
-fx-padding: 8 12;
-fx-font-family: "Segoe UI";
-fx-font-size: 12px;
-fx-text-fill: #333;
-fx-alignment: CENTER_LEFT;
}
.table-row-cell:filled:selected {
-fx-background-color: #4e73df;
-fx-text-fill: white;
}
.table-row-cell:filled:selected,
.table-cell:filled:selected {
-fx-background-color: #4e73df;
-fx-text-fill: white;
}
.table-row-cell:hover {
-fx-background-color: #eef3ff;
}