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