При доработке типовой конфигурации 1С:Управление производственным предприятием 1.3 (УПП) разработчики часто сталкиваются с задачей отображения подразделений сотрудников. При исследовании справочника СотрудникиОрганизаций обнаруживается реквизит ТекущееПодразделениеКомпании. Однако при выводе этого поля на форму элемента возникает проблема: пользователь выбирает подразделение, нажимает кнопку записи, но при повторном открытии или сразу после сохранения значение очищается. Разберем архитектурные причины такого поведения платформы и рассмотрим правильные способы получения данных о подразделениях с помощью запросов.
Проанализируем структуру типового решения УПП 1.3. В конфигурации кадровые данные разделены на два независимых контура:
РаботникиОрганизаций. Подразделения берутся из справочника ПодразделенияОрганизаций.Работники. В этом контуре используется справочник Подразделения.Реквизиты справочника СотрудникиОрганизаций с префиксами Текущее... (например, ТекущееПодразделениеКомпании, ТекущаяДолжностьКомпании) не являются источником первичных данных. Они представляют собой денормализованные кэширующие поля. Эти реквизиты служат исключительно для ускорения отрисовки списков и динамических форм. При любой стандартной записи элемента или проведении кадровых документов типовые процедуры перезаписывают эти поля актуальными срезами из регистров. Если в базе управленческий кадровый учет отключен или документы по компании не проводятся, система при перезаписи всегда будет принудительно обнулять данные реквизиты. Напрямую редактировать их в форме бессмысленно.
Разберем по шагам, как выстроить правильную выборку работающих сотрудников с их подразделениями, если учет ведется только по организациям, а отчет требуется построить в структуре управленческих подразделений.
Для сопоставления структуры организации со структурой компании в УПП предусмотрен специальный регистр сведений — СоответствиеПодразделенийИПодразделенийОрганизаций. Актуальное состояние сотрудников на заданную дату хранится в таблице среза последних регистра РаботникиОрганизаций.
Посмотрим на пример запроса, связывающего эти данные:
ВЫБРАТЬ
РаботникиОрганизацийСрезПоследних.Сотрудник КАК Сотрудник,
РаботникиОрганизацийСрезПоследних.ПодразделениеОрганизации КАК ПодразделениеОрганизации,
СоответствиеПодразделенийИПодразделенийОрганизаций.Подразделение КАК Подразделение,
РаботникиОрганизацийСрезПоследних.Период КАК ДатаИзменения
ИЗ
РегистрСведений.РаботникиОрганизаций.СрезПоследних(&Период, ) КАК РаботникиОрганизацийСрезПоследних
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.СоответствиеПодразделенийИПодразделенийОрганизаций КАК СоответствиеПодразделенийИПодразделенийОрганизаций
ПО РаботникиОрганизацийСрезПоследних.ПодразделениеОрганизации = СоответствиеПодразделенийИПодразделенийОрганизаций.ПодразделениеОрганизации
И РаботникиОрганизацийСрезПоследних.Организация = СоответствиеПодразделенийИПодразделенийОрганизаций.Организация
ГДЕ
РаботникиОрганизацийСрезПоследних.ПричинаИзмененияСостояния <> ЗНАЧЕНИЕ(Перечисление.ПричиныИзмененияСостояния.Увольнение)
Обратим внимание на ключевые моменты построения этого запроса:
СрезПоследних(&Период, ) конкретную дату. Если параметр опущен, срез берется на текущую дату, что может исказить результаты при ретроспективном анализе.РаботникиОрганизаций хранит историю кадровых перемещений. Чтобы исключить бывших работников, обязательно добавляем условие в секцию ГДЕ: исключаем записи, у которых ПричинаИзмененияСостояния равна значению перечисления Увольнение.СоответствиеПодразделенийИПодразделенийОрганизаций являются не только подразделение организации, но и сама Организация. Соединение необходимо производить по обеим колонкам, чтобы избежать дублирования строк при совпадении наименований в разных юридических лицах.При использовании левого соединения нередко возникает ситуация, когда для части сотрудников поле Подразделение возвращает NULL, хотя регламентированное подразделение заполнено. Проанализируем возможные причины:
СоответствиеПодразделенийИПодразделенийОрганизаций не заполняется автоматически. Если кадровик создал новое подразделение организации и перевел туда людей, но администратор не указал соответствие управленческому подразделению в этом регистре, связь не сработает. Для выявления таких разрывов полезно регулярно запускать проверку незаполненных соответствий.Ошибочным подходом является разворачивание иерархии через цепочку реквизитов в запросе вида Подразделение.Родитель.Родитель.... Такой код жестко ограничен фиксированной глубиной, создает избыточную нагрузку на СУБД и ломается при появлении более глубоких уровней вложенности.
Рассмотрим штатные и эффективные способы вывода древовидной структуры:
ИТОГИ ПО
Подразделение ИЕРАРХИЯ
При обходе выборки во встроенном языке используем режим иерархического обхода:
Выборка = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией);
Пока Выборка.Следующий() Цикл
// Вывод родительских и дочерних узлов в дерево или табличный документ
КонецЦикла;
ДеревоДанных = РезультатЗапроса.Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией);
ЭлементыФормы.ДеревоРезультата.СоздатьКолонки();
Подразделение достаточно установить тип группировки «Иерархия». Платформа самостоятельно построит корректное дерево произвольной глубины вложенности без необходимости ручного программирования рекурсий.