Как получить список заказов клиента и их дополнительные реквизиты из документа реализации товаров и услуг в 1С:ERP?

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

При работе с системой 1С:ERP 2.x часто возникает необходимость получить информацию из связанных документов. В данном случае мы рассмотрим ситуацию, когда требуется извлечь список заказов клиента, которые являются основаниями для документа "Реализация товаров и услуг", а также получить дополнительные реквизиты, связанные с этими заказами. Изначально может показаться, что для хранения нескольких заказов используется реквизит типа СписокЗначений, однако это не так. Разберем подробнее, как корректно построить запросы для решения этой задачи.

Понимание хранения связей и дополнительных реквизитов в 1С:ERP

Прежде всего, давайте выясним причину распространенного заблуждения относительно реквизитов типа СписокЗначений. В 1С СписокЗначений — это коллекция, используемая преимущественно в коде или для интерфейсных элементов, но не для непосредственного хранения данных в информационной базе. Если документ (например, "Реализация товаров и услуг") связан с несколькими другими документами (например, "Заказами клиента"), эта связь обычно реализуется через табличные части основного документа или через регистры сведений.

В типовой конфигурации 1С:ERP 2.x документ "Реализация товаров и услуг" может быть оформлен на основании нескольких "Заказов клиента". Эта связь формируется через табличную часть «Товары» документа "Реализация товаров и услуг". Каждая строка табличной части "Товары" содержит поле ЗаказКлиента, которое ссылается на соответствующий заказ.

Дополнительные реквизиты, такие как "Иные сведения из заказа", также не хранятся напрямую в основной таблице документа. Для их хранения используются специальные табличные части или регистры сведений, которые связываются с основным объектом (в нашем случае, с документом "Заказ клиента"). Это означает, что для получения значений дополнительных реквизитов в запросе всегда потребуется дополнительное соединение с соответствующей таблицей (например, Документ.ЗаказКлиента.ДополнительныеРеквизиты).

Шаг 1: Получение списка заказов клиента из реализации

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

Рассмотрим пример запроса:


ВЫБРАТЬ РАЗЛИЧНЫЕ
    Док.ЗаказКлиента КАК ЗаказКлиента
ИЗ
    Документ.РеализацияТоваровУслуг.Товары КАК Док
ГДЕ
    Док.Ссылка = &Ссылка
    И Док.ЗаказКлиента.Ссылка ЕСТЬ НЕ NULL

В этом запросе:

  1. Мы выбираем поле ЗаказКлиента из табличной части Документ.РеализацияТоваровУслуг.Товары.
  2. Используем КАК Док для краткости обращения.
  3. Условие Док.Ссылка = &Ссылка позволяет отфильтровать данные по конкретному документу реализации, передаваемому как параметр.
  4. Условие Док.ЗаказКлиента.Ссылка ЕСТЬ НЕ NULL гарантирует, что мы получим только те строки, где действительно указан заказ клиента, избегая пустых ссылок.
  5. Ключевое слово ВЫБРАТЬ РАЗЛИЧНЫЕ исключает дубликаты, если один и тот же заказ встречается в нескольких строках табличной части "Товары" данной реализации.

Шаг 2: Извлечение дополнительных реквизитов из связанных заказов

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

Понимание ЛЕВОГО СОЕДИНЕНИЯ (LEFT JOIN) и условий ПО (ON) / ГДЕ (WHERE)

Это критически важный момент для построения корректных запросов в 1С. Разберем принципы:

  1. ЛЕВОЕ СОЕДИНЕНИЕ (LEFT JOIN): Этот тип соединения возвращает все строки из левой (основной) таблицы и только те строки из правой (присоединяемой), которые соответствуют условию соединения. Если для строки из левой таблицы нет соответствующих строк в правой, поля из правой таблицы будут заполнены значением NULL. Это позволяет нам сохранить все данные из основной таблицы, даже если связанные данные отсутствуют.
  2. Условия в секции ПО (ON): Условия, указанные в секции ПО для ЛЕВОГО СОЕДИНЕНИЯ, применяются до объединения таблиц. Это означает, что они фильтруют строки правой таблицы *перед* тем, как произойдет соединение. Строки из левой таблицы, для которых не нашлось совпадений в правой таблице (с учетом условий в ПО), все равно будут включены в результат, а поля правой таблицы будут NULL.
  3. Условия в секции ГДЕ (WHERE): Условия, указанные в секции ГДЕ, применяют фильтрацию после того, как соединение уже выполнено. Если в условии ГДЕ используется поле из правой таблицы ЛЕВОГО СОЕДИНЕНИЯ и это поле не равно NULL (т.е. мы ищем конкретное значение), то строки, для которых в правой таблице не нашлось соответствий (и чьи поля были заполнены NULL), будут отсечены. Это фактически превращает ЛЕВОЕ СОЕДИНЕНИЕ во ВНУТРЕННЕЕ СОЕДИНЕНИЕ (INNER JOIN) для этих строк, поскольку отбрасываются все строки, для которых в правой таблице не было соответствия или условие в ГДЕ не выполнилось для NULL-значений.

Для нашей задачи — получить дополнительный реквизит "Иные сведения из заказа" из документа "Заказ клиента" — нам необходимо использовать ЛЕВОЕ СОЕДИНЕНИЕ. Условие по имени дополнительного реквизита (например, ЗаказКлиентаДополнительныеРеквизиты.Свойство = &ДопРеквизитИныеСведенияИзЗаказа) следует перенести из секции ГДЕ в секцию ПО. Это гарантирует, что мы увидим все реализации и их заказы, даже если у заказа нет нужного дополнительного реквизита или он не заполнен.

Пример некорректного запроса и его исправление

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


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

В этом запросе проблема заключается в строке И ЗаказКлиентаДополнительныеРеквизиты.Свойство = &ДопРеквизитИныеСведенияИзЗаказа, находящейся в секции ГДЕ. Если для какого-либо ЗаказКлиента не существует дополнительного реквизита с заданным свойством (или его значение равно NULL), то соответствующая строка из Документ.ЗаказКлиента.ДополнительныеРеквизиты не будет найдена, и поля из этой таблицы станут NULL. Условие в ГДЕ, применяемое к NULL-значению, отсечет эту строку, фактически превратив ЛЕВОЕ СОЕДИНЕНИЕ во ВНУТРЕННЕЕ СОЕДИНЕНИЕ.

Чтобы избежать этого и получить все реализации с заказами, даже если доп. реквизит не найден, нам необходимо перенести условие на дополнительный реквизит в секцию ПО:


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

Разберем этот исправленный запрос:

  1. В качестве основной таблицы мы используем Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары. Это логично, поскольку именно эта табличная часть содержит ссылки на заказы клиента.
  2. Мы выполняем ЛЕВОЕ СОЕДИНЕНИЕ с таблицей Документ.ЗаказКлиента.ДополнительныеРеквизиты КАК ЗаказКлиентаДополнительныеРеквизиты.
  3. В секции ПО мы указываем два условия:
    • РеализацияТоваровУслугТовары.ЗаказКлиента = ЗаказКлиентаДополнительныеРеквизиты.Ссылка: это основное условие соединения по ссылке на заказ.
    • ЗаказКлиентаДополнительныеРеквизиты.Свойство = &ДопРеквизитИныеСведенияИзЗаказа: это условие фильтрации по конкретному дополнительному реквизиту. Перенос его в ПО означает, что мы пытаемся найти соответствующий доп. реквизит *во время* соединения. Если он не находится, поля из ЗаказКлиентаДополнительныеРеквизиты будут NULL, но строка из РеализацияТоваровУслугТовары все равно останется в результате.
  4. В секции ГДЕ мы оставляем только условие РеализацияТоваровУслугТовары.Ссылка = &Ссылка, чтобы отфильтровать данные по конкретной реализации. Это условие применяется к базовой таблице, поэтому оно безопасно.

Данный запрос обеспечит нам список всех заказов, связанных с указанной реализацией, и для каждого заказа будет показано значение дополнительного реквизита "Иные сведения из заказа". Если у заказа такой реквизит отсутствует или не заполнен, соответствующее поле Значение будет NULL, что является ожидаемым поведением для ЛЕВОГО СОЕДИНЕНИЯ.

Окончательное решение и дополнительные рекомендации

Если нам требуется получить не только значение дополнительного реквизита, но и, например, контрагента заказа клиента, то его можно получить прямым обращением через точку к ссылке на заказ:


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

В этом запросе мы добавили РеализацияТоваровУслугТовары.ЗаказКлиента.Контрагент. Это возможно, так как ЗаказКлиента является ссылочным типом данных, и через него можно напрямую обращаться к реквизитам связанного документа.

Для выгрузки в ДиаДок, как в нашем случае, мы можем получить нужные нам сведения:

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

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

Изучив эти принципы, вы сможете более эффективно работать со сложными запросами в 1С:ERP, корректно извлекая данные из связанных объектов и их дополнительных реквизитов.

← На главную