feat: Enhance transaction model and dashboard with Azure OAuth integration

This commit is contained in:
2026-05-10 22:36:13 +12:00
parent d50c1c5bba
commit 9130629b58
10 changed files with 403 additions and 26 deletions
+20 -5
View File
@@ -13,7 +13,9 @@ from app.service.recon_service import (
router = APIRouter(prefix="/api", tags=["Transactions"])
@router.get("/transactions", response_model=list[Transaction], summary="List transactions")
@router.get(
"/transactions", response_model=list[Transaction], summary="List transactions"
)
async def list_transactions_endpoint(
status: StatusType | None = Query(None, description="Filter by status"),
flag: FlagType | None = Query(None, description="Filter by flag"),
@@ -21,7 +23,11 @@ async def list_transactions_endpoint(
return list_transactions(status=status, flag=flag)
@router.get("/transactions/{transaction_id}", response_model=Transaction, summary="Get a transaction")
@router.get(
"/transactions/{transaction_id}",
response_model=Transaction,
summary="Get a transaction",
)
async def get_transaction_endpoint(transaction_id: str) -> Transaction:
transaction = get_transaction_by_id(transaction_id)
if transaction is None:
@@ -29,15 +35,24 @@ async def get_transaction_endpoint(transaction_id: str) -> Transaction:
return transaction
@router.post("/transactions", response_model=Transaction, status_code=201, summary="Submit a transaction")
@router.post(
"/transactions",
response_model=Transaction,
status_code=201,
summary="Submit a transaction",
)
async def create_transaction_endpoint(transaction: Transaction) -> Transaction:
created = add_transaction(transaction)
if not created:
raise HTTPException(status_code=409, detail=f"{transaction.transaction_id} already exists")
raise HTTPException(
status_code=409, detail=f"{transaction.transaction_id} already exists"
)
return transaction
@router.delete("/transactions/{transaction_id}", status_code=204, summary="Delete a transaction")
@router.delete(
"/transactions/{transaction_id}", status_code=204, summary="Delete a transaction"
)
async def delete_transaction_endpoint(transaction_id: str) -> None:
removed = delete_transaction_by_id(transaction_id)
if not removed:
+6 -2
View File
@@ -11,14 +11,18 @@ load_dotenv(PROJECT_ROOT / ".env")
class Settings:
def __init__(self) -> None:
self.session_secret_key = getenv("SESSION_SECRET_KEY", "change-me-in-production")
self.session_secret_key = getenv(
"SESSION_SECRET_KEY", "change-me-in-production"
)
self.azure_tenant_id = getenv("AZURE_TENANT_ID")
self.azure_client_id = getenv("AZURE_CLIENT_ID")
self.azure_client_secret = getenv("AZURE_CLIENT_SECRET")
@property
def azure_configured(self) -> bool:
return bool(self.azure_tenant_id and self.azure_client_id and self.azure_client_secret)
return bool(
self.azure_tenant_id and self.azure_client_id and self.azure_client_secret
)
@lru_cache
-1
View File
@@ -8,4 +8,3 @@ app = create_app()
if __name__ == "__main__":
uvicorn.run("app.main:app", host="0.0.0.0", port=8000, reload=True)
+6
View File
@@ -11,6 +11,12 @@ class Transaction(BaseModel):
amount: float
status: Literal["Matched", "Unmatched", "Pending"]
flag: Literal["None", "Duplicate", "Threshold Breach", "Manual Review"]
reference_id: str
counterparty: str
currency: str
booking_date: date
settlement_date: date
description: str
class ReconSummary(BaseModel):
+243 -1
View File
@@ -15,6 +15,12 @@ TRANSACTIONS: list[Transaction] = [
amount=12400.00,
status="Matched",
flag="None",
reference_id="REF-2026-0001",
counterparty="Morgan Stanley",
currency="USD",
booking_date=date(2026, 4, 9),
settlement_date=date(2026, 4, 12),
description="FX Swap USD/EUR",
),
Transaction(
transaction_id="TXN-002",
@@ -23,6 +29,12 @@ TRANSACTIONS: list[Transaction] = [
amount=3750.50,
status="Unmatched",
flag="Duplicate",
reference_id="REF-2026-0002",
counterparty="JPMorgan Chase",
currency="USD",
booking_date=date(2026, 4, 10),
settlement_date=date(2026, 4, 13),
description="Interest rate swap",
),
Transaction(
transaction_id="TXN-003",
@@ -31,6 +43,12 @@ TRANSACTIONS: list[Transaction] = [
amount=88200.00,
status="Unmatched",
flag="Threshold Breach",
reference_id="REF-2026-0003",
counterparty="Goldman Sachs",
currency="GBP",
booking_date=date(2026, 4, 10),
settlement_date=date(2026, 4, 14),
description="Cross-currency swap",
),
Transaction(
transaction_id="TXN-004",
@@ -39,6 +57,12 @@ TRANSACTIONS: list[Transaction] = [
amount=540.00,
status="Matched",
flag="None",
reference_id="REF-2026-0004",
counterparty="Barclays",
currency="EUR",
booking_date=date(2026, 4, 11),
settlement_date=date(2026, 4, 15),
description="Bond settlement",
),
Transaction(
transaction_id="TXN-005",
@@ -47,11 +71,229 @@ TRANSACTIONS: list[Transaction] = [
amount=21000.00,
status="Pending",
flag="Manual Review",
reference_id="REF-2026-0005",
counterparty="Deutsche Bank",
currency="USD",
booking_date=date(2026, 4, 12),
settlement_date=date(2026, 4, 16),
description="Equity options trade",
),
Transaction(
transaction_id="TXN-006",
date=date(2026, 4, 14),
account="ACC-5542",
amount=15600.00,
status="Matched",
flag="None",
reference_id="REF-2026-0006",
counterparty="Credit Suisse",
currency="CHF",
booking_date=date(2026, 4, 13),
settlement_date=date(2026, 4, 17),
description="Repo transaction",
),
Transaction(
transaction_id="TXN-007",
date=date(2026, 4, 14),
account="ACC-8834",
amount=2250.75,
status="Unmatched",
flag="Threshold Breach",
reference_id="REF-2026-0007",
counterparty="Citigroup",
currency="JPY",
booking_date=date(2026, 4, 13),
settlement_date=date(2026, 4, 18),
description="Forward contract",
),
Transaction(
transaction_id="TXN-008",
date=date(2026, 4, 15),
account="ACC-2201",
amount=45000.00,
status="Matched",
flag="None",
reference_id="REF-2026-0008",
counterparty="UBS",
currency="USD",
booking_date=date(2026, 4, 14),
settlement_date=date(2026, 4, 19),
description="Commodity swap",
),
Transaction(
transaction_id="TXN-009",
date=date(2026, 4, 15),
account="ACC-7123",
amount=8900.50,
status="Pending",
flag="Manual Review",
reference_id="REF-2026-0009",
counterparty="HSBC",
currency="AUD",
booking_date=date(2026, 4, 14),
settlement_date=date(2026, 4, 20),
description="Index futures",
),
Transaction(
transaction_id="TXN-010",
date=date(2026, 4, 16),
account="ACC-3345",
amount=1200.00,
status="Matched",
flag="None",
reference_id="REF-2026-0010",
counterparty="Bank of America",
currency="USD",
booking_date=date(2026, 4, 15),
settlement_date=date(2026, 4, 21),
description="Interest rate cap",
),
Transaction(
transaction_id="TXN-011",
date=date(2026, 4, 16),
account="ACC-5501",
amount=67500.00,
status="Unmatched",
flag="Duplicate",
reference_id="REF-2026-0011",
counterparty="Wells Fargo",
currency="EUR",
booking_date=date(2026, 4, 15),
settlement_date=date(2026, 4, 22),
description="Currency forward",
),
Transaction(
transaction_id="TXN-012",
date=date(2026, 4, 17),
account="ACC-6789",
amount=3400.25,
status="Matched",
flag="None",
reference_id="REF-2026-0012",
counterparty="BNY Mellon",
currency="GBP",
booking_date=date(2026, 4, 16),
settlement_date=date(2026, 4, 23),
description="Bond purchase",
),
Transaction(
transaction_id="TXN-013",
date=date(2026, 4, 17),
account="ACC-1234",
amount=52100.00,
status="Pending",
flag="Threshold Breach",
reference_id="REF-2026-0013",
counterparty="State Street",
currency="JPY",
booking_date=date(2026, 4, 16),
settlement_date=date(2026, 4, 24),
description="Swaption settlement",
),
Transaction(
transaction_id="TXN-014",
date=date(2026, 4, 18),
account="ACC-9876",
amount=920.50,
status="Matched",
flag="None",
reference_id="REF-2026-0014",
counterparty="Nomura",
currency="CHF",
booking_date=date(2026, 4, 17),
settlement_date=date(2026, 4, 25),
description="Basket trade",
),
Transaction(
transaction_id="TXN-015",
date=date(2026, 4, 18),
account="ACC-4455",
amount=29300.00,
status="Unmatched",
flag="Manual Review",
reference_id="REF-2026-0015",
counterparty="Mizuho",
currency="USD",
booking_date=date(2026, 4, 17),
settlement_date=date(2026, 4, 26),
description="Variance swap",
),
Transaction(
transaction_id="TXN-016",
date=date(2026, 4, 19),
account="ACC-2288",
amount=11500.75,
status="Matched",
flag="None",
reference_id="REF-2026-0016",
counterparty="RBC",
currency="CAD",
booking_date=date(2026, 4, 18),
settlement_date=date(2026, 4, 27),
description="CDS contract",
),
Transaction(
transaction_id="TXN-017",
date=date(2026, 4, 19),
account="ACC-3399",
amount=76800.00,
status="Unmatched",
flag="Threshold Breach",
reference_id="REF-2026-0017",
counterparty="Scotiabank",
currency="AUD",
booking_date=date(2026, 4, 18),
settlement_date=date(2026, 4, 28),
description="Total return swap",
),
Transaction(
transaction_id="TXN-018",
date=date(2026, 4, 20),
account="ACC-5566",
amount=4100.25,
status="Matched",
flag="None",
reference_id="REF-2026-0018",
counterparty="TD Bank",
currency="USD",
booking_date=date(2026, 4, 19),
settlement_date=date(2026, 4, 29),
description="Equity collar",
),
Transaction(
transaction_id="TXN-019",
date=date(2026, 4, 20),
account="ACC-7788",
amount=18600.00,
status="Pending",
flag="Manual Review",
reference_id="REF-2026-0019",
counterparty="MUFG",
currency="EUR",
booking_date=date(2026, 4, 19),
settlement_date=date(2026, 4, 30),
description="Volatility swap",
),
Transaction(
transaction_id="TXN-020",
date=date(2026, 4, 21),
account="ACC-9900",
amount=8250.50,
status="Matched",
flag="None",
reference_id="REF-2026-0020",
counterparty="Sumitomo Mitsui",
currency="GBP",
booking_date=date(2026, 4, 20),
settlement_date=date(2026, 5, 1),
description="Knock-out option",
),
]
def list_transactions(status: StatusType | None = None, flag: FlagType | None = None) -> list[Transaction]:
def list_transactions(
status: StatusType | None = None, flag: FlagType | None = None
) -> list[Transaction]:
results = TRANSACTIONS
if status:
results = [item for item in results if item.status == status]
+14 -6
View File
@@ -12,27 +12,35 @@ project_root = Path(__file__).resolve().parents[2]
templates = Jinja2Templates(directory=str(project_root / "data" / "templates"))
@router.get("/", response_class=HTMLResponse, include_in_schema=False)
@router.get("/", response_model=None, include_in_schema=False)
async def dashboard(
request: Request,
recon_job_name: str | None = Query(default=None),
as_at_date: str | None = Query(default=None),
) -> HTMLResponse:
) -> HTMLResponse | RedirectResponse:
user = request.session.get("user")
if not user:
return RedirectResponse(url="/login", status_code=302)
# if not user:
# return RedirectResponse(url="/login", status_code=302)
transactions = list_transactions()
if as_at_date:
transactions = [item for item in transactions if item.date.isoformat() == as_at_date]
transactions = [
item for item in transactions if item.date.isoformat() == as_at_date
]
results = [
{
"Transaction ID": item.transaction_id,
"Txn ID": item.transaction_id,
"Date": item.date.isoformat(),
"Ref ID": item.reference_id,
"Account": item.account,
"Counterparty": item.counterparty,
"Amount": f"{item.amount:,.2f}",
"CCY": item.currency,
"Booking": item.booking_date.isoformat(),
"Settlement": item.settlement_date.isoformat(),
"Description": item.description,
"Status": item.status,
"Flag": item.flag,
}