mirror of
https://github.com/Horizontal42/warehouse-master.git
synced 2026-11-10 16:02:07 +03:00
8.9 KiB
8.9 KiB
Архитектура
Обзор
Warehouse Master — десктопное приложение для печати нумерованных этикеток в PDF и синхронизации пересборки ноутбуков через Google Sheets. Два независимых таба: Labels (генерация PDF) и Reassembly (синхронизация с таблицей).
Структура файлов
| Файл | Назначение |
|---|---|
| main.py | Entry point, главное окно LabelApp, жизненный цикл табов, dispatch i18n |
| printing.py | Утилиты: файловый ввод-вывод, пути к ресурсам, обнаружение принтеров, папка конфига |
| styles.py | Генератор стилей: build_stylesheet(dark: bool) → возвращает CSS для обеих тем |
| workers.py | Фоновые потоки: PDFWorker (reportlab), ReassemblyWorker (синхронизация gspread) |
| widgets.py | Переиспользуемые UI компоненты: NoScrollComboBox, RangeRowWidget, PreferencesDialog |
| strings.py | i18n: словарь STRINGS {"en": {...}, "ru": {...}} со всеми строками UI |
| sheet_manager.py | Обёртка Google Sheets API: конфиг-управляемые индексы колонок, вычисление категории, батч-обновления |
| sheet_config.example.json | Шаблон конфига: листы, маппинги колонок, переводчики типов, статусы |
| WarehouseMaster.spec | Спек PyInstaller: бандлит Inter.ttf, собирает single-file exe |
| requirements.txt | Зависимости: PyQt5, reportlab, gspread, google-auth |
Ответственность модулей
main.py
- Загружает/сохраняет QSettings (тема, язык, состояние окна)
- Рендерит два таба: labels и reassembly
- Подключает обработчики событий
- Вызывает
_retranslate()при смене языка - Очистка временных файлов при закрытии
printing.py
resource_path()— ищет бандлированные файлы (Inter.ttf, icon.ico)app_data_dir()—%APPDATA%/WarehouseMaster/для token.json, локальных конфиговconfig_path()— возвращает sheet_config.json (локальный или из appdata)register_fonts()— загружает Inter.ttf в reportlabget_installed_printers()— перечисляет принтеры Windows (win32api)open_file()— кросс-платформенный открыватель файлов (Windows/macOS/Linux)print_file()— печать на конкретный принтер или системный по умолчанию
styles.py
build_stylesheet(dark: bool)— одна функция, возвращает полный CSS палитры- Цвета захардкожены (dark: #212529 фон, #f8f9fa текст; light: инверсия)
- Вызывается при переключении темы →
setStyleSheet()
workers.py
PDFWorker(QThread)— генерирует PDF в фоне, эмититfinished(filepath)илиerror_occurred(str)ReassemblyWorker(QThread)— вызываетSheetManager.process_reassembly(), эмититfinished_signal(comment, tech_op)илиerror_signal(str)- Оба предотвращают блокировку UI
widgets.py
NoScrollComboBox— подавляет скроллинг на колесике мышиRangeRowWidget— поля start/end + кнопки +/-; управляется callback-амиPreferencesDialog— язык + тема + URL таблицы + папка сохранения, сохраняет в QSettings
strings.py
- Два словаря:
STRINGS["en"],STRINGS["ru"] - Ключи:
"window_title","tab_labels", шаблоны сообщений с{n}плейсхолдерами - Никакой логики переключения локали — main.py читает
settings.value("locale")и индексирует
sheet_manager.py
SheetManager(sheet_url, config_path)— загружает config.json, авторизует gspreadprocess_reassembly(laptop_id, parts_str, ssd_id, akb_id)— атомарно обновляет: отмечает запчасти отгруженными, пишет грейды в строку ноутбука, пересчитывает категорию (D/C/B/A/NEW)_calculate_category(laptop_data)— правило-based скоринг (D если любая деталь ≤rank 1, NEW если все ≥A и cycles < 150 и т.д.)- Управляется конфигом: все индексы колонок, range clears, значения статусов из sheet_config.json
Горячие пути
Генерация PDF
- Пользователь нажимает «Generate PDF»
_start_generation()валидирует диапазоны (start ≤ end), спавнитPDFWorkerPDFWorker.run()→reportlab.canvasрисует этикетки в сеточном макете- При успехе:
_on_pdf_done()→ открыть файл или печать - UI разблокирован (рабочий поток)
Синхронизация пересборки
- Пользователь заполняет заказ/запчасти, нажимает «Apply changes»
_run_reassembly()спавнитReassemblyWorkerReassemblyWorker.run()→SheetManager.process_reassembly()- Manager: фетчит ID из всех листов, обрабатывает каждую запчасть (SSD/АКБ/запчасть), вычисляет новую категорию
- Батч-обновляет все листы, возвращает comment + tech строки
_on_reassembly_done()отображает результаты- UI разблокирован
Добавление функциональности
Новый формат этикетки (например 6×6 сетка)
- Добавить в
PAGE_SIZESиDEFAULT_FONT_SIZESв workers.py - Добавить radio button в
_init_gen_tab() - Добавить ветку в
PDFWorker.run()для макета сетки
Новое поле пересборки
- Добавить ключ конфига в sheet_config.example.json (например
"warranty_months": 52) - Прочитать в
SheetManager.__init__()через self.cfg - Добавить обработку в
_process_part()или новый_process_<thing>()метод
Новый язык
- Добавить
STRINGS["new_lang"]словарь в strings.py - Добавить в языковую комбобокс в PreferencesDialog (widgets.py)
- Маппировать индекс комбо → код локали (например index 2 → "de")
Новый лист Google Sheets
- Добавить в
sheet_config.json→worksheets - Добавить маппинги колонок если нужно
- Добавить fetch/batch в методы
SheetManager - Подключить в
process_reassembly()
Подводные камни
- QSettings кейс: На Linux имена файлов case-sensitive; использовать lowercase ключи (
"sheet_url"не"SheetURL") - Обновление токена: Если учетные данные gspread истекают во время сессии, рабочий поток упадёт; пользователь должен перезапустить и переавторизоваться
- Выравнивание колонок: В reportlab canvas Y-ось направлена вверх (0 = внизу). Сеточный макет правильно смещает Y через
ph - (row + 1) * cell_h - Конфиг отсутствует: Если sheet_config.json не найден, SheetManager выбросит на инит; обработать в UI с try-except, показать понятное сообщение
- Кириллические грейды: Перед проверкой ранга нормализовать кириллицу А/В/С/Д → латинь A/B/C/D через
str.maketrans() - Принтер не найден: Если сохранённый принтер удалён,
findText()вернёт -1; guard передsetCurrentIndex() - Временные файлы: Если приложение упадёт, временные PDF не удалятся; рассмотреть OS-уровневую очистку tmp директорий (автоматическая purge)