Implement display artifacts, pricing integrity, draft base and publish preview bundle
This commit is contained in:
@@ -1,17 +1,40 @@
|
||||
from decimal import Decimal, InvalidOperation
|
||||
from typing import List
|
||||
|
||||
from pydantic import BaseModel, field_validator
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
|
||||
|
||||
def _validate_decimal_amount(value: Decimal) -> Decimal:
|
||||
try:
|
||||
normalized = Decimal(value)
|
||||
except (InvalidOperation, TypeError, ValueError) as exc:
|
||||
raise ValueError("Некорректная сумма") from exc
|
||||
|
||||
if not normalized.is_finite():
|
||||
raise ValueError("Некорректная сумма")
|
||||
|
||||
return normalized
|
||||
|
||||
|
||||
class DeleteResponse(BaseModel):
|
||||
status: str
|
||||
|
||||
|
||||
class PricingCategoryCreateRequest(BaseModel):
|
||||
name: str
|
||||
code: str | None = None
|
||||
name: str = Field(..., min_length=1, max_length=255)
|
||||
code: str | None = Field(default=None, max_length=128)
|
||||
|
||||
|
||||
class PricingCategoryUpdateRequest(BaseModel):
|
||||
name: str = Field(..., min_length=1, max_length=255)
|
||||
code: str | None = Field(default=None, max_length=128)
|
||||
|
||||
|
||||
class PricingCategoryItem(BaseModel):
|
||||
pricing_category_id: str
|
||||
scheme_id: str
|
||||
name: str
|
||||
code: str | None = None
|
||||
code: str | None
|
||||
created_at: str
|
||||
|
||||
|
||||
class PricingCategoryCreateResponse(BaseModel):
|
||||
@@ -28,98 +51,41 @@ class PricingCategoryUpdateResponse(BaseModel):
|
||||
code: str | None
|
||||
|
||||
|
||||
class DeleteResponse(BaseModel):
|
||||
status: str
|
||||
|
||||
|
||||
class PriceRuleCreateRequest(BaseModel):
|
||||
pricing_category_id: str | None = None
|
||||
target_type: str
|
||||
target_ref: str
|
||||
pricing_category_id: str | None = Field(default=None, max_length=32)
|
||||
target_type: str = Field(..., pattern="^(seat|group|sector)$")
|
||||
target_ref: str = Field(..., min_length=1, max_length=128)
|
||||
amount: Decimal
|
||||
currency: str = "RUB"
|
||||
|
||||
@field_validator("target_type")
|
||||
@classmethod
|
||||
def validate_target_type(cls, value: str) -> str:
|
||||
allowed = {"sector", "group", "seat"}
|
||||
if value not in allowed:
|
||||
raise ValueError("Поле target_type должно быть одним из: sector, group, seat")
|
||||
return value
|
||||
|
||||
@field_validator("currency")
|
||||
@classmethod
|
||||
def validate_currency(cls, value: str) -> str:
|
||||
if value != "RUB":
|
||||
raise ValueError("В v1 поддерживается только валюта RUB")
|
||||
return value
|
||||
|
||||
@field_validator("amount", mode="before")
|
||||
@classmethod
|
||||
def parse_amount(cls, value):
|
||||
if value is None:
|
||||
raise ValueError("Поле amount обязательно")
|
||||
text = str(value).strip()
|
||||
if text == "":
|
||||
raise ValueError("Поле amount обязательно")
|
||||
try:
|
||||
return Decimal(text)
|
||||
except (InvalidOperation, ValueError):
|
||||
raise ValueError("Некорректная сумма. Используйте формат 2500.00")
|
||||
currency: str = Field(default="RUB", min_length=3, max_length=8)
|
||||
|
||||
@field_validator("amount")
|
||||
@classmethod
|
||||
def validate_amount(cls, value: Decimal) -> Decimal:
|
||||
if value < Decimal("0.00"):
|
||||
raise ValueError("Сумма не может быть отрицательной")
|
||||
if value.quantize(Decimal("0.01")) != value:
|
||||
raise ValueError("Сумма должна быть с точностью до 2 знаков после запятой")
|
||||
return value
|
||||
return _validate_decimal_amount(value)
|
||||
|
||||
|
||||
class PriceRuleUpdateRequest(BaseModel):
|
||||
pricing_category_id: str | None = None
|
||||
target_type: str
|
||||
target_ref: str
|
||||
pricing_category_id: str | None = Field(default=None, max_length=32)
|
||||
target_type: str = Field(..., pattern="^(seat|group|sector)$")
|
||||
target_ref: str = Field(..., min_length=1, max_length=128)
|
||||
amount: Decimal
|
||||
currency: str = "RUB"
|
||||
|
||||
@field_validator("target_type")
|
||||
@classmethod
|
||||
def validate_target_type(cls, value: str) -> str:
|
||||
allowed = {"sector", "group", "seat"}
|
||||
if value not in allowed:
|
||||
raise ValueError("Поле target_type должно быть одним из: sector, group, seat")
|
||||
return value
|
||||
|
||||
@field_validator("currency")
|
||||
@classmethod
|
||||
def validate_currency(cls, value: str) -> str:
|
||||
if value != "RUB":
|
||||
raise ValueError("В v1 поддерживается только валюта RUB")
|
||||
return value
|
||||
|
||||
@field_validator("amount", mode="before")
|
||||
@classmethod
|
||||
def parse_amount(cls, value):
|
||||
if value is None:
|
||||
raise ValueError("Поле amount обязательно")
|
||||
text = str(value).strip()
|
||||
if text == "":
|
||||
raise ValueError("Поле amount обязательно")
|
||||
try:
|
||||
return Decimal(text)
|
||||
except (InvalidOperation, ValueError):
|
||||
raise ValueError("Некорректная сумма. Используйте формат 2500.00")
|
||||
currency: str = Field(default="RUB", min_length=3, max_length=8)
|
||||
|
||||
@field_validator("amount")
|
||||
@classmethod
|
||||
def validate_amount(cls, value: Decimal) -> Decimal:
|
||||
if value < Decimal("0.00"):
|
||||
raise ValueError("Сумма не может быть отрицательной")
|
||||
if value.quantize(Decimal("0.01")) != value:
|
||||
raise ValueError("Сумма должна быть с точностью до 2 знаков после запятой")
|
||||
return value
|
||||
return _validate_decimal_amount(value)
|
||||
|
||||
|
||||
class PriceRuleItem(BaseModel):
|
||||
price_rule_id: str
|
||||
scheme_id: str
|
||||
pricing_category_id: str | None
|
||||
target_type: str
|
||||
target_ref: str
|
||||
amount: Decimal | str
|
||||
currency: str
|
||||
created_at: str
|
||||
|
||||
|
||||
class PriceRuleCreateResponse(BaseModel):
|
||||
@@ -142,30 +108,6 @@ class PriceRuleUpdateResponse(BaseModel):
|
||||
currency: str
|
||||
|
||||
|
||||
class PricingCategoryItem(BaseModel):
|
||||
pricing_category_id: str
|
||||
scheme_id: str
|
||||
name: str
|
||||
code: str | None
|
||||
created_at: str
|
||||
|
||||
|
||||
class PriceRuleItem(BaseModel):
|
||||
price_rule_id: str
|
||||
scheme_id: str
|
||||
pricing_category_id: str | None
|
||||
target_type: str
|
||||
target_ref: str
|
||||
amount: Decimal
|
||||
currency: str
|
||||
created_at: str
|
||||
|
||||
|
||||
class SchemePricingResponse(BaseModel):
|
||||
categories: List[PricingCategoryItem]
|
||||
rules: List[PriceRuleItem]
|
||||
|
||||
|
||||
class EffectiveSeatPriceResponse(BaseModel):
|
||||
scheme_id: str
|
||||
scheme_version_id: str
|
||||
@@ -175,5 +117,15 @@ class EffectiveSeatPriceResponse(BaseModel):
|
||||
matched_rule_level: str
|
||||
matched_target_ref: str
|
||||
pricing_category_id: str | None
|
||||
amount: Decimal
|
||||
amount: Decimal | str
|
||||
currency: str
|
||||
|
||||
|
||||
class SchemePricingResponse(BaseModel):
|
||||
categories: list[PricingCategoryItem]
|
||||
rules: list[PriceRuleItem]
|
||||
|
||||
|
||||
class PricingBundleResponse(BaseModel):
|
||||
categories: list[PricingCategoryItem]
|
||||
rules: list[PriceRuleItem]
|
||||
|
||||
Reference in New Issue
Block a user