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

Программист 1С v8.3 (Обычные формы) 1С:Управление торговлей Торговля и дистрибуция
← На главную

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

Разберем по шагам, как правильно внести изменения в текст запроса, скорректировать постобработку выборки и избежать распространенных ошибок платформы 1С.

Анализ структуры типового запроса печатной формы

Посмотрим на типовой механизм выборки данных табличной части Товары. В типовых конфигурациях («Управление торговлей», «Комплексная автоматизация») сбор данных для печатных форм часто строится на сложных запросах с использованием вложенных таблиц, группировок и секции ОБЪЕДИНИТЬ ВСЕ (для объединения основных товаров и возвратной тары).

Рассмотрим фрагмент исходного запроса, формирующего таблицу ЗапросТовары:


Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ
|	ВложенныйЗапрос.Номенклатура КАК Номенклатура,
|	ВложенныйЗапрос.МассаБрутто КАК МассаБрутто,
|	ВложенныйЗапрос.Номенклатура.НаименованиеПолное КАК ТоварНаименование,
|	ВложенныйЗапрос.ЕдиницаИзмерения КАК ЕдиницаИзмерения,
|	ВложенныйЗапрос.Характеристика КАК Характеристика,
|	ВложенныйЗапрос.Количество КАК Количество
|ИЗ
|	(ВЫБРАТЬ
|		СУММА(РеализацияТоваровУслуг.Количество * РеализацияТоваровУслуг.Номенклатура.ЕдиницаХраненияОстатков.Вес) КАК МассаБрутто,
|		РеализацияТоваровУслуг.ЕдиницаИзмерения КАК ЕдиницаИзмерения,
|		РеализацияТоваровУслуг.ХарактеристикаНоменклатуры КАК Характеристика,
|		СУММА(РеализацияТоваровУслуг.Количество) КАК Количество
|	ИЗ
|		Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслуг
|	ГДЕ
|		РеализацияТоваровУслуг.Ссылка = &ТекущийДокумент
|	СГРУППИРОВАТЬ ПО
|		РеализацияТоваровУслуг.Номенклатура,
|		РеализацияТоваровУслуг.ЕдиницаИзмерения,
|		РеализацияТоваровУслуг.ХарактеристикаНоменклатуры) КАК ВложенныйЗапрос
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
|	РеализацияТоваровУслуг.Номенклатура КАК Номенклатура,
|	РеализацияТоваровУслуг.МассаБрутто КАК МассаБрутто,
|	РеализацияТоваровУслуг.Номенклатура.НаименованиеПолное КАК ТоварНаименование,
|	РеализацияТоваровУслуг.Номенклатура.ЕдиницаХраненияОстатков КАК ЕдиницаИзмерения,
|	NULL КАК Характеристика,
|	РеализацияТоваровУслуг.Количество КАК Количество
|ИЗ
|	(ВЫБРАТЬ
|		РеализацияТоваровУслуг.Номенклатура КАК Номенклатура,
|		СУММА(РеализацияТоваровУслуг.Количество * РеализацияТоваровУслуг.Номенклатура.ЕдиницаХраненияОстатков.Вес) КАК МассаБрутто,
|		СУММА(РеализацияТоваровУслуг.Количество) КАК Количество
|	ИЗ
|		Документ.РеализацияТоваровУслуг.ВозвратнаяТара КАК РеализацияТоваровУслуг
|	ГДЕ
|		РеализацияТоваровУслуг.Ссылка = &ТекущийДокумент
|	СГРУППИРОВАТЬ ПО
|		РеализацияТоваровУслуг.Номенклатура) КАК РеализацияТоваровУслуг";

Правила добавления нового реквизита в запрос

Если в табличную часть документа был добавлен собственный реквизит (например, Характеристика1), при его извлечении необходимо строго соблюдать три правила модификации запроса:

  1. Добавление в выборку вложенного запроса: поле РеализацияТоваровУслуг.Характеристика1 должно быть явно выбрано во вложенной таблице.
  2. Обязательная группировка: поскольку во вложенном запросе используется агрегатная функция СУММА(), любое неагрегатное поле обязано присутствовать в блоке СГРУППИРОВАТЬ ПО. Если забыть указать добавленный реквизит в группировке, СУБД вернет синтаксическую ошибку.
  3. Соблюдение структуры объединения: во второй ветке ОБЪЕДИНИТЬ ВСЕ (по таблице возвратной тары) необходимо обязательно добавить поле-заглушку NULL КАК Характеристика1 на той же позиции, чтобы типы и количество колонок обеих частей запроса совпадали.

Оптимизация: отказ от запросов в цикле

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


// Неправильный подход: запрос выполняется на каждой итерации цикла
Для Каждого СтрокаТЗ Из ЗапросТовары Цикл
	Если СтрокаТЗ.ЕдиницаИзмерения.Наименование = "пог. м" Тогда
		Запрос = Новый Запрос;
		Запрос.Текст = 
		"ВЫБРАТЬ
		|	ЕдиницыИзмерения.Коэффициент
		|ИЗ
		|	Справочник.ЕдиницыИзмерения КАК ЕдиницыИзмерения
		|ГДЕ
		|	ЕдиницыИзмерения.Владелец = &ВыбТовар
		|	И ЕдиницыИзмерения.ЕдиницаПоКлассификатору = &ВыбШт";
		// ... выполнение запроса
	КонецЕсли;
КонецЦикла;

Для корректного решения объединим выборку данных документа и пересчет единиц в одном запросе с помощью ЛЕВОЕ СОЕДИНЕНИЕ:


// Оптимальный вариант соединения со справочником единиц измерения
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ЕдиницыИзмерения КАК ЕдиницыШт
	ПО ЕдиницыШт.Владелец = РеализацияТоваровУслуг.Номенклатура
	И ЕдиницыШт.ЕдиницаПоКлассификатору = &ЕдиницаШтука

В самой табличной части документа реализации типовых конфигураций 1С уже хранится реквизит Коэффициент, поэтому количество в базовых единицах можно получить простым умножением: СтрокаТЗ.Количество * СтрокаТЗ.Коэффициент.

Формирование строки наименования груза

Рассмотрим постобработку результата запроса при формировании результирующей строки описания груза с учетом проверки заполненности реквизитов:


НаимГруза = "";
КолВо = 0;

Для Каждого СтрокаТЗ Из ЗапросТовары Цикл
	
	// Определение текстового представления характеристики
	СтрокаХарактеристики = "";
	Если ЗначениеЗаполнено(СтрокаТЗ.Характеристика1) Тогда
		СтрокаХарактеристики = " " + СокрЛП(СтрокаТЗ.Характеристика1);
	ИначеЕсли ЗначениеЗаполнено(СтрокаТЗ.Характеристика) Тогда
		СтрокаХарактеристики = " " + СокрЛП(СтрокаТЗ.Характеристика);
	КонецЕсли;
	
	// Расчет количества
	ПересчитанноеКоличество = СтрокаТЗ.Количество;
	
	// Формирование итоговой строки по позиции
	НаимГруза = НаимГруза + СтрокаТЗ.ТоварНаименование + СтрокаХарактеристики + " - " + Строка(ПересчитанноеКоличество) + " шт., ";
	КолВо = КолВо + ПересчитанноеКоличество;

КонецЦикла;

// Удаление завершающей запятой
Если СтрДлина(НаимГруза) > 2 Тогда
	НаимГруза = Лев(НаимГруза, СтрДлина(НаимГруза) - 2) + ".";
КонецЕсли;

ДанныеДокумента.Вставить("Пункт3_1", НаимГруза);
ДанныеДокумента.Вставить("Пункт3_2", "Всего: " + Формат(КолВо, "ЧДЦ=0") + " шт.");

Причины сбоев при отладке и поведение «Поле объекта не обнаружено»

Если код написан без синтаксических ошибок, но платформа выдает исключение Поле объекта не обнаружено (Характеристика) или условие Если ЗначениеЗаполнено(...) ведет себя нелогично, выясним причину такого поведения:

  1. Динамическое обновление конфигурации: при обновлении базы в режиме демонобновления клиентский сеанс может удерживать старую версию метаданных. В результате добавленные реквизиты не видны в структуре возвращаемой таблицы. Решением является полный перезапуск клиентского приложения и очистка кэша.
  2. Несовпадение псевдонимов в запросе: имя свойства в результирующей таблице значений совпадает с псевдонимом (КАК ИмяПоля) в самом верхнем блоке ВЫБРАТЬ. Если во вложенном запросе реквизит назван ХарактеристикаНоменклатуры, а наверху указан как Характеристика, обращаться в цикле нужно строго по имени верхнего псевдонима — СтрокаТЗ.Характеристика.
  3. Неявное приведение к строке: при конкатенации ссылочного типа со строкой платформа выполняет дополнительное обращение к базе данных. Чтобы избежать замедления, рекомендуется выбирать представление прямо в запросе при помощи функции ПРЕДСТАВЛЕНИЕ(РеализацияТоваровУслуг.ХарактеристикаНоменклатуры) КАК ХарактеристикаПредставление.
← На главную