Add source
This commit is contained in:
128
pom.xml
Normal file
128
pom.xml
Normal 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>
|
||||
27
src/main/java/org/example/Component.java
Normal file
27
src/main/java/org/example/Component.java
Normal 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;
|
||||
}
|
||||
}
|
||||
180
src/main/java/org/example/ConnectionManager.java
Normal file
180
src/main/java/org/example/ConnectionManager.java
Normal 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);
|
||||
}
|
||||
}
|
||||
154
src/main/java/org/example/DatabaseInitializer.java
Normal file
154
src/main/java/org/example/DatabaseInitializer.java
Normal 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);
|
||||
}
|
||||
}
|
||||
263
src/main/java/org/example/DsolApiService.java
Normal file
263
src/main/java/org/example/DsolApiService.java
Normal 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;
|
||||
}
|
||||
}
|
||||
162
src/main/java/org/example/DsolFactoryAPI.java
Normal file
162
src/main/java/org/example/DsolFactoryAPI.java
Normal 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;
|
||||
}
|
||||
}
|
||||
335
src/main/java/org/example/EnhancedTableView.java
Normal file
335
src/main/java/org/example/EnhancedTableView.java
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
189
src/main/java/org/example/ExcelExporter.java
Normal file
189
src/main/java/org/example/ExcelExporter.java
Normal 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()));
|
||||
}
|
||||
}
|
||||
191
src/main/java/org/example/ExcelKKReader.java
Normal file
191
src/main/java/org/example/ExcelKKReader.java
Normal 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;
|
||||
}
|
||||
}
|
||||
118
src/main/java/org/example/HomeController.java
Normal file
118
src/main/java/org/example/HomeController.java
Normal 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;
|
||||
}
|
||||
}
|
||||
918
src/main/java/org/example/LZKExcelGenerator.java
Normal file
918
src/main/java/org/example/LZKExcelGenerator.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
541
src/main/java/org/example/LZKGeneratorController.java
Normal file
541
src/main/java/org/example/LZKGeneratorController.java
Normal 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; }
|
||||
}
|
||||
}
|
||||
421
src/main/java/org/example/Main.java
Normal file
421
src/main/java/org/example/Main.java
Normal 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("Система управления базами данных\nПоддержка 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;
|
||||
}
|
||||
}
|
||||
27
src/main/java/org/example/OrderQuantity.java
Normal file
27
src/main/java/org/example/OrderQuantity.java
Normal 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;
|
||||
}
|
||||
}
|
||||
1665
src/main/java/org/example/ReportsController.java
Normal file
1665
src/main/java/org/example/ReportsController.java
Normal file
File diff suppressed because it is too large
Load Diff
505
src/main/java/org/example/SettingsController.java
Normal file
505
src/main/java/org/example/SettingsController.java
Normal 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;
|
||||
}
|
||||
}
|
||||
364
src/main/java/org/example/SettingsPanel.java
Normal file
364
src/main/java/org/example/SettingsPanel.java
Normal 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("Неверный пароль!\nПопробуйте еще раз.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
389
src/main/java/org/example/StageController.java
Normal file
389
src/main/java/org/example/StageController.java
Normal 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;
|
||||
}
|
||||
}
|
||||
189
src/main/java/org/example/StageManagementService.java
Normal file
189
src/main/java/org/example/StageManagementService.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
642
src/main/java/org/example/WeeklyReportController.java
Normal file
642
src/main/java/org/example/WeeklyReportController.java
Normal 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; }
|
||||
}
|
||||
}
|
||||
307
src/main/java/org/example/dao/ComponentDAO.java
Normal file
307
src/main/java/org/example/dao/ComponentDAO.java
Normal 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;
|
||||
}
|
||||
}
|
||||
137
src/main/java/org/example/dao/LimitDrawingCardDAO.java
Normal file
137
src/main/java/org/example/dao/LimitDrawingCardDAO.java
Normal 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);
|
||||
}
|
||||
}
|
||||
213
src/main/java/org/example/dao/StageDAO.java
Normal file
213
src/main/java/org/example/dao/StageDAO.java
Normal 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;
|
||||
}
|
||||
}
|
||||
4
src/main/resources/META-INF/MANIFEST.MF
Normal file
4
src/main/resources/META-INF/MANIFEST.MF
Normal file
@@ -0,0 +1,4 @@
|
||||
Manifest-Version: 1.0
|
||||
Main-Class: org.example.Main
|
||||
Application-Name: AccessLZK
|
||||
Application-Version: 1.0
|
||||
10
src/main/resources/config.properties
Normal file
10
src/main/resources/config.properties
Normal 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
BIN
src/main/resources/icon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 179 KiB |
379
src/main/resources/styles.css
Normal file
379
src/main/resources/styles.css
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user