116 lines
3.7 KiB
Python
116 lines
3.7 KiB
Python
"""Point ledger APIs — Phase 7.2."""
|
|
from __future__ import annotations
|
|
|
|
from uuid import UUID
|
|
|
|
from fastapi import APIRouter, Depends, Query
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.api.deps import get_db, require_tenant
|
|
from app.api.permissions import require_permissions
|
|
from app.models.types import LedgerEntryType
|
|
from app.permissions.definitions import (
|
|
LOYALTY_POINTS_ADJUST,
|
|
LOYALTY_POINTS_EARN,
|
|
LOYALTY_POINTS_EXPIRE,
|
|
LOYALTY_POINTS_REDEEM,
|
|
LOYALTY_POINTS_VIEW,
|
|
)
|
|
from app.schemas.ledger import (
|
|
PointAdjustRequest,
|
|
PointBalanceRead,
|
|
PointEarnRequest,
|
|
PointExpireRequest,
|
|
PointLedgerEntryRead,
|
|
PointLedgerPage,
|
|
PointRedeemRequest,
|
|
)
|
|
from app.services.point_engine import PointEngineService
|
|
from shared.security import CurrentUser
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
@router.get("/{account_id}/balance", response_model=PointBalanceRead)
|
|
async def get_balance(
|
|
account_id: UUID,
|
|
tenant_id: UUID = Depends(require_tenant),
|
|
db: AsyncSession = Depends(get_db),
|
|
_user: CurrentUser = Depends(require_permissions(LOYALTY_POINTS_VIEW)),
|
|
):
|
|
return await PointEngineService(db).get_balance(tenant_id, account_id)
|
|
|
|
|
|
@router.get("/{account_id}/ledger", response_model=PointLedgerPage)
|
|
async def list_ledger(
|
|
account_id: UUID,
|
|
offset: int = Query(default=0, ge=0),
|
|
limit: int = Query(default=50, ge=1, le=200),
|
|
entry_type: LedgerEntryType | None = None,
|
|
q: str | None = Query(default=None, max_length=100),
|
|
tenant_id: UUID = Depends(require_tenant),
|
|
db: AsyncSession = Depends(get_db),
|
|
_user: CurrentUser = Depends(require_permissions(LOYALTY_POINTS_VIEW)),
|
|
):
|
|
rows, total = await PointEngineService(db).list_entries(
|
|
tenant_id,
|
|
account_id,
|
|
entry_type=entry_type,
|
|
offset=offset,
|
|
limit=limit,
|
|
q=q,
|
|
)
|
|
return PointLedgerPage(
|
|
items=rows, total=total, offset=offset, limit=limit
|
|
)
|
|
|
|
|
|
@router.post("/{account_id}/earn", response_model=PointLedgerEntryRead)
|
|
async def earn_points(
|
|
account_id: UUID,
|
|
body: PointEarnRequest,
|
|
tenant_id: UUID = Depends(require_tenant),
|
|
db: AsyncSession = Depends(get_db),
|
|
user: CurrentUser = Depends(require_permissions(LOYALTY_POINTS_EARN)),
|
|
):
|
|
return await PointEngineService(db).earn(tenant_id, account_id, body, actor=user)
|
|
|
|
|
|
@router.post("/{account_id}/redeem", response_model=PointLedgerEntryRead)
|
|
async def redeem_points(
|
|
account_id: UUID,
|
|
body: PointRedeemRequest,
|
|
tenant_id: UUID = Depends(require_tenant),
|
|
db: AsyncSession = Depends(get_db),
|
|
user: CurrentUser = Depends(require_permissions(LOYALTY_POINTS_REDEEM)),
|
|
):
|
|
return await PointEngineService(db).redeem(
|
|
tenant_id, account_id, body, actor=user
|
|
)
|
|
|
|
|
|
@router.post("/{account_id}/adjust", response_model=PointLedgerEntryRead)
|
|
async def adjust_points(
|
|
account_id: UUID,
|
|
body: PointAdjustRequest,
|
|
tenant_id: UUID = Depends(require_tenant),
|
|
db: AsyncSession = Depends(get_db),
|
|
user: CurrentUser = Depends(require_permissions(LOYALTY_POINTS_ADJUST)),
|
|
):
|
|
return await PointEngineService(db).adjust(
|
|
tenant_id, account_id, body, actor=user
|
|
)
|
|
|
|
|
|
@router.post("/{account_id}/expire-points", response_model=PointLedgerEntryRead)
|
|
async def expire_points(
|
|
account_id: UUID,
|
|
body: PointExpireRequest,
|
|
tenant_id: UUID = Depends(require_tenant),
|
|
db: AsyncSession = Depends(get_db),
|
|
user: CurrentUser = Depends(require_permissions(LOYALTY_POINTS_EXPIRE)),
|
|
):
|
|
return await PointEngineService(db).expire(
|
|
tenant_id, account_id, body, actor=user
|
|
)
|