Почему фискальный регистратор ШТРИХ-М пропускает строки при печати чека из 1С?

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

В практике сопровождения розничной торговли на базе конфигурации 1С:Управление торговлей 10.3 иногда возникает редкая, но критическая ошибка: фискальный регистратор (например, ШТРИХ-М-ФР-К) печатает чек, в котором отсутствует одна или несколько позиций из табличной части документа. При этом 1С не выдает никаких сообщений об ошибках, чек считается успешно закрытым, но итоговая сумма на бумажной ленте и в Z-отчете оказывается меньше, чем в проведенном документе 1С. Разберем по шагам, почему возникает эта проблема, почему касса не аннулирует чек при расхождении сумм и как гарантированно устранить этот сбой.

Выясним причину: почему касса закрывает чек с пропущенной строкой

Для понимания сути проблемы проанализируем механизм взаимодействия 1С с фискальным регистратором через драйвер торгового оборудования. Существует две основные группы причин: аппаратные сбои интерфейса передачи данных и логические особенности обработки команд.

В большинстве случаев потеря строки происходит по следующим причинам:

  1. Переполнение входного буфера ККТ и отсутствие аппаратного контроля потока. При передаче длинных чеков команды регистрации строк (Sale) отправляются в порт одна за другой с высокой скоростью. Микроконтроллер кассового аппарата не успевает обрабатывать входящие данные одновременно с физическим протягиванием чековой ленты и печатью. Если в настройках виртуального или физического COM-порта не включено аппаратное управление потоком (RTS/CTS), буфер переполняется, и байты команды фиксации строки теряются.
  2. Нестабильная работа виртуального COM-порта (USB) или платы расширения PCI. При подключении кассы через переходники USB-to-COM или установке интерфейсных плат PCI под нагрузкой операционной системы драйвер эмуляции порта может кратковременно зависать или терять пакеты данных.
  3. Импульсные просадки питания 24 В. Старые блоки питания со временем теряют емкость сглаживающих конденсаторов. В моменты максимального потребления (нагрев термолинейки при печати или срабатывание ножа автоотрезчика) напряжение кратковременно падает. Аппарат не уходит в полную перезагрузку, но сетевой контроллер пропускает текущий входящий пакет.
  4. Ошибки парсинга из-за спецсимволов. Если в наименовании товара присутствуют скрытые служебные символы (перевод строки, возврат каретки, нулевой символ или табуляция), внутренний парсер кассы считает команду поврежденной и отклоняет ее.

Но почему в таком случае чек не аннулируется? Проанализируем логику работы драйвера DrvFR.dll: фискальный регистратор отменяет чек только в том случае, если переданная сумма оплаты меньше накопленного внутри кассы подытога. Если в обработке обслуживания 1С сумма закрытия берется не из реквизита документа, а запрашивается как текущий подытог регистратора (метод GetSubtotal) либо команда закрытия вызывается без проверки сдачи, касса просто считает, что покупателю пробили меньше товаров, и успешно закрывает чек на фактически принятую сумму.

Шаг 1. Включение логирования для точной локализации ошибки

Поскольку ситуация повторяется редко, отловить ее стандартными средствами отладки тяжело. Начнем с включения детальных логов драйвера и фиксации данных на стороне 1С.

Включим журналирование драйвера ШТРИХ-М. Для этого перейдем в форму настройки торгового оборудования (или утилиту «Тест драйвера ФР»), откроем окно «Свойства» и в разделе «Дополнительно» включим ведение лог-файла. В лог будут построчно записываться все байты команд, ответы фискального накопителя и статусы ResultCode.

Параллельно настроим фиксацию отправки строк непосредственно в коде 1С, чтобы при возникновении расхождений сопоставить лог драйвера с данными документа:


// В модуле обработки обслуживания перед циклом печати чека
ЗаписьЖурналаРегистрации("ПечатьЧека", УровеньЖурналаРегистрации.Информация, , 
    ДокументСсылка, "Начало печати чека по документу " + Строка(ДокументСсылка) + ", строк: " + Строка(ТаблицаТоваров.Количество()));

Для Каждого Стр Из ТаблицаТоваров Цикл
    ЗаписьЖурналаРегистрации("ПечатьЧека", УровеньЖурналаРегистрации.Информация, , 
        ДокументСсылка, "Отправка строки " + Строка(Стр.НомерСтроки) + ": " + Стр.Номенклатура.Наименование + ", Сумма: " + Строка(Стр.Сумма));
КонецЦикла;

Шаг 2. Корректировка параметров связи и таймаутов

Рассмотрим физический уровень передачи данных. Если кассовый аппарат подключен через физический или виртуальный COM-порт, выполним следующие настройки:

  1. Снижение скорости обмена. В утилите «Тест драйвера» и свойствах оборудования в 1С установим скорость передачи данных 19200 бод вместо 115200. Скорость печати термопринтера все равно значительно ниже скорости потока данных, поэтому кассир не заметит замедления, однако риск переполнения буфера контроллера аппарата снизится до минимума.
  2. Увеличение времени ожидания ответа (Timeout). В параметрах подключения увеличим таймаут с стандартных 100–300 мс до 1000–1500 мс. Это позволит драйверу дождаться ответа от регистратора, если тот занят отрезкой бумаги или продвижением ленты.
  3. Аппаратный контроль потока. В диспетчере устройств Windows для соответствующего COM-порта в закладке «Параметры порта» установим «Управление потоком» в значение RTS/CTS (аппаратное), при условии, что используется полноценный интерфейсный кабель.

Шаг 3. Доработка кода печати: проверка ResultCode и очистка данных

Разберем программное решение проблемы. Главная ошибка типовых алгоритмов — игнорирование свойств возврата драйвера ResultCode после выполнения каждой фискальной операции. Если регистратор вернул ненулевой код (ошибка формата, занятость устройства), цикл 1С должен немедленно прерваться, а открытый чек — аннулироваться.

Посмотрим на пример правильной организации цикла регистрации строк:


Для Каждого Стр Из ТаблицаТоваров Цикл
    
    // Очищаем наименование от непечатных управляющих символов и обрезаем по длине
    ОчищенноеНаименование = СокрЛП(Стр.Номенклатура.Наименование);
    ОчищенноеНаименование = СтрЗаменить(ОчищенноеНаименование, Символы.ПС, " ");
    ОчищенноеНаименование = СтрЗаменить(ОчищенноеНаименование, Символы.ВК, " ");
    ОчищенноеНаименование = СтрЗаменить(ОчищенноеНаименование, Символы.Таб, " ");
    ОчищенноеНаименование = Лев(ОчищенноеНаименование, 36); // Ограничение буфера наименования ККТ
    
    // Заполняем свойства драйвера
    Драйвер.ItemName = ОчищенноеНаименование;
    Драйвер.Price    = Стр.Цена;
    Драйвер.Quantity = Стр.Количество;
    Драйвер.Summ     = Стр.Сумма;
    
    // Выполняем регистрацию продажи
    Драйвер.Sale();
    
    // Обязательно проверяем код завершения команды
    Если Драйвер.ResultCode <> 0 Тогда
        ТекстОшибки = "Ошибка регистрации строки " + Строка(Стр.НомерСтроки) + ": " + Драйвер.ResultCodeDescription;
        ЗаписьЖурналаРегистрации("ПечатьЧека", УровеньЖурналаРегистрации.Ошибка, , ДокументСсылка, ТекстОшибки);
        
        // Прерываем выполнение и аннулируем частично пробитый чек
        Драйвер.CancelCheck();
        ВызватьИсключение ТекстОшибки;
    КонецЕсли;
    
    // Небольшая задержка для синхронизации буфера (throttling) при интенсивной печати
    // Даем процессору кассы завершить позиционирование ленты
    // При необходимости можно использовать паузу через внешние компоненты или WScript.Sleep
    
КонецЦикла;

Шаг 4. Сверка подытога перед закрытием чека

Чтобы исключить любую возможность закрытия чека с неполной суммой, перед вызовом финализирующей команды оплаты добавим контрольную сверку. Запросим у регистратора накопленный промежуточный итог и сравним его с итоговой суммой документа в 1С:


// Запрашиваем подытог у фискального аппарата
Драйвер.GetSubtotal();

Если Драйвер.ResultCode = 0 Тогда
    СуммаВКассе = Драйвер.Summ;
    Если СуммаВКассе <> ДокументСсылка.СуммаДокумента Тогда
        // Суммы не совпали - чек закрывать категорически нельзя
        Драйвер.CancelCheck();
        ТекстРасхождения = "Сумма по чеку в кассе (" + Строка(СуммаВКассе) + 
                           ") не совпадает с суммой документа (" + Строка(ДокументСсылка.СуммаДокумента) + ")! Чек аннулирован.";
        ЗаписьЖурналаРегистрации("ПечатьЧека", УровеньЖурналаРегистрации.Ошибка, , ДокументСсылка, ТекстРасхождения);
        ВызватьИсключение ТекстРасхождения;
    КонецЕсли;
Иначе
    Драйвер.CancelCheck();
    ВызватьИсключение "Не удалось получить подытог чека: " + Драйвер.ResultCodeDescription;
КонецЕсли;

Шаг 5. Аппаратная диагностика

Если программные проверки не выявили поврежденных строк, а таймауты и скорости скорректированы, обратим внимание на физическое состояние оборудования:

  1. Проверка блока питания. Замерьте мультиметром выходное напряжение внешнего блока питания под нагрузкой. Если в момент старта печати напряжение проседает ниже 21–22 В, блок питания подлежит замене на исправный (номинал 24 В, не менее 2.5–3 А).
  2. Замена кабеля и порта. При использовании переходников USB-RS232 рекомендуется заменить их на переходники с чипом FTDI или подключить аппарат к физическому COM-порту на материнской плате, избегая дешевых плат PCI, которые часто дают сбои по шине при скачках прерываний.

Реализация жесткой проверки ResultCode каждой товарной строки и валидация GetSubtotal() гарантируют, что касса никогда не пробьет чек с отсутствующей строкой, а расхождения между отчетами и фактической выручкой будут полностью исключены.

← На главную