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

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

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

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

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

Проанализируем распространенные ошибки, с которыми сталкиваются разработчики при попытке динамически изменить типовой запрос конструктора печати:

  1. Сортировка вложенной таблицы: когда в запросе используется выборка табличной части через точку Реализация_товаров.Товары.(...), предложение УПОРЯДОЧИТЬ ПО нельзя ставить в самый конец общего запроса. Поле вложенной таблицы недоступно на верхнем уровне группировки, поэтому сортировка должна быть прописана внутри скобок вложенной таблицы либо запрос должен быть переписан напрямую к физической таблице Документ.Реализация_товаров.Товары.
  2. Обращение к несуществующим реквизитам: попытка написать в запросе конструкцию вида УПОРЯДОЧИТЬ ПО Номенклатура.ДлинаНаименования приводит к исключению, поскольку у объекта СправочникСсылка.Номенклатура нет стандартного реквизита или свойства длины строки.
  3. Неверное получение параметров от пользователя: вызов метода НайтиПоРеквизиту ищет ссылку на объект в базе данных, а не считывает выбор пользователя из диалогового окна.

Способ 1. Сортировка через Таблицу Значений (универсальный для любых версий платформы)

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

Разберем по шагам логику решения:

  1. Отказываемся от выборки через вложенную таблицу в пользу плоского запроса к табличной части документа.
  2. Выгружаем результат запроса в объект ТаблицаЗначений.
  3. Если пользователь выбрал сортировку по длине, добавляем числовую колонку, в цикле вычисляем длину наименования с помощью функции СтрДлина() и сортируем таблицу.
  4. Выводим строки в ТабличныйДокумент с пересчетом порядкового номера строки.

Посмотрим на пример реализации серверной процедуры формирования печатной формы, принимающей режим сортировки в качестве параметра:


Процедура Печать(ТабДок, Ссылка, РежимСортировки) Экспорт

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

КонецПроцедуры

Способ 2. Сортировка средствами запроса (для платформы 8.3.20 и выше)

Рассмотрим современный вариант решения. Начиная с версии платформы 8.3.20, в языке запросов появилась встроенная функция ДЛИНАСТРОКИ(). Благодаря этому отпадает необходимость выгружать данные в промежуточные коллекции и рассчитывать длины в цикле.

Посмотрим на пример построения динамического запроса для выборки табличной части:


Запрос = Новый Запрос;
ТекстЗапроса = 
"ВЫБРАТЬ
|	Товары.Номенклатура КАК Номенклатура,
|	Товары.Количество КАК Количество,
|	Товары.Цена КАК Цена,
|	Товары.Сумма КАК Сумма
|ИЗ
|	Документ.Реализация_товаров.Товары КАК Товары
|ГДЕ
|	Товары.Ссылка = &Ссылка
|{УПОРЯДОЧИТЬ ПО
|	ПолеСортировки}";

Если РежимСортировки = "ПоДлине" Тогда
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "{УПОРЯДОЧИТЬ ПО\n|	ПолеСортировки}", "УПОРЯДОЧИТЬ ПО ДЛИНАСТРОКИ(Товары.Номенклатура.Наименование) ВОЗР");
ИначеЕсли РежимСортировки = "ПоНаименованию" Тогда
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "{УПОРЯДОЧИТЬ ПО\n|	ПолеСортировки}", "УПОРЯДОЧИТЬ ПО Товары.Номенклатура.Наименование ВОЗР");
Иначе
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "{УПОРЯДОЧИТЬ ПО\n|	ПолеСортировки}", "УПОРЯДОЧИТЬ ПО Товары.НомерСтроки ВОЗР");
КонецЕсли;

Запрос.Текст = ТекстЗапроса;
Запрос.Параметры.Вставить("Ссылка", Ссылка);
ВыборкаТовары = Запрос.Выполнить().Выбрать();

Организация диалога с пользователем на клиенте

В управляемом интерфейсе серверные процедуры формирования документа не имеют доступа к пользовательскому интерфейсу. Запрос варианта сортировки должен выполняться на стороне клиента перед отправкой команды на сервер.

Разберем пример клиентского кода с использованием асинхронного диалога выбора из списка:


&НаКлиенте
Процедура ПечатьСВыбором(Команда)
	
	СписокВариантов = Новый СписокЗначений;
	СписокВариантов.Добавить("ПоНаименованию", "По наименованию (А-Я)");
	СписокВариантов.Добавить("ПоДлине", "По длине наименования");
	СписокВариантов.Добавить("БезСортировки", "По порядку добавления");
	
	Оповещение = Новый ОписаниеОповещения("ПослеВыбораСортировки", ЭтотОбъект);
	ПоказатьВыборИзСписка(Оповещение, СписокВариантов);
	
КонецПроцедуры

&НаКлиенте
Процедура ПослеВыбораСортировки(ВыбранныйЭлемент, ДополнительныеПараметры) Экспорт
	
	Если ВыбранныйЭлемент = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	СформироватьПечатнуюФормуНаСервере(ВыбранныйЭлемент.Значение);
	
КонецПроцедуры

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

← На главную