Как при выборе контрагента в разных документах открывать справочник в определенной папке (клиенты/поставщики) в 1С 7.7?

Программист 1С v7.7 Управленческий учет IT и автоматизация бизнеса
← На главную

При работе в программной среде «1С:Предприятие 7.7» часто возникает задача оптимизировать интерфейс для ускорения ввода документов. Одной из таких задач является автоматическое открытие справочника контрагентов на конкретной группе (папке) в зависимости от контекста. Например, когда мы оформляем документ РасходнаяНакладная, логично сразу показывать пользователю группу «Клиенты» (или «Покупатели»), а при создании документа ПриходнаяНакладная — группу «Поставщики».

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

Разбор типичных ошибок при реализации в 1С 7.7

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


// Ошибочный код! Не используйте его в своих конфигурациях.
Процедура ПриНачалеВыбораЗначения(РеквИмя, ФСО)
    Если РеквИмя = "Контрагент" Тогда
        КонтекстВыбораКонтрагента = Контрагент.ТекущийЭлемент();
        Если КонтекстВыбораКонтрагента.Выбран() <> 1 Тогда
            НачГруппаКонтрагентов = СоздатьОбъект("Справочник.Contragents");
            // ... поиск по наименованию ...
            КонтекстВыбораКонтрагента.ИспользоватьРодителя(НачГруппаКонтрагентов);
        КонецЕсли;
    КонецЕсли;
КонецПроцедуры

Выясним причину, почему данный код не будет работать в реальной базе 1С 7.7:

  1. Попытка вызвать метод у ссылки: Переменная КонтекстВыбораКонтрагента получает значение через Контрагент.ТекущийЭлемент(). Это возвращает ссылку на элемент (тип значения «Справочник»). Метод ИспользоватьРодителя() не существует у элементов-ссылок. Он может быть вызван только у объекта типа справочник, созданного через СоздатьОбъект(), либо внутри модуля самой формы списка.
  2. Проблема с асинхронным открытием: Если просто отключить стандартную обработку установкой флага ФСО = 0 и вызвать функцию ОткрытьФорму(), система откроет форму списка справочника немодально. Программа продолжит выполнение кода, не дожидаясь выбора пользователя, и выбранное значение не вернется в документ автоматически.
  3. Множественный избыточный поиск: Повторяющиеся вызовы метода НайтиПоНаименованию() замедляют работу системы и делают код запутанным и сложным для поддержки.

Теперь рассмотрим правильные, протестированные и надежные способы реализации этой задачи.

Способ №1. Модальный выбор с фильтрацией по родителю (Рекомендуемый)

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

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


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

Для документа «Приходная накладная» код будет абсолютно идентичным, за исключением одной строки: вместо наименования группы "Клиенты" вам нужно будет указать "Поставщики".

Способ №2. Передача контекста в немодальную форму (Без изменения справочника)

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

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


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

// Поскольку форма открыта немодально, выбор вернется в предопределенную процедуру документа
Процедура ОбработкаВыбораЗначения(ВыбрЗнач, ИдентРекв, ФСО)
    
    Если ИдентРекв = "Контрагент" Тогда
        // Заполняем реквизит выбранным значением
        Контрагент = ВыбрЗнач;
    КонецЕсли;
    
КонецПроцедуры

Способ №3. Передача параметра в форму списка (Классический метод «1С»)

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

Шаг 1. Пропишем код вызова в модуле формы документа:


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

Шаг 2. Теперь нам необходимо обработать переданный параметр в модуле Формы Списка самого справочника Контрагенты. Откроем форму списка справочника в конфигураторе, перейдем в ее модуль и найдем предопределенную процедуру ПриОткрытии:


Процедура ПриОткрытии()
    
    // Проверяем, был ли передан параметр при открытии формы
    Если ПустоеЗначение(Форма.Параметр) = 0 Тогда
        
        // Проверяем, является ли переданный параметр группой справочника
        Если Форма.Параметр.ЭтоГруппа() = 1 Тогда
            
            // Устанавливаем эту группу в качестве родителя в форме списка
            // Второй параметр (0) запрещает выходить «вверх» к корню справочника
            ИспользоватьРодителя(Форма.Параметр, 0);
            
        КонецЕсли;
        
    КонецЕсли;
    
КонецПроцедуры

Проблема юзабилити: Папки против Реквизитов-Флагов

Рассматривая этот технический вопрос, мы обязательно должны проанализировать архитектуру хранения данных в вашей информационной базе. На форуме справедливо прозвучал вопрос: «Что делать, когда клиент является одновременно покупателем машинок и продавцом колес?»

Если вы делите контрагентов по жестким физическим папкам («Клиенты» и «Поставщики»), вы неизбежно столкнетесь со следующими проблемами:

Альтернативное современное решение

Гораздо более правильным архитектурным решением является отказ от деления контрагентов по папкам на основе их роли. Вместо этого в справочнике Контрагенты создаются два реквизита типа «Число» (длиной 1) или «Флаг»:

  1. Покупатель (Да/Нет)
  2. Поставщик (Да/Нет)

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

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


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

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

← На главную