При разработке отчетов и обработок на платформе 1С 7.7 часто возникает задача сопоставить данные из табличной части документа (например, банковской выписки) с дополнительными реквизитами справочников (кодами, полными наименованиями контрагентов). Попытка объединить две таблицы значений (ТЗ) через вложенные циклы или некорректный вызов метода НайтиЗначение() нередко приводит к ошибкам: во всех строках дублируются данные первого найденного элемента или поиск перестает работать вовсе.
Разберем подробно причины такого поведения, особенности работы встроенных методов платформы и правильные способы решения этой задачи.
Проанализируем типовые ошибки, которые возникают при реализации алгоритма поиска и заполнения реквизитов:
УстановитьЗначение(), в итоге в первую таблицу запишутся данные из самой последней строки второй таблицы.НайтиЗначение(): переменная номера строки для этого метода является двунаправленным параметром (входным и выходным). Если не сбрасывать ее значение перед каждым вызовом, поиск выдает неверный результат.Если ПустоеЗначение(...) = 1 Тогда Продолжить; КонецЕсли;, помещенная в конец тела цикла уже после вызова метода НоваяСтрока(), приводит к созданию лишних пустых строк в результирующей таблице.СокрЛП() и последующий поиск по наименованию снижают надежность, так как наименования могут совпадать или содержать скрытые пробелы.Рассмотрим подробнее синтаксис метода НайтиЗначение() объекта ТаблицаЗначений:
ТЗ.НайтиЗначение(ИскомоеЗначение, ПеременнаяСтроки, ИмяИлиНомерКолонки);
Ключевая особенность заключается в том, как платформа обрабатывает второй параметр — ПеременнаяСтроки:
0, платформа выполняет поиск по всей колонке, начиная с первой строки (строка 1). Если же в переменную передано число N > 0, поиск начнется именно со строки под номером N.0, а значение переменной остается неизменным.Выясним причину ошибки: если объявить переменную нСтр = 0; до начала цикла и не сбрасывать ее внутри каждой итерации, то после первого успешного поиска (например, в строке 1) переменная получит значение 1. При следующей итерации поиск начнется со строки 1, снова найдет первое совпадение, и в текущую строку опять подставится первый контрагент. Именно поэтому сброс переменной в 0 внутри цикла перед каждым поиском обязателен.
Разберем по шагам правильную организацию обхода строк документа и сопоставления данных со справочником контрагентов.
Шаг 1. Формирование таблицы контрагентов (ТЗ2):
ТЗ2 = СоздатьОбъект("ТаблицаЗначений");
ТЗ2.НоваяКолонка("КК", "Строка", 10);
ТЗ2.НоваяКолонка("Наименование2", "Строка", 100);
ТЗ2.НоваяКолонка("ПолнНаименование", "Строка", 200);
СпрКонтр = СоздатьОбъект("Справочник.Контрагенты");
СпрКонтр.ВыбратьЭлементы();
Пока СпрКонтр.ПолучитьЭлемент() = 1 Цикл
Если СпрКонтр.ЭтоГруппа() = 1 Тогда
Продолжить;
КонецЕсли;
// Проверяем принадлежность к нужной группе
Если СпрКонтр.ПринадлежитГруппе(ГруппаДомбай) = 1 Тогда
ТЗ2.НоваяСтрока();
ТЗ2.КК = СпрКонтр.Код;
ТЗ2.Наименование2 = СокрЛП(СпрКонтр.Наименование);
ТЗ2.ПолнНаименование = СокрЛП(СпрКонтр.ПолнНаименование);
КонецЕсли;
КонецЦикла;
Шаг 2. Обход строк документа и поиск в ТЗ2:
Посмотрим на пример правильного заполнения первой таблицы с обязательным сбросом переменной номера строки:
ТЗ1 = СоздатьОбъект("ТаблицаЗначений");
ТЗ1.НоваяКолонка("НомерСтроки", "Число", 5);
ТЗ1.НоваяКолонка("КоррСчет", "Строка", 10);
ТЗ1.НоваяКолонка("Субконто1", "Строка", 100);
ТЗ1.НоваяКолонка("Субконто2", "Строка", 100);
ТЗ1.НоваяКолонка("Приход", "Число", 15, 2);
ТЗ1.НоваяКолонка("КК", "Строка", 10);
ТЗ1.НоваяКолонка("ПолнНаименование", "Строка", 200);
СчетчикСтрок = 0;
Выписка = Докум;
Выписка.ВыбратьСтроки();
Пока Выписка.ПолучитьСтроку() = 1 Цикл
// Проверку на пустое значение выполняем ДО добавления новой строки
Если ПустоеЗначение(Выписка.Приход) = 1 Тогда
Продолжить;
КонецЕсли;
ТЗ1.НоваяСтрока();
СчетчикСтрок = СчетчикСтрок + 1;
ТЗ1.НомерСтроки = СчетчикСтрок;
ТЗ1.КоррСчет = СокрЛП(Выписка.КоррСчет);
ТЗ1.Субконто1 = СокрЛП(Выписка.Субконто1);
ТЗ1.Субконто2 = СокрЛП(Выписка.Субконто2);
ТЗ1.Приход = Выписка.Приход;
// Обязательно сбрасываем переменную в 0 перед каждым вызовом
нСтр = 0;
Если ТЗ2.НайтиЗначение(ТЗ1.Субконто1, нСтр, "Наименование2") = 1 Тогда
ТЗ1.КК = ТЗ2.ПолучитьЗначение(нСтр, "КК");
ТЗ1.ПолнНаименование = ТЗ2.ПолучитьЗначение(нСтр, "ПолнНаименование");
КонецЕсли;
Сообщить(ТЗ1.КК + " | " + ТЗ1.ПолнНаименование + " | " + ТЗ1.Приход);
КонецЦикла;
В реальной разработке на 1С 7.7 можно существенно сократить код и ускорить его работу следующими методами:
Субконто1 содержит ссылку на справочник Контрагенты, создавать вспомогательную таблицу ТЗ2 вообще не требуется. Достаточно прочитать реквизиты напрямую:
Если ПустоеЗначение(Выписка.Субконто1) = 0 Тогда
Если Выписка.Субконто1.Вид() = "Контрагенты" Тогда
ТЗ1.КК = Выписка.Субконто1.Код;
ТЗ1.ПолнНаименование = Выписка.Субконто1.ПолнНаименование;
КонецЕсли;
КонецЕсли;
ВыгрузитьТабличнуюЧасть(): вместо последовательного перебора строк через ВыбратьСтроки() / ПолучитьСтроку() гораздо быстрее выгрузить табличную часть документа целиком в таблицу значений вызовом Выписка.ВыгрузитьТабличнуюЧасть(ТЗ1);, после чего добавить недостающие колонки и заполнить их.Запрос: для выборки и группировки данных с одновременным соединением реквизитов документа и справочников наиболее производительным штатным инструментом платформы 7.7 является механизм запросов с последующей выгрузкой в таблицу значений методом Запрос.Выгрузить(ТЗ);.