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

Программист 1С v8.3 (Обычные формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

При разработке внешних обработок и документов в конфигурациях 1С на обычных формах (таких как «Управление торговлей 10.3», «Управление производственным предприятием 1.3» или «Бухгалтерия предприятия 2.0») программисты часто сталкиваются с задачей взаимной фильтрации реквизитов. Типичный сценарий: на форме присутствуют поля Организация, Контрагент и Договор контрагента. Необходимо сделать так, чтобы при выборе договора пользователь видел только те соглашения, которые заключены между выбранной организацией и выбранным контрагентом, а также реализовать автоматическую подстановку договора при изменении других реквизитов.

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

Часть 1. Разбор ошибки двойного открытия формы выбора (событие НачалоВыбора)

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

Посмотрим на исходный пример кода, который вызывает зацикливание:


Процедура ВыбДоговорНачалоВыбора(Элемент)
    нФормаВыбора = Справочники.ДоговорыКонтрагентов.ПолучитьФормуВыбора(,Элемент);
    нФормаВыбора.Отбор.Организация.Использование = Истина;
    нФормаВыбора.Отбор.Организация.ВидСравнения = ВидСравнения.Равно;
    нФормаВыбора.Отбор.Организация.Установить(ВыбОрганизации); 
    нФормаВыбора.Отбор.Владелец.Использование = Истина;
    нФормаВыбора.Отбор.Владелец.ВидСравнения = ВидСравнения.Равно;
    нФормаВыбора.Отбор.Владелец.Установить(ВыбКонтрагент);  
    нФормаВыбора.ОткрытьМодально();
КонецПроцедуры

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

Чтобы решить эту проблему, необходимо использовать параметр СтандартнаяОбработка, который передается в предопределенное событие НачалоВыбора. Установка этого параметра в значение Ложь сообщает платформе, что стандартное открытие формы выполнять не нужно, так как разработчик полностью переопределил это действие в коде.

Часть 2. Правильное программное ограничение списка договоров

Помимо отключения стандартной обработки, крайне важно отказаться от использования устаревшего метода ОткрытьМодально(). Модальные окна блокируют интерфейс программы, ухудшают производительность и не поддерживаются в современных веб-клиентах. Вместо этого мы будем использовать немодальный метод Открыть().

Разберем по шагам правильную реализацию процедуры НачалоВыбора для поля договора:


Процедура ВыбДоговорНачалоВыбора(Элемент, СтандартнаяОбработка)
    
    // 1. Отключаем стандартный механизм открытия формы выбора платформой
    СтандартнаяОбработка = Ложь;
    
    // 2. Получаем форму выбора справочника "Договоры контрагентов".
    // Вторым параметром передаем "Элемент" (владелец формы), чтобы платформа 
    // автоматически вернула выбранный договор обратно в это поле ввода.
    ФормаВыбораДоговора = Справочники.ДоговорыКонтрагентов.ПолучитьФормуВыбора("ФормаВыбора", Элемент, Элемент);
    
    // 3. Устанавливаем отбор по Организации (если она заполнена на форме)
    Если ЗначениеЗаполнено(ВыбОрганизации) Тогда
        ФормаВыбораДоговора.Отбор.Организация.Использование = Истина;
        ФормаВыбораДоговора.Отбор.Организация.ВидСравнения = ВидСравнения.Равно;
        ФормаВыбораДоговора.Отбор.Организация.Установить(ВыбОрганизации);
    Иначе
        // Если организация не выбрана, очищаем использование отбора
        ФормаВыбораДоговора.Отбор.Организация.Использование = Ложь;
    КонецЕсли;
    
    // 4. Устанавливаем отбор по Владельцу (Контрагенту)
    Если ЗначениеЗаполнено(ВыбКонтрагент) Тогда
        ФормаВыбораДоговора.Отбор.Владелец.Использование = Истина;
        ФормаВыбораДоговора.Отбор.Владелец.ВидСравнения = ВидСравнения.Равно;
        ФормаВыбораДоговора.Отбор.Владелец.Установить(ВыбКонтрагент);
    Иначе
        ФормаВыбораДоговора.Отбор.Владелец.Использование = Ложь;
    КонецЕсли;
    
    // 5. Открываем форму выбора в обычном немодальном режиме
    ФормаВыбораДоговора.Открыть();
    
КонецПроцедуры

Обратите внимание на проверку ЗначениеЗаполнено(). Если пользователь еще не выбрал контрагента или организацию, отбор по ним устанавливать нельзя (иначе платформа попытается найти договоры с пустой организацией или пустым контрагентом, и список выбора окажется абсолютно пустым). Если реквизиты не заполнены, мы просто отключаем использование соответствующих отборов.

Часть 3. Реализация полноценного автозаполнения договора

Ограничение списка выбора в форме — это отличное решение, но для максимального удобства пользователя систему нужно научить автоматически подставлять подходящий договор сразу после того, как были изменены поля «Организация» или «Контрагент». Это избавит пользователя от лишних кликов мышью.

Для реализации этого функционала проанализируем структуру данных конфигурации «Управление торговлей 10.3». У каждого контрагента в системе есть реквизит ОсновнойДоговорКонтрагента. Логика автоматического подбора должна быть следующей:

  1. Проверяем, заполнены ли контрагент и организация.
  2. Проверяем, совпадает ли организация в основном договоре контрагента с организацией, выбранной на форме. Если совпадает — подставляем этот договор.
  3. Если основной договор оформлен на другую организацию или не заполнен, мы делаем запрос к базе данных и ищем любой другой действующий (не помеченный на удаление) договор этого контрагента с нашей организацией.
  4. Если договоров нет, очищаем поле договора.

Реализуем вспомогательную функцию поиска договора и обработчики событий изменения полей на форме:


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

// Обработчик события изменения поля "Организация"
Процедура ВыбОрганизацииПриИзменении(Элемент)
    // Пересчитываем и заполняем договор при изменении организации
    ВыбДоговор = ПолучитьДоговорПоУмолчанию(ВыбКонтрагент, ВыбОрганизации);
КонецПроцедуры

// Обработчик события изменения поля "Контрагент"
Процедура ВыбКонтрагентПриИзменении(Элемент)
    // Пересчитываем и заполняем договор при изменении контрагента
    ВыбДоговор = ПолучитьДоговорПоУмолчанию(ВыбКонтрагент, ВыбОрганизации);
КонецПроцедуры

Резюме и рекомендации

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

← На главную