При разработке прикладных решений на платформе «1С:Предприятие» начинающие специалисты часто сталкиваются с непониманием того, как связаны между собой элементы экранных форм, ссылки на объекты базы данных и физические реквизиты таблиц СУБД. Классическим проявлением такой путаницы является системная ошибка «Неверно указан реквизит» при вызове метода НайтиПоРеквизиту.
В этой статье мы подробно проанализируем, почему возникает данная ошибка, разберем ключевое логическое заблуждение разработчиков при работе с реквизитами формы и рассмотрим три эффективных практических способа получения проводок (движений по регистрам) документа, если ссылка на него нам уже известна.
Для начала давайте детально разберем фрагмент кода, который привел к возникновению ошибки у автора темы:
ОтражениеСсылка = Документы.ПеремещениеТоваров.НайтиПоРеквизиту("ПеремещениеТоваров", ПеремещениеТ);
Чтобы понять, почему платформа выдает исключение, обратимся к назначению и синтаксису метода НайтиПоРеквизиту менеджера объектов. Этот метод предназначен для программного поиска элемента справочника или документа в базе данных по значению одного из его собственных реквизитов, заданных в дереве метаданных конфигурации.
Синтаксис метода выглядит следующим образом:
Документы.[ИмяДокумента].НайтиПоРеквизиту(<ИмяРеквизита>, <ЗначениеРеквизита>)
Рассмотрим назначение его параметров:
Строка) — имя реального реквизита объекта метаданных, который физически существует в таблице базы данных (например, "Организация", "Контрагент", "СкладОтправитель").В ошибочном коде автора первым параметром передается строковое литеральное значение "ПеремещениеТоваров". Платформа пытается найти у документа «Перемещение товаров» реквизит с точно таким же именем. Поскольку документ не может содержать в структуре своих реквизитов самого себя под собственным именем, платформа закономерно прерывает выполнение кода и сообщает разработчику: «Ошибка при вызове метода контекста (НайтиПоРеквизиту) по причине: Неверно указан реквизит».
Основная ошибка автора заключается в неверном понимании архитектуры работы клиент-серверного взаимодействия платформы и структуры экранных форм. Давайте проанализируем ход мыслей начинающего программиста:
«Я создал на форме реквизит (поле ввода) с именем "ПеремещениеТ". Пользователь выбрал туда документ перемещения. Значит, чтобы получить этот документ в коде, мне нужно вызвать метод НайтиПоРеквизиту и передать туда имя моего поля с формы».
Это в корне неверно. Выясним, почему:
ПеремещениеТ с типом ДокументСсылка.ПеремещениеТоваров) — это временная переменная в памяти клиентского приложения или серверного контекста формы. Она служит исключительно для удержания значения, которое выбрал или ввел пользователь на экране.Комментарий у документа).Самое главное: переменная ПеремещениеТ на форме уже содержит в себе готовую уникальную ссылку на документ в базе данных. Ссылка (тип ДокументСсылка) — это и есть готовый уникальный ключ (GUID), по которому СУБД однозначно идентифицирует запись. Искать то, что пользователь уже выбрал и ссылка на что у нас уже находится в переменной, не имеет никакого практического смысла. Искать больше ничего не нужно — объект уже у нас «в руках».
В профессиональной среде разработчиков использование методов класса НайтиПо... (таких как НайтиПоРеквизиту, НайтиПоКоду, НайтиПоНаименованию) строго регламентируется стандартами разработки фирмы «1С». Рассмотрим критические нюансы и «подводные камни» этого метода:
Основная задача автора темы — получить проводки («вытащить проводки»), сформированные конкретным документом перемещения товаров. Поскольку ссылка на документ уже находится в переменной ПеремещениеТ, мы можем решить эту задачу тремя классическими способами. Разберем их по шагам.
Использование механизма запросов в 1С — это «золотой стандарт» разработки. Запросы работают быстрее всего, так как СУБД выполняет выборку данных на сервере и передает в оперативную память приложения только строго определенные поля. Это минимизирует сетевой трафик и нагрузку на сервер приложений.
Посмотрим на пример запроса, извлекающего записи из Хозрасчетного регистра бухгалтерии, где нашим регистратором является документ ПеремещениеТ:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Хозрасчетный.Период КАК Период,
| Хозрасчетный.СчетДт КАК СчетДт,
| Хозрасчетный.СчетКт КАК СчетКт,
| Хозрасчетный.Сумма КАК Сумма,
| Хозрасчетный.Содержание КАК Содержание
|ИЗ
| РегистрБухгалтерии.Хозрасчетный КАК Хозрасчетный
|ГДЕ
| Хозрасчетный.Регистратор = &Регистратор";
// Устанавливаем параметр запроса - нашу готовую ссылку из формы
Запрос.УстановитьПараметр("Регистратор", ПеремещениеТ);
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
// Выполняем обработку каждой проводки
СообщениеТекст = Шаблон("Проводка: %1 - %2 на сумму %3 руб. (%4)",
Выборка.СчетДт,
Выборка.СчетКт,
Выборка.Сумма,
Выборка.Содержание);
ОбщегоНазначенияКлиентСервер.СообщитьПользователю(СообщениеТекст);
КонецЦикла;
Преимущества метода: Высокая скорость обработки информации, возможность накладывать дополнительные фильтры (например, отбирать проводки только по определенному счету), легкая группировка и сортировка результатов непосредственно средствами СУБД.
Этот способ отлично подходит в ситуациях, когда вам необходимо не просто прочитать проводки документа для анализа, но и программно модифицировать их (например, скорректировать суммы, изменить счета или полностью удалить движения). Этот подход работает медленнее запроса, но крайне прост в реализации.
Рассмотрим, как прочитать набор записей регистра бухгалтерии по конкретному документу:
// Создаем объект набора записей нужного регистра
НаборЗаписей = РегистрыБухгалтерии.Хозрасчетный.СоздатьНаборЗаписей();
// Устанавливаем отбор по регистратору (нашему документу)
НаборЗаписей.Отбор.Регистратор.Установить(ПеремещениеТ);
// Считываем движения из базы данных в память сервера приложений
НаборЗаписей.Прочитать();
// Обходим коллекцию движений в цикле
Для Каждого Запись Из НаборЗаписей Цикл
СчетДт = Запись.СчетДт;
СчетКт = Запись.СчетКт;
Сумма = Запись.Сумма;
// Здесь мы можем проанализировать или изменить поля записи
// Например: Запись.Сумма = Сумма * 1.1; // Увеличим сумму на 10%
КонецЦикла;
// Если мы вносили изменения, их необходимо записать обратно в базу данных:
// НаборЗаписей.Записать();
Преимущества метода: Идеально подходит для алгоритмов корректировки движений, так как позволяет напрямую редактировать записи регистра и сохранять их обратно в базу данных.
Данный способ ориентирован на использование внутри модулей самих документов (например, в процедуре проведения ОбработкаПроведения), когда объект документа уже находится в оперативной памяти сервера приложений. Использовать этот метод «извне» (из внешней обработки или формы другого объекта) не рекомендуется, так как для этого придется принудительно получать «тяжелый» объект из базы данных по имеющейся у нас ссылке.
Проанализируем структуру кода для этого варианта:
// Получаем тяжелый объект документа из ссылки (Не рекомендуется для простого чтения данных!)
ДокументОбъект = ПеремещениеТ.ПолучитьОбъект();
// Обращаемся к коллекции движений документа и принудительно считываем записи нужного регистра
ДокументОбъект.Движения.Хозрасчетный.Прочитать();
// Перебираем проводки
Для Каждого Проводка Из ДокументОбъект.Движения.Хозрасчетный Цикл
СчетДт = Проводка.СчетДт;
СчетКт = Проводка.СчетКт;
Сумма = Проводка.Сумма;
// Обрабатываем проводку
КонецЦикла;
Преимущества метода: Простота интеграции кода внутри родных процедур проведения документов в конфигураторе.
Подводя итог, сформулируем ключевые правила для качественной и быстрой разработки на платформе 1С:
НайтиПоРеквизиту, если у вас на форме или в алгоритме уже присутствует ссылка (ДокументСсылка или СправочникСсылка) на необходимый объект. Ссылка уже дает прямой доступ к данным.