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) from PyQt6.QtCore import Qt, pyqtSignal from PyQt6.QtGui import QIcon, QAction import os class DatabaseManager: def __init__(self): self.conn = sqlite3.connect('service_requests.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 ) ''') # Таблица заявок cursor.execute(''' CREATE TABLE IF NOT EXISTS service_requests ( id INTEGER PRIMARY KEY AUTOINCREMENT, client_name TEXT NOT NULL, client_phone TEXT NOT NULL, equipment_type TEXT NOT NULL, equipment_model TEXT NOT NULL, problem_description TEXT NOT NULL, status TEXT DEFAULT 'Новая', priority TEXT DEFAULT 'Средний', request_type TEXT DEFAULT 'Ремонт', assigned_operator TEXT, assigned_master TEXT, created_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, completed_date TIMESTAMP, observer_group TEXT ) ''') # Таблица истории заявок cursor.execute(''' CREATE TABLE IF NOT EXISTS request_history ( id INTEGER PRIMARY KEY AUTOINCREMENT, request_id INTEGER, user_id INTEGER, action 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, 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, 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, quantity INTEGER, status TEXT DEFAULT 'Заказан', ordered_by INTEGER, order_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (request_id) REFERENCES service_requests (id) ) ''') # Создаем тестовых пользователей self.create_test_users() self.conn.commit() def create_test_users(self): cursor = self.conn.cursor() test_users = [ ('client1', '123', 'client', 'Иван Иванов', '+79161234567'), ('operator1', '123', 'operator', 'Петр Петров', '+79167654321'), ('master1', '123', 'master', 'Сергей Сергеев', '+79169998877'), ('admin1', '123', 'admin', 'Администратор Системы', '+79160001122') ] for user in test_users: try: cursor.execute( 'INSERT INTO users (username, password, role, full_name, phone) VALUES (?, ?, ?, ?, ?)', user ) except sqlite3.IntegrityError: pass # Пользователь уже существует self.conn.commit() class ServiceRequestApp(QMainWindow): def __init__(self): super().__init__() self.db = DatabaseManager() self.current_user = None self.init_ui() def init_ui(self): self.setWindowTitle('Система учета заявок на ремонт оргтехники') self.setGeometry(100, 100, 1200, 700) # Центральный виджет 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('Вход в систему')) 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) 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 == 'client': self.setup_client_interface() elif role == 'operator': self.setup_operator_interface() elif role == 'master': self.setup_master_interface() elif role == 'admin': self.setup_admin_interface() def setup_client_interface(self): # Вкладка создания заявки create_tab = QWidget() layout = QVBoxLayout(create_tab) form_layout = QFormLayout() self.client_name = QLineEdit(self.current_user['full_name']) self.client_phone = QLineEdit() self.equipment_type = QComboBox() self.equipment_type.addItems(['Принтер', 'Копир', 'Сканер', 'МФУ', 'Компьютер']) self.equipment_model = QLineEdit() self.problem_description = QTextEdit() form_layout.addRow('ФИО:', self.client_name) form_layout.addRow('Телефон:', self.client_phone) form_layout.addRow('Тип оборудования:', self.equipment_type) form_layout.addRow('Модель:', self.equipment_model) form_layout.addRow('Описание проблемы:', self.problem_description) layout.addLayout(form_layout) submit_btn = QPushButton('Создать заявку') submit_btn.clicked.connect(self.create_service_request) layout.addWidget(submit_btn) self.main_tabs.addTab(create_tab, 'Новая заявка') # Вкладка моих заявок requests_tab = QWidget() layout = QVBoxLayout(requests_tab) self.client_requests_table = QTableWidget() self.client_requests_table.setColumnCount(6) self.client_requests_table.setHorizontalHeaderLabels([ 'ID', 'Оборудование', 'Модель', 'Статус', 'Дата создания', 'Приоритет' ]) layout.addWidget(self.client_requests_table) details_btn = QPushButton('Просмотреть детали') details_btn.clicked.connect(self.show_request_details) layout.addWidget(details_btn) self.main_tabs.addTab(requests_tab, 'Мои заявки') self.load_client_requests() def setup_operator_interface(self): # Вкладка всех заявок requests_tab = QWidget() layout = QVBoxLayout(requests_tab) self.operator_requests_table = QTableWidget() self.operator_requests_table.setColumnCount(8) 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() 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(QLabel('Группа наблюдателей:')) control_layout.addWidget(self.observer_group) control_layout.addWidget(update_btn) layout.addWidget(control_group) self.main_tabs.addTab(requests_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(8) 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(7) 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_layout = QHBoxLayout() parts_layout.addWidget(QLabel('Запчасть:')) self.part_name = QLineEdit() self.part_quantity = QLineEdit() self.part_quantity.setText('1') parts_layout.addWidget(self.part_name) parts_layout.addWidget(QLabel('Количество:')) parts_layout.addWidget(self.part_quantity) order_parts_btn = QPushButton('Заказать запчасти') order_parts_btn.clicked.connect(self.order_parts) parts_layout.addWidget(order_parts_btn) control_layout.addLayout(parts_layout) # Прикрепление файлов file_layout = QHBoxLayout() attach_file_btn = QPushButton('Прикрепить файл') attach_file_btn.clicked.connect(self.attach_file) file_layout.addWidget(attach_file_btn) control_layout.addLayout(file_layout) # Создание отчета report_layout = QHBoxLayout() create_report_btn = QPushButton('Создать отчет') create_report_btn.clicked.connect(self.create_report) report_layout.addWidget(create_report_btn) control_layout.addLayout(report_layout) layout.addWidget(control_group) self.main_tabs.addTab(requests_tab, 'Мои заявки') self.load_master_requests() def setup_admin_interface(self): # Админский интерфейс будет похож на операторский с дополнительными функциями self.setup_operator_interface() # Добавляем вкладку управления пользователями users_tab = QWidget() layout = QVBoxLayout(users_tab) self.users_table = QTableWidget() self.users_table.setColumnCount(4) self.users_table.setHorizontalHeaderLabels(['ID', 'Логин', 'ФИО', 'Роль']) 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, 'Пользователи') self.load_users() def create_service_request(self): cursor = self.db.conn.cursor() cursor.execute(''' INSERT INTO service_requests (client_name, client_phone, equipment_type, equipment_model, problem_description) VALUES (?, ?, ?, ?, ?) ''', ( self.client_name.text(), self.client_phone.text(), self.equipment_type.currentText(), self.equipment_model.text(), self.problem_description.toPlainText() )) self.db.conn.commit() QMessageBox.information(self, 'Успех', 'Заявка успешно создана!') # Очищаем форму self.client_phone.clear() self.equipment_model.clear() self.problem_description.clear() def load_client_requests(self): cursor = self.db.conn.cursor() cursor.execute(''' SELECT id, equipment_type, equipment_model, status, created_date, priority FROM service_requests WHERE client_name = ? ORDER BY created_date DESC ''', (self.current_user['full_name'],)) requests = cursor.fetchall() self.client_requests_table.setRowCount(len(requests)) for row, request in enumerate(requests): for col, value in enumerate(request): self.client_requests_table.setItem(row, col, QTableWidgetItem(str(value))) def load_operator_requests(self): cursor = self.db.conn.cursor() cursor.execute(''' SELECT id, client_name, equipment_type, equipment_model, status, priority, created_date, assigned_operator FROM service_requests ORDER BY created_date DESC ''') requests = cursor.fetchall() self.operator_requests_table.setRowCount(len(requests)) for row, request in enumerate(requests): for col, value in enumerate(request): self.operator_requests_table.setItem(row, col, QTableWidgetItem(str(value) if value is not None else '')) def load_master_requests(self): cursor = self.db.conn.cursor() cursor.execute(''' SELECT id, client_name, equipment_type, equipment_model, status, priority, created_date 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): self.master_requests_table.setItem(row, col, QTableWidgetItem(str(value))) 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 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_users(self): cursor = self.db.conn.cursor() cursor.execute('SELECT id, username, full_name, role 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 show_request_details(self): current_row = self.client_requests_table.currentRow() if current_row >= 0: request_id = self.client_requests_table.item(current_row, 0).text() cursor = self.db.conn.cursor() cursor.execute('SELECT * FROM service_requests WHERE id = ?', (request_id,)) request = cursor.fetchone() if request: details = f""" ID: {request[0]} Клиент: {request[1]} Телефон: {request[2]} Оборудование: {request[3]} Модель: {request[4]} Описание проблемы: {request[5]} Статус: {request[6]} Приоритет: {request[7]} Тип: {request[8]} Дата создания: {request[11]} """ QMessageBox.information(self, 'Детали заявки', details) 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 )) 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 )) 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, quantity, ordered_by) VALUES (?, ?, ?, ?) ''', ( request_id, self.part_name.text(), int(self.part_quantity.text()), self.current_user['id'] )) self.db.conn.commit() # Обновляем статус заявки cursor.execute(''' UPDATE service_requests SET status = 'Ожидает запчасти' WHERE id = ? ''', (request_id,)) self.db.conn.commit() self.part_name.clear() self.part_quantity.setText('1') self.load_master_requests() QMessageBox.information(self, 'Успех', 'Запчасти заказаны!') def attach_file(self): 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, uploaded_by) VALUES (?, ?, ?, ?) ''', ( request_id, filename, file_path, self.current_user['id'] )) 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) layout = QVBoxLayout(report_dialog) form_layout = QFormLayout() work_description = QTextEdit() parts_used = QLineEdit() time_spent = QLineEdit() form_layout.addRow('Описание работы:', work_description) form_layout.addRow('Использованные запчасти:', parts_used) form_layout.addRow('Затраченное время (часы):', time_spent) 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(): cursor = self.db.conn.cursor() cursor.execute(''' INSERT INTO reports (request_id, master_id, work_description, parts_used, time_spent) VALUES (?, ?, ?, ?, ?) ''', ( request_id, self.current_user['id'], work_description.toPlainText(), parts_used.text(), float(time_spent.text()) )) # Обновляем статус заявки на выполненную cursor.execute(''' UPDATE service_requests SET status = 'Выполнена', completed_date = CURRENT_TIMESTAMP WHERE id = ? ''', (request_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 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 FROM service_requests WHERE status = 'Выполнена' AND (client_name LIKE ? OR equipment_type LIKE ? OR equipment_model LIKE ?) ORDER BY completed_date DESC ''', (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 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 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() role = QComboBox() role.addItems(['client', 'operator', 'master', 'admin']) form_layout.addRow('Логин:', username) form_layout.addRow('Пароль:', password) form_layout.addRow('ФИО:', full_name) form_layout.addRow('Телефон:', phone) 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) VALUES (?, ?, ?, ?, ?) ''', ( username.text(), password.text(), role.currentText(), full_name.text(), phone.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() class Dialog(QDialog): pass # Вспомогательный класс для диалогов if __name__ == '__main__': app = QApplication(sys.argv) window = ServiceRequestApp() window.show() sys.exit(app.exec())