feat: добавлена интеграция синхронизации с Recommerce
This commit is contained in:
119
myproject/integrations/recommerce/tasks.py
Normal file
119
myproject/integrations/recommerce/tasks.py
Normal file
@@ -0,0 +1,119 @@
|
||||
import logging
|
||||
from celery import shared_task
|
||||
from django.db import transaction
|
||||
from django_tenants.utils import schema_context
|
||||
from integrations.models import RecommerceIntegration
|
||||
from integrations.recommerce.services import RecommerceService
|
||||
from products.models import Product
|
||||
from integrations.recommerce.exceptions import RecommerceError, RecommerceAPIError
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@shared_task(bind=True)
|
||||
def sync_products_batch_task(self, product_ids, options=None, schema_name=None):
|
||||
"""
|
||||
Celery задача для массовой синхронизации товаров с Recommerce.
|
||||
|
||||
Args:
|
||||
product_ids (list): Список ID товаров (PK) для синхронизации
|
||||
options (dict): Настройки синхронизации
|
||||
- fields (list): Список полей для обновления ['price', 'count', 'content', 'images']
|
||||
- create_if_missing (bool): Создавать товар, если он не найден (404)
|
||||
schema_name (str): Имя схемы тенанта для выполнения запросов
|
||||
"""
|
||||
if options is None:
|
||||
options = {}
|
||||
|
||||
fields = options.get('fields', [])
|
||||
create_if_missing = options.get('create_if_missing', False)
|
||||
|
||||
# Используем schema_context для выполнения запросов в правильной tenant схеме
|
||||
if schema_name:
|
||||
with schema_context(schema_name):
|
||||
return _do_sync(product_ids, fields, create_if_missing)
|
||||
else:
|
||||
return _do_sync(product_ids, fields, create_if_missing)
|
||||
|
||||
|
||||
def _do_sync(product_ids, fields, create_if_missing):
|
||||
"""
|
||||
Внутренняя функция для выполнения синхронизации в контексте схемы.
|
||||
|
||||
Args:
|
||||
product_ids (list): Список ID товаров (PK) для синхронизации
|
||||
fields (list): Список полей для обновления ['price', 'count', 'content', 'images']
|
||||
create_if_missing (bool): Создавать товар, если он не найден (404)
|
||||
"""
|
||||
# 1. Получаем интеграцию
|
||||
integration = RecommerceIntegration.objects.filter(is_active=True).first()
|
||||
if not integration or not integration.is_configured:
|
||||
msg = "Recommerce integration is not active or configured."
|
||||
logger.error(msg)
|
||||
return {"success": False, "error": msg}
|
||||
|
||||
service = RecommerceService(integration)
|
||||
|
||||
# 2. Получаем товары
|
||||
products = Product.objects.filter(pk__in=product_ids)
|
||||
|
||||
results = {
|
||||
"total": len(product_ids),
|
||||
"success": 0,
|
||||
"failed": 0,
|
||||
"created": 0,
|
||||
"updated": 0,
|
||||
"errors": []
|
||||
}
|
||||
|
||||
logger.info(f"Starting Recommerce sync for {len(product_ids)} products. Fields: {fields}, create_if_missing: {create_if_missing}")
|
||||
|
||||
for product in products:
|
||||
try:
|
||||
# Если fields пустой или содержит 'all' - обновляем всё (передаем None в сервис)
|
||||
# Иначе передаем список полей.
|
||||
# В сервисе update_product(fields=None) обновляет всё.
|
||||
|
||||
# Маппинг опций фронтенда на логику сервиса
|
||||
# Фронт: ['price', 'count', 'content', 'images']
|
||||
# Сервис: ожидает список полей или None (всё).
|
||||
# Если выбраны не все галочки, передаем конкретные поля.
|
||||
# Но 'content' и 'images' в сервисе могут не поддерживаться напрямую как ключи,
|
||||
# нужно смотреть реализацию to_api_product.
|
||||
# Пока передаем как есть, предполагая, что сервис или маппер разберется,
|
||||
# либо если выбрано "все", передаем None.
|
||||
|
||||
# Упрощение: если выбраны все основные группы, считаем это полным обновлением
|
||||
is_full_update = False
|
||||
if 'content' in fields and 'images' in fields and 'price' in fields and 'count' in fields:
|
||||
is_full_update = True
|
||||
|
||||
service_fields = None if is_full_update else fields
|
||||
|
||||
# Если список полей пуст (ничего не выбрано), но задача запущена - странно, но пропустим
|
||||
if not is_full_update and not service_fields:
|
||||
logger.warning(f"Product {product.id}: No fields selected for update.")
|
||||
continue
|
||||
|
||||
try:
|
||||
service.update_product(product, fields=service_fields)
|
||||
results["updated"] += 1
|
||||
results["success"] += 1
|
||||
|
||||
except RecommerceAPIError as e:
|
||||
# Если товар не найден (404) и разрешено создание
|
||||
if e.status_code == 404 and create_if_missing:
|
||||
logger.info(f"Product {product.sku} not found. Creating...")
|
||||
service.create_product(product)
|
||||
results["created"] += 1
|
||||
results["success"] += 1
|
||||
else:
|
||||
raise e
|
||||
|
||||
except Exception as e:
|
||||
results["failed"] += 1
|
||||
error_msg = f"Product {product.sku} ({product.id}): {str(e)}"
|
||||
logger.error(error_msg)
|
||||
results["errors"].append(error_msg)
|
||||
|
||||
logger.info(f"Recommerce sync completed. Results: {results}")
|
||||
return results
|
||||
Reference in New Issue
Block a user