Как найти строку в табличной части документа 1С 8.3 на управляемых формах по одному или нескольким реквизитам?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

Особенности поиска в табличных частях управляемых форм

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

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

Поиск строки по одному реквизиту

Рассмотрим классический пример, когда нам нужно найти первую строку в табличной части по значению одного конкретного реквизита, например, по Номенклатуре. Для этого мы создадим клиентскую функцию, которая будет перебирать все строки табличной части и проверять условие совпадения реквизита.

Шаг 1: Создание функции поиска

Мы определим функцию, которая принимает в качестве параметров саму табличную часть документа (как коллекцию строк) и искомое значение реквизита. Функция будет возвращать найденную строку или Неопределено, если строка не найдена.


&НаКлиенте
// Функция для поиска строки в табличной части по значению одного реквизита.
//
// Параметры:
//   ТабличнаяЧастьДокумента - Коллекция строк табличной части (например, Объект.Товары).
//   ИскомаяНоменклатура    - Значение реквизита "Номенклатура", которое мы ищем.
//
// Возвращаемое значение:
//   СтрокаТабличнойЧасти или Неопределено, если строка не найдена.
Функция НайтиСтрокуТЧПоНоменклатуре(ТабличнаяЧастьДокумента, ИскомаяНоменклатура)

    // Перебираем каждую строку в переданной табличной части.
    Для Каждого СтрокаТЧ Из ТабличнаяЧастьДокумента Цикл
        // Проверяем, совпадает ли значение реквизита "Номенклатура"
        // текущей строки с искомым значением.
        Если СтрокаТЧ.Номенклатура = ИскомаяНоменклатура Тогда
            // Если совпадение найдено, сразу возвращаем эту строку,
            // так как нам нужна первая попавшаяся.
            Возврат СтрокаТЧ;
        КонецЕсли;
    КонецЦикла;

    // Если цикл завершился и ни одна строка не была найдена,
    // возвращаем Неопределено.
    Возврат Неопределено; 

КонецФункции

Шаг 2: Вызов функции

Вызвать эту функцию вы можете в любом клиентском или серверном контексте, где доступна коллекция строк табличной части. Например, из обработчика события формы:


&НаКлиенте
Процедура ПриНажатииКнопкиПоиска(Команда)
    // Предположим, что мы ищем конкретную номенклатуру
    // из какого-то реквизита формы или заранее определенного значения.
    ИскомаяНоменклатура = Справочники.Номенклатура.НайтиПоКоду("00001"); // Пример поиска по коду

    // Вызываем нашу функцию, передавая ей табличную часть "Товары"
    // и искомое значение номенклатуры.
    НайденнаяСтрока = НайтиСтрокуТЧПоНоменклатуре(Объект.Товары, ИскомаяНоменклатура);

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

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

Поиск строки по нескольким реквизитам

Часто возникает потребность найти строку, которая соответствует сразу нескольким условиям. Например, нам нужно найти строку не только по Номенклатуре, но и по ее Характеристике, используя контроль и анализ данных в табличной части — для этого подойдёт поиск дубликатов по реквизитам табличных частей. Для этого мы просто расширим условие внутри нашего цикла, добавив дополнительные проверки.

Шаг 1: Модификация функции поиска

Мы добавим в функцию новые параметры для каждого дополнительного реквизита, по которому будет производиться поиск. Внутри цикла условие Если будет объединять все проверки через оператор И.


&НаКлиенте
// Функция для поиска строки в табличной части по значениям нескольких реквизитов.
//
// Параметры:
//   ТабличнаяЧастьДокумента - Коллекция строк табличной части (например, Объект.Товары).
//   ИскомаяНоменклатура    - Значение реквизита "Номенклатура".
//   ИскомаяХарактеристика  - Значение реквизита "Характеристика".
//
// Возвращаемое значение:
//   СтрокаТабличнойЧасти или Неопределено, если строка не найдена.
Функция НайтиСтрокуТЧПоНесколькимРеквизитам(ТабличнаяЧастьДокумента, ИскомаяНоменклатура, ИскомаяХарактеристика)

    Для Каждого СтрокаТЧ Из ТабличнаяЧастьДокумента Цикл
        // Проверяем совпадение по всем заданным реквизитам одновременно.
        Если СтрокаТЧ.Номенклатура = ИскомаяНоменклатура И
           СтрокаТЧ.Характеристика = ИскомаяХарактеристика Тогда
            // Если все условия выполнены, возвращаем найденную строку.
            Возврат СтрокаТЧ;
        КонецЕсли;
    КонецЦикла;

    Возврат Неопределено;
КонецФункции

Шаг 2: Вызов функции с несколькими параметрами


&НаКлиенте
Процедура ПриНажатииКнопкиПоискаПоДвумРеквизитам(Команда)
    // Определяем искомые значения для номенклатуры и характеристики.
    ИскомаяНоменклатура   = Справочники.Номенклатура.НайтиПоКоду("00001");
    ИскомаяХарактеристика = Справочники.ХарактеристикиНоменклатуры.НайтиПоНаименованию("Красный");

    // Вызываем модифицированную функцию.
    НайденнаяСтрока = НайтиСтрокуТЧПоНесколькимРеквизитам(Объект.Товары, ИскомаяНоменклатура, ИскомаяХарактеристика);

    Если НайденнаяСтрока <> Неопределено Тогда
        Сообщить("Строка с номенклатурой '" + НайденнаяСтрока.Номенклатура +
                 "' и характеристикой '" + НайденнаяСтрока.Характеристика + "' найдена.");
    Иначе
        Сообщить("Строка с указанными номенклатурой и характеристикой не найдена.");
    КонецЕсли;
КонецПроцедуры

Таким образом, мы можем легко адаптировать функцию для поиска по любому количеству реквизитов, просто добавляя их в параметры функции и в условие Если.

Альтернативные подходы и оптимизация

Хотя цикл Для Каждого по клиентской табличной части обычно достаточно эффективен, существуют ситуации, когда могут потребоваться более продвинутые подходы или когда поиск выполняется на сервере.

Поиск всех совпадающих строк

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


&НаКлиенте
// Функция для поиска всех строк в табличной части по значению одного реквизита.
//
// Параметры:
//   ТабличнаяЧастьДокумента - Коллекция строк табличной части.
//   ИскомаяНоменклатура    - Значение реквизита "Номенклатура".
//
// Возвращаемое значение:
//   Массив строк табличной части, соответствующих условию.
Функция НайтиВсеСтрокиТЧПоНоменклатуре(ТабличнаяЧастьДокумента, ИскомаяНоменклатура)
    НайденныеСтроки = Новый Массив; // Создаем новый массив для сбора результатов

    Для Каждого СтрокаТЧ Из ТабличнаяЧастьДокумента Цикл
        Если СтрокаТЧ.Номенклатура = ИскомаяНоменклатура Тогда
            НайденныеСтроки.Добавить(СтрокаТЧ); // Добавляем найденную строку в массив
        КонецЕсли;
    КонецЦикла;

    Возврат НайденныеСтроки;
КонецФункции

Теперь функция будет возвращать массив всех строк, соответствующих условию. Мы можем затем перебрать этот массив или выполнить с ним другие операции.


&НаКлиенте
Процедура ПриНажатииКнопкиПоискаВсехСтрок(Команда)
    ИскомаяНоменклатура = Справочники.Номенклатура.НайтиПоКоду("00001");
    МассивНайденныхСтрок = НайтиВсеСтрокиТЧПоНоменклатуре(Объект.Товары, ИскомаяНоменклатура);

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

Серверный поиск с использованием запросов или метода НайтиСтроки()

Если данные табличной части очень велики, и поиск требуется выполнять очень часто или на сервере (например, при расчете каких-либо итогов, не связанных напрямую с интерактивным редактированием формы), то прямая работа с коллекцией строк формы может быть менее оптимальной. В таких случаях можно рассмотреть следующие подходы:

  1. Использование запросов к базе данных: Если нам нужен поиск по данным, которые уже записаны в базу или находятся в оперативном получении с сервера, мы можем сформировать запрос к табличной части документа. Этот метод особенно полезен, когда нужно получить агрегированные данные или выполнять сложные выборки.

    
    &НаСервере
    // Пример серверной функции для поиска строк через запрос
    Функция НайтиСтрокиТЧНаСервереЗапросом(СсылкаНаДокумент, ИскомаяНоменклатура)
        Запрос = Новый Запрос;
        Запрос.Текст =
        "ВЫБРАТЬ
        |    Документ.Товары.Ссылка КАК СсылкаНаДокументСтроки,
        |    Документ.Товары.НомерСтроки,
        |    Документ.Товары.Номенклатура,
        |    Документ.Товары.Количество
        |ИЗ
        |    Документ.ИмяВашегоДокумента.Товары КАК Документ.Товары
        |ГДЕ
        |    Документ.Товары.Ссылка = &СсылкаНаДокумент
        |    И Документ.Товары.Номенклатура = &ИскомаяНоменклатура";
    
        Запрос.УстановитьПараметр("СсылкаНаДокумент", СсылкаНаДокумент);
        Запрос.УстановитьПараметр("ИскомаяНоменклатура", ИскомаяНоменклатура);
    
        РезультатЗапроса = Запрос.Выполнить();
        Выборка = РезультатЗапроса.Выбрать();
    
        МассивНайденныхДанных = Новый Массив;
        Пока Выборка.Следующий() Цикл
            МассивНайденныхДанных.Добавить(Выборка.Номенклатура); // Можно добавить всю выборку
        КонецЦикла;
        Возврат МассивНайденныхДанных;
    КонецФункции
    
  2. Использование метода НайтиСтроки() для коллекций объектов: Если мы работаем с данными, которые представляют собой коллекцию типа ДокументОбъект.<ИмяТабличнойЧасти> (то есть, это данные, полученные с сервера, а не клиентская коллекция формы), то можем использовать встроенный метод НайтиСтроки(). Этот метод позволяет найти строки по соответствию значений реквизитов.

    Например, если у вас есть объект документа, полученный с сервера (ДокументОбъект), вы можете выполнить поиск так:

    
    &НаСервере
    // Пример серверной функции для поиска строк с помощью НайтиСтроки()
    Функция НайтиСтрокиТЧНаСервере(СсылкаНаДокумент, ИскомаяНоменклатура)
        ДокОбъект = СсылкаНаДокумент.ПолучитьОбъект();
        Если ДокОбъект = Неопределено Тогда
            Возврат Новый Массив;
        КонецЕсли;
    
        // Создаем структуру с условиями поиска.
        // Ключ структуры - имя реквизита табличной части, значение - искомое значение.
        УсловияПоиска = Новый Структура("Номенклатура", ИскомаяНоменклатура);
    
        // Метод НайтиСтроки() возвращает массив найденных строк.
        НайденныеСтроки = ДокОбъект.Товары.НайтиСтроки(УсловияПоиска);
                
        Возврат НайденныеСтроки;
    КонецФункции
    

    Этот подход более элегантен, чем цикл, если он применим, так как он использует встроенные механизмы платформы. Однако он работает только для серверных коллекций объектов, а не для клиентских коллекций на форме.

  3. Предварительная индексация с помощью хэш-таблиц или структур: Для очень больших табличных частей и при необходимости многократного быстрого поиска по одним и тем же ключам (например, Номенклатура) можно рассмотреть создание вспомогательной структуры или хэш-таблицы. Мы можем на клиенте или на сервере один раз при загрузке данных проиндексировать табличную часть, создав Структуру или Соответствие, где ключом будет искомый реквизит (или комбинация реквизитов), а значением — ссылка на строку табличной части или ее индекс. После этого поиск по ключу в такой структуре будет практически мгновенным. Однако этот метод усложняет код и требует дополнительного управления актуальностью индекса при изменении данных в табличной части.

Заключение

Мы видим, что в 1С 8.3 на управляемых формах основной и наиболее универсальный способ поиска строки в табличной части по реквизиту — это перебор коллекции строк в цикле. Мы подробно рассмотрели, как создать такие функции для поиска по одному или нескольким реквизитам, а также для получения всех совпадающих строк. Кроме того, мы затронули альтернативные методы для серверного поиска и сценарии, требующие оптимизации, такие как использование запросов или метода НайтиСтроки() для объектных коллекций. Выбирая подходящий метод, всегда учитывайте контекст выполнения (клиент/сервер), объем данных и частоту обращений к поиску.

← На главную