food-market/app/ui/theme.py
Claude efce552e2b Отдельный репозиторий для базы, фасовки товара, ввод истории
Три правки по замечаниям.

1. База и код разъехались по разным репозиториям. Автокоммит и раньше
   трогал только vault.fmdb, но лежал он в той же ветке, что и исходники,
   поэтому пуш тащил всю историю программы. Теперь data/ — самостоятельный
   клон food-records со своим .git, а исходники его игнорируют целиком.
   При первом запуске приложение встаёт на уже существующую историю
   сервера, а не заводит параллельную.

2. Фасовки товара. Пачка печенья 10 шт за 200 ₽ и та же печенька поштучно
   за 30 ₽ — один товар с двумя фасовками. Остатки, себестоимость и FIFO
   считаются в базовых единицах, поэтому поштучные продажи вычитаются из
   купленных пачек. Размер фасовки хранится в документе слепком.
   Миграция схемы 1→2 не меняет поведение уже заведённых данных.

3. Быстрый ввод продаж за период: выбор диапазона дат, строка таблицы —
   отдельная продажа, ввод с клавиатуры, запись всех разом. Незнакомое имя
   заводится как контрагент, повтор периода подсвечивается.

Тесты больше не имеют настроенного remote, чтобы не ходить в сеть.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 21:25:39 +03:00

185 lines
5.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Тёмная тема.
Палитра задаётся через QPalette, а не только через QSS: иначе системные
диалоги и всплывающие подсказки остались бы светлыми и резали глаза.
"""
from __future__ import annotations
from PySide6.QtCore import QLocale, Qt
from PySide6.QtGui import QColor, QPalette
BG = "#1c1f24"
BG_ALT = "#23272e"
SURFACE = "#2b3038"
BORDER = "#3a4048"
TEXT = "#e6e8ea"
TEXT_DIM = "#98a0aa"
ACCENT = "#4c9aff"
OK = "#4caf7d"
WARN = "#d9a441"
DANGER = "#e0605e"
MUTED = "#6c757d"
STATUS_COLORS = {
"settled": OK,
"open": ACCENT,
"due_soon": WARN,
"overdue": DANGER,
}
def apply(app) -> None:
app.setStyle("Fusion")
# Иначе поля ввода показывают «1,800.00», а таблицы рядом — «1 800,00».
QLocale.setDefault(QLocale(QLocale.Russian, QLocale.Russia))
palette = QPalette()
palette.setColor(QPalette.Window, QColor(BG))
palette.setColor(QPalette.WindowText, QColor(TEXT))
palette.setColor(QPalette.Base, QColor(BG_ALT))
palette.setColor(QPalette.AlternateBase, QColor(SURFACE))
palette.setColor(QPalette.Text, QColor(TEXT))
palette.setColor(QPalette.Button, QColor(SURFACE))
palette.setColor(QPalette.ButtonText, QColor(TEXT))
palette.setColor(QPalette.ToolTipBase, QColor(SURFACE))
palette.setColor(QPalette.ToolTipText, QColor(TEXT))
palette.setColor(QPalette.Highlight, QColor(ACCENT))
palette.setColor(QPalette.HighlightedText, QColor("#ffffff"))
palette.setColor(QPalette.PlaceholderText, QColor(TEXT_DIM))
palette.setColor(QPalette.Link, QColor(ACCENT))
palette.setColor(QPalette.Disabled, QPalette.Text, QColor(MUTED))
palette.setColor(QPalette.Disabled, QPalette.ButtonText, QColor(MUTED))
palette.setColor(QPalette.Disabled, QPalette.WindowText, QColor(MUTED))
app.setPalette(palette)
app.setStyleSheet(QSS)
QSS = f"""
QWidget {{
font-size: 13px;
}}
QLabel[role="h1"] {{ font-size: 21px; font-weight: 600; }}
QLabel[role="h2"] {{ font-size: 15px; font-weight: 600; }}
QLabel[role="dim"] {{ color: {TEXT_DIM}; }}
QLabel[role="metric"] {{ font-size: 24px; font-weight: 600; }}
QFrame[role="card"] {{
background: {SURFACE};
border: 1px solid {BORDER};
border-radius: 8px;
}}
QFrame[role="banner"] {{
background: #3a2f1c;
border: 1px solid {WARN};
border-radius: 6px;
}}
QListWidget#sidebar {{
background: {BG_ALT};
border: none;
border-right: 1px solid {BORDER};
outline: none;
padding-top: 8px;
}}
QListWidget#sidebar::item {{
padding: 10px 16px;
margin: 1px 6px;
border-radius: 6px;
color: {TEXT_DIM};
}}
QListWidget#sidebar::item:selected {{
background: {SURFACE};
color: {TEXT};
font-weight: 600;
}}
QListWidget#sidebar::item:hover {{ color: {TEXT}; }}
QPushButton {{
background: {SURFACE};
border: 1px solid {BORDER};
border-radius: 6px;
padding: 6px 14px;
}}
QPushButton:hover {{ border-color: {ACCENT}; }}
QPushButton:disabled {{ color: {MUTED}; border-color: {BORDER}; }}
QPushButton[role="primary"] {{
background: {ACCENT};
border-color: {ACCENT};
color: #ffffff;
font-weight: 600;
}}
QPushButton[role="primary"]:hover {{ background: #5aa6ff; }}
QPushButton[role="danger"]:hover {{ border-color: {DANGER}; color: {DANGER}; }}
QLineEdit, QDateEdit, QComboBox, QSpinBox, QDoubleSpinBox, QPlainTextEdit, QTextEdit {{
background: {BG_ALT};
border: 1px solid {BORDER};
border-radius: 6px;
padding: 5px 8px;
selection-background-color: {ACCENT};
}}
QLineEdit:focus, QDateEdit:focus, QComboBox:focus,
QSpinBox:focus, QDoubleSpinBox:focus, QPlainTextEdit:focus {{
border-color: {ACCENT};
}}
QComboBox::drop-down {{ border: none; width: 18px; }}
QComboBox QAbstractItemView {{
background: {BG_ALT};
border: 1px solid {BORDER};
selection-background-color: {ACCENT};
}}
QTableWidget, QTreeWidget {{
background: {BG_ALT};
alternate-background-color: {SURFACE};
border: 1px solid {BORDER};
border-radius: 6px;
gridline-color: {BORDER};
selection-background-color: {ACCENT};
}}
QHeaderView::section {{
background: {SURFACE};
color: {TEXT_DIM};
border: none;
border-bottom: 1px solid {BORDER};
padding: 7px 8px;
font-weight: 600;
}}
QTableWidget::item, QTreeWidget::item {{ padding: 4px 6px; }}
QProgressBar {{
background: {BG};
border: 1px solid {BORDER};
border-radius: 6px;
height: 14px;
text-align: center;
color: {TEXT};
}}
QProgressBar::chunk {{ border-radius: 5px; }}
QStatusBar {{ border-top: 1px solid {BORDER}; }}
QStatusBar::item {{ border: none; }}
QScrollBar:vertical {{ background: transparent; width: 10px; margin: 0; }}
QScrollBar::handle:vertical {{ background: {BORDER}; border-radius: 5px; min-height: 30px; }}
QScrollBar::handle:vertical:hover {{ background: {MUTED}; }}
QScrollBar::add-line, QScrollBar::sub-line {{ height: 0; width: 0; }}
QScrollBar:horizontal {{ background: transparent; height: 10px; }}
QScrollBar::handle:horizontal {{ background: {BORDER}; border-radius: 5px; min-width: 30px; }}
QTabWidget::pane {{ border: 1px solid {BORDER}; border-radius: 6px; }}
QTabBar::tab {{
background: transparent;
padding: 7px 14px;
color: {TEXT_DIM};
border-bottom: 2px solid transparent;
}}
QTabBar::tab:selected {{ color: {TEXT}; border-bottom-color: {ACCENT}; }}
"""