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