Как вывести наименование и код номенклатуры контрагента в форму списка справочника Номенклатура в 1С?

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

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

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

Анализ типичных ошибок начинающих разработчиков

Рассмотрим распространенные проблемы, с которыми сталкиваются при попытке вывода данных в табличное поле списка:

  1. Ошибка «Недостаточно фактических параметров»: возникает в тех случаях, когда при объявлении функции указано больше формальных параметров, чем передается при фактическом вызове (например, в заголовке функции объявлено Функция ПолучитьНоменклатуруКонтрагентов(Номенклатура, НоменклатураКонтрагентов), а вызывается как ПолучитьНоменклатуруКонтрагентов(ДанныеСтроки.Номенклатура)).
  2. Ошибка «Значение не является значением объектного типа»: попытка прямого обращения к ДанныеСтроки вне контекста или обращение к несуществующему полю (в списке справочника ссылка на текущий элемент хранится в свойстве Ссылка, а не Номенклатура).
  3. Архитектурная ошибка (запрос в цикле): выполнение запроса к базе данных для каждой отдельной строки списка в обработчиках событий приводит к критическому падению производительности и зависанию формы при прокрутке списка.
  4. Неопределенность связи «один ко многим»: у одной позиции номенклатуры в регистре может быть зарегистрировано несколько контрагентов. Без явного указания поставщика или группировки выборка вернет произвольную запись.

Правильная настройка колонок табличного поля

Перед написанием программного кода необходимо настроить визуальные элементы формы списка:

  1. Откроем форму списка справочника Номенклатура в конфигураторе.
  2. В свойствах табличного поля списка добавим новые колонки: НаименованиеНоменклатурыКонтрагента и КодНоменклатурыКонтрагента.
  3. Важно: свойство Данные (Data) добавленных колонок обязательно оставляем пустым, так как заполнение будет производиться исключительно программным способом.

Реализация пакетного чтения данных в событии ПриПолученииДанных

В обычном приложении табличное поле списка с типом СправочникСписок не поддерживает произвольные запросы. Для отображения вычисляемых данных предназначено событие ПриПолученииДанных. Оно передает массив уже прочитанных строк ОформленияСтрок, что позволяет получить данные для всей видимой порции списка одним запросом.

Разберем по шагам алгоритм правильной реализации:

  1. Обойдем коллекцию ОформленияСтрок, отсечем группы справочника и соберем массив ссылок на отображаемые элементы номенклатуры.
  2. Выполним один запрос к регистру сведений НоменклатураКонтрагентов с условием ГДЕ Номенклатура В (&МассивНоменклатуры).
  3. Если требуется выводить данные основного поставщика, свяжем номенклатуру с ее реквизитом ОсновнойПоставщик. Если поставщик не задан, сгруппируем данные по номенклатуре.
  4. Поместим результат запроса в структуру или объект Соответствие для быстрого доступа по ключу ссылки.
  5. Повторно обойдем ОформленияСтрок и заполним текст ячеек методом УстановитьТекст().

Посмотрим на готовый пример процедуры обработчика:


Процедура СписокПриПолученииДанных(Элемент, ОформленияСтрок)
    
    МассивНоменклатуры = Новый Массив;
    
    // Шаг 1. Собираем ссылки на номенклатуру видимых строк
    Для Каждого Оформление Из ОформленияСтрок Цикл
        // Пропускаем папки (группы), если справочник иерархический
        Если Оформление.ДанныеСтроки.ЭтоГруппа Тогда
            Продолжить;
        КонецЕсли;
        
        МассивНоменклатуры.Добавить(Оформление.ДанныеСтроки.Ссылка);
    КонецЦикла;
    
    Если МассивНоменклатуры.Количество() = 0 Тогда
        Возврат;
    КонецЕсли;
    
    // Шаг 2. Получаем данные из регистра сведений одним пакетным запросом
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |    СпрНоменклатура.Ссылка КАК Номенклатура,
        |    ЕСТЬNULL(РС.НаименованиеНоменклатурыКонтрагента, """") КАК НаименованиеНоменклатурыКонтрагента,
        |    ЕСТЬNULL(РС.КодНоменклатурыКонтрагента, """") КАК КодНоменклатурыКонтрагента
        |ИЗ
        |    Справочник.Номенклатура КАК СпрНоменклатура
        |    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.НоменклатураКонтрагентов КАК РС
        |        ПО СпрНоменклатура.Ссылка = РС.Номенклатура
        |        И СпрНоменклатура.ОсновнойПоставщик = РС.Контрагент
        |ГДЕ
        |    СпрНоменклатура.Ссылка В(&МассивНоменклатуры)";
    
    Запрос.УстановитьПараметр("МассивНоменклатуры", МассивНоменклатуры);
    
    Выгрузка = Запрос.Выполнить().Выгрузить();
    
    // Шаг 3. Формируем соответствие для мгновенного поиска
    ДанныеПоНоменклатуре = Новый Соответствие;
    Для Каждого Стр Из Выгрузка Цикл
        ДанныеПоНоменклатуре.Вставить(Стр.Номенклатура, Новый Структура(
            "Наименование, Код",
            Стр.НаименованиеНоменклатурыКонтрагента,
            Стр.КодНоменклатурыКонтрагента
        ));
    КонецЦикла;
    
    // Шаг 4. Устанавливаем значения в ячейки колонок
    Для Каждого Оформление Из ОформленияСтрок Цикл
        Если Оформление.ДанныеСтроки.ЭтоГруппа Тогда
            Продолжить;
        КонецЕсли;
        
        ДанныеСтрокиНоменклатуры = ДанныеПоНоменклатуре[Оформление.ДанныеСтроки.Ссылка];
        Если ДанныеСтрокиНоменклатуры <> Неопределено Тогда
            
            Если Не ПустаяСтрока(ДанныеСтрокиНоменклатуры.Наименование) Тогда
                Оформление.Ячейки.НаименованиеНоменклатурыКонтрагента.УстановитьТекст(ДанныеСтрокиНоменклатуры.Наименование);
            КонецЕсли;
            
            Если Не ПустаяСтрока(ДанныеСтрокиНоменклатуры.Код) Тогда
                Оформление.Ячейки.КодНоменклатурыКонтрагента.УстановитьТекст(ДанныеСтрокиНоменклатуры.Код);
            КонецЕсли;
            
        КонецЕсли;
    КонецЦикла;

КонецПроцедуры

Особенности работы с регистром при отсутствии основного поставщика

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

В этом случае в запросе следует использовать группировку с агрегатными функциями МИНИМУМ или МАКСИМУМ, чтобы исключить дублирование строк при объединении:


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

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

← На главную