sales fix
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
import struct
|
||||
from array import array
|
||||
|
||||
try:
|
||||
import audioop as _audioop
|
||||
except ModuleNotFoundError:
|
||||
try:
|
||||
import audioop_lts as _audioop # type: ignore[import-not-found]
|
||||
except ModuleNotFoundError:
|
||||
_audioop = None
|
||||
|
||||
|
||||
def _native_is_little_endian() -> bool:
|
||||
return struct.pack("=h", 1) == struct.pack("<h", 1)
|
||||
|
||||
|
||||
def _read_int16_samples(fragment: bytes, width: int) -> array:
|
||||
if width != 2:
|
||||
raise NotImplementedError("audioop_compat fallback currently supports only 16-bit PCM")
|
||||
samples = array("h")
|
||||
samples.frombytes(fragment)
|
||||
if not _native_is_little_endian():
|
||||
samples.byteswap()
|
||||
return samples
|
||||
|
||||
|
||||
def _write_int16_samples(samples: array) -> bytes:
|
||||
output = array("h", samples)
|
||||
if not _native_is_little_endian():
|
||||
output.byteswap()
|
||||
return output.tobytes()
|
||||
|
||||
|
||||
def _clip_int16(value: float) -> int:
|
||||
return max(-32768, min(32767, int(round(value))))
|
||||
|
||||
|
||||
class _AudioopFallback:
|
||||
@staticmethod
|
||||
def rms(fragment: bytes, width: int) -> int:
|
||||
samples = _read_int16_samples(fragment, width)
|
||||
if not samples:
|
||||
return 0
|
||||
mean_square = sum(sample * sample for sample in samples) / len(samples)
|
||||
return int(math.sqrt(mean_square))
|
||||
|
||||
@staticmethod
|
||||
def tomono(fragment: bytes, width: int, lfactor: float, rfactor: float) -> bytes:
|
||||
samples = _read_int16_samples(fragment, width)
|
||||
if len(samples) % 2 != 0:
|
||||
raise ValueError("Stereo PCM must contain an even number of samples")
|
||||
mono = array("h")
|
||||
for index in range(0, len(samples), 2):
|
||||
left = samples[index]
|
||||
right = samples[index + 1]
|
||||
mono.append(_clip_int16((left * lfactor) + (right * rfactor)))
|
||||
return _write_int16_samples(mono)
|
||||
|
||||
@staticmethod
|
||||
def ratecv(
|
||||
fragment: bytes,
|
||||
width: int,
|
||||
nchannels: int,
|
||||
inrate: int,
|
||||
outrate: int,
|
||||
state,
|
||||
weightA: int = 1,
|
||||
weightB: int = 0,
|
||||
) -> tuple[bytes, None]:
|
||||
if nchannels <= 0:
|
||||
raise ValueError("nchannels must be positive")
|
||||
if inrate <= 0 or outrate <= 0:
|
||||
raise ValueError("Sample rates must be positive")
|
||||
samples = _read_int16_samples(fragment, width)
|
||||
if not samples or inrate == outrate:
|
||||
return fragment, None
|
||||
if len(samples) % nchannels != 0:
|
||||
raise ValueError("PCM fragment size does not match channel count")
|
||||
|
||||
frame_count = len(samples) // nchannels
|
||||
output_frame_count = max(1, int(round(frame_count * outrate / inrate)))
|
||||
output = array("h")
|
||||
|
||||
for out_index in range(output_frame_count):
|
||||
position = out_index * inrate / outrate
|
||||
left_index = min(int(position), frame_count - 1)
|
||||
right_index = min(left_index + 1, frame_count - 1)
|
||||
fraction = max(0.0, min(1.0, position - left_index))
|
||||
for channel_index in range(nchannels):
|
||||
left_sample = samples[(left_index * nchannels) + channel_index]
|
||||
right_sample = samples[(right_index * nchannels) + channel_index]
|
||||
interpolated = left_sample + ((right_sample - left_sample) * fraction)
|
||||
output.append(_clip_int16(interpolated))
|
||||
return _write_int16_samples(output), None
|
||||
|
||||
|
||||
audioop = _audioop or _AudioopFallback()
|
||||
|
||||
__all__ = ["audioop"]
|
||||
@@ -0,0 +1,685 @@
|
||||
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)
|
||||
@@ -0,0 +1,445 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from sqlalchemy import Boolean, Float, Index, Integer, String, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from services.shared.sql_models import Base
|
||||
|
||||
|
||||
class SalesLeadRow(Base):
|
||||
__tablename__ = "sales_leads"
|
||||
__table_args__ = (
|
||||
Index("idx_sales_leads_tenant_status_score", "tenant_id", "status", "lead_score"),
|
||||
Index("idx_sales_leads_phone_email", "phone", "email"),
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
lead_id: Mapped[str] = mapped_column(String(64), unique=True, index=True)
|
||||
tenant_id: Mapped[str] = mapped_column(String(64), index=True)
|
||||
source_type: Mapped[str] = mapped_column(String(64), index=True)
|
||||
source_channel: Mapped[str] = mapped_column(String(32), index=True)
|
||||
source_campaign_id: Mapped[str | None] = mapped_column(String(128), nullable=True, index=True)
|
||||
full_name: Mapped[str] = mapped_column(String(256), index=True)
|
||||
company_name: Mapped[str | None] = mapped_column(String(256), nullable=True, index=True)
|
||||
phone: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||||
email: Mapped[str | None] = mapped_column(String(256), nullable=True, index=True)
|
||||
messenger_handles_json: Mapped[str] = mapped_column(Text, default="{}")
|
||||
lead_temperature: Mapped[str] = mapped_column(String(16), index=True)
|
||||
lead_score: Mapped[float] = mapped_column(Float, default=50.0, index=True)
|
||||
customer_type: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||||
segment_type: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||||
initial_need_summary: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
preferred_channel: Mapped[str] = mapped_column(String(32), index=True)
|
||||
assigned_agent_type: Mapped[str] = mapped_column(String(32), index=True)
|
||||
status: Mapped[str] = mapped_column(String(32), index=True)
|
||||
crm_customer_id: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||||
created_at: Mapped[str] = mapped_column(String(64), index=True)
|
||||
updated_at: Mapped[str] = mapped_column(String(64), index=True)
|
||||
|
||||
|
||||
class SalesDealRow(Base):
|
||||
__tablename__ = "sales_deals"
|
||||
__table_args__ = (
|
||||
Index("idx_sales_deals_tenant_stage_status", "tenant_id", "stage_id", "status"),
|
||||
Index("idx_sales_deals_customer_channel", "customer_id", "current_channel"),
|
||||
Index("idx_sales_deals_next_action", "next_action_at", "status"),
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
deal_id: Mapped[str] = mapped_column(String(64), unique=True, index=True)
|
||||
tenant_id: Mapped[str] = mapped_column(String(64), index=True)
|
||||
lead_id: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||||
customer_id: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||||
pipeline_id: Mapped[str] = mapped_column(String(64), default="sales_default", index=True)
|
||||
stage_id: Mapped[str] = mapped_column(String(64), index=True)
|
||||
scenario_type: Mapped[str] = mapped_column(String(64), index=True)
|
||||
priority: Mapped[int] = mapped_column(Integer, default=3, index=True)
|
||||
title: Mapped[str] = mapped_column(String(512))
|
||||
need_summary: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
product_context_json: Mapped[str] = mapped_column(Text, default="{}")
|
||||
estimated_amount: Mapped[float | None] = mapped_column(Float, nullable=True)
|
||||
final_amount: Mapped[float | None] = mapped_column(Float, nullable=True)
|
||||
currency: Mapped[str] = mapped_column(String(16), default="KZT")
|
||||
payment_model: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||||
document_required: Mapped[bool] = mapped_column(Boolean, default=True)
|
||||
payment_required: Mapped[bool] = mapped_column(Boolean, default=True)
|
||||
assigned_human_user_id: Mapped[str | None] = mapped_column(String(128), nullable=True, index=True)
|
||||
assigned_ai_orchestrator_id: Mapped[str | None] = mapped_column(String(128), nullable=True)
|
||||
preferred_channel: Mapped[str] = mapped_column(String(32), index=True)
|
||||
current_channel: Mapped[str] = mapped_column(String(32), index=True)
|
||||
status: Mapped[str] = mapped_column(String(32), index=True, default="active")
|
||||
won_reason: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
lost_reason: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
close_reason: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
next_action_type: Mapped[str | None] = mapped_column(String(128), nullable=True, index=True)
|
||||
next_action_at: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||||
last_contact_at: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||||
closed_at: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||||
created_at: Mapped[str] = mapped_column(String(64), index=True)
|
||||
updated_at: Mapped[str] = mapped_column(String(64), index=True)
|
||||
|
||||
|
||||
class SalesCommunicationSessionRow(Base):
|
||||
__tablename__ = "sales_communication_sessions"
|
||||
__table_args__ = (
|
||||
Index("idx_sales_comm_deal_started", "deal_id", "started_at"),
|
||||
Index("idx_sales_comm_channel_status", "channel_type", "status"),
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
communication_id: Mapped[str] = mapped_column(String(64), unique=True, index=True)
|
||||
tenant_id: Mapped[str] = mapped_column(String(64), index=True)
|
||||
deal_id: Mapped[str] = mapped_column(String(64), index=True)
|
||||
lead_id: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||||
customer_id: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||||
channel_type: Mapped[str] = mapped_column(String(16), index=True)
|
||||
direction: Mapped[str] = mapped_column(String(16), index=True)
|
||||
agent_type: Mapped[str] = mapped_column(String(32), index=True)
|
||||
started_at: Mapped[str] = mapped_column(String(64), index=True)
|
||||
ended_at: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||||
duration_sec: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
||||
subject: Mapped[str | None] = mapped_column(String(512), nullable=True)
|
||||
status: Mapped[str] = mapped_column(String(32), index=True, default="active")
|
||||
summary: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
transcript_id: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||||
next_action_type: Mapped[str | None] = mapped_column(String(128), nullable=True)
|
||||
next_action_at: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||||
sentiment: Mapped[str | None] = mapped_column(String(32), nullable=True)
|
||||
result_code: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||||
metadata_json: Mapped[str] = mapped_column(Text, default="{}")
|
||||
created_at: Mapped[str] = mapped_column(String(64), index=True)
|
||||
updated_at: Mapped[str] = mapped_column(String(64), index=True)
|
||||
|
||||
|
||||
class SalesMessageRow(Base):
|
||||
__tablename__ = "sales_messages"
|
||||
__table_args__ = (
|
||||
Index("idx_sales_messages_deal_sent", "deal_id", "sent_at"),
|
||||
Index("idx_sales_messages_ext_id", "channel_provider", "external_message_id"),
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
message_id: Mapped[str] = mapped_column(String(64), unique=True, index=True)
|
||||
tenant_id: Mapped[str] = mapped_column(String(64), index=True)
|
||||
deal_id: Mapped[str] = mapped_column(String(64), index=True)
|
||||
communication_id: Mapped[str] = mapped_column(String(64), index=True)
|
||||
sender_type: Mapped[str] = mapped_column(String(32), index=True)
|
||||
sender_id: Mapped[str | None] = mapped_column(String(128), nullable=True, index=True)
|
||||
channel_provider: Mapped[str] = mapped_column(String(32), index=True)
|
||||
external_message_id: Mapped[str | None] = mapped_column(String(128), nullable=True, index=True)
|
||||
body: Mapped[str] = mapped_column(Text)
|
||||
attachments_json: Mapped[str] = mapped_column(Text, default="[]")
|
||||
delivery_status: Mapped[str | None] = mapped_column(String(32), nullable=True, index=True)
|
||||
read_status: Mapped[str | None] = mapped_column(String(32), nullable=True, index=True)
|
||||
message_metadata_json: Mapped[str] = mapped_column(Text, default="{}")
|
||||
sent_at: Mapped[str] = mapped_column(String(64), index=True)
|
||||
created_at: Mapped[str] = mapped_column(String(64), index=True)
|
||||
|
||||
|
||||
class SalesCallRow(Base):
|
||||
__tablename__ = "sales_calls"
|
||||
__table_args__ = (
|
||||
Index("idx_sales_calls_deal_started", "deal_id", "started_at"),
|
||||
Index("idx_sales_calls_ext_provider", "provider", "external_call_id"),
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
call_id: Mapped[str] = mapped_column(String(64), unique=True, index=True)
|
||||
tenant_id: Mapped[str] = mapped_column(String(64), index=True)
|
||||
deal_id: Mapped[str] = mapped_column(String(64), index=True)
|
||||
communication_id: Mapped[str] = mapped_column(String(64), index=True)
|
||||
phone_number: Mapped[str] = mapped_column(String(64), index=True)
|
||||
direction: Mapped[str] = mapped_column(String(16), index=True)
|
||||
provider: Mapped[str] = mapped_column(String(64), index=True)
|
||||
external_call_id: Mapped[str | None] = mapped_column(String(128), nullable=True, index=True)
|
||||
recording_url: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
transcript_status: Mapped[str] = mapped_column(String(32), index=True, default="pending")
|
||||
transcript_id: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||||
call_status: Mapped[str] = mapped_column(String(32), index=True, default="started")
|
||||
summary: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
started_at: Mapped[str] = mapped_column(String(64), index=True)
|
||||
ended_at: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||||
duration_sec: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
||||
created_at: Mapped[str] = mapped_column(String(64), index=True)
|
||||
updated_at: Mapped[str] = mapped_column(String(64), index=True)
|
||||
|
||||
|
||||
class SalesTranscriptRow(Base):
|
||||
__tablename__ = "sales_transcripts"
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
transcript_id: Mapped[str] = mapped_column(String(64), unique=True, index=True)
|
||||
tenant_id: Mapped[str] = mapped_column(String(64), index=True)
|
||||
call_id: Mapped[str] = mapped_column(String(64), index=True)
|
||||
language: Mapped[str] = mapped_column(String(16), index=True, default="ru")
|
||||
transcript_text: Mapped[str] = mapped_column(Text)
|
||||
diarization_json: Mapped[str] = mapped_column(Text, default="{}")
|
||||
extracted_entities_json: Mapped[str] = mapped_column(Text, default="{}")
|
||||
created_at: Mapped[str] = mapped_column(String(64), index=True)
|
||||
updated_at: Mapped[str] = mapped_column(String(64), index=True)
|
||||
|
||||
|
||||
class SalesNoteRow(Base):
|
||||
__tablename__ = "sales_notes"
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
note_id: Mapped[str] = mapped_column(String(64), unique=True, index=True)
|
||||
tenant_id: Mapped[str] = mapped_column(String(64), index=True)
|
||||
deal_id: Mapped[str] = mapped_column(String(64), index=True)
|
||||
author_type: Mapped[str] = mapped_column(String(32), index=True)
|
||||
author_id: Mapped[str | None] = mapped_column(String(128), nullable=True, index=True)
|
||||
note_type: Mapped[str] = mapped_column(String(64), index=True)
|
||||
content: Mapped[str] = mapped_column(Text)
|
||||
created_at: Mapped[str] = mapped_column(String(64), index=True)
|
||||
|
||||
|
||||
class SalesOfferRow(Base):
|
||||
__tablename__ = "sales_offers"
|
||||
__table_args__ = (
|
||||
Index("idx_sales_offers_deal_status", "deal_id", "status"),
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
offer_id: Mapped[str] = mapped_column(String(64), unique=True, index=True)
|
||||
tenant_id: Mapped[str] = mapped_column(String(64), index=True)
|
||||
deal_id: Mapped[str] = mapped_column(String(64), index=True)
|
||||
offer_type: Mapped[str] = mapped_column(String(32), index=True)
|
||||
title: Mapped[str] = mapped_column(String(256))
|
||||
description: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
line_items_json: Mapped[str] = mapped_column(Text, default="[]")
|
||||
pricing_json: Mapped[str] = mapped_column(Text, default="{}")
|
||||
total_amount: Mapped[float] = mapped_column(Float, default=0)
|
||||
currency: Mapped[str] = mapped_column(String(16), default="KZT")
|
||||
validity_until: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||||
status: Mapped[str] = mapped_column(String(32), index=True, default="draft")
|
||||
rendered_document_url: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
created_by_type: Mapped[str] = mapped_column(String(32), default="text_ai")
|
||||
created_at: Mapped[str] = mapped_column(String(64), index=True)
|
||||
updated_at: Mapped[str] = mapped_column(String(64), index=True)
|
||||
|
||||
|
||||
class SalesConditionRow(Base):
|
||||
__tablename__ = "sales_conditions"
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
condition_id: Mapped[str] = mapped_column(String(64), unique=True, index=True)
|
||||
tenant_id: Mapped[str] = mapped_column(String(64), index=True)
|
||||
deal_id: Mapped[str] = mapped_column(String(64), index=True)
|
||||
product_name: Mapped[str | None] = mapped_column(String(256), nullable=True)
|
||||
service_name: Mapped[str | None] = mapped_column(String(256), nullable=True)
|
||||
quantity: Mapped[float | None] = mapped_column(Float, nullable=True)
|
||||
unit: Mapped[str | None] = mapped_column(String(32), nullable=True)
|
||||
delivery_mode: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||||
execution_date: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||||
start_date: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||||
end_date: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||||
payment_terms: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
custom_terms_json: Mapped[str] = mapped_column(Text, default="{}")
|
||||
agreed_price: Mapped[float | None] = mapped_column(Float, nullable=True)
|
||||
currency: Mapped[str] = mapped_column(String(16), default="KZT")
|
||||
confirmed_at: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||||
created_at: Mapped[str] = mapped_column(String(64), index=True)
|
||||
updated_at: Mapped[str] = mapped_column(String(64), index=True)
|
||||
|
||||
|
||||
class SalesCounterpartyRow(Base):
|
||||
__tablename__ = "sales_counterparties"
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
counterparty_id: Mapped[str] = mapped_column(String(64), unique=True, index=True)
|
||||
tenant_id: Mapped[str] = mapped_column(String(64), index=True)
|
||||
deal_id: Mapped[str] = mapped_column(String(64), unique=True, index=True)
|
||||
customer_id: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||||
company_name: Mapped[str | None] = mapped_column(String(256), nullable=True)
|
||||
full_name: Mapped[str | None] = mapped_column(String(256), nullable=True)
|
||||
bin_iin: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||||
address: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
bank_details_json: Mapped[str] = mapped_column(Text, default="{}")
|
||||
signer_name: Mapped[str | None] = mapped_column(String(256), nullable=True)
|
||||
signer_role: Mapped[str | None] = mapped_column(String(128), nullable=True)
|
||||
signer_basis: Mapped[str | None] = mapped_column(String(256), nullable=True)
|
||||
email_for_docs: Mapped[str | None] = mapped_column(String(256), nullable=True)
|
||||
phone_for_docs: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||||
completeness_status: Mapped[str] = mapped_column(String(32), index=True, default="draft")
|
||||
created_at: Mapped[str] = mapped_column(String(64), index=True)
|
||||
updated_at: Mapped[str] = mapped_column(String(64), index=True)
|
||||
|
||||
|
||||
class SalesDocumentRow(Base):
|
||||
__tablename__ = "sales_documents"
|
||||
__table_args__ = (
|
||||
Index("idx_sales_documents_deal_status", "deal_id", "status"),
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
document_id: Mapped[str] = mapped_column(String(64), unique=True, index=True)
|
||||
tenant_id: Mapped[str] = mapped_column(String(64), index=True)
|
||||
deal_id: Mapped[str] = mapped_column(String(64), index=True)
|
||||
customer_id: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||||
document_type: Mapped[str] = mapped_column(String(64), index=True)
|
||||
template_id: Mapped[str | None] = mapped_column(String(128), nullable=True)
|
||||
version: Mapped[int] = mapped_column(Integer, default=1)
|
||||
status: Mapped[str] = mapped_column(String(32), index=True, default="draft")
|
||||
file_url: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
rendered_payload_json: Mapped[str] = mapped_column(Text, default="{}")
|
||||
external_sign_provider_id: Mapped[str | None] = mapped_column(String(128), nullable=True)
|
||||
signed_at: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||||
created_at: Mapped[str] = mapped_column(String(64), index=True)
|
||||
updated_at: Mapped[str] = mapped_column(String(64), index=True)
|
||||
|
||||
|
||||
class SalesInvoiceRow(Base):
|
||||
__tablename__ = "sales_invoices"
|
||||
__table_args__ = (
|
||||
Index("idx_sales_invoices_deal_status_due", "deal_id", "status", "due_date"),
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
invoice_id: Mapped[str] = mapped_column(String(64), unique=True, index=True)
|
||||
tenant_id: Mapped[str] = mapped_column(String(64), index=True)
|
||||
deal_id: Mapped[str] = mapped_column(String(64), index=True)
|
||||
customer_id: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||||
invoice_number: Mapped[str] = mapped_column(String(64), unique=True, index=True)
|
||||
basis_document_id: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||||
amount: Mapped[float] = mapped_column(Float, default=0)
|
||||
currency: Mapped[str] = mapped_column(String(16), default="KZT")
|
||||
due_date: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||||
status: Mapped[str] = mapped_column(String(32), index=True, default="draft")
|
||||
payment_link: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
line_items_json: Mapped[str] = mapped_column(Text, default="[]")
|
||||
metadata_json: Mapped[str] = mapped_column(Text, default="{}")
|
||||
issued_at: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||||
paid_at: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||||
created_at: Mapped[str] = mapped_column(String(64), index=True)
|
||||
updated_at: Mapped[str] = mapped_column(String(64), index=True)
|
||||
|
||||
|
||||
class SalesPaymentRow(Base):
|
||||
__tablename__ = "sales_payments"
|
||||
__table_args__ = (
|
||||
Index("idx_sales_payments_deal_status", "deal_id", "status"),
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
payment_id: Mapped[str] = mapped_column(String(64), unique=True, index=True)
|
||||
tenant_id: Mapped[str] = mapped_column(String(64), index=True)
|
||||
deal_id: Mapped[str] = mapped_column(String(64), index=True)
|
||||
invoice_id: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||||
payment_provider: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||||
external_payment_id: Mapped[str | None] = mapped_column(String(128), nullable=True, index=True)
|
||||
amount: Mapped[float] = mapped_column(Float, default=0)
|
||||
currency: Mapped[str] = mapped_column(String(16), default="KZT")
|
||||
status: Mapped[str] = mapped_column(String(32), index=True, default="pending")
|
||||
paid_at: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||||
payment_method: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||||
failure_reason: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
metadata_json: Mapped[str] = mapped_column(Text, default="{}")
|
||||
created_at: Mapped[str] = mapped_column(String(64), index=True)
|
||||
updated_at: Mapped[str] = mapped_column(String(64), index=True)
|
||||
|
||||
|
||||
class SalesStageHistoryRow(Base):
|
||||
__tablename__ = "sales_stage_history"
|
||||
__table_args__ = (
|
||||
Index("idx_sales_stage_history_deal_changed", "deal_id", "changed_at"),
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
history_id: Mapped[str] = mapped_column(String(64), unique=True, index=True)
|
||||
tenant_id: Mapped[str] = mapped_column(String(64), index=True)
|
||||
deal_id: Mapped[str] = mapped_column(String(64), index=True)
|
||||
from_stage_id: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||||
to_stage_id: Mapped[str] = mapped_column(String(64), index=True)
|
||||
changed_by_type: Mapped[str] = mapped_column(String(32), index=True)
|
||||
changed_by_id: Mapped[str | None] = mapped_column(String(128), nullable=True, index=True)
|
||||
reason: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
changed_at: Mapped[str] = mapped_column(String(64), index=True)
|
||||
|
||||
|
||||
class SalesEscalationRow(Base):
|
||||
__tablename__ = "sales_escalations"
|
||||
__table_args__ = (
|
||||
Index("idx_sales_escalations_deal_status", "deal_id", "status"),
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
escalation_id: Mapped[str] = mapped_column(String(64), unique=True, index=True)
|
||||
tenant_id: Mapped[str] = mapped_column(String(64), index=True)
|
||||
deal_id: Mapped[str] = mapped_column(String(64), index=True)
|
||||
escalation_type: Mapped[str] = mapped_column(String(64), index=True)
|
||||
reason: Mapped[str] = mapped_column(Text)
|
||||
severity: Mapped[str] = mapped_column(String(16), index=True)
|
||||
status: Mapped[str] = mapped_column(String(32), index=True, default="open")
|
||||
assigned_to_user_id: Mapped[str | None] = mapped_column(String(128), nullable=True, index=True)
|
||||
created_at: Mapped[str] = mapped_column(String(64), index=True)
|
||||
resolved_at: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||||
|
||||
|
||||
class SalesAutomationTaskRow(Base):
|
||||
__tablename__ = "sales_automation_tasks"
|
||||
__table_args__ = (
|
||||
Index("idx_sales_tasks_run_status", "run_at", "status"),
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
task_id: Mapped[str] = mapped_column(String(64), unique=True, index=True)
|
||||
tenant_id: Mapped[str] = mapped_column(String(64), index=True)
|
||||
deal_id: Mapped[str] = mapped_column(String(64), index=True)
|
||||
task_type: Mapped[str] = mapped_column(String(64), index=True)
|
||||
payload_json: Mapped[str] = mapped_column(Text, default="{}")
|
||||
run_at: Mapped[str] = mapped_column(String(64), index=True)
|
||||
status: Mapped[str] = mapped_column(String(32), index=True, default="pending")
|
||||
retry_count: Mapped[int] = mapped_column(Integer, default=0)
|
||||
last_error: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
created_at: Mapped[str] = mapped_column(String(64), index=True)
|
||||
updated_at: Mapped[str] = mapped_column(String(64), index=True)
|
||||
|
||||
|
||||
class SalesChannelSwitchRow(Base):
|
||||
__tablename__ = "sales_channel_switches"
|
||||
__table_args__ = (
|
||||
Index("idx_sales_channel_switches_deal_switched", "deal_id", "switched_at"),
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
switch_id: Mapped[str] = mapped_column(String(64), unique=True, index=True)
|
||||
tenant_id: Mapped[str] = mapped_column(String(64), index=True)
|
||||
deal_id: Mapped[str] = mapped_column(String(64), index=True)
|
||||
communication_id: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||||
from_channel: Mapped[str] = mapped_column(String(32), index=True)
|
||||
to_channel: Mapped[str] = mapped_column(String(32), index=True)
|
||||
reason_for_channel_switch: Mapped[str] = mapped_column(Text)
|
||||
switched_at: Mapped[str] = mapped_column(String(64), index=True)
|
||||
|
||||
|
||||
class SalesExternalLinkRow(Base):
|
||||
__tablename__ = "sales_external_links"
|
||||
__table_args__ = (
|
||||
Index("idx_sales_external_links_deal_sync", "deal_id", "last_sync_at"),
|
||||
Index("idx_sales_external_links_thread", "external_thread_id"),
|
||||
Index("idx_sales_external_links_voice_session", "voice_session_id"),
|
||||
Index("idx_sales_external_links_external_call", "external_call_id"),
|
||||
Index("idx_sales_external_links_interaction", "interaction_id"),
|
||||
Index("idx_sales_external_links_customer_phone", "customer_id", "phone_number"),
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
link_id: Mapped[str] = mapped_column(String(64), unique=True, index=True)
|
||||
tenant_id: Mapped[str] = mapped_column(String(64), index=True)
|
||||
deal_id: Mapped[str] = mapped_column(String(64), index=True)
|
||||
communication_id: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||||
sales_call_id: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||||
channel_provider: Mapped[str] = mapped_column(String(32), index=True)
|
||||
external_thread_id: Mapped[str | None] = mapped_column(String(128), nullable=True)
|
||||
external_chat_id: Mapped[str | None] = mapped_column(String(128), nullable=True)
|
||||
external_call_id: Mapped[str | None] = mapped_column(String(128), nullable=True)
|
||||
voice_session_id: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||||
ai_session_id: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||||
interaction_id: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||||
customer_id: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||||
phone_number: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||||
external_status: Mapped[str | None] = mapped_column(String(32), nullable=True, index=True)
|
||||
link_metadata_json: Mapped[str] = mapped_column(Text, default="{}")
|
||||
created_at: Mapped[str] = mapped_column(String(64), index=True)
|
||||
updated_at: Mapped[str] = mapped_column(String(64), index=True)
|
||||
last_sync_at: Mapped[str] = mapped_column(String(64), index=True)
|
||||
Reference in New Issue
Block a user