ЭтотОбъект, ссылки и выборки: доступ к данным из модуля объекта 1С 8.3
Содержание
В модуле объекта 1С к самому объекту обращаются через свойство ЭтотОбъект, а к его реквизитам и табличным частям можно обращаться напрямую по имени: Наименование, Дата, Товары. Запись ЭтотОбъект.Наименование и просто Наименование в модуле объекта означают одно и то же.
Сложности начинаются за пределами модуля объекта: в модуле формы ЭтотОбъект — это уже форма, в модуле менеджера его нет вовсе, а в общем модуле объект нужно сначала получить по ссылке или создать. Ниже — как обратиться к объекту в каждом из этих мест, как выбирать данные из базы и как правильно записывать изменения. Всё относится к платформе 8.3 и к любой конфигурации, будь то БП 3.0, ЗУП 3.1 или самописная.
Где вы пишете код: модуль объекта, формы или менеджера
Первое, что нужно понять: «обратиться к объекту» в разных модулях делается по-разному. Платформа даёт каждому модулю свой контекст.
| Где код | Что такое ЭтотОбъект | Как получить данные объекта |
|---|---|---|
| Модуль объекта (справочника, документа, обработки) | Сам объект: СправочникОбъект, ДокументОбъект | Реквизиты по имени или ЭтотОбъект.Реквизит |
| Модуль управляемой формы | Сама форма (ФормаКлиентскогоПриложения) | Реквизит формы Объект |
| Модуль менеджера | Свойства нет | Через параметр-ссылку или конструктор |
| Общий модуль | Свойства нет | Через параметр-ссылку, ПолучитьОбъект() или запрос |
| Модуль набора записей регистра | Сам набор записей | ЭтотОбъект.Отбор, перебор Для Каждого Запись Из ЭтотОбъект |
Модуль объекта открывается в конфигураторе так: правой кнопкой по объекту в дереве метаданных → Открыть модуль объекта. В свойствах объекта на вкладке «Прочее» есть кнопка с тем же названием.
Обращение к самому объекту внутри его модуля
Внутри модуля объекта не нужно ничего получать — объект уже «вы». Пример для справочника «Номенклатура»:
Процедура ПередЗаписью(Отказ)
Если ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
// Обращение к реквизиту напрямую
Наименование = СокрЛП(Наименование);
// То же самое через ЭтотОбъект
Если ПустаяСтрока(ЭтотОбъект.Артикул) Тогда
ЭтотОбъект.Артикул = "Б/А";
КонецЕсли;
КонецПроцедуры
ЭтотОбъект нужен в трёх случаях:
- 🔹 когда объект надо передать параметром в другую процедуру:
ОбщийМодуль.Проверить(ЭтотОбъект); - 🔹 когда имя локальной переменной совпадает с именем реквизита и нужно явно указать реквизит;
- 🔹 для читаемости в длинных процедурах, где непонятно, откуда берётся
Сумма.
Табличные части доступны так же: Товары.Итог("Сумма"), Товары.Добавить(), Для Каждого Строка Из Товары Цикл. Движения документа — через коллекцию Движения: Движения.ТоварыНаСкладах.Записывать = Истина.
Обращение из модуля формы
В управляемой форме данные объекта лежат в основном реквизите формы, который по умолчанию называется Объект. Это не сам объект, а его копия в виде ДанныеФормыСтруктура. На клиенте читать и менять реквизиты можно (Объект.Наименование), а вызвать экспортный метод модуля объекта нельзя.
Чтобы выполнить процедуру из модуля объекта, объект «разворачивают» на сервере:
&НаСервере
Процедура ЗаполнитьТоварыНаСервере()
ДокументОбъект = РеквизитФормыВЗначение("Объект");
ДокументОбъект.ЗаполнитьПоОстаткам(); // экспортная процедура модуля объекта
ЗначениеВРеквизитФормы(ДокументОбъект, "Объект");
КонецПроцедуры
Процедура в модуле объекта должна быть объявлена с ключевым словом Экспорт, иначе снаружи её не видно: Процедура ЗаполнитьПоОстаткам() Экспорт.
Без ЗначениеВРеквизитФормы изменения, сделанные методом, на форме не появятся: вы поменяли копию, а форма показывает свою.
Получение объекта по ссылке и создание нового
Вне модуля объекта у вас обычно есть ссылка — значение типа СправочникСсылка.Номенклатура или ДокументСсылка.ПоступлениеТоваровУслуг. Ссылка только указывает на запись в базе. Чтобы что-то изменить, нужен объект.
Метод ПолучитьОбъект()
НоменклатураОбъект = СсылкаНаТовар.ПолучитьОбъект();
Если НоменклатураОбъект = Неопределено Тогда
// Объект удалён из базы, ссылка «битая»
Возврат;
КонецЕсли;
НоменклатураОбъект.Комментарий = "Проверено";
НоменклатураОбъект.Записать();
ПолучитьОбъект() читает объект из базы целиком, вместе со всеми табличными частями. Для документа на 5 000 строк это заметная нагрузка, поэтому получать объект «просто чтобы посмотреть реквизит» не стоит.
Конструкторы через менеджер
Новый объект создаётся методами менеджера:
| Что создаём | Метод |
|---|---|
| Элемент справочника | Справочники.Номенклатура.СоздатьЭлемент() |
| Группу справочника | Справочники.Номенклатура.СоздатьГруппу() |
| Документ | Документы.ПоступлениеТоваровУслуг.СоздатьДокумент() |
| Набор записей регистра | РегистрыСведений.ЦеныНоменклатуры.СоздатьНаборЗаписей() |
| Менеджер записи (регистр сведений) | РегистрыСведений.ЦеныНоменклатуры.СоздатьМенеджерЗаписи() |
У нового объекта ссылка пустая до первой записи: ЭтотОбъект.Ссылка.Пустая() вернёт Истина. Проверить, что объект новый, можно и методом ЭтоНовый(). Если ссылка нужна заранее (например, чтобы сослаться на документ из другого объекта до записи), используют ПолучитьСсылкуНового() — он возвращает ссылку, которую объект получит при записи.
Обращение через глобальный контекст
Менеджеры всех объектов конфигурации доступны из любого серверного модуля через свойства глобального контекста: Справочники, Документы, РегистрыСведений, РегистрыНакопления, Перечисления, Константы.
Типичные обращения:
- 📌 поиск элемента:
Справочники.Валюты.НайтиПоКоду("643"),Справочники.Склады.НайтиПоНаименованию("Основной склад")— оба возвращают ссылку, а если ничего не найдено — пустую ссылку; - 📌 предопределённый элемент:
Справочники.Организации.ИмяПредопределенногоилиПредопределенноеЗначение("Справочник.Организации.ИмяПредопределенного")— второй вариант работает и на клиенте; - 📌 значение перечисления:
Перечисления.СтавкиНДС.НДС20; - 📌 константа:
Константы.ВалютаРегламентированногоУчета.Получить()и.Установить(Значение); - 📌 экспортная функция модуля менеджера:
Документы.РеализацияТоваровУслуг.ИмяФункции(Параметры).
НайтиПоНаименованию возвращает первый найденный элемент. Если в справочнике два элемента с одинаковым наименованием, вы получите любой из них. Для ключевых данных ищите по коду или запросом с дополнительными условиями.
Обращение к реквизиту через точку от ссылки
Запись СсылкаНаДоговор.Владелец.ИНН работает, но каждая точка — это чтение из базы. Платформа загружает объект целиком в кэш, чтобы отдать одно поле. В цикле по тысяче строк это превращается в тысячу лишних обращений.
В конфигурациях на Библиотеке стандартных подсистем (БП 3.0, ЗУП 3.1, УТ 11 и др.) для этого есть функции ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Ссылка, "ИмяРеквизита") и ОбщегоНазначения.ЗначенияРеквизитовОбъекта(Ссылка, "Реквизит1, Реквизит2"). Они читают только нужные поля одним запросом.
Выборка данных из базы: запросы и метод Выбрать()
Когда нужно много объектов, обращаются не к каждому по отдельности, а выбирают их пачкой.
Запрос — основной способ
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.Артикул КАК Артикул
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| НЕ Номенклатура.ПометкаУдаления
| И Номенклатура.Артикул = &Артикул";
Запрос.УстановитьПараметр("Артикул", "А-100");
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Сообщить(Выборка.Артикул);
КонецЦикла;
Результат запроса можно не только перебрать выборкой, но и выгрузить в таблицу значений: Запрос.Выполнить().Выгрузить(). Если нужно потом изменить найденные объекты, в запросе выбирают поле Ссылка и для каждой строки вызывают Выборка.Ссылка.ПолучитьОбъект().
Выборка через менеджер
У менеджеров справочников и документов есть метод Выбрать(): Справочники.Номенклатура.Выбрать(). Он перебирает элементы без текста запроса. Из такой выборки объект получают методом Выборка.ПолучитьОбъект().
| Способ | Когда подходит | Минус |
|---|---|---|
| Запрос | Отбор по нескольким условиям, соединения, итоги | Нужен текст запроса |
Менеджер.Выбрать() | Простой перебор всех элементов | Слабые возможности отбора |
| Обращение через точку | Одно-два поля в единичном случае | Медленно в циклах |
Текст запроса удобно собирать конструктором: в модуле правой кнопкой → Конструктор запроса. Он сам расставит псевдонимы и вертикальные черты.
Ключи записей и наборы записей регистров
У записи регистра нет ссылки, как у справочника. Её однозначно определяет набор измерений (и период для периодического регистра). Поэтому к записям регистров обращаются тремя способами.
Ключ записи
Ключ — значение вида РегистрСведенийКлючЗаписи.ЦеныНоменклатуры. Он нужен, например, чтобы открыть форму конкретной записи. Создаётся из структуры значений измерений:
ЗначенияКлюча = Новый Структура;
ЗначенияКлюча.Вставить("Период", НачалоДня(ТекущаяДатаСеанса()));
ЗначенияКлюча.Вставить("Номенклатура", СсылкаНаТовар);
Ключ = РегистрыСведений.ЦеныНоменклатуры.СоздатьКлючЗаписи(ЗначенияКлюча);
Менеджер записи — одна запись независимого регистра сведений
МенеджерЗаписи = РегистрыСведений.ЦеныНоменклатуры.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Период = НачалоДня(ТекущаяДатаСеанса());
МенеджерЗаписи.Номенклатура = СсылкаНаТовар;
МенеджерЗаписи.Прочитать();
Если МенеджерЗаписи.Выбран() Тогда
МенеджерЗаписи.Цена = 1500;
МенеджерЗаписи.Записать();
КонецЕсли;
Менеджер записи доступен только для регистров сведений, независимых от регистратора.
Набор записей — группа записей по отбору
Набор = РегистрыСведений.ЦеныНоменклатуры.СоздатьНаборЗаписей();
Набор.Отбор.Номенклатура.Установить(СсылкаНаТовар);
Набор.Прочитать();
Для Каждого Запись Из Набор Цикл
Запись.Цена = Запись.Цена * 1.1;
КонецЦикла;
Набор.Записать();
Набор записывается целиком по отбору: всё, что под отбор попадает, но не оказалось в наборе в момент записи, будет удалено. Если забыть Прочитать() и сразу записать набор с одной новой строкой, вы потеряете все остальные записи по этому товару.
Для чтения без изменения проще методы менеджера: СрезПоследних(Дата, Отбор) возвращает таблицу значений, ПолучитьПоследнее(Дата, Отбор) — структуру с ресурсами. Для регистров накопления обычно используют запрос к виртуальным таблицам Остатки и Обороты.
Изменение и запись объекта
Запись снаружи
После изменения объект записывают методом Записать(). Для документа можно сразу провести:
ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
Отменить проведение — РежимЗаписиДокумента.ОтменаПроведения. Пометить на удаление — УстановитьПометкуУдаления(Истина); для документа это ещё и отменит проведение.
Ошибки записи лучше перехватывать, особенно в обработках, которые меняют сотни объектов:
Попытка
ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
Исключение
ЗаписьЖурналаРегистрации("Перепроведение", УровеньЖурналаРегистрации.Ошибка,,
ДокументОбъект.Ссылка, ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
КонецПопытки;
Изменения внутри обработчиков модуля объекта
Внутри модуля объекта реквизиты меняют в обработчике ПередЗаписью — они попадут в базу при текущей записи. Вызывать Записать() там нельзя: обработчик сработает снова, и получится рекурсия.
В ПриЗаписи объект уже записан (транзакция ещё открыта). Менять реквизиты ЭтотОбъект здесь бессмысленно — в базу они не попадут без ещё одной записи. Зато здесь удобно писать связанные данные: наборы записей регистров, другие объекты.
Чтобы передать флаг из внешнего кода в обработчики, используйте коллекцию ДополнительныеСвойства:
// Снаружи
ДокументОбъект.ДополнительныеСвойства.Вставить("БезПроверкиОстатков", Истина);
ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
// В модуле объекта, в ОбработкаПроведения
Если ДополнительныеСвойства.Свойство("БезПроверкиОстатков") Тогда
// пропускаем контроль
КонецЕсли;
Флаг ОбменДанными.Загрузка = Истина отключает проверки и большинство действий в обработчиках типовых конфигураций. Ставьте его только при загрузке данных, которые уже проверены в другой базе, иначе в рабочую базу попадут объекты в обход контроля.
Если объект могут одновременно править пользователи, перед изменением его можно заблокировать: Объект.Заблокировать(). При попытке заблокировать объект, который уже открыт на редактирование в другом сеансе, будет исключение — его тоже стоит обработать.
Частые промахи при обращении к объекту
- ❌
ЭтотОбъектв модуле формы вместоОбъект. Код видаЭтотОбъект.Наименованиев форме ищет реквизит формы, а не справочника, и падает с ошибкой «Поле объекта не обнаружено». - ❌ Ссылка нового объекта в
ПередЗаписью. У нового элементаСсылкаещё пустая. Если по ней строится запрос или запись в регистр, используйтеПолучитьСсылкуНового()либо перенесите код вПриЗаписи. - ❌
ПолучитьОбъект()без проверки. Для удалённого объекта метод вернётНеопределено, и следующая строка упадёт. - ❌ Получение объекта в цикле только для чтения. Обработка по 10 000 документам, которая вызывает
ПолучитьОбъект()ради даты, работает в разы дольше запроса по тем же данным. - ❌ Вызов клиентского кода из модуля объекта. Модуль объекта выполняется на сервере:
Предупреждение(),ОткрытьФорму()там недоступны. Для сообщений пользователю естьСообщить()или объектСообщениеПользователю. - ❌ Набор записей без
Прочитать(). Описано выше: при записи пропадут чужие строки под тем же отбором.
Частые вопросы
Можно ли обратиться к объекту из общего модуля без параметра?
МойМодуль.Обработать(ЭтотОбъект).Чем ЭтотОбъект отличается от Ссылка в модуле объекта?
ЭтотОбъект — данные в памяти, включая несохранённые изменения. Ссылка — указатель на запись в базе; обращение через неё прочитает сохранённую версию.Как вызвать процедуру модуля объекта из модуля менеджера?
Ссылка.ПолучитьОбъект()) или создайте конструктором, затем вызовите экспортную процедуру этого объекта.Почему изменения в ПриЗаписи не сохраняются?
ПриЗаписи объект уже записан. Меняйте реквизиты в ПередЗаписью, а в ПриЗаписи пишите только связанные данные.Есть ли ЭтотОбъект в модуле обработки или отчёта?
ЭтотОбъект — сам экземпляр обработки или отчёта с его реквизитами и табличными частями.