Разработка кастомных импорт/экспорт модулей 1С-Битрикс
Стандартный обмен данными Битрикс — CommerceML для 1С, CSV-импорт в инфоблоки — покрывает типовые задачи. Когда источник данных нестандартен, структура данных сложная или нужна двусторонняя синхронизация в реальном времени — пишем собственный модуль.
Архитектура кастомного модуля
Модуль Битрикс — это директория в /local/modules/{vendor}.{modulename}/, зарегистрированная через RegisterModule. Структура:
local/modules/company.import/
├── install/
│ ├── index.php # InstallDB(), UnInstallDB(), DoInstall()
│ └── db/mysql/install.sql
├── lib/
│ ├── Importer.php # основная логика
│ ├── Parser/
│ │ ├── XmlParser.php
│ │ └── CsvParser.php
│ └── Queue/
│ └── ImportQueue.php
├── admin/
│ └── import_settings.php # административный интерфейс
├── include.php
└── .settings.php
Импорт из произвольных источников
Импорт из XML (кастомная схема, не CommerceML):
namespace Company\Import;
use Bitrix\Main\Loader;
use Bitrix\Catalog\ProductTable;
class Importer {
private \SimpleXMLElement $xml;
public function __construct(string $filePath) {
Loader::includeModule('iblock');
Loader::includeModule('catalog');
$this->xml = simplexml_load_file($filePath);
}
public function run(): array {
$stats = ['created' => 0, 'updated' => 0, 'errors' => 0];
foreach ($this->xml->products->product as $product) {
try {
$this->processProduct($product, $stats);
} catch (\Throwable $e) {
\Bitrix\Main\Diag\Debug::writeToFile($e->getMessage(), 'IMPORT ERROR', '/bitrix/modules/company.import/error.log');
$stats['errors']++;
}
}
return $stats;
}
private function processProduct(\SimpleXMLElement $p, array &$stats): void {
$externalId = (string)$p->id;
$existing = $this->findByExternalId($externalId);
$fields = [
'IBLOCK_ID' => IMPORT_IBLOCK_ID,
'NAME' => (string)$p->name,
'CODE' => \CUtil::translit((string)$p->name, 'ru'),
'ACTIVE' => (string)$p->is_active === '1' ? 'Y' : 'N',
'PROPERTY_VALUES' => [
'EXTERNAL_ID' => $externalId,
'VENDOR_CODE' => (string)$p->sku,
'DESCRIPTION' => (string)$p->description,
],
];
if ($existing) {
\CIBlockElement::Update($existing, $fields);
$stats['updated']++;
} else {
$el = new \CIBlockElement();
$newId = $el->Add($fields);
if (!$newId) throw new \RuntimeException($el->LAST_ERROR);
$stats['created']++;
}
// Обновляем цену и остаток
\CPrice::SetBasePrice($newId ?? $existing, (float)$p->price, 'RUB');
\CCatalogProduct::Update($newId ?? $existing, ['QUANTITY' => (int)$p->stock]);
}
}
Пакетная обработка и прогресс
При большом объёме данных (100 000+ записей) обработка через веб-запрос невозможна — timeout. Используем агент Битрикс с сохранением прогресса:
// В таблице хранится: файл, текущая позиция, статистика
class ImportQueue {
public static function processChunk(int $jobId, int $offset, int $limit = 500): array {
$job = ImportJobTable::getById($jobId)->fetch();
// ... читаем $limit строк начиная с $offset
// ... обрабатываем
// ... обновляем прогресс в БД
return ['processed' => $count, 'total' => $job['total_rows']];
}
}
// Агент вызывается каждую минуту, обрабатывает следующий чанк
function ImportAgent(): string {
$activeJob = getActiveImportJob();
if (!$activeJob) return '';
$result = ImportQueue::processChunk($activeJob['id'], $activeJob['offset']);
if ($activeJob['offset'] + $result['processed'] >= $result['total']) {
markJobComplete($activeJob['id']);
return ''; // агент завершён
}
return 'ImportAgent();'; // агент продолжает
}
Экспорт данных
Экспорт в произвольный формат для внешней системы:
class Exporter {
public function exportOrders(\DateTime $from, \DateTime $to): string {
$orders = \Bitrix\Sale\OrderTable::getList([
'filter' => [
'>=DATE_INSERT' => $from->format('d.m.Y H:i:s'),
'<=DATE_INSERT' => $to->format('d.m.Y H:i:s'),
'CANCELED' => 'N',
],
'select' => ['ID', 'ACCOUNT_NUMBER', 'PRICE', 'CURRENCY', 'DATE_INSERT', 'USER_ID'],
])->fetchAll();
$xml = new \XMLWriter();
$xml->openMemory();
$xml->startDocument('1.0', 'UTF-8');
$xml->startElement('orders');
foreach ($orders as $order) {
$xml->startElement('order');
$xml->writeElement('id', $order['ID']);
$xml->writeElement('number', $order['ACCOUNT_NUMBER']);
$xml->writeElement('amount', $order['PRICE']);
$xml->writeElement('date', $order['DATE_INSERT']->format(\DateTime::ATOM));
// ... позиции заказа
$xml->endElement();
}
$xml->endElement();
return $xml->outputMemory();
}
}
Двусторонняя синхронизация
Самое сложное — синхронизация без потерь при одновременных изменениях в обеих системах.
Решение: timestamp-based sync с полем SYNC_HASH:
ALTER TABLE b_iblock_element ADD COLUMN sync_hash VARCHAR(32);
ALTER TABLE b_iblock_element ADD COLUMN synced_at DATETIME;
При экспорте записываем хеш состояния. При следующем импорте: если хеш изменился в источнике — обновляем в Битрикс. Если хеш изменился в Битрикс (пользователь отредактировал) — отправляем изменения обратно в источник. Если оба изменились — конфликт, логируем для ручного разбора.
Административный интерфейс модуля
// admin/import_settings.php
require_once $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_admin_before.php';
$APPLICATION->SetTitle('Настройки импорта');
// Форма настроек: путь к FTP, расписание агента, маппинг полей
// Лог последних запусков с результатами (создано/обновлено/ошибки)
// Кнопка "Запустить сейчас"
Форматы и источники
| Формат/Источник | Инструменты | Особенности |
|---|---|---|
| XML (кастомный) | SimpleXML, XMLReader | XMLReader для файлов > 100 МБ |
| CSV/XLSX | PhpSpreadsheet, fgetcsv | XLSX — бинарный, требует библиотеку |
| JSON REST API | curl, Guzzle | Пагинация, rate limiting |
| FTP/SFTP | phpseclib | Автоматическая загрузка файлов |
| 1С CommerceML | Встроенный обмен Битрикс | Кастомизация через события |
| Google Sheets | Google Sheets API v4 | Для небольших объёмов |
Сроки
| Этап | Срок |
|---|---|
| Анализ форматов и маппинг полей | 1–2 дня |
| Разработка парсера/экспортера | 3–5 дней |
| Пакетная обработка, агент Битрикс | 2–3 дня |
| Административный интерфейс | 2–3 дня |
| Двусторонняя синхронизация (если нужна) | 3–5 дней |
| Тестирование на реальных данных | 2–3 дня |
Итого: 2–3 недели для одностороннего импорта; 3–4 недели для двусторонней синхронизации.







