Почему организация не выбирается во втором запросе пакета 1С?

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

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

Предположим, у нас есть два пакета запросов, которые очень похожи по структуре. Первый пакет успешно возвращает данные организации, а второй, несмотря на аналогичный код, выдает пустые значения для поля "Организация".

Исходный код запросов

Для понимания проблемы, обратимся к фрагментам кода, представленным в сообщении:

Пакет запросов 1:


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

Пакет запросов 2:


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

Как мы видим, действительно, запросы очень похожи. Основное отличие между первым и вторым блоком запросов (внутри одного пакета) заключается в используемой табличной части документа Документ.ХХХ_НарядЗадание: первый использует ОтгруженоЗаказчиком, а второй — ОтгруженоИсполнителем. Однако, проблема, как выяснилось, заключалась в другом.

Суть проблемы: пустые ссылки на организацию

Причиной, по которой во втором запросе (или в какой-то части результатов) не заполнялась организация, оказалась пустая ссылка на объект "Организация" в одном из документов, который передавался в параметр &Документ (или &МасОтгруженоЗаказчиком / &МасОтгруженоИсполнителем, в зависимости от контекста). Автор темы обнаружил, что если в документе, ссылка на который содержится в массиве для запроса, поле "Организация" было не заполнено, то соответствующее поле в результате запроса оставалось пустым.

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

Распространенное заблуждение, которое возникло у автора, заключалось в том, что условие НашДокумент.Ссылка В(&Документ) работает только для последнего документа в переданном массиве. Это не так. Оператор В (аналогично IN в SQL) проверяет, является ли значение поля НашДокумент.Ссылка одним из значений, перечисленных в переданном параметре-массиве &Документ. Если в массиве содержится несколько ссылок, условие будет истинно для любого документа, чья ссылка присутствует в этом массиве. То есть, запрос корректно обрабатывает все ссылки, но возвращает пустые данные для тех документов, где реквизит "Организация" не был заполнен.

Почему это влияет на печатную форму?

Если в вашем коде, который формирует печатную форму (или макет), предусмотрена логика, зависящая от наличия заполненной организации (например, вывод контактных данных организации, ее юридического лица, банковских реквизитов и т.д.), то при получении пустой ссылки для поля "Организация" эти блоки могут оставаться незаполненными или вызывать ошибки, если не предусмотрена специальная обработка для пустых значений. Именно поэтому, после того как автор вручную заполнил поле "Организация" в "проблемном" документе, все данные стали корректно отображаться.

Решение проблемы и лучшие практики

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

  1. Пошаговая отладка запросов

    Использование отладчика 1С — это наш основной инструмент для диагностики подобных проблем. Мы можем пошагово выполнить код и посмотреть содержимое переменных и результатов запросов.

    1. Установка точек останова: Установите точку останова непосредственно перед строкой НашиДанные = ОсновнойЗапрос.ВыполнитьПакет();
    2. Проверка параметров: В режиме отладки проверьте содержимое параметров МасОтгруженоЗаказчиком, МасОтгруженоИсполнителем и Документ. Убедитесь, что они содержат ожидаемые ссылки на документы.
    3. Проверка текста запроса: Скопируйте значение ОсновнойЗапрос.Текст в консоль запросов 1С (или в конфигураторе) и выполните его. Это позволит нам увидеть сырые данные, которые возвращает запрос, без какой-либо последующей обработки в коде.
    4. Анализ результатов: После выполнения запроса в консоли, внимательно изучите колонку "Организация". Если там присутствуют пустые ссылки для некоторых записей, это указывает на проблему с данными в исходных документах.
    5. Проверка обработки результатов: Далее, пошагово выполните код после ВыполнитьПакет(). Обратите внимание на то, как происходит заполнение переменных Шапка и ВыборкаСтрок.
  2. Обеспечение целостности данных

    Лучший способ избежать пустых ссылок — это предотвратить их появление на этапе ввода данных. Мы можем реализовать проверки заполнения обязательных реквизитов (таких как "Организация") в формах документов или при их записи.

    
    // Пример проверки заполнения в модуле объекта документа
    Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
        Если Организация.Пустая() Тогда
            Сообщить("Не заполнена организация документа!", СтатусСообщения.Важное);
            Отказ = Истина;
        КонецЕсли;
    КонецПроцедуры
    

    Такие проверки гарантируют, что в базе данных не будет документов с незаполненными ключевыми реквизитами.

  3. Обработка пустых ссылок непосредственно в запросе

    Если мы не можем гарантировать заполненность данных, или работаем с уже существующими данными, где могут быть пробелы, мы можем использовать функции языка запросов 1С для обработки пустых ссылок.

    1. Функция ЕСТЬNULL: Эта функция позволяет заменить NULL-значение на заданное нами значение по умолчанию. В случае пустой ссылки на объект "Организация" мы можем заменить ее на пустую ссылку того же типа или на предопределенный элемент.

      
      |    ЕСТЬNULL(НашДокумент.Организация, ЗНАЧЕНИЕ(Справочник.Организации.ПустаяСсылка)) КАК Организация,
      |    ЕСТЬNULL(НашДокумент.Организация.ЮрФизЛицо, ЗНАЧЕНИЕ(Справочник.ЮрФизЛица.ПустаяСсылка)) КАК ЮрФизЛицо,
      

      Используя ЗНАЧЕНИЕ(Справочник.Организации.ПустаяСсылка), мы гарантируем, что тип данных останется корректным (ссылка), но значение будет пустым, что может быть полезно для дальнейшей обработки, предотвращая ошибки при попытке обратиться к несуществующим полям пустой ссылки.

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

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

      Здесь, если НашДокумент.Организация будет пустой ссылкой, строка из НашДокумент все равно попадет в результат, а НаименованиеОрганизации будет NULL. Это позволяет нам гибко обрабатывать отсутствие связанных данных, не теряя основные записи.

  4. Корректная обработка результатов пакетного запроса

    Независимо от того, как мы формируем данные, важно правильно их обработать после выполнения запроса. В приведенном коде используется следующая конструкция:

    
    НашиДанные = ОсновнойЗапрос.ВыполнитьПакет();
    Шапка      = НашиДанные[0].Выбрать();
    Шапка.Следующий();
    ВыборкаСтрок = НашиДанные[1].Выгрузить();
    

    Здесь НашиДанные[0] — это результат первого запроса пакета (запрос по шапке документа). Метод Выбрать() возвращает объект ВыборкаДанныхЗапроса. Вызов Шапка.Следующий() перемещает курсор к первой (и только одной) записи выборки. Если параметр &Документ содержит массив из нескольких ссылок, и первый запрос пакета должен вернуть несколько записей (по одной на каждый документ), то текущий код обработает только первую запись из результата первого запроса. Все остальные записи будут проигнорированы, что может привести к неполному или некорректному заполнению отчета, если он рассчитан на данные со всех документов.

    Если мы ожидаем несколько записей для шапки, нам следует использовать цикл:

    
    НашиДанные = ОсновнойЗапрос.ВыполнитьПакет();
    ВыборкаШапки = НашиДанные[0].Выбрать();
    Пока ВыборкаШапки.Следующий() Цикл
        // Обрабатываем каждую запись шапки
        // Например, добавляем данные в таблицу отчета
        Сообщить("Обработан документ: " + ВыборкаШапки.Номер + ", Организация: " + ВыборкаШапки.Организация);
    КонецЦикла;
    
    ВыборкаСтрок = НашиДанные[1].Выгрузить();
    

    Эта модификация гарантирует, что мы обработаем все данные, полученные из первого запроса пакета, а не только первую запись.

Общие рекомендации при работе с запросами

Подводя итог, можно выделить несколько ключевых мосновных моментов, которые помогут нам в работе с запросами в 1С:

  1. Всегда проверяйте исходные данные: Прежде чем винить запрос, убедитесь, что в базе данных присутствуют те данные, которые мы ожидаем получить. Это особенно актуально для ссылочных полей.
  2. Используйте консоль запросов: Это мощный инструмент для быстрого тестирования запросов и анализа их результатов без необходимости запуска всей обработки или отчета. Мы можем подставлять различные параметры и видеть, как меняется результат.
  3. Явное указание псевдонимов полей: Хотя в 1С это не всегда строго необходимо, явное указание псевдонимов (КАК) делает запрос более читаемым и понятным, особенно при выборке одноименных полей из разных таблиц или при использовании выражений.
  4. Понимайте механизмы работы 1С: Разница между прямым доступом к реквизиту (Документ.Организация) и использованием соединений (ЛЕВОЕ СОЕДИНЕНИЕ) имеет важное значение. Прямой доступ к несуществующей ссылке возвращает пустую ссылку, а соединение может возвращать NULL-значения для полей из отсутствующей части соединения.
  5. Остерегайтесь скрытых преобразований типов: При работе с полями, которые могут быть разных типов (например, пустая строка или ссылка на пустой справочник), важно убедиться, что запрос обрабатывает их корректно.

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

← На главную