180 lines
5.1 KiB
Python
180 lines
5.1 KiB
Python
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
|