При интеграции систем дистанционного банковского обслуживания или обработке реестров платежей перед разработчиком часто встает задача массовой печати и фискализации чеков на кассовом аппарате (ККТ). Стандартные механизмы конфигураций 1С, как правило, жестко привязаны к проведенным документам системы, таким как ПоступлениеНаРасчетныйСчет или ПриходныйКассовыйОрдер.
Создание сотен фиктивных документов в базе 1С:Бухгалтерия только ради фискализации — крайне неэффективное решение. Это приводит к раздуванию базы данных, путанице в регистрах учета и необходимости последующего удаления этих документов. В данной статье мы подробно разберем, как реализовать прямую фискализацию реестров на ККМ (на примере АТОЛ 55Ф) без создания документов в базе данных.
Рассмотрим стандартную ситуацию: ежедневно в компанию поступает реестр из банка, содержащий 200–250 оплат от физических лиц. Если программно создавать документы под каждую транзакцию:
Выясним, как обойти это ограничение и отправить данные напрямую на фискальный накопитель (ФН) и в ОФД.
Самый надежный, быстрый и гибкий способ — это прямое взаимодействие с драйвером ККТ АТОЛ 10-й версии. Современные драйверы АТОЛ поддерживают работу с JSON-заданиями. Вместо пошаговой отправки низкоуровневых команд (открытие чека, добавление позиции, оплата, закрытие чека) мы можем передать драйверу один структурированный JSON-пакет, содержащий всю информацию о чеке.
Рассмотрим по шагам, как реализовать этот метод в обработке 1С:
Для начала нам необходимо загрузить Native-компоненту драйвера АТОЛ. Ее можно хранить непосредственно в макете нашей внешней обработки.
// Попытка подключения компоненты из макета обработки
Если Не ПодключитьВнешнююКомпоненту("ОбщийМакет.ДрайверАТОЛККТ10", "Atol", ТипВнешнейКомпоненты.Native) Тогда
// Если в макете нет, пробуем загрузить системную компоненту
Если Не ПодключитьВнешнююКомпоненту("AddIn.Atol.Fptr") Тогда
ВызватьИсключение "Не удалось подключить драйвер АТОЛ ККТ 10! Проверьте установку локальных драйверов на ПК.";
КонецЕсли;
КонецЕсли;
// Создание объекта драйвера
Драйвер = Новый ("AddIn.Atol.Fptr");
Установим параметры соединения с кассой. Это может быть USB, COM-порт или сетевое IP-подключение.
// Настройка параметров подключения (пример для USB-подключения)
Драйвер.setParam(Драйвер.LIBFPTR_PARAM_COMM_CHANNEL, Драйвер.LIBFPTR_PORT_USB);
Драйвер.open();
Если Не Драйвер.isOpened() Тогда
ВызватьИсключение "Касса не подключена или занята другим процессом!";
КонецЕсли;
Сформируем JSON-структуру чека. Важно учесть требования ФЗ-54. Поскольку оплаты пришли по банку, покупатель отсутствует физически. Мы обязаны отправить электронный чек, указав контакты покупателя (Email или телефон) в теге 1008.
// Формируем структуру JSON-задания чека продажи (sell)
ЗаданиеСтруктура = Новый Структура;
ЗаданиеСтруктура.Вставить("type", "sell");
ЗаданиеСтруктура.Вставить("electronically", Истина); // Электронный чек без печати на бумаге
ЗаданиеСтруктура.Вставить("taxationType", "osn"); // Система налогообложения (например, ОСН)
// Информация о клиенте (Тег 1008)
КлиентИнфо = Новый Структура;
КлиентИнфо.Вставить("emailOrPhone", СтрокаРеестра.КонтактыКлиента); // Например: "client@mail.ru" или "+79991112233"
ЗаданиеСтруктура.Вставить("clientInfo", КлиентИнфо);
// Позиции чека
МассивПозиций = Новый Массив;
Позиция = Новый Структура;
Позиция.Вставить("type", "position");
Позиция.Вставить("name", "Оплата по договору № " + СтрокаРеестра.НомерДоговора);
Позиция.Вставить("price", СтрокаРеестра.Сумма);
Позиция.Вставить("quantity", 1);
Позиция.Вставить("amount", СтрокаРеестра.Сумма);
Позиция.Вставить("department", 1);
Позиция.Вставить("paymentMethod", "fullPayment"); // Полный расчет
Позиция.Вставить("paymentObject", "service"); // Услуга
// Налоговая ставка
НалогДетали = Новый Структура;
НалогДетали.Вставить("type", "vat20"); // НДС 20%
Позиция.Вставить("tax", НалогДетали);
МассивПозиций.Добавить(Позиция);
ЗаданиеСтруктура.Вставить("items", МассивПозиций);
// Способ оплаты (Безналичные / Электронные)
МассивОплат = Новый Массив;
Оплата = Новый Структура;
Оплата.Вставить("type", "electronically");
Оплата.Вставить("sum", СтрокаРеестра.Сумма);
МассивОплат.Добавить(Оплата);
ЗаданиеСтруктура.Вставить("payments", МассивОплат);
// Преобразуем структуру 1С в JSON-строку
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, ЗаданиеСтруктура);
JSONСтрока = ЗаписьJSON.Закрыть();
Вызовем метод processJson, который передаст задание в ККТ АТОЛ:
Драйвер.setParam(Драйвер.LIBFPTR_PARAM_JSON_DATA, JSONСтрока);
Если Драйвер.processJson() <> 0 Тогда
КодОшибки = Драйвер.getParamInt(Драйвер.LIBFPTR_PARAM_ERROR_CODE);
ОписаниеОшибки = Драйвер.getParamString(Драйвер.LIBFPTR_PARAM_ERROR_DESCRIPTION);
Сообщить("Ошибка фискализации: " + ОписаниеОшибки + " (Код: " + КодОшибки + ")");
Иначе
// Получаем результат работы
РезультатJSON = Драйвер.getParamString(Драйвер.LIBFPTR_PARAM_JSON_DATA);
Сообщить("Чек успешно пробит! Результат ККТ: " + РезультатJSON);
КонецЕсли;
После завершения цикла обработки реестра обязательно закройте соединение с драйвером с помощью вызова метода Драйвер.close().
Проанализируем ситуацию, когда политика безопасности или требования ИТ-департамента запрещают прямое подключение внешних компонент в обход стандартной подсистемы БПО 1С. В этом случае мы можем использовать функции интеграции БПО без создания реальных документов базы данных.
В БПО фискализация происходит посредством передачи подготовленного XML-пакета, формат которого стандартизирован (структура CheckPackage). Мы можем программно собрать данный XML-пакет по данным нашей таблицы реестра и передать его менеджеру оборудования.
Разберем этот алгоритм по шагам:
Для отправки команд нам потребуется ссылка на элемент справочника подключаемого оборудования ККТ.
// Находим подключенное устройство (кассу АТОЛ 55Ф)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗРЕШЕННЫЕ ПЕРВЫЕ 1
| ПодключаемоеОборудование.Ссылка КАК Ссылка
|ИЗ
| Справочник.ПодключаемоеОборудование КАК ПодключаемоеОборудование
|ГДЕ
| НЕ ПодключаемоеОборудование.ПометкаУдаления
| И ПодключаемоеОборудование.УстройствоАктивно";
РезультатЗапроса = Запрос.Выполнить();
Если РезультатЗапроса.Пустой() Тогда
ВызватьИсключение "Активное подключаемое оборудование ККТ не найдено!";
КонецЕсли;
Выборка = РезультатЗапроса.Выбрать();
Выборка.Следующий();
ИДУстройства = Выборка.Ссылка;
Сформируем строку XML вручную через объект ЗаписьXML или с использованием сериализатора XDTO.
ЗаписьXML = Новый ЗаписьXML;
ПараметрыЗаписиXML = Новый ПараметрыЗаписиXML("UTF-8", "1.0", Ложь);
ЗаписьXML.УстановитьСтроку(ПараметрыЗаписиXML);
ЗаписьXML.ЗаписатьОбъявлениеXML();
ЗаписьXML.ЗаписатьНачалоЭлемента("CheckPackage");
// Шапка чека
ЗаписьXML.ЗаписатьНачалоЭлемента("Parameters");
ЗаписьXML.ЗаписатьАтрибут("CashierName", "Администратор");
ЗаписьXML.ЗаписатьАтрибут("CustomerEmail", СтрокаРеестра.Email);
ЗаписьXML.ЗаписатьАтрибут("TaxVariant", "1"); // Например, УСН доход
ЗаписьXML.ЗаписатьКонецЭлемента(); // Parameters
// Позиции чека
ЗаписьXML.ЗаписатьНачалоЭлемента("Positions");
ЗаписьXML.ЗаписатьНачалоЭлемента("FiscalItem");
ЗаписьXML.ЗаписатьАтрибут("Name", "Оплата услуг по реестру");
ЗаписьXML.ЗаписатьАтрибут("Quantity", "1");
ЗаписьXML.ЗаписатьАтрибут("Price", Строка(СтрокаРеестра.Сумма));
ЗаписьXML.ЗаписатьАтрибут("Amount", Строка(СтрокаРеестра.Сумма));
ЗаписьXML.ЗаписатьАтрибут("TaxSide", "0"); // Без НДС или соответствующая ставка
ЗаписьXML.ЗаписатьАтрибут("PaymentMethod", "4"); // Полный расчет
ЗаписьXML.ЗаписатьАтрибут("PaymentSubject", "4"); // Услуга
ЗаписьXML.ЗаписатьКонецЭлемента(); // FiscalItem
ЗаписьXML.ЗаписатьКонецЭлемента(); // Positions
// Оплата чека
ЗаписьXML.ЗаписатьНачалоЭлемента("Payments");
ЗаписьXML.ЗаписатьНачалоЭлемента("Payment");
ЗаписьXML.ЗаписатьАтрибут("Type", "1"); // Безналичная оплата
ЗаписьXML.ЗаписатьАтрибут("Amount", Строка(СтрокаРеестра.Сумма));
ЗаписьXML.ЗаписатьКонецЭлемента(); // Payment
ЗаписьXML.ЗаписатьКонецЭлемента(); // Payments
ЗаписьXML.ЗаписатьКонецЭлемента(); // CheckPackage
XMLСтрокаПакета = ЗаписьXML.Закрыть();
Для отправки пакета на ККТ используем стандартные методы БПО клиентской части:
ПараметрыОперации = Новый Структура;
ПараметрыОперации.Вставить("Электронно", Истина);
ПараметрыОперации.Вставить("АдресПолучателя", СтрокаРеестра.Email);
Оповещение = Новый ОписаниеОповещения("ПослеФискализацииЧека", ЭтотОбъект);
МенеджерОборудованияКлиент.НачатьФискализациюЧекаНаФискальномУстройстве(
Оповещение,
УникальныйИдентификатор,
ИДУстройства,
XMLСтрокаПакета,
ПараметрыОперации
);
Преимущество этого метода в том, что все настройки портов, скоростей и параметров подключения ККТ берутся из стандартного справочника оборудования 1С. Вам не нужно зашивать IP-адреса и COM-порты в код обработки.
Рассмотрим современную альтернативу для клиент-серверных баз 1С (особенно если сервер 1С находится в облаке или в другом ЦОД, а ККТ подключена к локальному компьютеру бухгалтера). Прямое подключение COM/Native-компоненты на сервере в этом случае невозможно.
Для решения этой проблемы компания АТОЛ поставляет встроенный Web-сервер (входит в дистрибутив ДККТ 10). Web-сервер запускается как локальная служба на ПК, к которому подключена касса АТОЛ 55Ф (по умолчанию используется порт 16730).
В этом сценарии 1С выступает в роли обычного HTTP-клиента. Алгоритм действий выглядит так:
HTTPСоединение мы отправляем POST-запрос на адрес локального веб-сервера.Посмотрим на пример отправки запроса к Web-серверу АТОЛ в коде 1С:
// Создаем HTTP-соединение с компьютером, где запущена служба АТОЛ
Соединение = Новый HTTPСоединение("localhost", 16730);
// Формируем POST-запрос
Запрос = Новый HTTPЗапрос("/api/v1/requests");
Запрос.Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");
Запрос.УстановитьТелоИзСтроки(JSONСтрока, КодировкаТекста.UTF8);
Попытка
Ответ = Соединение.ОтправитьДляОбработки(Запрос);
Если Ответ.КодСостояния = 201 Тогда // Создано
Сообщить("Задание успешно отправлено на Web-сервер ККТ!");
Иначе
Сообщить("Ошибка Web-сервера. Код ответа: " + Ответ.КодСостояния);
КонецЕсли;
Исключение
Сообщить("Не удалось связаться с Web-сервером ККТ: " + ОписаниеОшибки());
КонецПопытки;
При пакетной фискализации реестров платежей из банков обязательно учтите следующие требования законодательства, чтобы уберечь компанию от крупных штрафов налоговой инспекции:
paymentMethod = "payment" (оплата кредита). Если это предоплата — "advance" (аванс) или "prepayment" (предоплата).Мы подробно разобрали три эффективных способа фискализации реестров платежей на оборудовании АТОЛ без захламления базы данных лишними документами. Выберите наиболее подходящий вариант под архитектуру вашей системы 1С и приступайте к реализации!