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