Интеграция 1С-Битрикс с PIM-системой Akeneo
Akeneo — специализированная система управления информацией о продуктах (PIM). Когда в каталоге тысячи товаров с десятками атрибутов, переводами на несколько языков и вариантами для разных каналов, хранить всё это напрямую в инфоблоке Битрикс становится неудобно. Akeneo берёт на себя обогащение и валидацию данных, Битрикс — продажи и транзакции. Интеграция строится на Akeneo REST API.
Архитектура потока данных
Типичная схема:
1С (номенклатура, цены, остатки)
↓
Akeneo
(описания, атрибуты, медиа, переводы)
↓
Битрикс
(каталог, корзина, заказы)
Akeneo является «мастером» продуктового контента. 1С — источником для SKU, цен и остатков. Битрикс — каналом продаж, который читает данные из Akeneo.
Направление синхронизации: Akeneo → Битрикс. Обратный поток (Битрикс → Akeneo) используется редко — только для передачи статистики (просмотры, заказы) при наличии такой потребности.
Akeneo REST API
Akeneo предоставляет REST API с OAuth 2.0 (client credentials):
POST /api/oauth/v1/token
GET /api/rest/v1/products?search={"enabled":[{"operator":"=","value":true}]}
GET /api/rest/v1/products/{code}
GET /api/rest/v1/product-models
GET /api/rest/v1/categories
GET /api/rest/v1/attributes
GET /api/rest/v1/media-files/{code}/download
Получение токена:
class AkeneoClient
{
private string $baseUrl;
private string $token;
public function __construct(
string $baseUrl,
string $clientId,
string $secret,
string $username,
string $password
) {
$this->baseUrl = rtrim($baseUrl, '/');
$this->token = $this->authenticate($clientId, $secret, $username, $password);
}
private function authenticate(
string $clientId,
string $secret,
string $username,
string $password
): string {
$http = new \Bitrix\Main\Web\HttpClient();
$http->setHeader('Authorization',
'Basic ' . base64_encode($clientId . ':' . $secret));
$http->setHeader('Content-Type', 'application/json');
$response = $http->post(
$this->baseUrl . '/api/oauth/v1/token',
json_encode([
'grant_type' => 'password',
'username' => $username,
'password' => $password,
])
);
$data = json_decode($response, true);
return $data['access_token'] ?? throw new \RuntimeException('Akeneo auth failed');
}
public function getProducts(int $page = 1, int $limit = 100): array
{
$http = new \Bitrix\Main\Web\HttpClient();
$http->setHeader('Authorization', 'Bearer ' . $this->token);
$response = $http->get(
$this->baseUrl . '/api/rest/v1/products'
. '?page=' . $page . '&limit=' . $limit
. '&with_attribute_options=true'
);
return json_decode($response, true)['_embedded']['items'] ?? [];
}
public function getMediaFile(string $code): string
{
$http = new \Bitrix\Main\Web\HttpClient();
$http->setHeader('Authorization', 'Bearer ' . $this->token);
return $http->get($this->baseUrl . '/api/rest/v1/media-files/' . $code . '/download');
}
}
Маппинг атрибутов Akeneo → Битрикс
Каждый атрибут Akeneo нужно сопоставить со свойством инфоблока Битрикс. Маппинг хранится в конфигурационном файле или HL-блоке:
// /local/config/akeneo-mapping.php
return [
// 'код_атрибута_akeneo' => 'КОД_СВОЙСТВА_БИТРИКС'
'description' => 'DETAIL_TEXT', // Специальное поле
'short_description' => 'PREVIEW_TEXT',
'brand' => 'BRAND',
'weight' => 'WEIGHT',
'color' => 'COLOR',
'material' => 'MATERIAL',
'care_instructions' => 'CARE',
'country_of_origin' => 'COUNTRY_ORIGIN',
];
Атрибуты Akeneo могут быть локализованными (разные значения для разных локалей). Маппинг локалей:
$localeMap = [
'ru_RU' => 'ru',
'en_US' => 'en',
'de_DE' => 'de',
];
Синхронизация: логика агента
Агент Битрикс запускает синхронизацию по расписанию. Для 50 000+ товаров — инкрементальный режим (только изменённые с момента последней синхронизации):
function syncAkeneoProductsAgent(): string
{
$lastSync = \Bitrix\Main\Config\Option::get('akeneo_sync', 'last_sync', '');
$client = new AkeneoClient(
AKENEO_URL, AKENEO_CLIENT_ID, AKENEO_SECRET,
AKENEO_USER, AKENEO_PASSWORD
);
$mapping = include '/local/config/akeneo-mapping.php';
$syncTime = date('c');
$page = 1;
do {
$products = $client->getProducts($page, 100);
foreach ($products as $akeneoProduct) {
syncSingleProduct($akeneoProduct, $mapping, $client);
}
$page++;
} while (count($products) === 100);
\Bitrix\Main\Config\Option::set('akeneo_sync', 'last_sync', $syncTime);
return __FUNCTION__ . '();';
}
function syncSingleProduct(array $product, array $mapping, AkeneoClient $client): void
{
$sku = $product['identifier']; // Код товара в Akeneo = артикул
$enabled = $product['enabled'];
// Ищем товар по артикулу
$existing = CIBlockElement::GetList(
[],
['IBLOCK_ID' => CATALOG_IBLOCK_ID, 'PROPERTY_CML2_ARTICLE' => $sku]
)->Fetch();
$el = new CIBlockElement();
$fields = [
'IBLOCK_ID' => CATALOG_IBLOCK_ID,
'ACTIVE' => $enabled ? 'Y' : 'N',
'NAME' => getAkeneoValue($product['values']['name'] ?? [], 'ru_RU'),
];
// Обрабатываем свойства через маппинг
$properties = [];
foreach ($mapping as $akeneoCode => $bitrixCode) {
$value = getAkeneoValue($product['values'][$akeneoCode] ?? [], 'ru_RU');
if ($value !== null) {
if (in_array($bitrixCode, ['DETAIL_TEXT', 'PREVIEW_TEXT', 'NAME'])) {
$fields[$bitrixCode] = $value;
} else {
$properties[$bitrixCode] = $value;
}
}
}
if ($existing) {
$el->Update($existing['ID'], $fields);
CIBlockElement::SetPropertyValuesEx($existing['ID'], CATALOG_IBLOCK_ID, $properties);
} else {
$fields['IBLOCK_SECTION_ID'] = resolveCategoryId($product['categories'][0] ?? null);
$newId = $el->Add($fields);
if ($newId) {
CIBlockElement::SetPropertyValuesEx($newId, CATALOG_IBLOCK_ID, $properties);
}
}
}
function getAkeneoValue(array $values, string $locale): mixed
{
foreach ($values as $entry) {
if (($entry['locale'] === $locale || $entry['locale'] === null)
&& $entry['scope'] === null) {
return $entry['data'];
}
}
return null;
}
Синхронизация медиафайлов
Изображения в Akeneo хранятся как медиафайлы. Атрибут типа pim_catalog_image возвращает код файла (_media_links). Скачиваем и сохраняем в Битрикс:
function syncProductImage(int $productId, string $mediaCode, AkeneoClient $client): void
{
$cacheKey = 'akeneo_media_' . md5($mediaCode);
$cache = \Bitrix\Main\Data\Cache::createInstance();
if ($cache->initCache(86400 * 7, $cacheKey, '/akeneo/media')) {
return; // уже загружено
}
$imageContent = $client->getMediaFile($mediaCode);
$ext = pathinfo($mediaCode, PATHINFO_EXTENSION) ?: 'jpg';
$tmpFile = sys_get_temp_dir() . '/' . $productId . '_' . uniqid() . '.' . $ext;
file_put_contents($tmpFile, $imageContent);
$fileId = \CFile::MakeFileArray($tmpFile);
\CIBlockElement::SetPropertyValues($productId, CATALOG_IBLOCK_ID, $fileId, 'MORE_PHOTO');
unlink($tmpFile);
$cache->startDataCache(86400 * 7, $cacheKey, '/akeneo/media');
$cache->endDataCache([]);
}
Синхронизация категорий
Иерархия категорий Akeneo синхронизируется в разделы инфоблока:
GET /api/rest/v1/categories?parent=master
Ответ содержит code, parent, labels. Рекурсивно создаём разделы инфоблока через CIBlockSection::Add(), сохраняя соответствие akeneo_code ↔ IBLOCK_SECTION_ID в HL-блоке AkeneoMapping.
Сроки реализации
| Объём | Состав | Срок |
|---|---|---|
| 1 000–5 000 SKU, базовые атрибуты | Клиент + маппинг + агент | 1–2 недели |
| 10 000–50 000 SKU + медиафайлы + i18n | Инкрементальный sync + очередь обработки | 3–4 недели |
| Двусторонняя синхронизация + product models (конфигурации) | Полный двунаправленный обмен + Webhook | 5–7 недель |







