Создание фида ценообразования по шаблону

Фид для интеграции можно создать с помощью экспорта каталога по шаблону. Для этого небходимо создать профиль экспорта:

  • Перейдите на страницу "Настройки"
  • Перейдите на вкладку "Экспорт"
  • Нажмите на кнопку "+"
  • Укажите название профиля экспорта, выберите профиль ценообразования, по которому будет подготовлен фид, и выберите формат "Каталог по шаблону"
  • Нажмите "Сохранить"

После сохранения профиля экспорта станет доступна настройка шаблона интеграции.

Редактирование существующего профиля:

  • Нажмите напротив этого профиля на значок карандашика.
  • Отредактируйте название или добавьте/удалите профиль ценообразования.
  • Нажмите кнопку "Сохранить".

Удаление существующего профиля:

  • Нажмите напротив этого профиля на значок корзинки.
  • Подтвердите удаление.

Настройка шаблона интеграции:

  • Перейдите на вкладку "Настройка шаблона".
  • Выберите тип выгрузки параметров.
  • Выберите тип валидации (при необходимости).
  • Выберите шаблон (нажамите кнопку "Шаблоны") или напишите свой.
  • Нажмите кнопку "Сохранить".

Для написания шаблона мы используем язык Scriban. Scriban - это быстрый, мощный, безопасный и легкий язык сценариев. Подробнее с языком можно ознакимиться здесь. Проверить корректность работы шаблона можно нажав на кнопку "Тест". После этого появится окно "Результаты", в котором, при корректнонаписаном шаблоне, появится результат (как будет выглядеть фид).

Шаблон на вход получает объект model, содержащий следующие поля:

  • Currency - [строка] - валюта профиля ценообразования
  • Products - [список объектов] - список товаров каталога
  • AllCategories - [список объектов] - список категорий каталога
  • Categories - [список объектов] - список категорий товаров из ценообразования и их родителей
  • Offers - [список объектов] - список офферов (цен, сформированных по правилам профиля ценообазования)
  • MaxDeliveryCost - [число] - максимальная стоимость доставки по стране среди всех офферов
  • MaxDeliveryTime - [число] - максимальный срок доставки среди всех офферов

Объекты из спискаов Categories и AllCategories содержат следующие данные о категориях:

  • Id - [число] - Id
  • Name - [текст] - название категории
  • SingularName - [текст] - название категории в единственном числе
  • ParentId - [число или null] - Id родительской категории

Объекты из списка Products содержат следующие данные о товарах:

Объекты из списка Parameters содержат следующие данные о характеристиках товаров:

  • Name - [текст] - название характеристики
  • DefinitionId - [текст] - идентификатор характеристики
  • DefinitionExternalId - [текст или null] - внешний идентификатор характеристики
  • Order - [текст] - порядок сортировки характеристики
  • Group - [текст или null] - группа характеристики
  • GroupId - [число или null] - идентификатор группы характеристики
  • GroupOrder - [число или null] - порядок сортировки группы
  • Value - [текст] - значение характеристики (формат зависит от типа: Да/Нет, число, диапазон min...max, одно значение enum, список флагов через ; , список моделей через ; , строка/выражение)
  • Unit - [текст] - единица измерения (только для числовых/диапазонных и numeric enum/flags)

Объекты из списка Tags содержат следующие данные о тегах товаров:

  • Id - [число] - Id
  • Name - [текст] - название тега

Объекты из списка MinRetailPrices содержат данные о минимальных розничных ценах (МРЦ), рассчитанных на основе цен поставщиков:

Объект OriginalRecord содержит исходную (необработанную) запись строки прайса поставщика:

  • [ключ: значение] - [любой тип] - поля оригинальной строки прайса поставщика, сформированные из исходного JSON без нормализации
  • Param - [объект] - параметры товара из оригинального прайса в виде словаря
    где:
    • ключ[текст] – название параметра
    • значение[любой тип] – значение параметра из прайса поставщика

Объекты из списка Offers содержат следующие данные об офферах (предложениях):

Добавление условий в шаблон

Условия помогают в выборе товаров, соответствующих определенным критериям. Для добавления условий удобно пользоваться функциями. Функции небходимо определить до шаблона и вызвать внутри шаблона. Пример:

{{-func condition(offer)
    if (offer.MinHighCompetitorPrice == null && offer.MinLowCompetitorPrice == null)
        ret true
    else
        ret false
    end
end-}}
<?xml version="1.0" encoding="utf-8"?>
<yml_catalog date="{{ date.now | date.to_string '%F %R' }}">
  <shop>
    <categories>
      {{- for category in model.Categories}}
      <category id="{{category.Id}}" parentId="{{category.ParentId}}">{{category.Name}}</category>
      {{- end }}
    </categories>
    <company>ООО "Компания"</company>
    <currencies>
      <currency id="{{model.Currency}}" />
    </currencies>
    <delivery-options>
      <option cost="{{model.MaxDeliveryCost}}" days="{{model.MaxDeliveryTime}}" />
    </delivery-options>
    <name>Название магазина</name>
    <url>https://example.ru/</url>
    <offers>
      {{- for offer in model.Offers}}
      {{- if condition offer }}
      <offer id="{{offer.OfferId}}" type="vendor.model" available="true">
        <barcode>{{offer.BarCodes | array.join ", "}}</barcode>
        <categoryId>{{offer.CategoryId}}</categoryId>
        <currencyId>{{model.Currency}}</currencyId>
        <outlets>
          <outlet id="0" instock="{{offer.InStockAmount}}" />
        </outlets>
        <model>{{offer.Model}} {{offer.Color}}</model>
        <name>{{offer.CategorySingularName}} {{offer.Vendor}} {{offer.Model}} {{offer.Color}} {{offer.Article}}</name>
        {{- for parameter in offer.Parameters}}
        <param name="{{parameter.Name}}" unit="{{parameter.Unit}}">{{parameter.Value}}</param>
        {{- if parameter.Name == "Страна-производитель" -}}
        {{- capture country_of_origin -}}
        {{parameter.Value}}
        {{- end -}}
        {{- end -}}
        {{- if parameter.Name == "Длина в упаковке" -}}
        {{- capture length -}}
        {{parameter.Value}}
        {{- end -}}
        {{- end -}}
        {{- if parameter.Name == "Ширина в упаковке" -}}
        {{- capture width -}}
        {{parameter.Value}}
        {{- end -}}
        {{- end -}}
        {{- if parameter.Name == "Высота в упаковке" -}}
        {{- capture height -}}
        {{parameter.Value}}
        {{- end -}}
        {{- end -}}
        {{- if parameter.Name == "Вес" -}}
        {{- capture weight -}}
        {{parameter.Value}}
        {{- end -}}
        {{- end -}}
        {{- end -}}
        {{- for picture in offer.Pictures}}
        <picture>{{picture}}</picture>
        {{- end }}
        <country_of_origin>{{country_of_origin}}</country_of_origin>
        <dimensions>{{length}}/{{width}}/{{height}}</dimensions>
        <weight>{{weight}}</weight>
        <description>{{offer.Description}}</description>
        <price>{{offer.Price}}</price>
        <typePrefix>{{offer.CategorySingularName}}</typePrefix>
        <url>https://techshop.ru/product/?XML_ID={{offer.ExternalId}}</url>
        <vendor>{{offer.Vendor}}</vendor>
        <vendorCode>{{offer.Article}}</vendorCode>
        <shop-sku>{{offer.OfferId}}</shop-sku>
      </offer>
      {{- end }}
      {{- end }}
    </offers>
  </shop>
</yml_catalog>

Вызов функции:

 <offers>
      {{- for offer in model.Offers}}
      {{- if condition offer }}
      <offer id="{{offer.OfferId}}" type="vendor.model" available="true">
        <barcode>{{offer.BarCodes | array.join ", "}}</barcode>
       ...
        <shop-sku>{{offer.OfferId}}</shop-sku>
      </offer>
      {{- end }}
      {{- end }}
</offers>

Примеры условий:

  • Цены выше до n % от цен конкурентов 1-го уровня
{{-func condition(offer, ConditionVal)
 if (offer.MinHighCompetitorPrice != null && ConditionVal != null)
     if (offer.Price > offer.MinHighCompetitorPrice && (offer.Price - offer.MinHighCompetitorPrice) * 100 / offer.MinHighCompetitorPrice < ConditionVal)
             ret true
     else
             ret false
     end
 end
end-}}
  • Цены = ценам конкурентов 1-го уровня
{{-func condition(offer)
 if (offer.MinHighCompetitorPrice != null)
     if (offer.MinHighCompetitorPrice == Price)
             ret true
     else
             ret false
     end
 end
end-}}
  • Цена ниже, чем у 1-го, но выше более n %, чем у 2-го
{{-func condition(offer, ConditionVal)
 if (offer.MinHighCompetitorPrice != null && offer.MinLowCompetitorPrice != null)
     if (offer.Price < offer.MinHighCompetitorPrice && offer.Price > offer.MinLowCompetitorPrice && (offer.Price - offer.MinLowCompetitorPrice) * 100 / offer.MinLowCompetitorPrice > ConditionVal)
             ret true
     else
             ret false
     end
 end
end-}}
  • Цена ниже, чем у 1-го, но выше не более n %, чем у 2-го
{{-func condition(offer, ConditionVal)
 if (offer.MinHighCompetitorPrice != null && offer.MinLowCompetitorPrice != null)
     if (offer.Price < offer.MinHighCompetitorPrice && offer.Price > offer.MinLowCompetitorPrice && (offer.Price - offer.MinLowCompetitorPrice) * 100 / offer.MinLowCompetitorPrice <= ConditionVal)
             ret true
     else
             ret false
     end
 end
end-}}
  • Цена ниже, чем у 1-го и 2-го
{{-func condition(offer)
 if (offer.MinHighCompetitorPrice != null && offer.MinLowCompetitorPrice != null)
     if (offer.Price < offer.MinHighCompetitorPrice && offer.Price < offer.MinLowCompetitorPrice)
             ret true
     else
             ret false
     end
 end
end-}}
  • Нет товара у конкурентов, но есть у нас
{{-func condition(offer)
 if (offer.MinHighCompetitorPrice == null && offer.MinLowCompetitorPrice == null)
             ret true
     else
             ret false
 end
end-}}
  • Минимальное время доставки в диапазоне MinDeliveryTime-MaxDeliveryTime дня и маржа в диапазоне MinProfit-MaxProfit %
{{-func condition(offer, MinProfit, MaxProfit, MinDeliveryTime, MaxDeliveryTime)
 if (offer.Profit >= MinProfit && offer.Profit <= MaxProfit && offer.DeliveryTime >= MinDeliveryTime && offer.DeliveryTime <= MaxDeliveryTime)
             ret true
     else
             ret false
 end
end-}}
  • Функция для получения родительской категории любого уровня (положительный level для подсчета уровня с корня дерева, отрицательный - для подсчета с категории товара):
categories = {}
for category in model.Categories
    categories[category.Id] = category
end

# use positive level to iterate categories from root
# use negative level to iterate categories from leaf
func getCategory(offer, level)
    i = 0
    path = []
    current = categories[offer.CategoryId]
    while current
        path[i] = current
        i = i + 1
        current = categories[current?.ParentId ?? 0]
    end
    if level < 0 && path.size > -level
        ret path[-level]
    end
    if path.size >= level
    begin
        ret path[path.size - level - 1]
    end
    ret null
end

Условий может быть несколько (при этом они должны называться по разному), несколько условий можно скомбинировать в одно, a также добавлять свои условия. Проверить корректность условий можно здесь.

Для выполнения экспорта:

  • Перейдите на страницу "Задачи".
  • Перейдите на вкладку "Экспорт".
  • Выберите необходимый профиль экспорта.
  • Нажмите кнопку "Запустить".

Статус задачи, а также историю последних выполненых экспортов можно посмотреть в списке "Последние экспорты".Результатом выполнения задачи является файл в формате .xml, который можно скачать нажав напротив нужного экспорта "Скачать". В настройках шаблона можно посмотеть ссылку на этот файл.