Как найти строку в табличной части документа 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 на управляемых формах основной и наиболее универсальный способ поиска строки в табличной части по реквизиту — это перебор коллекции строк в цикле. Мы подробно рассмотрели, как создать такие функции для поиска по одному или нескольким реквизитам, а также для получения всех совпадающих строк. Кроме того, мы затронули альтернативные методы для серверного поиска и сценарии, требующие оптимизации, такие как использование запросов или метода НайтиСтроки() для объектных коллекций. Выбирая подходящий метод, всегда учитывайте контекст выполнения (клиент/сервер), объем данных и частоту обращений к поиску. При необходимости расширить интерактивные возможности работы с табличной частью, включая подсчет итогов с учетом фильтрации, можно использовать готовый набор функций для организации итогов в подвале табличных частей управляемых форм.

← На главную