Как исправить ошибку «Переменная не определена» при установке параметров запроса в 1С?

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

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

Разбор причины ошибки: Область видимости и именование реквизитов

Проанализируем ситуацию: в коде процедуры ОбработкаПроведения вызывается метод Запрос.УстановитьПараметр("Контрагенты", Контрагенты). Система выдает ошибку, указывая, что переменная Контрагенты не определена. Давайте выясним, почему это происходит.

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

Чтобы решить эту проблему, выполним следующие шаги:

  1. Откроем дерево конфигурации и проверим точное имя реквизита в объекте Документ.РеализацияТоваров.
  2. Если реквизит называется Контрагент, исправим строку установки параметра:

// Правильная передача реквизита документа в параметр запроса
Запрос.УстановитьПараметр("Контрагенты", ЭтотОбъект.Контрагент); 

Использование обращения ЭтотОбъект.ИмяРеквизита помогает избежать путаницы с локальными переменными и делает код более читаемым, явно указывая, откуда берутся данные.

Исправление работы с моментом времени в запросе

В представленном коде автора есть еще одна критическая ошибка: Запрос.УстановитьПараметр("Момент", Неопределено). Разберем, к чему это приводит. Передача Неопределено в качестве периода для виртуальных таблиц остатков заставляет систему получать данные на «бесконечность» (31.12.3999). Это означает, что запрос увидит все будущие движения, что в корне неверно для оперативного контроля остатков при проведении.

Рассмотрим, как правильно установить момент времени, чтобы получить актуальные остатки до совершения текущей операции:


// Получаем остатки на момент времени документа, исключая его собственные движения
ГраницаКонтроля = Новый Граница(МоментВремени(), ВидГраницы.Исключая);
Запрос.УстановитьПараметр("Момент", ГраницаКонтроля);

Если мы используем ВидГраницы.Исключая, запрос вернет состояние регистра за миг до проведения текущего документа. Это особенно важно при перепроведении, чтобы документ не «видел» свои старые записи и не считал их доступным остатком.

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

Проанализируем текст запроса. В нем дважды происходит левое соединение с одной и той же виртуальной таблицей РегистрНакопления.БронированиеТоваров.Остатки. Один раз для получения «чужой» брони (где контрагент не равен текущему), второй раз — для «своей» брони. Это создает лишнюю нагрузку на сервер базы данных.

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


|ВЫБРАТЬ
|    ВТДок.Номенклатура КАК Номенклатура,
|    ЕСТЬNULL(Бронь.КоличествоЗабронированногоОстаток, 0) КАК ВсегоЗабронировано,
|    ВЫБОР 
|        КОГДА Бронь.Контрагенты = &КонтрагентПараметр 
|        ТОГДА ЕСТЬNULL(Бронь.КоличествоЗабронированногоОстаток, 0) 
|        ИНАЧЕ 0 
|    КОНЕЦ КАК МояБронь
|ИЗ
|    ВТДок КАК ВТДок
|ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.БронированиеТоваров.Остатки(&Момент, 
|    Номенклатура В (ВЫБРАТЬ Номенклатура ИЗ ВТДок)) КАК Бронь
|ПО ВТДок.Номенклатура = Бронь.Номенклатура

Такой подход позволяет сократить количество обращений к таблицам базы данных и делает запрос более производительным.

Управляемые блокировки данных

При выполнении контроля остатков крайне важно обеспечить целостность данных. Если два пользователя одновременно проводят документ на одну и ту же позицию, может возникнуть ситуация «двойного расхода», когда оба видят положительный остаток, и оба списывают его в минус. Чтобы этого избежать, используем объект БлокировкаДанных.

Разберем пример реализации блокировки перед выполнением запроса:


Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.ОстаткиТоваров");
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
ЭлементБлокировки.ИсточникДанных = СписокНоменклатуры; // Табличная часть или ВТ
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Номенклатура", "Номенклатура");
Блокировка.Заблокировать();

Только после установки блокировки можно смело выполнять Запрос.Выполнить(). Это гарантирует, что пока ваш документ анализирует остатки, другой процесс не сможет изменить данные по этим же позициям номенклатуры.

Итоги и рекомендации

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

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

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

← На главную