zov-tech/backend-py/app/config.py
wasrusgen f5ee9e5b33 feat: warehouse module — ОТГРУЗКИ.xlsx в дашборде менеджера
- backend: новый модуль drive.py (Google Drive download + 5-мин кэш)
- backend: /api/shipments — читает xlsx из Drive, парсит листы «ЗОВ ДД.ММ.ГГ»,
  возвращает позиции (Заказ/Дозаказ) сгруппированные по дате отгрузки с завода
- config: поле shipments_file_id (SHIPMENTS_FILE_ID env; дефолт = ID ОТГРУЗКИ.xlsx)
- frontend: секция «📦 Отгрузки» на главной менеджера (после активных проектов),
  загружается параллельно с замерами и pending; показывает последние 3 партии
- CSS: стили .ship-group / .ship-row / .ship-badge / .ship-check
- deps: добавлен openpyxl>=3.1.0

ВАЖНО после деплоя: добавить сервис-аккаунт как Viewer к ОТГРУЗКИ.xlsx в Drive
и прописать SHIPMENTS_FILE_ID в /opt/zov-tech/deploy/.env на сервере.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 07:21:23 +03:00

58 lines
2.2 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.

"""Конфиг бэкенда — читается из переменных окружения."""
from __future__ import annotations
import os
from dataclasses import dataclass
from functools import lru_cache
@dataclass(frozen=True)
class Config:
bot_token: str
admin_tg_id: int
sheet_id: str
google_credentials_path: str
gigachat_auth_key: str
gigachat_model: str
gigachat_scope: str
active_period_days: int
grace_period_days: int
proxy6_token: str # пусто = без прокси (прямой HTTP)
proxy_static_list: str # статический список прокси через запятую: "http://user:pass@host:port,..."
proxy_list_file: str # путь к файлу со списком прокси в формате "host:port:user:pass" или "http://..."
# Внутренний секрет для вызовов бота → бэкенда (без initData)
internal_secret: str
# Google Drive ID файла ОТГРУЗКИ.xlsx (склад/отгрузки с завода)
shipments_file_id: str
def _required(name: str) -> str:
val = os.getenv(name)
if not val:
raise RuntimeError(f"Missing required env var: {name}")
return val
@lru_cache(maxsize=1)
def get_config() -> Config:
return Config(
bot_token=_required("BOT_TOKEN"),
admin_tg_id=int(os.getenv("ADMIN_TG_ID", "0")),
sheet_id=_required("SHEET_ID"),
google_credentials_path=os.getenv("GOOGLE_CREDENTIALS_PATH", "/app/credentials.json"),
gigachat_auth_key=_required("GIGACHAT_AUTH_KEY"),
gigachat_model=os.getenv("GIGACHAT_MODEL", "GigaChat-Pro"),
gigachat_scope=os.getenv("GIGACHAT_SCOPE", "GIGACHAT_API_PERS"),
active_period_days=int(os.getenv("ACTIVE_PERIOD_DAYS", "90")),
grace_period_days=int(os.getenv("GRACE_PERIOD_DAYS", "14")),
proxy6_token=os.getenv("PROXY6_TOKEN", ""),
proxy_static_list=os.getenv("PROXY_STATIC_LIST", ""),
proxy_list_file=os.getenv("PROXY_LIST_FILE", ""),
internal_secret=os.getenv("INTERNAL_SECRET", ""),
shipments_file_id=os.getenv("SHIPMENTS_FILE_ID", "1fER4NmEgSznvPKJWXOqLDDkTxH6wm78E"),
)