feat: Implement currency management with models, routes, and UI updates; add backfill script for existing records
This commit is contained in:
@@ -16,7 +16,8 @@ class _CostBase(BaseModel):
|
||||
scenario_id: int
|
||||
amount: float
|
||||
description: Optional[str] = None
|
||||
currency_code: str = "USD"
|
||||
currency_code: Optional[str] = "USD"
|
||||
currency_id: Optional[int] = None
|
||||
|
||||
@field_validator("currency_code")
|
||||
@classmethod
|
||||
@@ -31,8 +32,12 @@ class CapexCreate(_CostBase):
|
||||
|
||||
class CapexRead(_CostBase):
|
||||
id: int
|
||||
# use from_attributes so Pydantic reads attributes off SQLAlchemy model
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
# optionally include nested currency info
|
||||
currency: Optional["CurrencyRead"] = None
|
||||
|
||||
|
||||
class OpexCreate(_CostBase):
|
||||
pass
|
||||
@@ -41,12 +46,41 @@ class OpexCreate(_CostBase):
|
||||
class OpexRead(_CostBase):
|
||||
id: int
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
currency: Optional["CurrencyRead"] = None
|
||||
|
||||
|
||||
class CurrencyRead(BaseModel):
|
||||
id: int
|
||||
code: str
|
||||
name: Optional[str] = None
|
||||
symbol: Optional[str] = None
|
||||
is_active: Optional[bool] = True
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
# forward refs
|
||||
CapexRead.model_rebuild()
|
||||
OpexRead.model_rebuild()
|
||||
|
||||
|
||||
# Capex endpoints
|
||||
@router.post("/capex", response_model=CapexRead)
|
||||
def create_capex(item: CapexCreate, db: Session = Depends(get_db)):
|
||||
db_item = Capex(**item.model_dump())
|
||||
payload = item.model_dump()
|
||||
# Prefer explicit currency_id if supplied
|
||||
cid = payload.get("currency_id")
|
||||
if not cid:
|
||||
code = (payload.pop("currency_code", "USD") or "USD").strip().upper()
|
||||
currency_cls = __import__(
|
||||
"models.currency", fromlist=["Currency"]).Currency
|
||||
currency = db.query(currency_cls).filter_by(code=code).one_or_none()
|
||||
if currency is None:
|
||||
currency = currency_cls(code=code, name=code, symbol=None)
|
||||
db.add(currency)
|
||||
db.flush()
|
||||
payload["currency_id"] = currency.id
|
||||
db_item = Capex(**payload)
|
||||
db.add(db_item)
|
||||
db.commit()
|
||||
db.refresh(db_item)
|
||||
@@ -61,7 +95,19 @@ def list_capex(db: Session = Depends(get_db)):
|
||||
# Opex endpoints
|
||||
@router.post("/opex", response_model=OpexRead)
|
||||
def create_opex(item: OpexCreate, db: Session = Depends(get_db)):
|
||||
db_item = Opex(**item.model_dump())
|
||||
payload = item.model_dump()
|
||||
cid = payload.get("currency_id")
|
||||
if not cid:
|
||||
code = (payload.pop("currency_code", "USD") or "USD").strip().upper()
|
||||
currency_cls = __import__(
|
||||
"models.currency", fromlist=["Currency"]).Currency
|
||||
currency = db.query(currency_cls).filter_by(code=code).one_or_none()
|
||||
if currency is None:
|
||||
currency = currency_cls(code=code, name=code, symbol=None)
|
||||
db.add(currency)
|
||||
db.flush()
|
||||
payload["currency_id"] = currency.id
|
||||
db_item = Opex(**payload)
|
||||
db.add(db_item)
|
||||
db.commit()
|
||||
db.refresh(db_item)
|
||||
|
||||
Reference in New Issue
Block a user