65 lines
2.8 KiB
Python
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",
|
|
)
|