Реализован клиент для работы с API Recommerce, включая: - Клиент с методами для работы с товарами и заказами - Сервисный слой для высокоуровневых операций - Мапперы данных между форматами - Обработку исключений Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
105 lines
4.6 KiB
Python
105 lines
4.6 KiB
Python
from typing import List, Dict, Any, Optional
|
||
from django.conf import settings
|
||
from .client import RecommerceClient
|
||
from .mappers import to_api_product, from_api_order
|
||
from .exceptions import RecommerceError
|
||
# Imports for typing only to avoid circular dependency issues at module level if possible
|
||
# but for simplicity in this structure we'll import inside methods if needed or use 'Any'
|
||
|
||
|
||
class RecommerceService:
|
||
"""
|
||
Высокоуровневый сервис для интеграции с Recommerce.
|
||
Предоставляет методы для синхронизации товаров и заказов.
|
||
"""
|
||
|
||
def __init__(self, integration_instance):
|
||
"""
|
||
Args:
|
||
integration_instance: Экземпляр модели RecommerceIntegration
|
||
"""
|
||
self.integration = integration_instance
|
||
self.client = RecommerceClient(
|
||
store_url=integration_instance.store_url,
|
||
api_token=integration_instance.api_token
|
||
)
|
||
|
||
def update_product(self, product: Any, fields: Optional[List[str]] = None) -> bool:
|
||
"""
|
||
Обновить товар в Recommerce.
|
||
|
||
Args:
|
||
product: Экземпляр Product
|
||
fields: Список полей для обновления (например ['price', 'count']).
|
||
Если None - обновляются все поля.
|
||
|
||
Returns:
|
||
bool: Успех операции
|
||
"""
|
||
# Получаем остаток, если нужно
|
||
stock_count = None
|
||
if fields is None or 'count' in fields:
|
||
# Пытаемся получить остаток.
|
||
# Логика получения остатка может зависеть от вашей системы inventory.
|
||
# Здесь предполагаем, что у product есть метод или связь для получения общего остатка.
|
||
# Для простоты используем первый попавшийся Stock или 0
|
||
# В реальном проекте тут должна быть логика выбора склада
|
||
stock = product.stocks.first()
|
||
stock_count = int(stock.quantity_free) if stock else 0
|
||
|
||
data = to_api_product(product, stock_count=stock_count, fields=fields)
|
||
|
||
try:
|
||
# Сначала пробуем обновить
|
||
# SKU берем из data или продукта
|
||
sku = data.get('sku', getattr(product, 'sku', str(product.id)))
|
||
|
||
# Recommerce API: POST /catalog/products/{sku} для обновления
|
||
self.client.update_product(sku, data)
|
||
return True
|
||
|
||
except RecommerceError as e:
|
||
# Если 404 - товар не найден, можно попробовать создать?
|
||
# В рамках "простой" интеграции - пока просто логируем или рейзим
|
||
# Если нужно автоматическое создание:
|
||
# if isinstance(e, RecommerceAPIError) and e.status_code == 404:
|
||
# return self.create_product(product)
|
||
raise e
|
||
|
||
def create_product(self, product: Any) -> bool:
|
||
"""Создать товар в Recommerce"""
|
||
# Для создания нужны все поля
|
||
stock = product.stocks.first()
|
||
stock_count = int(stock.quantity_free) if stock else 0
|
||
|
||
data = to_api_product(product, stock_count=stock_count, fields=None)
|
||
self.client.create_product(data)
|
||
return True
|
||
|
||
def get_new_orders(self, updated_after: str) -> List[Dict[str, Any]]:
|
||
"""
|
||
Получить список новых заказов из Recommerce.
|
||
|
||
Args:
|
||
updated_after: Дата в формате 'Y-m-d-H-i-s'
|
||
|
||
Returns:
|
||
List[Dict]: Список DTO заказов (готовых для сохранения)
|
||
"""
|
||
raw_orders = self.client.get_orders(updated_after=updated_after)
|
||
|
||
orders_dto = []
|
||
for raw_order in raw_orders:
|
||
dto = from_api_order(raw_order)
|
||
orders_dto.append(dto)
|
||
|
||
return orders_dto
|
||
|
||
def check_connection(self) -> bool:
|
||
"""Проверить соединение"""
|
||
try:
|
||
# Пробуем получить список заказов (легкий запрос)
|
||
self.client.get_orders()
|
||
return True
|
||
except RecommerceError:
|
||
return False |