Development of Data Import Module for 1C-Bitrix
Importing data to 1C-Bitrix is not just "uploading CSV". It's transforming data from a foreign format into Bitrix structures with consideration for infoblock, merchandise catalog, CRM, users. The built-in import exists only for the catalog via CommerceML and YML; for everything else, a separate module is written.
What the Standard Provides and Where It Stops
The standard catalog module supports import via CSaleImport and the CDataExchangeXML class for CommerceML 2.x. Works acceptably for simple catalogs. Problems start with:
- arbitrary formats (Excel, JSON, non-standard XML schemas)
- importing to multiple infoblocks simultaneously
- updating existing records with field merge logic
- importing users, orders, CRM entities
- large files (10,000+ rows) without timeouts
Module Architecture
The module vendor.importer is built around three abstractions: Reader (reads source), Transformer (transforms data), Writer (writes to Bitrix).
SourceFile → Reader → RawRow[] → Transformer → MappedRow[] → Writer → Bitrix entities
ReaderInterface:
interface ReaderInterface
{
public function open(string $filePath): void;
public function readNext(): ?array; // null = end of file
public function getHeaders(): array;
public function close(): void;
}
Implementations: CsvReader, XlsxReader (via PhpSpreadsheet), XmlReader (via XMLReader for streaming large files), JsonReader.
Field Mapping
The mapping configuration is stored in b_vendor_importer_mapping:
{
"source": "csv",
"target": "catalog_product",
"iblock_id": 5,
"fields": {
"article": "PROPERTY_ARTICLE",
"name": "NAME",
"price": "CATALOG_PRICE_1",
"stock": "CATALOG_STORE_PRODUCT_AMOUNT"
},
"key_field": "article",
"update_strategy": "merge"
}
key_field determines the field for identifying an existing record (article, external ID, email). update_strategy:
-
merge— updates only fields from mapping, doesn't touch others -
replace— full record replacement -
skip— skip existing, only new
Streaming Large Files
You can't process 50,000 rows in a single HTTP request. We use a session approach with an agent:
// Step 1: upload file, create job
$job = ImportJobTable::add([
'FILE_PATH' => $uploadedPath,
'MAPPING_ID' => $mappingId,
'STATUS' => 'pending',
'TOTAL' => 0,
'PROCESSED' => 0,
]);
// Step 2: agent reads in batches
public static function processChunk(int $jobId, int $offset, int $limit = 100): string
{
$job = ImportJobTable::getById($jobId)->fetch();
$reader = ReaderFactory::create($job['FILE_PATH']);
$reader->open($job['FILE_PATH']);
// skip offset rows, read limit rows
// ...process, update PROCESSED
}
The agent is called every minute, processes the next batch, updates the counter. Progress is visible in the admin interface in real time.
Writer: Writing to Iblock
class IblockElementWriter implements WriterInterface
{
public function write(MappedRow $row): WriteResult
{
$existing = $this->findByKey($row->getKeyValue());
if ($existing) {
$result = \CIBlockElement::Update($existing['ID'], $row->toIblockArray());
} else {
$element = new \CIBlockElement();
$result = $element->Add($row->toIblockArray());
}
return new WriteResult($result !== false, $existing ? 'updated' : 'created');
}
}
Similar Writer classes: CrmLeadWriter (via \Bitrix\Crm\LeadTable), SaleOrderWriter (via \Bitrix\Sale\Order::create()), UserWriter (via \CUser::Add/Update).
Data Validation
Before writing, each row passes validation. Rules are described declaratively:
$rules = [
'EMAIL' => ['required', 'email'],
'PRICE' => ['required', 'numeric', 'min:0'],
'NAME' => ['required', 'max:255'],
'STATUS' => ['in:active,inactive,pending'],
];
Rows with errors are not written, they are logged in b_vendor_importer_error with row number, field, and reason. After import, an error report is available with the option to download CSV of "problematic rows".
Notifications and Logging
Upon completion, the module sends an email notification to the initiator via \Bitrix\Main\Mail\Event::send(). The notification includes: total rows, created/updated/errors, link to the log.
The log is stored in b_vendor_importer_log: job_id, line_number, action (created/updated/skipped/error), entity_id, message, created_at.
Development Timeline
| Stage | Duration |
|---|---|
| Architecture, interfaces, installer | 1 day |
| Readers: CSV, XLSX, XML, JSON | 2 days |
| Mapping, transformer, configurator | 2 days |
| Writers for specific Bitrix entities | 3 days |
| Streaming processing, agents | 2 days |
| Validation, logging, error report | 1 day |
| Admin interface, testing | 2 days |
Total: 13 working days. Non-standard source formats or complex transformation logic (deduplication, enrichment from external APIs) are estimated separately.







