from __future__ import annotations from typing import Literal from pydantic import BaseModel, Field SalesLeadTemperature = Literal["warm", "hot"] SalesChannelType = Literal["text", "voice"] SalesPreferredChannel = Literal["telegram", "whatsapp", "webchat", "email", "voice"] SalesDirection = Literal["inbound", "outbound"] SalesAgentType = Literal["text_ai", "voice_ai", "human"] SalesLeadStatus = Literal["new_qualified_lead", "warm_lead", "hot_lead", "enrichment_required", "converted", "lost"] SalesDealStatus = Literal["active", "won", "lost", "postponed", "closed"] SalesScenarioType = Literal[ "quick_sale", "consultative_sale", "quotation_based_sale", "booking_based_sale", "subscription_sale", "custom_human_escalation", ] SalesOfferType = Literal["offer", "quotation", "estimate", "order", "booking", "appointment", "subscription_plan"] SalesOfferStatus = Literal["draft", "sent", "viewed", "accepted", "rejected"] SalesTranscriptStatus = Literal["pending", "ready"] SalesDocumentType = Literal[ "contract", "offer_acceptance", "appendix", "specification", "invoice_basis_doc", "order_confirmation", "booking_confirmation", "appointment_confirmation", "additional_agreement", ] SalesDocumentStatus = Literal["draft", "sent", "under_review", "confirmed", "signed"] SalesInvoiceStatus = Literal["draft", "issued", "sent", "partially_paid", "paid", "overdue", "canceled"] SalesPaymentStatus = Literal["pending", "success", "failed", "canceled", "partial"] SalesEscalationSeverity = Literal["low", "medium", "high", "critical"] SalesEscalationStatus = Literal["open", "in_progress", "resolved"] SalesAutomationStatus = Literal["pending", "running", "completed", "failed", "canceled"] class SalesLeadCreate(BaseModel): source_type: str = Field(default="crm") source_channel: str = Field(default="webchat") source_campaign_id: str | None = None full_name: str = Field(min_length=2) company_name: str | None = None phone: str | None = None email: str | None = None messenger_handles: dict = Field(default_factory=dict) lead_temperature: SalesLeadTemperature = "warm" lead_score: float = Field(default=50.0, ge=0, le=100) customer_type: str | None = None segment_type: str | None = None initial_need_summary: str | None = None preferred_channel: SalesPreferredChannel = "telegram" assigned_agent_type: SalesAgentType = "text_ai" status: SalesLeadStatus = "new_qualified_lead" priority: int = Field(default=3, ge=1, le=5) title: str | None = None estimated_amount: float | None = Field(default=None, ge=0) currency: str = "KZT" class SalesLeadUpdate(BaseModel): full_name: str | None = Field(default=None, min_length=2) company_name: str | None = None phone: str | None = None email: str | None = None messenger_handles: dict | None = None lead_temperature: SalesLeadTemperature | None = None lead_score: float | None = Field(default=None, ge=0, le=100) customer_type: str | None = None segment_type: str | None = None initial_need_summary: str | None = None preferred_channel: SalesPreferredChannel | None = None assigned_agent_type: SalesAgentType | None = None status: SalesLeadStatus | None = None class SalesLeadEnrichIn(BaseModel): need_summary: str | None = None product_context: dict = Field(default_factory=dict) customer_type: str | None = None preferred_channel: SalesPreferredChannel | None = None scenario_type: SalesScenarioType | None = None required_fields: dict = Field(default_factory=dict) class SalesLeadOut(BaseModel): lead_id: str tenant_id: str source_type: str source_channel: str source_campaign_id: str | None = None full_name: str company_name: str | None = None phone: str | None = None email: str | None = None messenger_handles: dict = Field(default_factory=dict) lead_temperature: SalesLeadTemperature lead_score: float customer_type: str | None = None segment_type: str | None = None initial_need_summary: str | None = None preferred_channel: SalesPreferredChannel assigned_agent_type: SalesAgentType status: SalesLeadStatus crm_customer_id: str | None = None created_at: str updated_at: str class SalesDealCreate(BaseModel): lead_id: str | None = None customer_id: str | None = None stage_id: str = "new_qualified_lead" scenario_type: SalesScenarioType = "quick_sale" priority: int = Field(default=3, ge=1, le=5) title: str = Field(min_length=3) need_summary: str | None = None product_context: dict = Field(default_factory=dict) estimated_amount: float | None = Field(default=None, ge=0) final_amount: float | None = Field(default=None, ge=0) currency: str = "KZT" payment_model: str | None = None document_required: bool = True payment_required: bool = True preferred_channel: SalesPreferredChannel = "telegram" current_channel: SalesPreferredChannel = "telegram" class SalesDealUpdate(BaseModel): title: str | None = Field(default=None, min_length=3) need_summary: str | None = None product_context: dict | None = None estimated_amount: float | None = Field(default=None, ge=0) final_amount: float | None = Field(default=None, ge=0) currency: str | None = None payment_model: str | None = None document_required: bool | None = None payment_required: bool | None = None preferred_channel: SalesPreferredChannel | None = None current_channel: SalesPreferredChannel | None = None assigned_human_user_id: str | None = None next_action_type: str | None = None next_action_at: str | None = None class SalesDealStageChangeIn(BaseModel): stage_id: str = Field(min_length=2) reason: str | None = None class SalesDealScenarioIn(BaseModel): scenario_type: SalesScenarioType class SalesDealNextActionIn(BaseModel): next_action_type: str = Field(min_length=2) next_action_at: str | None = None payload: dict = Field(default_factory=dict) class SalesDealCloseIn(BaseModel): status: Literal["won", "lost", "postponed"] reason: str | None = None class SalesDealOut(BaseModel): deal_id: str tenant_id: str lead_id: str | None = None customer_id: str | None = None pipeline_id: str = "sales_default" stage_id: str scenario_type: SalesScenarioType priority: int title: str need_summary: str | None = None product_context: dict = Field(default_factory=dict) estimated_amount: float | None = None final_amount: float | None = None currency: str payment_model: str | None = None document_required: bool = True payment_required: bool = True assigned_human_user_id: str | None = None assigned_ai_orchestrator_id: str | None = None preferred_channel: str current_channel: str status: SalesDealStatus won_reason: str | None = None lost_reason: str | None = None close_reason: str | None = None next_action_type: str | None = None next_action_at: str | None = None last_contact_at: str | None = None closed_at: str | None = None created_at: str updated_at: str class SalesCommunicationStartIn(BaseModel): channel_type: SalesChannelType direction: SalesDirection = "outbound" agent_type: SalesAgentType | None = None subject: str | None = None summary: str | None = None next_action_type: str | None = None next_action_at: str | None = None class SalesCommunicationSummaryIn(BaseModel): summary: str = Field(min_length=2) result_code: str | None = None sentiment: str | None = None next_action_type: str | None = None next_action_at: str | None = None class SalesCommunicationSwitchChannelIn(BaseModel): to_channel: SalesPreferredChannel reason_for_channel_switch: str = Field(min_length=3) class SalesCommunicationBindExternalIn(BaseModel): channel_provider: str | None = None telegram_thread_id: str | None = None telegram_chat_id: str | None = None voice_session_id: str | None = None external_call_id: str | None = None metadata: dict = Field(default_factory=dict) class SalesCommunicationOut(BaseModel): communication_id: str tenant_id: str deal_id: str lead_id: str | None = None customer_id: str | None = None channel_type: SalesChannelType direction: SalesDirection agent_type: SalesAgentType started_at: str ended_at: str | None = None duration_sec: int | None = None subject: str | None = None status: str summary: str | None = None transcript_id: str | None = None next_action_type: str | None = None next_action_at: str | None = None sentiment: str | None = None result_code: str | None = None metadata: dict = Field(default_factory=dict) created_at: str updated_at: str class SalesMessageWebhookIn(BaseModel): deal_id: str | None = None lead_id: str | None = None phone: str | None = None channel_provider: str = "telegram" external_message_id: str | None = None sender_id: str | None = None body: str = Field(min_length=1) attachments: list[dict] = Field(default_factory=list) metadata: dict = Field(default_factory=dict) class SalesMessageSendIn(BaseModel): deal_id: str communication_id: str | None = None channel_provider: str = "telegram" sender_type: Literal["text_ai", "human"] = "text_ai" sender_id: str | None = None body: str = Field(min_length=1) attachments: list[dict] = Field(default_factory=list) metadata: dict = Field(default_factory=dict) class SalesMessageOut(BaseModel): message_id: str deal_id: str communication_id: str sender_type: str sender_id: str | None = None channel_provider: str external_message_id: str | None = None body: str attachments: list[dict] = Field(default_factory=list) delivery_status: str | None = None read_status: str | None = None metadata: dict = Field(default_factory=dict) sent_at: str created_at: str class SalesCallWebhookIn(BaseModel): deal_id: str | None = None lead_id: str | None = None phone_number: str provider: str = "asterisk" external_call_id: str | None = None subject: str | None = None class SalesCallCompleteIn(BaseModel): summary: str | None = None recording_url: str | None = None result_code: str | None = None class SalesTranscriptIn(BaseModel): language: str = "ru" transcript_text: str = Field(min_length=1) diarization: dict = Field(default_factory=dict) extracted_entities: dict = Field(default_factory=dict) class SalesTranscriptOut(BaseModel): transcript_id: str call_id: str language: str transcript_text: str diarization: dict = Field(default_factory=dict) extracted_entities: dict = Field(default_factory=dict) created_at: str updated_at: str class SalesCallOut(BaseModel): call_id: str deal_id: str communication_id: str phone_number: str direction: SalesDirection provider: str external_call_id: str | None = None recording_url: str | None = None transcript_status: SalesTranscriptStatus transcript_id: str | None = None call_status: str started_at: str ended_at: str | None = None duration_sec: int | None = None summary: str | None = None created_at: str updated_at: str class SalesOfferCreate(BaseModel): offer_type: SalesOfferType = "offer" title: str = Field(min_length=3) description: str | None = None line_items: list[dict] = Field(default_factory=list) pricing: dict = Field(default_factory=dict) total_amount: float = Field(ge=0) currency: str = "KZT" validity_until: str | None = None rendered_document_url: str | None = None class SalesOfferOut(BaseModel): offer_id: str deal_id: str offer_type: SalesOfferType title: str description: str | None = None line_items: list[dict] = Field(default_factory=list) pricing: dict = Field(default_factory=dict) total_amount: float currency: str validity_until: str | None = None status: SalesOfferStatus rendered_document_url: str | None = None created_by_type: str created_at: str updated_at: str class SalesConditionUpsertIn(BaseModel): product_name: str | None = None service_name: str | None = None quantity: float | None = Field(default=None, ge=0) unit: str | None = None delivery_mode: str | None = None execution_date: str | None = None start_date: str | None = None end_date: str | None = None payment_terms: str | None = None custom_terms: dict = Field(default_factory=dict) agreed_price: float | None = Field(default=None, ge=0) currency: str = "KZT" class SalesConditionOut(BaseModel): condition_id: str deal_id: str product_name: str | None = None service_name: str | None = None quantity: float | None = None unit: str | None = None delivery_mode: str | None = None execution_date: str | None = None start_date: str | None = None end_date: str | None = None payment_terms: str | None = None custom_terms: dict = Field(default_factory=dict) agreed_price: float | None = None currency: str confirmed_at: str | None = None created_at: str updated_at: str class SalesCounterpartyUpsertIn(BaseModel): company_name: str | None = None full_name: str | None = None bin_iin: str | None = None address: str | None = None bank_details: dict = Field(default_factory=dict) signer_name: str | None = None signer_role: str | None = None signer_basis: str | None = None email_for_docs: str | None = None phone_for_docs: str | None = None class SalesCounterpartyOut(BaseModel): counterparty_id: str deal_id: str customer_id: str | None = None company_name: str | None = None full_name: str | None = None bin_iin: str | None = None address: str | None = None bank_details: dict = Field(default_factory=dict) signer_name: str | None = None signer_role: str | None = None signer_basis: str | None = None email_for_docs: str | None = None phone_for_docs: str | None = None completeness_status: str created_at: str updated_at: str class SalesDocumentCreate(BaseModel): document_type: SalesDocumentType template_id: str | None = None version: int = Field(default=1, ge=1) rendered_payload: dict = Field(default_factory=dict) file_url: str | None = None class SalesDocumentOut(BaseModel): document_id: str deal_id: str customer_id: str | None = None document_type: SalesDocumentType template_id: str | None = None version: int status: SalesDocumentStatus file_url: str | None = None rendered_payload: dict = Field(default_factory=dict) external_sign_provider_id: str | None = None signed_at: str | None = None created_at: str updated_at: str class SalesInvoiceCreate(BaseModel): basis_document_id: str | None = None amount: float = Field(ge=0) currency: str = "KZT" due_date: str | None = None payment_link: str | None = None line_items: list[dict] = Field(default_factory=list) metadata: dict = Field(default_factory=dict) class SalesInvoiceOut(BaseModel): invoice_id: str deal_id: str customer_id: str | None = None invoice_number: str basis_document_id: str | None = None amount: float currency: str due_date: str | None = None status: SalesInvoiceStatus payment_link: str | None = None line_items: list[dict] = Field(default_factory=list) metadata: dict = Field(default_factory=dict) issued_at: str | None = None paid_at: str | None = None created_at: str updated_at: str class SalesPaymentWebhookIn(BaseModel): deal_id: str invoice_id: str | None = None payment_provider: str = "manual" external_payment_id: str | None = None amount: float = Field(ge=0) currency: str = "KZT" status: SalesPaymentStatus = "success" paid_at: str | None = None payment_method: str | None = None failure_reason: str | None = None metadata: dict = Field(default_factory=dict) class SalesPaymentReconcileIn(BaseModel): status: SalesPaymentStatus failure_reason: str | None = None metadata: dict = Field(default_factory=dict) class SalesPaymentOut(BaseModel): payment_id: str deal_id: str invoice_id: str | None = None payment_provider: str | None = None external_payment_id: str | None = None amount: float currency: str status: SalesPaymentStatus paid_at: str | None = None payment_method: str | None = None failure_reason: str | None = None metadata: dict = Field(default_factory=dict) created_at: str updated_at: str class SalesEscalationCreateIn(BaseModel): escalation_type: str = Field(min_length=2) reason: str = Field(min_length=3) severity: SalesEscalationSeverity = "medium" assigned_to_user_id: str | None = None class SalesEscalationOut(BaseModel): escalation_id: str deal_id: str escalation_type: str reason: str severity: SalesEscalationSeverity status: SalesEscalationStatus assigned_to_user_id: str | None = None created_at: str resolved_at: str | None = None class SalesAutomationTaskOut(BaseModel): task_id: str deal_id: str task_type: str payload: dict = Field(default_factory=dict) run_at: str status: SalesAutomationStatus retry_count: int = 0 last_error: str | None = None created_at: str updated_at: str class SalesStageHistoryOut(BaseModel): history_id: str deal_id: str from_stage_id: str | None = None to_stage_id: str changed_by_type: str changed_by_id: str | None = None reason: str | None = None changed_at: str class SalesChannelSwitchOut(BaseModel): switch_id: str deal_id: str communication_id: str | None = None from_channel: str to_channel: str reason_for_channel_switch: str switched_at: str class SalesTimelineEventOut(BaseModel): ts: str kind: str title: str body: str meta: dict = Field(default_factory=dict) class SalesWorkspaceOut(BaseModel): lead: SalesLeadOut | None = None deal: SalesDealOut communications: list[SalesCommunicationOut] = Field(default_factory=list) messages: list[SalesMessageOut] = Field(default_factory=list) calls: list[SalesCallOut] = Field(default_factory=list) offers: list[SalesOfferOut] = Field(default_factory=list) conditions: list[SalesConditionOut] = Field(default_factory=list) counterparty: SalesCounterpartyOut | None = None documents: list[SalesDocumentOut] = Field(default_factory=list) invoices: list[SalesInvoiceOut] = Field(default_factory=list) payments: list[SalesPaymentOut] = Field(default_factory=list) escalations: list[SalesEscalationOut] = Field(default_factory=list) tasks: list[SalesAutomationTaskOut] = Field(default_factory=list) stage_history: list[SalesStageHistoryOut] = Field(default_factory=list) channel_switches: list[SalesChannelSwitchOut] = Field(default_factory=list) timeline: list[SalesTimelineEventOut] = Field(default_factory=list) class SalesDashboardStageOut(BaseModel): stage_id: str label: str count: int = 0 amount: float = 0.0 class SalesDashboardSummaryOut(BaseModel): leads_total: int = 0 deals_active: int = 0 deals_won: int = 0 deals_lost: int = 0 overdue_invoices: int = 0 payment_expected: float = 0.0 payment_received: float = 0.0 voice_sessions: int = 0 text_sessions: int = 0 channel_switches: int = 0 human_escalations: int = 0 stage_counts: list[SalesDashboardStageOut] = Field(default_factory=list) hottest_deals: list[SalesDealOut] = Field(default_factory=list) class SalesTelegramSyncIn(BaseModel): thread_id: str = Field(min_length=1) chat_id: str = Field(min_length=1) interaction_id: str | None = None customer_id: str | None = None phone_number: str | None = None display_name: str | None = None queue_id: str | None = None status: str | None = None ai_state: str | None = None ai_handoff_reason: str | None = None message_id: str | None = None external_message_id: str | None = None text: str | None = None direction: str = "inbound" author_type: str | None = None author_id: str | None = None happened_at: str | None = None metadata: dict = Field(default_factory=dict) class SalesVoiceSyncIn(BaseModel): call_id: str = Field(min_length=1) interaction_id: str | None = None queue_id: str | None = None queue_code: str | None = None caller_number: str | None = None caller_name: str | None = None voice_session_id: str | None = None ai_session_id: str | None = None ai_state: str | None = None handoff_reason: str | None = None telephony_status: str | None = None call_status: str | None = None started_at: str | None = None ended_at: str | None = None summary: str | None = None transcript_text: str | None = None metadata: dict = Field(default_factory=dict)