Как реализовать отбор актуальных элементов справочника по состоянию на дату документа в 1С?

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

В практике разработки на платформе 1С часто возникает задача, когда список доступных для выбора элементов справочника должен зависеть от их состояния в определенный момент времени. Типичный пример — выбор транспортного средства (ТС) в документе, где пользователю должны предлагаться только «Годные» машины. Сложность заключается в том, что состояние ТС — это не статичная характеристика, а периодическая величина, которая меняется со временем (вчера машина была годна, сегодня — в ремонте, завтра — снова годна).

В этой статье мы подробно разберем, как реализовать такой механизм, используя управляемые формы, регистры сведений и программные фильтры.

Архитектура хранения данных

Прежде чем приступать к коду, проанализируем, где должны храниться данные. Если мы просто добавим реквизит Состояние в справочник ТранспортныеСредства, мы сможем видеть только текущий статус. Это не позволит нам корректно оформить документ «задним числом» или будущей датой.

Для решения задачи нам понадобится Периодический регистр сведений (например, СостоянияТС). Его структура должна быть следующей:

  1. Измерение: ТС (тип СправочникСсылка.ТранспортныеСредства).
  2. Ресурс: Состояние (тип ПеречислениеСсылка.СостоянияТС со значениями Годен, ВРемонте, НеГоден).
  3. Периодичность: устанавливается в зависимости от бизнес-логики (например, «В пределах дня» или «В пределах секунды»).

Способ 1. Фильтрация через событие НачалоВыбора

Рассмотрим наиболее гибкий способ — программную установку отбора в момент, когда пользователь нажимает на кнопку выбора в поле документа. Мы перехватим это действие, определим список подходящих ТС на дату документа и ограничим список выбора.

Разберем алгоритм по шагам:

  1. Создадим на форме документа обработчик события НачалоВыбора для реквизита ТС.
  2. На сервере выполним запрос к виртуальной таблице СрезПоследних регистра сведений.
  3. Получим массив ссылок на ТС, состояние которых равно «Годен».
  4. Передадим этот массив в параметры формы выбора справочника.

Пример программного кода для реализации этого метода:


&НаКлиенте
Процедура ТСНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
    
    СтандартнаяОбработка = Ложь; // Отключаем стандартное открытие формы
    
    // Получаем дату документа, на которую нужно проверить состояние
    ДатаОценки = Объект.Дата;
    
    // Получаем список годных ТС через серверную функцию
    СписокГодныхТС = ПолучитьСписокГодныхТСНаСервере(ДатаОценки);
    
    // Формируем параметры для открытия формы выбора
    ПараметрыВыбора = Новый Структура;
    ПараметрыВыбора.Вставить("Отбор", Новый Структура("Ссылка", СписокГодныхТС));
    ПараметрыВыбора.Вставить("ЗакрыватьПриВыборе", Истина);
    
    // Открываем форму выбора справочника с наложенным фильтром
    ОткрытьФорму("Справочник.ТранспортныеСредства.ФормаВыбора", ПараметрыВыбора, Элемент);
    
КонецПроцедуры

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

Важный нюанс: использование конструкции Отбор.Ссылка с видом сравнения ВСписке (который автоматически применяется, если передать массив в структуру отбора) позволяет пользователю видеть только те объекты, которые прошли проверку.

Способ 2. Модификация динамического списка формы выбора

Если нам нужно, чтобы отбор работал более «нативно» или если список ТС очень велик (массивы ссылок могут замедлять работу), проанализируем другой вариант — модификацию запроса в самой форме выбора справочника.

В этом случае мы передаем дату документа в форму выбора как параметр, а в форме выбора настраиваем ДинамическийСписок. Рассмотрим реализацию:

  1. В форме выбора справочника в событии ПриСозданииНаСервере принимаем параметр даты.
  2. Меняем текст запроса динамического списка, добавляя левое соединение с регистром сведений.

Пример кода в форме выбора справочника:


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

Чтобы этот код сработал, в документе при открытии формы выбора нужно просто передать параметр ДатаОтбора.

Контроль изменения даты документа

Разберем ситуацию, на которую часто указывают опытные разработчики: что произойдет, если пользователь сначала выбрал ТС на 1-е число месяца (когда оно было годно), а затем изменил дату документа на 15-е число (когда ТС уже в ремонте)?

В этом случае данные в документе станут неактуальными. Чтобы этого избежать, необходимо реализовать проверку в событии ПриИзменении даты документа или в обработчике ПередЗаписью.

Проанализируем пример проверки при изменении даты:


&НаКлиенте
Процедура ДатаПриИзменении(Элемент)
    ПроверитьАктуальностьТС();
КонецПроцедуры

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

Резюме

Мы рассмотрели комплексный подход к решению задачи. Основные выводы:

Выяснив эти принципы, вы сможете реализовывать любые сложные отборы, будь то остатки товаров на складах, статусы сотрудников или доступность оборудования.

← На главную