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

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

В процессе разработки на платформе 1С:Предприятие 8.2 (обычное приложение) часто возникает задача сопоставления данных табличной части документа с информацией из справочников. Типичный сценарий: в документе указан некий строковый идентификатор или код, по которому нужно найти соответствующий элемент в справочнике и заполнить дополнительные реквизиты в строках документа. Рассмотрим подробно, как это реализовать правильно, проанализируем типичные ошибки новичков и выберем наиболее оптимальный метод решения.

Анализ типичной ошибки при использовании выборки

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


СпрСогл = Справочники.СогласованиеТехНаименованийМатериаловСБух.Выбрать();
Для каждого стр Из товары Цикл
    Пока СпрСогл.Следующий() Цикл
        Если СокрЛП(стр.НоменклатурныйНомер) = СокрЛП(SпрСогл.Код) Тогда
            Сообщить(СпрСогл.Код);
        КонецЕсли;
    КонецЦикла;
КонецЦикла;

Выясним причину, почему этот код не работает. Объект Выборка в 1С имеет внутренний указатель. При первом проходе цикла по первой строке табличной части выборка справочника доходит до самого конца. Когда выполнение переходит ко второй строке документа, выборка уже находится в состоянии "конец", и метод Следующий() сразу возвращает Ложь. Чтобы такой код заработал, выборку необходимо либо инициализировать заново внутри цикла (что крайне неэффективно), либо использовать метод Сбросить().

Однако даже с исправлениями такой подход считается "плохим тоном" (или "гуано-кодом"), так как он заставляет систему перебирать весь справочник для каждой строки документа. Если в справочнике 10 000 элементов, а в документе 100 строк, система выполнит 1 000 000 операций сравнения.

Решение проблемы с помощью метода НайтиПоКоду

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

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

  1. Обойдем строки табличной части документа.
  2. Для каждой строки получим код для поиска.
  3. Используем метод НайтиПоКоду().
  4. Проверим, найден ли элемент (не является ли ссылка пустой).
  5. Запишем результат в нужную колонку или выведем сообщение.

Для каждого Стр Из Товары Цикл
    ПоисковыйКод = Стр.НоменклатурныйНомер;
    // Ищем ссылку на элемент справочника
    СсылкаНаСпр = Справочники.СогласованиеТехНаименованийМатериаловСБух.НайтиПоКоду(ПоисковыйКод);
    
    // Обязательно проверяем, найден ли элемент
    Если НЕ СсылкаНаСпр.Пустая() Тогда
        Сообщить("Найден код: " + СсылкаНаСпр.Код);
        // Записываем данные в ТЧ, если это необходимо
        // Стр.КодИзСправочника = СсылкаНаСпр.Код; 
    Иначе
        Сообщить("Элемент с кодом " + ПоисковыйКод + " не найден!");
    КонецЕсли;
КонецЦикла;

Важный нюанс: при поиске по коду убедитесь, что типы данных совпадают. Если в справочнике код строковый (например, длиной 9 символов с ведущими нулями "000000123"), а в документе в колонке НоменклатурныйНомер записано число или строка без нулей ("123"), поиск вернет пустую ссылку. В таких случаях строку нужно предварительно дополнить нулями до нужной длины.

Оптимальный способ: использование запросов

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

Рассмотрим алгоритм работы с запросом:

  1. Выгружаем колонку с кодами из табличной части в массив.
  2. Передаем этот массив в запрос в качестве параметра.
  3. Выполняем запрос с условием ГДЕ Код В (&МассивКодов).
  4. Получаем результат и обходим его, заполняя данные в документе.

// 1. Подготовка списка кодов
МассивКодов = Товары.ВыгрузитьКолонку("НоменклатурныйНомер");

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

Запрос.УстановитьПараметр("МассивКодов", МассивКодов);

// 3. Выполнение запроса
РезультатЗапроса = Запрос.Выполнить();

// 4. Заполнение данных (используем Соответствие для быстрого поиска в памяти)
ТаблицаРезультата = РезультатЗапроса.Выгрузить();
СоответствиеКодов = Новый Соответствие;
Для каждого СтрРез Из ТаблицаРезультата Цикл
    СоответствиеКодов.Вставить(СтрРез.Код, СтрРез.Ссылка);
КонецЦикла;

// 5. Обход ТЧ документа и заполнение
Для каждого СтрокаТЧ Из Товары Цикл
    НайденнаяСсылка = СоответствиеКодов.Получить(СтрокаТЧ.НоменклатурныйНомер);
    Если НайденнаяСсылка <> Неопределено Тогда
        // Здесь производим запись данных в ТЧ
        Сообщить("Для строки найден объект: " + НайденнаяСсылка);
    КонецЕсли;
КонецЦикла;

Проанализируем преимущества этого метода. Во-первых, обращение к базе происходит один раз. Во-вторых, использование объекта Соответствие позволяет мгновенно находить данные в оперативной памяти без повторных обращений к серверу БД. Это критически важно, если в документе сотни или тысячи строк.

Особенности работы метода Сообщить() и отладки

Если вы написали код, но метод Сообщить() ничего не выводит, не спешите паниковать. В системе 1С есть несколько причин такого поведения:

  1. Выполнение на сервере: В управляемом приложении (и иногда в обычном при работе через серверные модули) сообщения, сформированные на сервере, передаются клиенту только после завершения всего серверного вызова. Если код "завис" или прервался ошибкой раньше, сообщения не отобразятся.
  2. Несоответствие типов: Если условие Если СокрЛП(стр.НоменклатурныйНомер) = СокрЛП(СпрСогл.Код) не выполняется, сообщение просто не вызывается. Используйте точку останова (клавиша F9) на строке с Если и посмотрите значения переменных в табло.
  3. Пустые данные: Проверьте, заполнены ли вообще данные в табличной части Товары на момент выполнения кода. Возможно, вы пытаетесь обработать пустую таблицу.

Для надежной проверки данных рекомендуем использовать визуальное сравнение в отладчике. Обращайте внимание на тип значения: "123" (строка) не равно 123 (число). Также учитывайте наличие невидимых символов и неразрывных пробелов, которые функция СокрЛП может не удалять.

Рекомендации по записи данных в ТЧ

Существует мнение, что для записи данных лучше выгрузить ТЧ в таблицу значений, обработать её и загрузить обратно. Посмотрим на этот подход:


ТЧ_Таблица = ЭтотОбъект.Товары.Выгрузить();
// ... обработка ТЧ_Таблица ...
ЭтотОбъект.Товары.Загрузить(ТЧ_Таблица);

Этот метод допустим, но в обычном приложении 8.2 он часто избыточен. Прямой обход строк через Для каждого СтрокаТЧ Из Товары Цикл работает быстрее и потребляет меньше памяти, так как не создает дубликат данных в виде таблицы значений. Используйте Выгрузить/Загрузить только если вам нужно выполнить сложные структурные изменения таблицы (например, сортировку или группировку), которые сложно реализовать при простом обходе.

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

← На главную