При переносе информационной базы 1С (например, конфигурации 1С:Розница) с локального компьютера на терминальный сервер (RDP) технические специалисты часто сталкиваются со странным поведением торгового оборудования. Все базовые функции — пробитие чеков, открытие и закрытие смены, внесение денег — продолжают работать в штатном режиме, но при попытке выполнить операцию изъятия (выплаты) наличных программа аварийно завершает работу с системной ошибкой. Проанализируем, почему возникает эта проблема, как она связана с переходом на RDP и как правильно скорректировать программный код для восстановления работоспособности оборудования.
В процессе выполнения операции изъятия денег из кассы система 1С обращается к внешней обработке обслуживания торгового оборудования. В момент передачи параметров драйверу ККТ происходит аварийная остановка со следующим системным сообщением:
{ВнешняяОбработка.АТОЛФискальныеРегистраторы.МодульОбъекта(5004)}: Ошибка при вызове метода контекста (setParam)
Объект.Драйвер.setParam(Объект.Драйвер.LIBFPTR_PARAM_SUM, Сумма);
по причине:
Произошла исключительная ситуация (0x80070057)
Код ошибки 0x80070057 в операционной системе Windows расшифровывается как E_INVALIDARG (недопустимый аргумент или неверный параметр). Это означает, что COM-объект драйвера ККТ АТОЛ получил на вход данные, которые не соответствуют ожидаемому формату, типу данных или допустимому диапазону значений.
Драйвер АТОЛ 10-й версии осуществляет строгую валидацию всех параметров перед их отправкой в физическую память ККТ. Для параметра суммы операции LIBFPTR_PARAM_SUM драйвер ожидает исключительно положительное вещественное число. Передача отрицательного числа напрямую нарушает внутренние бизнес-правила API драйвера, что мгновенно генерирует исключение на уровне платформы Windows.
На первый взгляд кажется, что причиной сбоя послужил именно переход в терминальный режим (RDP) и проброс виртуальных COM-портов. На самом деле здесь имеет место классическая ложная корреляция. При переносе базы данных на сервер администрирования произошло сопутствующее изменение, которое и привело к активации скрытой ошибки в кодовой базе 1С:
ИспользуемыйДрайверККМ = 1) разработчик предусмотрел защиту от отрицательных чисел, принудительно вычисляя абсолютное значение суммы с помощью конструкции МодульСумма = Макс(Сумма, -Сумма);.ИспользуемыйДрайверККМ = 2). В этой ветке разработчик сторонней обработки забыл реализовать аналогичное приведение числа по модулю. В результате при изъятии денег переменная Сумма, имеющая отрицательное значение в бизнес-логике 1С, без изменений передавалась напрямую в метод setParam драйвера АТОЛ 10, вызывая фатальный сбой.Поскольку операция внесения денег передает в процедуру изначально положительное число, она продолжала успешно работать как локально, так и на сервере.
Разберем, как выглядит проблемный участок кода в модуле внешней обработки обслуживания ККТ:
ИначеЕсли ИспользуемыйДрайверККМ = 2 Тогда
// Устанавливаем пользователя/кассира
КАС_УстановитьКассираДляОперации(Объект, Пароль);
Если Сумма > 0 Тогда
Объект.Драйвер.setParam(Объект.Драйвер.LIBFPTR_PARAM_SUM, Сумма);
Объект.Драйвер.cashIncome();
ИначеЕсли Сумма < 0 Тогда
// Ошибка: в метод setParam передается отрицательное значение переменной Сумма!
Объект.Драйвер.setParam(Объект.Драйвер.LIBFPTR_PARAM_SUM, Сумма);
Объект.Драйвер.cashOutcome();
КонецЕсли;
Для исправления этой ошибки нам необходимо исключить передачу отрицательного знака в параметр LIBFPTR_PARAM_SUM. ККТ АТОЛ самостоятельно понимает характер операции (выплата/изъятие денежных средств из денежного ящика) благодаря вызову специализированного метода драйвера cashOutcome(). Сумма выплаты при этом обязана быть строго положительной.
Скорректируем алгоритм, добавив получение модуля числа:
ИначеЕсли ИспользуемыйДрайверККМ = 2 Тогда
// Устанавливаем пользователя/кассира
КАС_УстановитьКассираДляОперации(Объект, Пароль);
Если Сумма > 0 Тогда
Объект.Драйвер.setParam(Объект.Драйвер.LIBFPTR_PARAM_SUM, Сумма);
Объект.Драйвер.cashIncome();
ИначеЕсли Сумма < 0 Тогда
// Вычисляем модуль числа для корректной работы драйвера АТОЛ 10
ПоложительнаяСумма = Макс(Сумма, -Сумма);
Объект.Драйвер.setParam(Объект.Драйвер.LIBFPTR_PARAM_SUM, ПоложительнаяСумма);
Объект.Драйвер.cashOutcome();
КонецЕсли;
Использование функции Макс(Сумма, -Сумма) — это стандартный и надежный способ получения абсолютного значения числа (математического модуля) в среде встроенного языка 1С, гарантирующий корректную передачу данных в COM-объект.
Раз уж мы коснулись работы торгового оборудования в терминальном режиме, рассмотрим несколько важных архитектурных нюансов. Проброс физических COM-портов через стандартный механизм RDP-клиента (COM Port Redirection) является крайне нестабильным решением, которое часто приводит к "зависанию" кассы, задержкам при печати чеков и периодической потере связи.
Для оптимизации работы ККТ в многопользовательской серверной среде рекомендуем применить следующие подходы:
fptr10_server.exe).16710). Данные будут передаваться напрямую по протоколу TCP/IP, минуя нестабильные шлюзы RDP.Новый COMОбъект("AddIn.Fptr10")). В многопользовательской RDP-среде COM-технология требует регистрации библиотек в реестре операционной системы под учетной записью администратора и часто конфликтует при одновременном обращении разных пользователей.
Применение современной Native-компоненты (технология Native API от 1С), которая поставляется в виде zip-архива напрямую в конфигурацию, позволяет изолировать сеансы пользователей и обеспечивает стабильную кроссплатформенную работу без дополнительных манипуляций с реестром Windows Server.Реализация предложенных исправлений в кодовой части интеграции с драйвером АТОЛ 10 полностью решает проблему аварийного завершения сеанса 1С при инкассации, а оптимизация сетевого подключения кассы сделает работу кассиров быстрой и бесперебойной.