After Graduate Update

This commit is contained in:
Daniel 2025-11-26 19:31:33 +03:00
parent b92a91ab37
commit c6917dd85e
69 changed files with 7540 additions and 0 deletions

View file

@ -0,0 +1,2 @@
**/__pycache__/
.venv/

3
robbery/master_pol-module_1_2/.idea/.gitignore generated vendored Normal file
View file

@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

View file

@ -0,0 +1 @@
main.py

View file

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.venv" />
</content>
<orderEntry type="jdk" jdkName="Python 3.11 virtualenv at C:\Users\student\Desktop\master_pol-module_1_2\.venv" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Black">
<option name="sdkName" value="Python 3.11 virtualenv at C:\Users\student\Desktop\master_pol-module_1_2\.venv" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.11 virtualenv at C:\Users\student\Desktop\master_pol-module_1_2\.venv" project-jdk-type="Python SDK" />
</project>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/master_pol-module_1_2.iml" filepath="$PROJECT_DIR$/.idea/master_pol-module_1_2.iml" />
</modules>
</component>
</project>

View file

@ -0,0 +1,55 @@
# MasterPol
Графическое приложение на PyQt6 для работы с базой данных MySQL.
## Подготовка проекта
1. **Клонируйте репозиторий и перейдите в папку проекта:**
```sh
git clone <адрес-репозитория>
cd master_pol
```
2. **Создайте и активируйте виртуальное окружение:**
```sh
python -m venv .venv
.venv\Scripts\activate # Windows
# source .venv/bin/activate # Linux/MacOS
```
3. **Установите зависимости:**
```sh
pip install -r requirements.txt
```
4. **Создайте базу данных и выполните SQL-скрипт:**
- Запустите MySQL и выполните скрипт `app/database/script.sql` для создания необходимых таблиц и данных:
```sh
mysql -u <user> -p <db_name> < app/database/script.sql
```
- Замените `<user>` и `<db_name>` на свои значения.
5. **Проверьте параметры подключения к базе данных:**
- Откройте файл `app/database/db.py` и убедитесь, что значения для подключения (host, user, password, database) указаны верно.
## Запуск приложения
```sh
python app/main.py
```
## Структура проекта
- `app/main.py` — точка входа, запуск приложения
- `app/components/` — компоненты интерфейса
- `app/database/` — работа с БД, скрипты и настройки
- `app/pages/` — страницы приложения
- `app/res/` — ресурсы (цвета, шрифты)
---

View file

@ -0,0 +1,108 @@
from PyQt6.QtWidgets import (
QDialog,
QVBoxLayout,
QFormLayout,
QLineEdit,
QPushButton,
QComboBox,
QSpinBox,
QMessageBox,
)
from PyQt6.QtCore import Qt
from res.colors import ACCENT_COLOR
from dto.partners_dto import PartnerUpdateDto, PartnersInfo
class EditPartnerDialog(QDialog):
def __init__(self, partner_data: PartnersInfo, parent=None):
super().__init__(parent)
self.partner_data = partner_data
self.setup_ui()
self.load_partner_types()
self.fill_form()
self.result = None
def setup_ui(self):
self.setWindowTitle("Редактирование партнера")
self.setFixedSize(500, 400)
layout = QVBoxLayout()
form_layout = QFormLayout()
# Создаем поля формы
self.partner_type = QComboBox()
self.partner_name = QLineEdit()
self.first_name = QLineEdit()
self.last_name = QLineEdit()
self.middle_name = QLineEdit()
self.email = QLineEdit()
self.phone = QLineEdit()
self.address = QLineEdit()
self.inn = QLineEdit()
self.rating = QSpinBox()
self.rating.setRange(0, 10)
# Добавляем поля в форму
form_layout.addRow("Тип партнера:", self.partner_type)
form_layout.addRow("Название:", self.partner_name)
form_layout.addRow("Имя директора:", self.first_name)
form_layout.addRow("Фамилия директора:", self.last_name)
form_layout.addRow("Отчество директора:", self.middle_name)
form_layout.addRow("Email:", self.email)
form_layout.addRow("Телефон:", self.phone)
form_layout.addRow("Адрес:", self.address)
form_layout.addRow("ИНН:", self.inn)
form_layout.addRow("Рейтинг:", self.rating)
# Кнопки
self.save_button = QPushButton("Сохранить")
self.cancel_button = QPushButton("Отмена")
self.save_button.clicked.connect(self.save_changes)
self.cancel_button.clicked.connect(self.reject)
layout.addLayout(form_layout)
layout.addWidget(self.save_button)
layout.addWidget(self.cancel_button)
self.setLayout(layout)
# Стили
self.setStyleSheet(
f"""
QPushButton {{
background-color: {ACCENT_COLOR};
padding: 8px;
border-radius: 4px;
}}
"""
)
def load_partner_types(self):
types = ['ООО', "ЗАО"]
for i, val in enumerate(types):
self.partner_type.addItem(val, i + 1)
def fill_form(self):
pass
def save_changes(self):
try:
partner_data = PartnerUpdateDto(
id=self.partner_data.id,
partner_type_id=self.partner_type.currentData(),
partner_name=self.partner_name.text(),
first_name=self.first_name.text(),
last_name=self.last_name.text(),
middle_name=self.middle_name.text(),
email=self.email.text(),
phone=self.phone.text(),
address=self.address.text(),
inn=self.inn.text(),
rating=self.rating.value(),
)
db.update_partner(partner_data)
self.accept()
except Exception as e:
QMessageBox.critical(
self, "Ошибка", f"Не удалось сохранить изменения: {str(e)}"
)

View file

@ -0,0 +1,94 @@
from dataclasses import dataclass
from PyQt6.QtWidgets import QWidget, QLabel, QVBoxLayout, QHBoxLayout, QFrame
from PyQt6.QtCore import Qt, pyqtSignal
from res.colors import ACCENT_COLOR, SECONDARY_COLOR
from res.fonts import MAIN_FONT
from dto.partners_dto import PartnersInfo
class PartnerCard(QFrame):
doubleClicked = pyqtSignal(PartnersInfo)
def __init__(self, info: PartnersInfo):
super().__init__()
self.info = info
self.init_ui()
self.set_styles()
def mouseDoubleClickEvent(self, a0):
self.doubleClicked.emit(self.info)
return super().mouseDoubleClickEvent(a0)
def init_ui(self):
main_layout = QVBoxLayout()
self.setLayout(main_layout)
# Верхняя строка: Тип | Наименование и скидка
header_layout = QHBoxLayout()
header_text = QLabel(f"{self.info.type_name} | {self.info.partner_name}")
header_text.setObjectName("partnerHeader")
discount_text = QLabel(f"{self.info.discount}%")
discount_text.setObjectName("partnerDiscount")
header_layout.addWidget(header_text)
header_layout.addWidget(discount_text, alignment=Qt.AlignmentFlag.AlignRight)
# Информация о директоре
director_text = QLabel(f"Директор")
director_text.setObjectName("fieldLabel")
director_name = QLabel(
f"{self.info.last_name_director} {self.info.first_name_director} {self.info.middle_name_director}"
)
# Контактная информация
phone_text = QLabel(f"+{self.info.phone_partner}")
# Рейтинг
rating_layout = QHBoxLayout()
rating_label = QLabel("Рейтинг:")
rating_label.setObjectName("fieldLabel")
rating_value = QLabel(str(self.info.rating))
rating_layout.addWidget(rating_label)
rating_layout.addWidget(rating_value)
rating_layout.addStretch()
# Добавляем все элементы в главный layout
main_layout.addLayout(header_layout)
main_layout.addWidget(director_text)
main_layout.addWidget(director_name)
main_layout.addWidget(phone_text)
main_layout.addLayout(rating_layout)
def set_styles(self):
self.setStyleSheet(
"""
PartnerCard {
border: 1px solid #ccc;
border-radius: 4px;
padding: 10px;
margin: 5px;
background-color: white;
}
QLabel {
font-family: %s;
}
#partnerHeader {
font-size: 18px;
font-weight: bold;
color: %s;
}
#partnerDiscount {
font-size: 18px;
font-weight: bold;
color: %s;
}
#fieldLabel {
color: gray;
font-size: 14px;
}
"""
% (MAIN_FONT, ACCENT_COLOR, SECONDARY_COLOR)
)

View file

@ -0,0 +1,84 @@
import pymysql as psql
from dto.partners_dto import PartnerUpdateDto
class Database:
def __init__(self, host, user, password, db):
self.connection = psql.connect(
host=host,
user=user,
password=password,
database=db,
cursorclass=psql.cursors.DictCursor,
)
def authorize_user(self, username, password):
query = "SELECT * FROM users WHERE username=%s AND password=%s"
with self.connection.cursor() as cur:
cur.execute(query, (username, password))
result = cur.fetchone()
return result is not None
def execute_select(self, query, params=None):
"""Выполняет SELECT запрос и возвращает результаты"""
with self.connection.cursor() as cur:
if params:
cur.execute(query, params)
else:
cur.execute(query)
return cur.fetchall()
def get_partner_types(self):
"""Получает все типы партнеров из таблицы partner_types"""
query = "SELECT * FROM partners_type"
with self.connection.cursor() as cur:
cur.execute(query)
return cur.fetchall()
def update_partner(self, partners_info: PartnerUpdateDto):
with self.connection.cursor() as cur:
cur.callproc(
"upd_partner",
(
partners_info.partner_type_id,
partners_info.id,
partners_info.partner_name,
partners_info.first_name,
partners_info.last_name,
partners_info.middle_name,
partners_info.email,
partners_info.phone,
partners_info.address,
partners_info.inn,
partners_info.rating,
),
)
self.connection.commit()
def get_disc(self, partner_name):
"""
Получает скидку для партнера, вызывая функцию get_disc из БД
"""
# Сначала получим ID партнера по его имени
query = "SELECT id FROM partners WHERE partner_name = %s"
with self.connection.cursor() as cur:
cur.execute(query, (partner_name,))
result = cur.fetchone()
if not result:
return 0
# Вызываем функцию get_disc из БД
query = "SELECT get_disc(%s) as discount"
cur.execute(query, (result["id"],))
discount_result = cur.fetchone()
return discount_result["discount"] if discount_result else 0
db = None
try:
db = Database(host="localhost", user="root", password="", db="master_pol")
print("Database connection established.")
except psql.MySQLError as e:
print(f"Error connecting to database: {e}")

View file

@ -0,0 +1,460 @@
CREATE DATABASE master_pol;
use master_pol;
CREATE TABLE `partners` (
`id` INTEGER NOT NULL AUTO_INCREMENT UNIQUE,
`partner_type_id` INTEGER NOT NULL,
`partner_name` VARCHAR(255) NOT NULL,
`first_name_director` VARCHAR(50) NOT NULL,
`last_name_director` VARCHAR(50) NOT NULL,
`middle_name_director` VARCHAR(255),
`email_partner` VARCHAR(100) NOT NULL,
`phone_partner` VARCHAR(15) NOT NULL,
`address` VARCHAR(255) NOT NULL,
`INN` VARCHAR(10) NOT NULL,
`rating` INTEGER NOT NULL,
`logo` LONGBLOB,
PRIMARY KEY(`id`)
);
CREATE TABLE `partners_type` (
`id` INTEGER NOT NULL AUTO_INCREMENT UNIQUE,
`name` VARCHAR(255),
PRIMARY KEY(`id`)
);
CREATE TABLE `products` (
`id` INTEGER NOT NULL AUTO_INCREMENT UNIQUE,
`article` VARCHAR(10) NOT NULL,
`name` VARCHAR(100) NOT NULL,
`product_type_id` INTEGER NOT NULL,
`description` VARCHAR(255),
`picture` LONGBLOB,
`min_price_partners` DECIMAL(10,2) NOT NULL,
`cert_quality` LONGBLOB,
`standard_number` VARCHAR(255),
`selfcost` DECIMAL(10,2),
`length` DECIMAL(10,2),
`width` DECIMAL(10,2),
`height` DECIMAL(10,2),
`weight_no_package` DECIMAL(10,2),
`weight_with_package` DECIMAL(10,2),
`time_to_create_min` INTEGER,
`workshop_number` INTEGER,
`people_count_production` INTEGER,
`product_current_stock` INTEGER NOT NULL DEFAULT 0,
PRIMARY KEY(`id`)
);
CREATE TABLE `products_types` (
`id` INTEGER NOT NULL AUTO_INCREMENT UNIQUE,
`name` VARCHAR(70) NOT NULL,
`coefficent` DECIMAL(3,2) NOT NULL,
PRIMARY KEY(`id`)
);
CREATE TABLE `product_partners` (
`id` INTEGER NOT NULL AUTO_INCREMENT UNIQUE,
`product_id` INTEGER NOT NULL,
`partner_id` INTEGER NOT NULL,
`amount` INTEGER NOT NULL,
`sale_date` DATE NOT NULL,
PRIMARY KEY(`id`)
);
CREATE TABLE `employees` (
`id` INTEGER NOT NULL AUTO_INCREMENT UNIQUE,
`employee_type_id` INTEGER NOT NULL,
`first_name` VARCHAR(50) NOT NULL,
`last_name` VARCHAR(50) NOT NULL,
`middle_name` VARCHAR(60) NULL,
`birth_date` DATE NOT NULL,
`passport_data` VARCHAR(11) NOT NULL,
`bank_details` VARCHAR(100) NOT NULL,
`has_family` BOOLEAN,
`health_status` VARCHAR(25),
PRIMARY KEY(`id`)
);
CREATE TABLE `employees_types` (
`id` INTEGER NOT NULL AUTO_INCREMENT UNIQUE,
`name` VARCHAR(50) NOT NULL,
PRIMARY KEY(`id`)
);
CREATE TABLE `users` (
`id` INTEGER NOT NULL AUTO_INCREMENT UNIQUE,
`username` VARCHAR(30) NOT NULL,
`password` VARCHAR(80) NOT NULL,
`employee_id` INTEGER NOT NULL,
PRIMARY KEY(`id`)
);
CREATE TABLE `materials` (
`id` INTEGER NOT NULL AUTO_INCREMENT UNIQUE,
`material_type_id` INTEGER NOT NULL,
`supplier_id` INTEGER NOT NULL,
`name` VARCHAR(60) NOT NULL,
`package_quantity` INTEGER NOT NULL,
`unit` VARCHAR(20) NOT NULL,
`cost` DECIMAL(8,2) NOT NULL,
`image` LONGBLOB,
`min_stock` INTEGER,
`material_current_stock` INTEGER NOT NULL DEFAULT 0,
PRIMARY KEY(`id`)
);
CREATE TABLE `materials_type` (
`id` INTEGER NOT NULL AUTO_INCREMENT UNIQUE,
`name` VARCHAR(50) NOT NULL,
`defect_percent` DECIMAL(10,2) NOT NULL,
PRIMARY KEY(`id`)
);
CREATE TABLE `products_recipes` (
`id` INTEGER NOT NULL AUTO_INCREMENT UNIQUE,
`product_id` INTEGER NOT NULL,
`material_id` INTEGER NOT NULL,
`material_count` INTEGER NOT NULL,
PRIMARY KEY(`id`)
);
CREATE TABLE `partners_rating_history` (
`id` INTEGER NOT NULL AUTO_INCREMENT UNIQUE,
`partner_id` INTEGER NOT NULL,
`new_rating` INTEGER NOT NULL,
`changed` DATETIME NOT NULL,
PRIMARY KEY(`id`)
);
CREATE TABLE `orders` (
`id` INTEGER NOT NULL AUTO_INCREMENT UNIQUE,
`partner_id` INTEGER NOT NULL,
`manager_id` INTEGER NOT NULL,
`total_price` DECIMAL(10,2) NOT NULL,
`order_payment` DECIMAL(10,2) NOT NULL DEFAULT 0,
`created` DATETIME NOT NULL,
`status` ENUM('created', 'waiting prepayment', 'prepayment received', 'completed', 'canceled', 'ready for shipment', 'pending', 'in production') NOT NULL,
`prepayment_date` DATETIME,
`payment_date` DATETIME,
PRIMARY KEY(`id`)
);
CREATE TABLE `products_orders` (
`id` INTEGER NOT NULL AUTO_INCREMENT UNIQUE,
`order_id` INTEGER NOT NULL,
`product_id` INTEGER NOT NULL,
`quantity` INTEGER NOT NULL,
`agreed_price_per` DECIMAL(8,2),
`production_date` DATE,
PRIMARY KEY(`id`)
);
CREATE TABLE `suppliers` (
`id` INTEGER NOT NULL AUTO_INCREMENT UNIQUE,
`name` VARCHAR(50) NOT NULL,
`INN` VARCHAR(10) NOT NULL,
PRIMARY KEY(`id`)
);
CREATE TABLE `materials_supply_history` (
`id` INTEGER NOT NULL AUTO_INCREMENT UNIQUE,
`material_id` INTEGER NOT NULL,
`supplier_id` INTEGER NOT NULL,
`quantity` INTEGER NOT NULL,
`delivery_date` DATE NOT NULL,
PRIMARY KEY(`id`)
);
CREATE TABLE `materials_movement` (
`id` INTEGER NOT NULL AUTO_INCREMENT UNIQUE,
`material_id` INTEGER NOT NULL,
`amount` INTEGER NOT NULL,
`movement_type` ENUM('incoming', 'reserve', 'write off') NOT NULL DEFAULT 'incoming',
`movement_date` DATETIME NOT NULL,
PRIMARY KEY(`id`)
);
CREATE TABLE `employees_access` (
`id` INTEGER NOT NULL AUTO_INCREMENT UNIQUE,
`employee_id` INTEGER NOT NULL,
`door_id` INTEGER NOT NULL,
`access_date` DATETIME NOT NULL,
PRIMARY KEY(`id`)
);
ALTER TABLE `partners`
ADD FOREIGN KEY(`partner_type_id`) REFERENCES `partners_type`(`id`)
ON UPDATE NO ACTION ON DELETE NO ACTION;
ALTER TABLE `products`
ADD FOREIGN KEY(`product_type_id`) REFERENCES `products_types`(`id`)
ON UPDATE NO ACTION ON DELETE NO ACTION;
ALTER TABLE `product_partners`
ADD FOREIGN KEY(`product_id`) REFERENCES `products`(`id`)
ON UPDATE NO ACTION ON DELETE NO ACTION;
ALTER TABLE `product_partners`
ADD FOREIGN KEY(`partner_id`) REFERENCES `partners`(`id`)
ON UPDATE NO ACTION ON DELETE NO ACTION;
ALTER TABLE `employees`
ADD FOREIGN KEY(`employee_type_id`) REFERENCES `employees_types`(`id`)
ON UPDATE NO ACTION ON DELETE NO ACTION;
ALTER TABLE `users`
ADD FOREIGN KEY(`employee_id`) REFERENCES `employees`(`id`)
ON UPDATE NO ACTION ON DELETE NO ACTION;
ALTER TABLE `materials`
ADD FOREIGN KEY(`material_type_id`) REFERENCES `materials_type`(`id`)
ON UPDATE NO ACTION ON DELETE NO ACTION;
ALTER TABLE `products_recipes`
ADD FOREIGN KEY(`product_id`) REFERENCES `products`(`id`)
ON UPDATE NO ACTION ON DELETE NO ACTION;
ALTER TABLE `products_recipes`
ADD FOREIGN KEY(`material_id`) REFERENCES `materials`(`id`)
ON UPDATE NO ACTION ON DELETE NO ACTION;
ALTER TABLE `partners_rating_history`
ADD FOREIGN KEY(`partner_id`) REFERENCES `partners`(`id`)
ON UPDATE NO ACTION ON DELETE NO ACTION;
ALTER TABLE `orders`
ADD FOREIGN KEY(`partner_id`) REFERENCES `partners`(`id`)
ON UPDATE NO ACTION ON DELETE NO ACTION;
ALTER TABLE `orders`
ADD FOREIGN KEY(`manager_id`) REFERENCES `employees`(`id`)
ON UPDATE NO ACTION ON DELETE NO ACTION;
ALTER TABLE `products_orders`
ADD FOREIGN KEY(`order_id`) REFERENCES `orders`(`id`)
ON UPDATE NO ACTION ON DELETE NO ACTION;
ALTER TABLE `products_orders`
ADD FOREIGN KEY(`product_id`) REFERENCES `products`(`id`)
ON UPDATE NO ACTION ON DELETE NO ACTION;
ALTER TABLE `materials`
ADD FOREIGN KEY(`supplier_id`) REFERENCES `suppliers`(`id`)
ON UPDATE NO ACTION ON DELETE NO ACTION;
ALTER TABLE `materials_supply_history`
ADD FOREIGN KEY(`material_id`) REFERENCES `materials`(`id`)
ON UPDATE NO ACTION ON DELETE NO ACTION;
ALTER TABLE `materials_supply_history`
ADD FOREIGN KEY(`supplier_id`) REFERENCES `suppliers`(`id`)
ON UPDATE NO ACTION ON DELETE NO ACTION;
ALTER TABLE `materials_movement`
ADD FOREIGN KEY(`material_id`) REFERENCES `materials`(`id`)
ON UPDATE NO ACTION ON DELETE NO ACTION;
ALTER TABLE `employees_access`
ADD FOREIGN KEY(`employee_id`) REFERENCES `employees`(`id`)
ON UPDATE NO ACTION ON DELETE NO ACTION;
INSERT INTO materials_type (name, defect_percent) VALUES
('Тип материала 1', 0.001),
('Тип материала 2', 0.0095),
('Тип материала 3', 0.0028),
('Тип материала 4', 0.0055),
('Тип материала 5', 0.0034);
INSERT INTO products_types (name, coefficent) VALUES
('Ламинат', 2.35),
('Массивная доска', 5.15),
('Паркетная доска', 4.34),
('Пробковое покрытие', 1.5);
INSERT INTO partners_type (name) VALUES
('ЗАО'),
('ООО'),
('ПАО'),
('ОАО');
INSERT INTO partners (partner_type_id, partner_name, first_name_director, last_name_director, middle_name_director, email_partner, phone_partner, address, INN, rating) VALUES
(1, 'База Строитель', 'Александра', 'Иванова', 'Ивановна', 'aleksandraivanova@ml.ru', '4931234567', '652050, Кемеровская область, город Юрга, ул. Лесная, 15', '2222455179', 7),
(2, 'Паркет 29', 'Василий', 'Петров', 'Петрович', 'vppetrov@vl.ru', '9871235678', '164500, Архангельская область, город Северодвинск, ул. Строителей, 18', '3333888520', 7),
(3, 'Стройсервис', 'Андрей', 'Соловьев', 'Николаевич', 'ansolovev@st.ru', '8122233200', '188910, Ленинградская область, город Приморск, ул. Парковая, 21', '4440391035', 7),
(4, 'Ремонт и отделка', 'Екатерина', 'Воробьева', 'Валерьевна', 'ekaterina.vorobeva@ml.ru', '4442223311', '143960, Московская область, город Реутов, ул. Свободы, 51', '1111520857', 5),
(1, 'МонтажПро', 'Степан', 'Степанов', 'Сергеевич', 'stepanov@stepan.ru', '9128883333', '309500, Белгородская область, город Старый Оскол, ул. Рабочая, 122', '5552431140', 10);
INSERT INTO products (article, name, product_type_id, min_price_partners) VALUES
('8758385', 'Паркетная доска Ясень темный однополосная 14 мм', 3, 4456.90),
('8858958', 'Инженерная доска Дуб Французская елка однополосная 12 мм', 3, 7330.99),
('7750282', 'Ламинат Дуб дымчато-белый 33 класс 12 мм', 1, 1799.33),
('7028748', 'Ламинат Дуб серый 32 класс 8 мм с фаской', 1, 3890.41),
('5012543', 'Пробковое напольное клеевое покрытие 32 класс 4 мм', 4, 5450.59);
INSERT INTO product_partners (product_id, partner_id, amount, sale_date) VALUES
(1, 1, 15500, '2023-03-23'),
(3, 1, 12350, '2023-12-18'),
(4, 1, 37400, '2024-06-07'),
(2, 2, 35000, '2022-12-02'),
(5, 2, 1250, '2023-05-17'),
(3, 2, 1000, '2024-06-07'),
(1, 2, 7550, '2024-07-01'),
(1, 3, 7250, '2023-01-22'),
(2, 3, 2500, '2024-07-05'),
(4, 4, 59050, '2023-03-20'),
(3, 4, 37200, '2024-03-12'),
(5, 4, 4500, '2024-05-14'),
(3, 5, 50000, '2023-09-19'),
(4, 5, 670000, '2023-11-10'),
(1, 5, 35000, '2024-04-15'),
(2, 5, 25000, '2024-06-12');
-- === 1. Типы сотрудников ===
INSERT INTO employees_types (name)
VALUES
('Менеджер'),
('Бухгалтер'),
('Программист'),
('Охранник'),
('Уборщик');
-- === 2. Сотрудники ===
INSERT INTO employees (
employee_type_id, first_name, last_name, middle_name, birth_date,
passport_data, bank_details, has_family, health_status
)
VALUES
-- Менеджеры
(1, 'Иван', 'Петров', 'Сергеевич', '1988-03-15', '40051234567', '123456789', TRUE, 'Хорошее'),
(1, 'Мария', 'Сидорова', 'Игоревна', '1990-11-02', '40057891234', '987654321', FALSE, 'Отличное'),
-- Программист
(3, 'Андрей', 'Кузнецов', 'Алексеевич', '1995-07-21', '40101234567', '111122223333', TRUE, 'Хорошее'),
-- Бухгалтер
(2, 'Елена', 'Морозова', 'Павловна', '1982-05-08', '40104561234', '444455556666', TRUE, 'Удовлетворительное'),
-- Охранник
(4, 'Сергей', 'Волков', 'Владимирович', '1979-09-10', '40205678901', '555566667777', FALSE, 'Хорошее'),
-- Уборщик
(5, 'Наталья', 'Орлова', 'Геннадьевна', '1975-12-25', '40307891234', '888899990000', TRUE, 'Хорошее');
-- === 3. Пользователи ===
-- Пользователи, связанные с менеджерами
INSERT INTO users (username, password, employee_id)
VALUES
('ivan', 'test', 1),
('manager_maria', 'hashed_password_456', 2);
CREATE VIEW show_partners
AS
SELECT p.id, pt.name AS type_name, p.partner_name, p.first_name_director, p.last_name_director, p.middle_name_director, p.phone_partner, p.rating
FROM partners p JOIN partners_type pt
ON
p.partner_type_id = pt.id;
DELIMITER //
CREATE PROCEDURE add_parther (IN p_partner_type_id INT, IN p_partner_name VARCHAR(255),
IN p_first_name_director VARCHAR(50), IN p_last_name_director VARCHAR(50), IN p_middle_name_director VARCHAR(255),
IN p_email_partner VARCHAR(100), IN p_phone_partner VARCHAR(15), IN p_address VARCHAR(255), IN p_INN VARCHAR(10), IN p_rating INT)
BEGIN
INSERT INTO partners (
partner_type_id,
partner_name,
first_name_director,
last_name_director,
middle_name_director,
email_partner,
phone_partner,
address,
INN,
rating
) VALUES (
p_partner_type_id,
p_partner_name,
p_first_name_director,
p_last_name_director,
p_middle_name_director,
p_email_partner,
p_phone_partner,
p_address,
p_INN,
p_rating
);
END //
DELIMITER ;
DELIMITER //
CREATE PROCEDURE upd_partner (IN p_partner_type_id INT, IN p_id INT, IN p_partner_name VARCHAR(255),
IN p_first_name_director VARCHAR(50), IN p_last_name_director VARCHAR(50), IN p_middle_name_director VARCHAR(255),
IN p_email_partner VARCHAR(100), IN p_phone_partner VARCHAR(15), IN p_address VARCHAR(255), IN p_INN VARCHAR(10), IN p_rating INT)
BEGIN
UPDATE partners
SET
partner_type_id = p_partner_type_id,
partner_name = p_partner_name,
first_name_director = p_first_name_director,
last_name_director = p_last_name_director,
middle_name_director = p_middle_name_director,
email_partner = p_email_partner,
phone_partner = p_phone_partner,
address = p_address,
INN = p_INN,
rating = p_rating
WHERE id = p_id;
END //
DELIMITER ;
DELIMITER //
CREATE FUNCTION get_disc(partner_id INT)
RETURNS INT
BEGIN
DECLARE total_amount INT;
SELECT SUM(amount) INTO total_amount
FROM product_partners
WHERE partner_id = partner_id;
IF total_amount >= 300000 THEN RETURN 15;
ELSEIF total_amount >= 50000 THEN RETURN 10;
ELSEIF total_amount >= 10000 THEN RETURN 5;
ELSE RETURN 0;
END IF;
END //
DELIMITER ;
DELIMITER //
CREATE PROCEDURE partner_history(IN p_partner_id INT)
BEGIN
SELECT
pr.name AS product_name,
pp.amount AS quantity,
pp.sale_date AS sale_date
FROM product_partners pp JOIN products pr
ON
pp.product_id = pr.id
WHERE pp.partner_id = p_partner_id
ORDER BY pp.sale_date DESC;
END//
DELIMITER ;

View file

@ -0,0 +1,29 @@
from dataclasses import dataclass
@dataclass
class PartnersInfo:
id: int
type_name: str
partner_name: str
first_name_director: str
last_name_director: str
middle_name_director: str
phone_partner: str
rating: int
discount: float
@dataclass
class PartnerUpdateDto:
id: int
partner_type_id: int
partner_name: str
first_name: str
last_name: str
middle_name: str
email: str
phone: str
address: str
inn: str
rating: int

View file

@ -0,0 +1,11 @@
from PyQt6.QtWidgets import QApplication
from PyQt6.QtGui import QIcon
from pages.auth_page import AuthPage
app = QApplication([])
app.setWindowIcon(QIcon("app/res/imgs/master_pol.ico"))
start_page = AuthPage()
start_page.show()
app.exec()

View file

@ -0,0 +1,94 @@
from PyQt6.QtWidgets import (
QWidget,
QLabel,
QFormLayout,
QPushButton,
QMessageBox,
QLineEdit,
QVBoxLayout,
)
from PyQt6.QtCore import Qt
from res.colors import ACCENT_COLOR, SECONDARY_COLOR, ACCENT_COLOR_HOVER
from res.fonts import MAIN_FONT
class AuthPage(QWidget):
def __init__(self):
super().__init__()
self.setup_window()
self.init_ui()
self.set_styles()
def setup_window(self):
self.setWindowTitle("Авторизация")
self.setFixedSize(400, 250)
def init_ui(self):
self.main_layout = QVBoxLayout()
self.form_layout: QFormLayout = QFormLayout()
self.title = QLabel("Авторизация")
self.title.setObjectName("title")
self.username_label = QLabel("Логин:")
self.password_label = QLabel("Пароль:")
self.username_input = QLineEdit()
self.password_input = QLineEdit()
self.password_input.setEchoMode(QLineEdit.EchoMode.Password)
self.login_button = QPushButton("Войти")
self.form_layout.addRow(self.username_label, self.username_input)
self.form_layout.addRow(self.password_label, self.password_input)
self.form_layout.addRow(self.login_button)
self.setLayout(self.main_layout)
self.main_layout.addWidget(self.title, alignment=Qt.AlignmentFlag.AlignHCenter)
self.main_layout.addStretch()
self.main_layout.addLayout(self.form_layout)
self.main_layout.addStretch()
self.login_button.clicked.connect(self.handle_login)
def handle_login(self):
username = self.username_input.text()
password = self.password_input.text()
if not username or not password:
QMessageBox.warning(self, "Ошибка", "Пожалуйста, заполните все поля.")
return
from pages.partners_page import PartnersPage
self.partners_page = PartnersPage()
self.partners_page.show()
self.close()
def set_styles(self):
self.setStyleSheet(
"""QLabel { font-size: 16px; font-family: %(MAIN_FONT)s}
#title {
font-size: 24px;
font-weight: bold;
color: %(ACCENT_COLOR)s;
}
QPushButton {
background-color: %(ACCENT_COLOR)s;
border: 1px solid black;
color: %(SECONDARY_COLOR)s;
font-weight: bold;
padding: 5px;
}
QPushButton:hover {
background-color: %(ACCENT_COLOR_HOVER)s;
}
"""
% {
"ACCENT_COLOR": ACCENT_COLOR,
"SECONDARY_COLOR": SECONDARY_COLOR,
"MAIN_FONT": MAIN_FONT,
"ACCENT_COLOR_HOVER": ACCENT_COLOR_HOVER,
}
)

View file

@ -0,0 +1,130 @@
from PyQt6.QtWidgets import QWidget, QLabel, QVBoxLayout, QScrollArea, QVBoxLayout
from PyQt6.QtCore import Qt
from components.partner_card import PartnerCard, PartnersInfo
from res.colors import ACCENT_COLOR
class PartnersPage(QWidget):
def __init__(self):
super().__init__()
self.setup_window()
self.init_ui()
self.load_partners()
def setup_window(self):
self.setWindowTitle("Партнеры")
self.resize(800, 600)
def init_ui(self):
main_layout = QVBoxLayout()
self.setLayout(main_layout)
# Заголовок
title = QLabel("Партнеры")
title.setObjectName("title")
title.setStyleSheet(
f"""
#title {{
font-size: 24px;
font-weight: bold;
color: {ACCENT_COLOR};
margin-bottom: 20px;
}}
"""
)
main_layout.addWidget(title, alignment=Qt.AlignmentFlag.AlignHCenter)
# Создаем область прокрутки
scroll_area = QScrollArea()
scroll_area.setWidgetResizable(True)
scroll_content = QWidget()
self.partners_layout = QVBoxLayout(scroll_content)
scroll_area.setWidget(scroll_content)
main_layout.addWidget(scroll_area)
def handle_partner_double_click(self, partner_info: PartnersInfo):
from components.edit_partner_dialog import EditPartnerDialog
dialog = EditPartnerDialog(partner_info, self)
dialog.exec()
def load_partners(self):
# Тестовые данные партнеров
test_partners = [
{
"id": 1,
"type_name": "Золотой партнер",
"partner_name": "ООО 'ТехноПрофи'",
"first_name_director": "Иван",
"last_name_director": "Петров",
"middle_name_director": "Сергеевич",
"phone_partner": "+7 (495) 123-45-67",
"rating": 4.8,
"discount": 15.0
},
{
"id": 2,
"type_name": "Серебряный партнер",
"partner_name": "ИП Сидоров А.В.",
"first_name_director": "Алексей",
"last_name_director": "Сидоров",
"middle_name_director": "Викторович",
"phone_partner": "+7 (495) 234-56-78",
"rating": 4.2,
"discount": 10.0
},
{
"id": 3,
"type_name": "Бронзовый партнер",
"partner_name": "ООО 'СтройМастер'",
"first_name_director": "Мария",
"last_name_director": "Иванова",
"middle_name_director": "Олеговна",
"phone_partner": "+7 (495) 345-67-89",
"rating": 3.9,
"discount": 7.5
},
{
"id": 4,
"type_name": "Золотой партнер",
"partner_name": "АО 'ПромИнвест'",
"first_name_director": "Сергей",
"last_name_director": "Козлов",
"middle_name_director": "Анатольевич",
"phone_partner": "+7 (495) 456-78-90",
"rating": 4.9,
"discount": 18.0
},
{
"id": 5,
"type_name": "Стандартный партнер",
"partner_name": "ООО 'ТоргСервис'",
"first_name_director": "Ольга",
"last_name_director": "Смирнова",
"middle_name_director": "Дмитриевна",
"phone_partner": "+7 (495) 567-89-01",
"rating": 3.5,
"discount": 5.0
}
]
# Создаем карточки партнеров на основе тестовых данных
for partner in test_partners:
partner_info = PartnersInfo(
id=partner["id"],
type_name=partner["type_name"],
partner_name=partner["partner_name"],
first_name_director=partner["first_name_director"],
last_name_director=partner["last_name_director"],
middle_name_director=partner["middle_name_director"],
phone_partner=partner["phone_partner"],
rating=partner["rating"],
discount=partner["discount"],
)
# Создаем и добавляем карточку партнера
partner_card = PartnerCard(partner_info)
partner_card.doubleClicked.connect(self.handle_partner_double_click)
self.partners_layout.addWidget(partner_card)
self.partners_layout.addStretch()

View file

@ -0,0 +1,4 @@
MAIN_COLOR = "#FFFFFF"
SECONDARY_COLOR = "#F4E8D3"
ACCENT_COLOR = "#67BA80"
ACCENT_COLOR_HOVER = "#529265"

View file

@ -0,0 +1 @@
MAIN_FONT = "Segoe UI"

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

View file

@ -0,0 +1,35 @@
from string import Template
from res.colors import MAIN_COLOR, SECONDARY_COLOR, ACCENT_COLOR
from res.fonts import MAIN_FONT
styles_template = Template(
"""
QWidget {
font-family: {MAIN_FONT};
background-color: {MAIN_COLOR}
color: {SECONDARY_COLOR};
}
QPushButton {
background-color: {ACCENT_COLOR};
border: none;
padding: 8px 16px;
border-radius: 4px;
}
QPushButton:hover {
background-color: {SECONDARY_COLOR};
}
QLineEdit {
padding: 6px;
border: 1px solid {ACCENT_COLOR};
border-radius: 4px;
background-color: white;
}
"""
)
styles = styles_template.substitute(
MAIN_FONT=MAIN_FONT,
MAIN_COLOR=MAIN_COLOR,
SECONDARY_COLOR=SECONDARY_COLOR,
ACCENT_COLOR=ACCENT_COLOR,
)

Binary file not shown.