Как исправить ошибку «Неверно указан реквизит» в методе НайтиПоРеквизиту и правильно получить проводки документа

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

При разработке прикладных решений на платформе «1С:Предприятие» начинающие специалисты часто сталкиваются с непониманием того, как связаны между собой элементы экранных форм, ссылки на объекты базы данных и физические реквизиты таблиц СУБД. Классическим проявлением такой путаницы является системная ошибка «Неверно указан реквизит» при вызове метода НайтиПоРеквизиту.

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

Выясняем причину ошибки «Неверно указан реквизит»

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


ОтражениеСсылка = Документы.ПеремещениеТоваров.НайтиПоРеквизиту("ПеремещениеТоваров", ПеремещениеТ);

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

Синтаксис метода выглядит следующим образом:

Документы.[ИмяДокумента].НайтиПоРеквизиту(<ИмяРеквизита>, <ЗначениеРеквизита>)

Рассмотрим назначение его параметров:

  1. ИмяРеквизита (тип Строка) — имя реального реквизита объекта метаданных, который физически существует в таблице базы данных (например, "Организация", "Контрагент", "СкладОтправитель").
  2. ЗначениеРеквизита — значение, по которому система должна отфильтровать записи (например, конкретная ссылка на элемент справочника «Организации»).

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

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

Основная ошибка автора заключается в неверном понимании архитектуры работы клиент-серверного взаимодействия платформы и структуры экранных форм. Давайте проанализируем ход мыслей начинающего программиста:

«Я создал на форме реквизит (поле ввода) с именем "ПеремещениеТ". Пользователь выбрал туда документ перемещения. Значит, чтобы получить этот документ в коде, мне нужно вызвать метод НайтиПоРеквизиту и передать туда имя моего поля с формы».

Это в корне неверно. Выясним, почему:

Самое главное: переменная ПеремещениеТ на форме уже содержит в себе готовую уникальную ссылку на документ в базе данных. Ссылка (тип ДокументСсылка) — это и есть готовый уникальный ключ (GUID), по которому СУБД однозначно идентифицирует запись. Искать то, что пользователь уже выбрал и ссылка на что у нас уже находится в переменной, не имеет никакого практического смысла. Искать больше ничего не нужно — объект уже у нас «в руках».

Скрытые ограничения и особенности метода НайтиПоРеквизиту

В профессиональной среде разработчиков использование методов класса НайтиПо... (таких как НайтиПоРеквизиту, НайтиПоКоду, НайтиПоНаименованию) строго регламентируется стандартами разработки фирмы «1С». Рассмотрим критические нюансы и «подводные камни» этого метода:

  1. Возврат только первой ссылки. Метод всегда возвращает ссылку только на одну (первую попавшуюся в выборке СУБД) запись. Если в базе данных существует несколько документов с одинаковым значением искомого реквизита, получить остальные элементы данным методом невозможно. Вы рискуете получить не те данные, на которые рассчитывали.
  2. Проблема производительности и индексации. Поиск по неиндексированному полю приводит к полному сканированию таблицы базы данных (Table Scan). На реальных рабочих базах данных объемом в сотни гигабайт это вызывает колоссальные задержки, перегрузку дисковой подсистемы сервера и жесткие блокировки таблиц СУБД. Чтобы использовать этот метод безопасно, у реквизита в конфигураторе обязательно должно быть установлено свойство «Индексировать» или «Индексировать с дополнительным упорядочиванием».
  3. Ограничения типов данных. Метод не предназначен для поиска по реквизитам неограниченной длины (например, длинный текст типа «Комментарий» или форматированный документ) и по реквизитам, расположенным внутри табличных частей документа.
  4. Ограничение прав доступа (RLS). Если в системе включены механизмы ограничения доступа на уровне записей (RLS), поиск по реквизиту может завершиться ошибкой или вернуть пустую ссылку, если первый найденный документ окажется недоступен текущему пользователю по правам.

Три правильных способа получить проводки (движения) по имеющейся ссылке

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

Способ 1. Получение проводок с помощью Запроса (Самый быстрый и рекомендуемый способ)

Использование механизма запросов в 1С — это «золотой стандарт» разработки. Запросы работают быстрее всего, так как СУБД выполняет выборку данных на сервере и передает в оперативную память приложения только строго определенные поля. Это минимизирует сетевой трафик и нагрузку на сервер приложений.

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


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Хозрасчетный.Период КАК Период,
    |	Хозрасчетный.СчетДт КАК СчетДт,
    |	Хозрасчетный.СчетКт КАК СчетКт,
    |	Хозрасчетный.Сумма КАК Сумма,
    |	Хозрасчетный.Содержание КАК Содержание
    |ИЗ
    |	РегистрБухгалтерии.Хозрасчетный КАК Хозрасчетный
    |ГДЕ
    |	Хозрасчетный.Регистратор = &Регистратор";

// Устанавливаем параметр запроса - нашу готовую ссылку из формы
Запрос.УстановитьПараметр("Регистратор", ПеремещениеТ);

РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();

Пока Выборка.Следующий() Цикл
    // Выполняем обработку каждой проводки
    СообщениеТекст = Шаблон("Проводка: %1 - %2 на сумму %3 руб. (%4)", 
                             Выборка.СчетДт, 
                             Выборка.СчетКт, 
                             Выборка.Сумма, 
                             Выборка.Содержание);
    ОбщегоНазначенияКлиентСервер.СообщитьПользователю(СообщениеТекст);
КонецЦикла;

Преимущества метода: Высокая скорость обработки информации, возможность накладывать дополнительные фильтры (например, отбирать проводки только по определенному счету), легкая группировка и сортировка результатов непосредственно средствами СУБД.

Способ 2. Чтение проводок через Набор записей регистра

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

Рассмотрим, как прочитать набор записей регистра бухгалтерии по конкретному документу:


// Создаем объект набора записей нужного регистра
НаборЗаписей = РегистрыБухгалтерии.Хозрасчетный.СоздатьНаборЗаписей();

// Устанавливаем отбор по регистратору (нашему документу)
НаборЗаписей.Отбор.Регистратор.Установить(ПеремещениеТ);

// Считываем движения из базы данных в память сервера приложений
НаборЗаписей.Прочитать();

// Обходим коллекцию движений в цикле
Для Каждого Запись Из НаборЗаписей Цикл
    СчетДт = Запись.СчетДт;
    СчетКт = Запись.СчетКт;
    Сумма  = Запись.Сумма;
    
    // Здесь мы можем проанализировать или изменить поля записи
    // Например: Запись.Сумма = Сумма * 1.1; // Увеличим сумму на 10%
КонецЦикла;

// Если мы вносили изменения, их необходимо записать обратно в базу данных:
// НаборЗаписей.Записать();

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

Способ 3. Получение проводок через свойство «Движения» объекта (Только для серверного контекста)

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

Проанализируем структуру кода для этого варианта:


// Получаем тяжелый объект документа из ссылки (Не рекомендуется для простого чтения данных!)
ДокументОбъект = ПеремещениеТ.ПолучитьОбъект();

// Обращаемся к коллекции движений документа и принудительно считываем записи нужного регистра
ДокументОбъект.Движения.Хозрасчетный.Прочитать();

// Перебираем проводки
Для Каждого Проводка Из ДокументОбъект.Движения.Хозрасчетный Цикл
    СчетДт = Проводка.СчетДт;
    СчетКт = Проводка.СчетКт;
    Сумма  = Проводка.Сумма;
    
    // Обрабатываем проводку
КонецЦикла;

Преимущества метода: Простота интеграции кода внутри родных процедур проведения документов в конфигураторе.

Резюме для разработчика

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

← На главную