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