Как правильно получить актуальное подразделение сотрудника по физлицу из регистра сведений в 1С?

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

При заполнении табличных частей документов и формировании кадровых отчетов часто требуется определить текущее подразделение или должность физического лица. Если строить сложный запрос с агрегатными функциями и вложенными подзапросами, легко получить дублирование строк или некорректные исторические значения. Разберем типичные ошибки таких запросов и пошагово построим правильные варианты выборки данных.

Анализ типичных ошибок в исходном запросе

Проанализируем структуру запроса, в котором разработчики пытаются вручную вычислить срез кадровых данных без использования стандартных возможностей платформы 1С:

  1. Агрегация ссылочных полей через МАКСИМУМ: использование конструкций вида МАКСИМУМ(РаботникиОрганизаций.ПодразделениеОрганизации) в сгруппированном запросе возвращает не подразделение на последнюю дату, а ссылку с максимальным внутренним идентификатором (GUID) или строковым представлением. В результате выбирается случайное историческое подразделение.
  2. Соединение по условию ПО (ИСТИНА): вложенный запрос без группировки по физическим лицам находит одну общую максимальную дату по всей таблице регистра. Соединение по условию ИСТИНА создает декартово произведение, искажая результаты выборки.
  3. Различие между физическим лицом и сотрудником: измерением кадровых регистров сведений (например, РаботникиОрганизаций) является Сотрудник, а Физлицо выступает реквизитом сотрудника. Одно физическое лицо может иметь несколько элементов в справочнике Сотрудники (при повторном приеме, совместительстве или договорах ГПХ).

Вариант 1. Соединение табличной части документа со срезом последних

Рассмотрим базовый и наиболее компактный вариант запроса. Если необходимо заполнить подразделения для строк табличной части документа, используем виртуальную таблицу СрезПоследних регистра сведений с фильтрацией по дате документа.


ВЫБРАТЬ
	ДокТЧ.ФизЛицо КАК ФизЛицо,
	ДокТЧ.ФизЛицо.ИдентификационныйКодЛичности КАК ИИНРаботника,
	ДокТЧ.ФизЛицо.РНН КАК РННРаботника,
	РаботникиОрганизацийСрезПоследних.Сотрудник КАК Сотрудник,
	РаботникиОрганизацийСрезПоследних.ПодразделениеОрганизации КАК Подразделение,
	РаботникиОрганизацийСрезПоследних.Должность КАК Должность
ИЗ
	Документ.РасчетСНиСО.ИсчисленныеООСМС КАК ДокТЧ
	ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.РаботникиОрганизаций.СрезПоследних(
		&Период,
		Организация = &Организация
			И Сотрудник.ВидЗанятости <> &ВнутреннееСовместительство
	) КАК РаботникиОрганизацийСрезПоследних
	ПО (РаботникиОрганизацийСрезПоследних.Сотрудник.Физлицо = ДокТЧ.ФизЛицо)
ГДЕ
	ДокТЧ.Ссылка = &Ссылка

Посмотрим на параметры этого запроса: в виртуальную таблицу передается &Период (дата актуальности или дата документа) и условие отбора по организации и основному месту работы. Это отсекает лишние записи и совместителей.

Вариант 2. Пакетный запрос с выборкой среза по физлицам через временные таблицы

Разберем по шагам более производительный и надежный сценарий, когда в регистре сведений измерением является Сотрудник, а связь с документом строится по ФизЛицо. Чтобы СУБД не выполняла полный срез по всей базе, поместим физические лица во временную таблицу и выполним точечный срез.

  1. Формирование списка физлиц: выбираем уникальный список физических лиц из табличной части документа во временную таблицу ВТ_ФизЛица.
  2. Определение актуальных дат: выбираем максимальный период записи кадрового регистра по каждому физлицу на указанную дату.
  3. Получение кадровых данных: соединяем срез периодов с реальной таблицей регистра для получения точных значений подразделения и должности.

ВЫБРАТЬ РАЗЛИЧНЫЕ
	ДокТЧ.ФизЛицо КАК ФизЛицо
ПОМЕСТИТЬ ВТ_ФизЛица
ИЗ
	Документ.РасчетСНиСО.ИсчисленныеООСМС КАК ДокТЧ
ГДЕ
	ДокТЧ.Ссылка = &Ссылка
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
	РаботникиОрганизаций.Организация КАК Организация,
	РаботникиОрганизаций.Сотрудник.Физлицо КАК ФизЛицо,
	МАКСИМУМ(РаботникиОрганизаций.Период) КАК Период
ПОМЕСТИТЬ ВТ_МаксимальныеПериоды
ИЗ
	РегистрСведений.РаботникиОрганизаций КАК РаботникиОрганизаций
ГДЕ
	РаботникиОрганизаций.Организация = &Организация
	И РаботникиОрганизаций.Период <= &Период
	И РаботникиОрганизаций.Сотрудник.Физлицо В
		(ВЫБРАТЬ ВТ.ФизЛицо ИЗ ВТ_ФизЛица КАК ВТ)
	И РаботникиОрганизаций.Сотрудник.ВидЗанятости <> &ВнутреннееСовместительство
СГРУППИРОВАТЬ ПО
	РаботникиОрганизаций.Организация,
	РаботникиОрганизаций.Сотрудник.Физлицо
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
	ВТ_ФизЛица.ФизЛицо КАК ФизЛицо,
	РаботникиОрганизаций.Сотрудник КАК Сотрудник,
	РаботникиОрганизаций.ПодразделениеОрганизации КАК Подразделение,
	РаботникиОрганизаций.Должность КАК Должность
ИЗ
	ВТ_ФизЛица КАК ВТ_ФизЛица
	ЛЕВОЕ СОЕДИНЕНИЕ ВТ_МаксимальныеПериоды КАК Периоды
		ПО (Периоды.ФизЛицо = ВТ_ФизЛица.ФизЛицо)
	ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.РаботникиОрганизаций КАК РаботникиОрганизаций
		ПО (РаботникиОрганизаций.Организация = Периоды.Организация)
		И (РаботникиОрганизаций.Период = Периоды.Период)
		И (РаботникиОрганизаций.Сотрудник.Физлицо = Периоды.ФизЛицо)

Важные рекомендации при работе с кадровыми регистрами

Выясним причину возможных расхождений и правила их предотвращения:

  1. Учет увольнений: если сотрудник уволен, в регистре сведений создается запись с признаком увольнения (или нулевой ставкой). При получении среза последних на дату после увольнения запрос вернет последнюю запись. Если уволенных сотрудников выводить не нужно, добавьте отбор по условию состояния сотрудника (например, ПричинаИзмененияСостояния <> &ПарамУволен).
  2. Использование программных методов: в типовых конфигурациях для получения срезов кадровых данных на дату рекомендуется вызывать типовые процедуры общих модулей (например, КадровыйУчет), которые учитывают все нюансы стажей, совмещений и кадровых перемещений.
  3. Программный срез для одного лица: если требуется быстро получить подразделение одного сотрудника в коде, используйте метод менеджера регистра:
    
    Отбор = Новый Структура;
    Отбор.Вставить("Организация", ГоловнаяОрганизация);
    Отбор.Вставить("Сотрудник", ТекущийСотрудник);
    
    Срез = РегистрыСведений.РаботникиОрганизаций.СрезПоследних(ДатаАктуальности, Отбор);
    Если Срез.Количество() > 0 Тогда
    	ТекущееПодразделение = Срез[0].ПодразделениеОрганизации;
    КонецЕсли;
    
← На главную