Предлагаемая публикация — просто некоторые заметки и наработки, позволившие мне быстро и почти безболезненно запустить в работу ККМ-онлайн модели Штрих-М 01Ф. Поскольку драйвер (а значит, и API) у многих моделей схож или вообще одинаков, думаю, можно распространить эти наработки ещё на несколько популярных моделей. На мой взгляд, и аппаратно, и программно указанный ФР прост, надёжен и стабилен (мне вот никакими адскими опытами пока не удалось ввергнуть его в ошибку, блокировку или иную бяку). По продукции Штрих-М на официальном сайте выложено множество документации, в т.ч. хорошее пользовательское описание и отличная документация для разработчиков, которой я и воспользовался. Там даже есть отрывочные примеры, правда, для 7.5 (если кому надо, могу выложить, я делал версию для 7.7 тоже).
Описанное хорошо подойдёт в случае, если разбираться в механизмах подключаемого оборудования БСП некогда, или если речь о конфигурациях на обычных формах, поколения УТ 10.3, как это было у меня. Или для самописных. Сверх опубликованного более ничего не требуется, никакие общие модули (я вообще на пустой конфе проверял сначала).
Установку и регистрацию в ОФД делал приглашённый специалист, но весь этот процесс также описан в руководствах.
Поскольку ничего внятного о тестовой эксплуатации узнать не удалось, работы велись сразу в «боевом» режиме, что обязывало минимизировать ошибки и количество тестовых запусков. Вообще, кто-то утверждает, что в природе есть тестовые фискальные накопители и даже якобы их можно купить, регистрируясь при этом в рабочем кабинете ОФД; некоторые ОФД открыли тестовые площадки приёма-перехвата данных, предоставляемые тоже по отдельному договору за отдельную плату. Но «старый добрый» способ теста — пробивать минимальные суммы — тоже законен и ФНС не напряжёт, и это мне в один голос говорили многие знающие люди. Так и поступил.
Общие принципы
Связь с аппаратом возможна через COM-порт кабелем usb, или через патчкорд по TCP-сокету. Важно, что связь через ком позволяет аппарату легко работать с терминалами. 1С где-то далеко, а рабочее место кассира оснащается ФР и надо лишь поставить драйвер на сам сервер да прокинуть порт (указать в настройках терминала и поправить безопасность, если потребуется), впрочем, эти приёмы обычно хорошо знают системные администраторы. У меня ФР уже работает с 1С-кой в терминале.
Разумеется, я реализовал только самые первоочерёдно нужные операции, вообще их существенно больше, но общий принцип одинаков. При установке драйвера сразу ставится и программная компонента, подключаемая как внешняя штатным образом (обычно она сама сразу нормально регистрируется в системе). ПО к этому ФР включает пользовательский интерфейс утилиты ручного управления, и, что ценно, его поведение абсолютно совпадает с поведением программного интерфейса этой же утилиты. Т.е. можно сначала безо всякой 1С оформить некую операцию, используя интерфейс утилиты, а затем попробовать отдать аналогичные команды в коде 1С.
При работе используется переменная, содержащая com-объект, чьи свойства и методы, собственно, и используются. Поскольку, по сути, ФР это просто маленький принтер с некоторыми пакетными предзаданными действиями и собственной памятью, мы запрашиваем драйвер о значениях неких свойств, устанавливаем значения этих свойств, вызываем методы. Принята следующая идеология: сначала устанавливаются значения свойств, нужные для вызова действия, потом вызывается метод. Все методы — без параметров. По вызове метода некоторые свойства меняют свои значения. Есть понятие «состояние ФР», определяющее, что он может и что нет в каждый момент времени. У некоторых из них есть уточняющие «подсостояния». Некоторые методы изменяют состояние ФР согласно своей внутренней логике. Подробнее всё это описано в руководстве для разработчика.
ФР обладает собственной аппаратной памятью, где хранит таблицы — это действительно плоские простые таблицы, никак не связанные между собой и наполненные значениями простых типов. «Настройка свойств» — «Таблицы». Без установленной связи с включённым аппаратом, ясен перец, недоступны. Советую полазить и ознакомиться. Они доступны для чтения и записи из программного кода. При работе с методами тем или иным свойствам указываются обычно числовые ID, соответствующие порядковым номерам строк в этих таблицах.
Почти каждый метод перед выполнением требует авторизации (указания ID пользователя), не ленитесь их указывать. Для выполнения некоторых методов требуется указание 29 (админ) или 30 (системный админ), так, например, простые смертные кассиры не могут закрыть смену и даже снять Х-отчёт. Подробнее эти завихрения разделения прав описаны в руководстве пользователя.
Исходники для основных действий.
// Инициализирует ФР как COM-объект; рекомендуется расположить в модуле "повт.исп." с сохранением в течение сеанса.
Процедура ИнициализироватьФР() Экспорт
Попытка
ПодключитьВнешнююКомпоненту("AddIn.DrvFR"); // достаточно штатной установки драйвера на локальный ПК или сервер
ком=Новый COMОбъект("AddIn.DrvFR"); // никакой более регистрации на современных ОС обычно не требуется
глФР=ком; // экспортная переменная глФР переменная должна быть объявлена в глобальном модуле обычного приложения
Исключение
Сообщить("Ошибка инициализации фискального регистратора кассовых чеков: "+ОписаниеОшибки(),СтатусСообщения.Важное);
КонецПопытки;
КонецПроцедуры
// Зависит от контекста реализации - например, это может быть параметр сеанса или реквизит справочника Пользователи, Кассы, Подразделения;
// возвращает элемент справочника, содержащего сведения о подключаемых устройствах; типовой или сделанный отдельно. Должен содержать
// сведения, необходимые для подключения к конкретному устройству (см. ПодключитьФР).
//
Функция ПолучитьИспользуемыйФР() Экспорт
//
КонецФункции
// Зависит от контекста реализации - например, это может быть параметр сеанса или реквизит справочника Пользователи, Кассы, Подразделения;
// возвращает число от 1 до 30, позицию в таблице пользователей и паролей ФР. Нулевое значение означает обычно невозможность работы.
// Число 29 означает права обычного администратора, 30 - системного администратора (под 30 рекомендуется вести тестирование и спец.операции).
Функция ПолучитьИдПользователя() Экспорт
//
КонецФункции
// Подключает физическое устройство, проверяет связь, приводит в рабочий режим логическое устройство.
// При нестабильной связи или нетривиальном кэшировании данных 1С/ОС, рекомендуется вызывать перед каждым рабочим действием.
//
// Возвращает успешность (булево). При ошибке возвращает Ложь.
//
// Параметры:
// рУстройство - логическая запись об устройстве (например, элемент справочника), содержащая обычно следующие сведения для подключения:
//BaudRate
//НомерПортаCOM
//Таймаут
//ТаймаутСоединения
//ТаймаутСоединенияTCP
//ПортTCP
//ИмяУстройства
//IPУстройства
//РежимПодключения (см.руководство; например, 0 - ком-порт, 6 - tcp-сокет)
//
// рКодОшибки - число; на входе 0, на выходе либо 0 (ошибок нет), либо код ошибки.
// рПояснятьОК - булево; комментировать ли успешное подключение.
//
Функция ПодключитьФР(рУстройство,рКодОшибки=0,рПояснятьОК=Ложь) Экспорт
Попытка
рКодОшибки=0;
Если не ЗначениеЗаполнено(рУстройство) Тогда Возврат Ложь КонецЕсли;
фр=глФР;
фр.Password=30; // можно ещё 29
рез=фр.CheckConnection();
Если рез<>0 Тогда
//Сообщить("ПодключитьФР, ошибка предварительной проверки подключения: "+фр.ResultCodeDescription);
//рКодОшибки=фр.ResultCode;
рУжеПодключены=Ложь;
Иначе
Если рПояснятьОК Тогда Сообщить("ПодключитьФР, предварительная проверка подключения успешна.") КонецЕсли;
// смотрим, что она определила
Попытка рУжеПодключены=фр.Connected Исключение рУжеПодключены=Ложь КонецПопытки;
КонецЕсли;
//
Если рУжеПодключены Тогда Возврат Истина КонецЕсли;
Если рУстройство.РежимПодключения=0 Тогда
фр.BaudRate=6;
ИначеЕсли рУстройство.РежимПодключения=6 Тогда
фр.BaudRate=1;
КонецЕсли;
//
фр.ComNumber=рУстройство.НомерПортаCOM;
фр.Timeout=рУстройство.Таймаут;
фр.ConnectionTimeout=рУстройство.ТаймаутСоединения;
фр.TCPConnectionTimeout=рУстройство.ТаймаутСоединенияTCP;
фр.SyncTimeout=0;
фр.ComputerName=рУстройство.ИмяУстройства;
фр.LDIndex=0;
фр.LockTimeout=10000;
фр.TCPPort=рУстройство.ПортTCP;
фр.IPAddress=рУстройство.IPУстройства;
фр.UseIPAddress=False;
фр.ConnectionType=рУстройство.РежимПодключения;
фр.EscapeIP="127.0.0.1";
фр.EscapePort=1000;
фр.EscapeTimeout=1000;
фр.SysAdminPassword=30;
фр.CardPayType=2;
фр.CardPayEnabled=False;
фр.LogCommands=False;
фр.LogMethods=False;
фр.SaleError=False;
фр.MobilePayEnabled=False;
фр.PayDepartment=15;
фр.ParamsPageIndex=0;
фр.RealPayDepartment=1;
фр.WaitForPrintingDelay=1000;
фр.BufferingType=0;
фр.FeedAfterCut=False;
фр.FeedLineCount=3;
фр.StatusCommand=0;
фр.LogMaxFileSize=10;
фр.LogMaxFileCount=10;
фр.CodePage=0;
фр.PrintJournalBeforeZReport=False;
фр.TranslationEnabled=False;
фр.AdjustRITimeout=False;
фр.ReconnectPort=False;
фр.DoNotSendENQ=False;
рез=фр.Connect();
Если рез<>0 Тогда
Сообщить("ПодключитьФР, ошибка собственно подключения: "+фр.ResultCodeDescription);
рКодОшибки=фр.ResultCode;
Возврат Ложь;
Иначе
Если рПояснятьОК Тогда Сообщить("ПодключитьФР, собственно подключение успешно.") КонецЕсли;
КонецЕсли;
Если рез<>0 Тогда
Сообщить("ПодключитьФР, ошибка заключительной проверки подключения: "+фр.ResultCodeDescription);
рКодОшибки=фр.ResultCode;
Возврат Ложь;
Иначе
Если рПояснятьОК Тогда Сообщить("ПодключитьФР, заключительная проверка подключения успешна.") КонецЕсли;
КонецЕсли;
Попытка рУжеПодключены=фр.Connected Исключение рУжеПодключены=Ложь КонецПопытки;
Возврат рУжеПодключены;
Исключение
Сообщить("ПодключитьФР, ошибка: "+ОписаниеОшибки(),СтатусСообщения.Важное);
Возврат Ложь;
КонецПопытки;
КонецФункции
// Возвращает успешность (булево). Необязательное; но важно, что при уничтожении переменной связь разрушается не всегда
// (возможно, проявляется описанное в СП кэширование подключенных объектов внешних компонент в течение всего сеанса).
//
Функция ОтключитьФР(рКодОшибки=0,рПояснятьОК=Ложь) Экспорт
Попытка
рКодОшибки=0;
фр=глФР;
фр.Password=ПолучитьИдПользователя();
Попытка рУжеПодключены=фр.Connected Исключение рУжеПодключены=Ложь КонецПопытки;
Если не рУжеПодключены Тогда Возврат Истина КонецЕсли;
рез=фр.Disconnect(); // собственно главное
Если рез<>0 Тогда
Сообщить("ОтключитьФР, ошибка подключения: "+фр.ResultCodeDescription);
рКодОшибки=фр.ResultCode;
Возврат Ложь;
Иначе
Если рПояснятьОК Тогда Сообщить("ОтключитьФР, подключение успешно.") КонецЕсли;
Возврат Истина;
КонецЕсли;
Исключение
Сообщить("ОтключитьФР, ошибка: "+ОписаниеОшибки(),СтатусСообщения.Важное);
Возврат Ложь;
КонецПопытки;
КонецФункции
// Возвращает структуру вида: НомерОператора, ЕстьЛента, РазрядностьСумм, РазрядностьКоличеств, Режим, ОписаниеРежима,
// Подрежим, ОписаниеПодрежима, СостояниеФН. Если Режим=8 (открытый документ), то добавляется РежимОткрытогоДокумента
// и ОперацийВЧеке.
//
// При ошибке возвращает Неопределено.
//
// Номера и назначение режимов и статусов (поле ECRMode):
//0 Принтер в рабочем режиме
//1 Выдача данных
//2 Открытая смена, 24 часа не кончились
//3 Открытая смена, 24 часа кончились
//4 Закрытая смена
//5 Блокировка по неправильному паролю налогового инспектора
//6 Ожидание подтверждения ввода даты
//7 Разрешение изменения положения десятичной точки
//8 Открытый документ // Продажа, Покупка, Возврат продажи, Возврат покупки, уточняется через ECRMode8Status
//9 Режим разрешения технологического обнуления (В этот режим ККМ переходит по включению питания, если некорректна информация в энергонезависимом ОЗУ ККМ).
//10 Тестовый прогон
//11 Печать полного фискального отчета
//12 Печать длинного отчета ЭКЛЗ
//13 Работа с фискальным подкладным документом
//14 Печать подкладного документа
//15 Фискальный подкладной документ сформирован
//
Функция ПолучитьРежимФР(рКодОшибки=0) Экспорт
Попытка
рКодОшибки=0;
фр=глФР;
фр.Password=ПолучитьИдПользователя();
Если фр.ECRMode=1 Тогда // вызывать нельзя
Возврат Неопределено;
КонецЕсли;
рРезультат=Новый Структура;
фр.GetDeviceMetrics();
Если фр.CapGetShortECRStatus Тогда
рез=фр.GetShortECRStatus();
Иначе
рез=фр.GetECRStatus(); // длинный полный
КонецЕсли;
Если рез<>0 Тогда
Сообщить("ПолучитьРежимФР, ошибка получения: "+фр.ResultCodeDescription);
рКодОшибки=фр.ResultCode;
Возврат Неопределено;
КонецЕсли;
рРезультат.Вставить("НомерОператора",фр.OperatorNumber);
рРезультат.Вставить("ЕстьЛента",фр.ReceiptRibbonIsPresent); // есть ли лента
рРезультат.Вставить("РазрядностьСумм",?(фр.PointPosition,2,0)); // для сумм, если Истина, то 2 разряда, иначе 0 разрядов
рРезультат.Вставить("РазрядностьКоличеств",?(фр.QuantityPointPosition,3,6)); // для количеств, если Истина, то 3 знака, иначе 6 знаков
рРезультат.Вставить("Режим",фр.ECRMode);
рРезультат.Вставить("ОписаниеРежима",фр.ECRModeDescription);
#Область ОписаниеПодрежимов
//0 Бумага есть – ККТ не в фазе печати операции – может принимать от хоста команды, связанные с печатью на том ленте, датчик которой сообщает о наличии бумаги.
//1 Пассивное отсутствие бумаги – ККМ не в фазе печати операции – не принимает от хоста команды, связанные с печатью на том ленте,
// датчик которой сообщает об отсутствии бумаги.
//2 Активное отсутствие бумаги – ККМ в фазе печати операции – принимает только команды, не связанные с печатью. Переход из этого подрежима только в подрежим 3.
//3 После активного отсутствия бумаги – ККМ ждет команду продолжения печати. Кроме этого принимает команды, не связанные с печатью.
//4 Фаза печати операции длинного отчета (полные фискальные отчеты, полные отчеты ЭКЛЗ, печать контрольных лент из ЭКЛЗ) – ККМ не принимает от
// хоста команды, связанные с печатью, кроме команды прерывания печати.
//5 Фаза печати операции – ККМ не принимает от хоста команды, связанные с печатью.
#КонецОбласти
рРезультат.Вставить("Подрежим",фр.ECRAdvancedMode);
рРезультат.Вставить("ОписаниеПодрежима",фр.ECRAdvancedModeDescription);
Если фр.ECRMode=8 Тогда
// 0 Открыт чек продажи, 1 Открыт чек покупки, 2 Открыт чек возврата продажи, 3 Открыт чек возврата покупки
рРезультат.Вставить("РежимОткрытогоДокумента",фр.ECRMode8Status);
рРезультат.Вставить("ОперацийВЧеке",фр.QuantityOfOperations); // количество операций в текущем чеке
КонецЕсли;
рРезультат.Вставить("СостояниеФН",фр.FMResultCode);
Возврат рРезультат;
Исключение
Сообщить("ПолучитьРежимФР, ошибка: "+ОписаниеОшибки(),СтатусСообщения.Важное);
Возврат Неопределено;
КонецПопытки;
КонецФункции
// Возвращает структуру вида СостояниеСмены (см.руководство программиста), НомерЧека, НомерСмены.
// При ошибке возвращает Неопределено.
//
Функция ПолучитьНомерСменыИЧека(рКодОшибки=0) Экспорт
Попытка
рКодОшибки=0;
фр=глФР;
фр.Password=30; // и только так
рез=фр.FNGetCurrentSessionParams();
Если рез<>0 Тогда
Сообщить("ПолучитьНомерСменыИЧека, ошибка получения: "+фр.ResultCodeDescription+" (использован пароль "+СокрЛП(фр.Password)+").");
рКодОшибки=фр.ResultCode;
Возврат Неопределено;
КонецЕсли;
рРезультат=Новый Структура;
рРезультат.Вставить("СостояниеСмены",фр.FNSessionState);
рРезультат.Вставить("НомерСмены",фр.SessionNumber); // если закрыта, то последней, если открыта, то текущей
рРезультат.Вставить("НомерЧека",фр.ReceiptNumber); // если закрыта, то 1, если открыта, то последниего текущего
Возврат рРезультат;
Исключение
Сообщить("ПолучитьНомерСменыИЧека, ошибка: "+ОписаниеОшибки(),СтатусСообщения.Важное);
Возврат Неопределено;
КонецПопытки;
КонецФункции
// Открывает кассовую смену. Возвращает успешность (булево).
Функция ОткрытьСмену(рКодОшибки=0) Экспорт
Попытка
рКодОшибки=0;
фр=глФР;
фр.Password=ПолучитьИдПользователя();
Если фр.ECRMode<>4 Тогда // смена уже открыта, или некий другой режим
рКодОшибки=99;
Возврат Истина;
КонецЕсли;
рез=фр.OpenSession();
Если рез<>0 Тогда
Сообщить("ОткрытьСмену, ошибка выполнения: "+фр.ResultCodeDescription);
рКодОшибки=фр.ResultCode;
Возврат Ложь;
КонецЕсли;
Сообщить("ОткрытьСмену, смена успешно открыта.");
Возврат Истина;
Исключение
Сообщить("ОткрытьСмену, ошибка: "+ОписаниеОшибки(),СтатусСообщения.Важное);
Возврат Ложь;
КонецПопытки;
КонецФункции
// Закрывает кассовую смену (снимает Z-отчёт). Возвращает успешность (булево).
Функция ЗакрытьСмену(рКодОшибки=0) Экспорт
Попытка
рКодОшибки=0;
фр=глФР;
фр.Password=30; // можно ещё 29
Если фр.ECRMode<>2 и фр.ECRMode<>3 Тогда // смена уже закрыта, или другой режим
рКодОшибки=99;
Возврат Истина;
КонецЕсли;
рез=фр.PrintReportWithCleaning();
Если рез<>0 Тогда
Сообщить("ЗакрытьСмену, ошибка выполнения: "+фр.ResultCodeDescription);
рКодОшибки=фр.ResultCode;
Возврат Ложь;
КонецЕсли;
Сообщить("ЗакрытьСмену, смена успешно закрыта.");
Возврат Истина;
Исключение
Сообщить("ЗакрытьСмену, ошибка: "+ОписаниеОшибки(),СтатусСообщения.Важное);
Возврат Ложь;
КонецПопытки;
КонецФункции
// Возвращает успешность (булево).
Функция СнятьХОтчет(рКодОшибки=0) Экспорт
Попытка
рКодОшибки=0;
фр=глФР;
фр.Password=30; // можно ещё 29
Если фр.ECRMode<>2 и фр.ECRMode<>3 Тогда // смена уже закрыта, или другой режим
Возврат Истина;
КонецЕсли;
рез=фр.PrintReportWithoutCleaning();
Если рез<>0 Тогда
Сообщить("СнятьХОтчет, ошибка снятия отчёта: "+фр.ResultCodeDescription);
рКодОшибки=фр.ResultCode;
Возврат Ложь;
КонецЕсли;
Сообщить("СнятьХОтчет, отчёт успешно снят.");
Возврат Истина;
Исключение
Сообщить("СнятьХОтчет, ошибка: "+ОписаниеОшибки(),СтатусСообщения.Важное);
Возврат Ложь;
КонецПопытки;
КонецФункции
// Выполняет печать чека (продажи, покупки, возврата продажи, возврата покупки)
// Возвращает булево, успешность. При ошибке возвращает Ложь.
// КодСтавкиНДС в данном случае общий на все позиции, т.е. для правильной печати у всех строк должна быть одинаковая ставка НДС.
//
// Параметры:
// рПараметры - структура с обязательными ключами:
// ДанныеПродажи (таблица значений или массив структур: колонки ПредставлениеНоменклатуры, Количество, Цена);
// КодПодразделения (число);
// КодСтавкиНДС (число), где 1 - 18%, 2 - 10%, 3 - 0%, 4 - Без налога, 5 - 18/118, 6 - 10/110;
// СуммыОплат (структура: ключ - число, 1 нал, 2 безнал; значение - сумма платежа).
// рКодОшибки - число, на входе 0, на выходе содержит числовой код ошибки. 0 - ошибок нет.
//
Функция НапечататьЧек(рПараметры,рКодОшибки=0) Экспорт
Попытка
рКодОшибки=0;
фр=глФР;
фр.Password=ПолучитьИдПользователя();
Если фр.ECRMode<>2 и фр.ECRMode<>3 и фр.ECRMode<>4 Тогда // смена уже закрыта, или другой режим
Возврат Истина;
КонецЕсли;
фр.CheckType=рПараметры.РежимЧека; // «0» - продажа, «1» - покупка, «2» - возврат продажи, «3» - возврат покупки.
рез=фр.OpenCheck();
Если рез<>0 Тогда
Сообщить("НапечататьЧек, ошибка открытия нового чека: "+фр.ResultCodeDescription);
рКодОшибки=фр.ResultCode;
Возврат Ложь;
КонецЕсли;
фр.StringForPrinting="============================================";
фр.UseReceiptRibbon=Истина;
фр.UseJournalRibbon=Ложь;
фр.PrintString();
Для каждого строДанных Из рПараметры.ДанныеПродажи Цикл
фр.Quantity=строДанных.Количество;
фр.Price=строДанных.Цена;
фр.Department=рПараметры.КодПодразделения;
фр.Tax1=рПараметры.КодСтавкиНДС;
фр.Tax2=0;
фр.Tax3=0;
фр.Tax4=0;
фр.StringForPrinting=СокрЛП(строДанных.ПредставлениеНоменклатуры);
//Работает в режимах 2 (проверка на окончание 24 часов производится запросом из ФП до
//выполнения операции), 4, 7, 8 (если статус 8-го режима ККМ=0) и 9
Если рПараметры.РежимЧека=0 Тогда
рез=фр.Sale(); // продажа
ИначеЕсли рПараметры.РежимЧека=1 Тогда
рез=фр.Buy(); // покупка
ИначеЕсли рПараметры.РежимЧека=2 Тогда
рез=фр.ReturnSale(); // возврат продажи
ИначеЕсли рПараметры.РежимЧека=3 Тогда
рез=фр.ReturnBuy(); // возврат покупки
КонецЕсли;
Если рез<>0 Тогда
Сообщить("НапечататьЧек, ошибка оформления записи продажи для "+СокрЛП(строДанных.ПредставлениеНоменклатуры)+": "+фр.ResultCodeDescription);
рКодОшибки=фр.ResultCode;
Возврат Ложь;
КонецЕсли;
КонецЦикла;
//
рез=фр.CheckSubTotal();
Если рез<>0 Тогда
Сообщить("НапечататьЧек, ошибка получения подытога чека: "+фр.ResultCodeDescription);
рКодОшибки=фр.ResultCode;
Возврат Ложь;
КонецЕсли;
фр.Summ1=0;
фр.Summ2=0;
фр.Summ3=0;
фр.Summ4=0;
Попытка фр.Summ1=рПараметры.СуммыОплат.Получить(1) Исключение КонецПопытки;
Попытка фр.Summ2=рПараметры.СуммыОплат.Получить(2) Исключение КонецПопытки;
Попытка фр.Summ2=рПараметры.СуммыОплат.Получить(3) Исключение КонецПопытки;
Попытка фр.Summ2=рПараметры.СуммыОплат.Получить(4) Исключение КонецПопытки;
фр.DiscountOnCheck=0; // в данной версии считается, что скидка уже заложена в изменённых ценах
фр.Tax1=рПараметры.КодСтавкиНДС;
фр.Tax2=0;
фр.StringForPrinting="============================================";
рез=фр.CloseCheck();
Если рез<>0 Тогда
Сообщить("НапечататьЧек, ошибка закрытия чека: "+фр.ResultCodeDescription);
рКодОшибки=фр.ResultCode;
Возврат Ложь;
КонецЕсли;
Возврат Истина;
Исключение
Сообщить("НапечататьЧек, ошибка: "+ОписаниеОшибки(),СтатусСообщения.Важное);
Возврат Ложь;
КонецПопытки;
КонецФункции
// Возвращает успешность (булево).
Функция АннулироватьЧек(рЗакрытиеАдминистратора=Ложь,рКодОшибки=0) Экспорт
Попытка
рКодОшибки=0;
фр=глФР;
Если рЗакрытиеАдминистратора Тогда
фр.Password=30; // системный
Иначе
фр.Password=ПолучитьИдПользователя();
КонецЕсли;
Если фр.ECRMode<>8 Тогда // чек не открыт
Возврат Истина;
КонецЕсли;
Если рЗакрытиеАдминистратора Тогда
рез=фр.SysAdminCancelCheck();
Иначе
рез=фр.CancelCheck();
КонецЕсли;
Если рез<>0 Тогда
Сообщить("АннулироватьЧек, ошибка отмены чека: "+фр.ResultCodeDescription);
рКодОшибки=фр.ResultCode;
Возврат Ложь;
КонецЕсли;
Сообщить("АннулироватьЧек, чек успешно аннулирован.");
Возврат Истина;
Исключение
Сообщить("АннулироватьЧек, ошибка: "+ОписаниеОшибки(),СтатусСообщения.Важное);
Возврат Ложь;
КонецПопытки;
КонецФункции
Я не стал реализовывать множество других команд, вроде «ПродолжитьПечать» или «ОтрезатьЧек», т.к. принцип, думаю, ясен, и сделать их самостоятельно не проблема. Кстати, «ПродолжитьПечать» (ContinuePrint) бывает архиполезна, если посередине печати чека, пока он открыт, случилась ошибка, а аннулирование чека (в т.ч. под админом) не прокатывает.
Примечания
Некоторые советы сверх комментариев в коде.
1. Если Вам непонятно, что делается со свойствами в момент выполнения некоей команды, вызываемой из тестовой утилиты, включите ведение лога («Настройка свойств» — «Дополнит.параметры» — «Лог») в свой файл, и внимательно прочитайте, что запишется при вызове команды. Так можно доподлинно узнать, «чего ему надо». Учтите, что программные вызовы драйвера (из вашей 1С) в лог не пишутся, только действия из интерфейса утилиты.
2. Большинство кнопок и некоторые поля в интерфейсе утилиты снабжено всплывающими подсказками, которые отображают имя команды API. Это удобно, чтобы понять, а что же скрывается за той или иной кнопкой, не нажимая её, безо всякого лога.
3. Если некая операция не удаётся программно, приходится использовать команды из интерфейса утилиты. Автоустановка ключей в какой-нибудь сложной ситуации с хитрым сочетанием факторов, предусмотренная разработчиками и не учтённая вами, поможет быстро выйти из тупика.
4. Используйте только латинские варианты свойств и методов. Русскоязычные — себе на уме.
5. Некоторые сообщения не то, чем кажутся. Например, ошибка несоответствия суммы подытога напрямую указанной сумме чека может выглядеть как отсутствие бумаги — и по коду, и по тексту ошибки, и по состоянию, в котором окажется ФР.
6. Если смена закрыта, то действия по чекам её открывают автоматически.
7. В зависимости от совокупности факторов (кэширование 1С, кэширование ОС) период жизни соединения колеблется, поэтому иногда приходится вызывать установку связи перед каждым полезным действием, а иногда и так прокатывает. Но, при этом, см. пункт 8.
8. Драйвер не понимает более 1 активного соединения, и неважно, это ваш COMОбъект или его родная утилита. Утилиту можно открыть «на посмотреть», но она вряд ли установит связь, пока существует соединение (ваша переменная в памяти 1С), и наоборот. Соединение в таких случаях не устанавливается, а наиболее характерная ошибка — таймаут. Не ставьте большой таймаут — всё равно не додумается. Чтобы запустить что-то, вам понадобится закрыть (выгрузить из памяти) что-то другое. В ряде случаев — ещё и выключить/включить сам ФР.
9. Методы и поля, представленные в интерфейсе утилиты, не жёсткая данность. Например, CheckSubTotal всегда вносит сумму в поле «summ1», но она может быть насильно перебита на другой тип оплаты. Учтите, «защита от дурака» у интерфейса утилиты понадёжнее, чем у методов API.
10. Не ленитесь прокручивать таблицы ФР при просмотре. Верхние строки могут быть пусты, а где-то внизу, строке на тридцатой, может поджидать интересный сюрприз.
Разумеется, весь этот примитивный код малополезен без инфраструктуры более высокого уровня — справочников, хранящих сведения об ФР и прочих кодах, документов вроде Кассовой смены, Чека ККМ итд. Но сие уж слишком зависит от конкретики, а опубликованное — оно общее.
Недостатки
Отмечу недостатки предложенного решения, возникшие либо из-за неполной ясности вопроса (в т.ч. юридической), либо из-за отсутствия надобности и времени) Они таковы:
1. Не допускается наличие в составе одного чека товаров с разными ставками НДС. Кому будет надо — это можно довольно легко доработать, но уже с упором на более высокоуровневые конструкции (табчасти документов, механику расчёта НДС итд).
2. Не обрабатываются скидки. Подразумевается, что скидка «размазана» пропорционально по ценам либо суммам позиций, или что цены указаны уже с учётом скидок. Важно, что в методе печати чека ФР оперирует ценой и количеством, и сам считает сумму, поэтому при всяких там скидках на сумму и иных манипуляциях, отличающих произведение цены и количества от итоговой суммы, следует это учитывать, либо допилить предложенный код до нормальной работы со скидками. Грешен, у меня на это времени пока нету.
3. Не раскрыты возможности оформления чеков и печати штрихкодов. Ну, тут уж мануал в руки и запасную катушку ленты в аппарат)
4. Проверка проводилась только на 3 модификациях ФР Штрих-М, и я не очень уверен, что на чём-то заковыристом абсолютно всё будет аналогично.
При небольшом допиливании или вовсе без него, должно работать и на 8.1, также уже гонял этот код с небольшими доработками под УФ на УТ 11.3 (заказчик не возжелал обновляться на типовую, ибо конфа весьма покуроченная).
Планирую докопаться до ФР АТОЛ, но у них с драйверами и их программными интерфейсами несколько больший разнобой, так что это пока наполеоновские планы.
Приглашаю всех к обсуждению. Если где накосячил, жду тухлых помидоров)
Вопрос, а как реализован механизм отправки электронного чека в виде СМС и письма на эл.почту?
Клиент вправе потребовать — электронный чек, помимо бумажного
(1) В руководстве программиста на эту тему есть целый блок, но я его пока не курил. Авось позже доберусь.
как хоть называется? А то 308 листов мне тоже курить как то не хочется.
(3) Опаньки, а нету… Где ж я видел-то… Ну, значит, в любом случае можно реализовать самостоятельно — почту через штатную 1С, а sms через любой подходящий сервис. Вопрос в чём — как Вы, к примеру, через sms передадите чек? Что там будет в этой sms?
Сервисы то есть штатные…СМС отправить не проблема…Но это должен делать ОФД, иначе как клиент поймёт,что это официальный чек. А как ОФД поймёт что нужно электронный чек клиенту отправить????
(5) Честно? Пока хз.
Руководство пользователя и программиста — Штрих-Мовские? Тогда лучше бы ссылками на скачивание на оф.сайте.
(5)
В реквизитах чека есть почта покупателя, если она заполнена то ОФД следовательно надо отсылать письмо
В письме не сам чек а ссылка на чек в ОФД
То есть, с Онлайн Штрихами работают старые методы?
Из новых у Вас используется только «состояние смены».
И грубо говоря, в старые обработки после открытия чека нужно только добавить ввод телефона, почты?
Типа:
Ну типа да, наверное. А собственно, кроме оплаты связи и работы с ФН, что уж такого принципиально нового?
Самому проверить пока на чем.
Очень смущает куча новых методов с префиксом «FN» без какого-то описания в документации.
Например, если есть «OpenSession», тогда зачем и что значат «FNBeginOpenSession» и «FNOpenSession».
Замечу, что я нарочно использовал старые добрые методы, а не всякие там шаблоны. Действует топорно, зато надёжно. И можно совершенно не зависеть от конфы)
(11) Думаю, это сессии отправки данных и работы именно ФН, а не кассовые смены.
«Согласно 54-ФЗ при расчётах продавец обязан выдать кассовый чек покупателю на бумажном носителе или в электронном виде по sms или e-mail (если есть техническая возможность).
ООО «Такском», выступая в роли оператора фискальных данных, автоматически отправляет электронные чеки в электронном виде по sms или e-mail, если они содержат адрес получателя.
Обратите внимание: Согласно 63-ФЗ «О связи» от покупателя необходимо получить письменное согласие или заявление на получение sms-рассылки до момента расчёта.»
Думаю в крупных магазинах никто не будет заморачиваться с вводом при оплате почты или телефона, еще и согласие печатать и подписывать. Скачал приложение проверка чека в ФНС, отсканил QR-код, нажал получить чек, ввел данные и все. Кому надо тот пускай сам его и получает.
Подскажите пожалуйста, у кого есть Штриховские онлайн кассы.
Каким «типом устройства» драйвер определяет онлайн кассы?
Можно посмотреть в «Тест драйвера» — «Состояние» -> «Параметры устройства».
Перерыл все доки, нигде нет. Есть предположение, что по аналогии тип устройства должен быть «7», но нужно точно.
Из всего найденного это самый простой и результативный подход.. Так что спасибо автору. И вопрос.. Попытался сделать скидку на чек.. ругается, не хочет. Вы пробовали скидки?
(16) Пробовал, построчные и в целом на чек. Печатает. А что у Вас ругается?
Команды по скидкам не отрабатываются. Ни построчно, ни на чек в целом. Но, возможно, это из-за прошивки. На одной кассе вообще не работала, пока апрельскую прошивку не поставили.
(18) Возможно дело в драйвере ККТ. У меня, например, в версии 4.13 ругался на вывод денег из кассы (что-то о недопустимой длине наименования), поставил 4.12 — все заработало нормально
Хорошая статья! Я через это прошел лет 10 назад, правда на Дельфи и вот что выродил к 54ФЗ
http://infostart.ru/public/609030/
(19)Самое прикольное, что при одинаковом драйвере на win 7 скидки работают, а на win 10 нет.. Веселуха !
В чеке выходит система налогообложения ЕНВД.. как ее поменять? Перерыл все.. тупик.. В стандартных 1с-овских программах она меняется.. а как это сделать командно не могу найти.. может кто подскажет.
(22) Могу рассказать только «от корней». Запустите проверку драйвера Штрих-М, т.е. его встроенную утилиту (аппарат должен быть включён и доступен на связи), нажмите «Таблицы» и посмотрите, что у Вас в таблице «Налоговые ставки». Если там есть такая строка вроде «ЕНВД», то смотрите, какой номер секции передаёт 1С драйверу и откуда ноги растут у этого номера секции. Если такой строки нет — сообщите, где (в каком месте) проявляется, что это ЕНВД. Где это на чеке итд.
(20) Мы практически совпали в подходе. Я сделал независимое от мудрёностей БСП и типовых конфигов, а ты сделал вообще не на 1С (кстати, была такая мысль тоже). И что 1С любит усложнять, соглашусь.
Вообще что хочу сказать: читайте мануал юзера и программиста от Штрих-М, юзайте утилиту проверки драйвера, и будет щастье. Там всё просто.
(22) ситема налогооблажения меняется в 18 таблице
Показать
(25)Спасибо. Попробую.
(25)
В этой таблице хранится система налогообложения, с которой касса регистрировалась. Там может стоять например «10» это ЕНВД + УСН Доход.
(22)
Чтобы пробить чек с определенной СНО нужно чек формировать командами FNOperation, закрывать командой FNCloseCheckEx предварительно указав TaxType
Показать
НалогооблАжение от слова Лажа? )))
По сути да, верно. И это описано в руководстве)
Подскажите пожалуйста, для операции FNOperation в каких реквизитах указывать такие поля как «Сумма по чеку постоплатой (кредит)» и «Сумма по чеку наличными» ? И еще в этой же операции есть реквизит Summ1, что в него необходимо записывать при частичных оплатах на опте?
Какая может быть сумма по ЧЕКУ в ТОВАРНОЙ строке? Какие еще виды платы в ТОВАРНОЙ строке?
На форуме ФНС есть примеры реализаций для оптовиков, так вот там в Части 3 есть пример с предоплатой и постоплатой (отсрочкой оплаты с частичными погашениями). Исходя из этих примеров оптовик при первой отгрузке клиенту печатает чек с указанием для каждой товарной позиции признака способа расчета «Передача в кредит», далее когда клиент приносит часть оплаты за отгрузку, оптовик печатает чек с выборочными позициями, в котором напротив каждой погашаемой позиции указывается признак способа расчета «Оплата кредита», а в подвале чека помимо графы «Сумма чека наличными» печатается графа «Сумма по чеку постоплатой (кредит)» в которой указывается сумма долга по отгрузке. Так вот вопрос: как это реализовать в рамках операции FNOperation ? Берем документацию от 18.05.2017 Драйвера ККТ 4.13 и видим в разделе для операции FNoperation, указывается реквизит Summ1, когда пытаюсь в Summ1 указать сумму по каждой позиции то при вызове FNCloseCheckEx выдается ошибка что сумма всех оплат меньше итога. Но я же указываю признак «Передача в кредит», в чем ошибка?
(31) В том, что пример на форуме ФНС не имеет ничего общего с реальной жизнью драйвера от Штрих.
(31)
Кстати, где и как указываете?
Совершенно верно. Ошибка будет. Первое: данные действия должны происходить только при использовании ФФД 1.05 или 1.1 На данный момент все используют ФФД 1.0. Второе недостающую сумму, которую вы указывали как предоплату или аванс надо заносить в 15 или 16 (точно не помню) операционный регистр. Но при любых действиях сумма товара и сумма оплат должна совпадать.
(33)
FR.PaymentTypeSign = 6; // 7 — Оплата Кредита
(34)
Фискальный переведен в режим ФФД 1.05 через Таблицу 17. Можно поподробнее про операционные регистры, это в обход команды FNoperation ?
(32)
т.е. начиная с 1 июля все оптовики обязаны прекратить принимать оплаты наличкой и ждать погоды с моря ?
Показать
К сожалению использую ФФД 1.0 и более подробно рассказать не могу.
(37)
Что за чушь? И что мешает работать с наличкой на ФФД 1.0? При чем тут признаки типа оплаты на каждой товарной строке и т.д.?
На ФФД 1.0 признаки способа расчета не работают. Данные теги не передаются в ОФД и так далее в ФНС. Поэтому на ФФД 1.0 авансовые платежи не учитываются.
(39)
По закону можно проводить чеки если оплата полная, т.е. сумма отгрузки равна сумме внесенной наличности (ну или больше, со сдачей). Если же приходит часть суммы наличкой, то как аванс мы ее проводить не можем, т.к. по сути авансом это не является, и предоплата на ФФД 1.0 не работает…какие еще варианты?
(40)
насколько актуальная данная информация? ОФД не принимаю все или все такие какие то принимают?
(42) Все зависит на каком ФФД работает ваш Фискальный накопитель. Насколько мне известно ФН для 1.05 начали изготавливать совсем недавно. У меня 7 касс и на всех стоят ФН с ФФД 1.0
(43) В таблице 17 в графе 17 стоит 2, что согласно вот этому описаниюhttps://forum.shtrih-m-partners.ru/index.php?topic=32170.0 означает ФФД 1.05
(38)
Попробовал передать сумму постоплаты через поле Сумма16, в офд пришел чек, а в подвале сумма указана «Сумма по чеку встречным представлением», попробовал потом через поле Сумма15 — чек вообще не пробивается, ругается на сумму безналичной оплаты…где передавать сумму постоплаты?
(45) Может таки дождаться готовности ВСЕХ участников процесса к работе с ФФД 1.05 и не бежать впереди паровоза?
(44) Это означает настройку кассы работать с этим форматом. ФН — не касса и его работа этой настройкой не регулируется. ФН, как уже было указано, пока все работают только с ФФД-1.0
(46)
Уже разобрался с Суммой15, а насчет дождаться — не получается, частичные платежи массово идут, а в ФФД 1.0 я не нашел способа их проведения
(48)Как со всем разберешься. Опиши опыт в теме. Думаю всем будет интересно.
(48)
И что там было?
(35)
И где сие описано? Или методом научного тыка?
(50)
Не аннулировал предыдущие неудачные попытки продаж
(51)
это написано в Части3 примеров выложенных ФНС на официальном форуме
https://forum.nalog.ru/index.php?showtopic=782331
ссылка:
Выкладываю итоговый результат работы:
чтобы пробить частичную оплату необходимо сначала в ФнОперация передать 6 в качестве признака способа расчета и вызвать ВыпуститьЧекНомер1 передав ему сумму всей накладной, далее при каждом платеже вызывать ФнОперация с указанием 7 в качестве признака способа расчета и ВыпуститьЧек с суммой которую вносит покупатель.
Вроде все согласно метод рекомендациямот фнс, если что-то напутал, поправьте
Показать
(53) 1. Вы отдаете себе отчет, что это только проект и он может еще 100500 раз поменяться?
2. Похоже, дают его тоьлко избранным, в число которых я не вхожу. Еще один повод не закладываться на него.
(55)
К сожалению я долго искал ответ на вопрос как проводить частичные продажи, чтобы они соответствовали 54-ФЗ, т.е. в чеках были полные наименования товаров и прочее, но не нашел ответа даже у самих налоговиков, и единственное что осталось это реализовать ФФД 1.05
Кто знает, как настроить ШТРИХ-М 01Ф, чтобы НДС печатался в каждой строке товара? В таблице 1 задавал Исчисление, Печать налогов
0,1 — только итог
1,1 — ошибка 35h
0,2 — только итог
1,2 — ошибка 35h
(57) Внимательно смотрим таблицу 17
(58) Региональные настройки?
(58) СПАСИБО огромное. В таблице 17 в строке «печать налога в операции» надо поставить 1.
Яков, прошу прощения, я случайно минус поставил, отличная статья
Хелп кто писал самоитоятельно, сие касается авансов т.е. расчета подарочными сертификатами, пришлось при закрытии чека метод CloseCheck() заменить на FNCloseCheckEx()
выглядит теперь сие вот так:
Показать
но в налоговую теперь все стало уходить как оплата по предоплате, т.е. как будто в Summ14 я передаю всю сумму оплаты. хотя там ноль, а вся сумма в Summ4 или Summ1.
ни кто не сталкивался с такой проблемой?
с 01 июля обязательные реквизиты ИНН покупателя и ИНН кассира. Кто-нибудь вносил их в чек? Какой командой или реквизитом это делается?
(63) Если API ещё не допилили, то, думаю, просто как вывод произвольной строки на чек. Хоть в шапку или подвал засунуть)
(63)
ИНН Кассира передается тегом 1203.
Передача тега — после открытия чека
Примерно так:
Показать
ИНН Кассира — как вариант — реквизит справочника Сотрудники (коим кассир является)
А вот ИНН покупателя — он откуда возьмется, при розничной продаже?
(54)
А если мне нужно просто заполнить PaymentTypeSign и PaymentItemSign, нужно ли обязательно заполнять все остальные поля, поскольку они выводятся Командой «Продажа»?
Для чего, вообще, предназначена команда ФНОперация?
(63)
https://its.1c.ru/db/kkt#content:104:hdoc ИНН кассира (тег 1203) не обязательно или как пишет 1С «может присутствовать».
С чего вы решили что ИНН Кассира обязательное поле?
согласно вот этому документу
Подскажите почему ручной расчет налогов не работает, нам нужно ндс по 18/118 пробить и сумма ндс берется только с разницы сделки
например: сумма 100 р. , ндс 5 р. т.к. ставка ндс 18/118
написал код, и не могу понять почему фискальник ругается на некорректные данные
Показать
А в какой момент он ругается-то, на каком операторе? На CheckSubTotal?
(69) вот в этот момент выдает что передаются неверные параметры и возвращает ошибку рез=фр.Sale(); // продажа
(69) почитал на разных источниках не нашел что именно передавать при ручном расчете налогов, а ведь нужно очень при ставке 18/118 передавать наш расчитанный НДС который естественно меньше от общей суммы
https://forum.infostart.ru/forum81/topic176078/ пишут что атол без проблем принимает ставку, а у штриха это вроде как не работает. Но ведь сам то параметр есть включения и отключения ручного расчета налогов
вот тут почитал
КАК ПЕРЕДАТЬ В Z ОТЧЕТ ФАМИЛИЮ КАССИРА
(72)
Насколько я понял, никак — отчеты печатаются только от администратора
(72) Однозначно никак. Действительно, только от админа всё. Ну разве что можно в утилите в рабочих таблицах менять строковое представление, кто есть админ, т.е. обладатель кодов 29 и 30. Это и программно можно сделать.
Использую AddCashIn.Context, функцию РегистрацияПродажи (Документация к штриховскому Кассиру 5)
Кассовый аппарат Штрих RR-01
Функция РегистрцияПродажи( Знач БлокСтрок, Знач СтрокаККМ, Знач Сумма_дрб, Знач Количество_дрб, Знач Секция_инт, Знач Налоги, КодОшибки_инт = 0, Знач Регистрация_Форма = Null, Знач ДопПараметры=Null) Экспорт
Что такое ДопПараметры и для чего их можно использовать?
Задача — передавать в ОФД дополнительные данные, которые должны отображаться в чеке ОФД.
«Доппараметры» подойдут?
Добрый день!
Ни как не могу разобраться с отправкой в кассовый аппарат ШТРИХ-М-02Ф тега 1229 (сумма акциза)
Использую драйвер 4.14.0.772
Судя по документации штриха тег 1229 имеет тип 3 (Тип VLN). Для тегов этого типа необходимо использовать поле TagValueBin (тип строка ) для передачи суммы акциза.
ККМ.Password = ПарольКассира ;
ККМ.TagNumber = 1229 ;
ККМ.TagType = 3 ;
ККМ.TagValueBin = ЗначениеТега ;
ККМ.TagValueLength = 6 ;
ККМ.FNSendTagOperation() ;
и вот возникает вопрос что должно быть в переменной ЗначениеТега.
Попробовал отправлять этот тег через программу «Тест драйвера».
Судя по всему в значение тега в этой программе необходимо указывать строку без разделителей дробной части — для 45.00 — указываем «4500» — из теста драйвера в чек в этом случае попадет 45.00
В логе теста драйвера прописывается следующая информация:
[03.10.2019 10:19:12.369] [00008328] [DEBUG] TDrvFR Set_TagNumber
[03.10.2019 10:19:12.369] [00008328] [DEBUG] TDrvFR Set_TagNumber: 1229
[03.10.2019 10:19:12.369] [00008328] [DEBUG] TDrvFR Set_TagType
[03.10.2019 10:19:12.369] [00008328] [DEBUG] TDrvFR Set_TagType: 3
[03.10.2019 10:19:12.369] [00008328] [DEBUG] TDrvFR Set_TagValueBin
[03.10.2019 10:19:12.369] [00008328] [DEBUG] TDrvFR Set_TagValueBin: ь
[03.10.2019 10:19:12.369] [00008328] [DEBUG] TDrvFR Set_TagValueLength
[03.10.2019 10:19:12.369] [00008328] [DEBUG] TDrvFR Set_TagValueLength: 6
[03.10.2019 10:19:12.369] [00008328] [DEBUG] TDrvFR FNSendTagOperation
Нужна помощь — какая информация должна быть передана в переменной ЗначениеТега, для корректной установки суммы акциза
(76) Сам сейчас занимаюсь этим же вопросом, только для 1227 и 1228; смогу сообщить что-то внятное — отпишусь.
(77)теги 1227 и 1228 имеют тип строка — для них все просто
причем эти теги привязаны к чеку а не к операции в чеке — поэтому ККМ.FNSendTag()
(76)После отправки запроса на суппорт штриха решение нашлось:
«Да, есть ньюанс в драйвере. Попробуйте так:
Driver.TagNumber = 1229;
Driver.TagType = 3;
Driver.TagValueLength = 6;
Driver.TagValueVLN = «4500»;
Driver.FNSendTagOperation();
Т.е. вместо TagValueBin используем TagValueVLN.»