К статье
Практический материал

Разработка и Конфигурация · 12 мин

Как получить объект на сервере 1С

Как получить объект на сервере 1С через ПолучитьОбъект, запросы и менеджеры объектов. Различия ссылки и объекта, примеры кода, типичные ошибки и проверка результата.

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

Ключевое различие, которое нужно понимать перед началом работы: ссылка — это лишь идентификатор записи в базе, а объект — это полноценная структура данных, загруженная в память, которую можно изменять и записывать. Чтение реквизитов доступно через ссылку напрямую (платформа сама выполнит обращение к базе), а вот для модификации данных объект получить придётся.

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

Чем ссылка отличается от объекта и когда нужен каждый тип

Ссылка (например, тип СправочникСсылка.Номенклатура или ДокументСсылка.РеализацияТоваровУслуг) — это уникальный идентификатор записи. Через ссылку можно читать реквизиты: выражение Ссылка.Наименование заставит платформу выполнить обращение к базе и вернуть значение. Но присвоить новое значение реквизиту через ссылку нельзя — попытка вызовет ошибку.

Объект (СправочникОбъект, ДокументОбъект) — это загруженная в память копия данных с возможностью редактирования. Только объект имеет методы Записать(), УстановитьПометкуУдаления(), Заполнить() и доступ к табличным частям для редактирования. Получение объекта — более тяжёлая операция: платформа читает все реквизиты и табличные части целиком.

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

Основной способ: метод ПолучитьОбъект() у ссылки

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

bsl
&НаСервере
Процедура ИзменитьНаименованиеНаСервере(СсылкаНаЭлемент)
	Объект = СсылкаНаЭлемент.ПолучитьОбъект();
	Объект.Наименование = "Новое наименование";
	Объект.Записать();
КонецПроцедуры

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

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

Получение объекта в серверном контексте формы

В управляемом приложении код формы делится на клиентский и серверный. Метод ПолучитьОбъект() можно вызывать только в процедурах с директивой &НаСервере или &НаСервереБезКонтекста. В клиентских процедурах (&НаКлиенте) ссылки доступны, но объект получить нельзя — попытка приведёт к ошибке компиляции или выполнения.

Типовой шаблон: клиентская процедура (например, обработчик команды) вызывает серверную процедуру и передаёт ей ссылку. Серверная процедура получает объект, изменяет и записывает его. Если серверная процедура не использует данные формы, объявляйте её с директивой &НаСервереБезКонтекста — так вызов будет быстрее, поскольку не сериализуются данные формы.

bsl
&НаКлиенте
Процедура ЗаполнитьКомментарий(Команда)
	ЗаполнитьКомментарийНаСервере(Объект.Ссылка);
КонецПроцедуры

&НаСервереБезКонтекста
Процедура ЗаполнитьКомментарийНаСервере(Ссылка)
	ДокОбъект = Ссылка.ПолучитьОбъект();
	ДокОбъект.Комментарий = "Заполнено программно";
	ДокОбъект.Записать();
КонецПроцедуры

Отдельный случай — форма уже открытого объекта. Если вы редактируете объект в форме и нужно сохранить изменения, не вызывайте ПолучитьОбъект() для той же ссылки: вы получите вторую копию данных, и запись приведёт либо к конфликту версий, либо к затиранию изменений формы. Для работы с данными текущей формы используйте РеквизитФормыВЗначение("Объект") и обратно — ЗначениеВРеквизитФормы().

Получение объектов через запрос

Когда нужно обработать много записей, получать объект каждой по отдельности в цикле — плохая идея. Сначала выполните запрос, отберите нужные ссылки, а затем в цикле получайте объекты только тех записей, которые действительно нужно изменить.

bsl
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|	Номенклатура.Ссылка КАК Ссылка
|ИЗ
|	Справочник.Номенклатура КАК Номенклатура
|ГДЕ
|	НЕ Номенклатура.ПометкаУдаления";

Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
	Объект = Выборка.Ссылка.ПолучитьОбъект();
	Объект.Записать();
КонецЦикла;

Если запрос нужен только для чтения, не тяните в него получение объекта вообще — выбирайте нужные поля прямо в запросе. Запрос возвращает данные быстрее, чем чтение через ссылку, потому что все поля извлекаются одним обращением к СУБД, а не по одному на каждый реквизит.

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

Создание нового объекта на сервере

Новый объект получают не через ссылку (её ещё нет), а через менеджер соответствующего типа: Справочники.Номенклатура.СоздатьЭлемент(), Документы.РеализацияТоваровУслуг.СоздатьДокумент(), Справочники.Контрагенты.СоздатьГруппу(). Менеджер возвращает объект с заполненными значениями по умолчанию, но ещё не записанный в базу.

bsl
НовыйЭлемент = Справочники.Номенклатура.СоздатьЭлемент();
НовыйЭлемент.Наименование = "Новая позиция";
НовыйЭлемент.Записать();
СсылкаНаНовый = НовыйЭлемент.Ссылка;

После вызова Записать() у объекта появляется ссылка, которую можно вернуть на клиент или использовать дальше. До записи свойство Ссылка у нового объекта пустое. Если нужно заранее знать ссылку до записи (например, для заполнения подчинённых данных), используйте УстановитьСсылкуНового() с предварительно полученной ссылкой через ПолучитьСсылку() менеджера объекта.

Работа в общих модулях и фоновых заданиях

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

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

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

Маршрут решения

Как выбрать способ

Нужно изменить одну запись по известной ссылкеВызовите Ссылка.ПолучитьОбъект() в серверной процедуре, измените и запишите
Нужно обработать много записейОтберите ссылки запросом, в цикле получайте объекты только изменяемых записей
Нужно только прочитать данныеИспользуйте запрос или чтение реквизитов через ссылку, объект не получайте
Редактируется объект текущей открытой формыИспользуйте РеквизитФормыВЗначение, а не ПолучитьОбъект по той же ссылке
Обработка долгая и блокирует интерфейсВынесите получение и запись объектов в фоновое задание

Проверка результата и типичные ошибки

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

Контрольные точки

Как проверить результат

Результат ПолучитьОбъект()
Норма

Возвращён объект, реквизиты читаются без ошибок

Тревожный признак

Возвращено Неопределено — ссылка битая или запись удалена

Вызов Записать()
Норма

Процедура завершилась без исключения, у нового объекта заполнена Ссылка

Тревожный признак

Исключение с текстом ошибки — смотрите журнал регистрации

Изменения в базе
Норма

Повторное чтение через ссылку возвращает новые значения

Тревожный признак

Значения старые — запись не выполнена или объект не тот

Конфликт версий
Норма

Запись проходит без сообщения о конфликте

Тревожный признак

Ошибка «Конфликт блокировок» — объект изменён другим сеансом, перечитайте и повторите

Типичные ошибки
01
Вызов ПолучитьОбъект() в клиентской процедуре — ошибка компиляции; выносите код в &НаСервере
02
Обращение к реквизитам результата без проверки на Неопределено — аварийное завершение при битой ссылке
03
Получение объекта только для чтения реквизитов — лишняя нагрузка, используйте запрос
04
Получение объекта по ссылке уже открытой в форме записи — конфликт версий или потеря изменений пользователя
05
Запись объектов в цикле без обработки исключений — первая же ошибка прерывает всю обработку без диагностики
06
Забытый вызов Записать() после изменения реквизитов — изменения остаются только в памяти и теряются

Ещё одна частая проблема — обмен данными. Если запись объекта должна или не должна регистрироваться в планах обмена, управляйте этим явно через Объект.ОбменДанными.Загрузка = Истина (для загрузки без проверок и без регистрации) или настройкой получателей. Игнорирование этого момента приводит к неожиданной миграции данных или, наоборот, к их отсутствию в узлах обмена.

При отладке серверного кода удобно временно выводить тип полученного значения через Сообщить(ТипЗнч(Результат)) — сразу видно, вернулся ли объект, Неопределено или, по ошибке, сама ссылка.
ПЗ
Практическое замечание
Итог

Что важно запомнить

Объект получают методом ПолучитьОбъект() у ссылки и только в серверном контексте. Для чтения данных объект не нужен — достаточно ссылки или запроса. Проверяйте результат на Неопределено, обрабатывайте исключения при Записать() и не получайте объект записи, уже открытой в форме редактирования.

Частые вопросы

Можно ли получить объект на клиенте в управляемом приложении?

Нет. В клиентском контексте управляемой формы доступны только ссылки. Объект получают в процедуре с директивой &НаСервере или &НаСервереБезКонтекста, передав туда ссылку параметром. В толстом клиенте обычного приложения объект на клиенте получить можно, но это устаревший режим работы.

Почему ПолучитьОбъект() возвращает Неопределено?

Это означает, что записи с такой ссылкой в базе нет: она удалена непосредственно (не через пометку удаления), ссылка получена из другой информационной базы или реквизит содержит «битую» ссылку. Перед использованием результата добавьте проверку: Если Объект = Неопределено Тогда ... КонецЕсли.

Что быстрее: ПолучитьОбъект() или запрос?

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

Как получить объект документа и провести его программно?

Получите объект через Ссылка.ПолучитьОбъект(), при необходимости измените реквизиты и вызовите Записать(РежимЗаписиДокумента.Проведение). Если проведение завершилось ошибкой, платформа вызовет исключение с описанием причины — его можно перехватить в попытке-исключении и зафиксировать в журнале регистрации.

Как изменить объект, который сейчас открыт в форме у пользователя?

Лучше не получать второй объект по той же ссылке — при записи возникнет конфликт версий или изменения пользователя будут затёрты. Варианты: изменять данные через саму форму (РеквизитФормыВЗначение), закрыть форму перед обработкой либо перечитывать объект непосредственно перед записью и аккуратно переносить изменения.

Нужно ли получать объект, чтобы установить пометку удаления?

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