Загрузка данных


import sys
import pymysql
import os
from datetime import datetime

from PyQt6 import uic
from PyQt6.QtWidgets import *
from PyQt6.QtGui import QPixmap
from PyQt6.QtCore import Qt

ROLE_GUEST = "Гость"
ROLE_CLIENT = "Клиент"
ROLE_MANAGER = "Менеджер"
ROLE_ADMIN = "Администратор"


def get_connection():
    try:
        connection = pymysql.connect(
            host="localhost",
            user="root",
            password="root",
            database="building_materials",
            cursorclass=pymysql.cursors.DictCursor
        )
        return connection
    except Exception as e:
        QMessageBox.critical(None, "Ошибка", f"Не удалось подключиться к БД:\n{e}")
        sys.exit(1)


class LoginWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        try:
            uic.loadUi("login.ui", self)
            self.loginButton.clicked.connect(self.login)
            self.guestButton.clicked.connect(self.guest_enter)
        except Exception as e:
            QMessageBox.critical(self, "Ошибка", f"Ошибка загрузки интерфейса:\n{e}")

    def login(self):
        try:
            login = self.loginEdit.text()
            password = self.passwordEdit.text()

            if not login or not password:
                QMessageBox.warning(self, "Ошибка", "Введите логин и пароль")
                return

            connection = get_connection()
            cursor = connection.cursor()

            sql = """
                  SELECT users.full_name,
                         roles.role_name
                  FROM users
                           JOIN roles ON users.role_id = roles.role_id
                  WHERE login = %s \
                    AND password = %s \
                  """

            cursor.execute(sql, (login, password))
            user = cursor.fetchone()
            cursor.close()
            connection.close()

            if user:
                full_name = user["full_name"]
                role = user["role_name"]

                self.products = ProductsWindow(role, full_name, login)
                self.products.show()
                self.close()
            else:
                QMessageBox.warning(self, "Ошибка", "Неверный логин или пароль")
        except Exception as e:
            QMessageBox.critical(self, "Ошибка", f"Ошибка при входе:\n{e}")

    def guest_enter(self):
        self.products = ProductsWindow(ROLE_GUEST, "Гость", None)
        self.products.show()
        self.close()


class ProductEditWindow(QMainWindow):
    def __init__(self, parent=None, product_id=None):
        super().__init__()
        self.parent = parent
        self.product_id = product_id
        self.image_path = None

        try:
            uic.loadUi("product_edit.ui", self)

            self.saveButton.clicked.connect(self.save)
            self.cancelButton.clicked.connect(self.close)
            self.loadImageButton.clicked.connect(self.load_image)

            if product_id:
                self.setWindowTitle("Редактирование товара")
                self.load_product()
            else:
                self.setWindowTitle("Добавление товара")

        except Exception as e:
            QMessageBox.critical(self, "Ошибка", f"Ошибка загрузки интерфейса:\n{e}")

    def load_image(self):
        file_path, _ = QFileDialog.getOpenFileName(
            self, "Выберите фото", "", "Images (*.png *.jpg *.jpeg *.bmp)"
        )
        if file_path:
            self.image_path = file_path
            pixmap = QPixmap(file_path)
            if not pixmap.isNull():
                self.imageLabel.setPixmap(pixmap.scaled(200, 200, Qt.AspectRatioMode.KeepAspectRatio))

    def load_product(self):
        try:
            connection = get_connection()
            cursor = connection.cursor()

            sql = """
                  SELECT product_name, \
                         category_id, \
                         description, \
                         manufacturer_id,
                         supplier_id, \
                         price, \
                         unit, \
                         discount, \
                         stock_quantity, \
                         image_path
                  FROM products \
                  WHERE product_id = %s \
                  """
            cursor.execute(sql, (self.product_id,))
            product = cursor.fetchone()

            if product:
                self.nameEdit.setText(product["product_name"])
                self.descEdit.setPlainText(product["description"] or "")
                self.priceEdit.setText(str(product["price"]))
                self.unitEdit.setText(product["unit"])
                self.discountEdit.setText(str(product["discount"]))
                self.stockEdit.setText(str(product["stock_quantity"]))

                cursor.execute("SELECT category_id, category_name FROM categories")
                categories = cursor.fetchall()
                self.categoryBox.clear()
                for cat in categories:
                    self.categoryBox.addItem(cat["category_name"], cat["category_id"])
                    if cat["category_id"] == product["category_id"]:
                        self.categoryBox.setCurrentIndex(self.categoryBox.count() - 1)

                cursor.execute("SELECT manufacturer_id, manufacturer_name FROM manufacturers")
                manufacturers = cursor.fetchall()
                self.manufacturerBox.clear()
                for man in manufacturers:
                    self.manufacturerBox.addItem(man["manufacturer_name"], man["manufacturer_id"])
                    if man["manufacturer_id"] == product["manufacturer_id"]:
                        self.manufacturerBox.setCurrentIndex(self.manufacturerBox.count() - 1)

                cursor.execute("SELECT supplier_id, supplier_name FROM suppliers")
                suppliers = cursor.fetchall()
                self.supplierBox.clear()
                for sup in suppliers:
                    self.supplierBox.addItem(sup["supplier_name"], sup["supplier_id"])
                    if sup["supplier_id"] == product["supplier_id"]:
                        self.supplierBox.setCurrentIndex(self.supplierBox.count() - 1)

                if product["image_path"] and os.path.exists(product["image_path"]):
                    self.image_path = product["image_path"]
                    pixmap = QPixmap(product["image_path"])
                    self.imageLabel.setPixmap(pixmap.scaled(200, 200, Qt.AspectRatioMode.KeepAspectRatio))

            cursor.close()
            connection.close()

        except Exception as e:
            QMessageBox.critical(self, "Ошибка", f"Ошибка загрузки товара:\n{e}")

    def save(self):
        try:
            if not self.nameEdit.text():
                QMessageBox.warning(self, "Ошибка", "Введите название товара")
                return
            if not self.priceEdit.text():
                QMessageBox.warning(self, "Ошибка", "Введите цену")
                return

            connection = get_connection()
            cursor = connection.cursor()

            category_id = self.categoryBox.currentData()
            manufacturer_id = self.manufacturerBox.currentData()
            supplier_id = self.supplierBox.currentData()

            if self.product_id:
                sql = """
                      UPDATE products \
                      SET product_name=%s, \
                          category_id=%s, \
                          description=%s, \
                          manufacturer_id=%s, \
                          supplier_id=%s, \
                          price=%s, \
                          unit=%s, \
                          discount=%s, \
                          stock_quantity=%s, \
                          image_path=%s
                      WHERE product_id = %s \
                      """
                params = (
                    self.nameEdit.text(), category_id, self.descEdit.toPlainText(),
                    manufacturer_id, supplier_id, float(self.priceEdit.text()),
                    self.unitEdit.text(), int(self.discountEdit.text() or 0),
                    int(self.stockEdit.text() or 0), self.image_path, self.product_id
                )
            else:
                sql = """
                      INSERT INTO products (product_name, category_id, description,
                                            manufacturer_id, supplier_id, price, unit, discount, stock_quantity, \
                                            image_path)
                      VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s) \
                      """
                params = (
                    self.nameEdit.text(), category_id, self.descEdit.toPlainText(),
                    manufacturer_id, supplier_id, float(self.priceEdit.text()),
                    self.unitEdit.text(), int(self.discountEdit.text() or 0),
                    int(self.stockEdit.text() or 0), self.image_path
                )

            cursor.execute(sql, params)
            connection.commit()
            cursor.close()
            connection.close()

            QMessageBox.information(self, "Успех", "Товар сохранён")
            if self.parent:
                self.parent.load_products()
            self.close()

        except Exception as e:
            QMessageBox.critical(self, "Ошибка", f"Ошибка сохранения товара:\n{e}")


class OrderEditWindow(QMainWindow):
    def __init__(self, parent=None, order_id=None):
        super().__init__()
        self.parent = parent
        self.order_id = order_id

        try:
            uic.loadUi("order_edit.ui", self)

            self.saveButton.clicked.connect(self.save)
            self.cancelButton.clicked.connect(self.close)

            if order_id:
                self.setWindowTitle("Редактирование заказа")
                self.load_order()
            else:
                self.setWindowTitle("Добавление заказа")
                self.load_clients()

        except Exception as e:
            QMessageBox.critical(self, "Ошибка", f"Ошибка загрузки интерфейса:\n{e}")

    def load_clients(self):
        try:
            connection = get_connection()
            cursor = connection.cursor()
            cursor.execute("SELECT user_id, full_name FROM users")
            clients = cursor.fetchall()
            self.clientBox.clear()
            for client in clients:
                self.clientBox.addItem(client["full_name"], client["user_id"])
            cursor.close()
            connection.close()
        except Exception as e:
            QMessageBox.critical(self, "Ошибка", f"Ошибка загрузки клиентов:\n{e}")

    def load_order(self):
        try:
            connection = get_connection()
            cursor = connection.cursor()

            sql = "SELECT order_id, order_date, delivery_date, pickup_address, client_id, code, status FROM orders WHERE order_id=%s"
            cursor.execute(sql, (self.order_id,))
            order = cursor.fetchone()

            if order:
                self.load_clients()
                self.dateEdit.setDate(datetime.strptime(str(order["order_date"]), "%Y-%m-%d").date())
                self.deliveryDateEdit.setDate(datetime.strptime(str(order["delivery_date"]), "%Y-%m-%d").date())
                self.addressEdit.setText(order["pickup_address"])
                self.codeEdit.setText(order["code"])
                self.statusEdit.setText(order["status"])

                for i in range(self.clientBox.count()):
                    if self.clientBox.itemData(i) == order["client_id"]:
                        self.clientBox.setCurrentIndex(i)
                        break

            cursor.close()
            connection.close()

        except Exception as e:
            QMessageBox.critical(self, "Ошибка", f"Ошибка загрузки заказа:\n{e}")

    def save(self):
        try:
            connection = get_connection()
            cursor = connection.cursor()

            if self.order_id:
                sql = """
                      UPDATE orders \
                      SET order_date=%s, \
                          delivery_date=%s, \
                          pickup_address=%s, \
                          client_id=%s, \
                          code=%s, \
                          status=%s
                      WHERE order_id = %s \
                      """
                params = (
                    self.dateEdit.date().toPyDate(), self.deliveryDateEdit.date().toPyDate(),
                    self.addressEdit.text(), self.clientBox.currentData(),
                    self.codeEdit.text(), self.statusEdit.text(), self.order_id
                )
            else:
                sql = """
                      INSERT INTO orders (order_date, delivery_date, pickup_address, client_id, code, status)
                      VALUES (%s, %s, %s, %s, %s, %s) \
                      """
                params = (
                    self.dateEdit.date().toPyDate(), self.deliveryDateEdit.date().toPyDate(),
                    self.addressEdit.text(), self.clientBox.currentData(),
                    self.codeEdit.text(), self.statusEdit.text()
                )

            cursor.execute(sql, params)
            connection.commit()
            cursor.close()
            connection.close()

            QMessageBox.information(self, "Успех", "Заказ сохранён")
            if self.parent:
                self.parent.load_orders()
            self.close()

        except Exception as e:
            QMessageBox.critical(self, "Ошибка", f"Ошибка сохранения заказа:\n{e}")


class OrdersWindow(QMainWindow):
    def __init__(self, role):
        super().__init__()
        self.role = role

        try:
            uic.loadUi("orders.ui", self)

            self.backButton.clicked.connect(self.go_back)
            self.load_orders()

            if self.role == ROLE_ADMIN:
                self.addButton.setVisible(True)
                self.editButton.setVisible(True)
                self.deleteButton.setVisible(True)
                self.addButton.clicked.connect(self.add_order)
                self.editButton.clicked.connect(self.edit_order)
                self.deleteButton.clicked.connect(self.delete_order)
            else:
                self.addButton.setVisible(False)
                self.editButton.setVisible(False)
                self.deleteButton.setVisible(False)

        except Exception as e:
            QMessageBox.critical(self, "Ошибка", f"Ошибка загрузки интерфейса:\n{e}")

    def go_back(self):
        self.close()

    def load_orders(self):
        try:
            connection = get_connection()
            cursor = connection.cursor()

            sql = """
                  SELECT order_id, \
                         code, \
                         order_date, \
                         delivery_date, \
                         status
                  FROM orders \
                  """

            cursor.execute(sql)
            orders = cursor.fetchall()
            cursor.close()
            connection.close()

            self.ordersList.clear()

            for order in orders:
                text = (
                    f"Заказ № {order['order_id']} | "
                    f"Дата заказа: {order['order_date']} | "
                    f"Дата доставки: {order['delivery_date']} | "
                    f"Код: {order['code']} | "
                    f"Статус: {order['status']}"
                )
                self.ordersList.addItem(text)

        except Exception as e:
            QMessageBox.critical(self, "Ошибка", f"Ошибка загрузки заказов:\n{e}")

    def add_order(self):
        self.order_edit = OrderEditWindow(self)
        self.order_edit.show()

    def edit_order(self):
        current_item = self.ordersList.currentItem()
        if not current_item:
            QMessageBox.warning(self, "Ошибка", "Выберите заказ для редактирования")
            return

        text = current_item.text()
        order_id = int(text.split("№")[1].split("|")[0].strip())
        self.order_edit = OrderEditWindow(self, order_id)
        self.order_edit.show()

    def delete_order(self):
        current_item = self.ordersList.currentItem()
        if not current_item:
            QMessageBox.warning(self, "Ошибка", "Выберите заказ для удаления")
            return

        reply = QMessageBox.question(self, "Подтверждение", "Удалить заказ?",
                                     QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)

        if reply == QMessageBox.StandardButton.Yes:
            try:
                text = current_item.text()
                order_id = int(text.split("№")[1].split("|")[0].strip())

                connection = get_connection()
                cursor = connection.cursor()

                cursor.execute("DELETE FROM order_items WHERE order_id=%s", (order_id,))
                cursor.execute("DELETE FROM orders WHERE order_id=%s", (order_id,))

                connection.commit()
                cursor.close()
                connection.close()

                QMessageBox.information(self, "Успех", "Заказ удалён")
                self.load_orders()

            except Exception as e:
                QMessageBox.critical(self, "Ошибка", f"Ошибка удаления заказа:\n{e}")


class ProductsWindow(QMainWindow):
    def __init__(self, role, full_name, login):
        super().__init__()

        try:
            uic.loadUi("products.ui", self)

            self.searchEdit.setVisible(False)
            self.filterBox.setVisible(False)
            self.sortBox.setVisible(False)
            self.ordersButton.setVisible(False)
            self.addButton.setVisible(False)
            self.editButton.setVisible(False)
            self.deleteButton.setVisible(False)

            self.role = role
            self.full_name = full_name
            self.login = login

            self.labelUser.setText(full_name)

            self.container = QWidget()
            self.scrollArea.setWidget(self.container)
            self.scrollArea.setWidgetResizable(True)

            self.verticalLayout = QVBoxLayout(self.container)

            self.load_suppliers()
            self.load_products()
            self.setup_role()

            self.backButton.clicked.connect(self.go_back)
            self.ordersButton.clicked.connect(self.open_orders)
            self.addButton.clicked.connect(self.add_product)
            self.editButton.clicked.connect(self.edit_product)
            self.deleteButton.clicked.connect(self.delete_product)

            self.searchEdit.textChanged.connect(self.load_products)
            self.filterBox.currentTextChanged.connect(self.load_products)
            self.sortBox.currentTextChanged.connect(self.load_products)

        except Exception as e:
            QMessageBox.critical(self, "Ошибка", f"Ошибка загрузки интерфейса:\n{e}")

    def load_products(self):
        try:
            for i in reversed(range(self.verticalLayout.count())):
                widget = self.verticalLayout.itemAt(i).widget()
                if widget:
                    widget.setParent(None)

            search = self.searchEdit.text()
            supplier = self.filterBox.currentText()
            sort = self.sortBox.currentText()

            sql = """
                  SELECT p.product_id, \
                         p.product_name, \
                         c.category_name, \
                         p.description, \
                         m.manufacturer_name, \
                         s.supplier_name, \
                         p.price, \
                         p.unit, \
                         p.discount, \
                         p.stock_quantity, \
                         p.image_path
                  FROM products p
                           JOIN categories c ON p.category_id = c.category_id
                           JOIN manufacturers m ON p.manufacturer_id = m.manufacturer_id
                           JOIN suppliers s ON p.supplier_id = s.supplier_id
                  WHERE (p.product_name LIKE %s
                      OR c.category_name LIKE %s
                      OR m.manufacturer_name LIKE %s
                      OR s.supplier_name LIKE %s) \
                  """

            params = [
                f"%{search}%",
                f"%{search}%",
                f"%{search}%",
                f"%{search}%"
            ]

            if supplier != "Все поставщики":
                sql += " AND s.supplier_name=%s"
                params.append(supplier)

            if sort == "По возрастанию":
                sql += " ORDER BY p.stock_quantity ASC"
            elif sort == "По убыванию":
                sql += " ORDER BY p.stock_quantity DESC"

            connection = get_connection()
            cursor = connection.cursor()
            cursor.execute(sql, params)
            products = cursor.fetchall()
            cursor.close()
            connection.close()

            for product in products:
                card = self.create_product_card(product)
                self.verticalLayout.addWidget(card)

        except Exception as e:
            QMessageBox.critical(self, "Ошибка", f"Ошибка загрузки товаров:\n{e}")

    def create_product_card(self, product):
        card = QWidget()
        card.setProperty("product_data", product)
        card.setCursor(Qt.CursorShape.PointingHandCursor)

        card_layout = QHBoxLayout(card)

        image_label = QLabel()
        image_label.setFixedSize(120, 120)

        if product["image_path"]:
            pixmap = QPixmap(product["image_path"])
            if not pixmap.isNull():
                image_label.setPixmap(pixmap.scaled(120, 120, Qt.AspectRatioMode.KeepAspectRatio))
            else:
                image_label.setText("Нет фото")
                image_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
        else:
            image_label.setText("Нет фото")
            image_label.setAlignment(Qt.AlignmentFlag.AlignCenter)

        info_text = (
            f"ID: {product['product_id']}\n"
            f"Категория: {product['category_name']}\n"
            f"Название: {product['product_name']}\n"
            f"Описание: {product['description']}\n"
            f"Производитель: {product['manufacturer_name']}\n"
            f"Поставщик: {product['supplier_name']}\n"
            f"Цена: {product['price']} руб.\n"
            f"Единица измерения: {product['unit']}\n"
            f"Количество: {product['stock_quantity']}"
        )

        info_label = QLabel(info_text)
        info_label.setWordWrap(True)

        discount_label = QLabel(str(product["discount"]) + "%")

        card_layout.addWidget(image_label)
        card_layout.addWidget(info_label)
        card_layout.addWidget(discount_label)

        card.setStyleSheet("QWidget {border: 1px solid black; margin: 5px;}")
        discount_label.setAlignment(Qt.AlignmentFlag.AlignCenter)

        return card

    def setup_role(self):
        if self.role == ROLE_GUEST:
            self.labelUser.setText("Вы вошли как Гость")

        elif self.role == ROLE_CLIENT:
            self.labelUser.setText(self.full_name)

        elif self.role == ROLE_MANAGER:
            self.labelUser.setText(f"{self.full_name} (Менеджер)")
            self.enable_manager_features()

        elif self.role == ROLE_ADMIN:
            self.labelUser.setText(f"{self.full_name} (Админ)")
            self.enable_manager_features()
            self.enable_admin_features()

    def enable_manager_features(self):
        self.searchEdit.setVisible(True)
        self.filterBox.setVisible(True)
        self.sortBox.setVisible(True)
        self.ordersButton.setVisible(True)

    def enable_admin_features(self):
        self.addButton.setVisible(True)
        self.editButton.setVisible(True)
        self.deleteButton.setVisible(True)

    def go_back(self):
        self.login = LoginWindow()
        self.login.show()
        self.close()

    def open_orders(self):
        self.orders_window = OrdersWindow(self.role)
        self.orders_window.show()

    def load_suppliers(self):
        try:
            connection = get_connection()
            cursor = connection.cursor()

            sql = "SELECT supplier_name FROM suppliers"
            cursor.execute(sql)
            suppliers = cursor.fetchall()
            cursor.close()
            connection.close()

            self.filterBox.clear()
            self.filterBox.addItem("Все поставщики")

            for supplier in suppliers:
                self.filterBox.addItem(supplier["supplier_name"])

        except Exception as e:
            QMessageBox.critical(self, "Ошибка", f"Ошибка загрузки поставщиков:\n{e}")

    def add_product(self):
        self.product_edit = ProductEditWindow(self)
        self.product_edit.show()

    def edit_product(self):
        for i in range(self.verticalLayout.count()):
            widget = self.verticalLayout.itemAt(i).widget()
            if widget:
                product_data = widget.property("product_data")
                if product_data:
                    self.product_edit = ProductEditWindow(self, product_data["product_id"])
                    self.product_edit.show()
                    return

        QMessageBox.warning(self, "Ошибка", "Нажмите на карточку товара для редактирования")

    def delete_product(self):
        for i in range(self.verticalLayout.count()):
            widget = self.verticalLayout.itemAt(i).widget()
            if widget:
                product_data = widget.property("product_data")
                if product_data:
                    reply = QMessageBox.question(self, "Подтверждение", "Удалить товар?",
                                                 QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
                    if reply == QMessageBox.StandardButton.Yes:
                        try:
                            connection = get_connection()
                            cursor = connection.cursor()

                            cursor.execute("DELETE FROM order_items WHERE product_id=%s", (product_data["product_id"],))
                            cursor.execute("DELETE FROM products WHERE product_id=%s", (product_data["product_id"],))

                            connection.commit()
                            cursor.close()
                            connection.close()

                            QMessageBox.information(self, "Успех", "Товар удалён")
                            self.load_products()
                        except Exception as e:
                            QMessageBox.critical(self, "Ошибка", f"Ошибка удаления товара:\n{e}")
                    return

        QMessageBox.warning(self, "Ошибка", "Нажмите на карточку товара для удаления")


app = QApplication(sys.argv)
window = LoginWindow()
window.show()
sys.exit(app.exec())