1302 lines
55 KiB
Python
1302 lines
55 KiB
Python
import sys
|
||
import sqlite3
|
||
from datetime import datetime
|
||
from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
|
||
QLabel, QLineEdit, QTextEdit, QComboBox, QPushButton, QTableWidget,
|
||
QTableWidgetItem, QTabWidget, QGroupBox, QMessageBox, QFileDialog,
|
||
QSplitter, QHeaderView, QFormLayout, QCheckBox, QDialog, QDateEdit)
|
||
from PyQt6.QtCore import Qt, pyqtSignal, QDate
|
||
from PyQt6.QtGui import QIcon, QAction
|
||
import os
|
||
|
||
class DatabaseManager:
|
||
def __init__(self):
|
||
self.conn = sqlite3.connect('service_requests_v2.db')
|
||
self.create_tables()
|
||
|
||
def create_tables(self):
|
||
cursor = self.conn.cursor()
|
||
|
||
# Таблица пользователей
|
||
cursor.execute('''
|
||
CREATE TABLE IF NOT EXISTS users (
|
||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
username TEXT UNIQUE NOT NULL,
|
||
password TEXT NOT NULL,
|
||
role TEXT NOT NULL,
|
||
full_name TEXT NOT NULL,
|
||
phone TEXT,
|
||
email TEXT
|
||
)
|
||
''')
|
||
|
||
# Таблица заявок с расширенными полями
|
||
cursor.execute('''
|
||
CREATE TABLE IF NOT EXISTS service_requests (
|
||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
client_name TEXT NOT NULL,
|
||
client_phone TEXT NOT NULL,
|
||
client_email TEXT,
|
||
equipment_type TEXT NOT NULL,
|
||
equipment_model TEXT NOT NULL,
|
||
serial_number TEXT,
|
||
problem_description TEXT NOT NULL,
|
||
status TEXT DEFAULT 'Новая',
|
||
priority TEXT DEFAULT 'Средний',
|
||
request_type TEXT DEFAULT 'Ремонт',
|
||
assigned_operator TEXT,
|
||
assigned_master TEXT,
|
||
observer_group TEXT,
|
||
created_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
completed_date TIMESTAMP,
|
||
deadline DATE,
|
||
marked_for_deletion BOOLEAN DEFAULT 0,
|
||
duplicate_of INTEGER
|
||
)
|
||
''')
|
||
|
||
# Таблица истории заявок
|
||
cursor.execute('''
|
||
CREATE TABLE IF NOT EXISTS request_history (
|
||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
request_id INTEGER,
|
||
user_id INTEGER,
|
||
action TEXT,
|
||
details TEXT,
|
||
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
FOREIGN KEY (request_id) REFERENCES service_requests (id)
|
||
)
|
||
''')
|
||
|
||
# Таблица вложений
|
||
cursor.execute('''
|
||
CREATE TABLE IF NOT EXISTS attachments (
|
||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
request_id INTEGER,
|
||
filename TEXT,
|
||
filepath TEXT,
|
||
file_type TEXT,
|
||
uploaded_by INTEGER,
|
||
upload_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
FOREIGN KEY (request_id) REFERENCES service_requests (id)
|
||
)
|
||
''')
|
||
|
||
# Таблица отчетов
|
||
cursor.execute('''
|
||
CREATE TABLE IF NOT EXISTS reports (
|
||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
request_id INTEGER,
|
||
master_id INTEGER,
|
||
work_description TEXT,
|
||
parts_used TEXT,
|
||
time_spent REAL,
|
||
labor_cost REAL,
|
||
parts_cost REAL,
|
||
total_cost REAL,
|
||
created_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
FOREIGN KEY (request_id) REFERENCES service_requests (id)
|
||
)
|
||
''')
|
||
|
||
# Таблица заказов запчастей
|
||
cursor.execute('''
|
||
CREATE TABLE IF NOT EXISTS part_orders (
|
||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
request_id INTEGER,
|
||
part_name TEXT,
|
||
part_number TEXT,
|
||
quantity INTEGER,
|
||
supplier TEXT,
|
||
estimated_cost REAL,
|
||
status TEXT DEFAULT 'Заказан',
|
||
ordered_by INTEGER,
|
||
order_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
expected_date DATE,
|
||
FOREIGN KEY (request_id) REFERENCES service_requests (id)
|
||
)
|
||
''')
|
||
|
||
# Таблица МТР (материально-технических ресурсов)
|
||
cursor.execute('''
|
||
CREATE TABLE IF NOT EXISTS material_needs (
|
||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
request_id INTEGER,
|
||
material_name TEXT,
|
||
material_type TEXT,
|
||
quantity INTEGER,
|
||
unit TEXT,
|
||
urgency TEXT DEFAULT 'Обычная',
|
||
status TEXT DEFAULT 'Требуется',
|
||
estimated_cost REAL,
|
||
notes TEXT,
|
||
created_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
FOREIGN KEY (request_id) REFERENCES service_requests (id)
|
||
)
|
||
''')
|
||
|
||
# Создаем тестовых пользователей для варианта 2
|
||
self.create_test_users()
|
||
|
||
self.conn.commit()
|
||
|
||
def create_test_users(self):
|
||
cursor = self.conn.cursor()
|
||
|
||
test_users = [
|
||
('operator1', '123', 'operator', 'Петр Петров', '+79167654321', 'operator@company.ru'),
|
||
('master1', '123', 'master', 'Сергей Сергеев', '+79169998877', 'master@company.ru'),
|
||
('admin1', '123', 'admin', 'Администратор Системы', '+79160001122', 'admin@company.ru')
|
||
]
|
||
|
||
for user in test_users:
|
||
try:
|
||
cursor.execute(
|
||
'INSERT INTO users (username, password, role, full_name, phone, email) VALUES (?, ?, ?, ?, ?, ?)',
|
||
user
|
||
)
|
||
except sqlite3.IntegrityError:
|
||
pass # Пользователь уже существует
|
||
|
||
self.conn.commit()
|
||
|
||
class ServiceRequestAppV2(QMainWindow):
|
||
def __init__(self):
|
||
super().__init__()
|
||
self.db = DatabaseManager()
|
||
self.current_user = None
|
||
self.init_ui()
|
||
|
||
def init_ui(self):
|
||
self.setWindowTitle('Система учета заявок на ремонт оргтехники - Вариант 2')
|
||
self.setGeometry(100, 100, 1400, 800)
|
||
|
||
# Центральный виджет
|
||
central_widget = QWidget()
|
||
self.setCentralWidget(central_widget)
|
||
|
||
# Основной layout
|
||
layout = QVBoxLayout(central_widget)
|
||
|
||
# Панель входа
|
||
self.login_widget = self.create_login_widget()
|
||
layout.addWidget(self.login_widget)
|
||
|
||
# Основной интерфейс (скрыт до входа)
|
||
self.main_tabs = QTabWidget()
|
||
self.main_tabs.setVisible(False)
|
||
layout.addWidget(self.main_tabs)
|
||
|
||
def create_login_widget(self):
|
||
widget = QWidget()
|
||
layout = QVBoxLayout(widget)
|
||
|
||
layout.addWidget(QLabel('Вход в систему - Вариант 2'))
|
||
|
||
form_layout = QFormLayout()
|
||
|
||
self.username_input = QLineEdit()
|
||
self.password_input = QLineEdit()
|
||
self.password_input.setEchoMode(QLineEdit.EchoMode.Password)
|
||
|
||
form_layout.addRow('Логин:', self.username_input)
|
||
form_layout.addRow('Пароль:', self.password_input)
|
||
|
||
layout.addLayout(form_layout)
|
||
|
||
login_btn = QPushButton('Войти')
|
||
login_btn.clicked.connect(self.login)
|
||
layout.addWidget(login_btn)
|
||
|
||
# Подсказка с тестовыми пользователями
|
||
hint = QLabel('Тестовые пользователи: operator1/123, master1/123, admin1/123')
|
||
hint.setStyleSheet('color: gray; font-size: 10px;')
|
||
layout.addWidget(hint)
|
||
|
||
return widget
|
||
|
||
def login(self):
|
||
username = self.username_input.text()
|
||
password = self.password_input.text()
|
||
|
||
cursor = self.db.conn.cursor()
|
||
cursor.execute(
|
||
'SELECT * FROM users WHERE username = ? AND password = ?',
|
||
(username, password)
|
||
)
|
||
|
||
user = cursor.fetchone()
|
||
|
||
if user:
|
||
self.current_user = {
|
||
'id': user[0],
|
||
'username': user[1],
|
||
'role': user[3],
|
||
'full_name': user[4]
|
||
}
|
||
self.show_main_interface()
|
||
else:
|
||
QMessageBox.warning(self, 'Ошибка', 'Неверный логин или пароль')
|
||
|
||
def show_main_interface(self):
|
||
self.login_widget.setVisible(False)
|
||
self.main_tabs.setVisible(True)
|
||
|
||
# Очищаем предыдущие вкладки
|
||
self.main_tabs.clear()
|
||
|
||
role = self.current_user['role']
|
||
|
||
if role == 'operator':
|
||
self.setup_operator_interface()
|
||
elif role == 'master':
|
||
self.setup_master_interface()
|
||
elif role == 'admin':
|
||
self.setup_admin_interface()
|
||
|
||
def setup_operator_interface(self):
|
||
# Вкладка регистрации заявок
|
||
register_tab = QWidget()
|
||
layout = QVBoxLayout(register_tab)
|
||
|
||
layout.addWidget(QLabel('Регистрация новой заявки'))
|
||
|
||
form_layout = QFormLayout()
|
||
|
||
self.op_client_name = QLineEdit()
|
||
self.op_client_phone = QLineEdit()
|
||
self.op_client_email = QLineEdit()
|
||
self.op_equipment_type = QComboBox()
|
||
self.op_equipment_type.addItems(['Принтер', 'Копир', 'Сканер', 'МФУ', 'Компьютер', 'Монитор', 'Телефон', 'Другое'])
|
||
self.op_equipment_model = QLineEdit()
|
||
self.op_serial_number = QLineEdit()
|
||
self.op_problem_description = QTextEdit()
|
||
|
||
form_layout.addRow('ФИО клиента:', self.op_client_name)
|
||
form_layout.addRow('Телефон:', self.op_client_phone)
|
||
form_layout.addRow('Email:', self.op_client_email)
|
||
form_layout.addRow('Тип оборудования:', self.op_equipment_type)
|
||
form_layout.addRow('Модель:', self.op_equipment_model)
|
||
form_layout.addRow('Серийный номер:', self.op_serial_number)
|
||
form_layout.addRow('Описание проблемы:', self.op_problem_description)
|
||
|
||
layout.addLayout(form_layout)
|
||
|
||
submit_btn = QPushButton('Зарегистрировать заявку')
|
||
submit_btn.clicked.connect(self.register_service_request)
|
||
layout.addWidget(submit_btn)
|
||
|
||
self.main_tabs.addTab(register_tab, 'Регистрация заявки')
|
||
|
||
# Вкладка управления заявками
|
||
management_tab = QWidget()
|
||
layout = QVBoxLayout(management_tab)
|
||
|
||
# Панель фильтров
|
||
filter_layout = QHBoxLayout()
|
||
filter_layout.addWidget(QLabel('Статус:'))
|
||
self.status_filter = QComboBox()
|
||
self.status_filter.addItems(['Все', 'Новая', 'В работе', 'Ожидает запчасти', 'Выполнена', 'Отменена'])
|
||
filter_layout.addWidget(self.status_filter)
|
||
|
||
filter_layout.addWidget(QLabel('Приоритет:'))
|
||
self.priority_filter = QComboBox()
|
||
self.priority_filter.addItems(['Все', 'Низкий', 'Средний', 'Высокий', 'Критичный'])
|
||
filter_layout.addWidget(self.priority_filter)
|
||
|
||
filter_btn = QPushButton('Применить фильтры')
|
||
filter_btn.clicked.connect(self.load_operator_requests)
|
||
filter_layout.addWidget(filter_btn)
|
||
|
||
layout.addLayout(filter_layout)
|
||
|
||
self.operator_requests_table = QTableWidget()
|
||
self.operator_requests_table.setColumnCount(10)
|
||
self.operator_requests_table.setHorizontalHeaderLabels([
|
||
'ID', 'Клиент', 'Оборудование', 'Модель', 'Статус', 'Приоритет', 'Тип', 'Дата', 'Оператор', 'Помечена на удаление'
|
||
])
|
||
layout.addWidget(self.operator_requests_table)
|
||
|
||
# Панель управления заявкой
|
||
control_group = QGroupBox('Управление заявкой')
|
||
control_layout = QHBoxLayout(control_group)
|
||
|
||
self.status_combo = QComboBox()
|
||
self.status_combo.addItems(['Новая', 'В работе', 'Ожидает запчасти', 'Выполнена', 'Отменена'])
|
||
|
||
self.priority_combo = QComboBox()
|
||
self.priority_combo.addItems(['Низкий', 'Средний', 'Высокий', 'Критичный'])
|
||
|
||
self.type_combo = QComboBox()
|
||
self.type_combo.addItems(['Ремонт', 'Обслуживание', 'Консультация', 'Диагностика'])
|
||
|
||
self.observer_group = QLineEdit()
|
||
self.observer_group.setPlaceholderText('Группа наблюдателей')
|
||
|
||
mark_delete_btn = QPushButton('Пометить на удаление')
|
||
mark_delete_btn.clicked.connect(self.mark_request_for_deletion)
|
||
|
||
update_btn = QPushButton('Обновить заявку')
|
||
update_btn.clicked.connect(self.update_request_operator)
|
||
|
||
control_layout.addWidget(QLabel('Статус:'))
|
||
control_layout.addWidget(self.status_combo)
|
||
control_layout.addWidget(QLabel('Приоритет:'))
|
||
control_layout.addWidget(self.priority_combo)
|
||
control_layout.addWidget(QLabel('Тип:'))
|
||
control_layout.addWidget(self.type_combo)
|
||
control_layout.addWidget(self.observer_group)
|
||
control_layout.addWidget(update_btn)
|
||
control_layout.addWidget(mark_delete_btn)
|
||
|
||
layout.addWidget(control_group)
|
||
|
||
self.main_tabs.addTab(management_tab, 'Управление заявками')
|
||
|
||
# Вкладка архива
|
||
archive_tab = QWidget()
|
||
layout = QVBoxLayout(archive_tab)
|
||
|
||
search_layout = QHBoxLayout()
|
||
self.archive_search = QLineEdit()
|
||
self.archive_search.setPlaceholderText('Поиск в архиве...')
|
||
search_btn = QPushButton('Найти')
|
||
search_btn.clicked.connect(self.search_archive)
|
||
|
||
search_layout.addWidget(self.archive_search)
|
||
search_layout.addWidget(search_btn)
|
||
layout.addLayout(search_layout)
|
||
|
||
self.archive_table = QTableWidget()
|
||
self.archive_table.setColumnCount(9)
|
||
self.archive_table.setHorizontalHeaderLabels([
|
||
'ID', 'Клиент', 'Оборудование', 'Модель', 'Статус', 'Дата создания', 'Дата завершения', 'Мастер', 'Тип'
|
||
])
|
||
layout.addWidget(self.archive_table)
|
||
|
||
self.main_tabs.addTab(archive_tab, 'Архив')
|
||
|
||
self.load_operator_requests()
|
||
self.load_archive()
|
||
|
||
def setup_master_interface(self):
|
||
# Вкладка назначенных заявок
|
||
requests_tab = QWidget()
|
||
layout = QVBoxLayout(requests_tab)
|
||
|
||
self.master_requests_table = QTableWidget()
|
||
self.master_requests_table.setColumnCount(8)
|
||
self.master_requests_table.setHorizontalHeaderLabels([
|
||
'ID', 'Клиент', 'Оборудование', 'Модель', 'Статус', 'Приоритет', 'Дата', 'Срок'
|
||
])
|
||
layout.addWidget(self.master_requests_table)
|
||
|
||
# Панель управления для мастера
|
||
control_group = QGroupBox('Управление ремонтом')
|
||
control_layout = QVBoxLayout(control_group)
|
||
|
||
# Смена статуса
|
||
status_layout = QHBoxLayout()
|
||
status_layout.addWidget(QLabel('Статус:'))
|
||
self.master_status_combo = QComboBox()
|
||
self.master_status_combo.addItems(['В работе', 'Ожидает запчасти', 'Выполнена', 'Отменена'])
|
||
status_layout.addWidget(self.master_status_combo)
|
||
|
||
update_status_btn = QPushButton('Обновить статус')
|
||
update_status_btn.clicked.connect(self.update_request_master)
|
||
status_layout.addWidget(update_status_btn)
|
||
|
||
control_layout.addLayout(status_layout)
|
||
|
||
# Заказ запчастей
|
||
parts_group = QGroupBox('Заказ запчастей')
|
||
parts_layout = QFormLayout(parts_group)
|
||
|
||
self.part_name = QLineEdit()
|
||
self.part_number = QLineEdit()
|
||
self.part_quantity = QLineEdit()
|
||
self.part_quantity.setText('1')
|
||
self.part_supplier = QLineEdit()
|
||
self.part_cost = QLineEdit()
|
||
|
||
parts_layout.addRow('Название запчасти:', self.part_name)
|
||
parts_layout.addRow('Номер запчасти:', self.part_number)
|
||
parts_layout.addRow('Количество:', self.part_quantity)
|
||
parts_layout.addRow('Поставщик:', self.part_supplier)
|
||
parts_layout.addRow('Примерная стоимость:', self.part_cost)
|
||
|
||
order_parts_btn = QPushButton('Заказать запчасти')
|
||
order_parts_btn.clicked.connect(self.order_parts)
|
||
parts_layout.addRow(order_parts_btn)
|
||
|
||
control_layout.addWidget(parts_group)
|
||
|
||
# Прикрепление файлов
|
||
file_layout = QHBoxLayout()
|
||
attach_photo_btn = QPushButton('Прикрепить фото')
|
||
attach_photo_btn.clicked.connect(self.attach_photo)
|
||
attach_file_btn = QPushButton('Прикрепить файл')
|
||
attach_file_btn.clicked.connect(self.attach_file)
|
||
|
||
file_layout.addWidget(attach_photo_btn)
|
||
file_layout.addWidget(attach_file_btn)
|
||
control_layout.addLayout(file_layout)
|
||
|
||
# Создание отчета
|
||
report_btn = QPushButton('Создать отчет о выполненной работе')
|
||
report_btn.clicked.connect(self.create_report)
|
||
control_layout.addWidget(report_btn)
|
||
|
||
# История заявки
|
||
history_btn = QPushButton('Просмотреть историю заявки')
|
||
history_btn.clicked.connect(self.show_request_history)
|
||
control_layout.addWidget(history_btn)
|
||
|
||
layout.addWidget(control_group)
|
||
|
||
self.main_tabs.addTab(requests_tab, 'Мои заявки')
|
||
|
||
# Вкладка заказанных запчастей
|
||
parts_tab = QWidget()
|
||
layout = QVBoxLayout(parts_tab)
|
||
|
||
self.parts_table = QTableWidget()
|
||
self.parts_table.setColumnCount(8)
|
||
self.parts_table.setHorizontalHeaderLabels([
|
||
'ID', 'Заявка', 'Запчасть', 'Номер', 'Кол-во', 'Статус', 'Дата заказа', 'Поставщик'
|
||
])
|
||
layout.addWidget(self.parts_table)
|
||
|
||
self.main_tabs.addTab(parts_tab, 'Заказанные запчасти')
|
||
|
||
self.load_master_requests()
|
||
self.load_master_parts()
|
||
|
||
def setup_admin_interface(self):
|
||
# Вкладка управления пользователями
|
||
users_tab = QWidget()
|
||
layout = QVBoxLayout(users_tab)
|
||
|
||
self.users_table = QTableWidget()
|
||
self.users_table.setColumnCount(6)
|
||
self.users_table.setHorizontalHeaderLabels(['ID', 'Логин', 'ФИО', 'Роль', 'Телефон', 'Email'])
|
||
layout.addWidget(self.users_table)
|
||
|
||
add_user_btn = QPushButton('Добавить пользователя')
|
||
add_user_btn.clicked.connect(self.show_add_user_dialog)
|
||
layout.addWidget(add_user_btn)
|
||
|
||
self.main_tabs.addTab(users_tab, 'Пользователи')
|
||
|
||
# Вкладка всех заявок
|
||
requests_tab = QWidget()
|
||
layout = QVBoxLayout(requests_tab)
|
||
|
||
self.admin_requests_table = QTableWidget()
|
||
self.admin_requests_table.setColumnCount(11)
|
||
self.admin_requests_table.setHorizontalHeaderLabels([
|
||
'ID', 'Клиент', 'Оборудование', 'Модель', 'Статус', 'Приоритет', 'Тип', 'Дата', 'Оператор', 'Мастер', 'Помечена на удаление'
|
||
])
|
||
layout.addWidget(self.admin_requests_table)
|
||
|
||
delete_btn = QPushButton('Удалить выбранные заявки')
|
||
delete_btn.clicked.connect(self.delete_marked_requests)
|
||
layout.addWidget(delete_btn)
|
||
|
||
self.main_tabs.addTab(requests_tab, 'Все заявки')
|
||
|
||
# Вкладка распределения заявок
|
||
distribution_tab = QWidget()
|
||
layout = QVBoxLayout(distribution_tab)
|
||
|
||
self.distribution_table = QTableWidget()
|
||
self.distribution_table.setColumnCount(9)
|
||
self.distribution_table.setHorizontalHeaderLabels([
|
||
'ID', 'Клиент', 'Оборудование', 'Статус', 'Оператор', 'Мастер', 'Группа наблюдателей', 'Срок исполнения', 'Приоритет'
|
||
])
|
||
layout.addWidget(self.distribution_table)
|
||
|
||
self.main_tabs.addTab(distribution_tab, 'Распределение заявок')
|
||
|
||
# Вкладка МТР (материально-технических ресурсов)
|
||
materials_tab = QWidget()
|
||
layout = QVBoxLayout(materials_tab)
|
||
|
||
# Панель управления МТР
|
||
mtr_control_layout = QHBoxLayout()
|
||
consolidate_btn = QPushButton('Консолидировать потребности в МТР')
|
||
consolidate_btn.clicked.connect(self.consolidate_material_needs)
|
||
generate_report_btn = QPushButton('Сформировать отчет по МТР')
|
||
generate_report_btn.clicked.connect(self.generate_mtr_report)
|
||
|
||
mtr_control_layout.addWidget(consolidate_btn)
|
||
mtr_control_layout.addWidget(generate_report_btn)
|
||
layout.addLayout(mtr_control_layout)
|
||
|
||
self.materials_table = QTableWidget()
|
||
self.materials_table.setColumnCount(10)
|
||
self.materials_table.setHorizontalHeaderLabels([
|
||
'ID', 'Заявка', 'Материал', 'Тип', 'Кол-во', 'Ед.изм', 'Срочность', 'Статус', 'Примерная стоимость', 'Примечания'
|
||
])
|
||
layout.addWidget(self.materials_table)
|
||
|
||
self.main_tabs.addTab(materials_tab, 'МТР')
|
||
|
||
# Вкладка архива
|
||
archive_tab = QWidget()
|
||
layout = QVBoxLayout(archive_tab)
|
||
|
||
search_layout = QHBoxLayout()
|
||
self.admin_archive_search = QLineEdit()
|
||
self.admin_archive_search.setPlaceholderText('Поиск в архиве...')
|
||
admin_search_btn = QPushButton('Найти')
|
||
admin_search_btn.clicked.connect(self.search_admin_archive)
|
||
|
||
search_layout.addWidget(self.admin_archive_search)
|
||
search_layout.addWidget(admin_search_btn)
|
||
layout.addLayout(search_layout)
|
||
|
||
self.admin_archive_table = QTableWidget()
|
||
self.admin_archive_table.setColumnCount(10)
|
||
self.admin_archive_table.setHorizontalHeaderLabels([
|
||
'ID', 'Клиент', 'Оборудование', 'Модель', 'Статус', 'Дата создания', 'Дата завершения', 'Мастер', 'Тип', 'Стоимость'
|
||
])
|
||
layout.addWidget(self.admin_archive_table)
|
||
|
||
self.main_tabs.addTab(archive_tab, 'Архив')
|
||
|
||
self.load_users()
|
||
self.load_admin_requests()
|
||
self.load_distribution()
|
||
self.load_materials()
|
||
self.load_admin_archive()
|
||
|
||
def register_service_request(self):
|
||
cursor = self.db.conn.cursor()
|
||
cursor.execute('''
|
||
INSERT INTO service_requests
|
||
(client_name, client_phone, client_email, equipment_type, equipment_model, serial_number, problem_description, assigned_operator)
|
||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||
''', (
|
||
self.op_client_name.text(),
|
||
self.op_client_phone.text(),
|
||
self.op_client_email.text(),
|
||
self.op_equipment_type.currentText(),
|
||
self.op_equipment_model.text(),
|
||
self.op_serial_number.text(),
|
||
self.op_problem_description.toPlainText(),
|
||
self.current_user['full_name']
|
||
))
|
||
|
||
self.db.conn.commit()
|
||
|
||
# Запись в историю
|
||
request_id = cursor.lastrowid
|
||
cursor.execute('''
|
||
INSERT INTO request_history (request_id, user_id, action, details)
|
||
VALUES (?, ?, ?, ?)
|
||
''', (request_id, self.current_user['id'], 'Создание заявки', 'Заявка зарегистрирована оператором'))
|
||
|
||
self.db.conn.commit()
|
||
|
||
QMessageBox.information(self, 'Успех', 'Заявка успешно зарегистрирована!')
|
||
|
||
# Очищаем форму
|
||
self.op_client_name.clear()
|
||
self.op_client_phone.clear()
|
||
self.op_client_email.clear()
|
||
self.op_equipment_model.clear()
|
||
self.op_serial_number.clear()
|
||
self.op_problem_description.clear()
|
||
|
||
def load_operator_requests(self):
|
||
cursor = self.db.conn.cursor()
|
||
|
||
status_filter = self.status_filter.currentText()
|
||
priority_filter = self.priority_filter.currentText()
|
||
|
||
query = '''
|
||
SELECT id, client_name, equipment_type, equipment_model, status, priority, request_type,
|
||
created_date, assigned_operator, marked_for_deletion
|
||
FROM service_requests
|
||
WHERE 1=1
|
||
'''
|
||
params = []
|
||
|
||
if status_filter != 'Все':
|
||
query += ' AND status = ?'
|
||
params.append(status_filter)
|
||
|
||
if priority_filter != 'Все':
|
||
query += ' AND priority = ?'
|
||
params.append(priority_filter)
|
||
|
||
query += ' ORDER BY created_date DESC'
|
||
|
||
cursor.execute(query, params)
|
||
requests = cursor.fetchall()
|
||
|
||
self.operator_requests_table.setRowCount(len(requests))
|
||
for row, request in enumerate(requests):
|
||
for col, value in enumerate(request):
|
||
item = QTableWidgetItem(str(value) if value is not None else '')
|
||
|
||
# Помечаем заявки на удаление
|
||
if col == 9 and value == 1:
|
||
item.setBackground(Qt.GlobalColor.yellow)
|
||
|
||
self.operator_requests_table.setItem(row, col, item)
|
||
|
||
def load_master_requests(self):
|
||
cursor = self.db.conn.cursor()
|
||
cursor.execute('''
|
||
SELECT id, client_name, equipment_type, equipment_model, status, priority, created_date, deadline
|
||
FROM service_requests
|
||
WHERE status != 'Выполнена' AND status != 'Отменена'
|
||
ORDER BY
|
||
CASE priority
|
||
WHEN 'Критичный' THEN 1
|
||
WHEN 'Высокий' THEN 2
|
||
WHEN 'Средний' THEN 3
|
||
WHEN 'Низкий' THEN 4
|
||
END,
|
||
created_date
|
||
''')
|
||
|
||
requests = cursor.fetchall()
|
||
|
||
self.master_requests_table.setRowCount(len(requests))
|
||
for row, request in enumerate(requests):
|
||
for col, value in enumerate(request):
|
||
item = QTableWidgetItem(str(value) if value is not None else '')
|
||
|
||
# Подсвечиваем просроченные заявки
|
||
if col == 7 and value:
|
||
deadline = QDate.fromString(value, 'yyyy-MM-dd')
|
||
if deadline < QDate.currentDate():
|
||
item.setBackground(Qt.GlobalColor.red)
|
||
|
||
self.master_requests_table.setItem(row, col, item)
|
||
|
||
def load_master_parts(self):
|
||
cursor = self.db.conn.cursor()
|
||
cursor.execute('''
|
||
SELECT po.id, sr.id, po.part_name, po.part_number, po.quantity, po.status, po.order_date, po.supplier
|
||
FROM part_orders po
|
||
JOIN service_requests sr ON po.request_id = sr.id
|
||
WHERE sr.assigned_master = ? OR ? = 'admin1'
|
||
ORDER BY po.order_date DESC
|
||
''', (self.current_user['full_name'], self.current_user['username']))
|
||
|
||
parts = cursor.fetchall()
|
||
|
||
self.parts_table.setRowCount(len(parts))
|
||
for row, part in enumerate(parts):
|
||
for col, value in enumerate(part):
|
||
self.parts_table.setItem(row, col, QTableWidgetItem(str(value) if value is not None else ''))
|
||
|
||
def load_admin_requests(self):
|
||
cursor = self.db.conn.cursor()
|
||
cursor.execute('''
|
||
SELECT id, client_name, equipment_type, equipment_model, status, priority, request_type,
|
||
created_date, assigned_operator, assigned_master, marked_for_deletion
|
||
FROM service_requests
|
||
ORDER BY created_date DESC
|
||
''')
|
||
|
||
requests = cursor.fetchall()
|
||
|
||
self.admin_requests_table.setRowCount(len(requests))
|
||
for row, request in enumerate(requests):
|
||
for col, value in enumerate(request):
|
||
item = QTableWidgetItem(str(value) if value is not None else '')
|
||
|
||
# Помечаем заявки на удаление
|
||
if col == 10 and value == 1:
|
||
item.setBackground(Qt.GlobalColor.yellow)
|
||
|
||
self.admin_requests_table.setItem(row, col, item)
|
||
|
||
def load_distribution(self):
|
||
cursor = self.db.conn.cursor()
|
||
cursor.execute('''
|
||
SELECT id, client_name, equipment_type, status, assigned_operator, assigned_master,
|
||
observer_group, deadline, priority
|
||
FROM service_requests
|
||
WHERE status != 'Выполнена' AND status != 'Отменена'
|
||
ORDER BY priority, created_date
|
||
''')
|
||
|
||
distributions = cursor.fetchall()
|
||
|
||
self.distribution_table.setRowCount(len(distributions))
|
||
for row, dist in enumerate(distributions):
|
||
for col, value in enumerate(dist):
|
||
item = QTableWidgetItem(str(value) if value is not None else '')
|
||
self.distribution_table.setItem(row, col, item)
|
||
|
||
def load_materials(self):
|
||
cursor = self.db.conn.cursor()
|
||
cursor.execute('''
|
||
SELECT mn.id, sr.id, mn.material_name, mn.material_type, mn.quantity, mn.unit,
|
||
mn.urgency, mn.status, mn.estimated_cost, mn.notes
|
||
FROM material_needs mn
|
||
JOIN service_requests sr ON mn.request_id = sr.id
|
||
ORDER BY mn.urgency DESC, mn.created_date
|
||
''')
|
||
|
||
materials = cursor.fetchall()
|
||
|
||
self.materials_table.setRowCount(len(materials))
|
||
for row, material in enumerate(materials):
|
||
for col, value in enumerate(material):
|
||
self.materials_table.setItem(row, col, QTableWidgetItem(str(value) if value is not None else ''))
|
||
|
||
def load_archive(self):
|
||
cursor = self.db.conn.cursor()
|
||
cursor.execute('''
|
||
SELECT id, client_name, equipment_type, equipment_model, status, created_date, completed_date, assigned_master, request_type
|
||
FROM service_requests
|
||
WHERE status = 'Выполнена'
|
||
ORDER BY completed_date DESC
|
||
''')
|
||
|
||
requests = cursor.fetchall()
|
||
|
||
self.archive_table.setRowCount(len(requests))
|
||
for row, request in enumerate(requests):
|
||
for col, value in enumerate(request):
|
||
self.archive_table.setItem(row, col, QTableWidgetItem(str(value) if value is not None else ''))
|
||
|
||
def load_admin_archive(self):
|
||
cursor = self.db.conn.cursor()
|
||
cursor.execute('''
|
||
SELECT sr.id, sr.client_name, sr.equipment_type, sr.equipment_model, sr.status,
|
||
sr.created_date, sr.completed_date, sr.assigned_master, sr.request_type,
|
||
COALESCE(r.total_cost, 0)
|
||
FROM service_requests sr
|
||
LEFT JOIN reports r ON sr.id = r.request_id
|
||
WHERE sr.status = 'Выполнена'
|
||
ORDER BY sr.completed_date DESC
|
||
''')
|
||
|
||
requests = cursor.fetchall()
|
||
|
||
self.admin_archive_table.setRowCount(len(requests))
|
||
for row, request in enumerate(requests):
|
||
for col, value in enumerate(request):
|
||
self.admin_archive_table.setItem(row, col, QTableWidgetItem(str(value) if value is not None else ''))
|
||
|
||
def load_users(self):
|
||
cursor = self.db.conn.cursor()
|
||
cursor.execute('SELECT id, username, full_name, role, phone, email FROM users')
|
||
|
||
users = cursor.fetchall()
|
||
|
||
self.users_table.setRowCount(len(users))
|
||
for row, user in enumerate(users):
|
||
for col, value in enumerate(user):
|
||
self.users_table.setItem(row, col, QTableWidgetItem(str(value)))
|
||
|
||
def update_request_operator(self):
|
||
current_row = self.operator_requests_table.currentRow()
|
||
if current_row >= 0:
|
||
request_id = self.operator_requests_table.item(current_row, 0).text()
|
||
|
||
cursor = self.db.conn.cursor()
|
||
cursor.execute('''
|
||
UPDATE service_requests
|
||
SET status = ?, priority = ?, request_type = ?, observer_group = ?, assigned_operator = ?
|
||
WHERE id = ?
|
||
''', (
|
||
self.status_combo.currentText(),
|
||
self.priority_combo.currentText(),
|
||
self.type_combo.currentText(),
|
||
self.observer_group.text(),
|
||
self.current_user['full_name'],
|
||
request_id
|
||
))
|
||
|
||
# Запись в историю
|
||
cursor.execute('''
|
||
INSERT INTO request_history (request_id, user_id, action, details)
|
||
VALUES (?, ?, ?, ?)
|
||
''', (request_id, self.current_user['id'], 'Изменение заявки',
|
||
f'Оператор изменил статус на {self.status_combo.currentText()}, приоритет на {self.priority_combo.currentText()}'))
|
||
|
||
self.db.conn.commit()
|
||
self.load_operator_requests()
|
||
QMessageBox.information(self, 'Успех', 'Заявка обновлена!')
|
||
|
||
def mark_request_for_deletion(self):
|
||
current_row = self.operator_requests_table.currentRow()
|
||
if current_row >= 0:
|
||
request_id = self.operator_requests_table.item(current_row, 0).text()
|
||
|
||
cursor = self.db.conn.cursor()
|
||
cursor.execute('''
|
||
UPDATE service_requests
|
||
SET marked_for_deletion = 1
|
||
WHERE id = ?
|
||
''', (request_id,))
|
||
|
||
self.db.conn.commit()
|
||
self.load_operator_requests()
|
||
QMessageBox.information(self, 'Успех', 'Заявка помечена на удаление!')
|
||
|
||
def update_request_master(self):
|
||
current_row = self.master_requests_table.currentRow()
|
||
if current_row >= 0:
|
||
request_id = self.master_requests_table.item(current_row, 0).text()
|
||
|
||
cursor = self.db.conn.cursor()
|
||
cursor.execute('''
|
||
UPDATE service_requests
|
||
SET status = ?, assigned_master = ?
|
||
WHERE id = ?
|
||
''', (
|
||
self.master_status_combo.currentText(),
|
||
self.current_user['full_name'],
|
||
request_id
|
||
))
|
||
|
||
# Запись в историю
|
||
cursor.execute('''
|
||
INSERT INTO request_history (request_id, user_id, action, details)
|
||
VALUES (?, ?, ?, ?)
|
||
''', (request_id, self.current_user['id'], 'Изменение статуса',
|
||
f'Мастер изменил статус на {self.master_status_combo.currentText()}'))
|
||
|
||
self.db.conn.commit()
|
||
self.load_master_requests()
|
||
QMessageBox.information(self, 'Успех', 'Статус заявки обновлен!')
|
||
|
||
def order_parts(self):
|
||
current_row = self.master_requests_table.currentRow()
|
||
if current_row >= 0:
|
||
request_id = self.master_requests_table.item(current_row, 0).text()
|
||
|
||
cursor = self.db.conn.cursor()
|
||
cursor.execute('''
|
||
INSERT INTO part_orders (request_id, part_name, part_number, quantity, supplier, estimated_cost, ordered_by)
|
||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||
''', (
|
||
request_id,
|
||
self.part_name.text(),
|
||
self.part_number.text(),
|
||
int(self.part_quantity.text()),
|
||
self.part_supplier.text(),
|
||
float(self.part_cost.text() or 0),
|
||
self.current_user['id']
|
||
))
|
||
|
||
self.db.conn.commit()
|
||
|
||
# Обновляем статус заявки
|
||
cursor.execute('''
|
||
UPDATE service_requests
|
||
SET status = 'Ожидает запчасти'
|
||
WHERE id = ?
|
||
''', (request_id,))
|
||
|
||
# Запись в историю
|
||
cursor.execute('''
|
||
INSERT INTO request_history (request_id, user_id, action, details)
|
||
VALUES (?, ?, ?, ?)
|
||
''', (request_id, self.current_user['id'], 'Заказ запчастей',
|
||
f'Заказана запчасть: {self.part_name.text()}, количество: {self.part_quantity.text()}'))
|
||
|
||
self.db.conn.commit()
|
||
|
||
# Очищаем форму
|
||
self.part_name.clear()
|
||
self.part_number.clear()
|
||
self.part_quantity.setText('1')
|
||
self.part_supplier.clear()
|
||
self.part_cost.clear()
|
||
|
||
self.load_master_requests()
|
||
self.load_master_parts()
|
||
|
||
QMessageBox.information(self, 'Успех', 'Запчасти заказаны!')
|
||
|
||
def attach_photo(self):
|
||
self.attach_file(file_type='photo')
|
||
|
||
def attach_file(self, file_type='document'):
|
||
current_row = self.master_requests_table.currentRow()
|
||
if current_row >= 0:
|
||
request_id = self.master_requests_table.item(current_row, 0).text()
|
||
|
||
file_path, _ = QFileDialog.getOpenFileName(self, 'Выберите файл')
|
||
if file_path:
|
||
filename = os.path.basename(file_path)
|
||
|
||
cursor = self.db.conn.cursor()
|
||
cursor.execute('''
|
||
INSERT INTO attachments (request_id, filename, filepath, file_type, uploaded_by)
|
||
VALUES (?, ?, ?, ?, ?)
|
||
''', (
|
||
request_id,
|
||
filename,
|
||
file_path,
|
||
file_type,
|
||
self.current_user['id']
|
||
))
|
||
|
||
# Запись в историю
|
||
cursor.execute('''
|
||
INSERT INTO request_history (request_id, user_id, action, details)
|
||
VALUES (?, ?, ?, ?)
|
||
''', (request_id, self.current_user['id'], 'Прикрепление файла',
|
||
f'Прикреплен файл: {filename}'))
|
||
|
||
self.db.conn.commit()
|
||
QMessageBox.information(self, 'Успех', 'Файл прикреплен!')
|
||
|
||
def create_report(self):
|
||
current_row = self.master_requests_table.currentRow()
|
||
if current_row >= 0:
|
||
request_id = self.master_requests_table.item(current_row, 0).text()
|
||
|
||
# Диалог для ввода отчета
|
||
report_dialog = QDialog(self)
|
||
report_dialog.setWindowTitle('Создание отчета о выполненной работе')
|
||
report_dialog.setModal(True)
|
||
report_dialog.resize(500, 400)
|
||
layout = QVBoxLayout(report_dialog)
|
||
|
||
form_layout = QFormLayout()
|
||
work_description = QTextEdit()
|
||
parts_used = QLineEdit()
|
||
time_spent = QLineEdit()
|
||
labor_cost = QLineEdit()
|
||
parts_cost = QLineEdit()
|
||
|
||
form_layout.addRow('Описание выполненной работы:', work_description)
|
||
form_layout.addRow('Использованные запчасти:', parts_used)
|
||
form_layout.addRow('Затраченное время (часы):', time_spent)
|
||
form_layout.addRow('Стоимость работы:', labor_cost)
|
||
form_layout.addRow('Стоимость запчастей:', parts_cost)
|
||
|
||
layout.addLayout(form_layout)
|
||
|
||
buttons_layout = QHBoxLayout()
|
||
save_btn = QPushButton('Сохранить отчет')
|
||
cancel_btn = QPushButton('Отмена')
|
||
|
||
buttons_layout.addWidget(save_btn)
|
||
buttons_layout.addWidget(cancel_btn)
|
||
layout.addLayout(buttons_layout)
|
||
|
||
def save_report():
|
||
total = float(labor_cost.text() or 0) + float(parts_cost.text() or 0)
|
||
|
||
cursor = self.db.conn.cursor()
|
||
cursor.execute('''
|
||
INSERT INTO reports (request_id, master_id, work_description, parts_used, time_spent, labor_cost, parts_cost, total_cost)
|
||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||
''', (
|
||
request_id,
|
||
self.current_user['id'],
|
||
work_description.toPlainText(),
|
||
parts_used.text(),
|
||
float(time_spent.text() or 0),
|
||
float(labor_cost.text() or 0),
|
||
float(parts_cost.text() or 0),
|
||
total
|
||
))
|
||
|
||
# Обновляем статус заявки на выполненную
|
||
cursor.execute('''
|
||
UPDATE service_requests
|
||
SET status = 'Выполнена', completed_date = CURRENT_TIMESTAMP
|
||
WHERE id = ?
|
||
''', (request_id,))
|
||
|
||
# Запись в историю
|
||
cursor.execute('''
|
||
INSERT INTO request_history (request_id, user_id, action, details)
|
||
VALUES (?, ?, ?, ?)
|
||
''', (request_id, self.current_user['id'], 'Создание отчета',
|
||
'Отчет о выполненной работе создан'))
|
||
|
||
self.db.conn.commit()
|
||
report_dialog.accept()
|
||
self.load_master_requests()
|
||
QMessageBox.information(self, 'Успех', 'Отчет создан!')
|
||
|
||
save_btn.clicked.connect(save_report)
|
||
cancel_btn.clicked.connect(report_dialog.reject)
|
||
|
||
report_dialog.exec()
|
||
|
||
def show_request_history(self):
|
||
current_row = self.master_requests_table.currentRow()
|
||
if current_row >= 0:
|
||
request_id = self.master_requests_table.item(current_row, 0).text()
|
||
|
||
cursor = self.db.conn.cursor()
|
||
cursor.execute('''
|
||
SELECT h.timestamp, u.full_name, h.action, h.details
|
||
FROM request_history h
|
||
JOIN users u ON h.user_id = u.id
|
||
WHERE h.request_id = ?
|
||
ORDER BY h.timestamp DESC
|
||
''', (request_id,))
|
||
|
||
history = cursor.fetchall()
|
||
|
||
history_dialog = QDialog(self)
|
||
history_dialog.setWindowTitle(f'История заявки #{request_id}')
|
||
history_dialog.setModal(True)
|
||
history_dialog.resize(600, 400)
|
||
layout = QVBoxLayout(history_dialog)
|
||
|
||
history_table = QTableWidget()
|
||
history_table.setColumnCount(4)
|
||
history_table.setHorizontalHeaderLabels(['Дата', 'Пользователь', 'Действие', 'Детали'])
|
||
history_table.setRowCount(len(history))
|
||
|
||
for row, record in enumerate(history):
|
||
for col, value in enumerate(record):
|
||
history_table.setItem(row, col, QTableWidgetItem(str(value)))
|
||
|
||
layout.addWidget(history_table)
|
||
|
||
close_btn = QPushButton('Закрыть')
|
||
close_btn.clicked.connect(history_dialog.accept)
|
||
layout.addWidget(close_btn)
|
||
|
||
history_dialog.exec()
|
||
|
||
def search_archive(self):
|
||
search_text = self.archive_search.text()
|
||
cursor = self.db.conn.cursor()
|
||
|
||
if search_text:
|
||
cursor.execute('''
|
||
SELECT id, client_name, equipment_type, equipment_model, status, created_date, completed_date, assigned_master, request_type
|
||
FROM service_requests
|
||
WHERE status = 'Выполнена' AND
|
||
(client_name LIKE ? OR equipment_type LIKE ? OR equipment_model LIKE ? OR assigned_master LIKE ?)
|
||
ORDER BY completed_date DESC
|
||
''', (f'%{search_text}%', f'%{search_text}%', f'%{search_text}%', f'%{search_text}%'))
|
||
else:
|
||
cursor.execute('''
|
||
SELECT id, client_name, equipment_type, equipment_model, status, created_date, completed_date, assigned_master, request_type
|
||
FROM service_requests
|
||
WHERE status = 'Выполнена'
|
||
ORDER BY completed_date DESC
|
||
''')
|
||
|
||
requests = cursor.fetchall()
|
||
|
||
self.archive_table.setRowCount(len(requests))
|
||
for row, request in enumerate(requests):
|
||
for col, value in enumerate(request):
|
||
self.archive_table.setItem(row, col, QTableWidgetItem(str(value) if value is not None else ''))
|
||
|
||
def search_admin_archive(self):
|
||
search_text = self.admin_archive_search.text()
|
||
cursor = self.db.conn.cursor()
|
||
|
||
if search_text:
|
||
cursor.execute('''
|
||
SELECT sr.id, sr.client_name, sr.equipment_type, sr.equipment_model, sr.status,
|
||
sr.created_date, sr.completed_date, sr.assigned_master, sr.request_type,
|
||
COALESCE(r.total_cost, 0)
|
||
FROM service_requests sr
|
||
LEFT JOIN reports r ON sr.id = r.request_id
|
||
WHERE sr.status = 'Выполнена' AND
|
||
(sr.client_name LIKE ? OR sr.equipment_type LIKE ? OR sr.equipment_model LIKE ? OR sr.assigned_master LIKE ?)
|
||
ORDER BY sr.completed_date DESC
|
||
''', (f'%{search_text}%', f'%{search_text}%', f'%{search_text}%', f'%{search_text}%'))
|
||
else:
|
||
cursor.execute('''
|
||
SELECT sr.id, sr.client_name, sr.equipment_type, sr.equipment_model, sr.status,
|
||
sr.created_date, sr.completed_date, sr.assigned_master, sr.request_type,
|
||
COALESCE(r.total_cost, 0)
|
||
FROM service_requests sr
|
||
LEFT JOIN reports r ON sr.id = r.request_id
|
||
WHERE sr.status = 'Выполнена'
|
||
ORDER BY sr.completed_date DESC
|
||
''')
|
||
|
||
requests = cursor.fetchall()
|
||
|
||
self.admin_archive_table.setRowCount(len(requests))
|
||
for row, request in enumerate(requests):
|
||
for col, value in enumerate(request):
|
||
self.admin_archive_table.setItem(row, col, QTableWidgetItem(str(value) if value is not None else ''))
|
||
|
||
def show_add_user_dialog(self):
|
||
dialog = QDialog(self)
|
||
dialog.setWindowTitle('Добавить пользователя')
|
||
dialog.setModal(True)
|
||
layout = QVBoxLayout(dialog)
|
||
|
||
form_layout = QFormLayout()
|
||
username = QLineEdit()
|
||
password = QLineEdit()
|
||
password.setEchoMode(QLineEdit.EchoMode.Password)
|
||
full_name = QLineEdit()
|
||
phone = QLineEdit()
|
||
email = QLineEdit()
|
||
role = QComboBox()
|
||
role.addItems(['operator', 'master', 'admin'])
|
||
|
||
form_layout.addRow('Логин:', username)
|
||
form_layout.addRow('Пароль:', password)
|
||
form_layout.addRow('ФИО:', full_name)
|
||
form_layout.addRow('Телефон:', phone)
|
||
form_layout.addRow('Email:', email)
|
||
form_layout.addRow('Роль:', role)
|
||
|
||
layout.addLayout(form_layout)
|
||
|
||
buttons_layout = QHBoxLayout()
|
||
save_btn = QPushButton('Сохранить')
|
||
cancel_btn = QPushButton('Отмена')
|
||
|
||
buttons_layout.addWidget(save_btn)
|
||
buttons_layout.addWidget(cancel_btn)
|
||
layout.addLayout(buttons_layout)
|
||
|
||
def save_user():
|
||
cursor = self.db.conn.cursor()
|
||
try:
|
||
cursor.execute('''
|
||
INSERT INTO users (username, password, role, full_name, phone, email)
|
||
VALUES (?, ?, ?, ?, ?, ?)
|
||
''', (
|
||
username.text(),
|
||
password.text(),
|
||
role.currentText(),
|
||
full_name.text(),
|
||
phone.text(),
|
||
email.text()
|
||
))
|
||
|
||
self.db.conn.commit()
|
||
dialog.accept()
|
||
self.load_users()
|
||
QMessageBox.information(self, 'Успех', 'Пользователь добавлен!')
|
||
except sqlite3.IntegrityError:
|
||
QMessageBox.warning(self, 'Ошибка', 'Пользователь с таким логином уже существует!')
|
||
|
||
save_btn.clicked.connect(save_user)
|
||
cancel_btn.clicked.connect(dialog.reject)
|
||
|
||
dialog.exec()
|
||
|
||
def delete_marked_requests(self):
|
||
cursor = self.db.conn.cursor()
|
||
cursor.execute('SELECT COUNT(*) FROM service_requests WHERE marked_for_deletion = 1')
|
||
count = cursor.fetchone()[0]
|
||
|
||
if count == 0:
|
||
QMessageBox.information(self, 'Информация', 'Нет заявок, помеченных на удаление')
|
||
return
|
||
|
||
reply = QMessageBox.question(self, 'Подтверждение',
|
||
f'Вы уверены, что хотите удалить {count} заявок, помеченных на удаление?',
|
||
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
|
||
|
||
if reply == QMessageBox.StandardButton.Yes:
|
||
cursor.execute('DELETE FROM service_requests WHERE marked_for_deletion = 1')
|
||
self.db.conn.commit()
|
||
self.load_admin_requests()
|
||
QMessageBox.information(self, 'Успех', f'Удалено {count} заявок')
|
||
|
||
def consolidate_material_needs(self):
|
||
cursor = self.db.conn.cursor()
|
||
|
||
# Создаем консолидированный список потребностей в МТР
|
||
cursor.execute('''
|
||
SELECT material_name, material_type, SUM(quantity), unit, urgency, SUM(estimated_cost)
|
||
FROM material_needs
|
||
WHERE status = 'Требуется'
|
||
GROUP BY material_name, material_type, unit, urgency
|
||
ORDER BY urgency, material_type, material_name
|
||
''')
|
||
|
||
consolidated = cursor.fetchall()
|
||
|
||
dialog = QDialog(self)
|
||
dialog.setWindowTitle('Консолидированные потребности в МТР')
|
||
dialog.setModal(True)
|
||
dialog.resize(700, 500)
|
||
layout = QVBoxLayout(dialog)
|
||
|
||
table = QTableWidget()
|
||
table.setColumnCount(6)
|
||
table.setHorizontalHeaderLabels(['Материал', 'Тип', 'Количество', 'Ед.изм', 'Срочность', 'Общая стоимость'])
|
||
table.setRowCount(len(consolidated))
|
||
|
||
for row, record in enumerate(consolidated):
|
||
for col, value in enumerate(record):
|
||
table.setItem(row, col, QTableWidgetItem(str(value)))
|
||
|
||
layout.addWidget(table)
|
||
|
||
close_btn = QPushButton('Закрыть')
|
||
close_btn.clicked.connect(dialog.accept)
|
||
layout.addWidget(close_btn)
|
||
|
||
dialog.exec()
|
||
|
||
def generate_mtr_report(self):
|
||
cursor = self.db.conn.cursor()
|
||
|
||
# Формируем отчет по МТР
|
||
cursor.execute('''
|
||
SELECT
|
||
mn.material_name,
|
||
mn.material_type,
|
||
SUM(mn.quantity) as total_quantity,
|
||
mn.unit,
|
||
mn.urgency,
|
||
COUNT(DISTINCT mn.request_id) as request_count,
|
||
SUM(mn.estimated_cost) as total_cost
|
||
FROM material_needs mn
|
||
WHERE mn.status = 'Требуется'
|
||
GROUP BY mn.material_name, mn.material_type, mn.unit, mn.urgency
|
||
ORDER BY mn.urgency DESC, total_cost DESC
|
||
''')
|
||
|
||
report_data = cursor.fetchall()
|
||
|
||
dialog = QDialog(self)
|
||
dialog.setWindowTitle('Отчет по материально-техническим ресурсам')
|
||
dialog.setModal(True)
|
||
dialog.resize(800, 600)
|
||
layout = QVBoxLayout(dialog)
|
||
|
||
layout.addWidget(QLabel('Отчет по потребностям в МТР'))
|
||
|
||
table = QTableWidget()
|
||
table.setColumnCount(7)
|
||
table.setHorizontalHeaderLabels(['Материал', 'Тип', 'Общее кол-во', 'Ед.изм', 'Срочность', 'Кол-во заявок', 'Общая стоимость'])
|
||
table.setRowCount(len(report_data))
|
||
|
||
total_cost = 0
|
||
for row, record in enumerate(report_data):
|
||
for col, value in enumerate(record):
|
||
table.setItem(row, col, QTableWidgetItem(str(value)))
|
||
total_cost += float(record[6] or 0)
|
||
|
||
layout.addWidget(table)
|
||
layout.addWidget(QLabel(f'Общая стоимость всех МТР: {total_cost:.2f} руб.'))
|
||
|
||
close_btn = QPushButton('Закрыть')
|
||
close_btn.clicked.connect(dialog.accept)
|
||
layout.addWidget(close_btn)
|
||
|
||
dialog.exec()
|
||
|
||
if __name__ == '__main__':
|
||
app = QApplication(sys.argv)
|
||
window = ServiceRequestAppV2()
|
||
window.show()
|
||
sys.exit(app.exec())
|