Описание кода и адаптация проекта под другую тему
=================================================

1. Общая структура проекта
--------------------------

Основные файлы:

main.py
  Главное приложение на PySide6.
  Содержит окно авторизации, список товаров, карточку товара, форму
  добавления/редактирования товара, работу с картинками и подсветку карточек.

database.py
  Слой работы с MySQL.
  Здесь находится подключение к БД, запрос пользователя для авторизации,
  получение списка товаров, фильтрация, сортировка, сохранение и удаление товара.

schema.sql
  SQL-скрипт создания базы данных dem_DB.
  В нем описаны таблицы:
  user, pickup_points, tovar, zakaz, order_items.

db_config.json
  Настройки подключения к БД.
  Если меняется имя базы, логин или пароль MySQL, это меняется здесь.

excel_to_csv.py
  Простой скрипт для преобразования Excel-файлов из папки excel в CSV-файлы
  в папку csv.

view_db_column_names.py
  Вспомогательный скрипт. Выводит названия столбцов таблиц БД через запятую.
  Удобен для phpMyAdmin, когда нужно заполнить поле "Название столбцов" при
  импорте CSV.

README.txt
  Инструкция по установке, созданию БД, импорту CSV и запуску приложения.

images/
  Папка с картинками товаров, логотипом Icon.png и заглушкой picture.png.

excel/
  Исходные Excel-файлы.

csv/
  CSV-файлы после запуска excel_to_csv.py.


2. Как устроена база данных
---------------------------

База называется:

dem_DB

Основные таблицы:

user
  Пользователи приложения.
  Поля:
  id_user, role, full_name, login, password

pickup_points
  Пункты выдачи заказов.
  Поля:
  id_points, address

tovar
  Товары.
  Поля:
  article, product_name, unit, price, supplier, manufacturer, category,
  current_discount, stock_quantity, description, image

zakaz
  Заказы.
  Поля:
  order_number, order_date, delivery_date, pickup_code, order_status,
  id_points, id_user

order_items
  Состав заказов.
  Поля:
  id_items, order_number, article, quantity

Связи:

zakaz.id_user -> user.id_user
zakaz.id_points -> pickup_points.id_points
order_items.order_number -> zakaz.order_number
order_items.article -> tovar.article

Из-за связи order_items.article -> tovar.article нельзя удалить товар, если он
уже есть в заказе.


3. Как устроено приложение main.py
----------------------------------

Верхние настройки:

APP_TITLE = "ООО Обувь"
  Заголовок окна приложения.
  Для другой темы можно заменить, например:
  "Магазин книг"
  "Магазин велосипедов"

IMG_DIR = Path("images")
  Папка, где хранятся картинки.

PLACEHOLDER_IMAGE = "picture.png"
  Картинка-заглушка, если у товара нет изображения.

PRODUCT_LABELS
  Словарь русских подписей для полей товара.
  Эти подписи показываются в форме добавления/редактирования товара.

Пример:

"product_name": "Название"
"category": "Категория"
"supplier": "Поставщик"

Если тема "книги", можно заменить подписи:

"manufacturer": "Автор"
"supplier": "Издательство"
"category": "Жанр"

Но названия колонок БД лучше оставить прежними, тогда меньше придется менять
код.


4. Окно авторизации
-------------------

Класс:

Login

Что делает:

1. Показывает логотип images/Icon.png.
2. Показывает поля логина и пароля.
3. Проверяет пользователя через database.py:

   self.db.get_user(login, password)

4. Позволяет войти как гость.

Роли:

Гость
  Может смотреть товары без поиска, фильтрации и сортировки.

Авторизированный клиент
  Может смотреть товары.

Менеджер
  Может смотреть товары с поиском, фильтрацией и сортировкой.

Администратор
  Может смотреть, добавлять, редактировать и удалять товары.

Проверка роли находится в App.__init__:

self.admin = user["role"] == "Администратор"
worker = user["role"] in ("Менеджер", "Администратор")


5. Список товаров
-----------------

Класс:

App

Что делает:

1. Загружает товары из БД:

   self.db.get_products(...)

2. Создает карточки товаров:

   Card(row, self.admin, self.db, self)

3. Показывает поиск, фильтр и сортировку для менеджера и администратора.
4. Показывает кнопку "Добавить" только администратору.


6. Карточка товара
------------------

Класс:

Card

Что показывает:

1. Картинку товара.
2. Категорию и название.
3. Описание.
4. Производителя.
5. Поставщика.
6. Цену.
7. Цену со скидкой, если скидка больше 0.
8. Единицу измерения.
9. Количество на складе.
10. Действующую скидку.

Картинки:

Если поле image пустое или файла нет, используется:

images/picture.png

Подсветка:

Если скидка больше 15:

background-color: #2E8B57

Если товара нет на складе:

background-color: #ADD8E6

Если одновременно скидка больше 15 и остаток 0, сработает зеленая подсветка,
потому что она проверяется первой.


7. Добавление и редактирование товара
-------------------------------------

Класс:

ProductDialog

Форма создается автоматически по словарю PRODUCT_LABELS.

Это значит:

1. В PRODUCT_LABELS перечислены поля товара.
2. Для каждого поля создается QLineEdit.
3. Для поля image добавляется кнопка "Выбрать".

При сохранении вызывается:

dialog.data()

Этот метод:

1. Забирает значения из полей.
2. Подставляет значения по умолчанию:
   unit = "шт."
   price = "0"
   current_discount = "0"
   stock_quantity = "0"
3. Копирует выбранную картинку в папку images.


8. Работа с картинками
----------------------

Функции:

image_path(name)
  Возвращает путь к картинке товара.
  Если картинки нет, возвращает images/picture.png.

save_image_to_images(value)
  Если пользователь выбрал файл картинки, копирует его в папку images.
  Если файл является изображением, он уменьшается до 300x200.
  В БД сохраняется только имя файла, например:

  1.jpg

remove_unused_image(db, image_name)
  Удаляет картинку из папки images, если она больше не используется ни одним
  товаром.

Защищенные картинки:

picture.png
Icon.png

Они не удаляются автоматически.


9. database.py
--------------

Главный класс:

Database

Основные методы:

get_user(login, password)
  Ищет пользователя по логину и паролю.

get_products(search="", supplier=None, sort=None)
  Возвращает список товаров.
  Поддерживает:
  - поиск;
  - фильтр по поставщику;
  - сортировку.

get_suppliers()
  Возвращает список поставщиков для фильтра.

save_product(data, old_article=None)
  Если old_article пустой, добавляет новый товар.
  Если old_article указан, обновляет существующий товар.

delete_product(article)
  Удаляет товар по артикулу.
  Если товар есть в order_items, MySQL запретит удаление через внешний ключ.

image_usage_count(image_name)
  Считает, сколько товаров используют конкретную картинку.


10. Как адаптировать проект под другую тему
-------------------------------------------

Самый простой способ: оставить таблицу tovar и ее поля, но изменить смысл
полей и подписи в интерфейсе.

Например, магазин книг:

article
  Артикул / ISBN / код книги

product_name
  Название книги

unit
  Единица измерения, например "шт."

price
  Цена

supplier
  Поставщик / магазин / склад

manufacturer
  Автор или издательство

category
  Жанр

current_discount
  Скидка

stock_quantity
  Количество на складе

description
  Описание книги

image
  Обложка книги

В main.py меняются:

APP_TITLE = "Магазин книг"

PRODUCT_LABELS = {
    "article": "ISBN",
    "product_name": "Название книги",
    "category": "Жанр",
    "manufacturer": "Автор",
    "supplier": "Издательство",
    ...
}

Для магазина велосипедов:

APP_TITLE = "Магазин велосипедов"

PRODUCT_LABELS:

"article": "Артикул"
"product_name": "Модель"
"category": "Тип велосипеда"
"manufacturer": "Бренд"
"supplier": "Поставщик"
"description": "Описание"
"image": "Фото"


11. Когда нужно менять schema.sql
---------------------------------

schema.sql нужно менять, если:

1. Меняется имя базы данных.
2. Добавляются новые поля.
3. Удаляются поля.
4. Меняются типы данных.
5. Меняются связи между таблицами.

Если нужно только заменить "обувь" на "книги", schema.sql можно не менять.
Достаточно поменять подписи в main.py и данные в Excel/CSV.


12. Если нужно добавить новое поле товара
-----------------------------------------

Например, нужно добавить поле "year" для года выпуска книги.

1. В schema.sql добавить поле в таблицу tovar:

year INT(4) NOT NULL,

2. В database.py добавить поле в список select_cols внутри get_products:

"year"

3. В main.py добавить подпись в PRODUCT_LABELS:

"year": "Год"

4. В карточке товара Card добавить вывод этого поля, если оно должно
   отображаться.

5. В CSV добавить новый столбец.

6. При импорте в phpMyAdmin добавить поле year в список названий столбцов.


13. Если нужно переименовать колонку БД
---------------------------------------

Например, заменить manufacturer на author.

Нужно изменить сразу несколько мест:

1. schema.sql:

manufacturer VARCHAR(255)

заменить на:

author VARCHAR(255)

2. database.py:
   - get_products: select_cols
   - search_fields

3. main.py:
   - PRODUCT_LABELS
   - Card, если поле выводится вручную

4. Excel/CSV:
   - порядок и количество столбцов должны соответствовать новой таблице.

Проще и безопаснее не переименовывать колонки, а менять только русские подписи
в PRODUCT_LABELS.


14. Как адаптировать Excel и CSV под другую тему
------------------------------------------------

Excel можно заменить на данные другой темы, но количество и порядок столбцов
для таблицы tovar должны соответствовать БД:

article,product_name,unit,price,supplier,manufacturer,category,current_discount,stock_quantity,description,image

Для книг это может быть:

ISBN,Название,шт.,Цена,Поставщик,Автор,Жанр,Скидка,Остаток,Описание,cover.jpg

Для велосипедов:

Артикул,Модель,шт.,Цена,Поставщик,Бренд,Тип,Скидка,Остаток,Описание,bike.jpg

После изменения Excel нужно выполнить:

python excel_to_csv.py

CSV появятся в папке csv.


15. Как работает excel_to_csv.py
--------------------------------

Скрипт:

1. Берет все .xlsx файлы из папки excel.
2. Открывает первый лист каждого файла.
3. Удаляет полностью пустые строки.
4. Убирает лишние пустые столбцы справа.
5. Сохраняет CSV в папку csv.

Запуск:

python excel_to_csv.py


16. Импорт CSV в phpMyAdmin
---------------------------

Для импорта важно:

1. Количество значений в строке CSV должно совпадать с количеством колонок,
   которые указаны в phpMyAdmin.
2. Если поле AUTO_INCREMENT, его обычно не импортируют.
3. Для user указывать:

role,full_name,login,password

4. Для pickup_points:

address

5. Для tovar:

article,product_name,unit,price,supplier,manufacturer,category,current_discount,stock_quantity,description,image

6. Для zakaz:

order_date,delivery_date,pickup_code,order_status,id_points,id_user

7. Для order_items:

order_number,article,quantity

Порядок импорта:

1. user
2. pickup_points
3. tovar
4. zakaz
5. order_items

Такой порядок нужен из-за внешних ключей.


17. Частые ошибки
-----------------

Ошибка 1452 Cannot add or update a child row
  Вставляется ссылка на несуществующую запись.
  Например, zakaz.id_user = 5, но в user нет id_user = 5.

Ошибка Duplicate entry
  Дубликат уникального значения.
  Например, одинаковый login в таблице user или одинаковый article в tovar.

Ошибка Column count doesn't match value count
  Количество значений в CSV не совпадает с количеством колонок импорта.
  Нужно проверить список колонок в phpMyAdmin и CSV.

Ошибка с датой
  Дата должна быть в формате YYYY-MM-DD.
  Например:
  2025-02-27

Неправильная дата:
  30.02.2025


18. Что менять для новой темы проекта кратко
--------------------------------------------

Минимальный вариант:

1. В main.py поменять APP_TITLE.
2. В main.py поменять PRODUCT_LABELS.
3. Заменить Excel-файлы на данные новой темы.
4. Запустить excel_to_csv.py.
5. Импортировать CSV в phpMyAdmin.

Расширенный вариант:

1. Изменить schema.sql.
2. Изменить database.py под новые поля.
3. Изменить main.py под новые поля.
4. Изменить Excel/CSV.
5. Пересоздать БД.
6. Импортировать данные заново.


19. Что лучше не менять без необходимости
-----------------------------------------

Лучше не менять:

article
product_name
unit
price
supplier
manufacturer
category
current_discount
stock_quantity
description
image

Эти поля уже используются в коде. Для другой темы проще поменять их смысл и
подписи, а не названия колонок.
