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