Как соединить табличные части "Товары" и "ШтрихкодыУпаковок" документа "РеализацияТоваровУслуг" в запросе 1С, если нет прямых связывающих полей?

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

При работе с документами в 1С часто возникает задача объединения данных из нескольких табличных частей. Особенно это актуально, когда между табличными частями нет очевидных прямых связывающих полей. Рассмотрим ситуацию на примере документа "РеализацияТоваровУслуг", где требуется объединить информацию из стандартной табличной части "Товары" и дополнительной табличной части "ШтрихкодыУпаковок".

Представим, что в табличной части "Товары" хранятся позиции реализации, а в табличной части "ШтрихкодыУпаковок" — уникальные QR-коды для каждой упаковки товара. Сложность заключается в том, что напрямую эти табличные части не связаны по общему полю. Как же построить запрос, чтобы корректно связать эти данные, избегая дубликатов и получая полный объем информации?

Начальный подход и анализ проблемы

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


ВЫБРАТЬ
    РеализацияТоваровУслугШтрихкодыУпаковок.Ссылка КАК Ссылка,
    РеализацияТоваровУслугШтрихкодыУпаковок.ШтрихкодУпаковки КАК ШтрихкодУпаковки,
    РеализацияТоваровУслугТовары.Номенклатура КАК Номенклатура,
    РеализацияТоваровУслугТовары.ЕдиницаИзмерения.Код КАК ЕдиницаИзмеренияКод,
    РеализацияТоваровУслугТовары.Цена КАК Цена,
    РеализацияТоваровУслугТовары.Количество КАК Количество,
    РеализацияТоваровУслугТовары.Сумма КАК Сумма,
    РеализацияТоваровУслугТовары.НалоговаяБазаНДС КАК НалоговаяБазаНДС,
    РеализацияТоваровУслуг.СуммаДокумента КАК СуммаДокумента
ИЗ
    Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг
        ЛЕВОЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
        ПО (РеализацияТоваровУслуг.Ссылка = РеализацияТоваровУслугТовары.Ссылка)
        ЛЕВОЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг.ШтрихкодыУпаковок КАК РеализацияТоваровУслугШтрихкодыУпаковок
        ПО (РеализацияТоваровУслуг.Ссылка = РеализацияТоваровУслугШтрихкодыУпаковок.Ссылка)
ГДЕ
    РеализацияТоваровУслуг.Ссылка = &Ссылка

Этот запрос, как выяснилось, приводил к некорректным результатам – значительному увеличению количества записей (дубликатам). Например, если в документе 5 позиций товаров и 6 штрихкодов, вместо ожидаемых 6 строк мы получаем 30 записей. Это происходит потому, что мы соединяем каждую строку из одной табличной части со всеми строками из другой табличной части, принадлежащими тому же документу. Для каждой строки из "Товары" будут найдены все строки из "ШтрихкодыУпаковок", и наоборот, что создает декартово произведение записей.

Ключевая идея: Связь через справочник "ШтрихкодыУпаковокТоваров"

Для корректного соединения нам необходимо найти общее звено между записями табличных частей "Товары" и "ШтрихкодыУпаковок". Таким звеном является номенклатура.

  1. В табличной части "Товары" у нас есть поле Номенклатура, которое является ссылкой на Справочник.Номенклатура.

  2. В табличной части "ШтрихкодыУпаковок" присутствует поле ШтрихкодУпаковки. Важно понимать, что это поле не является самой номенклатурой, а представляет собой ссылку на Справочник.ШтрихкодыУпаковокТоваров.

  3. Сам справочник Справочник.ШтрихкодыУпаковокТоваров, в свою очередь, содержит реквизит Номенклатура (и, возможно, ХарактеристикаНоменклатуры), который и ссылается на Справочник.Номенклатура.

Таким образом, мы можем использовать справочник ШтрихкодыУпаковокТоваров как "мостик" для соединения. То есть, мы будем соединять Номенклатуру из табличной части "Товары" с Номенклатурой, хранящейся внутри элемента справочника ШтрихкодыУпаковокТоваров, на который ссылается поле ШтрихкодУпаковки из табличной части "ШтрихкодыУпаковок".

Работа с ОпределяемымТипом.Номенклатура

В ходе обсуждения возник вопрос о работе с реквизитом Номенклатура в Справочник.ШтрихкодыУпаковокТоваров, который имеет тип ОпределяемыйТип.Номенклатура. Определяемый тип в 1С — это обычный составной тип данных, который позволяет сгруппировать несколько ссылочных типов (например, СправочникСсылка.Номенклатура, СправочникСсылка.НаборыУпаковок и т.д.) под одним именем.

В запросах к 1С с таким типом работать так же просто, как и с обычными ссылочными типами. Для доступа к реквизитам объекта, на который указывает ссылка, используется точечная нотация. Например, если у нас есть поле ШтрихкодУпаковки (тип которого СправочникСсылка.ШтрихкодыУпаковокТоваров), и в справочнике ШтрихкодыУпаковокТоваров есть реквизит Номенклатура, то мы можем обратиться к нему как ШтрихкодУпаковки.Номенклатура.

Платформа 1С автоматически "разыменует" эту ссылку и получит значение реквизита из соответствующего объекта справочника. Нет необходимости использовать конструкции типа ВЫРАЗИТЬ, если мы просто хотим получить значение реквизита.

Корректное соединение табличных частей в запросе

Теперь, понимая принцип связи, мы можем сформулировать корректный запрос. Нам потребуется соединить табличную часть "Товары" с табличной частью "ШтрихкодыУпаковок". В качестве условия соединения мы будем использовать поля, содержащие информацию о номенклатуре.

Рассмотрим структуру запроса по шагам:

  1. Основная таблица: Начнем с табличной части "Товары" документа "РеализацияТоваровУслуг". Она будет "левой" таблицей в нашем соединении, так как мы хотим получить все товары, даже если для них нет штрихкодов.

  2. Тип соединения: Используем ЛЕВОЕ СОЕДИНЕНИЕ, чтобы гарантировать, что все позиции из табличной части "Товары" попадут в результат, даже если для какой-то номенклатуры отсутствуют записи в табличной части "ШтрихкодыУпаковок".

  3. Соединяемая таблица: Присоединяем табличную часть "ШтрихкодыУпаковок".

  4. Условие соединения: Самое важное – условие ПО. Мы связываем Номенклатуру из таблицы "Товары" с Номенклатурой, полученной из справочника ШтрихкодыУпаковокТоваров, на который ссылается поле ШтрихкодУпаковки из соответствующей табличной части. То есть, условие будет выглядеть так: РеализацияТоваровУслугТовары.Номенклатура = РеализацияТоваровУслугШтрихкодыУпаковок.ШтрихкодУпаковки.Номенклатура.

  5. Фильтрация: Не забываем про условие ГДЕ РеализацияТоваровУслуг.Ссылка = &Ссылка, чтобы ограничить выборку записями только одного конкретного документа.

Представим один из вариантов запроса, который отражает эту логику:


ВЫБРАТЬ
    РеализацияТоваровУслуг.Ссылка КАК СсылкаДокумента,
    РеализацияТоваровУслуг.СуммаДокумента КАК СуммаДокумента,
    РеализацияТоваровУслугТовары.Номенклатура КАК Номенклатура,
    РеализацияТоваровУслугТовары.ЕдиницаИзмерения.Код КАК ЕдиницаИзмеренияКод,
    РеализацияТоваровУслугТовары.Цена КАК Цена,
    РеализацияТоваровУслугТовары.Количество КАК Количество,
    РеализацияТоваровУслугТовары.Сумма КАК Сумма,
    РеализацияТоваровУслугТовары.НалоговаяБазаНДС КАК НалоговаяБазаНДС,
    РеализацияТоваровУслугШтрихкодыУпаковок.ШтрихкодУпаковки КАК ШтрихкодУпаковки
ИЗ
    Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
        ЛЕВОЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг.ШтрихкодыУпаковок КАК РеализацияТоваровУслугШтрихкодыУпаковок
        ПО РеализацияТоваровУслугТовары.Номенклатура = РеализацияТоваровУслугШтрихкодыУпаковок.ШтрихкодУпаковки.Номенклатура
        ЛЕВОЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг
        ПО РеализацияТоваровУслугТовары.Ссылка = РеализацияТоваровУслуг.Ссылка
ГДЕ
    РеализацияТоваровУслугТовары.Ссылка = &Ссылка

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

Устранение дубликатов с помощью ВЫБРАТЬ РАЗЛИЧНЫЕ

Даже при правильном условии соединения, могут возникать ситуации, когда одна позиция номенклатуры в табличной части "Товары" соответствует нескольким штрихкодам в табличной части "ШтрихкодыУпаковок". Например, если у нас есть 10 штук товара "А", и для них было занесено 10 отдельных штрихкодов в ТЧ "ШтрихкодыУпаковок", то наш запрос выведет 10 строк для этого товара. Если же нам требуется получить каждую уникальную комбинацию полей, и при этом один товар (строка из ТЧ "Товары") может иметь несколько штрихкодов, и каждый из них должен быть представлен отдельной строкой, то это ожидаемое поведение.

Однако, если по условиям задачи нам нужно получить только уникальные строки, независимо от того, сколько раз повторяется та или иная комбинация полей, мы можем использовать оператор ВЫБРАТЬ РАЗЛИЧНЫЕ. Этот оператор удаляет полностью совпадающие строки из результата запроса.

Пример запроса с ВЫБРАТЬ РАЗЛИЧНЫЕ:


ВЫБРАТЬ РАЗЛИЧНЫЕ
    РеализацияТоваровУслуг.Ссылка КАК СсылкаДокумента,
    РеализацияТоваровУслуг.СуммаДокумента КАК СуммаДокумента,
    РеализацияТоваровУслугТовары.Номенклатура КАК Номенклатура,
    РеализацияТоваровУслугТовары.ЕдиницаИзмерения.Код КАК ЕдиницаИзмеренияКод,
    РеализацияТоваровУслугТовары.Цена КАК Цена,
    РеализацияТоваровУслугТовары.Количество КАК Количество,
    РеализацияТоваровУслугТовары.Сумма КАК Сумма,
    РеализацияТоваровУслугТовары.НалоговаяБазаНДС КАК НалоговаяБазаНДС,
    РеализацияТоваровУслугШтрихкодыУпаковок.ШтрихкодУпаковки КАК ШтрихкодУпаковки
ИЗ
    Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
        ЛЕВОЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг.ШтрихкодыУпаковок КАК РеализацияТоваровУслугШтрихкодыУпаковок
        ПО РеализацияТоваровУслугТовары.Номенклатура = РеализацияТоваровУслугШтрихкодыУпаковок.ШтрихкодУпаковки.Номенклатура
        ЛЕВОЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг
        ПО РеализацияТоваровУслугТовары.Ссылка = РеализацияТоваровУслуг.Ссылка
ГДЕ
    РеализацияТоваровУслугТовары.Ссылка = &Ссылка

Использование РАЗЛИЧНЫЕ может быть полезно, если вы видите, что запрос возвращает больше строк, чем ожидается, и эти лишние строки являются полными дубликатами. Однако, если каждый штрихкод уникален и вы хотите видеть каждую связь "товар-штрихкод" как отдельную строку, то РАЗЛИЧНЫЕ может быть не нужен, или даже нежелателен, если в результате работы запроса будут удалены уникальные сочетания данных, которые внешне выглядят как дубликаты.

Оптимизация и устранение ошибок

В процессе написания сложных запросов могут возникать ошибки, например, "Неоднозначное поле Номенклатура.Ссылка". Такие ошибки происходят, когда в запросе используются таблицы с одинаковыми именами полей, и система не может определить, из какой таблицы брать конкретное поле.

Для устранения таких неоднозначностей всегда рекомендуется использовать псевдонимы (алиасы) для таблиц. Псевдоним — это короткое имя, которое присваивается таблице после ключевого слова КАК. Затем при обращении к полям этой таблицы используется псевдоним.

Например, если вы попытались соединить Документ.РеализацияТоваровУслуг.Товары с Справочник.Номенклатура и Документ.РеализацияТоваровУслуг.ШтрихкодыУпаковок с Справочник.ШтрихкодыУпаковокТоваров, как в одном из ошибочных примеров, и не использовали псевдонимы, то поле Номенклатура.Ссылка могло бы быть неоднозначным. Если вы назвали Справочник.Номенклатура КАК Номенклатура, а потом где-то еще в запросе есть другая таблица с именем "Номенклатура" (например, поле из ТЧ), то возникнет конфликт.

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


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

Это делает запрос более читабельным и предотвращает ошибки неоднозначности. В нашем же случае, мы избегаем явного соединения со Справочник.Номенклатура, так как нам достаточно обращаться к реквизиту ШтрихкодУпаковки.Номенклатура, и 1С корректно разрешает эту ссылку.

Резюме и рекомендации

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

  1. Определите общий "мостик": Найдите общий справочник или реквизит, который логически связывает записи из разных табличных частей. В нашем случае это номенклатура.

  2. Используйте точечную нотацию для ссылок: Если одна табличная часть ссылается на справочник, а в этом справочнике есть нужный реквизит, обращайтесь к нему через точку (например, ТЧ.ПолеСсылающеесяНаСправочник.РеквизитСправочника).

  3. Выбирайте правильный тип соединения: Используйте ЛЕВОЕ СОЕДИНЕНИЕ, если вы хотите получить все записи из "основной" табличной части, даже если для них нет соответствий во "второй". Если нужны только совпадающие записи, используйте ВНУТРЕННЕЕ СОЕДИНЕНИЕ. ПОЛНОЕ СОЕДИНЕНИЕ используйте с осторожностью, понимая его последствия.

  4. Применяйте ВЫБРАТЬ РАЗЛИЧНЫЕ при необходимости: Если вы получаете дубликаты строк, которые не являются логически уникальными, используйте ВЫБРАТЬ РАЗЛИЧНЫЕ для очистки результата.

  5. Используйте псевдонимы таблиц: Для сложных запросов всегда присваивайте короткие псевдонимы таблицам, чтобы избежать ошибок неоднозначности и улучшить читаемость кода.

Предложенный нами финальный запрос позволяет получить все позиции товаров из документа "РеализацияТоваровУслуг" и связать их с соответствующими штрихкодами, используя логическую связь через номенклатуру, хранящуюся в Справочник.ШтрихкодыУпаковокТоваров. Это решение является гибким и подходит для большинства подобных задач в 1С.

← На главную