Initial commit from monorepo

This commit is contained in:
Ruslan Bakiev
2026-01-07 09:16:05 +07:00
commit 685fbbe14a
28 changed files with 2291 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
from .kyc_workflow_client import KycWorkflowClient
__all__ = ["KycWorkflowClient"]

View File

@@ -0,0 +1,117 @@
"""
KYC Workflow Client - контракт взаимодействия с Temporal KYC workflow.
Этот файл содержит методы для запуска KYC workflow из Django.
Approve/reject сигналы отправляются из Odoo.
"""
import asyncio
import logging
from dataclasses import dataclass, field
from typing import Optional
from django.conf import settings
from temporalio.client import Client
logger = logging.getLogger(__name__)
@dataclass
class KycWorkflowData:
"""Данные для запуска KYC workflow."""
kyc_request_id: str
team_name: str
owner_id: str
owner_email: str
country_code: str
country_data: dict = field(default_factory=dict)
# team_id создаётся в workflow после approve, не передаётся сюда
class KycWorkflowClient:
"""
Клиент для запуска KYC Application workflow.
Использование:
KycWorkflowClient.start(kyc_request)
Flow:
1. Django вызывает start() → запускает workflow
2. Workflow добавляет KYC в Odoo, ставит статус KYC_IN_REVIEW
3. Workflow ждёт сигнала approve/reject (из Odoo)
4. После approve → создаёт Logto org, ставит ACTIVE
"""
WORKFLOW_NAME = "kyc_application"
@classmethod
async def _get_client(cls) -> Client:
"""Получить подключение к Temporal."""
return await Client.connect(
settings.TEMPORAL_HOST,
namespace=settings.TEMPORAL_NAMESPACE,
)
@classmethod
async def start_async(cls, data: KycWorkflowData) -> str:
"""
Запустить KYC Application workflow (async).
Returns:
workflow_id: ID запущенного workflow
"""
client = await cls._get_client()
workflow_id = data.kyc_request_id
await client.start_workflow(
cls.WORKFLOW_NAME,
{
"kyc_request_id": data.kyc_request_id,
"team_name": data.team_name,
"owner_id": data.owner_id,
"owner_email": data.owner_email,
"country_code": data.country_code,
"country_data": data.country_data,
},
id=workflow_id,
task_queue=settings.TEMPORAL_TASK_QUEUE,
)
logger.info(f"KYC workflow started: {workflow_id}")
return workflow_id
@classmethod
def start(cls, kyc_request) -> Optional[str]:
"""
Запустить KYC Application workflow (sync wrapper).
Args:
kyc_request: KYCRequest model instance (главная модель)
Returns:
workflow_id или None при ошибке
"""
# Собираем данные страны через GenericForeignKey
country_data = kyc_request.get_country_data()
data = KycWorkflowData(
kyc_request_id=str(kyc_request.uuid),
team_name=kyc_request.team_name or country_data.get('company_name', ''),
owner_id=kyc_request.user_id,
owner_email=kyc_request.contact_email,
country_code=kyc_request.country_code,
country_data=country_data,
)
try:
workflow_id = asyncio.run(cls.start_async(data))
kyc_request.workflow_status = "active"
kyc_request.save(update_fields=["workflow_status", "updated_at"])
return workflow_id
except Exception as e:
logger.error(f"Failed to start KYC workflow: {e}")
kyc_request.workflow_status = "error"
kyc_request.save(update_fields=["workflow_status", "updated_at"])
return None