Параметры, которые сотрудник заполняет в произвольном обращении 1С:Кабинета сотрудника, не приходят в «1С:ЗУП» отдельными полями — в учётную систему попадает само обращение и PDF заявления. Но все значения в этом PDF есть. Поэтому рабочее решение без доработки сервиса такое: в расширении ЗУП извлекать текст из PDF утилитой pdftotext, разбирать его по меткам из настроек шаблона и заполнять нужные реквизиты, после чего автоматически создавать документы или отклонять обращения.
Ниже — как устроено такое решение, код на встроенном языке и подводные камни, которые стоит учесть заранее.
Задача: внутренние заявления через кабинет сотрудника
Типовой кабинет покрывает основные кадровые заявления: отпуск, справки, сведения о доходах. Но у многих компаний десятки внутренних форм — заявление на перенос отпуска по внутренним правилам, на компенсацию, на изменение графика. Для них в кабинете есть произвольное обращение с настраиваемым шаблоном.
Проблема — дальше. Чтобы ЗУП мог автоматически обработать заявление (создать документ, проверить даты, отклонить некорректное), ему нужны значения полей. А в обмене их нет: приходит обращение и присоединённый файл заявления.
Варианты, которые я рассматривал:
| Вариант | Плюсы | Минусы |
|---|---|---|
| Доработать сам кабинет сотрудника | Данные придут «правильно» | Риски при обновлениях сервиса, зависимость от его внутреннего устройства |
| Обрабатывать заявления вручную | Ничего не дорабатывать | Не масштабируется, ошибки кадровиков |
| Читать PDF заявления в ЗУП расширением | Сервис не меняется, ЗУП остаётся на поддержке | Нужна утилита и аккуратные настройки шаблонов |
Выбран третий вариант: всё, что нужно, уже приходит, осталось научиться это читать.
Как прочитать PDF в 1С

Платформа формирует PDF, но извлекать из него текст не умеет. Для этого есть открытая консольная утилита pdftotext (входит в пакеты Poppler и Xpdf, есть сборки под Windows и Linux). Она превращает PDF в текстовый файл, если в документе есть текстовый слой. Заявления из кабинета формируются из текста, а не из сканов, так что условие выполняется.
Практические моменты:
- Утилиту устанавливаю на сервер 1С администратором, а путь храню в константе. Выгружать исполняемый файл из макета во временный каталог при каждом вызове — плохая идея: это медленно, а антивирусы и политики безопасности справедливо блокируют запуск программ из временных каталогов.
- Запуск — через
ЗапуститьПриложениес ожиданием завершения, безWScript.Shellи вспомогательных программ вроде hstart. Так код работает и на Windows, и на Linux-сервере. - Кодировка вывода — UTF-8, ключ
-enc UTF-8. Ключ-layoutсохраняет расположение текста, что упрощает поиск по меткам.
Код: извлечение текста из PDF
Функция размещается в серверном общем модуле расширения.
// Извлекает текст из PDF с помощью утилиты pdftotext.
//
// Параметры:
// ДанныеPDF - ДвоичныеДанные - содержимое файла.
//
// Возвращаемое значение:
// Строка - текст документа; пустая строка, если извлечь не удалось.
//
Функция ТекстИзPDF(ДанныеPDF) Экспорт
ПутьКУтилите = Константы.ПутьКPdfToText.Получить();
Если Не ЗначениеЗаполнено(ПутьКУтилите) Тогда
ВызватьИсключение НСтр("ru = 'Не указан путь к утилите pdftotext.'");
КонецЕсли;
ИмяФайлаPDF = ПолучитьИмяВременногоФайла("pdf");
ИмяФайлаТекста = ПолучитьИмяВременногоФайла("txt");
ДанныеPDF.Записать(ИмяФайлаPDF);
СтрокаКоманды = СтрШаблон("""%1"" -enc UTF-8 -layout ""%2"" ""%3""",
ПутьКУтилите, ИмяФайлаPDF, ИмяФайлаТекста);
Результат = "";
Попытка
КодВозврата = Неопределено;
ЗапуститьПриложение(СтрокаКоманды, , Истина, КодВозврата);
Если КодВозврата = 0 Тогда
Текст = Новый ТекстовыйДокумент;
Текст.Прочитать(ИмяФайлаТекста, КодировкаТекста.UTF8);
Результат = Текст.ПолучитьТекст();
Иначе
ЗаписьЖурналаРегистрации("КабинетСотрудника.РазборPDF", УровеньЖурналаРегистрации.Ошибка, , ,
СтрШаблон("pdftotext вернул код %1", КодВозврата));
КонецЕсли;
Исключение
ЗаписьЖурналаРегистрации("КабинетСотрудника.РазборPDF", УровеньЖурналаРегистрации.Ошибка, , ,
ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
КонецПопытки;
УдалитьФайлыБезОшибок(ИмяФайлаPDF);
УдалитьФайлыБезОшибок(ИмяФайлаТекста);
Возврат Результат;
КонецФункции
Процедура УдалитьФайлыБезОшибок(ИмяФайла)
Попытка
УдалитьФайлы(ИмяФайла);
Исключение
// Временный файл не критичен, ошибку не поднимаем.
КонецПопытки;
КонецПроцедурыДвоичные данные заявления берутся из присоединённого файла обращения средствами БСП: РаботаСФайлами.ДвоичныеДанныеФайла(СсылкаНаФайл). Имя справочника присоединённых файлов обращения зависит от версии ЗУП — посмотрите его в дереве метаданных.
Разбор текста: метки и даты
Параметры заявления делятся на две группы.
Текстовые значения ищутся между двумя метками: «что стоит перед значением» и «что стоит после». Например, между «прошу перенести» и «в связи с».
// Возвращает текст между двумя метками или пустую строку.
Функция ЗначениеМеждуМетками(Текст, НачальнаяМетка, КонечнаяМетка) Экспорт
ПозицияНачала = СтрНайти(НРег(Текст), НРег(НачальнаяМетка));
Если ПозицияНачала = 0 Тогда
Возврат "";
КонецЕсли;
ПозицияНачала = ПозицияНачала + СтрДлина(НачальнаяМетка);
ПозицияКонца = СтрНайти(НРег(Текст), НРег(КонечнаяМетка), , ПозицияНачала);
Если ПозицияКонца = 0 Тогда
Возврат "";
КонецЕсли;
Возврат СокрЛП(Сред(Текст, ПозицияНачала, ПозицияКонца - ПозицияНачала));
КонецФункцииДаты в заявлении пишутся прописью месяца: «1 мая 2026 года». Ищем название месяца в родительном падеже: слово перед ним — число, слово после — год. Найденные даты нумеруются по порядку, а в настройке шаблона указывается, что первая дата — дата заявления, вторая — начало, третья — окончание.
// Возвращает массив дат вида «1 мая 2026 года» в порядке появления в тексте.
Функция ДатыИзТекста(Текст) Экспорт
Месяцы = Новый Соответствие;
ИменаМесяцев = СтрРазделить("января,февраля,марта,апреля,мая,июня,июля,августа,сентября,октября,ноября,декабря", ",");
Для Номер = 1 По ИменаМесяцев.Количество() Цикл
Месяцы.Вставить(ИменаМесяцев[Номер - 1], Номер);
КонецЦикла;
Разделители = " " + Символы.ПС + Символы.Таб + Символы.ВК + ",.«»""";
Слова = СтрРазделить(НРег(Текст), Разделители, Ложь);
Даты = Новый Массив;
Для Индекс = 1 По Слова.Количество() - 2 Цикл
НомерМесяца = Месяцы.Получить(Слова[Индекс]);
Если НомерМесяца = Неопределено Тогда
Продолжить;
КонецЕсли;
ДеньСтрокой = СтроковыеФункцииКлиентСервер.ТолькоЦифрыВСтроке(Слова[Индекс - 1]);
ГодСтрокой = Лев(СтроковыеФункцииКлиентСервер.ТолькоЦифрыВСтроке(Слова[Индекс + 1]), 4);
Если СтрДлина(ДеньСтрокой) = 0 Или СтрДлина(ГодСтрокой) <> 4 Тогда
Продолжить;
КонецЕсли;
Попытка
Даты.Добавить(Дата(Число(ГодСтрокой), НомерМесяца, Число(ДеньСтрокой)));
Исключение
// Некорректная дата, например «31 июня», пропускаем.
КонецПопытки;
КонецЦикла;
Возврат Даты;
КонецФункцииНастройки шаблонов вместо кода
Метки и номера дат я храню не в коде, а в регистре сведений расширения с измерениями «Шаблон обращения» и «Параметр» и ресурсами «Начальная метка», «Конечная метка», «Номер даты». Форма настройки открывается из шаблона произвольного обращения. Плюсы:
- новый вид заявления подключается без программиста;
- при изменении текста шаблона достаточно поправить метки;
- одна и та же процедура разбора работает для всех шаблонов.
Сам разбор вызывается при загрузке обращения: в расширении подключаюсь к обработчику записи объекта обращения аннотацией &После, проверяю, что обращение произвольное и для его шаблона есть настройки, и выполняю разбор. Если разбор долгий или файлов много, выношу его в регламентное задание, чтобы не замедлять обмен.
Что делать с полученными параметрами
Когда значения заполнены, открывается автоматизация:
- проверки и автоотклонение — например, дата начала раньше даты заявления или пересечение с уже оформленным отпуском;
- создание документов — черновик кадрового документа с заполненными полями для проверки кадровиком;
- маршрутизация — разные виды заявлений уходят разным ответственным.
Все доработки ЗУП делаю расширением: конфигурация остаётся на поддержке, кабинет сотрудника не меняется, и обновления обеих сторон проходят спокойно. Подробнее о подходе — на страницах доработка 1С:ЗУП и расширения 1С.
Типичные ошибки
- PDF без текстового слоя. Если файл — скан,
pdftotextвернёт пустую строку. Такие обращения помечайте для ручной обработки. - Утилита не найдена на сервере. Код выполняется на сервере 1С, а не на компьютере программиста; в кластере из нескольких рабочих серверов утилита нужна на каждом.
- Права пользователя службы. У учётной записи сервера 1С должно быть право запуска утилиты и записи во временный каталог.
- Изменился шаблон заявления. Метки перестают находиться — нужна проверка «все обязательные параметры заполнены» и понятное сообщение в журнале.
- Регистр и пробелы.
-layoutможет добавить лишние пробелы; ищите без учёта регистра и обрезайте результат.
Итог
- Параметры произвольного обращения не приходят в ЗУП полями, но есть в PDF заявления.
- Текст из PDF извлекается утилитой
pdftotext, установленной на сервер 1С и запускаемой черезЗапуститьПриложение. - Значения находятся по меткам, даты — по названиям месяцев; метки хранятся в настройках шаблона.
- Решение живёт в расширении ЗУП, кабинет сотрудника остаётся без изменений.
- Обязательно предусмотрите ручную обработку нераспознанных заявлений.




