96 lines
3.3 KiB
Python
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)
|