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
116 lines
3.9 KiB
Python
116 lines
3.9 KiB
Python
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,
|
||
)
|