Інтеграція 1С-Бітрікс з PIM-системою Akeneo

Наша компанія займається розробкою, підтримкою та обслуговуванням рішень на Бітрікс та Бітрікс24 будь-якої складності. Від простих односторінкових сайтів до складних інтернет-магазинів, CRM систем з інтеграцією 1С та телефонії. Досвід розробників підтверджено сертифікатами від вендора.
Пропоновані послуги
Показано 1 з 1 послугУсі 1626 послуг
Інтеграція 1С-Бітрікс з PIM-системою Akeneo
Середня
~1-2 тижні
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Розробка на базі Бітрікс, Бітрікс24, 1С для компанії Development of an Online
    585
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Розробка на базі 1С Підприємство для компанії МИРСАНБЕЛ
    751
  • image_crm_dolbimby_434_0.webp
    Розробка сайту на CRM Бітрікс24 для компанії DOLBIMBY
    657
  • image_crm_technotorgcomplex_453_0.webp
    Розробка на базі Бітрікс24 для компанії ТЕХНОТОРГКОМПЛЕКС
    989

Інтеграція 1С-Бітрікс із PIM-системою Akeneo

Akeneo — спеціалізована система управління інформацією про продукти (PIM). Коли в каталозі тисячі товарів з десятками атрибутів, перекладами кількома мовами та варіантами для різних каналів, зберігання всього цього безпосередньо в інфоблоці Бітрікс стає незручним. Akeneo займається збагаченням і валідацією даних, Бітрікс — продажами та трансакціями. Інтеграція будується на REST API Akeneo.

Архітектура потоку даних

Типовий потік:

1С (номенклатура, ціни, залишки)
            ↓
         Akeneo
   (описи, атрибути, медіа, переклади)
            ↓
         Бітрікс
    (каталог, кошик, замовлення)

Akeneo є "господарем" контенту продуктів. 1С — джерелом для SKU, цін та залишків. Бітрікс — каналом продажу, який читає дані з Akeneo.

Напрямок синхронізації: Akeneo → Бітрікс. Зворотний потік (Бітрікс → Akeneo) використовується рідко — лише для передачі статистики (перегляди, замовлення) якщо необхідно.

REST API Akeneo

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_attribute_code' => 'BITRIX_PROPERTY_CODE'
    '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_codeIBLOCK_SECTION_ID в HL-блоці AkeneoMapping.

Тривалість реалізації

Обсяг Склад Тривалість
1,000–5,000 SKU, базові атрибути Клієнт + маппінг + агент 1–2 тижня
10,000–50,000 SKU + медіа + i18n Інкрементальна синхронізація + очередь обробки 3–4 тижня
Двостороння синхронізація + product models Повний двонаправлений обмін + Webhook 5–7 тижнів