При интеграции торгового оборудования с решениями на платформе 1С разработчики часто сталкиваются со специфическим поведением драйверов ККТ (контрольно-кассовой техники). Особенно много вопросов возникает при переходе на новые форматы фискальных данных (ФФД 1.0.5 и выше) и необходимости разделения номенклатуры на товары и услуги. Типичная проблема, с которой сталкиваются программисты: при попытке оформить чек с типом «Возврат прихода» для услуги кассовый аппарат упорно пробивает обычный «Приход» (продажу).
В рамках данной статьи мы подробно разберем, почему возникает эта ошибка, проанализируем техническую сторону взаимодействия 1С с драйверами торгового оборудования (на примере популярных драйверов АТОЛ) и предоставим готовые шаблоны кода для исправления ситуации.
Перед тем как перейти к программному коду, снимем популярное юридическое заблуждение, которое часто озвучивают на форумах: «Услугу нельзя вернуть физически, так как она уже оказана, а значит, и чека возврата услуги существовать не может». С точки зрения гражданского и налогового законодательства РФ (в частности, 54-ФЗ), это утверждение абсолютно неверно по двум ключевым причинам:
Таким образом, техническая возможность оформления возврата услуги критически важна для любого бизнеса в сфере услуг.
Автор исходного обращения столкнулся с проблемой при использовании устаревшего, но все еще широко распространенного драйвера АТОЛ ДТО 8. Внедрение формата ФФД 1.0.5 потребовало обязательной передачи новых реквизитов: Признак предмета расчета (тег 1212, для услуг значение равно 4) и Признак способа расчета (тег 1214, например, «Полный расчет» со значением 4).
Чтобы реализовать эту поддержку в рамках старой архитектуры ДТО 8, разработчики АТОЛ внедрили механизм логических блоков с использованием методов BeginItem() и EndItem(). Внутри этого блока программист настраивает расширенные свойства позиции чека, включая тип номенклатуры:
Объект.ItemType = 4; // 4 означает "Услуга"
Главная техническая ошибка разработчика заключается в предположении, что блок BeginItem() ... EndItem() самостоятельно осуществляет фискализацию строки на основе глобального типа чека (заданного через CheckType). На самом деле эти методы выполняют исключительно подготовку (буферизацию) параметров позиции в памяти драйвера ККТ.
Для того чтобы позиция была физически записана в фискальный накопитель и распечатана на чековой ленте, сразу после вызова метода EndItem() необходимо вызвать фискализирующий метод. Если мы оформляем продажу, вызывается метод Registration() (или Sale()). Если же мы оформляем возврат, необходимо вызвать метод Return() (или ReturnSale()).
Если пропустить этот шаг и не вызвать специализированный метод регистрации возврата, кассовый аппарат либо выдаст ошибку формата при попытке закрыть чек, либо проигнорирует буферизованные данные и закроет чек по умолчанию как обычную продажу («Приход»).
Разберем по шагам правильный алгоритм реализации возврата услуги с использованием старых методов ДТО 8. Нам потребуется сначала инициализировать чек возврата, затем описать позицию в специальном блоке свойств и принудительно зафиксировать ее через вызов метода Return().
Рассмотрим пример кода на языке 1С:
// 1. Устанавливаем тип чека: 2 - Возврат прихода
Объект.CheckType = 2;
Объект.OpenCheck();
// 2. Открываем блок описания свойств позиции ФФД 1.0.5
Объект.BeginItem();
Объект.Name = "Медицинская услуга: Консультация специалиста";
Объект.Price = 1500.00; // Цена за единицу
Объект.Quantity = 1; // Количество
Объект.Summ = 1500.00; // Общая сумма по строке
// Передаем обязательные теги ФФД 1.0.5
Объект.ItemType = 4; // Признак предмета расчета: 4 - Услуга (тег 1212)
Объект.PaymentMode = 4; // Признак способа расчета: 4 - Полный расчет (тег 1214)
Объект.TaxNumber = 3; // Индекс ставки НДС (например, Без НДС)
// Закрываем блок описания свойств позиции
Объект.EndItem();
// 3. Физически регистрируем позицию как ВОЗВРАТ в ККТ (Критически важный шаг!)
Объект.Return();
// 4. Оформляем оплату (тип оплаты: 0 - Наличные, 1 - Безналичные)
Объект.TypeClose = 1;
Объект.Summ = 1500.00;
Объект.Payment();
// 5. Финализируем и закрываем чек возврата
Объект.CloseCheck();
Именно вызов метода Return() после EndItem() заставляет драйвер ККТ корректно сопоставить переданную услугу с операцией возврата денежных средств.
Использование ДТО 8 на сегодняшний день признано устаревшим. В актуальных версиях платформ 1С и прошивках ККТ применяется современный драйвер **АТОЛ 10-й версии** (компонента AddIn.Fptr10). Архитектура ДТО 10 была полностью переработана, что позволило исключить путаницу с методами регистрации.
В ДТО 10 больше нет разделения на методы Registration() и Return(), а также отсутствуют методы BeginItem() и EndItem(). Логика работы построена по принципу явной установки параметров через универсальный метод setParam() с последующей регистрацией позиции единым методом registerPosition(). Касса самостоятельно определяет тип операции на основе параметров открытого чека.
Проанализируем пример кода для оформления возврата услуги на ДТО 10:
// 1. Указываем тип чека - Возврат прихода (параметр LIBFPTR_RT_SELL_RETURN)
Драйвер.setParam(Драйвер.LIBFPTR_PARAM_RECEIPT_TYPE, Драйвер.LIBFPTR_RT_SELL_RETURN);
Драйвер.openReceipt();
// 2. Задаем базовые реквизиты товарной позиции
Драйвер.setParam(Драйвер.LIBFPTR_PARAM_COMMODITY_NAME, "Услуга по доставке груза");
Драйвер.setParam(Драйвер.LIBFPTR_PARAM_PRICE, 750.00);
Дriver.setParam(Драйвер.LIBFPTR_PARAM_QUANTITY, 1);
Драйвер.setParam(Драйвер.LIBFPTR_PARAM_TAX_TYPE, Драйвер.LIBFPTR_TAX_NO); // Без НДС
// 3. Указываем специфические теги ФФД 1.0.5 / 1.1 / 1.2
// Признак предмета расчета: LIBFPTR_RE_SERVICE = 4 (Услуга, тег 1212)
Драйвер.setParam(Драйвер.LIBFPTR_PARAM_ITEM_TYPE, Драйвер.LIBFPTR_RE_SERVICE);
// Признак способа расчета: LIBFPTR_PM_FULL_PAYMENT = 4 (Полный расчет, тег 1214)
Драйвер.setParam(Драйвер.LIBFPTR_PARAM_PAYMENT_MODE, Драйвер.LIBFPTR_PM_FULL_PAYMENT);
// 4. Регистрируем позицию в чеке (универсальный метод сам выполнит возврат!)
Драйвер.registerPosition();
// 5. Проводим оплату (безналичный расчет)
Драйвер.setParam(Драйвер.LIBFPTR_PARAM_PAYMENT_TYPE, Драйвер.LIBFPTR_PT_ELECTRONIC);
Драйвер.setParam(Драйвер.LIBFPTR_PARAM_PAYMENT_SUM, 750.00);
Драйвер.payment();
// 6. Закрываем чек
Драйвер.closeReceipt();
Как мы видим, в ДТО 10 процесс интеграции стал значительно чище и прозрачнее. Драйвер сам контролирует соответствие признаков чека и его позиций, снижая вероятность ошибок разработчика 1С.
При тестировании разработанных алгоритмов взаимодействия с кассой обязательно следуйте простым правилам:
SignMethodCalculation="4" и SignSubjectCalculation="4").Своевременный переход на использование современных версий драйверов (ДТО 10) и корректная последовательность вызова фискализирующих методов ККТ позволят вам избежать проблем с фискализацией услуг и полностью соответствовать требованиям законодательства.