""" =============================================================================== FILE: modules/web_api/routers/reports.py ROLE: REST API On-Demand генерации отчетов: - Сводка: за СЕГОДНЯ (оперативный контроль, текущий срез). - Детальный и Упрощенный отчет: строго за ВЧЕРА по финальному срезу Y. =============================================================================== """ import os import logging from datetime import datetime, timedelta from typing import Optional from pydantic import BaseModel from fastapi import APIRouter, HTTPException from config import DATE_TODAY, DATE_YESTERDAY from services.scud_etl.svodka_generator import generate_svodka_service from services.scud_etl.otchet_generator import generate_otchet_service from services.reports.simplified_builder import generate_simplified_excel from services.scud_etl.pipeline import load_best_snapshot_for_date, load_1c_files_for_date from services.scud_etl.merger import merge_scud_and_1c logger = logging.getLogger("REPORTS_API") router = APIRouter(prefix="/api/v1/reports", tags=["Reports"]) def get_previous_workday(target_date_str: str) -> str: """Вычисляет дату предыдущей рабочей смены (в понедельник возвращает пятницу).""" clean_date = target_date_str.replace('_', '.') try: dt = datetime.strptime(clean_date, "%d.%m.%Y") days_back = 3 if dt.weekday() == 0 else 1 return (dt - timedelta(days=days_back)).strftime("%d.%m.%Y") except Exception: return DATE_YESTERDAY class GenerateReportRequest(BaseModel): date: Optional[str] = None # ДД.ММ.ГГГГ time: Optional[str] = None # ЧЧ:ММ report_type: str # 'SVODKA', 'DETAILED', 'SIMPLIFIED', 'ALL' @router.post("/generate") def api_generate_report(req: GenerateReportRequest): """ Генерирует выбранный отчет: - SVODKA: на текущую дату (date). - DETAILED / SIMPLIFIED: строго за вчерашний рабочий день по финальному срезу Y. """ target_date = (req.date or DATE_TODAY).replace('_', '.') yesterday_date = get_previous_workday(target_date) r_type = req.report_type.upper() results = [] # 1. Ежедневная сводка (за СЕГОДНЯ) if r_type in ["SVODKA", "ALL"]: res_svodka = generate_svodka_service( target_date=target_date, target_time=req.time ) if res_svodka.get("status") == "success": results.append({ "type": "Сводка", "filename": res_svodka.get("filename"), "download_url": res_svodka.get("download_url"), "status": "success" }) else: results.append({ "type": "Сводка", "error": res_svodka.get("message"), "status": "error" }) # 2. Детальный отчет (строго за ВЧЕРА по финальному срезу Y) if r_type in ["DETAILED", "ALL"]: res_det = generate_otchet_service(target_date=yesterday_date) if res_det.get("status") == "success": results.append({ "type": "Детальный отчет", "filename": res_det.get("filename"), "download_url": res_det.get("download_url"), "status": "success" }) else: results.append({ "type": "Детальный отчет", "error": res_det.get("message"), "status": "error" }) # 3. Упрощенный отчет (строго за ВЧЕРА по финальному срезу Y) if r_type in ["SIMPLIFIED", "ALL"]: try: df_scud_y = load_best_snapshot_for_date(yesterday_date, prefer_final_y=True) if df_scud_y is not None and not df_scud_y.empty: df_staff_y, df_abs_y = load_1c_files_for_date(yesterday_date) df_merged_y = merge_scud_and_1c(df_scud_y, df_staff_y, df_abs_y) out_path = generate_simplified_excel(df_merged_y, date_str=yesterday_date) filename = os.path.basename(out_path) results.append({ "type": "Упрощенный отчет", "filename": filename, "download_url": f"/api/v1/files/download/reports/{filename}", "status": "success" }) else: results.append({ "type": "Упрощенный отчет", "error": f"Финальный срез СКУД (Y) за {yesterday_date} не найден.", "status": "error" }) except Exception as e: logger.exception(f"Ошибка формирования упрощенного отчета: {e}") results.append({ "type": "Упрощенный отчет", "error": str(e), "status": "error" }) return { "date": target_date, "yesterday_date": yesterday_date, "requested_type": r_type, "reports": results }