При автозаполнении файлов Word в 1С:Документооборот (1С:ДО) часто возникает задача красивого вывода виз и резолюций. Стандартный механизм выгружает ФИО авторов, однако в официальных документах гораздо солиднее выглядит указание их должностей или ролей. Кроме того, процесс согласования обычно завершается финальным вердиктом руководителя, который должен выводиться отдельно под заголовком "Решение", в то время как предыдущие записи согласующих лиц должны оставаться "Заключениями".
В этой статье мы подробно разберем, как решить обе эти задачи: заменить ФИО сотрудников на их актуальные должности (или роли) и корректно разделить промежуточные резолюции и финальное решение.
В исходном коде заполнения шаблона Word использовался простой перебор таблицы резолюций:
Резолюции = "";
ТЗРезолюции = РаботаСРезолюциями.ПолучитьРезолюции(Файл.ВладелецФайла, Истина, Истина);
Для Каждого Стр Из ТЗРезолюции Цикл
Резолюции = Резолюции + Символы.ВК + "Заключение от:" + " " + Стр.АвторРезолюции + ":" + Символы.ПС + Стр.ТекстРезолюции + Символы.ПС + Стр.ВнесРезолюцию + " " + " ("+Формат(Стр.ДатаРезолюции,"ДЛФ=D")+")" + Символы.ВК ;
КонецЦикла;
РезультатОбработки = Резолюции;
Этот алгоритм имеет два серьезных недостатка:
Стр.АвторРезолюции возвращает представление объекта типа СправочникСсылка.Пользователи (то есть ФИО сотрудника), а не его должность.Разберем по шагам, как исправить эти недостатки и оптимизировать код.
На форуме было предложено обращаться к регистру сведений СведенияОПользователяхДокументооборот напрямую:
СведенияПользователя = РегистрыСведений.СведенияОПользователяхДокументооборот.Получить(Новый Структура("Пользователь", Стр.АвторРезолюции));
Должность = СведенияПользователя.Должность;
Важный нюанс производительности: Обращение к базе данных методом Получить() внутри цикла (особенно если резолюций много) является "плохим тоном" в разработке на платформе 1С. Это создает избыточную нагрузку на СУБД. Кроме того, структура метаданных регистра в разных версиях 1С:ДО может незначительно меняться.
Для решения этой задачи в 1С:Документооборот предусмотрен стандартный программный интерфейс (API), который работает быстрее и безопаснее. Мы можем использовать экспортную функцию общего модуля РаботаСПользователями:
ДолжностьСсылка = РаботаСПользователями.ПолучитьДолжность(Стр.АвторРезолюции);
ДолжностьТекст = Строка(ДолжностьСсылка);
Эта функция возвращает ссылку на элемент справочника "Должности". Применив к ней функцию Строка(), мы получим наименование должности в виде обычного текста.
Иногда процессы в 1С:ДО настроены не на конкретных персональных исполнителей, а на роли (например, "Главный юрист", "Финансовый директор"). Поле АвторРезолюции всегда содержит конкретное физлицо, выполнившее задачу. Если вам необходимо вывести именно роль, в рамках которой пользователь оставил резолюцию, потребуется обратиться к истории задач по документу.
Для этого мы можем написать небольшой вспомогательный запрос, который найдет выполненную задачу по нашему предмету и вернет роль исполнителя:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗРЕШЕННЫЕ
| ЗадачиИсполнителя.РольИсполнителя КАК Роль
|ИЗ
| Задача.ЗадачаИсполнителя КАК ЗадачиИсполнителя
|ГДЕ
| ЗадачиИсполнителя.Предметы.Предмет = &Документ
| И ЗадачиИсполнителя.Исполнитель = &АвторРезолюции
| И ЗадачиИсполнителя.Выполнена = ИСТИНА
| И ЗадачиИсполнителя.РольИсполнителя <> ЗНАЧЕНИЕ(Справочник.РолиИсполнителей.ПустаяСсылка)";
Запрос.УстановитьПараметр("Документ", Файл.ВладелецФайла);
Запрос.УстановитьПараметр("АвторРезолюции", Стр.АвторРезолюции);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
ИмяДляВывода = Строка(Выборка.Роль);
Иначе
// Если задача была персональной (без роли), берем стандартную должность
ИмяДляВывода = Строка(РаботаСПользователями.ПолучитьДолжность(Стр.АвторРезолюции));
КонецЕсли;
Основная сложность, с которой столкнулся автор темы на форуме, заключалась в правильном определении последней резолюции. Поскольку индексация в таблицах значений 1С начинается с нуля, индекс последней строки всегда равен Количество() - 1.
Попытка проверять индекс внутри цикла Для Каждого с помощью метода Индекс() не только замедляет работу системы, но и легко запутывает разработчика. Кроме того, резолюции в таблице могут быть не отсортированы по времени, из-за чего "последняя" по индексу строка может оказаться не последней по дате принятия решения.
Чтобы реализовать эту логику максимально надежно, применим следующий алгоритм:
0 до Количество() - 2). Оформим их как "Заключение".Количество() - 1) обработаем отдельно вне цикла, оформив её как "Решение".Проанализируем реализацию этого подхода на практике:
ТЗРезолюции = РаботаСРезолюциями.ПолучитьРезолюции(Файл.ВладелецФайла, Истина, Истина);
// Сортируем по дате, чтобы последнее решение гарантированно оказалось в конце таблицы
ТЗРезолюции.Сортировать("ДатаРезолюции Возр");
КоличествоРезолюций = ТЗРезолюции.Количество();
Резолюции = "";
Решение = "";
// Проходим циклом по всем строкам, кроме финальной
Для Индекс = 0 По КоличествоРезолюций - 2 Цикл
Стр = ТЗРезолюции[Индекс];
Должность = Строка(РаботаСПользователями.ПолучитьДолжность(Стр.АвторРезолюции));
Резолюции = Резолюции + Символы.ВК + "Заключение от: " + Должность + ":" + Символы.ПС
+ Стр.ТекстРезолюции + Символы.ПС + Стр.ВнесРезолюцию + " "
+ " (" + Формат(Стр.ДатаРезолюции, "ДЛФ=D") + ")" + Символы.ВК;
КонецЦикла;
// Последнюю строку обрабатываем отдельно
Если КоличествоРезолюций > 0 Тогда
ПоследняяСтр = ТЗРезолюции[КоличествоРезолюций - 1];
ДолжностьПоследнего = Строка(РаботаСПользователями.ПолучитьДолжность(ПоследняяСтр.АвторРезолюции));
Решение = "Решение: " + ДолжностьПоследнего + ":" + Символы.ПС
+ ПоследняяСтр.ТекстРезолюции + Символы.ПС + ПоследняяСтр.ВнесРезолюцию + " "
+ " (" + Формат(ПоследняяСтр.ДатаРезолюции, "ДЛФ=D") + ")" + Символы.ВК;
КонецЕсли;
// Объединяем результаты для вывода в Word
РезультатОбработки = Резолюции + Символы.ВК + Решение;
Фраза в документе "Заключение от: Генеральный директор" с точки зрения русского языка выглядит несогласованно. Гораздо правильнее написать: "Заключение от: Генерального директора".
В 1С:Документооборот интегрирована библиотека стандартных подсистем (БСП), которая содержит мощные инструменты для склонения представлений объектов. Мы можем автоматически просклонять должность в родительный падеж (падеж под номером 2):
ДолжностьИменительный = Строка(РаботаСПользователями.ПолучитьДолжность(Стр.АвторРезолюции));
ДолжностьРодительный = "";
// Склоняем полученную строку в родительный падеж (параметр "2")
СклонениеПредставленийОбъектов.ПросклонятьПредставление(ДолжностьИменительный, 2, ДолжностьРодительный);
// Если склонение прошло успешно, используем его, иначе оставляем исходную должность
ДолжностьДляВывода = ?(ЗначениеЗаполнено(ДолжностьРодительный), ДолжностьРодительный, ДолжностьИменительный);
Использование этого небольшого дополнения сделает ваши печатные формы и заполняемые файлы Word полностью соответствующими правилам деловой переписки.
Объединим все наши наработки в единый готовый блок кода, который можно использовать в обработке автозаполнения шаблона Word:
ТЗРезолюции = РаботаСРезолюциями.ПолучитьРезолюции(Файл.ВладелецФайла, Истина, Истина);
ТЗРезолюции.Сортировать("ДатаРезолюции Возр");
КоличествоРезолюций = ТЗРезолюции.Количество();
Резолюции = "";
Решение = "";
// 1. Обработка всех промежуточных согласований ("Заключений")
Для Индекс = 0 По КоличествоРезолюций - 2 Цикл
Стр = ТЗРезолюции[Индекс];
// Получаем должность
ДолжностьИменительный = Строка(РаботаСПользователями.ПолучитьДолжность(Стр.АвторРезолюции));
ДолжностьРодительный = "";
СклонениеПредставленийОбъектов.ПросклонятьПредставление(ДолжностьИменительный, 2, ДолжностьРодительный);
ДолжностьДляВывода = ?(ЗначениеЗаполнено(ДолжностьРодительный), ДолжностьРодительный, ДолжностьИменительный);
Резолюции = Резолюции + Символы.ВК + "Заключение от " + ДолжностьДляВывода + ":" + Символы.ПС
+ Стр.ТекстРезолюции + Символы.ПС + Стр.ВнесРезолюцию + " "
+ " (" + Формат(Стр.ДатаРезолюции, "ДЛФ=D") + ")" + Символы.ВК;
КонецЦикла;
// 2. Обработка итогового утверждения ("Решения")
Если КоличествоРезолюций > 0 Тогда
ПоследняяСтр = ТЗРезолюции[КоличествоРезолюций - 1];
// Получаем должность последнего автора
ДолжностьИменительный = Строка(РаботаСПользователями.ПолучитьДолжность(ПоследняяСтр.АвторРезолюции));
ДолжностьРодительный = "";
СклонениеПредставленийОбъектов.ПросклонятьПредставление(ДолжностьИменительный, 2, ДолжностьРодительный);
ДолжностьДляВывода = ?(ЗначениеЗаполнено(ДолжностьРодительный), ДолжностьРодительный, ДолжностьИменительный);
Решение = "Решение (" + ДолжностьДляВывода + "):" + Символы.ПС
+ ПоследняяСтр.ТекстРезолюции + Символы.ПС + ПоследняяСтр.ВнесРезолюцию + " "
+ " (" + Формат(ПоследняяСтр.ДатаРезолюции, "ДЛФ=D") + ")" + Символы.ВК;
КонецЕсли;
// 3. Формирование финального результата
РезультатОбработки = Резолюции + Символы.ВК + Решение;
Этот код работает быстро, не создает лишних запросов к базе данных в цикле, корректно обрабатывает падежи должностей согласно правилам русского языка и аккуратно разделяет визы согласования и итоговую резолюцию руководителя.