feat(reports): stabilize on-demand generation, live presence and 1C fallback
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
"""
|
||||
===============================================================================
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user