Finance-Accounts/ar-aging-app/backend/app/api/routes/export.py

65 lines
2.8 KiB
Python

"""Generate and download the Accounts Receivable Aging workbook."""
from __future__ import annotations
import os
from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException
from fastapi.responses import FileResponse
from sqlalchemy.orm import Session as OrmSession
from ...db import models
from ...services.jobs import run_export, run_summary_export
from ..deps import db_dep, get_session_or_404
router = APIRouter(prefix="/api/sessions", tags=["export"])
@router.post("/{session_id}/export")
def start_export(session_id: int, background: BackgroundTasks, kind: str = "full",
db: OrmSession = Depends(db_dep)) -> dict:
"""kind='summary' → compact Finance pack (fast); kind='full' → complete audit workbook."""
if kind not in ("full", "summary"):
raise HTTPException(400, "kind must be 'full' or 'summary'.")
s = get_session_or_404(session_id, db)
if s.status not in ("processed", "exporting", "completed"):
raise HTTPException(400, "Process the session before exporting.")
if s.status == "exporting":
raise HTTPException(409, "Export already in progress.")
# Flip status synchronously so the frontend starts polling immediately.
s.status = "exporting"
s.progress_stage = "Queued for export"
s.progress_pct = 0.0
s.progress_rows_done = 0
s.progress_rows_total = 0
s.eta_seconds = 0
s.error = ""
db.commit()
background.add_task(run_summary_export if kind == "summary" else run_export, session_id)
return {"started": True, "kind": kind}
@router.get("/{session_id}/exports")
def list_exports(session_id: int, db: OrmSession = Depends(db_dep)) -> list[dict]:
get_session_or_404(session_id, db)
rows = db.query(models.ExportRecord).filter(
models.ExportRecord.session_id == session_id).order_by(
models.ExportRecord.generated_at.desc()).all()
return [{"id": r.id, "kind": r.kind or "full", "sha256": r.sha256, "size_bytes": r.size_bytes,
"generated_at": r.generated_at.isoformat() if r.generated_at else None,
"available": bool(r.path and os.path.exists(r.path))} for r in rows]
@router.get("/{session_id}/export/download")
def download_export(session_id: int, kind: str = "full", db: OrmSession = Depends(db_dep)):
get_session_or_404(session_id, db)
q = db.query(models.ExportRecord).filter(models.ExportRecord.session_id == session_id)
if kind in ("full", "summary"):
q = q.filter(models.ExportRecord.kind == kind)
r = q.order_by(models.ExportRecord.generated_at.desc()).first()
if not r or not r.path or not os.path.exists(r.path):
raise HTTPException(404, "No export available; generate it first.")
return FileResponse(
r.path, filename=os.path.basename(r.path),
media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
)