Как правильно передать параметры документа в запрос к регистру накопления

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

При разработке в среде 1С:Предприятие 8 начинающие разработчики часто сталкиваются с ситуацией, когда запрос к регистру накопления корректно работает в консоли запросов, но выдает ошибки при переносе в код конфигурации. Чаще всего проблема кроется не в самом тексте запроса, а в том, как передаются параметры из контекста документа. Давайте подробно разберем, как получить остатки товаров, используя данные текущего документа, и выясним, почему возникают ошибки типа «Переменная не определена».

Разбор ситуации: Почему не работает обращение к реквизитам?

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


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

// Здесь возникают вопросы: как передать Склад и Дату?
Запрос.УстановитьПараметр("Дата", ...);
Запрос.УстановитьПараметр("Номенклатура", Номенклатура);
Запрос.УстановитьПараметр("Склад", ...);

Основная причина ошибок — непонимание контекста, в котором исполняется программный код. В 1С существует два основных места для написания логики документа: Модуль объекта и Модуль формы.

Решение 1: Работа в Модуле объекта

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

Проанализируем правильный способ установки параметров в Модуле объекта:

  1. Склад: К реквизиту Склад мы обращаемся напрямую. Не нужно писать Объект.Склад или ЭтотОбъект.Склад (хотя второй вариант допустим, он обычно избыточен).
  2. Дата: Аналогично, используем реквизит Дата.

Пример кода для Модуля объекта:


Запрос.УстановитьПараметр("Склад", Склад); // Склад — это имя реквизита в метаданных документа
Запрос.УстановитьПараметр("Дата", Дата);   // Дата — стандартный реквизит любого документа

Ошибка Переменная не определена (Объект.Склад) в Модуле объекта возникает потому, что переменной Объект здесь просто не существует. Она характерна только для Модуля формы.

Решение 2: Работа в Модуле формы

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

Разберем, как это выглядит на практике:


// В модуле формы управляемого приложения
Запрос.УстановитьПараметр("Склад", Объект.Склад);
Запрос.УстановитьПараметр("Дата", Объект.Дата);

Важный нюанс: Если вы вызываете серверную процедуру из формы, убедитесь, что вы передаете в нее нужные данные или что данные формы синхронизированы с сервером.

Важные аспекты работы с остатками: МоментВремени и Граница

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

Рассмотрим использование специальных объектов:

Оптимизация: Почему нельзя делать запрос в цикле?

Часто разработчики совершают критическую ошибку: помещают запрос в цикл по строкам табличной части документа. Это создает огромную нагрузку на сервер базы данных. Давайте разберем, как сделать это правильно (методом «за один запрос»):

  1. Выгрузим все товары из табличной части в массив или временную таблицу.
  2. Передадим этот массив в параметр запроса.
  3. Используем условие В (&МассивНоменклатуры) в параметрах виртуальной таблицы.

Пример эффективного подхода:


МассивТоваров = Товары.ВыгрузитьКолонку("Номенклатура");

Запрос.Текст = 
    "ВЫБРАТЬ
    |   ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура,
    |   ЕСТЬNULL(ТоварыНаСкладахОстатки.ВНаличииОстаток, 0) КАК Остаток
    |ИЗ
    |   РегистрНакопления.ТоварыНаСкладах.Остатки(&Дата, 
    |       Склад = &Склад И Номенклатура В (&МассивТоваров)) КАК ТоварыНаСкладахОстатки";

Запрос.УстановитьПараметр("МассивТоваров", МассивТоваров);
Запрос.УстановитьПараметр("Склад", Склад);
Запрос.УстановитьПараметр("Дата", МоментВремени());

Обратите внимание на функцию ЕСТЬNULL. Мы настоятельно рекомендуем использовать её в запросах на получение остатков. Если товара нет на складе, виртуальная таблица не вернет строку, или вернет NULL при соединении таблиц. Функция ЕСТЬNULL(..., 0) гарантирует, что ваша программа получит число 0 вместо пустого значения, что предотвратит ошибки при дальнейших математических вычислениях.

Заключение

Подведем итоги нашего анализа. Чтобы успешно сформировать запрос к остаткам в контексте документа:

  1. Определите контекст: Используйте прямые имена реквизитов (Склад) в Модуле объекта и обращение через точку (Объект.Склад) в Модуле формы.
  2. Фильтруйте правильно: Всегда устанавливайте фильтры по Складу и Номенклатуре внутри параметров виртуальной таблицы Остатки(...), а не в секции ГДЕ.
  3. Будьте точны со временем: Вместо простой даты используйте МоментВремени или Границу.
  4. Думайте о производительности: Избегайте запросов в циклах, работайте сразу со всем списком товаров из документа.

Следуя этим правилам, вы не только решите проблему с «неопределенными переменными», но и напишете качественный, быстродействующий код, соответствующий стандартам разработки 1С.

← На главную