loapi/app/resources/payoff.py
2025-12-31 12:13:43 -06:00

103 lines
3.5 KiB
Python

"""Pay-off endpoints."""
from datetime import date
from decimal import Decimal
from enum import Enum
from typing import Optional
from fastapi import APIRouter, Depends, HTTPException, status
from pydantic import BaseModel, Field, field_validator
from sqlalchemy.orm import Session
from app.database import get_db, PO10DAY
from app.logging import get_logger
router = APIRouter()
logger = get_logger(__name__)
class PayoffStatus(str, Enum):
"""Enum for payoff status values."""
PROCESSED = "processed"
PENDING = "pending"
FAILED = "failed"
CANCELLED = "cancelled"
class PayoffRequest(BaseModel):
"""Request model for loan payoff."""
loan_id: str = Field(..., description="Loan identifier", min_length=1, max_length=50)
payoff_amount: Decimal = Field(..., description="Payoff amount", gt=0, decimal_places=2)
payoff_date: date = Field(..., description="Payoff date")
borrower_name: Optional[str] = Field(None, description="Borrower name", max_length=100)
payment_method: str = Field(..., description="Payment method", regex="^(check|wire|ach|cash)$")
notes: Optional[str] = Field(None, description="Additional notes", max_length=500)
@field_validator('payoff_date')
def validate_payoff_date(cls, v):
"""Validate payoff date is not in the future."""
from datetime import date
if v > date.today():
raise ValueError('Payoff date cannot be in the future')
return v
class PayoffResponse(BaseModel):
"""Response model for loan payoff."""
status: PayoffStatus
loan_id: str
payoff_amount: Decimal
payoff_date: date
transaction_id: str
@router.post("/payoff", response_model=PayoffResponse, status_code=status.HTTP_201_CREATED)
async def create_payoff(payoff_request: PayoffRequest, db: Session = Depends(get_db)) -> PayoffResponse:
"""Create a loan payoff record."""
logger.info(f"Processing payoff for loan {payoff_request.loan_id}")
# Check if loan_id already exists
existing_payoff = db.query(PO10DAY).filter(PO10DAY.loan_id == payoff_request.loan_id).first()
if existing_payoff:
logger.error(f"Payoff already exists for loan {payoff_request.loan_id}")
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail=f"Payoff already exists for loan {payoff_request.loan_id}"
)
import uuid
transaction_id = str(uuid.uuid4())
# Create database entry
try:
po10day_record = PO10DAY(
loan_id=payoff_request.loan_id,
payoff_amount=payoff_request.payoff_amount,
payoff_date=payoff_request.payoff_date,
borrower_name=payoff_request.borrower_name,
payment_method=payoff_request.payment_method,
notes=payoff_request.notes,
transaction_id=transaction_id
)
db.add(po10day_record)
db.commit()
db.refresh(po10day_record)
logger.info(f"Payoff created successfully for loan {payoff_request.loan_id}, transaction {transaction_id}")
except Exception as e:
db.rollback()
logger.error(f"Failed to create payoff for loan {payoff_request.loan_id}", exc_info=e)
raise e
response = PayoffResponse(
status=PayoffStatus.PROCESSED,
loan_id=payoff_request.loan_id,
payoff_amount=payoff_request.payoff_amount,
payoff_date=payoff_request.payoff_date,
transaction_id=transaction_id
)
return response