В процессе разработки на платформе 1С:Предприятие 8.2 (обычное приложение) часто возникает задача сопоставления данных табличной части документа с информацией из справочников. Типичный сценарий: в документе указан некий строковый идентификатор или код, по которому нужно найти соответствующий элемент в справочнике и заполнить дополнительные реквизиты в строках документа. Рассмотрим подробно, как это реализовать правильно, проанализируем типичные ошибки новичков и выберем наиболее оптимальный метод решения.
Разберем ситуацию, когда разработчик пытается использовать метод Выбрать() внутри цикла обхода табличной части. Это одна из самых распространенных ошибок, которая приводит к тому, что код либо не работает вовсе, либо работает крайне медленно. Проанализируем пример некорректного кода:
СпрСогл = Справочники.СогласованиеТехНаименованийМатериаловСБух.Выбрать();
Для каждого стр Из товары Цикл
Пока СпрСогл.Следующий() Цикл
Если СокрЛП(стр.НоменклатурныйНомер) = СокрЛП(SпрСогл.Код) Тогда
Сообщить(СпрСогл.Код);
КонецЕсли;
КонецЦикла;
КонецЦикла;
Выясним причину, почему этот код не работает. Объект Выборка в 1С имеет внутренний указатель. При первом проходе цикла по первой строке табличной части выборка справочника доходит до самого конца. Когда выполнение переходит ко второй строке документа, выборка уже находится в состоянии "конец", и метод Следующий() сразу возвращает Ложь. Чтобы такой код заработал, выборку необходимо либо инициализировать заново внутри цикла (что крайне неэффективно), либо использовать метод Сбросить().
Однако даже с исправлениями такой подход считается "плохим тоном" (или "гуано-кодом"), так как он заставляет систему перебирать весь справочник для каждой строки документа. Если в справочнике 10 000 элементов, а в документе 100 строк, система выполнит 1 000 000 операций сравнения.
Более простым и надежным способом для обычного приложения является использование встроенного метода поиска. Рассмотрим, как переписать алгоритм, чтобы он работал корректно. Вместо полного перебора справочника мы будем обращаться к конкретному элементу по его уникальному коду.
Разберем реализацию по шагам:
НайтиПоКоду().
Для каждого Стр Из Товары Цикл
ПоисковыйКод = Стр.НоменклатурныйНомер;
// Ищем ссылку на элемент справочника
СсылкаНаСпр = Справочники.СогласованиеТехНаименованийМатериаловСБух.НайтиПоКоду(ПоисковыйКод);
// Обязательно проверяем, найден ли элемент
Если НЕ СсылкаНаСпр.Пустая() Тогда
Сообщить("Найден код: " + СсылкаНаСпр.Код);
// Записываем данные в ТЧ, если это необходимо
// Стр.КодИзСправочника = СсылкаНаСпр.Код;
Иначе
Сообщить("Элемент с кодом " + ПоисковыйКод + " не найден!");
КонецЕсли;
КонецЦикла;
Важный нюанс: при поиске по коду убедитесь, что типы данных совпадают. Если в справочнике код строковый (например, длиной 9 символов с ведущими нулями "000000123"), а в документе в колонке НоменклатурныйНомер записано число или строка без нулей ("123"), поиск вернет пустую ссылку. В таких случаях строку нужно предварительно дополнить нулями до нужной длины.
Для профессиональной разработки в 1С использование запросов является золотым стандартом. Это позволяет избежать многократных обращений к базе данных внутри цикла. Вместо того чтобы выполнять поиск для каждой строки отдельно, мы выполним один запрос, который сразу вернет все необходимые данные для всей табличной части.
Рассмотрим алгоритм работы с запросом:
ГДЕ Код В (&МассивКодов).
// 1. Подготовка списка кодов
МассивКодов = Товары.ВыгрузитьКолонку("НоменклатурныйНомер");
// 2. Создание и настройка запроса
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Согласование.Код КАК Код,
| Согласование.Ссылка КАК Ссылка
|ИЗ
| Справочник.СогласованиеТехНаименованийМатериаловСБух КАК Согласование
|ГДЕ
| Согласование.Код В(&МассивКодов)";
Запрос.УстановитьПараметр("МассивКодов", МассивКодов);
// 3. Выполнение запроса
РезультатЗапроса = Запрос.Выполнить();
// 4. Заполнение данных (используем Соответствие для быстрого поиска в памяти)
ТаблицаРезультата = РезультатЗапроса.Выгрузить();
СоответствиеКодов = Новый Соответствие;
Для каждого СтрРез Из ТаблицаРезультата Цикл
СоответствиеКодов.Вставить(СтрРез.Код, СтрРез.Ссылка);
КонецЦикла;
// 5. Обход ТЧ документа и заполнение
Для каждого СтрокаТЧ Из Товары Цикл
НайденнаяСсылка = СоответствиеКодов.Получить(СтрокаТЧ.НоменклатурныйНомер);
Если НайденнаяСсылка <> Неопределено Тогда
// Здесь производим запись данных в ТЧ
Сообщить("Для строки найден объект: " + НайденнаяСсылка);
КонецЕсли;
КонецЦикла;
Проанализируем преимущества этого метода. Во-первых, обращение к базе происходит один раз. Во-вторых, использование объекта Соответствие позволяет мгновенно находить данные в оперативной памяти без повторных обращений к серверу БД. Это критически важно, если в документе сотни или тысячи строк.
Если вы написали код, но метод Сообщить() ничего не выводит, не спешите паниковать. В системе 1С есть несколько причин такого поведения:
Если СокрЛП(стр.НоменклатурныйНомер) = СокрЛП(СпрСогл.Код) не выполняется, сообщение просто не вызывается. Используйте точку останова (клавиша F9) на строке с Если и посмотрите значения переменных в табло.Товары на момент выполнения кода. Возможно, вы пытаетесь обработать пустую таблицу.Для надежной проверки данных рекомендуем использовать визуальное сравнение в отладчике. Обращайте внимание на тип значения: "123" (строка) не равно 123 (число). Также учитывайте наличие невидимых символов и неразрывных пробелов, которые функция СокрЛП может не удалять.
Существует мнение, что для записи данных лучше выгрузить ТЧ в таблицу значений, обработать её и загрузить обратно. Посмотрим на этот подход:
ТЧ_Таблица = ЭтотОбъект.Товары.Выгрузить();
// ... обработка ТЧ_Таблица ...
ЭтотОбъект.Товары.Загрузить(ТЧ_Таблица);
Этот метод допустим, но в обычном приложении 8.2 он часто избыточен. Прямой обход строк через Для каждого СтрокаТЧ Из Товары Цикл работает быстрее и потребляет меньше памяти, так как не создает дубликат данных в виде таблицы значений. Используйте Выгрузить/Загрузить только если вам нужно выполнить сложные структурные изменения таблицы (например, сортировку или группировку), которые сложно реализовать при простом обходе.
Подводя итог, отметим: всегда стремитесь использовать запросы и индексированные поля (такие как Код или Артикул) для поиска. Это обеспечит стабильную работу вашей программы даже при значительном росте объема данных в базе.