from decimal import Decimal, InvalidOperation 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 = 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 created_at: str class PricingCategoryCreateResponse(BaseModel): pricing_category_id: str scheme_id: str name: str code: str | None class PricingCategoryUpdateResponse(BaseModel): pricing_category_id: str scheme_id: str name: str code: str | None class PriceRuleCreateRequest(BaseModel): 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 = Field(default="RUB", min_length=3, max_length=8) @field_validator("amount") @classmethod def validate_amount(cls, value: Decimal) -> Decimal: return _validate_decimal_amount(value) class PriceRuleUpdateRequest(BaseModel): 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 = Field(default="RUB", min_length=3, max_length=8) @field_validator("amount") @classmethod def validate_amount(cls, value: Decimal) -> Decimal: 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): price_rule_id: str scheme_id: str pricing_category_id: str | None target_type: str target_ref: str amount: Decimal currency: str class PriceRuleUpdateResponse(BaseModel): price_rule_id: str scheme_id: str pricing_category_id: str | None target_type: str target_ref: str amount: Decimal currency: str class EffectiveSeatPriceResponse(BaseModel): scheme_id: str scheme_version_id: str seat_id: str sector_id: str | None group_id: str | None matched_rule_level: str matched_target_ref: str pricing_category_id: str | None amount: Decimal | str currency: str class SchemePricingResponse(BaseModel): categories: list[PricingCategoryItem] rules: list[PriceRuleItem] class PricingBundleResponse(BaseModel): categories: list[PricingCategoryItem] rules: list[PriceRuleItem]