Как правильно объединить две таблицы значений и найти данные в 1С 7.7?

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

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

Разберем подробно причины такого поведения, особенности работы встроенных методов платформы и правильные способы решения этой задачи.

Анализ проблемы и разбор частых ошибок

Проанализируем типовые ошибки, которые возникают при реализации алгоритма поиска и заполнения реквизитов:

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

Особенность работы метода НайтиЗначение() в 1С 7.7

Рассмотрим подробнее синтаксис метода НайтиЗначение() объекта ТаблицаЗначений:

ТЗ.НайтиЗначение(ИскомоеЗначение, ПеременнаяСтроки, ИмяИлиНомерКолонки);

Ключевая особенность заключается в том, как платформа обрабатывает второй параметр — ПеременнаяСтроки:

  1. Входное значение: если в переменной передан 0, платформа выполняет поиск по всей колонке, начиная с первой строки (строка 1). Если же в переменную передано число N > 0, поиск начнется именно со строки под номером N.
  2. Выходное значение: при успешном обнаружении значения платформа автоматически записывает в эту же переменную номер найденной строки. Если совпадений нет, метод возвращает 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. Прямое обращение через объектную ссылку: если реквизит Субконто1 содержит ссылку на справочник Контрагенты, создавать вспомогательную таблицу ТЗ2 вообще не требуется. Достаточно прочитать реквизиты напрямую:
    
    Если ПустоеЗначение(Выписка.Субконто1) = 0 Тогда
        Если Выписка.Субконто1.Вид() = "Контрагенты" Тогда
            ТЗ1.КК = Выписка.Субконто1.Код;
            ТЗ1.ПолнНаименование = Выписка.Субконто1.ПолнНаименование;
        КонецЕсли;
    КонецЕсли;
    
  2. Использование метода ВыгрузитьТабличнуюЧасть(): вместо последовательного перебора строк через ВыбратьСтроки() / ПолучитьСтроку() гораздо быстрее выгрузить табличную часть документа целиком в таблицу значений вызовом Выписка.ВыгрузитьТабличнуюЧасть(ТЗ1);, после чего добавить недостающие колонки и заполнить их.
  3. Использование встроенного объекта Запрос: для выборки и группировки данных с одновременным соединением реквизитов документа и справочников наиболее производительным штатным инструментом платформы 7.7 является механизм запросов с последующей выгрузкой в таблицу значений методом Запрос.Выгрузить(ТЗ);.
← На главную