Как вывести должность автора резолюции и отделить последнее решение в 1С:Документооборот?

Программист 1С v8.3 (Управляемые формы) 1С:Документооборот IT и автоматизация бизнеса
← На главную

При автозаполнении файлов Word в 1С:Документооборот (1С:ДО) часто возникает задача красивого вывода виз и резолюций. Стандартный механизм выгружает ФИО авторов, однако в официальных документах гораздо солиднее выглядит указание их должностей или ролей. Кроме того, процесс согласования обычно завершается финальным вердиктом руководителя, который должен выводиться отдельно под заголовком "Решение", в то время как предыдущие записи согласующих лиц должны оставаться "Заключениями".

В этой статье мы подробно разберем, как решить обе эти задачи: заменить ФИО сотрудников на их актуальные должности (или роли) и корректно разделить промежуточные резолюции и финальное решение.

Анализ исходной проблемы

В исходном коде заполнения шаблона Word использовался простой перебор таблицы резолюций:


Резолюции = "";
ТЗРезолюции = РаботаСРезолюциями.ПолучитьРезолюции(Файл.ВладелецФайла, Истина, Истина);
Для Каждого Стр Из ТЗРезолюции Цикл
    Резолюции = Резолюции + Символы.ВК + "Заключение от:" + " " + Стр.АвторРезолюции + ":" + Символы.ПС + Стр.ТекстРезолюции + Символы.ПС + Стр.ВнесРезолюцию + " " + " ("+Формат(Стр.ДатаРезолюции,"ДЛФ=D")+")" + Символы.ВК ;
КонецЦикла;
РезультатОбработки = Резолюции;

Этот алгоритм имеет два серьезных недостатка:

  1. Переменная Стр.АвторРезолюции возвращает представление объекта типа СправочникСсылка.Пользователи (то есть ФИО сотрудника), а не его должность.
  2. Цикл обрабатывает все строки одинаково, из-за чего финальное решение руководителя визуально ничем не отличается от рядовых заключений согласующих лиц.

Разберем по шагам, как исправить эти недостатки и оптимизировать код.

Шаг 1. Получение должности автора резолюции

На форуме было предложено обращаться к регистру сведений СведенияОПользователяхДокументооборот напрямую:


СведенияПользователя = РегистрыСведений.СведенияОПользователяхДокументооборот.Получить(Новый Структура("Пользователь", Стр.АвторРезолюции));
Должность = СведенияПользователя.Должность;

Важный нюанс производительности: Обращение к базе данных методом Получить() внутри цикла (особенно если резолюций много) является "плохим тоном" в разработке на платформе 1С. Это создает избыточную нагрузку на СУБД. Кроме того, структура метаданных регистра в разных версиях 1С:ДО может незначительно меняться.

Для решения этой задачи в 1С:Документооборот предусмотрен стандартный программный интерфейс (API), который работает быстрее и безопаснее. Мы можем использовать экспортную функцию общего модуля РаботаСПользователями:


ДолжностьСсылка = РаботаСПользователями.ПолучитьДолжность(Стр.АвторРезолюции);
ДолжностьТекст = Строка(ДолжностьСсылка);

Эта функция возвращает ссылку на элемент справочника "Должности". Применив к ней функцию Строка(), мы получим наименование должности в виде обычного текста.

Шаг 2. Как вывести именно роль исполнителя (если это необходимо)

Иногда процессы в 1С:ДО настроены не на конкретных персональных исполнителей, а на роли (например, "Главный юрист", "Финансовый директор"). Поле АвторРезолюции всегда содержит конкретное физлицо, выполнившее задачу. Если вам необходимо вывести именно роль, в рамках которой пользователь оставил резолюцию, потребуется обратиться к истории задач по документу.

Для этого мы можем написать небольшой вспомогательный запрос, который найдет выполненную задачу по нашему предмету и вернет роль исполнителя:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ РАЗРЕШЕННЫЕ
    |	ЗадачиИсполнителя.РольИсполнителя КАК Роль
    |ИЗ
    |	Задача.ЗадачаИсполнителя КАК ЗадачиИсполнителя
    |ГДЕ
    |	ЗадачиИсполнителя.Предметы.Предмет = &Документ
    |	И ЗадачиИсполнителя.Исполнитель = &АвторРезолюции
    |	И ЗадачиИсполнителя.Выполнена = ИСТИНА
    |	И ЗадачиИсполнителя.РольИсполнителя <> ЗНАЧЕНИЕ(Справочник.РолиИсполнителей.ПустаяСсылка)";

Запрос.УстановитьПараметр("Документ", Файл.ВладелецФайла);
Запрос.УстановитьПараметр("АвторРезолюции", Стр.АвторРезолюции);

Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
    ИмяДляВывода = Строка(Выборка.Роль);
Иначе
    // Если задача была персональной (без роли), берем стандартную должность
    ИмяДляВывода = Строка(РаботаСПользователями.ПолучитьДолжность(Стр.АвторРезолюции));
КонецЕсли;

Шаг 3. Отделение последней резолюции ("Решения")

Основная сложность, с которой столкнулся автор темы на форуме, заключалась в правильном определении последней резолюции. Поскольку индексация в таблицах значений 1С начинается с нуля, индекс последней строки всегда равен Количество() - 1.

Попытка проверять индекс внутри цикла Для Каждого с помощью метода Индекс() не только замедляет работу системы, но и легко запутывает разработчика. Кроме того, резолюции в таблице могут быть не отсортированы по времени, из-за чего "последняя" по индексу строка может оказаться не последней по дате принятия решения.

Чтобы реализовать эту логику максимально надежно, применим следующий алгоритм:

  1. Явно отсортируем таблицу резолюций по дате в возрастающем порядке.
  2. В цикле обойдем все строки, кроме самой последней (индексы от 0 до Количество() - 2). Оформим их как "Заключение".
  3. Последнюю строку (индекс Количество() - 1) обработаем отдельно вне цикла, оформив её как "Решение".

Проанализируем реализацию этого подхода на практике:


ТЗРезолюции = РаботаСРезолюциями.ПолучитьРезолюции(Файл.ВладелецФайла, Истина, Истина);
// Сортируем по дате, чтобы последнее решение гарантированно оказалось в конце таблицы
ТЗРезолюции.Сортировать("ДатаРезолюции Возр");

КоличествоРезолюций = ТЗРезолюции.Количество();
Резолюции = "";
Решение = "";

// Проходим циклом по всем строкам, кроме финальной
Для Индекс = 0 По КоличествоРезолюций - 2 Цикл
    Стр = ТЗРезолюции[Индекс];
    Должность = Строка(РаботаСПользователями.ПолучитьДолжность(Стр.АвторРезолюции));
    
    Резолюции = Резолюции + Символы.ВК + "Заключение от: " + Должность + ":" + Символы.ПС 
                + Стр.ТекстРезолюции + Символы.ПС + Стр.ВнесРезолюцию + " " 
                + " (" + Формат(Стр.ДатаРезолюции, "ДЛФ=D") + ")" + Символы.ВК;
КонецЦикла;

// Последнюю строку обрабатываем отдельно
Если КоличествоРезолюций > 0 Тогда
    ПоследняяСтр = ТЗРезолюции[КоличествоРезолюций - 1];
    ДолжностьПоследнего = Строка(РаботаСПользователями.ПолучитьДолжность(ПоследняяСтр.АвторРезолюции));
    
    Решение = "Решение: " + ДолжностьПоследнего + ":" + Символы.ПС 
              + ПоследняяСтр.ТекстРезолюции + Символы.ПС + ПоследняяСтр.ВнесРезолюцию + " " 
              + " (" + Формат(ПоследняяСтр.ДатаРезолюции, "ДЛФ=D") + ")" + Символы.ВК;
КонецЕсли;

// Объединяем результаты для вывода в Word
РезультатОбработки = Резолюции + Символы.ВК + Решение;

Шаг 4. Улучшение внешнего вида (Склонение должностей)

Фраза в документе "Заключение от: Генеральный директор" с точки зрения русского языка выглядит несогласованно. Гораздо правильнее написать: "Заключение от: Генерального директора".

В 1С:Документооборот интегрирована библиотека стандартных подсистем (БСП), которая содержит мощные инструменты для склонения представлений объектов. Мы можем автоматически просклонять должность в родительный падеж (падеж под номером 2):


ДолжностьИменительный = Строка(РаботаСПользователями.ПолучитьДолжность(Стр.АвторРезолюции));
ДолжностьРодительный = "";

// Склоняем полученную строку в родительный падеж (параметр "2")
СклонениеПредставленийОбъектов.ПросклонятьПредставление(ДолжностьИменительный, 2, ДолжностьРодительный);

// Если склонение прошло успешно, используем его, иначе оставляем исходную должность
ДолжностьДляВывода = ?(ЗначениеЗаполнено(ДолжностьРодительный), ДолжностьРодительный, ДолжностьИменительный);

Использование этого небольшого дополнения сделает ваши печатные формы и заполняемые файлы Word полностью соответствующими правилам деловой переписки.

Итоговый оптимизированный код

Объединим все наши наработки в единый готовый блок кода, который можно использовать в обработке автозаполнения шаблона Word:


ТЗРезолюции = РаботаСРезолюциями.ПолучитьРезолюции(Файл.ВладелецФайла, Истина, Истина);
ТЗРезолюции.Сортировать("ДатаРезолюции Возр");

КоличествоРезолюций = ТЗРезолюции.Количество();
Резолюции = "";
Решение = "";

// 1. Обработка всех промежуточных согласований ("Заключений")
Для Индекс = 0 По КоличествоРезолюций - 2 Цикл
    Стр = ТЗРезолюции[Индекс];
    
    // Получаем должность
    ДолжностьИменительный = Строка(РаботаСПользователями.ПолучитьДолжность(Стр.АвторРезолюции));
    ДолжностьРодительный = "";
    СклонениеПредставленийОбъектов.ПросклонятьПредставление(ДолжностьИменительный, 2, ДолжностьРодительный);
    ДолжностьДляВывода = ?(ЗначениеЗаполнено(ДолжностьРодительный), ДолжностьРодительный, ДолжностьИменительный);
    
    Резолюции = Резолюции + Символы.ВК + "Заключение от " + ДолжностьДляВывода + ":" + Символы.ПС 
                + Стр.ТекстРезолюции + Символы.ПС + Стр.ВнесРезолюцию + " " 
                + " (" + Формат(Стр.ДатаРезолюции, "ДЛФ=D") + ")" + Символы.ВК;
КонецЦикла;

// 2. Обработка итогового утверждения ("Решения")
Если КоличествоРезолюций > 0 Тогда
    ПоследняяСтр = ТЗРезолюции[КоличествоРезолюций - 1];
    
    // Получаем должность последнего автора
    ДолжностьИменительный = Строка(РаботаСПользователями.ПолучитьДолжность(ПоследняяСтр.АвторРезолюции));
    ДолжностьРодительный = "";
    СклонениеПредставленийОбъектов.ПросклонятьПредставление(ДолжностьИменительный, 2, ДолжностьРодительный);
    ДолжностьДляВывода = ?(ЗначениеЗаполнено(ДолжностьРодительный), ДолжностьРодительный, ДолжностьИменительный);
    
    Решение = "Решение (" + ДолжностьДляВывода + "):" + Символы.ПС 
              + ПоследняяСтр.ТекстРезолюции + Символы.ПС + ПоследняяСтр.ВнесРезолюцию + " " 
              + " (" + Формат(ПоследняяСтр.ДатаРезолюции, "ДЛФ=D") + ")" + Символы.ВК;
КонецЕсли;

// 3. Формирование финального результата
РезультатОбработки = Резолюции + Символы.ВК + Решение;

Этот код работает быстро, не создает лишних запросов к базе данных в цикле, корректно обрабатывает падежи должностей согласно правилам русского языка и аккуратно разделяет визы согласования и итоговую резолюцию руководителя.

← На главную