Developing an Order Import Module for 1C-Bitrix
On B2B projects, procurement managers work with price lists. They have an Excel file with SKUs and quantities and want to upload it to cart instead of clicking through the catalog. Standard Bitrix cannot do this — a separate module is needed.
Supported File Formats
Practice shows three main formats from clients:
-
XLSX / XLS — most common. Parsing via
PhpSpreadsheet(installed inlocal/vendor/via composer). - CSV — simple option, often used in automated exports from 1C.
- XML — rare but found in projects with EDI integration.
PDF upload not supported — reliable table data extraction not possible.
Module Structure
Module registered in local/modules/vendor.orderimport/. Main components:
-
lib/FileParser/XlsxParser.php— XLSX parser -
lib/FileParser/CsvParser.php— CSV parser -
lib/ProductMatcher.php— product search by SKU -
lib/CartFiller.php— cart filling -
components/vendor/orderimport.form/— upload form component
File Parsing
Uploaded file saved to temp directory, type determined by extension and MIME-type, then appropriate parser launched. Parser returns array of rows:
// Parse result: array of items
[
['sku' => 'ART-1234', 'qty' => 5, 'row' => 2],
['sku' => 'ART-5678', 'qty' => 10, 'row' => 3],
// ...
]
Parser skips header rows (first row or rows where SKU column contains text instead of number). Flexible configuration: column numbers for SKU and quantity set in module settings or determined automatically by headers.
Product Search by SKU
SKU in Bitrix stored in multiple places: XML_ID field of infoblock element, ARTICLE property (if used), ARTICLE field in b_catalog_product table. Search conducted sequentially:
// 1. Search by b_catalog_product.ARTICLE
$product = \Bitrix\Catalog\ProductTable::getList([
'filter' => ['=ARTICLE' => $sku],
'select' => ['ID', 'ARTICLE'],
])->fetch();
// 2. If not found — search by element XML_ID
if (!$product) {
$element = \CIBlockElement::GetList([], ['=XML_ID' => $sku, 'IBLOCK_ID' => $catalogIblockId], false, false, ['ID', 'XML_ID'])->Fetch();
}
Search result for each row — status: found, not_found, multiple (multiple with same SKU — ambiguous).
Preview Before Adding to Cart
User uploads file → sees table with parse result: for each row found product shown (name, photo, price, available stock), quantity from file and status. Rows with not_found status highlighted — user can manually specify product from catalog.
Only after user confirmation data sent to cart. This protects against accidental file errors.
Cart Filling
$basket = Sale\Basket::loadItemsForFUser(Sale\Fuser::getId(), SITE_ID);
foreach ($confirmedItems as $item) {
$basketItem = $basket->getExistsItem('catalog', $item['product_id']);
if ($basketItem) {
$basketItem->setField('QUANTITY', $basketItem->getQuantity() + $item['qty']);
} else {
$newItem = $basket->createItem('catalog', $item['product_id']);
$newItem->setFields([
'QUANTITY' => $item['qty'],
'PRODUCT_PROVIDER_CLASS' => \CCatalogProductProvider::class,
'LID' => SITE_ID,
'CURRENCY' => \Bitrix\Currency\CurrencyManager::getBaseCurrency(),
]);
}
}
$basket->save();
File Template for Download
Upload page shows link to download template — ready XLSX file with column headers ("SKU", "Quantity", "Note") and sample data. Template generated via PhpSpreadsheet on each request or provided as static file.
Upload Logging
All upload attempts recorded in b_orderimport_log table:
| Field | Type | Purpose |
|---|---|---|
| ID | int auto_increment | — |
| USER_ID | int | Who uploaded |
| FILE_NAME | varchar(255) | Original filename |
| ROWS_TOTAL | int | Rows in file |
| ROWS_FOUND | int | Products found |
| ROWS_ADDED | int | Added to cart |
| STATUS | enum | SUCCESS, PARTIAL, FAILED |
| CREATED_AT | datetime | — |
Development Timeline
| Scope | Components | Duration |
|---|---|---|
| Basic | XLSX/CSV parsing, SKU search, cart addition | 5–7 days |
| Standard | + Preview, manual matching, template | 9–12 days |
| Extended | + XML, logging, multiple price columns, stock check | 14–18 days |







