ИИ Сканер документов предназначен для автоматизации этого процесса. Программа функционирует как локальный сервис, принимающий на вход файл документа и возвращающий структурированные данные в формате JSON. Приложение разворачивается на рабочей станции пользователя или выделенном сервере, что обеспечивает обработку данных без передачи во внешние контуры при использовании локальных моделей.
В настоящей статье рассматривается практическая реализация интеграции между платформой 1С:Предприятие и приложением. Описываются механизмы HTTP-взаимодействия, форматы данных, методы обработки ответов и сценарии использования в различных типах документов.
Функциональные возможности приложения
Приложение реализует следующие функции:
Поддерживаемые типы документов:
-
Счета на оплату — извлечение реквизитов организаций, товарных позиций, стоимостных показателей, ставок НДС
-
Реквизиты организаций — ИНН, КПП, ОГРН, банковские реквизиты
-
Анкеты — персональные данные, паспортная информация, контактные данные
-
Паспорта граждан РФ — серия, номер, дата выдачи, код подразделения
-
Водительские удостоверения — номер, дата выдачи
-
Дипломы — квалификация, специальность, номер бланка
-
Патенты — данные иностранных граждан
-
Электронные трудовые книжки (ЭТК) — перечень мест работы с периодами
Поддерживаемые форматы файлов:
-
Графические форматы: JPEG, PNG, BMP, WebP, TIFF
-
Документы: PDF, DOCX, TXT, RTF, HTML
Интеграционные возможности:
-
REST API на порту 5000
-
Поддержка multipart/form-data для передачи файлов
-
Возврат результатов в формате JSON
-
Работа с несколькими LLM-провайдерами (OpenAI, GigaChat, LM Studio, llama.cpp)
Архитектура взаимодействия
Взаимодействие между 1С и приложением строится по схеме клиент-сервер с использованием HTTP-протокола:
-
1С выступает в роли HTTP-клиента
-
Приложение предоставляет REST API эндпоинты
-
Передача файлов осуществляется методом POST с типом содержимого multipart/form-data
-
Результат возвращается в формате JSON
Эндпоинты API:
| Тип документа | Эндпоинт | Метод |
|---|---|---|
| Счет на оплату | /api/extract_invoice |
POST |
| Реквизиты компании | /api/extract_company |
POST |
| Анкета | /api/extract_anketa |
POST |
| Паспорт | /api/extract_passport |
POST |
| Водительское удостоверение | /api/extract_driver_license |
POST |
| Диплом | /api/extract_diploma |
POST |
| Патент | /api/extract_patent |
POST |
| ЭТК | /api/extract_etk |
POST |
Все эндпоинты принимают поле file в multipart/form-data и возвращают JSON следующей структуры:
{ "success": true, "data": { // Структура данных зависит от типа документа }, "file_name": "document.pdf" }
Реализация HTTP-клиента в 1С
Создание HTTP-соединения
Для установки соединения с приложением используется объект HTTPСоединение. Параметры подключения задаются при инициализации модуля.
// Модуль ИнтеграцияРаспознавание
Перем Хост, Порт;
Процедура Инициализация(Знач АдресСервера = "localhost", Знач ПортСервера = 5000) Экспорт
Хост = АдресСервера;
Порт = ПортСервера;
КонецПроцедуры
Функция СоздатьСоединение() Экспорт
Таймаут = 120; // Время ожидания ответа в секундах
Возврат Новый HTTPСоединение(Хост, Порт, , , , Таймаут);
КонецФункции
Формирование и отправка запроса
Для передачи файла используется метод POST с multipart/form-data. Тело запроса формируется в соответствии со спецификацией RFC 7578.
Функция ОтправитьФайл(Эндпоинт, ПутьКФайлу) Экспорт
Соединение = СоздатьСоединение();
Запрос = Новый HTTPЗапрос(Эндпоинт);
// Загрузка файла в двоичные данные
ДанныеФайла = Новый ДвоичныеДанные(ПутьКФайлу);
ИмяФайла = Новый Файл(ПутьКФайлу).Имя;
// Генерация границы для multipart
Граница = "----WebKitFormBoundary" + Строка(Новый УникальныйИдентификатор());
Заголовки = Новый Соответствие();
Заголовки.Вставить("Content-Type", "multipart/form-data; boundary=" + Граница);
Запрос.Заголовки = Заголовки;
// Формирование тела запроса
СтрокаТела = "--" + Граница + Символы.ПС;
СтрокаТела = СтрокаТела + "Content-Disposition: form-data; name=""file""; filename=""" + ИмяФайла + """" + Символы.ПС;
СтрокаТела = СтрокаТела + "Content-Type: application/octet-stream" + Символы.ПС + Символы.ПС;
// Кодирование файла в Base64
Base64Строка = Base64СтрокаИзДвоичныхДанных(ДанныеФайла);
СтрокаТела = СтрокаТела + Base64Строка + Символы.ПС;
СтрокаТела = СтрокаТела + "--" + Граница + "--";
Запрос.УстановитьТелоИзСтроки(СтрокаТела, КодировкаТекста.UTF8);
Попытка
Ответ = Соединение.ОтправитьДляОбработки(Запрос);
Если Ответ.КодСостояния = 200 Тогда
Возврат ОбработатьОтвет(Ответ);
Иначе
ЗаписьЖурналаРегистрации("Распознавание.Ошибка", УровеньЖурналаРегистрации.Ошибка, , ,
"Код состояния: " + Ответ.КодСостояния);
Возврат Неопределено;
КонецЕсли;
Исключение
ЗаписьЖурналаРегистрации("Распознавание.Ошибка", УровеньЖурналаРегистрации.Ошибка, , ,
"Ошибка соединения: " + ОписаниеОшибки());
Возврат Неопределено;
КонецПопытки;
КонецФункции
Обработка JSON-ответа
Ответ приложения представляет собой JSON-строку, которая десериализуется в структуру 1С.
Функция ОбработатьОтвет(Ответ) Экспорт
ТекстОтвета = Ответ.ПолучитьТелоКакСтроку();
Чтение = Новый ЧтениеJSON;
Чтение.УстановитьСтроку(ТекстОтвета);
Результат = ПрочитатьJSON(Чтение);
Чтение.Закрыть();
Если Результат.Свойство("success") И Результат.success = Истина Тогда
Если Результат.Свойство("data") Тогда
Возврат Результат.data;
КонецЕсли;
Иначе
Если Результат.Свойство("error") Тогда
ВызватьИсключение "Ошибка распознавания: " + Результат.error;
КонецЕсли;
КонецЕсли;
Возврат Неопределено;
КонецФункции
Функции для различных типов документов
Для каждого типа документа реализуется отдельная функция-обертка, вызывающая соответствующий эндпоинт.
Функция РаспознатьСчет(ПутьКФайлу) Экспорт
Возврат ОтправитьФайл("/api/extract_invoice", ПутьКФайлу);
КонецФункции
Функция РаспознатьКомпанию(ПутьКФайлу) Экспорт
Возврат ОтправитьФайл("/api/extract_company", ПутьКФайлу);
КонецФункции
Функция РаспознатьАнкету(ПутьКФайлу) Экспорт
Возврат ОтправитьФайл("/api/extract_anketa", ПутьКФайлу);
КонецФункции
Функция РаспознатьПаспорт(ПутьКФайлу) Экспорт
Возврат ОтправитьФайл("/api/extract_passport", ПутьКФайлу);
КонецФункции
Функция РаспознатьВодительскоеУдостоверение(ПутьКФайлу) Экспорт
Возврат ОтправитьФайл("/api/extract_driver_license", ПутьКФайлу);
КонецФункции
Функция РаспознатьДиплом(ПутьКФайлу) Экспорт
Возврат ОтправитьФайл("/api/extract_diploma", ПутьКФайлу);
КонецФункции
Функция РаспознатьПатент(ПутьКФайлу) Экспорт
Возврат ОтправитьФайл("/api/extract_patent", ПутьКФайлу);
КонецФункции
Функция РаспознатьЭТК(ПутьКФайлу) Экспорт
Возврат ОтправитьФайл("/api/extract_etk", ПутьКФайлу);
КонецФункции
Практические сценарии использования
1. Заполнение документа "Счет на оплату"
Функция обработки счета извлекает реквизиты контрагента, номер, дату, суммы и товарные позиции.
Процедура ЗагрузитьСчетИзФайла(Команда)
Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
Диалог.Заголовок = "Выберите файл счета";
Диалог.Фильтр = "PDF (*.pdf)|*.pdf|Изображения (*.jpg;*.png)|*.jpg;*.png|Word (*.docx)|*.docx";
Если Не Диалог.Выбрать() Тогда
Возврат;
КонецЕсли;
ДанныеСчета = РаспознатьСчет(Диалог.ПолноеИмяФайла);
Если ДанныеСчета = Неопределено Тогда
Возврат;
КонецЕсли;
ЗаполнитьРеквизитыСчета(ДанныеСчета);
КонецПроцедуры
Процедура ЗаполнитьРеквизитыСчета(Данные)
// Заполнение реквизитов контрагента
Если Данные.Свойство("requisites") Тогда
Реквизиты = Данные.requisites;
Если Реквизиты.Свойство("inn") И Реквизиты.inn <> "" Тогда
Контрагент = Справочники.Контрагенты.НайтиПоРеквизиту("ИНН", Реквизиты.inn);
Если Контрагент <> Неопределено Тогда
Объект.Контрагент = Контрагент;
КонецЕсли;
КонецЕсли;
КонецЕсли;
// Заполнение основных реквизитов
Если Данные.Свойство("invoice_number") Тогда
Объект.НомерСчета = Данные.invoice_number;
КонецЕсли;
Если Данные.Свойство("invoice_date") Тогда
Объект.ДатаСчета = ПреобразоватьДату(Данные.invoice_date);
КонецЕсли;
Если Данные.Свойство("total_amount") Тогда
Объект.СуммаИтого = Данные.total_amount;
КонецЕсли;
Если Данные.Свойство("total_vat") Тогда
Объект.СуммаНДС = Данные.total_vat;
КонецЕсли;
// Заполнение табличной части
Если Данные.Свойство("items") Тогда
Объект.Товары.Очистить();
Для Каждого Позиция Из Данные.items Цикл
НоваяСтрока = Объект.Товары.Добавить();
НоваяСтрока.Наименование = Позиция.name;
НоваяСтрока.Количество = Позиция.quantity;
НоваяСтрока.Цена = Позиция.price;
НоваяСтрока.Сумма = Позиция.amount;
Если Позиция.Свойство("vat_rate") Тогда
НоваяСтрока.СтавкаНДС = Позиция.vat_rate;
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецПроцедуры
Функция ПреобразоватьДату(СтрокаДаты)
// Поддерживаемые форматы: ДД.ММ.ГГГГ, ДД/ММ/ГГГГ, ГГГГ-ММ-ДД
Разделители = Новый Массив;
Разделители.Добавить(".");
Разделители.Добавить("/");
Разделители.Добавить("-");
Для Каждого Разделитель Из Разделители Цикл
Если Найти(СтрокаДаты, Разделитель) > 0 Тогда
Части = СтрРазделить(СтрокаДаты, Разделитель);
Если Части.Количество() = 3 Тогда
// Определяем порядок: ДД.ММ.ГГГГ или ГГГГ-ММ-ДД
Если СтрДлина(Части[0]) = 4 Тогда
Возврат Дата(Число(Части[0]), Число(Части[1]), Число(Части[2]));
Иначе
Возврат Дата(Число(Части[2]), Число(Части[1]), Число(Части[0]));
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Возврат Неопределено;
КонецФункции
2. Заполнение карточки сотрудника по паспорту
Данные паспорта используются для заполнения личной карточки сотрудника.
Процедура ЗагрузитьПаспорт(Команда)
Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
Диалог.Заголовок = "Выберите скан паспорта";
Диалог.Фильтр = "Изображения (*.jpg;*.png)|*.jpg;*.png|PDF (*.pdf)|*.pdf";
Если Не Диалог.Выбрать() Тогда
Возврат;
КонецЕсли;
ДанныеПаспорта = РаспознатьПаспорт(Диалог.ПолноеИмяФайла);
Если ДанныеПаспорта = Неопределено Тогда
Возврат;
КонецЕсли;
ЗаполнитьДанныеПаспорта(ДанныеПаспорта);
КонецПроцедуры
Процедура ЗаполнитьДанныеПаспорта(Данные)
Если Данные.Свойство("ФИО") Тогда
Объект.ФИО = Данные.ФИО;
КонецЕсли;
Если Данные.Свойство("Дата_рождения") Тогда
Объект.ДатаРождения = ПреобразоватьДату(Данные.Дата_рождения);
КонецЕсли;
Если Данные.Свойство("Место_рождения") Тогда
Объект.МестоРождения = Данные.Место_рождения;
КонецЕсли;
// Разделение номера паспорта на серию и номер
Если Данные.Свойство("Номер_паспорта") Тогда
НомерПаспорта = Данные.Номер_паспорта;
Если СтрДлина(НомерПаспорта) >= 10 Тогда
Объект.СерияПаспорта = Лев(НомерПаспорта, 4);
Объект.НомерПаспорта = Прав(НомерПаспорта, 6);
КонецЕсли;
КонецЕсли;
Если Данные.Свойство("Дата_выдачи") Тогда
Объект.ДатаВыдачиПаспорта = ПреобразоватьДату(Данные.Дата_выдачи);
КонецЕсли;
Если Данные.Свойство("Кем_выдан") Тогда
Объект.КемВыданПаспорт = Данные.Кем_выдан;
КонецЕсли;
Если Данные.Свойство("Код_подразделения") Тогда
Объект.КодПодразделения = Данные.Код_подразделения;
КонецЕсли;
КонецПроцедуры
3. Импорт электронной трудовой книжки
ЭТК содержит перечень мест работы, который загружается в табличную часть документа.
Процедура ЗагрузитьЭТК(Команда)
Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
Диалог.Заголовок = "Выберите файл ЭТК";
Диалог.Фильтр = "PDF (*.pdf)|*.pdf|Изображения (*.jpg;*.png)|*.jpg;*.png";
Если Не Диалог.Выбрать() Тогда
Возврат;
КонецЕсли;
ДанныеЭТК = РаспознатьЭТК(Диалог.ПолноеИмяФайла);
Если ДанныеЭТК = Неопределено Тогда
Возврат;
КонецЕсли;
Если ДанныеЭТК.Свойство("места_работы") Тогда
Объект.МестаРаботы.Очистить();
Для Каждого Место Из ДанныеЭТК.места_работы Цикл
НоваяСтрока = Объект.МестаРаботы.Добавить();
Если Место.Свойство("период_работы") Тогда
НоваяСтрока.ПериодРаботы = Место.период_работы;
КонецЕсли;
Если Место.Свойство("место_работы") Тогда
НоваяСтрока.Организация = Место.место_работы;
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецПроцедуры
Универсальный модуль интеграции
Для упрощения использования реализован универсальный модуль, инкапсулирующий все функции взаимодействия.
// Модуль ИнтеграцияРаспознавание
Перем Хост, Порт;
Процедура Инициализация(Знач АдресСервера = "localhost", Знач ПортСервера = 5000) Экспорт
Хост = АдресСервера;
Порт = ПортСервера;
КонецПроцедуры
Функция СоздатьСоединение()
Таймаут = 120;
Возврат Новый HTTPСоединение(Хост, Порт, , , , Таймаут);
КонецФункции
Функция ОтправитьФайл(Эндпоинт, ПутьКФайлу)
// Реализация отправки файла
// ...
КонецФункции
Функция ОбработатьОтвет(Ответ)
// Реализация обработки JSON
// ...
КонецФункции
Функция РаспознатьДокумент(ПутьКФайлу, ТипДокумента) Экспорт
Эндпоинты = Новый Соответствие;
Эндпоинты.Вставить("Счет", "/api/extract_invoice");
Эндпоинты.Вставить("Компания", "/api/extract_company");
Эндпоинты.Вставить("Анкета", "/api/extract_anketa");
Эндпоинты.Вставить("Паспорт", "/api/extract_passport");
Эндпоинты.Вставить("ВУ", "/api/extract_driver_license");
Эндпоинты.Вставить("Диплом", "/api/extract_diploma");
Эндпоинты.Вставить("Патент", "/api/extract_patent");
Эндпоинты.Вставить("ЭТК", "/api/extract_etk");
Если Эндпоинты.Свойство(ТипДокумента) Тогда
Возврат ОтправитьФайл(Эндпоинты[ТипДокумента], ПутьКФайлу);
Иначе
ВызватьИсключение "Неизвестный тип документа: " + ТипДокумента;
КонецЕсли;
КонецФункции
// Обертки для конкретных типов документов
Функция РаспознатьСчет(ПутьКФайлу) Экспорт
Возврат РаспознатьДокумент(ПутьКФайлу, "Счет");
КонецФункции
Функция РаспознатьПаспорт(ПутьКФайлу) Экспорт
Возврат РаспознатьДокумент(ПутьКФайлу, "Паспорт");
КонецФункции
// Аналогично для остальных типов
Обработка ошибок
Проверка доступности сервиса
Перед отправкой документа рекомендуется проверять доступность приложения.
Функция ПроверитьДоступность() Экспорт
Попытка
Соединение = СоздатьСоединение();
Запрос = Новый HTTPЗапрос("/api/extract_invoice");
Ответ = Соединение.Получить(Запрос);
Возврат Истина;
Исключение
Возврат Ложь;
КонецПопытки;
КонецФункции
Повторные попытки при сбоях
Реализован механизм повторных попыток с экспоненциальной задержкой.
Функция ОтправитьСПовторами(Эндпоинт, ПутьКФайлу, МаксПопыток = 3) Экспорт
Для НомерПопытки = 1 По МаксПопыток Цикл
Попытка
Результат = ОтправитьФайл(Эндпоинт, ПутьКФайлу);
Если Результат <> Неопределено Тогда
Возврат Результат;
КонецЕсли;
Исключение
Если НомерПопытки = МаксПопыток Тогда
ЗаписьЖурналаРегистрации("Распознавание.Ошибка", УровеньЖурналаРегистрации.Ошибка, , ,
"Ошибка после " + МаксПопыток + " попыток: " + ОписаниеОшибки());
ВызватьИсключение;
КонецЕсли;
ВремяОжидания = 2 ^ (НомерПопытки - 1);
Приостановить(ВремяОжидания);
КонецПопытки;
КонецЦикла;
КонецФункции
Проверка размера файла
Функция ПроверитьРазмерФайла(ПутьКФайлу, МаксимальныйРазмерМБ = 20) Экспорт
Файл = Новый Файл(ПутьКФайлу);
РазмерМБ = Файл.Размер() / (1024 * 1024);
Если РазмерМБ > МаксимальныйРазмерМБ Тогда
Сообщить("Размер файла превышает допустимый (" + МаксимальныйРазмерМБ + " МБ)");
Возврат Ложь;
КонецЕсли;
Возврат Истина;
КонецФункции
Технические параметры
Сетевые параметры:
-
Хост: localhost (по умолчанию), может быть изменен на IP-адрес сервера
-
Порт: 5000
-
Протокол: HTTP (поддерживается HTTPS с самоподписанным сертификатом)
-
Таймаут: 120 секунд
Форматы данных:
-
Запрос: multipart/form-data
-
Ответ: application/json
-
Кодировка: UTF-8
Ограничения:
-
Рекомендуемый максимальный размер файла: 20 МБ
-
Максимальное количество страниц в PDF: не ограничено, но обработка больших файлов может занимать продолжительное время
Логирование:
-
Журнал регистрации 1С: "Распознавание.Ошибка"
-
Уровни: Ошибка, Предупреждение, Информация
Ссылка на программу
Вступайте в нашу телеграмм-группу Инфостарт