@@ -0,0 +1,145 @@
|
||||
from flask import Blueprint, Response, jsonify, request
|
||||
|
||||
from app.routes.auth_decorators import require_auth
|
||||
from app.services.analytics.export_sections import export_filename_base, parse_export_sections
|
||||
from app.services.analytics.finance_summary import build_finance_summary
|
||||
from app.services.analytics.pdf_export import build_analytics_pdf
|
||||
from app.services.analytics.plan_fact_layers import build_plan_fact_rows
|
||||
from app.services.analytics.stock_forecast import build_stock_forecast
|
||||
from app.services.analytics.xlsx_summary import build_finance_summary_xlsx
|
||||
|
||||
bp = Blueprint("analytics", __name__, url_prefix="/api/analytics")
|
||||
|
||||
|
||||
def _error(message: str, status_code: int = 400):
|
||||
return jsonify({"error": True, "message": message}), status_code
|
||||
|
||||
|
||||
def _export_params():
|
||||
date_from = (request.args.get("date_from") or "").strip()
|
||||
date_to = (request.args.get("date_to") or "").strip()
|
||||
if not date_from or not date_to:
|
||||
return None, None, None, None, _error("Укажите date_from и date_to (YYYY-MM-DD)", 400)
|
||||
recipe_id = (request.args.get("recipe_id") or "").strip() or None
|
||||
recipe_ids = (request.args.get("recipe_ids") or "").strip() or None
|
||||
return date_from, date_to, recipe_id, recipe_ids, None
|
||||
|
||||
|
||||
@bp.get("/finance")
|
||||
@require_auth
|
||||
def finance_summary():
|
||||
return jsonify(
|
||||
build_finance_summary(
|
||||
date_from=request.args.get("date_from"),
|
||||
date_to=request.args.get("date_to"),
|
||||
recipe_id=(request.args.get("recipe_id") or "").strip() or None,
|
||||
recipe_ids=(request.args.get("recipe_ids") or "").strip() or None,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@bp.get("/plan-fact")
|
||||
@require_auth
|
||||
def plan_fact():
|
||||
return jsonify(
|
||||
build_plan_fact_rows(
|
||||
date_from=request.args.get("date_from"),
|
||||
date_to=request.args.get("date_to"),
|
||||
recipe_id=(request.args.get("recipe_id") or "").strip() or None,
|
||||
recipe_ids=(request.args.get("recipe_ids") or "").strip() or None,
|
||||
client_id=(request.args.get("client_id") or "").strip() or None,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@bp.get("/stock-forecast")
|
||||
@require_auth
|
||||
def stock_forecast():
|
||||
from app.models import Component
|
||||
|
||||
return jsonify(
|
||||
build_stock_forecast(
|
||||
plan_date=request.args.get("plan_date"),
|
||||
Component=Component,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def _filter_context_kwargs():
|
||||
return {
|
||||
"filter_farms": (request.args.get("filter_farms") or "").strip() or None,
|
||||
"filter_dispensers": (request.args.get("filter_dispensers") or "").strip() or None,
|
||||
"filter_recipes": (request.args.get("filter_recipes") or "").strip() or None,
|
||||
}
|
||||
|
||||
|
||||
@bp.get("/export")
|
||||
@require_auth
|
||||
def analytics_export():
|
||||
date_from, date_to, recipe_id, recipe_ids, err = _export_params()
|
||||
if err:
|
||||
return err
|
||||
fmt = (request.args.get("format") or "xlsx").strip().lower()
|
||||
section = (request.args.get("section") or "all").strip().lower()
|
||||
sections = parse_export_sections(section)
|
||||
base_name = export_filename_base(
|
||||
date_from=date_from, date_to=date_to, sections=sections
|
||||
)
|
||||
ctx = _filter_context_kwargs()
|
||||
if fmt == "pdf":
|
||||
data = build_analytics_pdf(
|
||||
date_from=date_from,
|
||||
date_to=date_to,
|
||||
recipe_id=recipe_id,
|
||||
recipe_ids=recipe_ids,
|
||||
section=section,
|
||||
**ctx,
|
||||
)
|
||||
return Response(
|
||||
data,
|
||||
mimetype="application/pdf",
|
||||
headers={
|
||||
"Content-Disposition": f'attachment; filename="{base_name}.pdf"'
|
||||
},
|
||||
)
|
||||
if fmt in ("xlsx", "excel"):
|
||||
data = build_finance_summary_xlsx(
|
||||
date_from=date_from,
|
||||
date_to=date_to,
|
||||
recipe_id=recipe_id,
|
||||
recipe_ids=recipe_ids,
|
||||
section=section,
|
||||
**ctx,
|
||||
)
|
||||
return Response(
|
||||
data,
|
||||
mimetype="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
headers={
|
||||
"Content-Disposition": f'attachment; filename="{base_name}.xlsx"'
|
||||
},
|
||||
)
|
||||
return _error("format должен быть xlsx или pdf", 400)
|
||||
|
||||
|
||||
@bp.get("/finance/export")
|
||||
@require_auth
|
||||
def finance_export():
|
||||
date_from, date_to, recipe_id, recipe_ids, err = _export_params()
|
||||
if err:
|
||||
return err
|
||||
section = (request.args.get("section") or "all").strip().lower()
|
||||
sections = parse_export_sections(section)
|
||||
data = build_finance_summary_xlsx(
|
||||
date_from=date_from,
|
||||
date_to=date_to,
|
||||
recipe_id=recipe_id,
|
||||
recipe_ids=recipe_ids,
|
||||
section=section,
|
||||
**_filter_context_kwargs(),
|
||||
)
|
||||
filename = f"{export_filename_base(date_from=date_from, date_to=date_to, sections=sections)}.xlsx"
|
||||
return Response(
|
||||
data,
|
||||
mimetype="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
headers={"Content-Disposition": f'attachment; filename="{filename}"'},
|
||||
)
|
||||
Reference in New Issue
Block a user