1C-Bitrix Integration with Mercury (Merkury)
Mercury is the Russian federal state veterinary certification information system (FGIS). Businesses handling products of animal origin (meat, fish, dairy, eggs) are required to issue electronic veterinary accompanying documents (eVAD) for every product movement. An online store accepting orders for such goods faces a specific challenge: when goods arrive at the warehouse and when they are dispatched, incoming eVADs must be cancelled and outgoing ones issued through the Mercury system.
Interaction architecture
Mercury provides a REST API (https://api.vetrf.ru/schema/platform/v2/). Authentication uses an API key issued by Rosselkhoznadzor (the Russian agricultural regulator) after the business is registered in the system.
Supplier → ships goods with eVAD in Mercury
→ Store receives goods → cancels incoming eVAD (acceptVetDocument)
→ Warehouse storage: inventory unit with eVAD ID
→ Order placed → sale to an individual
→ Write-off via return transaction (transferReceiptTransaction)
When selling to an end consumer (individual), no new eVAD needs to be issued — it is sufficient to cancel the document for the sold quantity.
PHP client for Mercury API
class MerkuryClient
{
private string $apiUrl = 'https://api.vetrf.ru/schema/platform/v2';
private string $apiKey;
private string $issuerId; // enterprise ID in Mercury
public function __construct(string $apiKey, string $issuerId)
{
$this->apiKey = $apiKey;
$this->issuerId = $issuerId;
}
public function getIncomingDocuments(string $enterpriseGuid): array
{
return $this->request('GET',
"/enterprises/{$enterpriseGuid}/vetDocuments",
['status' => 'CONFIRMED', 'type' => 'INCOMING']
);
}
public function extinguishDocument(
string $enterpriseGuid,
string $vetDocId,
float $quantity,
string $unit
): string {
$response = $this->request('POST',
"/enterprises/{$enterpriseGuid}/vetDocuments/{$vetDocId}/extinguish",
[
'returnedQuantity' => $quantity,
'returnedUnit' => $unit,
'operatorId' => $this->issuerId,
]
);
return $response['application']['applicationId'];
}
public function processTransaction(
string $enterpriseGuid,
string $transactionId
): array {
return $this->request('POST',
"/enterprises/{$enterpriseGuid}/applications/{$transactionId}/process"
);
}
private function request(string $method, string $path, array $params = []): array
{
// ... HTTP request with header 'apiKey: {KEY}'
}
}
Storing eVADs in 1C-Bitrix
Each batch of goods in the warehouse corresponds to one or more eVADs. Table structure:
class VetDocumentTable extends \Bitrix\Main\ORM\Data\DataManager
{
public static function getTableName(): string { return 'local_merkury_vet_docs'; }
public static function getMap(): array
{
return [
new \Bitrix\Main\ORM\Fields\IntegerField('ID', ['primary' => true, 'autocomplete' => true]),
new \Bitrix\Main\ORM\Fields\IntegerField('PRODUCT_ID'), // catalog product
new \Bitrix\Main\ORM\Fields\StringField('VET_DOC_ID'), // document UUID in Mercury
new \Bitrix\Main\ORM\Fields\StringField('ENTERPRISE_GUID'), // enterprise GUID
new \Bitrix\Main\ORM\Fields\FloatField('QUANTITY'), // remaining stock
new \Bitrix\Main\ORM\Fields\StringField('UNIT'), // unit of measure: kg, piece
new \Bitrix\Main\ORM\Fields\StringField('STATUS'), // 'active', 'extinguished'
new \Bitrix\Main\ORM\Fields\DatetimeField('EXPIRE_DATE'), // expiry date
];
}
}
When a batch arrives, a record is created with the eVAD ID and quantity. On sale, the quantity decreases; when exhausted, the status changes to extinguished.
Cancelling eVADs on dispatch
public function handleOrderShipped(\Bitrix\Sale\Order $order): void
{
$basket = $order->getBasket();
foreach ($basket as $item) {
$productId = $item->getProductId();
$quantity = $item->getQuantity();
// Find the active eVAD for the product
$vetDoc = VetDocumentTable::getList([
'filter' => ['PRODUCT_ID' => $productId, 'STATUS' => 'active'],
'order' => ['EXPIRE_DATE' => 'ASC'], // nearest expiry first
'limit' => 1,
])->fetch();
if (!$vetDoc) {
\Bitrix\Main\Diag\Debug::writeToFile(
"No eVAD for product {$productId}",
'[MERKURY ERROR]', '/local/logs/merkury.log'
);
continue;
}
$appId = $this->merkury->extinguishDocument(
$vetDoc['ENTERPRISE_GUID'],
$vetDoc['VET_DOC_ID'],
$quantity,
$vetDoc['UNIT']
);
// Process the transaction
$this->merkury->processTransaction($vetDoc['ENTERPRISE_GUID'], $appId);
// Update the remaining quantity in the local table
$newQty = $vetDoc['QUANTITY'] - $quantity;
VetDocumentTable::update($vetDoc['ID'], [
'QUANTITY' => max(0, $newQty),
'STATUS' => $newQty <= 0 ? 'extinguished' : 'active',
]);
}
}
Case study: farm produce store
An online store selling meat and dairy products from farms. Approximately 250 orders per week. Goods arrive from 15 suppliers, each batch with a separate eVAD. The goal: automate receiving (cancelling incoming eVADs) and dispatch (write-off on sale).
Key details:
The Mercury API operates asynchronously: a cancellation request creates an "application" that must be processed with a separate call. Sometimes applications take several minutes to process — the Mercury system experiences significant load.
A two-step model was implemented: on dispatch, an application is created and queued; a cron job runs every 2 minutes and calls processTransaction for pending applications.
Goods receiving: the storekeeper enters the receipt ID from the supplier's delivery note in the 1C-Bitrix admin area → the system automatically retrieves the list of incoming eVADs from that supplier for the past 24 hours → the storekeeper confirms the batch correspondence → Mercury records the receipt.
| Metric | Before | After |
|---|---|---|
| Time to process a single batch receipt | 15 min (manual entry in Mercury) | 3 min (confirmation) |
| eVAD cancellation on sale | Not performed (violation) | Automatic |
| Rosselkhoznadzor inspections | Compliance notice issued | No findings |
Stock synchronisation
Periodic reconciliation: quantities in the local_merkury_vet_docs table are compared with enterprise stock in Mercury (GET /enterprises/{guid}/stockEntries). Discrepancies are logged and the administrator is notified.
Scope of work
- Mercury registration, obtaining API key (via Rosselkhoznadzor)
- PHP client development: incoming documents, cancellation, reconciliation
- eVAD storage table in 1C-Bitrix
- Receiving interface in the admin area
- Automatic cancellation on dispatch with retry queue
- Stock synchronisation (cron), discrepancy monitoring
Timeline: 6–10 weeks depending on the number of suppliers and warehouse management complexity.







