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

96 lines
3.3 KiB
Python

"""Session (month-end closing) CRUD and parameters."""
from __future__ import annotations
from datetime import date, datetime
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
from sqlalchemy.orm import Session as OrmSession
from ...config import DEFAULT_CLEARING_LAG_DAYS, DEFAULT_TOLERANCE
from ...db import models
from ..deps import db_dep, get_session_or_404, session_dict
router = APIRouter(prefix="/api/sessions", tags=["sessions"])
class SessionCreate(BaseModel):
name: str
month_end_date: date | None = None
reporting_currency: str = "USD"
clearing_lag_days: int = DEFAULT_CLEARING_LAG_DAYS
rounding_tolerance: float = DEFAULT_TOLERANCE
allowance_for_returns: float = 0.0
# zero (default) | carry_forward | manual
opening_mode: str = "zero"
opening_source_session_id: int | None = None
class SessionUpdate(BaseModel):
name: str | None = None
month_end_date: date | None = None
reporting_currency: str | None = None
clearing_lag_days: int | None = None
rounding_tolerance: float | None = None
allowance_for_returns: float | None = None
manual_adjustment: float | None = None
manual_adjustment_note: str | None = None
opening_mode: str | None = None
opening_source_session_id: int | None = None
@router.get("")
def list_sessions(db: OrmSession = Depends(db_dep)) -> list[dict]:
rows = db.query(models.Session).order_by(models.Session.created_at.desc()).all()
return [session_dict(s) for s in rows]
@router.post("")
def create_session(body: SessionCreate, db: OrmSession = Depends(db_dep)) -> dict:
me = body.month_end_date
s = models.Session(
name=body.name,
month_end_date=me,
reporting_month=me.strftime("%Y-%m") if me else None,
reporting_currency=body.reporting_currency,
clearing_lag_days=body.clearing_lag_days,
rounding_tolerance=body.rounding_tolerance,
allowance_for_returns=body.allowance_for_returns,
opening_mode=body.opening_mode or "zero",
opening_source_session_id=body.opening_source_session_id,
status="draft",
)
db.add(s)
db.commit()
from .ar import seed_opening_from_prior
seed_opening_from_prior(db, s)
return session_dict(s)
@router.get("/{session_id}")
def get_session(session_id: int, db: OrmSession = Depends(db_dep)) -> dict:
return session_dict(get_session_or_404(session_id, db))
@router.patch("/{session_id}")
def update_session(session_id: int, body: SessionUpdate,
db: OrmSession = Depends(db_dep)) -> dict:
s = get_session_or_404(session_id, db)
data = body.model_dump(exclude_unset=True)
for k, v in data.items():
setattr(s, k, v)
if "month_end_date" in data and s.month_end_date:
s.reporting_month = s.month_end_date.strftime("%Y-%m")
db.commit()
return session_dict(s)
@router.delete("/{session_id}")
def delete_session(session_id: int, db: OrmSession = Depends(db_dep)) -> dict:
"""Delete a closing and every row/file that belongs to it."""
s = get_session_or_404(session_id, db)
if s.status in ("processing", "exporting"):
raise HTTPException(409, "This closing is still processing — wait for it to finish.")
from ...services.store import purge_session
return purge_session(db, session_id)