from decimal import Decimal, InvalidOperation from typing import List from pydantic import BaseModel, field_validator class PricingCategoryCreateRequest(BaseModel): name: str code: str | None = None class PricingCategoryUpdateRequest(BaseModel): name: str code: str | None = None 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 DeleteResponse(BaseModel): status: str class PriceRuleCreateRequest(BaseModel): pricing_category_id: str | None = None target_type: str target_ref: str 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") @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 class PriceRuleUpdateRequest(BaseModel): pricing_category_id: str | None = None target_type: str target_ref: str 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") @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 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 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 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 currency: str