Как устранить взаимные блокировки в 1С при одновременной печати чеков на онлайн-кассах?

Программист 1С v8.3 (Обычные формы) Управленческий учет Торговля и дистрибуция
← На главную

Работа большого количества кассиров в терминальном режиме (через подключения к удаленному рабочему столу RDP) на единой базе данных 1С часто сопровождается серьезными техническими сбоями. Один из самых распространенных и критичных — возникновение взаимных блокировок (deadlocks) СУБД при одновременной печати фискальных чеков. В этой статье мы подробно разберем, почему проведение документов (например, Приходных кассовых ордеров или Чек ККМ) зависает, почему чеки пробиваются на ККТ, но не проводятся в программе, и как правильно перестроить архитектуру системы, чтобы полностью избавиться от этой проблемы.

Выясняем причину: почему возникают длинные транзакции и блокировки?

Проанализируем классическую ошибку проектирования, с которой сталкиваются многие разработчики. В типовых или доработанных конфигурациях код печати чека часто вызывают непосредственно из модуля объекта документа, внутри обработчика ОбработкаПроведения или связанных с ним транзакционных процедур. Рассмотрим цепочку событий, которая к этому приводит:

  1. Кассир нажимает кнопку «Провести» в документе «Приходный кассовый ордер» (ПКО).
  2. Платформа 1С открывает транзакцию СУБД и накладывает эксклюзивные блокировки на таблицы регистров и самого документа, чтобы гарантировать согласованность данных.
  3. Внутри транзакции вызывается код драйвера ККТ для отправки команды печати чека.
  4. Физическая печать чека на ККМ — процесс крайне медленный по меркам информационных систем. Обмен данными с кассой, передача фискального признака и сама печать бумажной ленты могут занимать от 2 до 15 секунд (особенно при медленном пробросе COM-портов через RDP-сессию).
  5. В течение всех этих 15 секунд транзакция в СУБД остается открытой! Все таблицы, к которым обратился документ, заблокированы на уровне СУБД.
  6. Если в этот момент второй кассир пытается провести свой ПКО, его сеанс встает в очередь ожидания блокировки.
  7. По истечении времени ожидания (таймаута) у второго кассира возникает ошибка блокировки. А у первого кассира из-за конфликта транзакций операция может завершиться аварийно: чек на ККТ уже физически напечатан (так как драйвер ККТ выполнил команду), но транзакция 1С откатилась назад. В итоге документ в базе остался непроведенным, и признак пробития чека не зафиксировался.

Решение 1: Вынесение печати чека ККТ за рамки транзакции проведения

Главное золотое правило разработки в 1С: никогда не выполняйте внешние, медленные операции (интеграция с ККТ, веб-сервисами, отправка SMS) внутри транзакции проведения документа! Транзакция должна выполняться мгновенно — за доли секунды.

Разберем по шагам правильный алгоритм работы на примере обычных форм (для управляемых форм принцип аналогичен):

  1. Пользователь нажимает на кнопку пробития чека. На самом деле эта кнопка должна сначала выполнять запись и проведение документа на сервере. Это происходит быстро, так как ККТ еще не задействована.
  2. После успешного проведения документа (транзакция СУБД уже закрыта и все блокировки с таблиц базы данных сняты!) управление возвращается на клиента.
  3. Только теперь клиентский код вызывает драйвер торгового оборудования и отправляет чек на печать.
  4. После успешного пробития чека ККТ возвращает фискальные реквизиты (номер чека, номер смены, фискальный признак).
  5. Мы программно записываем эти реквизиты в документ без повторного проведения, используя свойство ОбменДанными.Загрузка = Истина, чтобы избежать лишних блокировок и обхода проверок при записи.

Рассмотрим пример кода, реализующий этот безопасный паттерн:


// Пример кода на клиенте для обычных форм
Процедура КомандаПробитьЧек(Кнопка)

    // Шаг 1. Пытаемся провести документ в транзакции базы данных (быстро)
    Если Не ЭтотОбъект.Записать(РежимЗаписиДокумента.Проведение) Тогда
        Предупреждение("Не удалось провести документ! Пробитие чека отменено.");
        Возврат;
    КонецЕсли;

    // Шаг 2. Транзакция закрыта. Теперь выполняем печать чека на ККТ
    ФискальныеРеквизиты = НапечататьЧекНаККТ(ЭтотОбъект.Ссылка);

    Если ФискальныеРеквизиты <> Неопределено Тогда
        // Шаг 3. Записываем фискальные признаки обратно в документ без перепроведения
        ДокОбъект = ЭтотОбъект.Ссылка.ПолучитьОбъект();
        ДокОбъект.ОбменДанными.Загрузка = Истина;
        ДокОбъект.НомерЧекаККМ = ФискальныеРеквизиты.НомерЧека;
        ДокОбъект.НомерСменыККМ = ФискальныеРеквизиты.НомерСмены;
        ДокОбъект.ЧекПробит = Истина;
        ДокОбъект.Записать(РежимЗаписиДокумента.Запись);
        
        Оповестить("ОбновилсяДокументПКО", ЭтотОбъект.Ссылка);
        Сообщить("Чек успешно пробит на ККТ!");
    Иначе
        ВызватьИсключение "Ошибка печати чека. Обратитесь к администратору!";
    КонецЕсли;

КонецПроцедуры

Решение 2: Реализация механизма «Очередь чеков»

Если у вас высокая интенсивность продаж (чеки бьются каждые несколько секунд), даже клиентское разделение операций может вызывать накладки, если одна касса физически не справляется с потоком команд. В современных конфигурациях 1С (таких как «1С:Розница» или «1С:УНФ») для этого используется паттерн асинхронной печати через очередь.

Как спроектировать данный механизм самостоятельно:

Инфраструктурное решение: Отказ от проброса портов RDP и переход на Web-сервер АТОЛ

Зачастую проблема взаимных блокировок и зависаний ККТ усугубляется нестабильным каналом связи. При работе через удаленный рабочий стол (RDP) стандартный проброс COM-портов создает огромные задержки при передаче сигналов драйвера. Касса "зависает", увеличивая время транзакции в 1С.

Рекомендуем современное решение для драйверов АТОЛ 10-й версии:

  1. Подключите онлайн-кассу ККТ по сетевому интерфейсу (Ethernet или Wi-Fi) напрямую в локальную сеть предприятия вместо подключения по USB к локальному компьютеру.
  2. Настройте встроенный веб-сервер ДТО 10 (Драйвер торгового оборудования). Веб-сервер АТОЛ принимает JSON-задания по протоколу HTTP.
  3. В настройках подключения оборудования в 1С укажите сетевой адрес веб-сервера ККТ вместо локального COM-порта. Теперь запросы на печать будут отправляться мгновенно по сети, минуя медленные механизмы проброса портов RDP.
  4. Это также решает проблему монопольного захвата портов: веб-сервер умеет корректно ставить входящие сетевые запросы от разных терминальных сессий пользователей в собственную внутреннюю очередь ККТ.

Динамическое управление подключением ККТ в коде

Частой ошибкой начинающих программистов является захват устройства при открытии формы документа или РМК и удержание порта до закрытия формы. В результате другие пользователи вообще не могут воспользоваться этой кассой.

Проанализируйте ваш код интеграции с торговым оборудованием. Подключение должно выполняться по принципу «точно в срок»:


// Безопасный алгоритм отправки команды на ККТ
РезультатПодключения = ПолучитьСерверТО().ПодключитьОборудование(Клиент, УстройствоККТ);
Если РезультатПодключения Тогда
    // Выполняем фискализацию
    РезультатПечати = ПолучитьСерверТО().ПечатьЧека(УстройствоККТ, ПараметрыЧека);
    
    // КРИТИЧЕСКИ ВАЖНО: сразу отключаем оборудование, чтобы освободить порт для других пользователей
    ПолучитьСерверТО().ОтключитьОборудование(Клиент, УстройствоККТ);
КонецЕсли;

Применяя данные архитектурные и технические решения, вы полностью исключите проблему взаимных блокировок СУБД, оптимизируете нагрузку на терминальный сервер и обеспечите бесперебойную работу кассиров даже в пиковые часы продаж.

← На главную