"""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)