feat: добавлена интеграция синхронизации с Recommerce
This commit is contained in:
@@ -2,63 +2,90 @@ from typing import Dict, Any, List, Optional
|
||||
from decimal import Decimal
|
||||
|
||||
|
||||
def to_api_product(product: Any, stock_count: Optional[int] = None, fields: Optional[List[str]] = None) -> Dict[str, Any]:
|
||||
def to_api_product(
|
||||
product: Any,
|
||||
stock_count: Optional[int] = None,
|
||||
fields: Optional[List[str]] = None,
|
||||
for_create: bool = False
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Преобразование внутреннего товара в формат Recommerce API.
|
||||
|
||||
|
||||
Recommerce API использует form-data для POST запросов, поэтому вложенные поля
|
||||
представляются в плоском формате: price[amount], price[currency]
|
||||
|
||||
Args:
|
||||
product: Экземпляр модели Product
|
||||
stock_count: Остаток товара (вычисляется отдельно, т.к. зависит от склада)
|
||||
fields: Список полей для экспорта. Если None - экспортируются все базовые поля.
|
||||
Поддерживаемые поля: 'sku', 'name', 'price', 'description', 'count', 'images'.
|
||||
|
||||
for_create: Если True - включает все обязательные поля для создания товара.
|
||||
|
||||
Returns:
|
||||
Dict: JSON-совместимый словарь для API
|
||||
Dict: Плоский словарь для form-data запроса
|
||||
"""
|
||||
data = {}
|
||||
|
||||
|
||||
# Если поля не указаны, берем базовый набор (без тяжелых полей типа картинок)
|
||||
if fields is None:
|
||||
fields = ['sku', 'name', 'price']
|
||||
|
||||
|
||||
# Для создания товара добавляем все обязательные поля
|
||||
if for_create:
|
||||
fields = list(set(fields) | {'sku', 'name', 'price', 'parent_category_sku'})
|
||||
|
||||
# SKU (Артикул) - обязательное поле для идентификации
|
||||
# Если product.sku нет, используем id (как fallback, но лучше sku)
|
||||
sku = getattr(product, 'sku', str(product.id))
|
||||
|
||||
# Всегда добавляем SKU если это не обновление, но для update метода API SKU идет в URL.
|
||||
# Но маппер просто готовит данные.
|
||||
|
||||
|
||||
if 'sku' in fields:
|
||||
data['sku'] = sku
|
||||
|
||||
|
||||
if 'name' in fields:
|
||||
data['name'] = product.name
|
||||
|
||||
|
||||
if 'parent_category_sku' in fields:
|
||||
# TODO: Добавить поле recommerce_category_sku в модель Product или Category
|
||||
# Пока пытаемся взять из атрибута, если он есть
|
||||
category_sku = getattr(product, 'recommerce_category_sku', None)
|
||||
if category_sku:
|
||||
data['parent_category_sku'] = category_sku
|
||||
|
||||
if 'price' in fields:
|
||||
# Recommerce ожидает price[amount] и price[currency]
|
||||
# Предполагаем, что валюта магазина совпадает с валютой Recommerce (BYN)
|
||||
data['price'] = {
|
||||
'amount': float(product.sale_price or 0),
|
||||
'currency': 'BYN' # Можно вынести в настройки
|
||||
}
|
||||
|
||||
# Recommerce ожидает price[amount] и price[currency] в form-data формате
|
||||
# Значения передаём как строки (так работает в проверенном скрипте)
|
||||
has_discount = product.sale_price and product.price and product.sale_price < product.price
|
||||
|
||||
if has_discount:
|
||||
# Есть скидка: текущая цена = sale_price, старая = price
|
||||
data['price[amount]'] = str(float(product.sale_price))
|
||||
data['price[currency]'] = 'BYN'
|
||||
# price_old - как в рабочем скрипте (не old_price из документации)
|
||||
data['price_old[amount]'] = str(float(product.price))
|
||||
data['price_old[currency]'] = 'BYN'
|
||||
# TODO: is_special='true' принимается API но игнорируется при обновлении.
|
||||
# Возможно работает только при создании товара.
|
||||
else:
|
||||
# Нет скидки: только основная цена
|
||||
data['price[amount]'] = str(float(product.price or 0))
|
||||
data['price[currency]'] = 'BYN'
|
||||
|
||||
if 'content' in fields:
|
||||
# content включает название и описание
|
||||
data['name'] = product.name
|
||||
data['description'] = product.description or ''
|
||||
|
||||
if 'description' in fields:
|
||||
data['description'] = product.description or ''
|
||||
|
||||
|
||||
if 'count' in fields and stock_count is not None:
|
||||
data['count'] = stock_count
|
||||
|
||||
|
||||
if 'images' in fields:
|
||||
# Примерная логика для картинок
|
||||
# Recommerce ожидает массив URL или объектов.
|
||||
# Документация: images[] - Картинки товара
|
||||
images = []
|
||||
if hasattr(product, 'images'):
|
||||
for img in product.images.all():
|
||||
if img.image:
|
||||
images.append(img.image.url)
|
||||
if images:
|
||||
data['images'] = images
|
||||
# Recommerce ожидает images[] для массива картинок
|
||||
if hasattr(product, 'photos'):
|
||||
for idx, photo in enumerate(product.photos.all()):
|
||||
if photo.image:
|
||||
data[f'images[{idx}]'] = photo.image.url
|
||||
|
||||
return data
|
||||
|
||||
|
||||
Reference in New Issue
Block a user