1. О чём эта статья
С 2014 года на российских платёжных документах часто печатают машиночитаемый QR-код по ГОСТ Р 56042-2014. В этом QR уже зашиты все реквизиты, которые нужны для оплаты: ИНН/КПП и наименование получателя, его банк, БИК, корсчёт, расчётный счёт, назначение и сумма платежа. Если поставщик такой QR нарисовал — бухгалтеру больше не нужно вбивать эти реквизиты руками и не нужно прогонять весь счёт через OCR: достаточно отсканировать или сфотографировать страницу с QR и передать её в обработку.
Обработка «Платёжка ИИ» делает ровно это: отправляет изображение счёта во внешнюю программу распознавания «QR-код Сканер документов», тот находит QR-код и извлекает из него реквизиты по ГОСТу, обработка находит или создаёт контрагента с банковским счётом и открывает заполненную форму нового платёжного поручения. Документ, как и в прошлом сценарии, вы проверяете сами — никаких скрытых проведений.
В статье — сценарий пользователя и подробный разбор реализации.
2. Что в комплекте
Решение состоит из двух частей:
- Внешняя обработка «Платёжка ИИ» — подключается как дополнительная обработка заполнения объекта для документа
ПлатежноеПоручениев 1С:Бухгалтерии 3.0. - Сервис распознавания «ИИ Сканер документов» — Windows-приложение, которое принимает изображение по HTTP, находит на нём QR-код по ГОСТ Р 56042-2014 и возвращает JSON с реквизитами. Скачивается с Инфостарта: //infostart.ru/marketplace/2764572/. Условно-бесплатное.
Главная особенность: один и тот же эндпоинт POST /api/extract_invoice умеет и распознавать обычные счета (OCR), и читать QR по ГОСТу — обработка не знает, что именно происходит на сервере. Для неё это прозрачно.
3. Установка и подключение
- Скачайте архив, сохраните
ПлатёжкаИИ.epfв любую папку. - В 1С:Бухгалтерии 3.0 откройте «Администрирование — Печатные формы, отчёты и обработки — Дополнительные отчёты и обработки».
- Добавьте новую обработку, укажите файл
ПлатёжкаИИ.epf. - Установите и запустите «ИИ Сканер документов» (по умолчанию слушает
http://localhost:5000). - Откройте любой документ «Платёжное поручение» (или список) — в командной панели появится кнопка «Распознать счёт и создать платёжное поручение…».
Регистрация команды — в СведенияОВнешнейОбработке():
ПараметрыРегистрации.Вставить("Вид", "ЗаполнениеОбъекта");
ПараметрыРегистрации.Вставить("Назначение", "Документ.ПлатежноеПоручение");
Команда.Идентификатор = "РаспознатьСчёт";
Команда.Представление = "Распознать счёт и создать платёжное поручение...";
Команда.Использование = "ОткрытиеФормы";
4. Сценарий работы пользователя
-
Нажмите «Распознать счёт и создать платёжное поручение…» — откроется форма обработки.
-
При необходимости поправьте адрес сервиса (по умолчанию
http://localhost:5000). -
Нажмите «Выбрать файл…» и выберите изображение страницы счёта, на которой напечатан QR-код. Это может быть:
- скан со сканера (PDF, JPG, PNG);
- фото со смартфона;
- скриншот PDF, открытого на экране.
Подойдёт файл любого формата, который понимает сервис (PDF/JPG/PNG/DOCX/TXT/RTF/HTML).
-
Нажмите «Распознать счёт». Обработка отправит файл на
/api/extract_invoice. Сервис найдёт QR по ГОСТу и вернёт JSON. Обработка разберёт его и покажет в поле «Данные распознавания (JSON)». Если по ИНН/КПП уже есть контрагент — он подставится в поле «Контрагент». -
Нажмите «Создать / Обновить контрагента» — обработка создаст нового или аккуратно дополнит существующего контрагента (с головным контрагентом, основным банковским счётом и контактной информацией).
-
Выберите вид операции (оплата поставщику / возврат покупателю) и нажмите «Создать платёжное поручение» — откроется форма нового документа
ПлатёжноеПоручениес заполненными реквизитами, суммой и назначением платежа.
Документ не создаётся автоматически — вы всегда видите итоговую форму и принимаете решение.
5. Почему QR по ГОСТу, а не OCR
Если вы видели обычное распознавание счёта через OCR, вы знаете главную боль: точность зависит от качества скана, шрифта, поворота, фона, артефактов печати. Ошибки в одной цифре ИНН или в одной букве наименования — и контрагент не найдётся в базе, придётся править руками.
QR по ГОСТ Р 56042-2014 решает эту проблему в корне:
- Точность близка к 100%. Это машиночитаемый код с контрольной суммой. Если QR считан — данные в нём гарантированно валидны. Не нужно угадывать «это 0 или О».
- Скорость. Сервису не надо прогонять через нейросеть весь лист A4 — он находит QR, декодирует его по стандарту, и всё. Это в разы быстрее OCR.
- Устойчивость к качеству. QR-код по ГОСТу специально печатается крупно, с широкими полями и в нижнем правом углу — чтобы можно было считать даже со смятого листа.
- Полнота данных. В QR зашиты ровно те поля, которые нужны для платёжного поручения: реквизиты получателя, банк, счета, сумма, назначение. Нет шума, нет «лишних» данных, которые пришлось бы разбирать.
Из ограничений — не все поставщики печатают QR по ГОСТу на своих счетах. Если QR на счёте нет — обработка через этот сервис не сработает (для таких случаев остаётся ручной ввод или классическое OCR, если сервис его тоже поддерживает).
6. Архитектура решения
Архитектурно обработка не изменилась — те же два модуля с тем же разделением ответственности:
- Модуль объекта (
ObjectModule.bsl) — серверная логика: поиск и создание контрагента, банка, банковского счёта, формирование значений заполнения для документа. HTTP здесь нет. - Модуль формы (
Forms/Форма/Module.bsl) — клиентская логика: выбор файла, HTTP-запрос, разбор JSON, передача данных на сервер через временное хранилище, открытие формы нового документа.
Это типовой паттерн для управляемых форм: всё, что требует сетевого I/O, идёт с клиента; всё, что пишет в ИБ, идёт через сервер.
7. HTTP-клиент: отправка файла на распознавание
Сервис принимает multipart/form-data точно так же, как и в случае обычного OCR. Код не меняется — тело собирается вручную, потому что у платформы нет встроенного помощника для multipart:
&НаКлиенте
Функция ОтправитьЗапросРаспознавания(АдресСервиса, ИмяФайла, ДвоичныеДанныеФайла)
АдресСервисаБезСлеша = СокрЛП(АдресСервиса);
Если Прав(АдресСервисаБезСлеша, 1) = "/" Тогда
АдресСервисаБезСлеша = Лев(АдресСервисаБезСлеша, СтрДлина(АдресСервисаБезСлеша) - 1);
КонецЕсли;
ЗащищенноеСоединение = Неопределено;
ХостСПортом = АдресСервисаБезСлеша;
Если НРег(Лев(ХостСПортом, 8)) = "https://" Тогда
ХостСПортом = Сред(ХостСПортом, 9);
ЗащищенноеСоединение = Новый ЗащищенноеСоединениеOpenSSL(Неопределено, Неопределено);
ИначеЕсли НРег(Лев(ХостСПортом, 7)) = "http://" Тогда
ХостСПортом = Сред(ХостСПортом, 8);
КонецЕсли;
Хост = ХостСПортом;
Порт = 0;
ПозицияДвоеточия = СтрНайти(ХостСПортом, ":");
Если ПозицияДвоеточия > 0 Тогда
Хост = Лев(ХостСПортом, ПозицияДвоеточия - 1);
Порт = Число(СокрЛП(Сред(ХостСПортом, ПозицияДвоеточия + 1)));
КонецЕсли;
Разделитель = "WmkBoundary" + Строка(Новый УникальныйИдентификатор());
ТелоЗапроса = СобратьТелоMultipart(ДвоичныеДанныеФайла, ИмяФайла, Разделитель);
Соединение = Новый HTTPСоединение(Хост, Порт, , , , 120, ЗащищенноеСоединение);
Запрос = Новый HTTPЗапрос("/api/extract_invoice");
Запрос.Заголовки.Вставить("Content-Type", "multipart/form-data; boundary=" + Разделитель);
Запрос.Заголовки.Вставить("Accept", "application/json");
Запрос.УстановитьТелоИзДвоичныхДанных(ТелоЗапроса);
Ответ = Соединение.ВызватьHTTPМетод("POST", Запрос);
// ...
КонецФункции
Поддерживаются http:// и https:// (через ЗащищенноеСоединениеOpenSSL), хвостовой / в адресе отрезается, таймаут — 120 секунд (с запасом на медленные сканы крупных PDF). Само тело multipart собирается по RFC 7578 — четыре кусочка через СоединитьДвоичныеДанные:
ЧастьЗаголовок = "--" + Разделитель + ПереводСтроки
+ "Content-Disposition: form-data; name=""file""; filename=""" + ИмяФайлаБезПути + """" + ПереводСтроки
+ "Content-Type: application/octet-stream" + ПереводСтроки + ПереводСтроки;
ЧастьРазделитель = "--" + Разделитель + "--" + ПереводСтроки;
Части = Новый Массив;
Части.Добавить(ПолучитьДвоичныеДанныеИзСтроки(ЧастьЗаголовок, КодировкаТекста.UTF8));
Части.Добавить(ДвоичныеДанныеФайла);
Части.Добавить(ПолучитьДвоичныеДанныеИзСтроки(Символы.ВК + Символы.ПС, КодировкаТекста.UTF8));
Части.Добавить(ПолучитьДвоичныеДанныеИзСтроки(ЧастьРазделитель, КодировкаТекста.UTF8));
Возврат СоединитьДвоичныеДанные(Части);
Граница формируется как WmkBoundary + уникальный идентификатор — без спецсимволов, которые могли бы сломать парсинг на сервере. Имя файла очищается от пути, чтобы сервер не получил C:\Users\...\scan.jpg в filename.
8. Разбор ответа и нормализация данных
В QR по ГОСТу кодируются все нужные реквизиты платежа, поэтому ответ сервиса для QR-сценария содержит те же поля, что и для обычного OCR — но блок items[] пустой (построчного перечня товаров в QR нет, есть только общая сумма):
{
"success": true,
"file_name": "scan_001.jpg",
"data": {
"organization_name": "ООО Ромашка",
"inn": "7701234567",
"kpp": "770101001",
"address": "101000, г. Москва, ул. Мясницкая, д. 1",
"bank_name": "ПАО СБЕРБАНК",
"bik": "044525225",
"checking_account": "40702810038000123456",
"corr_account": "30101810400000000225",
"invoice_number": "СЧ-42",
"invoice_date": "2026-09-10",
"has_vat": true,
"total_amount": 118000.0,
"total_vat": 18000.0,
"total_with_vat": 118000.0,
"vat_rate": "20",
"currency": "643",
"items": []
}
}
Разбор — через ЧтениеJSON с приведением к Соответствие:
Если ТипЗнч(Данные) = Тип("Соответствие") Тогда
Requisites = Данные.Получить("requisites");
Если ТипЗнч(Requisites) = Тип("Соответствие") Тогда
Для Каждого КлючЗначение Из Requisites Цикл
Если Данные.Получить(КлючЗначение.Ключ) = Неопределено Тогда
Данные.Вставить(КлючЗначение.Ключ, КлючЗначение.Значение);
КонецЕсли;
КонецЦикла;
КонецЕсли;
Результат.Данные = Данные;
КонецЕсли;
Если ТипЗнч(Данные) = Тип("Соответствие") Тогда
Requisites = Данные.Получить("requisites");
Если ТипЗнч(Requisites) = Тип("Соответствие") Тогда
Для Каждого КлючЗначение Из Requisites Цикл
Если Данные.Получить(КлючЗначение.Ключ) = Неопределено Тогда
Данные.Вставить(КлючЗначение.Ключ, КлючЗначение.Значение);
КонецЕсли;
КонецЦикла;
КонецЕсли;
Результат.Данные = Данные;
КонецЕсли;
Все дальнейшие серверные методы работают уже не с JSON, а с этим9. Поиск и создание контрагента, банка, банковского счёта
Все три сущности живут в одной транзакции. На вход — Соответствие с реквизитами, на выход — структура с результатом:
Функция СоздатьКонтрагента(Данные) Экспорт
Результат = НовыйРезультатСозданияКонтрагента();
НачатьТранзакцию();
Попытка
НовыйОбъект = Справочники.Контрагенты.СоздатьЭлемент();
ЗаполнитьРеквизитыКонтрагентаИзДанных(НовыйОбъект, Данные);
УстановитьРеквизитБезопасно(НовыйОбъект, "ГоловнойКонтрагент", НовыйОбъект.Ссылка);
НовыйОбъект.Записать();
Результат.Контрагент = НовыйОбъект.Ссылка;
РезультатСчета = СоздатьОбновитьБанковскийСчет(Результат.Контрагент, Данные);
Результат.СчетНовый = РезультатСчета.ЭтоНовый;
Результат.СчетОшибка = РезультатСчета.Ошибка;
Результат.БанкСоздан = РезультатСчета.БанкСоздан;
Результат.БанковскийСчет = РезультатСчета.БанковскийСчет;
Если РезультатСчета.БанковскийСчет <> Неопределено
И НЕ РезультатСчета.БанковскийСчет.Пустая() Тогда
Установлен = УстановитьРеквизитБезопасно(НовыйОбъект, "ОсновнойБанковскийСчет",
РезультатСчета.БанковскийСчет);
Если Установлен Тогда
НовыйОбъект.Записать();
КонецЕсли;
КонецЕсли;
ЗаписатьКонтактнуюИнформациюКонтрагента(Результат.Контрагент, Данные);
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
Результат.Ошибка = СтрШаблон(НСтр("ru = 'Не удалось создать контрагента: %1'"),
ИнформацияОбОшибке().Описание);
Возврат Результат;
КонецПопытки;
Возврат Результат;
КонецФункции
ОбновитьКонтрагента устроен так же — вместо СоздатьЭлемент() идёт ПолучитьОбъект(), и запись выполняется только если БылиИзменения. Это ключевая гигиена: нельзя перетирать ручные правки бухгалтера.
Банк ищется стандартно по БИК — Справочники.Банки.НайтиПоКоду(БИК). Если не нашёлся — создаётся минимальная карточка. Банковский счёт ищется по паре Владелец + НомерСчета, не нашёлся — создаётся с указанием банка и корр. счёта. Контактная информация пишется в регистр сведений КонтактнаяИнформация через НаборЗаписей.Отбор, что обновляет существующие записи без дублей.
10. Формирование значений заполнения для платёжного поручения
Мы не создаём документ сами — возвращаем структуру ЗначенияЗаполнения, и форма открывается через ОткрытьФорму с этими значениями. Так пользователь видит итоговый документ и сам решает, сохранять ли его.
Процедура ДополнитьЗаполнение(Заполнение, МетаданныеДокумента, ИмяРеквизита, Значение)
Если НЕ ЗначениеЗаполнено(Значение) Тогда
Возврат;
КонецЕсли;
Если НЕ ЕстьРеквизит(МетаданныеДокумента, ИмяРеквизита) Тогда
Возврат;
КонецЕсли;
Заполнение.Вставить(ИмяРеквизита, Значение);
КонецПроцедуры
Заполняется основной набор: дата, организация, контрагент, сумма, валюта, оба счёта (организации и контрагента), назначение платежа, вид платежа (Электронно), очередность (5), ставка и сумма НДС, вид операции, номер счёта на оплату.
Назначение платежа собирается по банковским правилам: «Оплата по счёту №… от …», и при наличии НДС — строка «Сумма NNN р. в т.ч. НДС (20%) NNN р.»:
Функция СформироватьНазначениеПлатежа(Данные)
НомерСчета = СокрЛП(Строка(Данные.Получить("invoice_number")));
ДатаСчета = СокрЛП(Строка(Данные.Получить("invoice_date")));
Части = Новый Массив;
Если ЗначениеЗаполнено(НомерСчета) Тогда
Шапка = "Оплата по счёту №" + НомерСчета;
Если ЗначениеЗаполнено(ДатаСчета) Тогда
Шапка = Шапка + " от " + ДатаСчета;
КонецЕсли;
Части.Добавить(Шапка);
Иначе
Части.Добавить("Оплата по счёту");
КонецЕсли;
// ...
Возврат СтрСоединить(Части, " ");
КонецФункции
В QR-сценарии блок items[] обычно пустой, поэтому перечень позиций через ; не строится — это и логично: в QR нет построчной детализации, есть только общая сумма. Если сервис вернёт непустой items[] (например, для гибридного сценария — QR + распознанные позиции), обработка их учтёт автоматически.
11. Безопасная работа с метаданными
Обработка рассчитана на разные конфигурации (БП, УТ, ERP, КА), где имена и состав реквизитов отличаются. Поэтому в коде два помощника — УстановитьРеквизитБезопасно и УстановитьРеквизитБезопасноЛюбоеИз:
Функция УстановитьРеквизитБезопасно(Объект, ИмяРеквизита, Значение)
Если НЕ ЗначениеЗаполнено(Значение) Тогда
Возврат Ложь;
КонецЕсли;
МетаданныеОбъекта = Объект.Метаданные();
Если НЕ ЕстьРеквизит(МетаданныеОбъекта, ИмяРеквизита) Тогда
Возврат Ложь;
КонецЕсли;
Если Объект[ИмяРеквизита] = Значение Тогда
Возврат Ложь;
КонецЕсли;
Объект[ИмяРеквизита] = Значение;
Возврат Истина;
КонецФункции
Функция УстановитьРеквизитБезопасноЛюбоеИз(Объект, Значение, Имя1, Имя2 = "", Имя3 = "")
Если НЕ ЗначениеЗаполнено(Значение) Тогда
Возврат Ложь;
КонецЕсли;
МетаданныеОбъекта = Объект.Метаданные();
ИменаРеквизитов = Новый Массив;
ИменаРеквизитов.Добавить(Имя1);
Если ЗначениеЗаполнено(Имя2) Тогда
ИменаРеквизитов.Добавить(Имя2);
КонецЕсли;
Если ЗначениеЗаполнено(Имя3) Тогда
ИменаРеквизитов.Добавить(Имя3);
КонецЕсли;
Для Каждого ИмяРеквизита Из ИменаРеквизитов Цикл
Если ЕстьРеквизит(МетаданныеОбъекта, ИмяРеквизита) Тогда
Возврат УстановитьРеквизитБезопасно(Объект, ИмяРеквизита, Значение);
КонецЕсли;
КонецЦикла;
Возврат Ложь;
КонецФункции
Логика простая: перед присвоением проверить, что такой реквизит вообще есть; не присваивать пустые значения; не трогать реквизит, если значение не менялось. Поиск идёт и по обычным реквизитам, и по стандартным:
Функция ЕстьРеквизит(МетаданныеОбъекта, ИмяРеквизита)
Если МетаданныеОбъекта.Реквизиты.Найти(ИмяРеквизита) <> Неопределено Тогда
Возврат Истина;
КонецЕсли;
Для Каждого Описание Из МетаданныеОбъекта.СтандартныеРеквизиты Цикл
Если Описание.Имя = ИмяРеквизита Тогда
Возврат Истина;
КонецЕсли;
КонецЦикла;
Возврат Ложь;
КонецФункции
Такой подход — ключ к переносимости обработки между конфигурациями без форка кода.
12. Ограничения и планы
- Не все счета содержат QR по ГОСТу. Если поставщик не напечатал машиночитаемый код — обработка не поможет, нужен ручной ввод или обычное OCR (если сервис его поддерживает).
- QR должен читаться. Заклеен штампом, обрезан, загрязнён — сервис не сможет декодировать. Следите за качеством сканов.
- В QR нет построчной детализации по товарам/услугам — только общая сумма и назначение. Если вам нужна расшифровка по позициям, её придётся добавлять руками.
- Только платёжное поручение. Акты, накладные, счета-фактуры — другие документы, для них нужны другие формы и другие QR-форматы.
- Совместимость: платформа 8.3.20+ / 8.5.x, режим управляемых форм.
В планах: добавить drag-and-drop файла в форму, кешировать распознанные данные, поддержать пакетную загрузку счетов.
13. Полезные ссылки
- Сервис распознавания «ИИ Сканер документов»: //infostart.ru/marketplace/2764572/
- ГОСТ Р 56042-2014 — стандарт машиночитаемого QR-кода на платёжных документах.
- Документация по
HTTPСоединение/HTTPЗапрос: https://its.1c.ru/db/v8320doc#bookmark:dev:TI000000358 - Документация по
ЧтениеJSON/ПрочитатьJSON: https://its.1c.ru/db/v8320doc#bookmark:dev:TI000000287
Проверено на следующих конфигурациях и релизах:
- Бухгалтерия предприятия, редакция 3.0, релизы 3.0.206.19
Вступайте в нашу телеграмм-группу Инфостарт