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
This commit is contained in:
@@ -30,12 +30,6 @@ async def preview_test_seat(
|
|||||||
seat_id=seat_id,
|
seat_id=seat_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
if not seat.seat_id:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
|
||||||
detail="Невозможно построить preview: у места отсутствует seat_id",
|
|
||||||
)
|
|
||||||
|
|
||||||
matched_rule_level = None
|
matched_rule_level = None
|
||||||
matched_target_ref = None
|
matched_target_ref = None
|
||||||
pricing_category_id = None
|
pricing_category_id = None
|
||||||
@@ -43,6 +37,27 @@ async def preview_test_seat(
|
|||||||
currency = None
|
currency = None
|
||||||
has_price = False
|
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:
|
try:
|
||||||
matched_rule_level, rule = await find_effective_price_rule(
|
matched_rule_level, rule = await find_effective_price_rule(
|
||||||
scheme_id=scheme.scheme_id,
|
scheme_id=scheme.scheme_id,
|
||||||
@@ -52,7 +67,7 @@ async def preview_test_seat(
|
|||||||
)
|
)
|
||||||
matched_target_ref = rule["target_ref"]
|
matched_target_ref = rule["target_ref"]
|
||||||
pricing_category_id = rule["pricing_category_id"]
|
pricing_category_id = rule["pricing_category_id"]
|
||||||
amount = rule["amount"]
|
amount = str(rule["amount"])
|
||||||
currency = rule["currency"]
|
currency = rule["currency"]
|
||||||
has_price = True
|
has_price = True
|
||||||
except HTTPException as exc:
|
except HTTPException as exc:
|
||||||
@@ -66,15 +81,25 @@ async def preview_test_seat(
|
|||||||
)
|
)
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||||
detail=f"Не удалось построить preview: {exc.__class__.__name__}: {exc}",
|
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(
|
return TestSeatPreviewResponse(
|
||||||
scheme_id=scheme.scheme_id,
|
scheme_id=scheme.scheme_id,
|
||||||
scheme_version_id=version.scheme_version_id,
|
scheme_version_id=version.scheme_version_id,
|
||||||
seat_id=seat.seat_id,
|
seat_id=seat.seat_id,
|
||||||
element_id=seat.element_id,
|
element_id=seat.element_id,
|
||||||
sector_id=seat.sector_id,
|
sector_id=seat.seat_id and seat.sector_id,
|
||||||
group_id=seat.group_id,
|
group_id=seat.group_id,
|
||||||
row_label=seat.row_label,
|
row_label=seat.row_label,
|
||||||
seat_number=seat.seat_number,
|
seat_number=seat.seat_number,
|
||||||
@@ -85,4 +110,6 @@ async def preview_test_seat(
|
|||||||
pricing_category_id=pricing_category_id,
|
pricing_category_id=pricing_category_id,
|
||||||
amount=amount,
|
amount=amount,
|
||||||
currency=currency,
|
currency=currency,
|
||||||
|
reason_code=reason_code,
|
||||||
|
reason_message=reason_message,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
from decimal import Decimal
|
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
class TestSeatPreviewResponse(BaseModel):
|
class TestSeatPreviewResponse(BaseModel):
|
||||||
scheme_id: str
|
scheme_id: str
|
||||||
scheme_version_id: str
|
scheme_version_id: str
|
||||||
seat_id: str
|
seat_id: str | None
|
||||||
element_id: str | None
|
element_id: str | None
|
||||||
sector_id: str | None
|
sector_id: str | None
|
||||||
group_id: str | None
|
group_id: str | None
|
||||||
@@ -17,5 +15,7 @@ class TestSeatPreviewResponse(BaseModel):
|
|||||||
matched_rule_level: str | None
|
matched_rule_level: str | None
|
||||||
matched_target_ref: str | None
|
matched_target_ref: str | None
|
||||||
pricing_category_id: str | None
|
pricing_category_id: str | None
|
||||||
amount: Decimal | None
|
amount: str | None
|
||||||
currency: str | None
|
currency: str | None
|
||||||
|
reason_code: str
|
||||||
|
reason_message: str
|
||||||
|
|||||||
@@ -84,3 +84,5 @@
|
|||||||
- This file is an operational route index, not a generated OpenAPI export.
|
- This file is an operational route index, not a generated OpenAPI export.
|
||||||
- Update this map in the same change set when adding, removing, renaming, or moving routes.
|
- Update this map in the same change set when adding, removing, renaming, or moving routes.
|
||||||
- Query guards such as expected_current_scheme_version_id / expected_scheme_version_id are part of the operational contract for optimistic concurrency on mutable flows.
|
- Query guards such as expected_current_scheme_version_id / expected_scheme_version_id are part of the operational contract for optimistic concurrency on mutable flows.
|
||||||
|
|
||||||
|
- Test mode response now includes reason_code and reason_message for sellability diagnostics.
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ Validate:
|
|||||||
Validate:
|
Validate:
|
||||||
- pricing bundle contains categories and rules arrays
|
- pricing bundle contains categories and rules arrays
|
||||||
- effective seat price resolves according to domain priority
|
- effective seat price resolves according to domain priority
|
||||||
- test seat preview explains selectable / has_price state
|
- test seat preview explains selectable / has_price state and returns reason_code / reason_message
|
||||||
- pricing write responses are stable and typed
|
- pricing write responses are stable and typed
|
||||||
- stale pricing mutation returns `detail.code = stale_draft_version`
|
- stale pricing mutation returns `detail.code = stale_draft_version`
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user