Setting Up Product Marking on 1C-Bitrix
From 2020–2023, most product categories fall under mandatory "Honest Sign" marking: clothing, footwear, medicines, dairy products, tobacco, tires, perfumes, cameras. When selling through an online store, marking codes must be transmitted to the cash receipt, and when receiving and shipping — to the MDLP monitoring system or GIS MT.
Marking Architecture in Bitrix
Bitrix does not have a built-in module for working with "Honest Sign". Integration is built from several components:
- Storing marking codes — binding DataMatrix codes to specific product units
- Transmitting code to cash receipt — via cash register module (ATOL, YooKassa, etc.)
- Reporting to GIS MT — when sold, the code is removed from circulation
Marking code (КМ) is a string like 010460437166456221hHdB3ePhGH1I\u001d91EE06\u001d92XqnSm5YoGVLWJjOz...
Storing Marking Codes
Codes are bound to SKU (trade offers) of the catalog information block. Storage options:
Option 1 — Information Block Property. Add property MARKING_CODE type "String" or "HTML/Text" to the information block of trade offers:
// Create property programmatically
CIBlockProperty::Add([
'NAME' => 'Marking Code',
'CODE' => 'MARKING_CODE',
'IBLOCK_ID' => OFFERS_IBLOCK_ID,
'PROPERTY_TYPE' => 'S',
'MULTIPLE' => 'N',
'ACTIVE' => 'Y'
]);
Option 2 — Separate Table for projects with high turnover (10,000+ codes):
CREATE TABLE b_marking_codes (
ID INT AUTO_INCREMENT PRIMARY KEY,
PRODUCT_ID INT NOT NULL,
CODE VARCHAR(255) NOT NULL UNIQUE,
STATUS ENUM('available', 'reserved', 'sold', 'returned') DEFAULT 'available',
ORDER_ID INT NULL,
DATE_SOLD DATETIME NULL,
INDEX idx_product_status (PRODUCT_ID, STATUS),
INDEX idx_code (CODE)
);
Binding Marking Code to Basket Item
When adding a product to the basket, a specific code must be reserved:
// Handler OnSaleBasketItemEntitySaved or custom logic
AddEventHandler('sale', 'OnSaleBasketBeforeSaved', function(\Bitrix\Main\Event $event) {
$basketItem = $event->getParameter('ENTITY');
$productId = $basketItem->getProductId();
// Find free marking code
$res = \Bitrix\Main\Application::getConnection()->query(
"SELECT ID, CODE FROM b_marking_codes
WHERE PRODUCT_ID = {$productId} AND STATUS = 'available'
LIMIT 1 FOR UPDATE"
);
$codeRow = $res->fetch();
if ($codeRow) {
// Reserve code
\Bitrix\Main\Application::getConnection()->query(
"UPDATE b_marking_codes SET STATUS = 'reserved', ORDER_ID = NULL
WHERE ID = {$codeRow['ID']}"
);
// Save code in basket item property
$basketItem->setField('PROPS', [
'MARKING_CODE' => $codeRow['CODE']
]);
}
});
Transmitting Code to Cash Receipt
A cash receipt under Federal Law 54-FZ with marked goods must contain the marking code in tag 1163. ATOL Online and other operators accept it in the marking_code field.
Modification of cash register module handler (class extension):
// Intercept receipt position formation
AddEventHandler('sale', 'OnCashboxBuildCheck', function($checkData) {
foreach ($checkData['ITEMS'] as &$item) {
$basketItemId = $item['BASKET_ID'] ?? null;
if ($basketItemId) {
// Get marking code from item properties
$res = \Bitrix\Sale\Internals\BasketPropertiesTable::getList([
'filter' => ['BASKET_ID' => $basketItemId, 'CODE' => 'MARKING_CODE'],
'select' => ['VALUE']
]);
if ($prop = $res->fetch()) {
$item['MARKING_CODE'] = $prop['VALUE'];
}
}
}
return $checkData;
});
Code Removal After Sale
After receipt fiscalization, the marking code must transition to "Sold" status and be removed from circulation in GIS MT. This is either automatically via cash register (if OFD is integrated with "Honest Sign"), or manually via GIS MT API.
Updating code status after payment:
AddEventHandler('sale', 'OnSalePaymentEntitySaved', function(\Bitrix\Main\Event $event) {
$payment = $event->getParameter('ENTITY');
if ($payment->getField('PAID') === 'Y') {
$order = $payment->getOrder();
$basket = $order->getBasket();
foreach ($basket as $basketItem) {
$props = $basketItem->getPropertyCollection();
// Find marking code in item properties
foreach ($props as $prop) {
if ($prop->getField('CODE') === 'MARKING_CODE') {
$code = $prop->getField('VALUE');
\Bitrix\Main\Application::getConnection()->query(
"UPDATE b_marking_codes SET STATUS = 'sold',
ORDER_ID = {$order->getId()},
DATE_SOLD = NOW()
WHERE CODE = '" . \Bitrix\Main\Application::getConnection()->getSqlHelper()->forSql($code) . "'"
);
}
}
}
}
});
Importing Marking Codes
Codes are received from the supplier as a file (CSV, Excel) or via EDI. For bulk import:
// Import script
$codes = file('/path/to/marking_codes.txt', FILE_IGNORE_NEW_LINES);
$db = \Bitrix\Main\Application::getConnection();
$productId = 1234; // SKU ID
foreach (array_chunk($codes, 1000) as $batch) {
$values = implode(',', array_map(function($code) use ($productId, $db) {
return "({$productId}, '" . $db->getSqlHelper()->forSql(trim($code)) . "', 'available')";
}, $batch));
$db->query("INSERT IGNORE INTO b_marking_codes (PRODUCT_ID, CODE, STATUS) VALUES {$values}");
}

