Работа большого количества кассиров в терминальном режиме (через подключения к удаленному рабочему столу RDP) на единой базе данных 1С часто сопровождается серьезными техническими сбоями. Один из самых распространенных и критичных — возникновение взаимных блокировок (deadlocks) СУБД при одновременной печати фискальных чеков. В этой статье мы подробно разберем, почему проведение документов (например, Приходных кассовых ордеров или Чек ККМ) зависает, почему чеки пробиваются на ККТ, но не проводятся в программе, и как правильно перестроить архитектуру системы, чтобы полностью избавиться от этой проблемы.
Проанализируем классическую ошибку проектирования, с которой сталкиваются многие разработчики. В типовых или доработанных конфигурациях код печати чека часто вызывают непосредственно из модуля объекта документа, внутри обработчика ОбработкаПроведения или связанных с ним транзакционных процедур. Рассмотрим цепочку событий, которая к этому приводит:
Главное золотое правило разработки в 1С: никогда не выполняйте внешние, медленные операции (интеграция с ККТ, веб-сервисами, отправка SMS) внутри транзакции проведения документа! Транзакция должна выполняться мгновенно — за доли секунды.
Разберем по шагам правильный алгоритм работы на примере обычных форм (для управляемых форм принцип аналогичен):
ОбменДанными.Загрузка = Истина, чтобы избежать лишних блокировок и обхода проверок при записи.Рассмотрим пример кода, реализующий этот безопасный паттерн:
// Пример кода на клиенте для обычных форм
Процедура КомандаПробитьЧек(Кнопка)
// Шаг 1. Пытаемся провести документ в транзакции базы данных (быстро)
Если Не ЭтотОбъект.Записать(РежимЗаписиДокумента.Проведение) Тогда
Предупреждение("Не удалось провести документ! Пробитие чека отменено.");
Возврат;
КонецЕсли;
// Шаг 2. Транзакция закрыта. Теперь выполняем печать чека на ККТ
ФискальныеРеквизиты = НапечататьЧекНаККТ(ЭтотОбъект.Ссылка);
Если ФискальныеРеквизиты <> Неопределено Тогда
// Шаг 3. Записываем фискальные признаки обратно в документ без перепроведения
ДокОбъект = ЭтотОбъект.Ссылка.ПолучитьОбъект();
ДокОбъект.ОбменДанными.Загрузка = Истина;
ДокОбъект.НомерЧекаККМ = ФискальныеРеквизиты.НомерЧека;
ДокОбъект.НомерСменыККМ = ФискальныеРеквизиты.НомерСмены;
ДокОбъект.ЧекПробит = Истина;
ДокОбъект.Записать(РежимЗаписиДокумента.Запись);
Оповестить("ОбновилсяДокументПКО", ЭтотОбъект.Ссылка);
Сообщить("Чек успешно пробит на ККТ!");
Иначе
ВызватьИсключение "Ошибка печати чека. Обратитесь к администратору!";
КонецЕсли;
КонецПроцедуры
Если у вас высокая интенсивность продаж (чеки бьются каждые несколько секунд), даже клиентское разделение операций может вызывать накладки, если одна касса физически не справляется с потоком команд. В современных конфигурациях 1С (таких как «1С:Розница» или «1С:УНФ») для этого используется паттерн асинхронной печати через очередь.
Как спроектировать данный механизм самостоятельно:
ОчередьЧековККТ с измерениями ДокументСсылка и ресурсами для хранения состояния фискализации (например, статус «В очереди», «Ошибка», «Пробит»).ОчередьЧековККТ, отправлять команды на ККТ по очереди и фиксировать результат.Зачастую проблема взаимных блокировок и зависаний ККТ усугубляется нестабильным каналом связи. При работе через удаленный рабочий стол (RDP) стандартный проброс COM-портов создает огромные задержки при передаче сигналов драйвера. Касса "зависает", увеличивая время транзакции в 1С.
Рекомендуем современное решение для драйверов АТОЛ 10-й версии:
Частой ошибкой начинающих программистов является захват устройства при открытии формы документа или РМК и удержание порта до закрытия формы. В результате другие пользователи вообще не могут воспользоваться этой кассой.
Проанализируйте ваш код интеграции с торговым оборудованием. Подключение должно выполняться по принципу «точно в срок»:
// Безопасный алгоритм отправки команды на ККТ
РезультатПодключения = ПолучитьСерверТО().ПодключитьОборудование(Клиент, УстройствоККТ);
Если РезультатПодключения Тогда
// Выполняем фискализацию
РезультатПечати = ПолучитьСерверТО().ПечатьЧека(УстройствоККТ, ПараметрыЧека);
// КРИТИЧЕСКИ ВАЖНО: сразу отключаем оборудование, чтобы освободить порт для других пользователей
ПолучитьСерверТО().ОтключитьОборудование(Клиент, УстройствоККТ);
КонецЕсли;
Применяя данные архитектурные и технические решения, вы полностью исключите проблему взаимных блокировок СУБД, оптимизируете нагрузку на терминальный сервер и обеспечите бесперебойную работу кассиров даже в пиковые часы продаж.