109 lines
3.9 KiB
Python
109 lines
3.9 KiB
Python
"""Per-session settings (reserves, FX rates) + global header-mapping rules."""
|
|
from __future__ import annotations
|
|
|
|
from datetime import date
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
from pydantic import BaseModel
|
|
from sqlalchemy.orm import Session as OrmSession
|
|
|
|
from ...core.column_map import FIELD_ORDER, normalize_header
|
|
from ...db import models
|
|
from ..deps import db_dep, get_session_or_404, to_dict
|
|
|
|
router = APIRouter(prefix="/api/sessions", tags=["settings"])
|
|
rules_router = APIRouter(prefix="/api/mapping-rules", tags=["mapping"])
|
|
|
|
|
|
class RuleIn(BaseModel):
|
|
header: str # raw or normalized source header
|
|
field: str # canonical internal field
|
|
|
|
|
|
@rules_router.get("")
|
|
def list_rules(db: OrmSession = Depends(db_dep)) -> dict:
|
|
rows = db.query(models.MappingRule).order_by(models.MappingRule.normalized_header).all()
|
|
return {
|
|
"fields": FIELD_ORDER,
|
|
"rules": [{"id": r.id, "normalized_header": r.normalized_header, "field": r.field}
|
|
for r in rows],
|
|
}
|
|
|
|
|
|
@rules_router.put("")
|
|
def upsert_rules(items: list[RuleIn], db: OrmSession = Depends(db_dep)) -> dict:
|
|
"""Save admin header-alias rules; applied to every future parse (no code change needed)."""
|
|
for it in items:
|
|
if it.field not in FIELD_ORDER:
|
|
raise HTTPException(400, f"Unknown field '{it.field}'.")
|
|
norm = normalize_header(it.header)
|
|
if not norm:
|
|
raise HTTPException(400, "Empty header.")
|
|
row = db.query(models.MappingRule).filter(
|
|
models.MappingRule.normalized_header == norm).first()
|
|
if row is None:
|
|
db.add(models.MappingRule(normalized_header=norm, field=it.field))
|
|
else:
|
|
row.field = it.field
|
|
db.commit()
|
|
return list_rules(db)
|
|
|
|
|
|
@rules_router.delete("/{rule_id}")
|
|
def delete_rule(rule_id: int, db: OrmSession = Depends(db_dep)) -> dict:
|
|
db.query(models.MappingRule).filter(models.MappingRule.id == rule_id).delete()
|
|
db.commit()
|
|
return list_rules(db)
|
|
|
|
|
|
class ReserveIn(BaseModel):
|
|
marketplace: str
|
|
account_type: str
|
|
amount: float = 0.0
|
|
|
|
|
|
class FxIn(BaseModel):
|
|
marketplace: str
|
|
currency: str = "USD"
|
|
rate: float = 1.0
|
|
source: str = "manual"
|
|
rate_date: date | None = None
|
|
|
|
|
|
@router.get("/{session_id}/reserves")
|
|
def get_reserves(session_id: int, db: OrmSession = Depends(db_dep)) -> list[dict]:
|
|
get_session_or_404(session_id, db)
|
|
rows = db.query(models.Reserve).filter(models.Reserve.session_id == session_id).all()
|
|
return [to_dict(r, ["marketplace", "account_type", "amount"]) for r in rows]
|
|
|
|
|
|
@router.put("/{session_id}/reserves")
|
|
def put_reserves(session_id: int, items: list[ReserveIn],
|
|
db: OrmSession = Depends(db_dep)) -> list[dict]:
|
|
get_session_or_404(session_id, db)
|
|
db.query(models.Reserve).filter(models.Reserve.session_id == session_id).delete()
|
|
for it in items:
|
|
db.add(models.Reserve(session_id=session_id, marketplace=it.marketplace,
|
|
account_type=it.account_type, amount=it.amount))
|
|
db.commit()
|
|
return get_reserves(session_id, db)
|
|
|
|
|
|
@router.get("/{session_id}/fx")
|
|
def get_fx(session_id: int, db: OrmSession = Depends(db_dep)) -> list[dict]:
|
|
get_session_or_404(session_id, db)
|
|
rows = db.query(models.FxRate).filter(models.FxRate.session_id == session_id).all()
|
|
return [to_dict(r, ["marketplace", "currency", "rate", "source", "rate_date"]) for r in rows]
|
|
|
|
|
|
@router.put("/{session_id}/fx")
|
|
def put_fx(session_id: int, items: list[FxIn], db: OrmSession = Depends(db_dep)) -> list[dict]:
|
|
get_session_or_404(session_id, db)
|
|
db.query(models.FxRate).filter(models.FxRate.session_id == session_id).delete()
|
|
for it in items:
|
|
db.add(models.FxRate(session_id=session_id, marketplace=it.marketplace,
|
|
currency=it.currency, rate=it.rate, source=it.source,
|
|
rate_date=it.rate_date))
|
|
db.commit()
|
|
return get_fx(session_id, db)
|