При работе в распределенной информационной базе (РИБ) на базе конфигурации «1С:Управление торговлей 10.3» часто возникает критическая ситуация: во время ежечасного обмена данными или при одновременной работе нескольких кассиров блокируются таблицы базы данных. В результате чек на фискальном регистраторе (ККТ) физически пробивается, но в самой программе 1С документ ЧекККМ не записывается или не проводится из-за ошибки ожидания блокировки. Кассирам приходится вручную сверять данные с личным кабинетом ОФД, что замедляет работу и приводит к кассовым разрывам.
В этой статье мы подробно разберем причины возникновения этой проблемы и рассмотрим несколько эффективных способов ее решения — от оптимизации кода до изменения архитектуры обмена данными.
Давайте проанализируем стандартный, но ошибочный алгоритм пробития чека, который часто используется в неоптимизированных конфигурациях:
НовыйЧек = Документы.ЧекККМ.СоздатьДокумент();
// Заполнение реквизитов чека...
НомерЧекаККТ = ПробитьЧекНаККМ(); // Физическая отправка команды на ККТ
НовыйЧек.НомерЧека = НомерЧекаККТ;
НовыйЧек.Записать(РежимЗаписиДокумента.Проведение);
При таком подходе возникает ключевая уязвимость: физическое пробитие чека на ККТ происходит до того, как документ гарантированно записан в базу данных 1С. Если в момент выполнения метода Записать(РежимЗаписиДокумента.Проведение) СУБД возвращает ошибку блокировки (из-за того, что параллельно идет сеанс обмена РИБ или другой кассир проводит документ), транзакция в 1С откатывается. Покупатель уходит с бумажным чеком, деньги по кассе проходят, а в 1С документа нет.
Кроме того, в конфигурации «Управление торговлей 10.3» по умолчанию используется автоматический режим блокировок. Это означает, что при проведении документа СУБД блокирует таблицы целиком, а не отдельные строки, что парализует работу всех кассиров при любой интенсивной операции.
---
Рассмотрим правильный и безопасный алгоритм взаимодействия с фискальным накопителем. Наша главная задача — сделать так, чтобы документ сначала появился в базе 1С в виде уникальной записи (черновика), и только после этого отправлялась команда на печать.
Разберем этот процесс по шагам:
Запись. Эта операция выполняется мгновенно и практически не вызывает блокировок. Документ получает постоянную ссылку в базе данных.Посмотрим на пример реализации данного алгоритма в коде:
// Шаг 1: Записываем чек как черновик
Попытка
СсылкаНаДокумент = ПолучитьСсылкуНовогоДокумента();
НовыйЧек.Записать(РежимЗаписиДокумента.Запись);
Исключение
ВызватьИсключение "Не удалось предварительно записать чек в базу: " + ОписаниеОшибки();
КонецПопытки;
// Шаг 2: Пробиваем на ККТ
ПараметрыЧека = СформироватьПакетДляККТ(НовыйЧек);
РезультатПечати = ПробитьЧекНаФизическомУстройстве(ПараметрыЧека);
Если РезультатПечати.Успех Тогда
// Шаг 3: Записываем фискальный номер и проводим документ
НовыйЧек.НомерЧекаККМ = РезультатПечати.НомерЧека;
НовыйЧек.Пробит = Истина;
Попытка
НовыйЧек.Записать(РежимЗаписиДокумента.Проведение);
Исключение
// Если проведение заблокировано, чек УЖЕ записан! Данные не потеряны.
ЗаписатьВЛогОшибок("Чек № " + РезультатПечати.НомерЧека + " пробит на ККТ, но не проведен в 1С. Требуется ручное допроведение.");
КонецПопытки;
Иначе
ВызватьИсключение "Ошибка печати чека на ККТ: " + РезультатПечати.Ошибка;
КонецЕсли;
---
Часто разработчики совершают ошибку, оборачивая весь процесс продажи в явную транзакцию с помощью операторов НачатьТранзакцию() и ЗафиксироватьТранзакцию(). Физическая печать чека на ККТ может занимать от 2 до 5 секунд. Если этот процесс находится внутри транзакции 1С, все таблицы СУБД, задействованные в проведении, будут заблокированы на эти секунды для всех остальных пользователей!
Правило: Никогда не вызывайте методы работы с торговым оборудованием (драйвер ККТ) внутри транзакционных блоков 1С. Процедура фискализации должна выполняться строго вне транзакции.
Если вам необходимо реализовать печать в привязке к транзакции базы данных, используйте обработчик события формы ПослеЗаписи. На момент вызова этого события транзакция записи в базу данных уже успешно завершена и закрыта, а значит, другие пользователи не будут заблокированы во время работы печатающего механизма кассы.
---
Если ваша база данных работает на MS SQL Server, частой причиной блокировок кассиров во время обмена РИБ является чтение данных процессом обмена. Чтение накладывает разделяемые блокировки (Shared Locks), которые конфликтуют с монопольными блокировками (Exclusive Locks) при записи чеков.
Чтобы решить эту проблему без изменения кода, включите режим версионирования строк на уровне SQL Server (Read Committed Snapshot Isolation — RCSI). Выполните следующий запрос в SQL Server Management Studio для вашей базы данных:
ALTER DATABASE [Имя_Вашей_Базы_Данных]
SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
ALTER DATABASE [Имя_Вашей_Базы_Данных]
SET READ_COMMITTED_SNAPSHOT ON;
ALTER DATABASE [Имя_Вашей_Базы_Данных]
SET MULTI_USER;
После включения RCSI процессы чтения (например, выгрузка данных в РИБ или построение отчетов администратором) будут читать «снимки» данных из базы tempdb, вообще не блокируя транзакции записи, совершаемые кассирами на фронт-офисе.
---
Проанализируем структуру распределенной информационной базы. Зачем передавать каждый индивидуальный ЧекККМ через каналы обмена РИБ в центральный офис каждый час? Это порождает колоссальную нагрузку на каналы связи и СУБД.
По классической методологии розничной торговли 1С:
Отчет о розничных продажах (ОРП).Рекомендуется исключить документ ЧекККМ из состава отправляемых объектов в планах обмена РИБ. Настройте правила так, чтобы в центральную базу передавался только итоговый Отчет о розничных продажах один раз в день (после закрытия смены). Это снизит транзакционную нагрузку во время ежечасных обменов практически до нуля.
---
Если параллельная работа кассиров неизбежна (например, две кассы работают в одной локальной базе на сервере), а блокировки все же кратковременно возникают, можно увеличить время ожидания блокировки. Вместо вывода моментального сообщения об ошибке система просто заставит сеанс второго кассира подождать 5-10 секунд, пока завершится проведение первого чека.
Установите этот параметр при старте системы или в настройках параметров базы данных:
// Установка времени ожидания блокировки в секундах
УстановитьВремяОжиданияБлокировкиДанных(15);
В сочетании с быстрым проведением (без лишних вызовов функций внутри транзакции) кассиры даже не заметят задержки, так как документ будет проводиться за доли секунды, а очередь транзакций будет обрабатываться сервером незаметно для пользователей.