After Graduate Update
This commit is contained in:
parent
b92a91ab37
commit
c6917dd85e
69 changed files with 7540 additions and 0 deletions
9
ressult/gui/__init__.py
Normal file
9
ressult/gui/__init__.py
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
# gui/__init__.py
|
||||
"""
|
||||
Пакет графического интерфейса с авторизацией
|
||||
"""
|
||||
from .login_window import LoginWindow
|
||||
from .main_window import MainWindow
|
||||
from .partner_form import PartnerForm
|
||||
from .sales_history import SalesHistoryWindow
|
||||
from .material_calculator import MaterialCalculatorWindow
|
||||
BIN
ressult/gui/__pycache__/__init__.cpython-314.pyc
Normal file
BIN
ressult/gui/__pycache__/__init__.cpython-314.pyc
Normal file
Binary file not shown.
BIN
ressult/gui/__pycache__/login_window.cpython-314.pyc
Normal file
BIN
ressult/gui/__pycache__/login_window.cpython-314.pyc
Normal file
Binary file not shown.
BIN
ressult/gui/__pycache__/main_window.cpython-314.pyc
Normal file
BIN
ressult/gui/__pycache__/main_window.cpython-314.pyc
Normal file
Binary file not shown.
BIN
ressult/gui/__pycache__/material_calculator.cpython-314.pyc
Normal file
BIN
ressult/gui/__pycache__/material_calculator.cpython-314.pyc
Normal file
Binary file not shown.
BIN
ressult/gui/__pycache__/partner_form.cpython-314.pyc
Normal file
BIN
ressult/gui/__pycache__/partner_form.cpython-314.pyc
Normal file
Binary file not shown.
BIN
ressult/gui/__pycache__/sales_history.cpython-314.pyc
Normal file
BIN
ressult/gui/__pycache__/sales_history.cpython-314.pyc
Normal file
Binary file not shown.
253
ressult/gui/login_window.py
Normal file
253
ressult/gui/login_window.py
Normal file
|
|
@ -0,0 +1,253 @@
|
|||
# gui/login_window.py
|
||||
"""
|
||||
Окно авторизации менеджера
|
||||
Соответствует требованиям ТЗ по аутентификации
|
||||
"""
|
||||
import sys
|
||||
from PyQt6.QtWidgets import (QApplication, QDialog, QVBoxLayout, QHBoxLayout,
|
||||
QLabel, QLineEdit, QPushButton, QMessageBox,
|
||||
QFrame, QCheckBox)
|
||||
from PyQt6.QtCore import Qt, pyqtSignal
|
||||
from PyQt6.QtGui import QFont, QPixmap, QIcon
|
||||
import requests
|
||||
from requests.auth import HTTPBasicAuth
|
||||
|
||||
class LoginWindow(QDialog):
|
||||
"""Окно авторизации системы MasterPol"""
|
||||
|
||||
login_success = pyqtSignal(dict) # Сигнал об успешной авторизации
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setup_ui()
|
||||
self.load_settings()
|
||||
|
||||
def setup_ui(self):
|
||||
"""Настройка интерфейса окна авторизации"""
|
||||
self.setWindowTitle("MasterPol - Авторизация")
|
||||
self.setFixedSize(400, 500)
|
||||
self.setModal(True)
|
||||
|
||||
# Установка иконки приложения
|
||||
try:
|
||||
self.setWindowIcon(QIcon("resources/icon.png"))
|
||||
except:
|
||||
pass
|
||||
|
||||
layout = QVBoxLayout()
|
||||
layout.setContentsMargins(30, 30, 30, 30)
|
||||
layout.setSpacing(0)
|
||||
|
||||
# Заголовок
|
||||
title_label = QLabel("MasterPol")
|
||||
title_label.setFont(QFont("Arial", 24, QFont.Weight.Bold))
|
||||
title_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
title_label.setStyleSheet("color: #007acc; margin-bottom: 20px;")
|
||||
|
||||
subtitle_label = QLabel("Система управления партнерами")
|
||||
subtitle_label.setFont(QFont("Arial", 12))
|
||||
subtitle_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
subtitle_label.setStyleSheet("color: #666; margin-bottom: 30px;")
|
||||
|
||||
layout.addWidget(title_label)
|
||||
layout.addWidget(subtitle_label)
|
||||
|
||||
# Форма авторизаци
|
||||
form_frame = QFrame()
|
||||
form_frame.setStyleSheet("""
|
||||
QFrame {
|
||||
background-color: white;
|
||||
border: 0px solid #ddd;
|
||||
border-radius: 4px;
|
||||
padding: 20px;
|
||||
}
|
||||
""")
|
||||
|
||||
form_layout = QVBoxLayout()
|
||||
form_layout.setSpacing(15)
|
||||
|
||||
# Поле логина
|
||||
username_layout = QVBoxLayout()
|
||||
username_label = QLabel("Имя пользователя:")
|
||||
username_label.setStyleSheet("font-weight: bold; color: #333;")
|
||||
|
||||
self.username_input = QLineEdit()
|
||||
self.username_input.setPlaceholderText("Введите имя пользователя")
|
||||
self.username_input.setStyleSheet("""
|
||||
QLineEdit {
|
||||
padding: 8px 12px;
|
||||
border: 2px solid #ccc;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
}
|
||||
QLineEdit:focus {
|
||||
border-color: #007acc;
|
||||
}
|
||||
""")
|
||||
|
||||
username_layout.addWidget(username_label)
|
||||
username_layout.addWidget(self.username_input)
|
||||
|
||||
# Поле пароля
|
||||
password_layout = QVBoxLayout()
|
||||
password_label = QLabel("Пароль:")
|
||||
password_label.setStyleSheet("font-weight: bold; color: #333;")
|
||||
|
||||
self.password_input = QLineEdit()
|
||||
self.password_input.setPlaceholderText("Введите пароль")
|
||||
self.password_input.setEchoMode(QLineEdit.EchoMode.Password)
|
||||
self.password_input.setStyleSheet("""
|
||||
QLineEdit {
|
||||
padding: 10px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
}
|
||||
QLineEdit:focus {
|
||||
border-color: #007acc;
|
||||
}
|
||||
""")
|
||||
|
||||
password_layout.addWidget(password_label)
|
||||
password_layout.addWidget(self.password_input)
|
||||
|
||||
# Запомнить меня
|
||||
self.remember_checkbox = QCheckBox("Запомнить меня")
|
||||
self.remember_checkbox.setStyleSheet("color: #333;")
|
||||
|
||||
# Кнопка входа
|
||||
self.login_button = QPushButton("Войти в систему")
|
||||
self.login_button.clicked.connect(self.authenticate)
|
||||
self.login_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #007acc;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 12px;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #005a9e;
|
||||
}
|
||||
QPushButton:disabled {
|
||||
background-color: #ccc;
|
||||
color: #666;
|
||||
}
|
||||
""")
|
||||
|
||||
# Подсказка
|
||||
hint_label = QLabel("Используйте логин: manager, пароль: pass123")
|
||||
hint_label.setStyleSheet("color: #666; font-size: 12px; margin-top: 10px;")
|
||||
hint_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
|
||||
form_layout.addLayout(username_layout)
|
||||
form_layout.addLayout(password_layout)
|
||||
form_layout.addWidget(self.remember_checkbox)
|
||||
form_layout.addWidget(self.login_button)
|
||||
form_layout.addWidget(hint_label)
|
||||
|
||||
form_frame.setLayout(form_layout)
|
||||
layout.addWidget(form_frame)
|
||||
|
||||
# Информация о системе
|
||||
info_label = QLabel("MasterPol v1.0.0\nСистема управления партнерами и продажами")
|
||||
info_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
info_label.setStyleSheet("color: #999; font-size: 11px; margin-top: 20px;")
|
||||
layout.addWidget(info_label)
|
||||
|
||||
self.setLayout(layout)
|
||||
|
||||
# Подключаем обработчики событий
|
||||
self.username_input.returnPressed.connect(self.authenticate)
|
||||
self.password_input.returnPressed.connect(self.authenticate)
|
||||
|
||||
def load_settings(self):
|
||||
"""Загрузка сохраненных настроек авторизации"""
|
||||
try:
|
||||
# Здесь можно добавить загрузку из файла настроек
|
||||
# Пока просто устанавливаем значения по умолчанию
|
||||
self.username_input.setText("manager")
|
||||
except:
|
||||
pass
|
||||
|
||||
def save_settings(self):
|
||||
"""Сохранение настроек авторизации"""
|
||||
if self.remember_checkbox.isChecked():
|
||||
# Здесь можно добавить сохранение в файл настроек
|
||||
pass
|
||||
|
||||
def authenticate(self):
|
||||
"""Аутентификация пользователя"""
|
||||
username = self.username_input.text().strip()
|
||||
password = self.password_input.text().strip()
|
||||
|
||||
if not username or not password:
|
||||
QMessageBox.warning(self, "Ошибка", "Заполните все поля")
|
||||
return
|
||||
|
||||
# Блокируем кнопку во время аутентификации
|
||||
self.login_button.setEnabled(False)
|
||||
self.login_button.setText("Проверка...")
|
||||
|
||||
try:
|
||||
# Выполняем аутентификацию через API
|
||||
response = requests.post(
|
||||
"http://localhost:8000/api/v1/auth/login",
|
||||
auth=HTTPBasicAuth(username, password),
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
user_data = response.json()
|
||||
|
||||
# Сохраняем настройки
|
||||
self.save_settings()
|
||||
|
||||
# Сохраняем учетные данные для будущих запросов
|
||||
user_data['auth'] = HTTPBasicAuth(username, password)
|
||||
|
||||
# Отправляем сигнал об успешной авторизации
|
||||
self.login_success.emit(user_data)
|
||||
|
||||
else:
|
||||
QMessageBox.warning(
|
||||
self,
|
||||
"Ошибка авторизации",
|
||||
"Неверное имя пользователя или пароль"
|
||||
)
|
||||
|
||||
except requests.exceptions.ConnectionError:
|
||||
QMessageBox.critical(
|
||||
self,
|
||||
"Ошибка подключения",
|
||||
"Не удалось подключиться к серверу.\n"
|
||||
"Убедитесь, что сервер запущен на localhost:8000"
|
||||
)
|
||||
except requests.exceptions.Timeout:
|
||||
QMessageBox.critical(
|
||||
self,
|
||||
"Ошибка подключения",
|
||||
"Превышено время ожидания ответа от сервера"
|
||||
)
|
||||
except Exception as e:
|
||||
QMessageBox.critical(
|
||||
self,
|
||||
"Ошибка",
|
||||
f"Произошла непредвиденная ошибка:\n{str(e)}"
|
||||
)
|
||||
finally:
|
||||
# Разблокируем кнопку
|
||||
self.login_button.setEnabled(True)
|
||||
self.login_button.setText("Войти в систему")
|
||||
|
||||
def main():
|
||||
"""Точка входа для тестирования окна авторизации"""
|
||||
app = QApplication(sys.argv)
|
||||
window = LoginWindow()
|
||||
window.show()
|
||||
sys.exit(app.exec())
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
574
ressult/gui/main_window
Normal file
574
ressult/gui/main_window
Normal file
|
|
@ -0,0 +1,574 @@
|
|||
# gui/main_window.py
|
||||
"""
|
||||
Главное окно приложения PyQt6 с поддержкой авторизации
|
||||
"""
|
||||
import sys
|
||||
import requests
|
||||
from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
|
||||
QHBoxLayout, QLabel, QPushButton, QListWidget,
|
||||
QListWidgetItem, QMessageBox, QFrame, QStackedWidget,
|
||||
QMenuBar, QMenu, QStatusBar, QToolBar)
|
||||
from PyQt6.QtCore import Qt, pyqtSignal
|
||||
from PyQt6.QtGui import QFont, QPixmap, QIcon, QAction
|
||||
from .partner_form import PartnerForm
|
||||
from .sales_history import SalesHistoryWindow
|
||||
from .material_calculator import MaterialCalculatorWindow
|
||||
|
||||
class PartnerCard(QFrame):
|
||||
"""Карточка партнера для отображения в списке"""
|
||||
partner_clicked = pyqtSignal(dict)
|
||||
|
||||
def __init__(self, partner_data):
|
||||
super().__init__()
|
||||
self.partner_data = partner_data
|
||||
self.setup_ui()
|
||||
|
||||
def setup_ui(self):
|
||||
self.setFrameStyle(QFrame.Shape.StyledPanel)
|
||||
self.setStyleSheet("""
|
||||
PartnerCard {
|
||||
background-color: white;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
margin: 4px;
|
||||
}
|
||||
PartnerCard:hover {
|
||||
background-color: #f5f5f5;
|
||||
border-color: #007acc;
|
||||
}
|
||||
""")
|
||||
|
||||
layout = QVBoxLayout()
|
||||
layout.setContentsMargins(8, 8, 8, 8)
|
||||
layout.setSpacing(4)
|
||||
|
||||
# Заголовок с типом и названием
|
||||
header_layout = QHBoxLayout()
|
||||
header_layout.setSpacing(4)
|
||||
|
||||
type_label = QLabel(f"{self.partner_data.get('partner_type', 'Тип не указан')} |")
|
||||
type_label.setStyleSheet("color: #666; font-weight: bold;")
|
||||
|
||||
name_label = QLabel(self.partner_data['company_name'])
|
||||
name_label.setStyleSheet("font-weight: bold; font-size: 14px;")
|
||||
name_label.setWordWrap(True)
|
||||
|
||||
# Безопасное преобразование рейтинга
|
||||
rating_value = self.partner_data.get('rating', 0)
|
||||
if isinstance(rating_value, float):
|
||||
rating_value = int(rating_value)
|
||||
|
||||
rating_label = QLabel(f"{rating_value}%")
|
||||
rating_label.setStyleSheet("color: #007acc; font-weight: bold;")
|
||||
|
||||
header_layout.addWidget(type_label)
|
||||
header_layout.addWidget(name_label)
|
||||
header_layout.addStretch()
|
||||
header_layout.addWidget(rating_label)
|
||||
|
||||
# Информация о директоре
|
||||
director_label = QLabel(self.partner_data.get('director_name', 'Директор не указан'))
|
||||
director_label.setStyleSheet("color: #444;")
|
||||
|
||||
# Контактная информация
|
||||
phone_label = QLabel(self.partner_data.get('phone', 'Телефон не указан'))
|
||||
phone_label.setStyleSheet("color: #666;")
|
||||
|
||||
layout.addLayout(header_layout)
|
||||
layout.addWidget(director_label)
|
||||
layout.addWidget(phone_label)
|
||||
|
||||
self.setLayout(layout)
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
"""Обработка клика на карточке"""
|
||||
if event.button() == Qt.MouseButton.LeftButton:
|
||||
self.partner_clicked.emit(self.partner_data)
|
||||
|
||||
class MainWindow(QMainWindow):
|
||||
"""Главное окно приложения с поддержкой авторизации"""
|
||||
|
||||
def __init__(self, user_data):
|
||||
super().__init__()
|
||||
self.user_data = user_data
|
||||
self.current_partner = None
|
||||
self.auth = user_data.get('auth')
|
||||
self.setup_ui()
|
||||
self.load_partners()
|
||||
|
||||
def setup_ui(self):
|
||||
"""Настройка интерфейса главного окна"""
|
||||
self.setWindowTitle(f"MasterPol - Система управления партнерами")
|
||||
self.setGeometry(100, 100, 1200, 700)
|
||||
|
||||
# Установка иконки приложения
|
||||
try:
|
||||
self.setWindowIcon(QIcon("resources/icon.png"))
|
||||
except:
|
||||
pass
|
||||
|
||||
# Создание меню
|
||||
self.create_menu()
|
||||
|
||||
# Создание тулбара
|
||||
self.create_toolbar()
|
||||
|
||||
# Создание статусной строки
|
||||
self.create_statusbar()
|
||||
|
||||
# Центральный виджет
|
||||
central_widget = QWidget()
|
||||
self.setCentralWidget(central_widget)
|
||||
|
||||
main_layout = QHBoxLayout()
|
||||
main_layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
# Левая панель - список партнеров
|
||||
left_panel = self.create_partners_panel()
|
||||
main_layout.addWidget(left_panel, 1)
|
||||
|
||||
# Правая панель - детальная информация
|
||||
self.right_panel = self.create_details_panel()
|
||||
main_layout.addWidget(self.right_panel, 2)
|
||||
|
||||
central_widget.setLayout(main_layout)
|
||||
|
||||
def create_menu(self):
|
||||
"""Создание меню приложения"""
|
||||
menubar = self.menuBar()
|
||||
|
||||
# Меню Файл
|
||||
file_menu = menubar.addMenu('Файл')
|
||||
|
||||
refresh_action = QAction('Обновить', self)
|
||||
refresh_action.setShortcut('F5')
|
||||
refresh_action.triggered.connect(self.load_partners)
|
||||
file_menu.addAction(refresh_action)
|
||||
|
||||
file_menu.addSeparator()
|
||||
|
||||
logout_action = QAction('Выход', self)
|
||||
logout_action.setShortcut('Ctrl+Q')
|
||||
logout_action.triggered.connect(self.logout)
|
||||
file_menu.addAction(logout_action)
|
||||
|
||||
# Меню Сервис
|
||||
service_menu = menubar.addMenu('Сервис')
|
||||
|
||||
calc_action = QAction('Калькулятор материалов', self)
|
||||
calc_action.triggered.connect(self.show_material_calculator)
|
||||
service_menu.addAction(calc_action)
|
||||
|
||||
# Меню Справка
|
||||
help_menu = menubar.addMenu('Справка')
|
||||
|
||||
about_action = QAction('О программе', self)
|
||||
about_action.triggered.connect(self.show_about)
|
||||
help_menu.addAction(about_action)
|
||||
|
||||
def create_toolbar(self):
|
||||
"""Создание панели инструментов"""
|
||||
toolbar = QToolBar("Основные инструменты")
|
||||
self.addToolBar(toolbar)
|
||||
|
||||
refresh_action = QAction('Обновить', self)
|
||||
refresh_action.triggered.connect(self.load_partners)
|
||||
toolbar.addAction(refresh_action)
|
||||
|
||||
toolbar.addSeparator()
|
||||
|
||||
add_partner_action = QAction('Добавить партнера', self)
|
||||
add_partner_action.triggered.connect(self.show_add_partner_form)
|
||||
toolbar.addAction(add_partner_action)
|
||||
|
||||
def create_statusbar(self):
|
||||
"""Создание статусной строки"""
|
||||
statusbar = self.statusBar()
|
||||
user_info = f"Пользователь: {self.user_data.get('full_name', 'Неизвестно')}"
|
||||
statusbar.showMessage(user_info)
|
||||
|
||||
def create_partners_panel(self):
|
||||
"""Создание панели списка партнеров"""
|
||||
panel = QWidget()
|
||||
panel.setMaximumWidth(400)
|
||||
layout = QVBoxLayout()
|
||||
layout.setContentsMargins(10, 10, 10, 10)
|
||||
layout.setSpacing(10)
|
||||
|
||||
# Заголовок
|
||||
title = QLabel("Партнеры")
|
||||
title.setFont(QFont("Arial", 16, QFont.Weight.Bold))
|
||||
title.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
title.setStyleSheet("padding: 10px;")
|
||||
layout.addWidget(title)
|
||||
|
||||
# Панель управления
|
||||
control_layout = QHBoxLayout()
|
||||
control_layout.setSpacing(10)
|
||||
|
||||
self.add_button = QPushButton("Добавить партнера")
|
||||
self.add_button.clicked.connect(self.show_add_partner_form)
|
||||
self.add_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #007acc;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #005a9e;
|
||||
}
|
||||
""")
|
||||
|
||||
self.refresh_button = QPushButton("Обновить")
|
||||
self.refresh_button.clicked.connect(self.load_partners)
|
||||
self.refresh_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #6c757d;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #545b62;
|
||||
}
|
||||
""")
|
||||
|
||||
control_layout.addWidget(self.add_button)
|
||||
control_layout.addWidget(self.refresh_button)
|
||||
control_layout.addStretch()
|
||||
|
||||
layout.addLayout(control_layout)
|
||||
|
||||
# Список партнеров
|
||||
self.partners_list = QListWidget()
|
||||
self.partners_list.setStyleSheet("""
|
||||
QListWidget {
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
background-color: white;
|
||||
outline: none;
|
||||
}
|
||||
QListWidget::item {
|
||||
border: none;
|
||||
padding: 0px;
|
||||
}
|
||||
QListWidget::item:selected {
|
||||
background-color: transparent;
|
||||
}
|
||||
""")
|
||||
layout.addWidget(self.partners_list)
|
||||
|
||||
# Кнопка расчета материалов
|
||||
self.calc_button = QPushButton("Калькулятор материалов")
|
||||
self.calc_button.clicked.connect(self.show_material_calculator)
|
||||
self.calc_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #17a2b8;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #138496;
|
||||
}
|
||||
""")
|
||||
layout.addWidget(self.calc_button)
|
||||
|
||||
panel.setLayout(layout)
|
||||
return panel
|
||||
|
||||
def create_details_panel(self):
|
||||
"""Создание панели детальной информации"""
|
||||
panel = QWidget()
|
||||
layout = QVBoxLayout()
|
||||
layout.setContentsMargins(10, 10, 10, 10)
|
||||
layout.setSpacing(10)
|
||||
|
||||
# Заголовок детальной информации
|
||||
self.details_title = QLabel("Выберите партнера")
|
||||
self.details_title.setFont(QFont("Arial", 14, QFont.Weight.Bold))
|
||||
self.details_title.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.details_title.setStyleSheet("padding: 10px;")
|
||||
layout.addWidget(self.details_title)
|
||||
|
||||
# Детальная информация о партнере - создаем пустой frame
|
||||
self.details_frame = QFrame()
|
||||
self.details_frame.setFrameStyle(QFrame.Shape.StyledPanel)
|
||||
self.details_frame.setStyleSheet("""
|
||||
QFrame {
|
||||
background-color: #f9f9f9;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
padding: 15px;
|
||||
}
|
||||
""")
|
||||
self.details_layout = QVBoxLayout()
|
||||
self.details_layout.setSpacing(8)
|
||||
self.details_frame.setLayout(self.details_layout)
|
||||
self.details_frame.hide()
|
||||
|
||||
layout.addWidget(self.details_frame)
|
||||
|
||||
# Кнопки управления выбранным партнером
|
||||
self.control_buttons = QWidget()
|
||||
buttons_layout = QHBoxLayout()
|
||||
buttons_layout.setSpacing(10)
|
||||
|
||||
self.edit_button = QPushButton("Редактировать")
|
||||
self.edit_button.clicked.connect(self.edit_partner)
|
||||
self.edit_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #007acc;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #005a9e;
|
||||
}
|
||||
""")
|
||||
self.edit_button.hide()
|
||||
|
||||
self.sales_button = QPushButton("История продаж")
|
||||
self.sales_button.clicked.connect(self.show_sales_history)
|
||||
self.sales_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #28a745;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #218838;
|
||||
}
|
||||
""")
|
||||
self.sales_button.hide()
|
||||
|
||||
self.discount_button = QPushButton("Расчет скидки")
|
||||
self.discount_button.clicked.connect(self.calculate_discount)
|
||||
self.discount_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #ffc107;
|
||||
color: black;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #e0a800;
|
||||
}
|
||||
""")
|
||||
self.discount_button.hide()
|
||||
|
||||
buttons_layout.addWidget(self.edit_button)
|
||||
buttons_layout.addWidget(self.sales_button)
|
||||
buttons_layout.addWidget(self.discount_button)
|
||||
buttons_layout.addStretch()
|
||||
|
||||
self.control_buttons.setLayout(buttons_layout)
|
||||
layout.addWidget(self.control_buttons)
|
||||
|
||||
# Добавляем растягивающийся элемент в конец
|
||||
layout.addStretch()
|
||||
|
||||
panel.setLayout(layout)
|
||||
return panel
|
||||
|
||||
def load_partners(self):
|
||||
"""Загрузка списка партнеров из API с авторизацией"""
|
||||
try:
|
||||
response = requests.get(
|
||||
"http://localhost:8000/api/v1/partners",
|
||||
auth=self.auth,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
self.partners_list.clear()
|
||||
partners = response.json()
|
||||
|
||||
for partner in partners:
|
||||
item = QListWidgetItem()
|
||||
card = PartnerCard(partner)
|
||||
card.partner_clicked.connect(self.show_partner_details)
|
||||
|
||||
# Устанавливаем фиксированный размер для элемента
|
||||
item.setSizeHint(card.sizeHint())
|
||||
self.partners_list.addItem(item)
|
||||
self.partners_list.setItemWidget(item, card)
|
||||
|
||||
# Сбрасываем выделение
|
||||
self.partners_list.clearSelection()
|
||||
self.current_partner = None
|
||||
self.details_title.setText("Выберите партнера")
|
||||
self.details_frame.hide()
|
||||
self.edit_button.hide()
|
||||
self.sales_button.hide()
|
||||
self.discount_button.hide()
|
||||
|
||||
elif response.status_code == 401:
|
||||
QMessageBox.warning(self, "Ошибка авторизации", "Сессия истекла. Пожалуйста, войдите снова.")
|
||||
self.logout()
|
||||
else:
|
||||
QMessageBox.warning(self, "Ошибка", "Не удалось загрузить партнеров")
|
||||
|
||||
except requests.exceptions.ConnectionError:
|
||||
QMessageBox.critical(self, "Ошибка", "Не удалось подключиться к серверу")
|
||||
except Exception as e:
|
||||
QMessageBox.warning(self, "Ошибка", f"Не удалось загрузить партнеров: {str(e)}")
|
||||
|
||||
def show_partner_details(self, partner_data):
|
||||
"""Отображение детальной информации о партнере"""
|
||||
self.current_partner = partner_data
|
||||
self.details_title.setText(partner_data['company_name'])
|
||||
|
||||
# Создаем новый виджет для деталей вместо очистки layout
|
||||
new_details_frame = QFrame()
|
||||
new_details_frame.setFrameStyle(QFrame.Shape.StyledPanel)
|
||||
new_details_frame.setStyleSheet("""
|
||||
QFrame {
|
||||
background-color: #f9f9f9;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
padding: 15px;
|
||||
}
|
||||
""")
|
||||
new_details_layout = QVBoxLayout()
|
||||
new_details_layout.setSpacing(8)
|
||||
|
||||
# Добавляем новую информацию
|
||||
details = [
|
||||
("Тип:", partner_data.get('partner_type', 'Не указан')),
|
||||
("ИНН:", partner_data.get('inn', 'Не указан')),
|
||||
("Директор:", partner_data.get('director_name', 'Не указан')),
|
||||
("Телефон:", partner_data.get('phone', 'Не указан')),
|
||||
("Email:", partner_data.get('email', 'Не указан')),
|
||||
("Рейтинг:", str(partner_data.get('rating', 0))),
|
||||
("Адрес:", partner_data.get('legal_address', 'Не указан')),
|
||||
("Регионы:", partner_data.get('sales_locations', 'Не указан'))
|
||||
]
|
||||
|
||||
for label, value in details:
|
||||
row_widget = QWidget()
|
||||
row_layout = QHBoxLayout(row_widget)
|
||||
row_layout.setContentsMargins(0, 2, 0, 2)
|
||||
|
||||
label_widget = QLabel(label)
|
||||
label_widget.setStyleSheet("font-weight: bold; min-width: 100px;")
|
||||
label_widget.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop)
|
||||
|
||||
value_widget = QLabel(str(value))
|
||||
value_widget.setWordWrap(True)
|
||||
value_widget.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop)
|
||||
|
||||
row_layout.addWidget(label_widget)
|
||||
row_layout.addWidget(value_widget)
|
||||
row_layout.addStretch()
|
||||
|
||||
new_details_layout.addWidget(row_widget)
|
||||
|
||||
new_details_frame.setLayout(new_details_layout)
|
||||
|
||||
# Заменяем старый details_frame на новый
|
||||
old_frame = self.details_frame
|
||||
layout = self.right_panel.layout()
|
||||
layout.replaceWidget(old_frame, new_details_frame)
|
||||
old_frame.deleteLater()
|
||||
|
||||
self.details_frame = new_details_frame
|
||||
self.details_layout = new_details_layout
|
||||
|
||||
self.details_frame.show()
|
||||
self.edit_button.show()
|
||||
self.sales_button.show()
|
||||
self.discount_button.show()
|
||||
|
||||
def show_add_partner_form(self):
|
||||
"""Открытие формы добавления партнера"""
|
||||
form = PartnerForm(self, auth=self.auth)
|
||||
form.partner_saved.connect(self.load_partners)
|
||||
form.exec()
|
||||
|
||||
def edit_partner(self):
|
||||
"""Редактирование выбранного партнера"""
|
||||
if self.current_partner:
|
||||
form = PartnerForm(self, self.current_partner, auth=self.auth)
|
||||
form.partner_saved.connect(self.load_partners)
|
||||
form.exec()
|
||||
|
||||
def show_sales_history(self):
|
||||
"""Открытие истории продаж партнера"""
|
||||
if self.current_partner:
|
||||
sales_window = SalesHistoryWindow(self.current_partner, self, auth=self.auth)
|
||||
sales_window.exec()
|
||||
|
||||
def calculate_discount(self):
|
||||
"""Расчет скидки для партнера с авторизацией"""
|
||||
if self.current_partner:
|
||||
try:
|
||||
response = requests.get(
|
||||
f"http://localhost:8000/api/v1/partners/{self.current_partner['partner_id']}/discount",
|
||||
auth=self.auth,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
discount_data = response.json()
|
||||
QMessageBox.information(
|
||||
self,
|
||||
"Расчет скидки",
|
||||
f"Партнер: {self.current_partner['company_name']}\n"
|
||||
f"Общие продажи: {discount_data['total_sales']}\n"
|
||||
f"Скидка: {discount_data['discount_percent']}%"
|
||||
)
|
||||
elif response.status_code == 401:
|
||||
QMessageBox.warning(self, "Ошибка авторизации", "Сессия истекла. Пожалуйста, войдите снова.")
|
||||
self.logout()
|
||||
|
||||
except Exception as e:
|
||||
QMessageBox.warning(self, "Ошибка", f"Не удалось рассчитать скидку: {str(e)}")
|
||||
|
||||
def show_material_calculator(self):
|
||||
"""Открытие калькулятора материалов"""
|
||||
calculator = MaterialCalculatorWindow(self, auth=self.auth)
|
||||
calculator.exec()
|
||||
|
||||
def logout(self):
|
||||
"""Выход из системы"""
|
||||
reply = QMessageBox.question(
|
||||
self,
|
||||
"Подтверждение выхода",
|
||||
"Вы уверены, что хотите выйти из системы?",
|
||||
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
|
||||
QMessageBox.StandardButton.No
|
||||
)
|
||||
|
||||
if reply == QMessageBox.StandardButton.Yes:
|
||||
self.close()
|
||||
# Здесь можно добавить вызов окна авторизации
|
||||
# или перезапуск приложения
|
||||
|
||||
def show_about(self):
|
||||
"""Показать информацию о программе"""
|
||||
QMessageBox.about(
|
||||
self,
|
||||
"О программе MasterPol",
|
||||
"MasterPol - Система управления партнерами\n\n"
|
||||
"Версия: 1.0.0\n"
|
||||
"Разработчик: Команда MasterPol\n\n"
|
||||
"Система предназначена для управления партнерами,\n"
|
||||
"учета продаж и расчета бизнес-показателей."
|
||||
)
|
||||
574
ressult/gui/main_window.py
Normal file
574
ressult/gui/main_window.py
Normal file
|
|
@ -0,0 +1,574 @@
|
|||
# gui/main_window.py
|
||||
"""
|
||||
Главное окно приложения PyQt6 с поддержкой авторизации
|
||||
"""
|
||||
import sys
|
||||
import requests
|
||||
from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
|
||||
QHBoxLayout, QLabel, QPushButton, QListWidget,
|
||||
QListWidgetItem, QMessageBox, QFrame, QStackedWidget,
|
||||
QMenuBar, QMenu, QStatusBar, QToolBar)
|
||||
from PyQt6.QtCore import Qt, pyqtSignal
|
||||
from PyQt6.QtGui import QFont, QPixmap, QIcon, QAction
|
||||
from .partner_form import PartnerForm
|
||||
from .sales_history import SalesHistoryWindow
|
||||
from .material_calculator import MaterialCalculatorWindow
|
||||
|
||||
class PartnerCard(QFrame):
|
||||
"""Карточка партнера для отображения в списке"""
|
||||
partner_clicked = pyqtSignal(dict)
|
||||
|
||||
def __init__(self, partner_data):
|
||||
super().__init__()
|
||||
self.partner_data = partner_data
|
||||
self.setup_ui()
|
||||
|
||||
def setup_ui(self):
|
||||
self.setFrameStyle(QFrame.Shape.StyledPanel)
|
||||
self.setStyleSheet("""
|
||||
PartnerCard {
|
||||
background-color: white;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
margin: 4px;
|
||||
}
|
||||
PartnerCard:hover {
|
||||
background-color: #f5f5f5;
|
||||
border-color: #007acc;
|
||||
}
|
||||
""")
|
||||
|
||||
layout = QVBoxLayout()
|
||||
layout.setContentsMargins(8, 8, 8, 8)
|
||||
layout.setSpacing(4)
|
||||
|
||||
# Заголовок с типом и названием
|
||||
header_layout = QHBoxLayout()
|
||||
header_layout.setSpacing(4)
|
||||
|
||||
type_label = QLabel(f"{self.partner_data.get('partner_type', 'Тип не указан')} |")
|
||||
type_label.setStyleSheet("color: #666; font-weight: bold;")
|
||||
|
||||
name_label = QLabel(self.partner_data['company_name'])
|
||||
name_label.setStyleSheet("font-weight: bold; font-size: 14px;")
|
||||
name_label.setWordWrap(True)
|
||||
|
||||
# Безопасное преобразование рейтинга
|
||||
rating_value = self.partner_data.get('rating', 0)
|
||||
if isinstance(rating_value, float):
|
||||
rating_value = int(rating_value)
|
||||
|
||||
rating_label = QLabel(f"{rating_value}%")
|
||||
rating_label.setStyleSheet("color: #007acc; font-weight: bold;")
|
||||
|
||||
header_layout.addWidget(type_label)
|
||||
header_layout.addWidget(name_label)
|
||||
header_layout.addStretch()
|
||||
header_layout.addWidget(rating_label)
|
||||
|
||||
# Информация о директоре
|
||||
director_label = QLabel(self.partner_data.get('director_name', 'Директор не указан'))
|
||||
director_label.setStyleSheet("color: #444;")
|
||||
|
||||
# Контактная информация
|
||||
phone_label = QLabel(self.partner_data.get('phone', 'Телефон не указан'))
|
||||
phone_label.setStyleSheet("color: #666;")
|
||||
|
||||
layout.addLayout(header_layout)
|
||||
layout.addWidget(director_label)
|
||||
layout.addWidget(phone_label)
|
||||
|
||||
self.setLayout(layout)
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
"""Обработка клика на карточке"""
|
||||
if event.button() == Qt.MouseButton.LeftButton:
|
||||
self.partner_clicked.emit(self.partner_data)
|
||||
|
||||
class MainWindow(QMainWindow):
|
||||
"""Главное окно приложения с поддержкой авторизации"""
|
||||
|
||||
def __init__(self, user_data):
|
||||
super().__init__()
|
||||
self.user_data = user_data
|
||||
self.current_partner = None
|
||||
self.auth = user_data.get('auth')
|
||||
self.setup_ui()
|
||||
self.load_partners()
|
||||
|
||||
def setup_ui(self):
|
||||
"""Настройка интерфейса главного окна"""
|
||||
self.setWindowTitle(f"MasterPol - Система управления партнерами")
|
||||
self.setGeometry(100, 100, 1200, 700)
|
||||
|
||||
# Установка иконки приложения
|
||||
try:
|
||||
self.setWindowIcon(QIcon("resources/icon.png"))
|
||||
except:
|
||||
pass
|
||||
|
||||
# Создание меню
|
||||
self.create_menu()
|
||||
|
||||
# Создание тулбара
|
||||
self.create_toolbar()
|
||||
|
||||
# Создание статусной строки
|
||||
self.create_statusbar()
|
||||
|
||||
# Центральный виджет
|
||||
central_widget = QWidget()
|
||||
self.setCentralWidget(central_widget)
|
||||
|
||||
main_layout = QHBoxLayout()
|
||||
main_layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
# Левая панель - список партнеров
|
||||
left_panel = self.create_partners_panel()
|
||||
main_layout.addWidget(left_panel, 1)
|
||||
|
||||
# Правая панель - детальная информация
|
||||
self.right_panel = self.create_details_panel()
|
||||
main_layout.addWidget(self.right_panel, 2)
|
||||
|
||||
central_widget.setLayout(main_layout)
|
||||
|
||||
def create_menu(self):
|
||||
"""Создание меню приложения"""
|
||||
menubar = self.menuBar()
|
||||
|
||||
# Меню Файл
|
||||
file_menu = menubar.addMenu('Файл')
|
||||
|
||||
refresh_action = QAction('Обновить', self)
|
||||
refresh_action.setShortcut('F5')
|
||||
refresh_action.triggered.connect(self.load_partners)
|
||||
file_menu.addAction(refresh_action)
|
||||
|
||||
file_menu.addSeparator()
|
||||
|
||||
logout_action = QAction('Выход', self)
|
||||
logout_action.setShortcut('Ctrl+Q')
|
||||
logout_action.triggered.connect(self.logout)
|
||||
file_menu.addAction(logout_action)
|
||||
|
||||
# Меню Сервис
|
||||
service_menu = menubar.addMenu('Сервис')
|
||||
|
||||
calc_action = QAction('Калькулятор материалов', self)
|
||||
calc_action.triggered.connect(self.show_material_calculator)
|
||||
service_menu.addAction(calc_action)
|
||||
|
||||
# Меню Справка
|
||||
help_menu = menubar.addMenu('Справка')
|
||||
|
||||
about_action = QAction('О программе', self)
|
||||
about_action.triggered.connect(self.show_about)
|
||||
help_menu.addAction(about_action)
|
||||
|
||||
def create_toolbar(self):
|
||||
"""Создание панели инструментов"""
|
||||
toolbar = QToolBar("Основные инструменты")
|
||||
self.addToolBar(toolbar)
|
||||
|
||||
refresh_action = QAction('Обновить', self)
|
||||
refresh_action.triggered.connect(self.load_partners)
|
||||
toolbar.addAction(refresh_action)
|
||||
|
||||
toolbar.addSeparator()
|
||||
|
||||
add_partner_action = QAction('Добавить партнера', self)
|
||||
add_partner_action.triggered.connect(self.show_add_partner_form)
|
||||
toolbar.addAction(add_partner_action)
|
||||
|
||||
def create_statusbar(self):
|
||||
"""Создание статусной строки"""
|
||||
statusbar = self.statusBar()
|
||||
user_info = f"Пользователь: {self.user_data.get('full_name', 'Неизвестно')}"
|
||||
statusbar.showMessage(user_info)
|
||||
|
||||
def create_partners_panel(self):
|
||||
"""Создание панели списка партнеров"""
|
||||
panel = QWidget()
|
||||
panel.setMaximumWidth(400)
|
||||
layout = QVBoxLayout()
|
||||
layout.setContentsMargins(10, 10, 10, 10)
|
||||
layout.setSpacing(10)
|
||||
|
||||
# Заголовок
|
||||
title = QLabel("Партнеры")
|
||||
title.setFont(QFont("Arial", 16, QFont.Weight.Bold))
|
||||
title.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
title.setStyleSheet("padding: 10px;")
|
||||
layout.addWidget(title)
|
||||
|
||||
# Панель управления
|
||||
control_layout = QHBoxLayout()
|
||||
control_layout.setSpacing(10)
|
||||
|
||||
self.add_button = QPushButton("Добавить партнера")
|
||||
self.add_button.clicked.connect(self.show_add_partner_form)
|
||||
self.add_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #007acc;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #005a9e;
|
||||
}
|
||||
""")
|
||||
|
||||
self.refresh_button = QPushButton("Обновить")
|
||||
self.refresh_button.clicked.connect(self.load_partners)
|
||||
self.refresh_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #6c757d;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #545b62;
|
||||
}
|
||||
""")
|
||||
|
||||
control_layout.addWidget(self.add_button)
|
||||
control_layout.addWidget(self.refresh_button)
|
||||
control_layout.addStretch()
|
||||
|
||||
layout.addLayout(control_layout)
|
||||
|
||||
# Список партнеров
|
||||
self.partners_list = QListWidget()
|
||||
self.partners_list.setStyleSheet("""
|
||||
QListWidget {
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
background-color: white;
|
||||
outline: none;
|
||||
}
|
||||
QListWidget::item {
|
||||
border: none;
|
||||
padding: 0px;
|
||||
}
|
||||
QListWidget::item:selected {
|
||||
background-color: transparent;
|
||||
}
|
||||
""")
|
||||
layout.addWidget(self.partners_list)
|
||||
|
||||
# Кнопка расчета материалов
|
||||
self.calc_button = QPushButton("Калькулятор материалов")
|
||||
self.calc_button.clicked.connect(self.show_material_calculator)
|
||||
self.calc_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #17a2b8;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #138496;
|
||||
}
|
||||
""")
|
||||
layout.addWidget(self.calc_button)
|
||||
|
||||
panel.setLayout(layout)
|
||||
return panel
|
||||
|
||||
def create_details_panel(self):
|
||||
"""Создание панели детальной информации"""
|
||||
panel = QWidget()
|
||||
layout = QVBoxLayout()
|
||||
layout.setContentsMargins(10, 10, 10, 10)
|
||||
layout.setSpacing(10)
|
||||
|
||||
# Заголовок детальной информации
|
||||
self.details_title = QLabel("Выберите партнера")
|
||||
self.details_title.setFont(QFont("Arial", 14, QFont.Weight.Bold))
|
||||
self.details_title.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.details_title.setStyleSheet("padding: 10px;")
|
||||
layout.addWidget(self.details_title)
|
||||
|
||||
# Детальная информация о партнере - создаем пустой frame
|
||||
self.details_frame = QFrame()
|
||||
self.details_frame.setFrameStyle(QFrame.Shape.StyledPanel)
|
||||
self.details_frame.setStyleSheet("""
|
||||
QFrame {
|
||||
background-color: #f9f9f9;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
padding: 15px;
|
||||
}
|
||||
""")
|
||||
self.details_layout = QVBoxLayout()
|
||||
self.details_layout.setSpacing(8)
|
||||
self.details_frame.setLayout(self.details_layout)
|
||||
self.details_frame.hide()
|
||||
|
||||
layout.addWidget(self.details_frame)
|
||||
|
||||
# Кнопки управления выбранным партнером
|
||||
self.control_buttons = QWidget()
|
||||
buttons_layout = QHBoxLayout()
|
||||
buttons_layout.setSpacing(10)
|
||||
|
||||
self.edit_button = QPushButton("Редактировать")
|
||||
self.edit_button.clicked.connect(self.edit_partner)
|
||||
self.edit_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #007acc;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #005a9e;
|
||||
}
|
||||
""")
|
||||
self.edit_button.hide()
|
||||
|
||||
self.sales_button = QPushButton("История продаж")
|
||||
self.sales_button.clicked.connect(self.show_sales_history)
|
||||
self.sales_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #28a745;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #218838;
|
||||
}
|
||||
""")
|
||||
self.sales_button.hide()
|
||||
|
||||
self.discount_button = QPushButton("Расчет скидки")
|
||||
self.discount_button.clicked.connect(self.calculate_discount)
|
||||
self.discount_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #ffc107;
|
||||
color: black;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #e0a800;
|
||||
}
|
||||
""")
|
||||
self.discount_button.hide()
|
||||
|
||||
buttons_layout.addWidget(self.edit_button)
|
||||
buttons_layout.addWidget(self.sales_button)
|
||||
buttons_layout.addWidget(self.discount_button)
|
||||
buttons_layout.addStretch()
|
||||
|
||||
self.control_buttons.setLayout(buttons_layout)
|
||||
layout.addWidget(self.control_buttons)
|
||||
|
||||
# Добавляем растягивающийся элемент в конец
|
||||
layout.addStretch()
|
||||
|
||||
panel.setLayout(layout)
|
||||
return panel
|
||||
|
||||
def load_partners(self):
|
||||
"""Загрузка списка партнеров из API с авторизацией"""
|
||||
try:
|
||||
response = requests.get(
|
||||
"http://localhost:8000/api/v1/partners",
|
||||
auth=self.auth,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
self.partners_list.clear()
|
||||
partners = response.json()
|
||||
|
||||
for partner in partners:
|
||||
item = QListWidgetItem()
|
||||
card = PartnerCard(partner)
|
||||
card.partner_clicked.connect(self.show_partner_details)
|
||||
|
||||
# Устанавливаем фиксированный размер для элемента
|
||||
item.setSizeHint(card.sizeHint())
|
||||
self.partners_list.addItem(item)
|
||||
self.partners_list.setItemWidget(item, card)
|
||||
|
||||
# Сбрасываем выделение
|
||||
self.partners_list.clearSelection()
|
||||
self.current_partner = None
|
||||
self.details_title.setText("Выберите партнера")
|
||||
self.details_frame.hide()
|
||||
self.edit_button.hide()
|
||||
self.sales_button.hide()
|
||||
self.discount_button.hide()
|
||||
|
||||
elif response.status_code == 401:
|
||||
QMessageBox.warning(self, "Ошибка авторизации", "Сессия истекла. Пожалуйста, войдите снова.")
|
||||
self.logout()
|
||||
else:
|
||||
QMessageBox.warning(self, "Ошибка", "Не удалось загрузить партнеров")
|
||||
|
||||
except requests.exceptions.ConnectionError:
|
||||
QMessageBox.critical(self, "Ошибка", "Не удалось подключиться к серверу")
|
||||
except Exception as e:
|
||||
QMessageBox.warning(self, "Ошибка", f"Не удалось загрузить партнеров: {str(e)}")
|
||||
|
||||
def show_partner_details(self, partner_data):
|
||||
"""Отображение детальной информации о партнере"""
|
||||
self.current_partner = partner_data
|
||||
self.details_title.setText(partner_data['company_name'])
|
||||
|
||||
# Создаем новый виджет для деталей вместо очистки layout
|
||||
new_details_frame = QFrame()
|
||||
new_details_frame.setFrameStyle(QFrame.Shape.StyledPanel)
|
||||
new_details_frame.setStyleSheet("""
|
||||
QFrame {
|
||||
background-color: #f9f9f9;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
padding: 15px;
|
||||
}
|
||||
""")
|
||||
new_details_layout = QVBoxLayout()
|
||||
new_details_layout.setSpacing(8)
|
||||
|
||||
# Добавляем новую информацию
|
||||
details = [
|
||||
("Тип:", partner_data.get('partner_type', 'Не указан')),
|
||||
("ИНН:", partner_data.get('inn', 'Не указан')),
|
||||
("Директор:", partner_data.get('director_name', 'Не указан')),
|
||||
("Телефон:", partner_data.get('phone', 'Не указан')),
|
||||
("Email:", partner_data.get('email', 'Не указан')),
|
||||
("Рейтинг:", str(partner_data.get('rating', 0))),
|
||||
("Адрес:", partner_data.get('legal_address', 'Не указан')),
|
||||
("Регионы:", partner_data.get('sales_locations', 'Не указан'))
|
||||
]
|
||||
|
||||
for label, value in details:
|
||||
row_widget = QWidget()
|
||||
row_layout = QHBoxLayout(row_widget)
|
||||
row_layout.setContentsMargins(0, 2, 0, 2)
|
||||
|
||||
label_widget = QLabel(label)
|
||||
label_widget.setStyleSheet("font-weight: bold; min-width: 100px;")
|
||||
label_widget.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop)
|
||||
|
||||
value_widget = QLabel(str(value))
|
||||
value_widget.setWordWrap(True)
|
||||
value_widget.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop)
|
||||
|
||||
row_layout.addWidget(label_widget)
|
||||
row_layout.addWidget(value_widget)
|
||||
row_layout.addStretch()
|
||||
|
||||
new_details_layout.addWidget(row_widget)
|
||||
|
||||
new_details_frame.setLayout(new_details_layout)
|
||||
|
||||
# Заменяем старый details_frame на новый
|
||||
old_frame = self.details_frame
|
||||
layout = self.right_panel.layout()
|
||||
layout.replaceWidget(old_frame, new_details_frame)
|
||||
old_frame.deleteLater()
|
||||
|
||||
self.details_frame = new_details_frame
|
||||
self.details_layout = new_details_layout
|
||||
|
||||
self.details_frame.show()
|
||||
self.edit_button.show()
|
||||
self.sales_button.show()
|
||||
self.discount_button.show()
|
||||
|
||||
def show_add_partner_form(self):
|
||||
"""Открытие формы добавления партнера"""
|
||||
form = PartnerForm(self, auth=self.auth)
|
||||
form.partner_saved.connect(self.load_partners)
|
||||
form.exec()
|
||||
|
||||
def edit_partner(self):
|
||||
"""Редактирование выбранного партнера"""
|
||||
if self.current_partner:
|
||||
form = PartnerForm(self, self.current_partner, auth=self.auth)
|
||||
form.partner_saved.connect(self.load_partners)
|
||||
form.exec()
|
||||
|
||||
def show_sales_history(self):
|
||||
"""Открытие истории продаж партнера"""
|
||||
if self.current_partner:
|
||||
sales_window = SalesHistoryWindow(self.current_partner, self, auth=self.auth)
|
||||
sales_window.exec()
|
||||
|
||||
def calculate_discount(self):
|
||||
"""Расчет скидки для партнера с авторизацией"""
|
||||
if self.current_partner:
|
||||
try:
|
||||
response = requests.get(
|
||||
f"http://localhost:8000/api/v1/partners/{self.current_partner['partner_id']}/discount",
|
||||
auth=self.auth,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
discount_data = response.json()
|
||||
QMessageBox.information(
|
||||
self,
|
||||
"Расчет скидки",
|
||||
f"Партнер: {self.current_partner['company_name']}\n"
|
||||
f"Общие продажи: {discount_data['total_sales']}\n"
|
||||
f"Скидка: {discount_data['discount_percent']}%"
|
||||
)
|
||||
elif response.status_code == 401:
|
||||
QMessageBox.warning(self, "Ошибка авторизации", "Сессия истекла. Пожалуйста, войдите снова.")
|
||||
self.logout()
|
||||
|
||||
except Exception as e:
|
||||
QMessageBox.warning(self, "Ошибка", f"Не удалось рассчитать скидку: {str(e)}")
|
||||
|
||||
def show_material_calculator(self):
|
||||
"""Открытие калькулятора материалов"""
|
||||
calculator = MaterialCalculatorWindow(self, auth=self.auth)
|
||||
calculator.exec()
|
||||
|
||||
def logout(self):
|
||||
"""Выход из системы"""
|
||||
reply = QMessageBox.question(
|
||||
self,
|
||||
"Подтверждение выхода",
|
||||
"Вы уверены, что хотите выйти из системы?",
|
||||
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
|
||||
QMessageBox.StandardButton.No
|
||||
)
|
||||
|
||||
if reply == QMessageBox.StandardButton.Yes:
|
||||
self.close()
|
||||
# Здесь можно добавить вызов окна авторизации
|
||||
# или перезапуск приложения
|
||||
|
||||
def show_about(self):
|
||||
"""Показать информацию о программе"""
|
||||
QMessageBox.about(
|
||||
self,
|
||||
"О программе MasterPol",
|
||||
"MasterPol - Система управления партнерами\n\n"
|
||||
"Версия: 1.0.0\n"
|
||||
"Разработчик: Команда MasterPol\n\n"
|
||||
"Система предназначена для управления партнерами,\n"
|
||||
"учета продаж и расчета бизнес-показателей."
|
||||
)
|
||||
616
ressult/gui/main_window.py.bak
Normal file
616
ressult/gui/main_window.py.bak
Normal file
|
|
@ -0,0 +1,616 @@
|
|||
# gui/main_wind/w.py
|
||||
"""
|
||||
Главное окно приложения PyQt6 с поддержкой авторизации
|
||||
"""
|
||||
import sys
|
||||
import requests
|
||||
from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
|
||||
QHBoxLayout, QLabel, QPushButton, QListWidget,
|
||||
QListWidgetItem, QMessageBox, QFrame, QStackedWidget,
|
||||
QMenuBar, QMenu, QStatusBar, QToolBar)
|
||||
from PyQt6.QtCore import Qt, pyqtSignal
|
||||
from PyQt6.QtGui import QFont, QPixmap, QIcon, QAction
|
||||
from .partner_form import PartnerForm
|
||||
from .sales_history import SalesHistoryWindow
|
||||
from .material_calculator import MaterialCalculatorWindow
|
||||
|
||||
class PartnerCard(QFrame):
|
||||
"""Карточка партнера для отображения в списке"""
|
||||
partner_clicked = pyqtSignal(dict)
|
||||
|
||||
def __init__(self, partner_data):
|
||||
super().__init__()
|
||||
self.partner_data = partner_data
|
||||
self.setup_ui()
|
||||
|
||||
def setup_ui(self):
|
||||
self.setFrameStyle(QFrame.Shape.StyledPanel)
|
||||
self.setStyleSheet("""
|
||||
PartnerCard {
|
||||
background-color: white;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
margin: 4px;
|
||||
}
|
||||
PartnerCard:hover {
|
||||
background-color: #f5f5f5;
|
||||
border-color: #007acc;
|
||||
}
|
||||
""")
|
||||
|
||||
layout = QVBoxLayout()
|
||||
layout.setContentsMargins(8, 8, 8, 8)
|
||||
layout.setSpacing(4)
|
||||
|
||||
# Заголовок с типом и названием
|
||||
header_layout = QHBoxLayout()
|
||||
header_layout.setSpacing(4)
|
||||
|
||||
type_label = QLabel(f"{self.partner_data.get('partner_type', 'Тип не указан')} |")
|
||||
type_label.setStyleSheet("color: #666; font-weight: bold;")
|
||||
|
||||
name_label = QLabel(self.partner_data['company_name'])
|
||||
name_label.setStyleSheet("font-weight: bold; font-size: 14px;")
|
||||
name_label.setWordWrap(True)
|
||||
|
||||
# Безопасное преобразование рейтинга
|
||||
rating_value = self.partner_data.get('rating', 0)
|
||||
if isinstance(rating_value, float):
|
||||
rating_value = int(rating_value)
|
||||
|
||||
rating_label = QLabel(f"{rating_value}%")
|
||||
rating_label.setStyleSheet("color: #007acc; font-weight: bold;")
|
||||
|
||||
header_layout.addWidget(type_label)
|
||||
header_layout.addWidget(name_label)
|
||||
header_layout.addStretch()
|
||||
header_layout.addWidget(rating_label)
|
||||
|
||||
# Информация о директоре
|
||||
QLabel(self.partner_data.get('director_name', 'Директор не указан'))
|
||||
director_label.setStyleSheet("color: #444;")
|
||||
|
||||
# Контактная информация
|
||||
phone_label = QLabel(self.partner_data.get('phone', 'Телефон не указан'))
|
||||
phone_label.setStyleSheet("color: #666;")
|
||||
|
||||
layout.addLayout(header_layout)
|
||||
layout.addWidget(director_label)
|
||||
layout.addWidget(phone_label)
|
||||
|
||||
self.setLayout(layout)
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
"""Обработка клика на карточке"""
|
||||
if event.button() == Qt.MouseButton.LeftButton:
|
||||
self.partner_clicked.emit(self.partner_data)
|
||||
|
||||
class MainWindow(QMainWindow):
|
||||
"""Главное окно приложения с поддержкой авторизации"""
|
||||
|
||||
def __init__(self, user_data):
|
||||
super().__init__()
|
||||
self.user_data = user_data
|
||||
self.current_partner = None
|
||||
self.orders_panel = None
|
||||
self.auth = user_data.get('auth')
|
||||
self.setup_ui()
|
||||
self.load_partners()
|
||||
|
||||
def setup_ui(self):
|
||||
"""Настройка интерфейса главного окна"""
|
||||
self.setWindowTitle(f"MasterPol - Система управления партнерами")
|
||||
self.setGeometry(100, 100, 1200, 700)
|
||||
|
||||
# Установка иконки приложения
|
||||
try:
|
||||
self.setWindowIcon(QIcon("resources/icon.png"))
|
||||
except:
|
||||
pass
|
||||
|
||||
# Создание меню
|
||||
self.create_menu()
|
||||
|
||||
# Создание тулбара
|
||||
self.create_toolbar()
|
||||
|
||||
# Создание статусной строки
|
||||
self.create_statusbar()
|
||||
|
||||
# Центральный виджет
|
||||
central_widget = QWidget()
|
||||
self.setCentralWidget(central_widget)
|
||||
|
||||
main_layout = QHBoxLayout()
|
||||
main_layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
# Левая панель - список партнеров
|
||||
left_panel = self.create_partners_panel()
|
||||
main_layout.addWidget(left_panel, 1)
|
||||
|
||||
# Правая панель - детальная информация
|
||||
self.right_panel = self.create_details_panel()
|
||||
main_layout.addWidget(self.right_panel, 2)
|
||||
|
||||
central_widget.setLayout(main_layout)
|
||||
|
||||
def create_menu(self):
|
||||
"""Создание меню приложения"""
|
||||
menubar = self.menuBar()
|
||||
|
||||
# Меню Файл
|
||||
file_menu = menubar.addMenu('Файл')
|
||||
|
||||
refresh_action = QAction('Обновить', self)
|
||||
refresh_action.setShortcut('F5')
|
||||
refresh_action.triggered.connect(self.load_partners)
|
||||
file_menu.addAction(refresh_action)
|
||||
|
||||
file_menu.addSeparator()
|
||||
|
||||
logout_action = QAction('Выход', self)
|
||||
logout_action.setShortcut('Ctrl+Q')
|
||||
logout_action.triggered.connect(self.logout)
|
||||
file_menu.addAction(logout_action)
|
||||
|
||||
# Меню Сервис
|
||||
service_menu = menubar.addMenu('Сервис')
|
||||
|
||||
calc_action = QAction('Калькулятор материалов', self)
|
||||
calc_action.triggered.connect(self.show_material_calculator)
|
||||
service_menu.addAction(calc_action)
|
||||
|
||||
# Меню Справка
|
||||
help_menu = menubar.addMenu('Справка')
|
||||
|
||||
about_action = QAction('О программе', self)
|
||||
about_action.triggered.connect(self.show_about)
|
||||
help_menu.addAction(about_action)
|
||||
|
||||
def create_toolbar(self):
|
||||
"""Создание панели инструментов"""
|
||||
toolbar = QToolBar("Основные инструменты")
|
||||
self.addToolBar(toolbar)
|
||||
|
||||
refresh_action = QAction('Обновить', self)
|
||||
refresh_action.triggered.connect(self.load_partners)
|
||||
toolbar.addAction(refresh_action)
|
||||
|
||||
toolbar.addSeparator()
|
||||
|
||||
add_partner_action = QAction('Добавить партнера', self)
|
||||
add_partner_action.triggered.connect(self.show_add_partner_form)
|
||||
toolbar.addAction(add_partner_action)
|
||||
|
||||
def create_statusbar(self):
|
||||
"""Создание статусной строки"""
|
||||
statusbar = self.statusBar()
|
||||
user_info = f"Пользователь: {self.user_data.get('full_name', 'Неизвестно')}"
|
||||
statusbar.showMessage(user_info)
|
||||
|
||||
def create_partners_panel(self):
|
||||
"""Создание панели списка партнеров"""
|
||||
panel = QWidget()
|
||||
panel.setMaximumWidth(400)
|
||||
layout = QVBoxLayout()
|
||||
layout.setContentsMargins(10, 10, 10, 10)
|
||||
layout.setSpacing(10)
|
||||
|
||||
# Заголовок
|
||||
title = QLabel("Партнеры")
|
||||
title.setFont(QFont("Arial", 16, QFont.Weight.Bold))
|
||||
title.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
title.setStyleSheet("padding: 10px;")
|
||||
layout.addWidget(title)
|
||||
|
||||
# Панель управления
|
||||
control_layout = QHBoxLayout()
|
||||
control_layout.setSpacing(10)
|
||||
|
||||
self.add_button = QPushButton("Добавить партнера")
|
||||
self.add_button.clicked.connect(self.show_add_partner_form)
|
||||
self.add_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #007acc;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #005a9e;
|
||||
}
|
||||
""")
|
||||
|
||||
self.refresh_button = QPushButton("Обновить")
|
||||
self.refresh_button.clicked.connect(self.load_partners)
|
||||
self.refresh_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #6c757d;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #545b62;
|
||||
}
|
||||
""")
|
||||
|
||||
control_layout.addWidget(self.add_button)
|
||||
control_layout.addWidget(self.refresh_button)
|
||||
control_layout.addStretch()
|
||||
|
||||
layout.addLayout(control_layout)
|
||||
|
||||
# Список партнеров
|
||||
self.partners_list = QListWidget()
|
||||
self.partners_list.setStyleSheet("""
|
||||
QListWidget {
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
background-color: white;
|
||||
outline: none;
|
||||
}
|
||||
QListWidget::item {
|
||||
border: none;
|
||||
padding: 0px;
|
||||
}
|
||||
QListWidget::item:selected {
|
||||
background-color: transparent;
|
||||
}
|
||||
""")
|
||||
layout.addWidget(self.partners_list)
|
||||
|
||||
# Кнопка расчета материалов
|
||||
self.calc_button = QPushButton("Калькулятор материалов")
|
||||
self.calc_button.clicked.connect(self.show_material_calculator)
|
||||
self.calc_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #17a2b8;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #138496;
|
||||
}
|
||||
""")
|
||||
layout.addWidget(self.calc_button)
|
||||
|
||||
panel.setLayout(layout)
|
||||
return panel
|
||||
|
||||
def create_details_panel(self):
|
||||
"""Создание панели детальной информации"""
|
||||
panel = QWidget()
|
||||
layout = QVBoxLayout()
|
||||
layout.setContentsMargins(10, 10, 10, 10)
|
||||
layout.setSpacing(10)
|
||||
|
||||
# Заголовок детальной информации
|
||||
self.details_title = QLabel("Выберите партнера")
|
||||
self.details_title.setFont(QFont("Arial", 14, QFont.Weight.Bold))
|
||||
self.details_title.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.details_title.setStyleSheet("padding: 10px;")
|
||||
layout.addWidget(self.details_title)
|
||||
|
||||
# Детальная информация о партнере - создаем пустой frame
|
||||
self.details_frame = QFrame()
|
||||
self.details_frame.setFrameStyle(QFrame.Shape.StyledPanel)
|
||||
self.details_frame.setStyleSheet("""
|
||||
QFrame {
|
||||
background-color: #f9f9f9;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
padding: 15px;
|
||||
}
|
||||
""")
|
||||
self.details_layout = QVBoxLayout()
|
||||
self.details_layout.setSpacing(8)
|
||||
self.details_frame.setLayout(self.details_layout)
|
||||
self.details_frame.hide()
|
||||
|
||||
layout.addWidget(self.details_frame)
|
||||
|
||||
# Кнопки управления выбранным партнером
|
||||
self.control_buttons = QWidget()
|
||||
buttons_layout = QHBoxLayout()
|
||||
buttons_layout.setSpacing(10)
|
||||
|
||||
self.edit_button = QPushButton("Редактировать")
|
||||
self.edit_button.clicked.connect(self.edit_partner)
|
||||
self.edit_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #007acc;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #005a9e;
|
||||
}
|
||||
""")
|
||||
self.edit_button.hide()
|
||||
|
||||
self.sales_button = QPushButton("История продаж")
|
||||
self.sales_button.clicked.connect(self.show_sales_history)
|
||||
self.sales_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #28a745;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #218838;
|
||||
}
|
||||
""")
|
||||
self.sales_button.hide()
|
||||
|
||||
self.discount_button = QPushButton("Расчет скидки")
|
||||
self.discount_button.clicked.connect(self.calculate_discount)
|
||||
self.discount_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #ffc107;
|
||||
color: black;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #e0a800;
|
||||
}
|
||||
""")
|
||||
self.discount_button.hide()
|
||||
|
||||
buttons_layout.addWidget(self.edit_button)
|
||||
buttons_layout.addWidget(self.sales_button)
|
||||
buttons_layout.addWidget(self.discount_button)
|
||||
buttons_layout.addStretch()
|
||||
|
||||
self.control_buttons.setLayout(buttons_layout)
|
||||
layout.addWidget(self.control_buttons)
|
||||
|
||||
# Добавляем растягивающийся элемент в конец
|
||||
layout.addStretch()
|
||||
|
||||
panel.setLayout(layout)
|
||||
return panel
|
||||
|
||||
def load_partners(self):
|
||||
"""Загрузка списка партнеров из API с авторизацией"""
|
||||
try:
|
||||
response = requests.get(
|
||||
"http://localhost:8000/api/v1/partners",
|
||||
auth=self.auth,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
self.partners_list.clear()
|
||||
partners = response.json()
|
||||
|
||||
for partner in partners:
|
||||
item = QListWidgetItem()
|
||||
card = PartnerCard(partner)
|
||||
card.partner_clicked.connect(self.show_partner_details)
|
||||
|
||||
# Устанавливаем фиксированный размер для элемента
|
||||
item.setSizeHint(card.sizeHint())
|
||||
self.partners_list.addItem(item)
|
||||
self.partners_list.setItemWidget(item, card)
|
||||
|
||||
# Сбрасываем выделение
|
||||
self.partners_list.clearSelection()
|
||||
self.current_partner = None
|
||||
self.details_title.setText("Выберите партнера")
|
||||
self.details_frame.hide()
|
||||
self.edit_button.hide()
|
||||
self.sales_button.hide()
|
||||
self.discount_button.hide()
|
||||
|
||||
elif response.status_code == 401:
|
||||
QMessageBox.warning(self, "Ошибка авторизации", "Сессия истекла. Пожалуйста, войдите снова.")
|
||||
self.logout()
|
||||
else:
|
||||
QMessageBox.warning(self, "Ошибка", "Не удалось загрузить партнеров")
|
||||
|
||||
except requests.exceptions.ConnectionError:
|
||||
QMessageBox.critical(self, "Ошибка", "Не удалось подключиться к серверу")
|
||||
except Exception as e:
|
||||
QMessageBox.warning(self, "Ошибка", f"Не удалось загрузить партнеров: {str(e)}")
|
||||
|
||||
def show_partner_details(self, partner_data):
|
||||
"""Отображение детальной информации о партнере"""
|
||||
self.current_partner = partner_data
|
||||
self.details_title.setText(partner_data['company_name'])
|
||||
|
||||
# Создаем новый виджет для деталей вместо очистки layout
|
||||
new_details_frame = QFrame()
|
||||
new_details_frame.setFrameStyle(QFrame.Shape.StyledPanel)
|
||||
new_details_frame.setStyleSheet("""
|
||||
QFrame {
|
||||
background-color: #f9f9f9;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
padding: 15px;
|
||||
}
|
||||
""")
|
||||
new_details_layout = QVBoxLayout()
|
||||
new_details_layout.setSpacing(8)
|
||||
|
||||
# Добавляем новую информацию
|
||||
details = [
|
||||
("Тип:", partner_data.get('partner_type', 'Не указан')),
|
||||
("ИНН:", partner_data.get('inn', 'Не указан')),
|
||||
("Директор:", partner_data.get('director_name', 'Не указан')),
|
||||
("Телефон:", partner_data.get('phone', 'Не указан')),
|
||||
("Email:", partner_data.get('email', 'Не указан')),
|
||||
("Рейтинг:", str(partner_data.get('rating', 0))),
|
||||
("Адрес:", partner_data.get('legal_address', 'Не указан')),
|
||||
("Регионы:", partner_data.get('sales_locations', 'Не указан'))
|
||||
]
|
||||
|
||||
# ЗАМЕНИТЕ этот блок кода в методе show_partner_details:
|
||||
for label, value in details:
|
||||
row_widget = QWidget()
|
||||
row_layout = QHBoxLayout(row_widget)
|
||||
row_layout.setContentsMargins(0, 2, 0, 2)
|
||||
|
||||
label_widget = QLabel(label)
|
||||
label_widget.setStyleSheet("font-weight: bold; min-width: 100px;")
|
||||
label_widget.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop)
|
||||
|
||||
value_widget = QLabel(str(value))
|
||||
value_widget.setWordWrap(True)
|
||||
value_widget.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop)
|
||||
|
||||
row_layout.addWidget(label_widget)
|
||||
row_layout.addWidget(value_widget)
|
||||
row_layout.addStretch()
|
||||
|
||||
new_details_layout.addWidget(row_widget)
|
||||
|
||||
# НА этот исправленный вариант:
|
||||
for label, value in details:
|
||||
# Создаем контейнер для строки
|
||||
row_container = QWidget()
|
||||
row_container.setFixedHeight(30) # Фиксированная высота для каждой строки
|
||||
row_layout = QHBoxLayout(row_container)
|
||||
row_layout.setContentsMargins(5, 0, 5, 0)
|
||||
row_layout.setSpacing(10)
|
||||
|
||||
# Лейбл (название поля)
|
||||
label_widget = QLabel(label)
|
||||
label_widget.setStyleSheet("""
|
||||
QLabel {
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
min-width: 120px;
|
||||
max-width: 120px;
|
||||
background-color: transparent;
|
||||
}
|
||||
""")
|
||||
label_widget.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter)
|
||||
|
||||
# Значение
|
||||
value_widget = QLabel(str(value))
|
||||
value_widget.setStyleSheet("""
|
||||
QLabel {
|
||||
color: #555;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
}
|
||||
""")
|
||||
value_widget.setWordWrap(True)
|
||||
value_widget.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter)
|
||||
|
||||
row_layout.addWidget(label_widget)
|
||||
row_layout.addWidget(value_widget)
|
||||
row_layout.addStretch()
|
||||
|
||||
new_details_layout.addWidget(row_container)
|
||||
|
||||
new_details_frame.setLayout(new_details_layout)
|
||||
|
||||
# Заменяем старый details_frame на новый
|
||||
old_frame = self.details_frame
|
||||
layout = self.right_panel.layout()
|
||||
layout.replaceWidget(old_frame, new_details_frame)
|
||||
old_frame.deleteLater()
|
||||
|
||||
self.details_frame = new_details_frame
|
||||
self.details_layout = new_details_layout
|
||||
|
||||
self.details_frame.show()
|
||||
self.edit_button.show()
|
||||
self.sales_button.show()
|
||||
self.discount_button.show()
|
||||
|
||||
def show_add_partner_form(self):
|
||||
"""Открытие формы добавления партнера"""
|
||||
form = PartnerForm(self, auth=self.auth)
|
||||
form.partner_saved.connect(self.load_partners)
|
||||
form.exec()
|
||||
|
||||
def edit_partner(self):
|
||||
"""Редактирование выбранного партнера"""
|
||||
if self.current_partner:
|
||||
form = PartnerForm(self, self.current_partner, auth=self.auth)
|
||||
form.partner_saved.connect(self.load_partners)
|
||||
form.exec()
|
||||
|
||||
def show_sales_history(self):
|
||||
"""Открытие истории продаж партнера"""
|
||||
if self.current_partner:
|
||||
sales_window = SalesHistoryWindow(self.current_partner, self, auth=self.auth)
|
||||
sales_window.exec()
|
||||
|
||||
def calculate_discount(self):
|
||||
"""Расчет скидки для партнера с авторизацией"""
|
||||
if self.current_partner:
|
||||
try:
|
||||
response = requests.get(
|
||||
f"http://localhost:8000/api/v1/partners/{self.current_partner['partner_id']}/discount",
|
||||
auth=self.auth,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
discount_data = response.json()
|
||||
QMessageBox.information(
|
||||
self,
|
||||
"Расчет скидки",
|
||||
f"Партнер: {self.current_partner['company_name']}\n"
|
||||
f"Общие продажи: {discount_data['total_sales']}\n"
|
||||
f"Скидка: {discount_data['discount_percent']}%"
|
||||
)
|
||||
elif response.status_code == 401:
|
||||
QMessageBox.warning(self, "Ошибка авторизации", "Сессия истекла. Пожалуйста, войдите снова.")
|
||||
self.logout()
|
||||
|
||||
except Exception as e:
|
||||
QMessageBox.warning(self, "Ошибка", f"Не удалось рассчитать скидку: {str(e)}")
|
||||
|
||||
def show_material_calculator(self):
|
||||
"""Открытие калькулятора материалов"""
|
||||
calculator = MaterialCalculatorWindow(self, auth=self.auth)
|
||||
calculator.exec()
|
||||
|
||||
def logout(self):
|
||||
"""Выход из системы"""
|
||||
reply = QMessageBox.question(
|
||||
self,
|
||||
"Подтверждение выхода",
|
||||
"Вы уверены, что хотите выйти из системы?",
|
||||
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
|
||||
QMessageBox.StandardButton.No
|
||||
)
|
||||
|
||||
if reply == QMessageBox.StandardButton.Yes:
|
||||
self.close()
|
||||
# Здесь можно добавить вызов окна авторизации
|
||||
# или перезапуск приложения
|
||||
|
||||
def show_about(self):
|
||||
"""Показать информацию о программе"""
|
||||
QMessageBox.about(
|
||||
self,
|
||||
"О программе MasterPol",
|
||||
"MasterPol - Система управления партнерами\n\n"
|
||||
"Версия: 1.0.0\n"
|
||||
"Разработчик: Команда MasterPol\n\n"
|
||||
"Система предназначена для управления партнерами,\n"
|
||||
"учета продаж и расчета бизнес-показателей."
|
||||
)
|
||||
160
ressult/gui/material_calculator.py
Normal file
160
ressult/gui/material_calculator.py
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
# gui/material_calculator.py
|
||||
"""
|
||||
Калькулятор материалов для производства
|
||||
Соответствует модулю 4 ТЗ
|
||||
"""
|
||||
from PyQt6.QtWidgets import (QDialog, QVBoxLayout, QHBoxLayout, QLabel,
|
||||
QLineEdit, QPushButton, QMessageBox, QFormLayout,
|
||||
QDoubleSpinBox, QSpinBox)
|
||||
from PyQt6.QtCore import Qt
|
||||
import requests
|
||||
import math
|
||||
|
||||
class MaterialCalculatorWindow(QDialog):
|
||||
def __init__(self, parent=None, auth=None):
|
||||
super().__init__(parent)
|
||||
self.auth = auth # Сохраняем auth, даже если не используется
|
||||
self.setup_ui()
|
||||
|
||||
def setup_ui(self):
|
||||
self.setWindowTitle("Калькулятор материалов для производства")
|
||||
self.setModal(True)
|
||||
self.resize(400, 300)
|
||||
|
||||
layout = QVBoxLayout()
|
||||
|
||||
# Заголовок
|
||||
title = QLabel("Расчет количества материала")
|
||||
title.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
title.setStyleSheet("font-size: 16px; font-weight: bold; margin: 10px;")
|
||||
layout.addWidget(title)
|
||||
|
||||
# Форма ввода параметров
|
||||
form_layout = QFormLayout()
|
||||
|
||||
self.product_type_id = QSpinBox()
|
||||
self.product_type_id.setRange(1, 100)
|
||||
form_layout.addRow("ID типа продукции:", self.product_type_id)
|
||||
|
||||
self.material_type_id = QSpinBox()
|
||||
self.material_type_id.setRange(1, 100)
|
||||
form_layout.addRow("ID типа материала:", self.material_type_id)
|
||||
|
||||
self.quantity = QSpinBox()
|
||||
self.quantity.setRange(1, 1000000)
|
||||
form_layout.addRow("Количество продукции:", self.quantity)
|
||||
|
||||
self.param1 = QDoubleSpinBox()
|
||||
self.param1.setRange(0.1, 1000.0)
|
||||
self.param1.setDecimals(2)
|
||||
form_layout.addRow("Параметр продукции 1:", self.param1)
|
||||
|
||||
self.param2 = QDoubleSpinBox()
|
||||
self.param2.setRange(0.1, 1000.0)
|
||||
self.param2.setDecimals(2)
|
||||
form_layout.addRow("Параметр продукции 2:", self.param2)
|
||||
|
||||
self.product_coeff = QDoubleSpinBox()
|
||||
self.product_coeff.setRange(0.1, 10.0)
|
||||
self.product_coeff.setDecimals(3)
|
||||
self.product_coeff.setValue(1.0)
|
||||
form_layout.addRow("Коэффициент типа продукции:", self.product_coeff)
|
||||
|
||||
self.defect_percent = QDoubleSpinBox()
|
||||
self.defect_percent.setRange(0.0, 50.0)
|
||||
self.defect_percent.setDecimals(1)
|
||||
self.defect_percent.setSuffix("%")
|
||||
form_layout.addRow("Процент брака материала:", self.defect_percent)
|
||||
|
||||
layout.addLayout(form_layout)
|
||||
|
||||
# Результат
|
||||
self.result_label = QLabel()
|
||||
self.result_label.setStyleSheet("font-weight: bold; color: #007acc; margin: 10px;")
|
||||
self.result_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
layout.addWidget(self.result_label)
|
||||
|
||||
# Кнопки
|
||||
buttons_layout = QHBoxLayout()
|
||||
|
||||
self.calculate_button = QPushButton("Рассчитать")
|
||||
self.calculate_button.clicked.connect(self.calculate_material)
|
||||
self.calculate_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #007acc;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #005a9e;
|
||||
}
|
||||
""")
|
||||
|
||||
self.close_button = QPushButton("Закрыть")
|
||||
self.close_button.clicked.connect(self.accept)
|
||||
|
||||
buttons_layout.addWidget(self.calculate_button)
|
||||
buttons_layout.addStretch()
|
||||
buttons_layout.addWidget(self.close_button)
|
||||
|
||||
layout.addLayout(buttons_layout)
|
||||
|
||||
self.setLayout(layout)
|
||||
|
||||
def calculate_material(self):
|
||||
"""Расчет количества материала с обработкой ошибок"""
|
||||
try:
|
||||
# Проверяем валидность входных данных
|
||||
if (self.param1.value() <= 0 or self.param2.value() <= 0 or
|
||||
self.product_coeff.value() <= 0 or self.defect_percent.value() < 0):
|
||||
self.result_label.setText("Ошибка: неверные параметры")
|
||||
self.result_label.setStyleSheet("color: red; font-weight: bold;")
|
||||
return
|
||||
|
||||
# Создаем данные для расчета
|
||||
calculation_data = {
|
||||
'product_type_id': self.product_type_id.value(),
|
||||
'material_type_id': self.material_type_id.value(),
|
||||
'quantity': self.quantity.value(),
|
||||
'param1': self.param1.value(),
|
||||
'param2': self.param2.value(),
|
||||
'product_coeff': self.product_coeff.value(),
|
||||
'defect_percent': self.defect_percent.value()
|
||||
}
|
||||
|
||||
# Локальный расчет (без API)
|
||||
material_quantity = self.calculate_locally(calculation_data)
|
||||
|
||||
if material_quantity >= 0:
|
||||
self.result_label.setText(
|
||||
f"Необходимое количество материала: {material_quantity} единиц"
|
||||
)
|
||||
self.result_label.setStyleSheet("color: #007acc; font-weight: bold;")
|
||||
else:
|
||||
self.result_label.setText("Ошибка: неверные параметры расчета")
|
||||
self.result_label.setStyleSheet("color: red; font-weight: bold;")
|
||||
|
||||
except Exception as e:
|
||||
self.result_label.setText(f"Ошибка расчета: {str(e)}")
|
||||
self.result_label.setStyleSheet("color: red; font-weight: bold;")
|
||||
|
||||
def calculate_locally(self, data):
|
||||
"""Локальный расчет материалов"""
|
||||
try:
|
||||
import math
|
||||
|
||||
# Расчет количества материала на одну единицу продукции
|
||||
material_per_unit = data['param1'] * data['param2'] * data['product_coeff']
|
||||
|
||||
# Расчет общего количества материала с учетом брака
|
||||
total_material = material_per_unit * data['quantity']
|
||||
total_material_with_defect = total_material * (1 + data['defect_percent'] / 100)
|
||||
|
||||
# Округление до целого числа в большую сторону
|
||||
return math.ceil(total_material_with_defect)
|
||||
|
||||
except:
|
||||
return -1
|
||||
344
ressult/gui/orders_panel.py
Normal file
344
ressult/gui/orders_panel.py
Normal file
|
|
@ -0,0 +1,344 @@
|
|||
# gui/orders_panel.py
|
||||
"""
|
||||
Панель управления заказами и продажами
|
||||
"""
|
||||
from PyQt6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel,
|
||||
QTableWidget, QTableWidgetItem, QPushButton,
|
||||
QHeaderView, QMessageBox, QDateEdit, QComboBox,
|
||||
QLineEdit, QFormLayout, QDialog, QDoubleSpinBox)
|
||||
from PyQt6.QtCore import Qt, QDate
|
||||
from PyQt6.QtGui import QFont
|
||||
import requests
|
||||
|
||||
class OrderForm(QDialog):
|
||||
"""Форма для добавления/редактирования заказа"""
|
||||
|
||||
order_saved = pyqtSignal()
|
||||
|
||||
def __init__(self, parent=None, order_data=None, auth=None, partners=None):
|
||||
super().__init__(parent)
|
||||
self.order_data = order_data
|
||||
self.auth = auth
|
||||
self.partners = partners or []
|
||||
self.setup_ui()
|
||||
|
||||
def setup_ui(self):
|
||||
self.setWindowTitle("Добавить заказ" if not self.order_data else "Редактировать заказ")
|
||||
self.setModal(True)
|
||||
self.resize(400, 300)
|
||||
|
||||
layout = QVBoxLayout()
|
||||
|
||||
# Форма ввода данных
|
||||
form_layout = QFormLayout()
|
||||
|
||||
# Выбор партнера
|
||||
self.partner_combo = QComboBox()
|
||||
self.partner_combo.addItem("Выберите партнера", None)
|
||||
for partner in self.partners:
|
||||
self.partner_combo.addItem(partner['company_name'], partner['partner_id'])
|
||||
form_layout.addRow("Партнер*:", self.partner_combo)
|
||||
|
||||
# Название продукта
|
||||
self.product_name = QLineEdit()
|
||||
self.product_name.setPlaceholderText("Введите название продукта")
|
||||
form_layout.addRow("Продукт*:", self.product_name)
|
||||
|
||||
# Количество
|
||||
self.quantity = QDoubleSpinBox()
|
||||
self.quantity.setRange(0.01, 100000.0)
|
||||
self.quantity.setDecimals(2)
|
||||
form_layout.addRow("Количество*:", self.quantity)
|
||||
|
||||
# Дата продажи
|
||||
self.sale_date = QDateEdit()
|
||||
self.sale_date.setDate(QDate.currentDate())
|
||||
self.sale_date.setCalendarPopup(True)
|
||||
form_layout.addRow("Дата продажи*:", self.sale_date)
|
||||
|
||||
layout.addLayout(form_layout)
|
||||
|
||||
# Кнопки
|
||||
buttons_layout = QHBoxLayout()
|
||||
|
||||
self.save_button = QPushButton("Сохранить")
|
||||
self.save_button.clicked.connect(self.save_order)
|
||||
self.save_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #28a745;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #218838;
|
||||
}
|
||||
""")
|
||||
|
||||
self.cancel_button = QPushButton("Отмена")
|
||||
self.cancel_button.clicked.connect(self.reject)
|
||||
|
||||
buttons_layout.addWidget(self.save_button)
|
||||
buttons_layout.addWidget(self.cancel_button)
|
||||
buttons_layout.addStretch()
|
||||
|
||||
layout.addLayout(buttons_layout)
|
||||
|
||||
self.setLayout(layout)
|
||||
|
||||
# Если редактирование, заполняем форму
|
||||
if self.order_data:
|
||||
self.fill_form()
|
||||
|
||||
def fill_form(self):
|
||||
"""Заполнение формы данными заказа"""
|
||||
data = self.order_data
|
||||
|
||||
# Устанавливаем партнера
|
||||
partner_index = self.partner_combo.findData(data.get('partner_id'))
|
||||
if partner_index >= 0:
|
||||
self.partner_combo.setCurrentIndex(partner_index)
|
||||
|
||||
self.product_name.setText(data.get('product_name', ''))
|
||||
self.quantity.setValue(float(data.get('quantity', 0)))
|
||||
|
||||
# Устанавливаем дату
|
||||
sale_date = data.get('sale_date')
|
||||
if sale_date:
|
||||
date = QDate.fromString(sale_date, 'yyyy-MM-dd')
|
||||
if date.isValid():
|
||||
self.sale_date.setDate(date)
|
||||
|
||||
def validate_form(self):
|
||||
"""Валидация данных формы"""
|
||||
errors = []
|
||||
|
||||
if not self.partner_combo.currentData():
|
||||
errors.append("Выберите партнера")
|
||||
|
||||
if not self.product_name.text().strip():
|
||||
errors.append("Введите название продукта")
|
||||
|
||||
if self.quantity.value() <= 0:
|
||||
errors.append("Количество должно быть больше 0")
|
||||
|
||||
return errors
|
||||
|
||||
def save_order(self):
|
||||
"""Сохранение заказа"""
|
||||
errors = self.validate_form()
|
||||
if errors:
|
||||
QMessageBox.warning(self, "Ошибка валидации", "\n".join(errors))
|
||||
return
|
||||
|
||||
order_data = {
|
||||
'partner_id': self.partner_combo.currentData(),
|
||||
'product_name': self.product_name.text().strip(),
|
||||
'quantity': self.quantity.value(),
|
||||
'sale_date': self.sale_date.date().toString('yyyy-MM-dd')
|
||||
}
|
||||
|
||||
try:
|
||||
if self.order_data:
|
||||
# Обновление существующего заказа
|
||||
response = requests.put(
|
||||
f"http://localhost:8000/api/v1/sales/{self.order_data['sale_id']}",
|
||||
json=order_data,
|
||||
auth=self.auth,
|
||||
timeout=10
|
||||
)
|
||||
else:
|
||||
# Создание нового заказа
|
||||
response = requests.post(
|
||||
"http://localhost:8000/api/v1/sales",
|
||||
json=order_data,
|
||||
auth=self.auth,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
self.order_saved.emit()
|
||||
QMessageBox.information(self, "Успех", "Заказ успешно сохранен")
|
||||
self.accept()
|
||||
elif response.status_code == 401:
|
||||
QMessageBox.warning(self, "Ошибка авторизации", "Сессия истекла. Пожалуйста, войдите снова.")
|
||||
else:
|
||||
error_msg = response.json().get('detail', 'Неизвестная ошибка')
|
||||
QMessageBox.warning(self, "Ошибка", f"Не удалось сохранить заказ: {error_msg}")
|
||||
|
||||
except requests.exceptions.ConnectionError:
|
||||
QMessageBox.critical(self, "Ошибка", "Не удалось подключиться к серверу")
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "Ошибка", f"Ошибка подключения: {str(e)}")
|
||||
|
||||
class OrdersPanel(QWidget):
|
||||
"""Панель управления заказами"""
|
||||
|
||||
def __init__(self, auth=None):
|
||||
super().__init__()
|
||||
self.auth = auth
|
||||
self.partners = []
|
||||
self.setup_ui()
|
||||
self.load_partners()
|
||||
self.load_orders()
|
||||
|
||||
def setup_ui(self):
|
||||
"""Настройка интерфейса панели заказов"""
|
||||
layout = QVBoxLayout()
|
||||
layout.setContentsMargins(10, 10, 10, 10)
|
||||
layout.setSpacing(10)
|
||||
|
||||
# Заголовок
|
||||
title = QLabel("Управление заказами")
|
||||
title.setFont(QFont("Arial", 16, QFont.Weight.Bold))
|
||||
title.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
layout.addWidget(title)
|
||||
|
||||
# Панель управления
|
||||
control_layout = QHBoxLayout()
|
||||
|
||||
self.add_button = QPushButton("Добавить заказ")
|
||||
self.add_button.clicked.connect(self.show_add_order_form)
|
||||
self.add_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #007acc;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #005a9e;
|
||||
}
|
||||
""")
|
||||
|
||||
self.refresh_button = QPushButton("Обновить")
|
||||
self.refresh_button.clicked.connect(self.load_orders)
|
||||
|
||||
control_layout.addWidget(self.add_button)
|
||||
control_layout.addWidget(self.refresh_button)
|
||||
control_layout.addStretch()
|
||||
|
||||
layout.addLayout(control_layout)
|
||||
|
||||
# Таблица заказов
|
||||
self.orders_table = QTableWidget()
|
||||
self.orders_table.setColumnCount(6)
|
||||
self.orders_table.setHorizontalHeaderLabels([
|
||||
"ID", "Партнер", "Продукт", "Количество", "Дата", "Действия"
|
||||
])
|
||||
self.orders_table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
|
||||
self.orders_table.setStyleSheet("""
|
||||
QTableWidget {
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
background-color: white;
|
||||
}
|
||||
QTableWidget::item {
|
||||
padding: 8px;
|
||||
}
|
||||
""")
|
||||
|
||||
layout.addWidget(self.orders_table)
|
||||
|
||||
self.setLayout(layout)
|
||||
|
||||
def load_partners(self):
|
||||
"""Загрузка списка партнеров"""
|
||||
try:
|
||||
response = requests.get(
|
||||
"http://localhost:8000/api/v1/partners",
|
||||
auth=self.auth,
|
||||
timeout=10
|
||||
)
|
||||
if response.status_code == 200:
|
||||
self.partners = response.json()
|
||||
except:
|
||||
self.partners = []
|
||||
|
||||
def load_orders(self):
|
||||
"""Загрузка списка заказов"""
|
||||
try:
|
||||
response = requests.get(
|
||||
"http://localhost:8000/api/v1/sales",
|
||||
auth=self.auth,
|
||||
timeout=10
|
||||
)
|
||||
if response.status_code == 200:
|
||||
orders = response.json()
|
||||
self.display_orders(orders)
|
||||
elif response.status_code == 401:
|
||||
QMessageBox.warning(self, "Ошибка авторизации", "Сессия истекла")
|
||||
except Exception as e:
|
||||
QMessageBox.warning(self, "Ошибка", f"Не удалось загрузить заказы: {str(e)}")
|
||||
|
||||
def display_orders(self, orders):
|
||||
"""Отображение заказов в таблице"""
|
||||
self.orders_table.setRowCount(len(orders))
|
||||
|
||||
for row, order in enumerate(orders):
|
||||
self.orders_table.setItem(row, 0, QTableWidgetItem(str(order.get('sale_id', ''))))
|
||||
self.orders_table.setItem(row, 1, QTableWidgetItem(order.get('company_name', 'Неизвестно')))
|
||||
self.orders_table.setItem(row, 2, QTableWidgetItem(order.get('product_name', '')))
|
||||
self.orders_table.setItem(row, 3, QTableWidgetItem(str(order.get('quantity', ''))))
|
||||
self.orders_table.setItem(row, 4, QTableWidgetItem(order.get('sale_date', '')))
|
||||
|
||||
# Кнопки действий
|
||||
actions_widget = QWidget()
|
||||
actions_layout = QHBoxLayout(actions_widget)
|
||||
actions_layout.setContentsMargins(4, 4, 4, 4)
|
||||
actions_layout.setSpacing(4)
|
||||
|
||||
delete_button = QPushButton("Удалить")
|
||||
delete_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #dc3545;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 4px 8px;
|
||||
border-radius: 3px;
|
||||
font-size: 11px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #c82333;
|
||||
}
|
||||
""")
|
||||
delete_button.clicked.connect(lambda checked, o=order: self.delete_order(o))
|
||||
|
||||
actions_layout.addWidget(delete_button)
|
||||
actions_layout.addStretch()
|
||||
|
||||
self.orders_table.setCellWidget(row, 5, actions_widget)
|
||||
|
||||
def show_add_order_form(self):
|
||||
"""Открытие формы добавления заказа"""
|
||||
form = OrderForm(self, auth=self.auth, partners=self.partners)
|
||||
form.order_saved.connect(self.load_orders)
|
||||
form.exec()
|
||||
|
||||
def delete_order(self, order):
|
||||
"""Удаление заказа"""
|
||||
reply = QMessageBox.question(
|
||||
self,
|
||||
"Подтверждение удаления",
|
||||
f"Вы уверены, что хотите удалить заказ #{order.get('sale_id')}?",
|
||||
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
|
||||
QMessageBox.StandardButton.No
|
||||
)
|
||||
|
||||
if reply == QMessageBox.StandardButton.Yes:
|
||||
try:
|
||||
response = requests.delete(
|
||||
f"http://localhost:8000/api/v1/sales/{order['sale_id']}",
|
||||
auth=self.auth,
|
||||
timeout=10
|
||||
)
|
||||
if response.status_code == 200:
|
||||
self.load_orders()
|
||||
elif response.status_code == 401:
|
||||
QMessageBox.warning(self, "Ошибка авторизации", "Сессия истекла")
|
||||
except Exception as e:
|
||||
QMessageBox.warning(self, "Ошибка", f"Не удалось удалить заказ: {str(e)}")
|
||||
193
ressult/gui/partner_form.py
Normal file
193
ressult/gui/partner_form.py
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
# gui/partner_form.py (обновленный)
|
||||
"""
|
||||
Форма для добавления/редактирования партнера с поддержкой авторизации
|
||||
"""
|
||||
from PyQt6.QtWidgets import (QDialog, QVBoxLayout, QHBoxLayout, QLabel,
|
||||
QLineEdit, QComboBox, QPushButton, QMessageBox,
|
||||
QFormLayout, QSpinBox)
|
||||
from PyQt6.QtCore import pyqtSignal
|
||||
import requests
|
||||
|
||||
class PartnerForm(QDialog):
|
||||
partner_saved = pyqtSignal()
|
||||
|
||||
def __init__(self, parent=None, partner_data=None, auth=None):
|
||||
super().__init__(parent)
|
||||
self.partner_data = partner_data
|
||||
self.auth = auth
|
||||
self.setup_ui()
|
||||
|
||||
def setup_ui(self):
|
||||
self.setWindowTitle("Добавить партнера" if not self.partner_data else "Редактировать партнера")
|
||||
self.setModal(True)
|
||||
self.resize(500, 400)
|
||||
|
||||
layout = QVBoxLayout()
|
||||
|
||||
# Форма ввода данных
|
||||
form_layout = QFormLayout()
|
||||
|
||||
self.company_name = QLineEdit()
|
||||
self.company_name.setPlaceholderText("Введите наименование компании")
|
||||
form_layout.addRow("Наименование компании*:", self.company_name)
|
||||
|
||||
self.inn = QLineEdit()
|
||||
self.inn.setPlaceholderText("Введите ИНН")
|
||||
form_layout.addRow("ИНН*:", self.inn)
|
||||
|
||||
self.partner_type = QComboBox()
|
||||
self.partner_type.addItems(["", "distributor", "retail", "wholesale", "dealer"])
|
||||
self.partner_type.setPlaceholderText("Выберите тип партнера")
|
||||
form_layout.addRow("Тип партнера:", self.partner_type)
|
||||
|
||||
self.rating = QSpinBox()
|
||||
self.rating.setRange(0, 100)
|
||||
self.rating.setSuffix("%")
|
||||
form_layout.addRow("Рейтинг:", self.rating)
|
||||
|
||||
self.legal_address = QLineEdit()
|
||||
self.legal_address.setPlaceholderText("Введите юридический адрес")
|
||||
form_layout.addRow("Юридический адрес:", self.legal_address)
|
||||
|
||||
self.director_name = QLineEdit()
|
||||
self.director_name.setPlaceholderText("Введите ФИО директора")
|
||||
form_layout.addRow("ФИО директора:", self.director_name)
|
||||
|
||||
self.phone = QLineEdit()
|
||||
self.phone.setPlaceholderText("+7XXXXXXXXXX")
|
||||
form_layout.addRow("Телефон:", self.phone)
|
||||
|
||||
self.email = QLineEdit()
|
||||
self.email.setPlaceholderText("email@example.com")
|
||||
form_layout.addRow("Email:", self.email)
|
||||
|
||||
self.sales_locations = QLineEdit()
|
||||
self.sales_locations.setPlaceholderText("Москва, Санкт-Петербург...")
|
||||
form_layout.addRow("Регионы продаж:", self.sales_locations)
|
||||
|
||||
layout.addLayout(form_layout)
|
||||
|
||||
# Кнопки
|
||||
buttons_layout = QHBoxLayout()
|
||||
|
||||
self.save_button = QPushButton("Сохранить")
|
||||
self.save_button.clicked.connect(self.save_partner)
|
||||
self.save_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #28a745;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #218838;
|
||||
}
|
||||
""")
|
||||
|
||||
self.cancel_button = QPushButton("Отмена")
|
||||
self.cancel_button.clicked.connect(self.reject)
|
||||
|
||||
buttons_layout.addWidget(self.save_button)
|
||||
buttons_layout.addWidget(self.cancel_button)
|
||||
buttons_layout.addStretch()
|
||||
|
||||
layout.addLayout(buttons_layout)
|
||||
|
||||
self.setLayout(layout)
|
||||
|
||||
# Если редактирование, заполняем форму
|
||||
if self.partner_data:
|
||||
self.fill_form()
|
||||
|
||||
def fill_form(self):
|
||||
"""Заполнение формы данными партнера"""
|
||||
data = self.partner_data
|
||||
self.company_name.setText(data.get('company_name', ''))
|
||||
self.inn.setText(data.get('inn', ''))
|
||||
|
||||
partner_type = data.get('partner_type', '')
|
||||
if partner_type:
|
||||
index = self.partner_type.findText(partner_type)
|
||||
if index >= 0:
|
||||
self.partner_type.setCurrentIndex(index)
|
||||
|
||||
# Безопасное преобразование рейтинга
|
||||
rating = data.get('rating', 0)
|
||||
if isinstance(rating, float):
|
||||
rating = int(rating)
|
||||
self.rating.setValue(rating)
|
||||
|
||||
self.legal_address.setText(data.get('legal_address', ''))
|
||||
self.director_name.setText(data.get('director_name', ''))
|
||||
self.phone.setText(data.get('phone', ''))
|
||||
self.email.setText(data.get('email', ''))
|
||||
self.sales_locations.setText(data.get('sales_locations', ''))
|
||||
|
||||
def validate_form(self):
|
||||
"""Валидация данных формы"""
|
||||
errors = []
|
||||
|
||||
if not self.company_name.text().strip():
|
||||
errors.append("Наименование компании обязательно")
|
||||
|
||||
if not self.inn.text().strip():
|
||||
errors.append("ИНН обязателен")
|
||||
|
||||
if self.phone.text() and not self.phone.text().startswith('+'):
|
||||
errors.append("Телефон должен начинаться с '+'")
|
||||
|
||||
return errors
|
||||
|
||||
def save_partner(self):
|
||||
"""Сохранение партнера с авторизацией"""
|
||||
errors = self.validate_form()
|
||||
if errors:
|
||||
QMessageBox.warning(self, "Ошибка валидации", "\n".join(errors))
|
||||
return
|
||||
|
||||
partner_data = {
|
||||
'company_name': self.company_name.text().strip(),
|
||||
'inn': self.inn.text().strip(),
|
||||
'partner_type': self.partner_type.currentText() or None,
|
||||
'rating': self.rating.value(),
|
||||
'legal_address': self.legal_address.text().strip() or None,
|
||||
'director_name': self.director_name.text().strip() or None,
|
||||
'phone': self.phone.text().strip() or None,
|
||||
'email': self.email.text().strip() or None,
|
||||
'sales_locations': self.sales_locations.text().strip() or None
|
||||
}
|
||||
|
||||
try:
|
||||
if self.partner_data:
|
||||
# Обновление существующего партнера
|
||||
response = requests.put(
|
||||
f"http://localhost:8000/api/v1/partners/{self.partner_data['partner_id']}",
|
||||
json=partner_data,
|
||||
auth=self.auth,
|
||||
timeout=10
|
||||
)
|
||||
else:
|
||||
# Создание нового партнера
|
||||
response = requests.post(
|
||||
"http://localhost:8000/api/v1/partners",
|
||||
json=partner_data,
|
||||
auth=self.auth,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
self.partner_saved.emit()
|
||||
QMessageBox.information(self, "Успех", "Партнер успешно сохранен")
|
||||
self.accept()
|
||||
elif response.status_code == 401:
|
||||
QMessageBox.warning(self, "Ошибка авторизации", "Сессия истекла. Пожалуйста, войдите снова.")
|
||||
else:
|
||||
error_msg = response.json().get('detail', 'Неизвестная ошибка')
|
||||
QMessageBox.warning(self, "Ошибка", f"Не удалось сохранить партнера: {error_msg}")
|
||||
|
||||
except requests.exceptions.ConnectionError:
|
||||
QMessageBox.critical(self, "Ошибка", "Не удалось подключиться к серверу")
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "Ошибка", f"Ошибка подключения: {str(e)}")
|
||||
186
ressult/gui/partner_form.py.bak
Normal file
186
ressult/gui/partner_form.py.bak
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
# gui/partner_form.py
|
||||
"""
|
||||
Форма для добавления/редактирования партнера
|
||||
Соответствует модулю 3 ТЗ
|
||||
"""
|
||||
from PyQt6.QtWidgets import (QDialog, QVBoxLayout, QHBoxLayout, QLabel,
|
||||
QLineEdit, QComboBox, QPushButton, QMessageBox,
|
||||
QFormLayout, QSpinBox)
|
||||
from PyQt6.QtCore import pyqtSignal
|
||||
import requests
|
||||
|
||||
class PartnerForm(QDialog):
|
||||
partner_saved = pyqtSignal()
|
||||
|
||||
def __init__(self, parent=None, partner_data=None):
|
||||
super().__init__(parent)
|
||||
self.partner_data = partner_data
|
||||
self.setup_ui()
|
||||
|
||||
def setup_ui(self):
|
||||
self.setWindowTitle("Добавить партнера" if not self.partner_data else "Редактировать партнера")
|
||||
self.setModal(True)
|
||||
self.resize(500, 400)
|
||||
|
||||
layout = QVBoxLayout()
|
||||
|
||||
# Форма ввода данных
|
||||
form_layout = QFormLayout()
|
||||
|
||||
self.company_name = QLineEdit()
|
||||
self.company_name.setPlaceholderText("Введите наименование компании")
|
||||
form_layout.addRow("Наименование компании*:", self.company_name)
|
||||
|
||||
self.inn = QLineEdit()
|
||||
self.inn.setPlaceholderText("Введите ИНН")
|
||||
form_layout.addRow("ИНН*:", self.inn)
|
||||
|
||||
self.partner_type = QComboBox()
|
||||
self.partner_type.addItems(["", "distributor", "retail", "wholesale", "dealer"])
|
||||
self.partner_type.setPlaceholderText("Выберите тип партнера")
|
||||
form_layout.addRow("Тип партнера:", self.partner_type)
|
||||
|
||||
self.rating = QSpinBox()
|
||||
self.rating.setRange(0, 100)
|
||||
self.rating.setSuffix("%")
|
||||
form_layout.addRow("Рейтинг:", self.rating)
|
||||
|
||||
self.legal_address = QLineEdit()
|
||||
self.legal_address.setPlaceholderText("Введите юридический адрес")
|
||||
form_layout.addRow("Юридический адрес:", self.legal_address)
|
||||
|
||||
self.director_name = QLineEdit()
|
||||
self.director_name.setPlaceholderText("Введите ФИО директора")
|
||||
form_layout.addRow("ФИО директора:", self.director_name)
|
||||
|
||||
self.phone = QLineEdit()
|
||||
self.phone.setPlaceholderText("+7XXXXXXXXXX")
|
||||
form_layout.addRow("Телефон:", self.phone)
|
||||
|
||||
self.email = QLineEdit()
|
||||
self.email.setPlaceholderText("email@example.com")
|
||||
form_layout.addRow("Email:", self.email)
|
||||
|
||||
self.sales_locations = QLineEdit()
|
||||
self.sales_locations.setPlaceholderText("Москва, Санкт-Петербург...")
|
||||
form_layout.addRow("Регионы продаж:", self.sales_locations)
|
||||
|
||||
layout.addLayout(form_layout)
|
||||
|
||||
# Кнопки
|
||||
buttons_layout = QHBoxLayout()
|
||||
|
||||
self.save_button = QPushButton("Сохранить")
|
||||
self.save_button.clicked.connect(self.save_partner)
|
||||
self.save_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #28a745;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #218838;
|
||||
}
|
||||
""")
|
||||
|
||||
self.cancel_button = QPushButton("Отмена")
|
||||
self.cancel_button.clicked.connect(self.reject)
|
||||
|
||||
buttons_layout.addWidget(self.save_button)
|
||||
buttons_layout.addWidget(self.cancel_button)
|
||||
buttons_layout.addStretch()
|
||||
|
||||
layout.addLayout(buttons_layout)
|
||||
|
||||
self.setLayout(layout)
|
||||
|
||||
# Если редактирование, заполняем форму
|
||||
if self.partner_data:
|
||||
self.fill_form()
|
||||
|
||||
# gui/partner_form.py (исправленный метод fill_form)
|
||||
def fill_form(self):
|
||||
"""Заполнение формы данными партнера"""
|
||||
data = self.partner_data
|
||||
self.company_name.setText(data.get('company_name', ''))
|
||||
self.inn.setText(data.get('inn', ''))
|
||||
|
||||
partner_type = data.get('partner_type', '')
|
||||
if partner_type:
|
||||
index = self.partner_type.findText(partner_type)
|
||||
if index >= 0:
|
||||
self.partner_type.setCurrentIndex(index)
|
||||
|
||||
# Безопасное преобразование рейтинга к int
|
||||
rating = data.get('rating', 0)
|
||||
if isinstance(rating, float):
|
||||
rating = int(rating)
|
||||
self.rating.setValue(rating)
|
||||
|
||||
self.legal_address.setText(data.get('legal_address', ''))
|
||||
self.director_name.setText(data.get('director_name', ''))
|
||||
self.phone.setText(data.get('phone', ''))
|
||||
self.email.setText(data.get('email', ''))
|
||||
self.sales_locations.setText(data.get('sales_locations', ''))
|
||||
|
||||
def validate_form(self):
|
||||
"""Валидация данных формы"""
|
||||
errors = []
|
||||
|
||||
if not self.company_name.text().strip():
|
||||
errors.append("Наименование компании обязательно")
|
||||
|
||||
if not self.inn.text().strip():
|
||||
errors.append("ИНН обязателен")
|
||||
|
||||
if self.phone.text() and not self.phone.text().startswith('+'):
|
||||
errors.append("Телефон должен начинаться с '+'")
|
||||
|
||||
return errors
|
||||
|
||||
def save_partner(self):
|
||||
"""Сохранение партнера"""
|
||||
errors = self.validate_form()
|
||||
if errors:
|
||||
QMessageBox.warning(self, "Ошибка валидации", "\n".join(errors))
|
||||
return
|
||||
|
||||
partner_data = {
|
||||
'company_name': self.company_name.text().strip(),
|
||||
'inn': self.inn.text().strip(),
|
||||
'partner_type': self.partner_type.currentText() or None,
|
||||
'rating': self.rating.value(),
|
||||
'legal_address': self.legal_address.text().strip() or None,
|
||||
'director_name': self.director_name.text().strip() or None,
|
||||
'phone': self.phone.text().strip() or None,
|
||||
'email': self.email.text().strip() or None,
|
||||
'sales_locations': self.sales_locations.text().strip() or None
|
||||
}
|
||||
|
||||
try:
|
||||
if self.partner_data:
|
||||
# Обновление существующего партнера
|
||||
response = requests.put(
|
||||
f"http://localhost:8000/api/v1/partners/{self.partner_data['partner_id']}",
|
||||
json=partner_data
|
||||
)
|
||||
else:
|
||||
# Создание нового партнера
|
||||
response = requests.post(
|
||||
"http://localhost:8000/api/v1/partners",
|
||||
json=partner_data
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
self.partner_saved.emit()
|
||||
QMessageBox.information(self, "Успех", "Партнер успешно сохранен")
|
||||
self.accept()
|
||||
else:
|
||||
error_msg = response.json().get('detail', 'Неизвестная ошибка')
|
||||
QMessageBox.warning(self, "Ошибка", f"Не удалось сохранить партнера: {error_msg}")
|
||||
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "Ошибка", f"Ошибка подключения: {str(e)}")
|
||||
91
ressult/gui/sales_history.py
Normal file
91
ressult/gui/sales_history.py
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
# gui/sales_history.py
|
||||
"""
|
||||
Окно истории продаж партнера
|
||||
Соответствует модулю 4 ТЗ
|
||||
"""
|
||||
from PyQt6.QtWidgets import (QDialog, QVBoxLayout, QHBoxLayout, QLabel,
|
||||
QTableWidget, QTableWidgetItem, QPushButton,
|
||||
QHeaderView, QMessageBox)
|
||||
from PyQt6.QtCore import Qt
|
||||
import requests
|
||||
|
||||
class SalesHistoryWindow(QDialog):
|
||||
def __init__(self, partner_data, parent=None):
|
||||
super().__init__(parent)
|
||||
self.partner_data = partner_data
|
||||
self.setup_ui()
|
||||
self.load_sales_history()
|
||||
|
||||
def setup_ui(self):
|
||||
self.setWindowTitle(f"История продаж - {self.partner_data['company_name']}")
|
||||
self.setModal(True)
|
||||
self.resize(800, 400)
|
||||
|
||||
layout = QVBoxLayout()
|
||||
|
||||
# Заголовок
|
||||
title = QLabel(f"История реализации продукции\n{self.partner_data['company_name']}")
|
||||
title.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
title.setStyleSheet("font-size: 16px; font-weight: bold; margin: 10px;")
|
||||
layout.addWidget(title)
|
||||
|
||||
# Таблица продаж
|
||||
self.sales_table = QTableWidget()
|
||||
self.sales_table.setColumnCount(4)
|
||||
self.sales_table.setHorizontalHeaderLabels([
|
||||
"ID", "Наименование продукции", "Количество", "Дата продажи"
|
||||
])
|
||||
self.sales_table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
|
||||
layout.addWidget(self.sales_table)
|
||||
|
||||
# Статистика
|
||||
self.stats_label = QLabel()
|
||||
self.stats_label.setStyleSheet("font-weight: bold; margin: 10px;")
|
||||
layout.addWidget(self.stats_label)
|
||||
|
||||
# Кнопки
|
||||
buttons_layout = QHBoxLayout()
|
||||
|
||||
self.close_button = QPushButton("Закрыть")
|
||||
self.close_button.clicked.connect(self.accept)
|
||||
|
||||
buttons_layout.addStretch()
|
||||
buttons_layout.addWidget(self.close_button)
|
||||
|
||||
layout.addLayout(buttons_layout)
|
||||
|
||||
self.setLayout(layout)
|
||||
|
||||
def load_sales_history(self):
|
||||
"""Загрузка истории продаж партнера"""
|
||||
try:
|
||||
response = requests.get(
|
||||
f"http://localhost:8000/api/v1/sales/partner/{self.partner_data['partner_id']}"
|
||||
)
|
||||
if response.status_code == 200:
|
||||
sales_data = response.json()
|
||||
self.display_sales_data(sales_data)
|
||||
else:
|
||||
QMessageBox.warning(self, "Ошибка", "Не удалось загрузить историю продаж")
|
||||
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "Ошибка", f"Ошибка подключения: {str(e)}")
|
||||
|
||||
def display_sales_data(self, sales_data):
|
||||
"""Отображение данных о продажах в таблице"""
|
||||
self.sales_table.setRowCount(len(sales_data))
|
||||
|
||||
total_quantity = 0
|
||||
for row, sale in enumerate(sales_data):
|
||||
self.sales_table.setItem(row, 0, QTableWidgetItem(str(sale['sale_id'])))
|
||||
self.sales_table.setItem(row, 1, QTableWidgetItem(sale['product_name']))
|
||||
self.sales_table.setItem(row, 2, QTableWidgetItem(str(sale['quantity'])))
|
||||
self.sales_table.setItem(row, 3, QTableWidgetItem(sale['sale_date']))
|
||||
|
||||
total_quantity += float(sale['quantity'])
|
||||
|
||||
# Обновление статистики
|
||||
self.stats_label.setText(
|
||||
f"Общее количество проданной продукции: {total_quantity}\n"
|
||||
f"Всего продаж: {len(sales_data)}"
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue