"""Schemas for delivery payment initialization.""" from datetime import datetime from decimal import Decimal, InvalidOperation from typing import Literal from pydantic import ( BaseModel, ConfigDict, EmailStr, Field, field_validator, model_validator, ) from pydantic.alias_generators import to_camel def _validate_positive_decimal_string(value: str) -> str: try: parsed = Decimal(value) except (InvalidOperation, TypeError, ValueError) as exc: raise ValueError("must be a positive decimal number") from exc if not parsed.is_finite() or parsed <= 0: raise ValueError("must be a positive decimal number") return value class _CamelModel(BaseModel): model_config = ConfigDict( alias_generator=to_camel, populate_by_name=False, extra="forbid", ) class Address(_CamelModel): city_id: int = Field(gt=0, strict=True) city: str = Field(min_length=1) street: str = Field(min_length=1) house: str = Field(min_length=1) apartment: str | None = None zip: str = Field(min_length=1) comment: str | None = None class Contact(_CamelModel): full_name: str = Field(min_length=1) email: str | None = None phone: str = Field(min_length=1) phone_ext: str | None = None has_extra_phone: str | None = None is_company: bool company_name: str | None = None inn: str | None = None kpp: str | None = None @model_validator(mode="after") def _validate_company_requisites(self) -> "Contact": if self.is_company: missing = [ name for name, value in ( ("companyName", self.company_name), ("inn", self.inn), ("kpp", self.kpp), ) if value is None or value == "" ] if missing: raise ValueError( f"{', '.join(missing)} are required when isCompany is true" ) return self class Content(_CamelModel): description: str | None = None declared_value: int = Field(alias="declared_value", strict=True) class SystemDataTariff(_CamelModel): provider: str = Field(min_length=1) service_name: str = Field(min_length=1) price: int = Field(gt=0, strict=True, description="Payment amount in kopecks.") delivery_days_min: int = Field(ge=0, strict=True) delivery_days_max: int = Field(ge=0, strict=True) tariff_code: str = Field(min_length=1) class Dimensions(_CamelModel): length: str = Field(min_length=1) width: str = Field(min_length=1) height: str = Field(min_length=1) _validate_dimensions = field_validator("length", "width", "height")( _validate_positive_decimal_string ) class SystemData(_CamelModel): tariff: SystemDataTariff parcel_type: Literal["doc", "parcel"] doc_packaging: Literal["envelope", "bag"] | None = None weight: str = Field(min_length=1) dimensions: Dimensions | None = None _validate_weight = field_validator("weight")(_validate_positive_decimal_string) @model_validator(mode="after") def _validate_dimensions_for_parcel_type(self) -> "SystemData": if self.parcel_type == "parcel" and self.dimensions is None: raise ValueError("dimensions are required when parcelType is 'parcel'") if self.parcel_type == "doc" and self.doc_packaging is None: raise ValueError("docPackaging is required when parcelType is 'doc'") return self class InitPaymentRequest(_CamelModel): sender_address: Address sender_contact: Contact receiver_address: Address receiver_contact: Contact content: Content pickup_date: datetime delivery_date: datetime | None = None account_email: EmailStr system_data: SystemData agree_privacy: bool agree_terms: bool class InitPaymentResponse(BaseModel): payment_url: str = Field(min_length=1) class TBankPaymentNotification(BaseModel): model_config = ConfigDict(extra="allow") TerminalKey: str = Field(min_length=1) OrderId: str = Field(min_length=1) Success: bool Status: str = Field(min_length=1) PaymentId: int = Field(gt=0, strict=True) ErrorCode: str = Field(min_length=1) Amount: int Token: str = Field(min_length=1)