Разработка и Конфигурация · 12 мин
Как получить объект на сервере 1С
Как получить объект на сервере 1С через ПолучитьОбъект, запросы и менеджеры объектов. Различия ссылки и объекта, примеры кода, типичные ошибки и проверка результата.
Получение объекта на сервере — одна из базовых операций при разработке на платформе 1С:Предприятие. Чаще всего задача сводится к тому, чтобы по имеющейся ссылке получить объект, изменить его реквизиты и записать обратно в базу. Однако у этой операции есть важные ограничения: не каждый контекст позволяет вызывать серверные методы напрямую, а неаккуратное использование ПолучитьОбъект() ведёт к лишней нагрузке на базу данных.
Ключевое различие, которое нужно понимать перед началом работы: ссылка — это лишь идентификатор записи в базе, а объект — это полноценная структура данных, загруженная в память, которую можно изменять и записывать. Чтение реквизитов доступно через ссылку напрямую (платформа сама выполнит обращение к базе), а вот для модификации данных объект получить придётся.
Ниже разобраны основные способы получения объекта на сервере: через менеджеры объектов, через запросы, в серверных процедурах общих модулей и на форме. Для каждого способа приведены условия применения, примеры кода и типичные ошибки.
Чем ссылка отличается от объекта и когда нужен каждый тип
Ссылка (например, тип СправочникСсылка.Номенклатура или ДокументСсылка.РеализацияТоваровУслуг) — это уникальный идентификатор записи. Через ссылку можно читать реквизиты: выражение Ссылка.Наименование заставит платформу выполнить обращение к базе и вернуть значение. Но присвоить новое значение реквизиту через ссылку нельзя — попытка вызовет ошибку.
Объект (СправочникОбъект, ДокументОбъект) — это загруженная в память копия данных с возможностью редактирования. Только объект имеет методы Записать(), УстановитьПометкуУдаления(), Заполнить() и доступ к табличным частям для редактирования. Получение объекта — более тяжёлая операция: платформа читает все реквизиты и табличные части целиком.
Практическое правило: если нужно только прочитать данные — работайте через ссылку или запрос. Получайте объект только тогда, когда планируете изменять и записывать данные. Это существенно влияет на производительность при пакетной обработке.
Основной способ: метод ПолучитьОбъект() у ссылки
Самый прямой способ получить объект на сервере — вызвать метод ПолучитьОбъект() у переменной, содержащей ссылку. Метод доступен для ссылок на справочники, документы, планы видов характеристик, планы счетов, задачи и другие объектные типы.
&НаСервере
Процедура ИзменитьНаименованиеНаСервере(СсылкаНаЭлемент)
Объект = СсылкаНаЭлемент.ПолучитьОбъект();
Объект.Наименование = "Новое наименование";
Объект.Записать();
КонецПроцедурыЕсли ссылка указывает на несуществующую запись (битая ссылка), ПолучитьОбъект() вернёт Неопределено. Попытка обратиться к реквизитам такого «объекта» вызовет ошибку выполнения. Поэтому перед работой с результатом стоит проверять его на Неопределено, особенно если ссылка получена из внешнего источника или из реквизита, который мог быть очищен.
Для предварительной проверки существования записи удобно использовать запрос или функцию ОбщегоНазначения.СсылкаСуществует() из библиотеки стандартных подсистем, если она внедрена в конфигурацию. Это дешевле, чем получать объект и ловить Неопределено.
Получение объекта в серверном контексте формы
В управляемом приложении код формы делится на клиентский и серверный. Метод ПолучитьОбъект() можно вызывать только в процедурах с директивой &НаСервере или &НаСервереБезКонтекста. В клиентских процедурах (&НаКлиенте) ссылки доступны, но объект получить нельзя — попытка приведёт к ошибке компиляции или выполнения.
Типовой шаблон: клиентская процедура (например, обработчик команды) вызывает серверную процедуру и передаёт ей ссылку. Серверная процедура получает объект, изменяет и записывает его. Если серверная процедура не использует данные формы, объявляйте её с директивой &НаСервереБезКонтекста — так вызов будет быстрее, поскольку не сериализуются данные формы.
&НаКлиенте
Процедура ЗаполнитьКомментарий(Команда)
ЗаполнитьКомментарийНаСервере(Объект.Ссылка);
КонецПроцедуры
&НаСервереБезКонтекста
Процедура ЗаполнитьКомментарийНаСервере(Ссылка)
ДокОбъект = Ссылка.ПолучитьОбъект();
ДокОбъект.Комментарий = "Заполнено программно";
ДокОбъект.Записать();
КонецПроцедурыОтдельный случай — форма уже открытого объекта. Если вы редактируете объект в форме и нужно сохранить изменения, не вызывайте ПолучитьОбъект() для той же ссылки: вы получите вторую копию данных, и запись приведёт либо к конфликту версий, либо к затиранию изменений формы. Для работы с данными текущей формы используйте РеквизитФормыВЗначение("Объект") и обратно — ЗначениеВРеквизитФормы().
Получение объектов через запрос
Когда нужно обработать много записей, получать объект каждой по отдельности в цикле — плохая идея. Сначала выполните запрос, отберите нужные ссылки, а затем в цикле получайте объекты только тех записей, которые действительно нужно изменить.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| НЕ Номенклатура.ПометкаУдаления";
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Объект = Выборка.Ссылка.ПолучитьОбъект();
Объект.Записать();
КонецЦикла;Если запрос нужен только для чтения, не тяните в него получение объекта вообще — выбирайте нужные поля прямо в запросе. Запрос возвращает данные быстрее, чем чтение через ссылку, потому что все поля извлекаются одним обращением к СУБД, а не по одному на каждый реквизит.
Для массовых модификаций учитывайте транзакционность: при записи тысяч объектов имеет смысл либо оборачивать всю операцию в транзакцию (риск длинных блокировок), либо фиксировать транзакции порциями. Второй вариант безопаснее для многопользовательской работы.
Создание нового объекта на сервере
Новый объект получают не через ссылку (её ещё нет), а через менеджер соответствующего типа: Справочники.Номенклатура.СоздатьЭлемент(), Документы.РеализацияТоваровУслуг.СоздатьДокумент(), Справочники.Контрагенты.СоздатьГруппу(). Менеджер возвращает объект с заполненными значениями по умолчанию, но ещё не записанный в базу.
НовыйЭлемент = Справочники.Номенклатура.СоздатьЭлемент();
НовыйЭлемент.Наименование = "Новая позиция";
НовыйЭлемент.Записать();
СсылкаНаНовый = НовыйЭлемент.Ссылка;После вызова Записать() у объекта появляется ссылка, которую можно вернуть на клиент или использовать дальше. До записи свойство Ссылка у нового объекта пустое. Если нужно заранее знать ссылку до записи (например, для заполнения подчинённых данных), используйте УстановитьСсылкуНового() с предварительно полученной ссылкой через ПолучитьСсылку() менеджера объекта.
Работа в общих модулях и фоновых заданиях
Для прикладной логики, вызываемой из разных мест, правильное место — общий модуль с установленным флагом «Сервер». Внутри такого модуля ПолучитьОбъект() доступен без ограничений. Если модуль должен вызываться с клиента, дополнительно включите флаг «Вызов сервера».
В фоновых заданиях (ФоновыеЗадания.Выполнить) и регламентных заданиях код выполняется на сервере полностью, поэтому получение объектов работает так же, как в серверной процедуре. Это удобно для длительных пакетных обработок, чтобы не блокировать сеанс пользователя.
Внешние обработки, запускаемые в управляемом приложении, по умолчанию выполняются в клиентском контексте формы обработки. Для получения объектов добавьте в модуль формы серверные процедуры или обращайтесь к серверному общему модулю.
Как выбрать способ
Проверка результата и типичные ошибки
После записи объекта убедитесь, что операция завершилась успешно. Метод Записать() при ошибке вызывает исключение, поэтому в пакетных обработках его помещают в попытку-исключение с записью в журнал регистрации. Проверить, что объект записан, можно по заполненности ссылки и отсутствию исключения, а также повторным чтением данных из базы.
Как проверить результат
Возвращён объект, реквизиты читаются без ошибок
Возвращено Неопределено — ссылка битая или запись удалена
Процедура завершилась без исключения, у нового объекта заполнена Ссылка
Исключение с текстом ошибки — смотрите журнал регистрации
Повторное чтение через ссылку возвращает новые значения
Значения старые — запись не выполнена или объект не тот
Запись проходит без сообщения о конфликте
Ошибка «Конфликт блокировок» — объект изменён другим сеансом, перечитайте и повторите
Ещё одна частая проблема — обмен данными. Если запись объекта должна или не должна регистрироваться в планах обмена, управляйте этим явно через Объект.ОбменДанными.Загрузка = Истина (для загрузки без проверок и без регистрации) или настройкой получателей. Игнорирование этого момента приводит к неожиданной миграции данных или, наоборот, к их отсутствию в узлах обмена.
При отладке серверного кода удобно временно выводить тип полученного значения через Сообщить(ТипЗнч(Результат)) — сразу видно, вернулся ли объект, Неопределено или, по ошибке, сама ссылка.
Что важно запомнить
Объект получают методом ПолучитьОбъект() у ссылки и только в серверном контексте. Для чтения данных объект не нужен — достаточно ссылки или запроса. Проверяйте результат на Неопределено, обрабатывайте исключения при Записать() и не получайте объект записи, уже открытой в форме редактирования.
Частые вопросы
Можно ли получить объект на клиенте в управляемом приложении?
Нет. В клиентском контексте управляемой формы доступны только ссылки. Объект получают в процедуре с директивой &НаСервере или &НаСервереБезКонтекста, передав туда ссылку параметром. В толстом клиенте обычного приложения объект на клиенте получить можно, но это устаревший режим работы.
Почему ПолучитьОбъект() возвращает Неопределено?
Это означает, что записи с такой ссылкой в базе нет: она удалена непосредственно (не через пометку удаления), ссылка получена из другой информационной базы или реквизит содержит «битую» ссылку. Перед использованием результата добавьте проверку: Если Объект = Неопределено Тогда ... КонецЕсли.
Что быстрее: ПолучитьОбъект() или запрос?
Для чтения данных запрос практически всегда эффективнее, особенно при обработке множества записей: он забирает только нужные поля одним обращением к СУБД. ПолучитьОбъект() оправдан, когда данные нужно изменить и записать, потому что только объект поддерживает запись.
Как получить объект документа и провести его программно?
Получите объект через Ссылка.ПолучитьОбъект(), при необходимости измените реквизиты и вызовите Записать(РежимЗаписиДокумента.Проведение). Если проведение завершилось ошибкой, платформа вызовет исключение с описанием причины — его можно перехватить в попытке-исключении и зафиксировать в журнале регистрации.
Как изменить объект, который сейчас открыт в форме у пользователя?
Лучше не получать второй объект по той же ссылке — при записи возникнет конфликт версий или изменения пользователя будут затёрты. Варианты: изменять данные через саму форму (РеквизитФормыВЗначение), закрыть форму перед обработкой либо перечитывать объект непосредственно перед записью и аккуратно переносить изменения.
Нужно ли получать объект, чтобы установить пометку удаления?
Да, пометка удаления — это изменение записи, поэтому нужен объект: получите его через ПолучитьОбъект() и вызовите УстановитьПометкуУдаления(). Для массовой пометки в типовых конфигурациях часто используют штатные механизмы удаления помеченных объектов, которые делают это автоматически.