Почему запрос к ценам номенклатуры не работает в расширении 1С:УТ 11, но работает во внешней обработке?

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

При разработке функционала для платформы 1С:Предприятие 8.3, особенно когда мы переносим или адаптируем код из внешних обработок в расширения конфигурации, мы можем столкнуться с неожиданным поведением. Одна из таких ситуаций, которая часто ставит в тупик разработчиков, — это когда запрос к данным работает безупречно во внешней обработке, но при включении той же логики в расширение начинает возвращать пустые результаты без каких-либо ошибок. В частности, мы рассмотрим случай, когда запрос к регистру сведений о ценах номенклатуры перестает работать в расширении конфигурации 1С:Управление торговлей 11 (УТ 11).

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

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

Представим, что у нас есть внешняя обработка, которая по заданному коду находит товар из справочника «Номенклатура», а затем отображает его цену (из регистра сведений «ЦеныНоменклатуры») и остаток (из регистра сведений «РаспределениеЗапасов»). Обработка работает корректно, когда мы открываем ее через меню «Файл» – «Открыть».

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

Рассмотрим проблемный фрагмент кода, который отвечает за получение цены:


ТекТовар = Справочники.Номенклатура.НайтиПоКоду( КодДляПоиска ).Ссылка; // Находим товар по коду

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

В этом коде мы сначала находим ссылку на номенклатуру по коду, а затем формируем запрос к виртуальной таблице РегистрСведений.ЦеныНоменклатуры.СрезПоследних. В запрос передаются три параметра: дата среза &ТекДата, ссылка на номенклатуру &ТекТовар и ссылка на вид цены &ВидЦены. Именно параметр &ВидЦены и оказался камнем преткновения.

Выясняем причину: изменение типа данных реквизита

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

В нашем случае, реквизит формы, предназначенный для хранения значения ВидЦены, во внешней обработке имел правильный тип — СправочникСсылка.ВидыЦен. Однако при добавлении обработки в расширение, без должного заимствования справочника ВидыЦен, тип этого реквизита изменился на Строка.

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

Именно из-за этого несоответствия типов запрос возвращал пустую выборку для цен, тогда как запрос для остатков (где, вероятно, параметр Номенклатура или другие параметры сохранили свои типы) продолжал работать.

Решение проблемы: восстанавливаем корректные типы данных

Для решения описанной проблемы нам необходимо выполнить следующие действия:

  1. Проверяем тип реквизита формы ВидЦены в расширении.

    Откроем конфигуратор и перейдем к нашей обработке, которая находится в расширении. Откроем ее форму и перейдем на вкладку «Реквизиты». Найдем реквизит, который используется для хранения значения ВидЦены. Внимательно проверим его тип данных. Если мы увидим, что тип изменился на Строка, а не на ожидаемый СправочникСсылка.ВидыЦен, то мы нашли корень проблемы.

  2. Заимствуем необходимые объекты в расширение.

    Чтобы избежать подобных преобразований типов, нам необходимо явно заимствовать в расширение все объекты метаданных основной конфигурации, с которыми наша обработка будет работать напрямую. В нашем случае это, как минимум, справочник Справочники.ВидыЦен. Откроем расширение в конфигураторе, перейдем на вкладку «Подсистемы» или «Общие» (в зависимости от структуры), а затем через контекстное меню или кнопку «Добавить» заимствуем справочник ВидыЦен из основной конфигурации.

    После заимствования справочника убедимся, что реквизит формы ВидЦены в нашей обработке имеет корректный тип СправочникСсылка.ВидыЦен. Возможно, после заимствования придется пересоздать реквизит формы или обновить тип вручную, если он не обновился автоматически.

  3. Корректируем код запроса (рекомендуемые улучшения).

    Хотя основная проблема была в типе параметра, мы можем рассмотреть и более оптимальный вариант запроса. Для виртуальных таблиц, таких как СрезПоследних, рекомендуется передавать условия отбора непосредственно в параметры функции среза, а не использовать секцию ГДЕ. Это позволяет платформе более эффективно строить запросы.

    Пример такого улучшенного запроса:

    
    Запрос = Новый Запрос;
    Текст = "ВЫБРАТЬ
    | ЦеныНоменклатуры.Номенклатура КАК Номенклатура,
    | ЦеныНоменклатуры.ВидЦены КАК ВидЦены,
    | ЦеныНоменклатуры.Цена КАК Цена
    |ИЗ
    | РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&ТекДата, ВидЦены = &ВидЦены И Номенклатура = &ТекТовар) КАК ЦеныНоменклатуры
    |
    | ";
    Запрос.Текст = Текст;
    Запрос.УстановитьПараметр("ТекДата",НачалоДня(ТекущаяДата));
    Запрос.УстановитьПараметр("ВидЦены",ВидЦены); // Теперь здесь должна быть ссылка
    Запрос.УстановитьПараметр("ТекТовар",ТекТовар);
    Результат = Запрос.Выполнить;
    

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

Подробное объяснение принципов работы расширений и типов данных

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

Необходимость заимствования объектов и их типов

При работе с расширениями конфигурации критически важно явно заимствовать из основной конфигурации все объекты метаданных, с которыми предполагается работа. Это касается справочников, документов, регистров и их реквизитов. Если объект, такой как Справочник.ВидыЦен, не был заимствован или был заимствован некорректно, система расширения может автоматически преобразовать его тип. Это может привести к тому, что ссылочные данные, передаваемые в запрос, будут восприниматься как строковые, что сделает отбор неработоспособным. Процесс заимствования гарантирует, что расширение «знает» о структуре и типах данных оригинального объекта, что позволяет ему правильно взаимодействовать с ним.

Особенности заимствования реквизитов с составными типами

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

Влияние версии платформы

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

Важность соответствия типов параметров запроса

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

Отладка расширений — основной инструмент

В случае возникновения подобных проблем, когда код работает как внешняя обработка, но не работает в расширении, одним из самых эффективных методов является использование отладчика. Отладчик 1С позволяет пошагово выполнять код и проверять значения и типы переменных в режиме реального времени. Мы можем установить точку останова непосредственно перед выполнением запроса и проанализировать значения всех параметров, которые передаются в запрос (&ТекДата, &ВидЦены, &ТекТовар). Особое внимание уделим проверке типов этих переменных. Например, мы можем использовать выражение ТипЗнч(ВидЦены) в окне отладчика, чтобы убедиться, что оно возвращает ВидЦены, а не Строка. Это поможет точно определить, на каком этапе происходит изменение типа данных или почему запрос возвращает пустой результат. Методики отладки расширений аналогичны отладке основной конфигурации, поэтому мы можем использовать все привычные инструменты.

Заключение

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

← На главную