Files
svg-backend/backend/app/api/routes/test_mode.py
greebo aab5a51654 feat(backend): add sellability reason codes and string price serialization to test seat preview
extend test seat preview with explicit sellability reason codes

serialize preview price amount as string for a stable API contract
improve diagnosis of non-sellable states for preview consumers
2026-03-19 20:07:19 +03:00

116 lines
3.9 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import logging
from fastapi import APIRouter, Depends, HTTPException, status
from app.core.config import settings
from app.repositories.pricing import find_effective_price_rule
from app.repositories.scheme_seats import get_scheme_version_seat_by_seat_id
from app.repositories.scheme_versions import get_current_scheme_version
from app.repositories.schemes import get_scheme_record_by_scheme_id
from app.schemas.test_mode import TestSeatPreviewResponse
from app.security.auth import require_api_key
router = APIRouter()
logger = logging.getLogger(__name__)
@router.get(f"{settings.api_v1_prefix}/schemes/{{scheme_id}}/test/seats/{{seat_id}}", response_model=TestSeatPreviewResponse)
async def preview_test_seat(
scheme_id: str,
seat_id: str,
role: str = Depends(require_api_key),
):
scheme = await get_scheme_record_by_scheme_id(scheme_id)
version = await get_current_scheme_version(
scheme_id=scheme.scheme_id,
current_version_number=scheme.current_version_number,
)
seat = await get_scheme_version_seat_by_seat_id(
scheme_version_id=version.scheme_version_id,
seat_id=seat_id,
)
matched_rule_level = None
matched_target_ref = None
pricing_category_id = None
amount = None
currency = None
has_price = False
if not seat.seat_id:
return TestSeatPreviewResponse(
scheme_id=scheme.scheme_id,
scheme_version_id=version.scheme_version_id,
seat_id=seat.seat_id,
element_id=seat.element_id,
sector_id=seat.sector_id,
group_id=seat.group_id,
row_label=seat.row_label,
seat_number=seat.seat_number,
selectable=False,
has_price=False,
matched_rule_level=None,
matched_target_ref=None,
pricing_category_id=None,
amount=None,
currency=None,
reason_code="missing_seat_id",
reason_message="Seat is not sellable because seat_id is missing.",
)
try:
matched_rule_level, rule = await find_effective_price_rule(
scheme_id=scheme.scheme_id,
seat_id=seat.seat_id,
group_id=seat.group_id,
sector_id=seat.sector_id,
)
matched_target_ref = rule["target_ref"]
pricing_category_id = rule["pricing_category_id"]
amount = str(rule["amount"])
currency = rule["currency"]
has_price = True
except HTTPException as exc:
if exc.status_code != status.HTTP_404_NOT_FOUND:
raise
except Exception as exc:
logger.exception(
"preview_test_seat failed for scheme_id=%s seat_id=%s",
scheme_id,
seat_id,
)
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail={
"code": "test_preview_failed",
"message": f"Не удалось построить preview: {exc.__class__.__name__}: {exc}",
},
)
if has_price:
reason_code = "ok"
reason_message = "Seat is sellable."
else:
reason_code = "no_price_rule"
reason_message = "Seat is not sellable because no effective price rule was found."
return TestSeatPreviewResponse(
scheme_id=scheme.scheme_id,
scheme_version_id=version.scheme_version_id,
seat_id=seat.seat_id,
element_id=seat.element_id,
sector_id=seat.seat_id and seat.sector_id,
group_id=seat.group_id,
row_label=seat.row_label,
seat_number=seat.seat_number,
selectable=has_price,
has_price=has_price,
matched_rule_level=matched_rule_level,
matched_target_ref=matched_target_ref,
pricing_category_id=pricing_category_id,
amount=amount,
currency=currency,
reason_code=reason_code,
reason_message=reason_message,
)