- Добавить константу RECOMMERCE_INFINITY_COUNT = 999999 в mappers.py - Изменить логику: product.in_stock определяет count (0 или 999999) - Добавить test_count.py для тестирования поля count - Обновить документацию recommerce_api.md с секцией Product Availability Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
116 lines
5.0 KiB
Python
116 lines
5.0 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, RECOMMERCE_INFINITY_COUNT
|
||
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:
|
||
# Используем in_stock для определения наличия в формате Recommerce:
|
||
# - in_stock=False → count=0 (нет в наличии)
|
||
# - in_stock=True → count=999999 (есть в наличии, "бесконечность")
|
||
stock_count = RECOMMERCE_INFINITY_COUNT if product.in_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.
|
||
|
||
Raises:
|
||
RecommerceError: Если отсутствует обязательное поле parent_category_sku
|
||
"""
|
||
# Для создания нужны все поля
|
||
# Используем in_stock для определения наличия в формате Recommerce
|
||
stock_count = RECOMMERCE_INFINITY_COUNT if product.in_stock else 0
|
||
|
||
data = to_api_product(product, stock_count=stock_count, fields=None, for_create=True)
|
||
|
||
# Проверяем обязательное поле для создания
|
||
if 'parent_category_sku' not in data:
|
||
raise RecommerceError(
|
||
f"Невозможно создать товар '{product.sku}' в Recommerce: "
|
||
f"не указана категория (parent_category_sku). "
|
||
f"Добавьте поле recommerce_category_sku к товару или категории."
|
||
)
|
||
|
||
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 |