Files
Andrey Smakotin eff9778539 fix(recommerce): использовать in_stock для определения наличия в API
- Добавить константу 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>
2026-01-13 14:35:10 +03:00

116 lines
5.0 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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